
Specifically, we now cast to a u32 instead of an i32, as well as use the validity check required by the specification. The current constructor is still quite far from the specification, as we directly set the indexed properties' length instead of going through the Array's overriden DefineOwnProperty. (and as a result the checks imposed by the ArraySetLength abstract operation)
188 lines
6 KiB
C++
188 lines
6 KiB
C++
/*
|
|
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
|
* Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/Function.h>
|
|
#include <LibJS/Heap/Heap.h>
|
|
#include <LibJS/Runtime/Array.h>
|
|
#include <LibJS/Runtime/ArrayConstructor.h>
|
|
#include <LibJS/Runtime/Error.h>
|
|
#include <LibJS/Runtime/GlobalObject.h>
|
|
#include <LibJS/Runtime/IteratorOperations.h>
|
|
#include <LibJS/Runtime/Shape.h>
|
|
|
|
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<RangeError>(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<TypeError>(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);
|
|
}
|
|
|
|
}
|