Quellcode durchsuchen

LibJS: Set name of anonymous functions during assignment

Linus Groh vor 5 Jahren
Ursprung
Commit
25cf0da2fb

+ 22 - 1
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<ScriptFunction&>(object);
+        if (function.name().is_empty())
+            function.set_name(name);
+    } else if (object.is_array()) {
+        auto& array = static_cast<Array&>(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<ReferenceError>("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;

+ 1 - 0
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; }

+ 33 - 5
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");