/* * Copyright (c) 2020, Andreas Kling * Copyright (c) 2020, Linus Groh * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include namespace JS { ArrayConstructor::ArrayConstructor(GlobalObject& global_object) : NativeFunction(vm().names.Array.as_string(), *global_object.function_prototype()) { } ArrayConstructor::~ArrayConstructor() { } void ArrayConstructor::initialize(GlobalObject& global_object) { auto& vm = this->vm(); NativeFunction::initialize(global_object); // 23.1.2.4 Array.prototype, https://tc39.es/ecma262/#sec-array.prototype define_property(vm.names.prototype, global_object.array_prototype(), 0); define_property(vm.names.length, Value(1), Attribute::Configurable); u8 attr = Attribute::Writable | Attribute::Configurable; define_native_function(vm.names.from, from, 1, attr); define_native_function(vm.names.isArray, is_array, 1, attr); define_native_function(vm.names.of, of, 0, attr); // 23.1.2.5 get Array [ @@species ], https://tc39.es/ecma262/#sec-get-array-@@species define_native_accessor(*vm.well_known_symbol_species(), symbol_species_getter, {}, Attribute::Configurable); } // 23.1.1.1 Array ( ...values ), https://tc39.es/ecma262/#sec-array Value ArrayConstructor::call() { if (vm().argument_count() <= 0) return Array::create(global_object()); if (vm().argument_count() == 1 && vm().argument(0).is_number()) { auto length = vm().argument(0); auto int_length = length.to_u32(global_object()); if (int_length != length.as_double()) { vm().throw_exception(global_object(), ErrorType::InvalidLength, "array"); return {}; } auto* array = Array::create(global_object()); array->indexed_properties().set_array_like_size(int_length); return array; } auto* array = Array::create(global_object()); for (size_t i = 0; i < vm().argument_count(); ++i) array->indexed_properties().append(vm().argument(i)); return array; } // 23.1.1.1 Array ( ...values ), https://tc39.es/ecma262/#sec-array Value ArrayConstructor::construct(FunctionObject&) { return call(); } // 23.1.2.1 Array.from ( items [ , mapfn [ , thisArg ] ] ), https://tc39.es/ecma262/#sec-array.from JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from) { auto value = vm.argument(0); auto object = value.to_object(global_object); if (!object) return {}; auto* array = Array::create(global_object); FunctionObject* map_fn = nullptr; if (!vm.argument(1).is_undefined()) { auto callback = vm.argument(1); if (!callback.is_function()) { vm.throw_exception(global_object, ErrorType::NotAFunction, callback.to_string_without_side_effects()); return {}; } map_fn = &callback.as_function(); } auto this_arg = vm.argument(2); // Array.from() lets you create Arrays from: if (auto size = object->indexed_properties().array_like_size()) { // * array-like objects (objects with a length property and indexed elements) MarkedValueList elements(vm.heap()); elements.ensure_capacity(size); for (size_t i = 0; i < size; ++i) { if (map_fn) { auto element = object->get(i); if (vm.exception()) return {}; auto map_fn_result = vm.call(*map_fn, this_arg, element, Value((i32)i)); if (vm.exception()) return {}; elements.append(map_fn_result); } else { elements.append(object->get(i)); if (vm.exception()) return {}; } } array->set_indexed_property_elements(move(elements)); } else { // * iterable objects i32 i = 0; get_iterator_values(global_object, value, [&](Value element) { if (vm.exception()) return IterationDecision::Break; if (map_fn) { auto map_fn_result = vm.call(*map_fn, this_arg, element, Value(i)); i++; if (vm.exception()) return IterationDecision::Break; array->indexed_properties().append(map_fn_result); } else { array->indexed_properties().append(element); } return IterationDecision::Continue; }); if (vm.exception()) return {}; } return array; } // 23.1.2.2 Array.isArray ( arg ), https://tc39.es/ecma262/#sec-array.isarray JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::is_array) { auto value = vm.argument(0); return Value(value.is_array(global_object)); } // 23.1.2.3 Array.of ( ...items ), https://tc39.es/ecma262/#sec-array.of JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::of) { auto this_value = vm.this_value(global_object); Value array; if (this_value.is_constructor()) { MarkedValueList arguments(vm.heap()); arguments.empend(vm.argument_count()); array = vm.construct(this_value.as_function(), this_value.as_function(), move(arguments)); if (vm.exception()) return {}; } else { array = Array::create(global_object); } auto& array_object = array.as_object(); for (size_t k = 0; k < vm.argument_count(); ++k) { array_object.define_property(k, vm.argument(k)); if (vm.exception()) return {}; } array_object.put(vm.names.length, Value(vm.argument_count())); if (vm.exception()) return {}; return array; } // 23.1.2.5 get Array [ @@species ], https://tc39.es/ecma262/#sec-get-array-@@species JS_DEFINE_NATIVE_GETTER(ArrayConstructor::symbol_species_getter) { return vm.this_value(global_object); } }