summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorIdan Horowitz <idan.horowitz@gmail.com>2022-03-05 23:44:49 +0200
committerIdan Horowitz <idan.horowitz@gmail.com>2022-03-06 01:38:25 +0200
commit7ebb421ee9800b5151805cd667c5b0d7a10326c9 (patch)
tree5a25a6cd3a40ed3d533138cf7dd33bcee794e589 /Userland
parent9fa78b1a0555a189b9780091596378d3b488e5ed (diff)
downloadserenity-7ebb421ee9800b5151805cd667c5b0d7a10326c9.zip
LibJS: Implement the object literal __proto__ property key special case
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibJS/AST.cpp11
-rw-r--r--Userland/Libraries/LibJS/AST.h1
-rw-r--r--Userland/Libraries/LibJS/Parser.cpp4
-rw-r--r--Userland/Libraries/LibJS/Tests/object-expression-__proto__.js6
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"]);
+});