浏览代码

LibJS: Stop using execute_ast_node in NewClass instruction

This change replaces usage of `execute_ast_node` to evaluate super
expression in NewClass by generating instructions instead.
Aliaksandr Kalenik 2 年之前
父节点
当前提交
1550e7c421

+ 54 - 39
Userland/Libraries/LibJS/AST.cpp

@@ -1913,55 +1913,28 @@ Completion ClassDeclaration::execute(Interpreter& interpreter) const
     return Optional<Value> {};
     return Optional<Value> {};
 }
 }
 
 
-// 15.7.14 Runtime Semantics: ClassDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
-ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_evaluation(VM& vm, DeprecatedFlyString const& binding_name, DeprecatedFlyString const& class_name) const
+ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::create_class_constructor(VM& vm, Environment* class_environment, Environment* environment, Value super_class, DeprecatedFlyString const& binding_name, DeprecatedFlyString const& class_name) const
 {
 {
     auto& realm = *vm.current_realm();
     auto& realm = *vm.current_realm();
 
 
-    auto* environment = vm.lexical_environment();
-    VERIFY(environment);
-    auto class_environment = new_declarative_environment(*environment);
-
     // We might not set the lexical environment but we always want to restore it eventually.
     // We might not set the lexical environment but we always want to restore it eventually.
     ArmedScopeGuard restore_environment = [&] {
     ArmedScopeGuard restore_environment = [&] {
         vm.running_execution_context().lexical_environment = environment;
         vm.running_execution_context().lexical_environment = environment;
     };
     };
 
 
-    if (!binding_name.is_null())
-        MUST(class_environment->create_immutable_binding(vm, binding_name, true));
-
     auto outer_private_environment = vm.running_execution_context().private_environment;
     auto outer_private_environment = vm.running_execution_context().private_environment;
     auto class_private_environment = new_private_environment(vm, outer_private_environment);
     auto class_private_environment = new_private_environment(vm, outer_private_environment);
 
 
+    auto proto_parent = GCPtr { realm.intrinsics().object_prototype() };
+    auto constructor_parent = realm.intrinsics().function_prototype();
+
     for (auto const& element : m_elements) {
     for (auto const& element : m_elements) {
         auto opt_private_name = element->private_bound_identifier();
         auto opt_private_name = element->private_bound_identifier();
         if (opt_private_name.has_value())
         if (opt_private_name.has_value())
             class_private_environment->add_private_name({}, opt_private_name.release_value());
             class_private_environment->add_private_name({}, opt_private_name.release_value());
     }
     }
 
 
-    auto proto_parent = GCPtr { realm.intrinsics().object_prototype() };
-    auto constructor_parent = realm.intrinsics().function_prototype();
-
     if (!m_super_class.is_null()) {
     if (!m_super_class.is_null()) {
-        vm.running_execution_context().lexical_environment = class_environment;
-
-        // Note: Since our execute does evaluation and GetValue in once we must check for a valid reference first
-
-        Value super_class;
-
-        if (vm.bytecode_interpreter_if_exists()) {
-            super_class = TRY(vm.execute_ast_node(*m_super_class));
-        } else {
-            auto reference = TRY(m_super_class->to_reference(vm.interpreter()));
-            if (reference.is_valid_reference()) {
-                super_class = TRY(reference.get_value(vm));
-            } else {
-                super_class = TRY(vm.execute_ast_node(*m_super_class));
-            }
-        }
-
-        vm.running_execution_context().lexical_environment = environment;
-
         if (super_class.is_null()) {
         if (super_class.is_null()) {
             proto_parent = nullptr;
             proto_parent = nullptr;
         } else if (!super_class.is_constructor()) {
         } else if (!super_class.is_constructor()) {
@@ -1990,12 +1963,23 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_e
     };
     };
 
 
     // FIXME: Step 14.a is done in the parser. By using a synthetic super(...args) which does not call @@iterator of %Array.prototype%
     // FIXME: Step 14.a is done in the parser. By using a synthetic super(...args) which does not call @@iterator of %Array.prototype%
-    auto class_constructor_value = TRY(vm.execute_ast_node(*m_constructor));
-
-    update_function_name(class_constructor_value, class_name);
-
-    VERIFY(class_constructor_value.is_function() && is<ECMAScriptFunctionObject>(class_constructor_value.as_function()));
-    auto* class_constructor = static_cast<ECMAScriptFunctionObject*>(&class_constructor_value.as_function());
+    auto const& constructor = *m_constructor;
+    auto class_constructor = ECMAScriptFunctionObject::create(
+        realm,
+        constructor.name(),
+        constructor.source_text(),
+        constructor.body(),
+        constructor.parameters(),
+        constructor.function_length(),
+        vm.lexical_environment(),
+        vm.running_execution_context().private_environment,
+        constructor.kind(),
+        constructor.is_strict_mode(),
+        constructor.might_need_arguments_object(),
+        constructor.contains_direct_call_to_eval(),
+        constructor.is_arrow_function());
+
+    class_constructor->set_name(class_name);
     class_constructor->set_home_object(prototype);
     class_constructor->set_home_object(prototype);
     class_constructor->set_is_class_constructor();
     class_constructor->set_is_class_constructor();
     class_constructor->define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
     class_constructor->define_direct_property(vm.names.prototype, prototype, Attribute::Writable);
@@ -2076,12 +2060,43 @@ ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_e
             [&](Handle<ECMAScriptFunctionObject> static_block_function) -> ThrowCompletionOr<void> {
             [&](Handle<ECMAScriptFunctionObject> static_block_function) -> ThrowCompletionOr<void> {
                 VERIFY(!static_block_function.is_null());
                 VERIFY(!static_block_function.is_null());
                 // We discard any value returned here.
                 // We discard any value returned here.
-                TRY(call(vm, *static_block_function.cell(), class_constructor_value));
+                TRY(call(vm, *static_block_function.cell(), class_constructor));
                 return {};
                 return {};
             }));
             }));
     }
     }
 
 
-    return class_constructor;
+    class_constructor->set_source_text(source_text());
+
+    return { class_constructor };
+}
+
+// 15.7.14 Runtime Semantics: ClassDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
+ThrowCompletionOr<ECMAScriptFunctionObject*> ClassExpression::class_definition_evaluation(VM& vm, DeprecatedFlyString const& binding_name, DeprecatedFlyString const& class_name) const
+{
+    auto* environment = vm.lexical_environment();
+    VERIFY(environment);
+    auto class_environment = new_declarative_environment(*environment);
+
+    Value super_class;
+
+    if (!binding_name.is_null())
+        MUST(class_environment->create_immutable_binding(vm, binding_name, true));
+
+    if (!m_super_class.is_null()) {
+        vm.running_execution_context().lexical_environment = class_environment;
+
+        // Note: Since our execute does evaluation and GetValue in once we must check for a valid reference first
+        auto reference = TRY(m_super_class->to_reference(vm.interpreter()));
+        if (reference.is_valid_reference()) {
+            super_class = TRY(reference.get_value(vm));
+        } else {
+            super_class = TRY(vm.execute_ast_node(*m_super_class));
+        }
+
+        vm.running_execution_context().lexical_environment = environment;
+    }
+
+    return create_class_constructor(vm, class_environment, environment, super_class, binding_name, class_name);
 }
 }
 
 
 void ASTNode::dump(int indent) const
 void ASTNode::dump(int indent) const

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

@@ -1427,6 +1427,7 @@ public:
     bool has_name() const { return !m_name.is_empty(); }
     bool has_name() const { return !m_name.is_empty(); }
 
 
     ThrowCompletionOr<ECMAScriptFunctionObject*> class_definition_evaluation(VM&, DeprecatedFlyString const& binding_name = {}, DeprecatedFlyString const& class_name = {}) const;
     ThrowCompletionOr<ECMAScriptFunctionObject*> class_definition_evaluation(VM&, DeprecatedFlyString const& binding_name = {}, DeprecatedFlyString const& class_name = {}) const;
+    ThrowCompletionOr<ECMAScriptFunctionObject*> create_class_constructor(VM&, Environment* class_environment, Environment* environment, Value super_class, DeprecatedFlyString const& binding_name = {}, DeprecatedFlyString const& class_name = {}) const;
 
 
 private:
 private:
     virtual bool is_class_expression() const override { return true; }
     virtual bool is_class_expression() const override { return true; }

+ 14 - 0
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -2273,9 +2273,23 @@ Bytecode::CodeGenerationErrorOr<void> ClassDeclaration::generate_bytecode(Byteco
     return {};
     return {};
 }
 }
 
 
+// 15.7.14 Runtime Semantics: ClassDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
 Bytecode::CodeGenerationErrorOr<void> ClassExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional<Bytecode::IdentifierTableIndex> lhs_name) const
 Bytecode::CodeGenerationErrorOr<void> ClassExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional<Bytecode::IdentifierTableIndex> lhs_name) const
 {
 {
+    // NOTE: Step 2 is not a part of NewClass instruction because it is assumed to be done before super class expression evaluation
+    generator.emit<Bytecode::Op::CreateLexicalEnvironment>();
+
+    if (has_name() || !lhs_name.has_value()) {
+        // NOTE: Step 3.a is not a part of NewClass instruction because it is assumed to be done before super class expression evaluation
+        auto interned_index = generator.intern_identifier(m_name);
+        generator.emit<Bytecode::Op::CreateVariable>(interned_index, Bytecode::Op::EnvironmentMode::Lexical, true);
+    }
+
+    if (m_super_class)
+        TRY(m_super_class->generate_bytecode(generator));
+
     generator.emit<Bytecode::Op::NewClass>(*this, lhs_name);
     generator.emit<Bytecode::Op::NewClass>(*this, lhs_name);
+
     return {};
     return {};
 }
 }
 
 

+ 13 - 7
Userland/Libraries/LibJS/Bytecode/Op.cpp

@@ -1148,17 +1148,23 @@ ThrowCompletionOr<void> NewClass::execute_impl(Bytecode::Interpreter& interprete
 {
 {
     auto& vm = interpreter.vm();
     auto& vm = interpreter.vm();
     auto name = m_class_expression.name();
     auto name = m_class_expression.name();
+    auto super_class = interpreter.accumulator();
 
 
-    ECMAScriptFunctionObject* class_object = nullptr;
+    // NOTE: NewClass expects classEnv to be active lexical environment
+    auto class_environment = vm.lexical_environment();
+    vm.running_execution_context().lexical_environment = interpreter.saved_lexical_environment_stack().take_last();
 
 
-    if (!m_class_expression.has_name() && m_lhs_name.has_value())
-        class_object = TRY(m_class_expression.class_definition_evaluation(vm, {}, interpreter.current_executable().get_identifier(m_lhs_name.value())));
-    else
-        class_object = TRY(m_class_expression.class_definition_evaluation(vm, name, name.is_null() ? ""sv : name));
+    DeprecatedFlyString binding_name;
+    DeprecatedFlyString class_name;
+    if (!m_class_expression.has_name() && m_lhs_name.has_value()) {
+        class_name = interpreter.current_executable().get_identifier(m_lhs_name.value());
+    } else {
+        binding_name = name;
+        class_name = name.is_null() ? ""sv : name;
+    }
 
 
-    class_object->set_source_text(m_class_expression.source_text());
+    interpreter.accumulator() = TRY(m_class_expression.create_class_constructor(vm, class_environment, vm.lexical_environment(), super_class, binding_name, class_name));
 
 
-    interpreter.accumulator() = class_object;
     return {};
     return {};
 }
 }