Browse Source

LibJS: Don't update names of resulting functions in object expression

The only cases where the name should be set is if the function comes
from a direct anonymous function expression.
davidot 2 years ago
parent
commit
2bbea62176

+ 12 - 9
Userland/Libraries/LibJS/AST.cpp

@@ -3095,18 +3095,21 @@ Completion ObjectExpression::execute(Interpreter& interpreter) const
             continue;
         }
 
-        if (value.is_function() && property.is_method())
+        auto property_key = TRY(PropertyKey::from_value(vm, key));
+
+        if (property.is_method()) {
+            VERIFY(value.is_function());
             static_cast<ECMAScriptFunctionObject&>(value.as_function()).set_home_object(object);
 
-        auto property_key = TRY(PropertyKey::from_value(vm, key));
-        auto name = TRY(get_function_property_name(property_key));
-        if (property.type() == ObjectProperty::Type::Getter) {
-            name = DeprecatedString::formatted("get {}", name);
-        } else if (property.type() == ObjectProperty::Type::Setter) {
-            name = DeprecatedString::formatted("set {}", name);
-        }
+            auto name = MUST(get_function_property_name(property_key));
+            if (property.type() == ObjectProperty::Type::Getter) {
+                name = DeprecatedString::formatted("get {}", name);
+            } else if (property.type() == ObjectProperty::Type::Setter) {
+                name = DeprecatedString::formatted("set {}", name);
+            }
 
-        update_function_name(value, name);
+            update_function_name(value, name);
+        }
 
         switch (property.type()) {
         case ObjectProperty::Type::Getter:

+ 4 - 1
Userland/Libraries/LibJS/Parser.cpp

@@ -1779,7 +1779,10 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
             }
             if (is_proto && property_type == ObjectProperty::Type::KeyValue)
                 property_type = ObjectProperty::Type::ProtoSetter;
-            properties.append(create_ast_node<ObjectProperty>({ m_source_code, rule_start.position(), position() }, *property_key, parse_expression(2), property_type, false));
+
+            auto rhs_expression = parse_expression(2);
+            bool is_method = is<FunctionExpression>(*rhs_expression);
+            properties.append(create_ast_node<ObjectProperty>({ m_source_code, rule_start.position(), position() }, *property_key, move(rhs_expression), property_type, is_method));
         } else if (property_key && property_value) {
             if (m_state.strict_mode && is<StringLiteral>(*property_key)) {
                 auto& string_literal = static_cast<StringLiteral const&>(*property_key);

+ 8 - 0
Userland/Libraries/LibJS/Tests/builtins/Object/Object.freeze.js

@@ -67,3 +67,11 @@ describe("normal behavior", () => {
         expect(o.foo).toBe("bar");
     });
 });
+
+test("does not override frozen function name", () => {
+    const func = Object.freeze(function () {
+        return 12;
+    });
+    const obj = Object.freeze({ name: func });
+    expect(obj.name()).toBe(12);
+});

+ 39 - 0
Userland/Libraries/LibJS/Tests/object-basic.js

@@ -215,3 +215,42 @@ describe("errors", () => {
         expect("({ ...foo: bar })").not.toEval();
     });
 });
+
+describe("naming of anon functions", () => {
+    test("method has name", () => {
+        expect({ func() {} }.func.name).toBe("func");
+    });
+
+    test("getter has name", () => {
+        expect(Object.getOwnPropertyDescriptor({ get func() {} }, "func").get.name).toBe(
+            "get func"
+        );
+    });
+
+    test("setter has name", () => {
+        expect(Object.getOwnPropertyDescriptor({ set func(v) {} }, "func").set.name).toBe(
+            "set func"
+        );
+    });
+
+    test("anon function property", () => {
+        expect({ func: function () {} }.func.name).toBe("func");
+    });
+
+    test("anon function from within parenthesis", () => {
+        expect({ func: function () {} }.func.name).toBe("func");
+    });
+
+    test("anon function from indirect expression", () => {
+        expect({ func: (0, function () {}) }.func.name).toBe("");
+    });
+
+    test("function from function call does not get named", () => {
+        function f() {
+            return function () {};
+        }
+
+        expect(f().name).toBe("");
+        expect({ func: f() }.func.name).toBe("");
+    });
+});