From aefa053473af12ddb38fa004530b67dd24934cc3 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Wed, 12 Apr 2023 23:13:47 +0200 Subject: [PATCH] LibJS: Add spec comments to TypedArrayConstructor --- .../LibJS/Runtime/TypedArrayConstructor.cpp | 138 ++++++++++++++---- 1 file changed, 112 insertions(+), 26 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayConstructor.cpp b/Userland/Libraries/LibJS/Runtime/TypedArrayConstructor.cpp index 38cfa5614a..2225d964ab 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayConstructor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, Linus Groh + * Copyright (c) 2020-2023, Linus Groh * * SPDX-License-Identifier: BSD-2-Clause */ @@ -49,86 +49,172 @@ ThrowCompletionOr TypedArrayConstructor::call() // 23.2.1.1 %TypedArray% ( ), https://tc39.es/ecma262/#sec-%typedarray% ThrowCompletionOr> TypedArrayConstructor::construct(FunctionObject&) { + // 1. Throw a TypeError exception. return vm().throw_completion(ErrorType::ClassIsAbstract, "TypedArray"); } // 23.2.2.1 %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] ), https://tc39.es/ecma262/#sec-%typedarray%.from JS_DEFINE_NATIVE_FUNCTION(TypedArrayConstructor::from) { + auto source = vm.argument(0); + auto map_fn_value = vm.argument(1); + auto this_arg = vm.argument(2); + + // 1. Let C be the this value. auto constructor = vm.this_value(); + + // 2. If IsConstructor(C) is false, throw a TypeError exception. if (!constructor.is_constructor()) return vm.throw_completion(ErrorType::NotAConstructor, TRY_OR_THROW_OOM(vm, constructor.to_string_without_side_effects())); - FunctionObject* map_fn = nullptr; - if (!vm.argument(1).is_undefined()) { - auto callback = vm.argument(1); - if (!callback.is_function()) - return vm.throw_completion(ErrorType::NotAFunction, TRY_OR_THROW_OOM(vm, callback.to_string_without_side_effects())); - map_fn = &callback.as_function(); + // 3. If mapfn is undefined, let mapping be false. + GCPtr map_fn; + + // 4. Else, + if (!map_fn_value.is_undefined()) { + // a. If IsCallable(mapfn) is false, throw a TypeError exception. + if (!map_fn_value.is_function()) + return vm.throw_completion(ErrorType::NotAFunction, TRY_OR_THROW_OOM(vm, map_fn_value.to_string_without_side_effects())); + + // b. Let mapping be true. + map_fn = &map_fn_value.as_function(); } - auto source = vm.argument(0); - auto this_arg = vm.argument(2); + // 5. Let usingIterator be ? GetMethod(source, @@iterator). + auto* using_iterator = TRY(source.get_method(vm, *vm.well_known_symbol_iterator())); - auto using_iterator = TRY(source.get_method(vm, *vm.well_known_symbol_iterator())); + // 6. If usingIterator is not undefined, then if (using_iterator) { + // a. Let values be ? IteratorToList(? GetIteratorFromMethod(source, usingIterator)). auto values = TRY(iterable_to_list(vm, source, using_iterator)); - MarkedVector arguments(vm.heap()); - arguments.empend(values.size()); - auto target_object = TRY(typed_array_create(vm, constructor.as_function(), move(arguments))); + // b. Let len be the number of elements in values. + auto length = values.size(); - for (size_t k = 0; k < values.size(); ++k) { + // c. Let targetObj be ? TypedArrayCreate(C, « 𝔽(len) »). + MarkedVector arguments(vm.heap()); + arguments.empend(length); + auto* target_object = TRY(typed_array_create(vm, constructor.as_function(), move(arguments))); + + // d. Let k be 0. + // e. Repeat, while k < len, + for (size_t k = 0; k < length; ++k) { + // i. Let Pk be ! ToString(𝔽(k)). + auto property_key = PropertyKey { k }; + + // ii. Let kValue be the first element of values. + // iii. Remove the first element from values. auto k_value = values[k]; + Value mapped_value; - if (map_fn) + + // iv. If mapping is true, then + if (map_fn) { + // 1. Let mappedValue be ? Call(mapfn, thisArg, « kValue, 𝔽(k) »). mapped_value = TRY(JS::call(vm, *map_fn, this_arg, k_value, Value(k))); - else + } + // v. Else, let mappedValue be kValue. + else { mapped_value = k_value; - TRY(target_object->set(k, mapped_value, Object::ShouldThrowExceptions::Yes)); + } + + // vi. Perform ? Set(targetObj, Pk, mappedValue, true). + TRY(target_object->set(property_key, mapped_value, Object::ShouldThrowExceptions::Yes)); + + // vii. Set k to k + 1. } + // f. Assert: values is now an empty List. + // NOTE: We don't actually empty the list. + + // g. Return targetObj. return target_object; } - auto array_like = MUST(source.to_object(vm)); + // 7. NOTE: source is not an Iterable so assume it is already an array-like object. + // 8. Let arrayLike be ! ToObject(source). + auto* array_like = MUST(source.to_object(vm)); + + // 9. Let len be ? LengthOfArrayLike(arrayLike). auto length = TRY(length_of_array_like(vm, *array_like)); + // 10. Let targetObj be ? TypedArrayCreate(C, « 𝔽(len) »). MarkedVector arguments(vm.heap()); arguments.empend(length); - auto target_object = TRY(typed_array_create(vm, constructor.as_function(), move(arguments))); + auto* target_object = TRY(typed_array_create(vm, constructor.as_function(), move(arguments))); + // 11. Let k be 0. + // 12. Repeat, while k < len, for (size_t k = 0; k < length; ++k) { - auto k_value = TRY(array_like->get(k)); + // a. Let Pk be ! ToString(𝔽(k)). + auto property_key = PropertyKey { k }; + + // b. Let kValue be ? Get(arrayLike, Pk). + auto k_value = TRY(array_like->get(property_key)); + Value mapped_value; - if (map_fn) + + // c. If mapping is true, then + if (map_fn) { + // i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, 𝔽(k) »). mapped_value = TRY(JS::call(vm, *map_fn, this_arg, k_value, Value(k))); - else + } + // d. Else, let mappedValue be kValue. + else { mapped_value = k_value; - TRY(target_object->set(k, mapped_value, Object::ShouldThrowExceptions::Yes)); + } + + // e. Perform ? Set(targetObj, Pk, mappedValue, true). + TRY(target_object->set(property_key, mapped_value, Object::ShouldThrowExceptions::Yes)); + + // f. Set k to k + 1. } + // 13. Return targetObj. return target_object; } // 23.2.2.2 %TypedArray%.of ( ...items ), https://tc39.es/ecma262/#sec-%typedarray%.of JS_DEFINE_NATIVE_FUNCTION(TypedArrayConstructor::of) { + // 1. Let len be the number of elements in items. auto length = vm.argument_count(); + + // 2. Let C be the this value. auto constructor = vm.this_value(); + + // 3. If IsConstructor(C) is false, throw a TypeError exception. if (!constructor.is_constructor()) return vm.throw_completion(ErrorType::NotAConstructor, TRY_OR_THROW_OOM(vm, constructor.to_string_without_side_effects())); + + // 4. Let newObj be ? TypedArrayCreate(C, « 𝔽(len) »). MarkedVector arguments(vm.heap()); arguments.append(Value(length)); - auto new_object = TRY(typed_array_create(vm, constructor.as_function(), move(arguments))); - for (size_t k = 0; k < length; ++k) - TRY(new_object->set(k, vm.argument(k), Object::ShouldThrowExceptions::Yes)); + auto* new_object = TRY(typed_array_create(vm, constructor.as_function(), move(arguments))); + + // 5. Let k be 0. + // 6. Repeat, while k < len, + for (size_t k = 0; k < length; ++k) { + // a. Let kValue be items[k]. + auto k_value = vm.argument(k); + + // b. Let Pk be ! ToString(𝔽(k)). + auto property_key = PropertyKey { k }; + + // c. Perform ? Set(newObj, Pk, kValue, true). + TRY(new_object->set(property_key, k_value, Object::ShouldThrowExceptions::Yes)); + + // d. Set k to k + 1. + } + + // 7. Return newObj. return new_object; } // 23.2.2.4 get %TypedArray% [ @@species ], https://tc39.es/ecma262/#sec-get-%typedarray%-@@species JS_DEFINE_NATIVE_FUNCTION(TypedArrayConstructor::symbol_species_getter) { + // 1. Return the this value. return vm.this_value(); }