diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 6afdc51961..28d56b5698 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -46,6 +46,23 @@ namespace JS { +static void update_function_name(Value& value, const FlyString& name) +{ + if (!value.is_object()) + return; + auto& object = value.as_object(); + if (object.is_function()) { + auto& function = static_cast(object); + if (function.name().is_empty()) + function.set_name(name); + } else if (object.is_array()) { + auto& array = static_cast(object); + for (size_t i = 0; i < array.elements().size(); ++i) { + update_function_name(array.elements()[i], name); + } + } +} + Value ScopeNode::execute(Interpreter& interpreter) const { return interpreter.run(*this); @@ -861,6 +878,7 @@ Value AssignmentExpression::execute(Interpreter& interpreter) const if (reference.is_unresolvable()) return interpreter.throw_exception("Invalid left-hand side in assignment"); + update_function_name(rhs_result, reference.name().as_string()); reference.put(interpreter, rhs_result); if (interpreter.exception()) @@ -965,7 +983,9 @@ Value VariableDeclaration::execute(Interpreter& interpreter) const auto initalizer_result = init->execute(interpreter); if (interpreter.exception()) return {}; - interpreter.set_variable(declarator.id().string(), initalizer_result, true); + auto variable_name = declarator.id().string(); + update_function_name(initalizer_result, variable_name); + interpreter.set_variable(variable_name, initalizer_result, true); } } return js_undefined(); @@ -1075,6 +1095,7 @@ Value ObjectExpression::execute(Interpreter& interpreter) const auto value = property.value().execute(interpreter); if (interpreter.exception()) return {}; + update_function_name(value, key); object->put(key, value); } return object; diff --git a/Libraries/LibJS/Runtime/ScriptFunction.h b/Libraries/LibJS/Runtime/ScriptFunction.h index 24338972d5..407ad3a845 100644 --- a/Libraries/LibJS/Runtime/ScriptFunction.h +++ b/Libraries/LibJS/Runtime/ScriptFunction.h @@ -45,6 +45,7 @@ public: virtual Value construct(Interpreter&) override; virtual const FlyString& name() const override { return m_name; }; + void set_name(const FlyString& name) { m_name = name; }; private: virtual bool is_script_function() const final { return true; } diff --git a/Libraries/LibJS/Tests/function-name.js b/Libraries/LibJS/Tests/function-name.js index 163dbab8b1..9a934c01fb 100644 --- a/Libraries/LibJS/Tests/function-name.js +++ b/Libraries/LibJS/Tests/function-name.js @@ -1,16 +1,44 @@ load("test-common.js"); try { - var f = function () { } - assert(f.name === ""); - assert((f.name = "f") === "f"); - assert(f.name === ""); + assert((function () { }).name === ""); - function foo() { } + var foo = function () { } assert(foo.name === "foo"); assert((foo.name = "bar") === "bar"); assert(foo.name === "foo"); + var a, b; + a = b = function () { } + assert(a.name === "b"); + assert(b.name === "b"); + + var arr = [ + function () { }, + function () { }, + function () { } + ]; + assert(arr[0].name === "arr"); + assert(arr[1].name === "arr"); + assert(arr[2].name === "arr"); + + var f; + var o = { a: function () { } }; + assert(o.a.name === "a"); + f = o.a; + assert(f.name === "a"); + assert(o.a.name === "a"); + o = { ...o, b: f }; + assert(o.a.name === "a"); + assert(o.b.name === "a"); + o.c = function () { }; + assert(o.c.name === "c"); + + function bar() { } + assert(bar.name === "bar"); + assert((bar.name = "baz") === "baz"); + assert(bar.name === "bar"); + assert(console.log.name === "log"); assert((console.log.name = "warn") === "warn"); assert(console.log.name === "log");