LibJS: Implement a very hackish "arguments" object
We now lazily create an "arguments" array inside functions when code tries to access it. This doesn't follow the spec at all but still covers a lot of the basic uses of arguments, i.e "arguments.length" and "arguments[n]"
This commit is contained in:
parent
e6dadd9e5b
commit
cc14b5a6d7
Notes:
sideshowbarker
2024-07-19 01:03:05 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/cc14b5a6d7b
4 changed files with 39 additions and 0 deletions
|
@ -62,6 +62,7 @@ namespace JS {
|
|||
P(abs) \
|
||||
P(acosh) \
|
||||
P(apply) \
|
||||
P(arguments) \
|
||||
P(asIntN) \
|
||||
P(asUintN) \
|
||||
P(asinh) \
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <AK/ScopeGuard.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibJS/Interpreter.h>
|
||||
#include <LibJS/Runtime/Array.h>
|
||||
#include <LibJS/Runtime/Error.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Reference.h>
|
||||
|
@ -116,6 +117,8 @@ void VM::gather_roots(HashTable<Cell*>& roots)
|
|||
for (auto& call_frame : m_call_stack) {
|
||||
if (call_frame->this_value.is_cell())
|
||||
roots.set(call_frame->this_value.as_cell());
|
||||
if (call_frame->arguments_object)
|
||||
roots.set(call_frame->arguments_object);
|
||||
for (auto& argument : call_frame->arguments) {
|
||||
if (argument.is_cell())
|
||||
roots.set(argument.as_cell());
|
||||
|
@ -166,6 +169,24 @@ void VM::set_variable(const FlyString& name, Value value, GlobalObject& global_o
|
|||
Value VM::get_variable(const FlyString& name, GlobalObject& global_object)
|
||||
{
|
||||
if (m_call_stack.size()) {
|
||||
if (name == names.arguments) {
|
||||
// HACK: Special handling for the name "arguments":
|
||||
// If the name "arguments" is defined in the current scope, for example via
|
||||
// a function parameter, or by a local var declaration, we use that.
|
||||
// Otherwise, we return a lazily constructed Array with all the argument values.
|
||||
// FIXME: Do something much more spec-compliant.
|
||||
auto possible_match = current_scope()->get_from_scope(name);
|
||||
if (possible_match.has_value())
|
||||
return possible_match.value().value;
|
||||
if (!call_frame().arguments_object) {
|
||||
call_frame().arguments_object = Array::create(global_object);
|
||||
for (auto argument : call_frame().arguments) {
|
||||
call_frame().arguments_object->indexed_properties().append(argument);
|
||||
}
|
||||
}
|
||||
return call_frame().arguments_object;
|
||||
}
|
||||
|
||||
for (auto* scope = current_scope(); scope; scope = scope->parent()) {
|
||||
auto possible_match = scope->get_from_scope(name);
|
||||
if (possible_match.has_value())
|
||||
|
|
|
@ -59,6 +59,7 @@ struct CallFrame {
|
|||
FlyString function_name;
|
||||
Value this_value;
|
||||
Vector<Value> arguments;
|
||||
Array* arguments_object { nullptr };
|
||||
ScopeObject* scope { nullptr };
|
||||
bool is_strict_mode { false };
|
||||
};
|
||||
|
|
16
Libraries/LibJS/Tests/arguments-object.js
Normal file
16
Libraries/LibJS/Tests/arguments-object.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
test("basic arguments object", () => {
|
||||
function foo() {
|
||||
return arguments.length;
|
||||
}
|
||||
expect(foo()).toBe(0);
|
||||
expect(foo(1)).toBe(1);
|
||||
expect(foo(1, 2)).toBe(2);
|
||||
expect(foo(1, 2, 3)).toBe(3);
|
||||
|
||||
function bar() {
|
||||
return arguments[1];
|
||||
}
|
||||
expect(bar("hello", "friends", ":^)")).toBe("friends");
|
||||
expect(bar("hello")).toBe(undefined);
|
||||
});
|
||||
|
Loading…
Add table
Reference in a new issue