diff options
-rw-r--r-- | Userland/Libraries/LibJS/AST.cpp | 11 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/AST.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Parser.cpp | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Tests/object-expression-__proto__.js | 6 |
4 files changed, 22 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 3120ab1677..3a3ead57b6 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -2994,6 +2994,17 @@ Completion ObjectExpression::execute(Interpreter& interpreter, GlobalObject& glo auto value = TRY(property.value().execute(interpreter, global_object)).release_value(); + // 8. If isProtoSetter is true, then + if (property.type() == ObjectProperty::Type::ProtoSetter) { + // a. If Type(propValue) is either Object or Null, then + if (value.is_object() || value.is_null()) { + // i. Perform ! object.[[SetPrototypeOf]](propValue). + MUST(object->internal_set_prototype_of(value.is_object() ? &value.as_object() : nullptr)); + } + // b. Return unused. + continue; + } + if (value.is_function() && property.is_method()) static_cast<ECMAScriptFunctionObject&>(value.as_function()).set_home_object(object); diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index ec79b117bf..d77a5d7720 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -1648,6 +1648,7 @@ public: Getter, Setter, Spread, + ProtoSetter, }; ObjectProperty(SourceRange source_range, NonnullRefPtr<Expression> key, RefPtr<Expression> value, Type property_type, bool is_method) diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index 946c27a148..3246f1c56b 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -1700,6 +1700,8 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression() property_key = parse_property_key(); } + // 4. Else if propKey is the String value "__proto__" and if IsComputedPropertyKey of PropertyName is false, then + // a. Let isProtoSetter be true. bool is_proto = (type == TokenType::StringLiteral || type == TokenType::Identifier) && is<StringLiteral>(*property_key) && static_cast<StringLiteral const&>(*property_key).value() == "__proto__"; if (property_type == ObjectProperty::Type::Getter || property_type == ObjectProperty::Type::Setter) { @@ -1741,6 +1743,8 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression() syntax_error("Property name '__proto__' must not appear more than once in object literal"); has_direct_proto_property = true; } + if (is_proto && property_type == ObjectProperty::Type::KeyValue) + property_type = ObjectProperty::Type::ProtoSetter; properties.append(create_ast_node<ObjectProperty>({ m_state.current_token.filename(), rule_start.position(), position() }, *property_key, parse_expression(2), property_type, false)); } else if (property_key && property_value) { if (m_state.strict_mode && is<StringLiteral>(*property_key)) { diff --git a/Userland/Libraries/LibJS/Tests/object-expression-__proto__.js b/Userland/Libraries/LibJS/Tests/object-expression-__proto__.js new file mode 100644 index 0000000000..bee5ccf99f --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/object-expression-__proto__.js @@ -0,0 +1,6 @@ +test("__proto__ property", () => { + expect(Object.getPrototypeOf({ __proto__: null })).toBeNull(); + expect(Object.getPrototypeOf({ __proto__: Array.prototype })).toEqual(Array.prototype); + expect(Object.getPrototypeOf({ "__proto__": Array.prototype })).toEqual(Array.prototype); // prettier-ignore + expect(Object.getOwnPropertyNames({ __proto__: Array.prototype, test: 1 })).toEqual(["test"]); +}); |