Browse Source

LibJS: Use ClassFieldInitializerStatement for class fields

This is necessary as we might have to perform named evaluation with the
field name.
Ideally we would also skip some setup parts of the function like
function_declaration_instantiation however this would require bigger
changes to ECMAScriptFunctionObject.
davidot 3 years ago
parent
commit
4c8090a45d

+ 32 - 2
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
 // 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
 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;
     ECMAScriptFunctionObject* initializer = nullptr;
     if (m_initializer) {
     if (m_initializer) {
         auto copy_initializer = 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(
         auto name = property_key.visit(
             [&](PropertyName const& property_name) -> String {
             [&](PropertyName const& property_name) -> String {
                 return property_name.is_number() ? property_name.to_string() : property_name.to_string_or_symbol().to_display_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 {
             [&](PrivateName const& private_name) -> String {
                 return private_name.description;
                 return private_name.description;
             });
             });
+
         // FIXME: A potential optimization is not creating the functions here since these are never directly accessible.
         // 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);
         initializer->set_home_object(&target);
     }
     }
 
 

+ 3 - 1
Userland/Libraries/LibJS/AST.h

@@ -1085,10 +1085,11 @@ private:
 
 
 class ClassField final : public ClassElement {
 class ClassField final : public ClassElement {
 public:
 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)
         : ClassElement(source_range, is_static)
         , m_key(move(key))
         , m_key(move(key))
         , m_initializer(move(init))
         , m_initializer(move(init))
+        , m_contains_direct_call_to_eval(contains_direct_call_to_eval)
     {
     {
     }
     }
 
 
@@ -1105,6 +1106,7 @@ public:
 private:
 private:
     NonnullRefPtr<Expression> m_key;
     NonnullRefPtr<Expression> m_key;
     RefPtr<Expression> m_initializer;
     RefPtr<Expression> m_initializer;
+    bool m_contains_direct_call_to_eval { false };
 };
 };
 
 
 class StaticInitializer final : public ClassElement {
 class StaticInitializer final : public ClassElement {

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

@@ -102,6 +102,11 @@ public:
         return ScopePusher(parser, &node, true);
         return ScopePusher(parser, &node, true);
     }
     }
 
 
+    static ScopePusher class_field_scope(Parser& parser)
+    {
+        return ScopePusher(parser, nullptr, false);
+    }
+
     void add_declaration(NonnullRefPtr<Declaration> declaration)
     void add_declaration(NonnullRefPtr<Declaration> declaration)
     {
     {
         if (declaration->is_lexical_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'");
                 syntax_error("Class cannot have field named 'constructor'");
 
 
             RefPtr<Expression> initializer;
             RefPtr<Expression> initializer;
+            bool contains_direct_call_to_eval = false;
 
 
             if (match(TokenType::Equals)) {
             if (match(TokenType::Equals)) {
                 consume();
                 consume();
 
 
                 TemporaryChange super_property_access_rollback(m_state.allow_super_property_lookup, true);
                 TemporaryChange super_property_access_rollback(m_state.allow_super_property_lookup, true);
                 TemporaryChange field_initializer_rollback(m_state.in_class_field_initializer, 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);
                 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();
             consume_or_insert_semicolon();
         }
         }
     }
     }

+ 3 - 1
Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h

@@ -78,13 +78,15 @@ public:
 protected:
 protected:
     virtual bool is_strict_mode() const final { return m_strict; }
     virtual bool is_strict_mode() const final { return m_strict; }
 
 
+    virtual Completion ordinary_call_evaluate_body();
+
 private:
 private:
     virtual bool is_ecmascript_function_object() const override { return true; }
     virtual bool is_ecmascript_function_object() const override { return true; }
     virtual void visit_edges(Visitor&) override;
     virtual void visit_edges(Visitor&) override;
 
 
     void prepare_for_ordinary_call(ExecutionContext& callee_context, Object* new_target);
     void prepare_for_ordinary_call(ExecutionContext& callee_context, Object* new_target);
     void ordinary_call_bind_this(ExecutionContext&, Value this_argument);
     void ordinary_call_bind_this(ExecutionContext&, Value this_argument);
-    Completion ordinary_call_evaluate_body();
+
     ThrowCompletionOr<void> function_declaration_instantiation(Interpreter*);
     ThrowCompletionOr<void> function_declaration_instantiation(Interpreter*);
 
 
     // Internal Slots of ECMAScript Function Objects, https://tc39.es/ecma262/#table-internal-slots-of-ecmascript-function-objects
     // Internal Slots of ECMAScript Function Objects, https://tc39.es/ecma262/#table-internal-slots-of-ecmascript-function-objects