diff options
-rw-r--r-- | Userland/Libraries/LibJS/AST.cpp | 34 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/AST.h | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Parser.cpp | 11 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h | 4 |
4 files changed, 48 insertions, 5 deletions
diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 471eb4cee0..009065051c 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -1278,6 +1278,35 @@ ThrowCompletionOr<ClassElement::ClassValue> ClassMethod::class_element_evaluatio } } +// We use this class to mimic Initializer : = AssignmentExpression of +// 10.2.1.3 Runtime Semantics: EvaluateBody, https://tc39.es/ecma262/#sec-runtime-semantics-evaluatebody +class ClassFieldInitializerStatement : public Statement { +public: + ClassFieldInitializerStatement(SourceRange source_range, NonnullRefPtr<Expression> expression, FlyString field_name) + : Statement(source_range) + , m_expression(move(expression)) + , m_class_field_identifier_name(move(field_name)) + { + } + + Value execute(Interpreter& interpreter, GlobalObject& global_object) const override + { + VERIFY(interpreter.vm().argument_count() == 0); + VERIFY(!m_class_field_identifier_name.is_empty()); + return TRY_OR_DISCARD(interpreter.vm().named_evaluation_if_anonymous_function(global_object, m_expression, m_class_field_identifier_name)); + } + + void dump(int) const override + { + // This should not be dumped as it is never part of an actual AST. + VERIFY_NOT_REACHED(); + } + +private: + NonnullRefPtr<Expression> m_expression; + FlyString m_class_field_identifier_name; // [[ClassFieldIdentifierName]] +}; + // 15.7.10 Runtime Semantics: ClassFieldDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classfielddefinitionevaluation ThrowCompletionOr<ClassElement::ClassValue> ClassField::class_element_evaluation(Interpreter& interpreter, GlobalObject& global_object, Object& target) const { @@ -1285,7 +1314,6 @@ ThrowCompletionOr<ClassElement::ClassValue> ClassField::class_element_evaluation ECMAScriptFunctionObject* initializer = nullptr; if (m_initializer) { auto copy_initializer = m_initializer; - auto body = create_ast_node<ExpressionStatement>(m_initializer->source_range(), copy_initializer.release_nonnull()); auto name = property_key.visit( [&](PropertyName const& property_name) -> String { return property_name.is_number() ? property_name.to_string() : property_name.to_string_or_symbol().to_display_string(); @@ -1293,8 +1321,10 @@ ThrowCompletionOr<ClassElement::ClassValue> ClassField::class_element_evaluation [&](PrivateName const& private_name) -> String { return private_name.description; }); + // FIXME: A potential optimization is not creating the functions here since these are never directly accessible. - initializer = ECMAScriptFunctionObject::create(interpreter.global_object(), name, *body, {}, 0, interpreter.lexical_environment(), interpreter.vm().running_execution_context().private_environment, FunctionKind::Regular, false, false); + auto function_code = create_ast_node<ClassFieldInitializerStatement>(m_initializer->source_range(), copy_initializer.release_nonnull(), name); + initializer = ECMAScriptFunctionObject::create(interpreter.global_object(), String::empty(), *function_code, {}, 0, interpreter.lexical_environment(), interpreter.vm().running_execution_context().private_environment, FunctionKind::Regular, true, false, m_contains_direct_call_to_eval, false); initializer->set_home_object(&target); } diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 64f4773f32..88ef640da4 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -1085,10 +1085,11 @@ private: class ClassField final : public ClassElement { public: - ClassField(SourceRange source_range, NonnullRefPtr<Expression> key, RefPtr<Expression> init, bool is_static) + ClassField(SourceRange source_range, NonnullRefPtr<Expression> key, RefPtr<Expression> init, bool contains_direct_call_to_eval, bool is_static) : ClassElement(source_range, is_static) , m_key(move(key)) , m_initializer(move(init)) + , m_contains_direct_call_to_eval(contains_direct_call_to_eval) { } @@ -1105,6 +1106,7 @@ public: private: NonnullRefPtr<Expression> m_key; RefPtr<Expression> m_initializer; + bool m_contains_direct_call_to_eval { false }; }; class StaticInitializer final : public ClassElement { diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index 0bd4ca0698..787fc3ff90 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -102,6 +102,11 @@ public: return ScopePusher(parser, &node, true); } + static ScopePusher class_field_scope(Parser& parser) + { + return ScopePusher(parser, nullptr, false); + } + void add_declaration(NonnullRefPtr<Declaration> declaration) { if (declaration->is_lexical_declaration()) { @@ -1071,16 +1076,20 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_ syntax_error("Class cannot have field named 'constructor'"); RefPtr<Expression> initializer; + bool contains_direct_call_to_eval = false; if (match(TokenType::Equals)) { consume(); TemporaryChange super_property_access_rollback(m_state.allow_super_property_lookup, true); TemporaryChange field_initializer_rollback(m_state.in_class_field_initializer, true); + + auto class_field_scope = ScopePusher::class_field_scope(*this); initializer = parse_expression(2); + contains_direct_call_to_eval = class_field_scope.contains_direct_call_to_eval(); } - elements.append(create_ast_node<ClassField>({ m_state.current_token.filename(), rule_start.position(), position() }, property_key.release_nonnull(), move(initializer), is_static)); + elements.append(create_ast_node<ClassField>({ m_state.current_token.filename(), rule_start.position(), position() }, property_key.release_nonnull(), move(initializer), contains_direct_call_to_eval, is_static)); consume_or_insert_semicolon(); } } diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h index 18dccadda1..ac313c205e 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h @@ -78,13 +78,15 @@ public: protected: virtual bool is_strict_mode() const final { return m_strict; } + virtual Completion ordinary_call_evaluate_body(); + private: virtual bool is_ecmascript_function_object() const override { return true; } virtual void visit_edges(Visitor&) override; void prepare_for_ordinary_call(ExecutionContext& callee_context, Object* new_target); void ordinary_call_bind_this(ExecutionContext&, Value this_argument); - Completion ordinary_call_evaluate_body(); + ThrowCompletionOr<void> function_declaration_instantiation(Interpreter*); // Internal Slots of ECMAScript Function Objects, https://tc39.es/ecma262/#table-internal-slots-of-ecmascript-function-objects |