diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 96301ddd8c..9cab06633a 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -1913,55 +1913,28 @@ Completion ClassDeclaration::execute(Interpreter& interpreter) const return Optional {}; } -// 15.7.14 Runtime Semantics: ClassDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation -ThrowCompletionOr ClassExpression::class_definition_evaluation(VM& vm, DeprecatedFlyString const& binding_name, DeprecatedFlyString const& class_name) const +ThrowCompletionOr 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* 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. ArmedScopeGuard restore_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 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) { auto opt_private_name = element->private_bound_identifier(); if (opt_private_name.has_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()) { - 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()) { proto_parent = nullptr; } else if (!super_class.is_constructor()) { @@ -1990,12 +1963,23 @@ ThrowCompletionOr 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% - auto class_constructor_value = TRY(vm.execute_ast_node(*m_constructor)); + 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()); - update_function_name(class_constructor_value, class_name); - - VERIFY(class_constructor_value.is_function() && is(class_constructor_value.as_function())); - auto* class_constructor = static_cast(&class_constructor_value.as_function()); + class_constructor->set_name(class_name); class_constructor->set_home_object(prototype); class_constructor->set_is_class_constructor(); class_constructor->define_direct_property(vm.names.prototype, prototype, Attribute::Writable); @@ -2076,12 +2060,43 @@ ThrowCompletionOr ClassExpression::class_definition_e [&](Handle static_block_function) -> ThrowCompletionOr { VERIFY(!static_block_function.is_null()); // 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 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 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 diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index f5110c032d..ef77764ebf 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -1427,6 +1427,7 @@ public: bool has_name() const { return !m_name.is_empty(); } ThrowCompletionOr class_definition_evaluation(VM&, DeprecatedFlyString const& binding_name = {}, DeprecatedFlyString const& class_name = {}) const; + ThrowCompletionOr create_class_constructor(VM&, Environment* class_environment, Environment* environment, Value super_class, DeprecatedFlyString const& binding_name = {}, DeprecatedFlyString const& class_name = {}) const; private: virtual bool is_class_expression() const override { return true; } diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 1693d8b3dc..33875a71a1 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -2273,9 +2273,23 @@ Bytecode::CodeGenerationErrorOr ClassDeclaration::generate_bytecode(Byteco return {}; } +// 15.7.14 Runtime Semantics: ClassDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation Bytecode::CodeGenerationErrorOr ClassExpression::generate_bytecode_with_lhs_name(Bytecode::Generator& generator, Optional 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(); + + 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(interned_index, Bytecode::Op::EnvironmentMode::Lexical, true); + } + + if (m_super_class) + TRY(m_super_class->generate_bytecode(generator)); + generator.emit(*this, lhs_name); + return {}; } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index 4c9c6957de..2ce37b857c 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -1148,17 +1148,23 @@ ThrowCompletionOr NewClass::execute_impl(Bytecode::Interpreter& interprete { auto& vm = interpreter.vm(); 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 {}; }