1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 02:47:35 +00:00

LibJS: Finish implementing mapped arguments exotic objects :^)

Now that the Object rewrite is in place, we have enough tools to
implement the mapped `arguments` propreties according to spec.

The basic mechanism is that the `arguments` object installs a hidden
parameter mapping object that property accesses get filtered through.
This is how accessing numeric properties on `arguments` are proxied
to the named identifier in the function scope.

When `arguments` is instantiated, getters and setters are created
for all the numeric properties on the object that correspond to
function arguments. These getters and setters can be deleted from the
object. This is all pretty intricate, so refer to the spec for details.

Note that the `arguments` object itself is still lazily instantiated
on first access within a function. This is non-conforming, and we'll
have to revisit this once we get around to improving function calls.
This commit is contained in:
Andreas Kling 2021-06-28 21:19:25 +02:00
parent 36cc011ae4
commit 77fa33e61a
4 changed files with 224 additions and 12 deletions

View file

@ -450,32 +450,30 @@ Object* create_unmapped_arguments_object(GlobalObject& global_object, Vector<Val
}
// 10.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env ), https://tc39.es/ecma262/#sec-createmappedargumentsobject
Object* create_mapped_arguments_object(GlobalObject& global_object, FunctionObject& function, Vector<FunctionNode::Parameter> const& formals, Vector<Value> const& arguments, Environment&)
Object* create_mapped_arguments_object(GlobalObject& global_object, FunctionObject& function, Vector<FunctionNode::Parameter> const& formals, Vector<Value> const& arguments, Environment& environment)
{
// FIXME: This implementation is incomplete and doesn't support the actual identifier mappings yet.
(void)formals;
auto& vm = global_object.vm();
// 1. Assert: formals does not contain a rest parameter, any binding patterns, or any initializers. It may contain duplicate identifiers.
// 2. Let len be the number of elements in argumentsList.
auto length = arguments.size();
VERIFY(arguments.size() <= NumericLimits<i32>::max());
i32 length = static_cast<i32>(arguments.size());
// 3. Let obj be ! MakeBasicObject(« [[Prototype]], [[Extensible]], [[ParameterMap]] »).
auto* object = vm.heap().allocate<ArgumentsObject>(global_object, global_object);
VERIFY(!vm.exception());
// 4. Set obj.[[GetOwnProperty]] as specified in 10.4.4.1.
// 5. Set obj.[[DefineOwnProperty]] as specified in 10.4.4.2.
// 6. Set obj.[[Get]] as specified in 10.4.4.3.
// 7. Set obj.[[Set]] as specified in 10.4.4.4.
// 8. Set obj.[[Delete]] as specified in 10.4.4.5.
// 9. Set obj.[[Prototype]] to %Object.prototype%.
auto* object = vm.heap().allocate<ArgumentsObject>(global_object, global_object, environment);
if (vm.exception())
return nullptr;
// 14. Let index be 0.
// 15. Repeat, while index < len,
for (size_t index = 0; index < length; ++index) {
for (i32 index = 0; index < length; ++index) {
// a. Let val be argumentsList[index].
auto value = arguments[index];
@ -490,6 +488,45 @@ Object* create_mapped_arguments_object(GlobalObject& global_object, FunctionObje
object->define_property_or_throw(vm.names.length, { .value = Value(length), .writable = true, .enumerable = false, .configurable = true });
VERIFY(!vm.exception());
// 17. Let mappedNames be a new empty List.
HashTable<FlyString> mapped_names;
// 18. Set index to numberOfParameters - 1.
// 19. Repeat, while index ≥ 0,
VERIFY(formals.size() <= NumericLimits<i32>::max());
for (i32 index = static_cast<i32>(formals.size()) - 1; index >= 0; --index) {
// a. Let name be parameterNames[index].
auto const& name = formals[index].binding.get<FlyString>();
// b. If name is not an element of mappedNames, then
if (mapped_names.contains(name))
continue;
// i. Add name as an element of the list mappedNames.
mapped_names.set(name);
// ii. If index < len, then
if (index < length) {
// 1. Let g be MakeArgGetter(name, env).
// 2. Let p be MakeArgSetter(name, env).
// 3. Perform map.[[DefineOwnProperty]](! ToString(𝔽(index)), PropertyDescriptor { [[Set]]: p, [[Get]]: g, [[Enumerable]]: false, [[Configurable]]: true }).
object->parameter_map().define_native_accessor(
String::number(index),
[&environment, name](VM&, GlobalObject&) -> Value {
auto variable = environment.get_from_environment(name);
if (!variable.has_value())
return {};
return variable->value;
},
[&environment, name](VM& vm, GlobalObject&) {
auto value = vm.argument(0);
environment.put_into_environment(name, Variable { value, DeclarationKind::Var });
return js_undefined();
},
Attribute::Configurable);
}
}
// 20. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor { [[Value]]: %Array.prototype.values%, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).
// FIXME: This is not guaranteed to be %Array.prototype.values%!
auto array_prototype_values = global_object.array_prototype()->get(vm.names.values);