diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 23c0f699128..deadf11ddea 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -128,7 +128,7 @@ Optional CallExpression::expression_string() const return {}; } -static ThrowCompletionOr class_key_to_property_name(VM& vm, Expression const& key) +static ThrowCompletionOr class_key_to_property_name(VM& vm, Expression const& key, Value prop_key) { if (is(key)) { auto& private_identifier = static_cast(key); @@ -137,7 +137,7 @@ static ThrowCompletionOr class_key_to_property_name(VM& vm, Ex return ClassElementName { private_environment->resolve_private_identifier(private_identifier.string()) }; } - auto prop_key = TRY(vm.execute_ast_node(key)); + VERIFY(!prop_key.is_empty()); if (prop_key.is_object()) prop_key = TRY(prop_key.to_primitive(vm, Value::PreferredType::String)); @@ -147,9 +147,9 @@ static ThrowCompletionOr class_key_to_property_name(VM& vm, Ex } // 15.4.5 Runtime Semantics: MethodDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-methoddefinitionevaluation -ThrowCompletionOr ClassMethod::class_element_evaluation(VM& vm, Object& target) const +ThrowCompletionOr ClassMethod::class_element_evaluation(VM& vm, Object& target, Value property_key) const { - auto property_key_or_private_name = TRY(class_key_to_property_name(vm, *m_key)); + auto property_key_or_private_name = TRY(class_key_to_property_name(vm, *m_key, property_key)); auto& method_function = *ECMAScriptFunctionObject::create(*vm.current_realm(), m_function->name(), m_function->source_text(), m_function->body(), m_function->parameters(), m_function->function_length(), m_function->local_variables_names(), vm.lexical_environment(), vm.running_execution_context().private_environment, m_function->kind(), m_function->is_strict_mode(), m_function->uses_this(), m_function->might_need_arguments_object(), m_function->contains_direct_call_to_eval(), m_function->is_arrow_function()); @@ -220,11 +220,11 @@ void ClassFieldInitializerStatement::dump(int) const } // 15.7.10 Runtime Semantics: ClassFieldDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classfielddefinitionevaluation -ThrowCompletionOr ClassField::class_element_evaluation(VM& vm, Object& target) const +ThrowCompletionOr ClassField::class_element_evaluation(VM& vm, Object& target, Value property_key) const { auto& realm = *vm.current_realm(); - auto property_key_or_private_name = TRY(class_key_to_property_name(vm, *m_key)); + auto property_key_or_private_name = TRY(class_key_to_property_name(vm, *m_key, property_key)); Handle initializer {}; if (m_initializer) { auto copy_initializer = m_initializer; @@ -268,7 +268,7 @@ Optional ClassMethod::private_bound_identifier() const } // 15.7.11 Runtime Semantics: ClassStaticBlockDefinitionEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-classstaticblockdefinitionevaluation -ThrowCompletionOr StaticInitializer::class_element_evaluation(VM& vm, Object& home_object) const +ThrowCompletionOr StaticInitializer::class_element_evaluation(VM& vm, Object& home_object, Value) const { auto& realm = *vm.current_realm(); @@ -291,7 +291,7 @@ ThrowCompletionOr StaticInitializer::class_element_eva return ClassValue { normal_completion(body_function) }; } -ThrowCompletionOr ClassExpression::create_class_constructor(VM& vm, Environment* class_environment, Environment* environment, Value super_class, Optional const& binding_name, DeprecatedFlyString const& class_name) const +ThrowCompletionOr ClassExpression::create_class_constructor(VM& vm, Environment* class_environment, Environment* environment, Value super_class, ReadonlySpan element_keys, Optional const& binding_name, DeprecatedFlyString const& class_name) const { auto& realm = *vm.current_realm(); @@ -300,18 +300,11 @@ ThrowCompletionOr ClassExpression::create_class_const vm.running_execution_context().lexical_environment = environment; }; - auto outer_private_environment = vm.running_execution_context().private_environment; - auto class_private_environment = new_private_environment(vm, outer_private_environment); + vm.running_execution_context().lexical_environment = class_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()); - } - if (!m_super_class.is_null()) { if (super_class.is_null()) { proto_parent = nullptr; @@ -334,12 +327,6 @@ ThrowCompletionOr ClassExpression::create_class_const auto prototype = Object::create_prototype(realm, proto_parent); VERIFY(prototype); - vm.running_execution_context().lexical_environment = class_environment; - vm.running_execution_context().private_environment = class_private_environment; - ScopeGuard restore_private_environment = [&] { - vm.running_execution_context().private_environment = outer_private_environment; - }; - // FIXME: Step 14.a is done in the parser. By using a synthetic super(...args) which does not call @@iterator of %Array.prototype% auto const& constructor = *m_constructor; auto class_constructor = ECMAScriptFunctionObject::create( @@ -377,9 +364,11 @@ ThrowCompletionOr ClassExpression::create_class_const Vector instance_fields; Vector static_elements; - for (auto const& element : m_elements) { + for (size_t element_index = 0; element_index < m_elements.size(); element_index++) { + auto const& element = m_elements[element_index]; + // Note: All ClassElementEvaluation start with evaluating the name (or we fake it). - auto element_value = TRY(element->class_element_evaluation(vm, element->is_static() ? *class_constructor : *prototype)); + auto element_value = TRY(element->class_element_evaluation(vm, element->is_static() ? *class_constructor : *prototype, element_keys[element_index])); if (element_value.has()) { auto& container = element->is_static() ? static_private_methods : instance_private_methods; diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index c2a8a582d25..14660e38a8a 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -1326,7 +1326,7 @@ public: // We use the Completion also as a ClassStaticBlockDefinition Record. using ClassValue = Variant; - virtual ThrowCompletionOr class_element_evaluation(VM&, Object& home_object) const = 0; + virtual ThrowCompletionOr class_element_evaluation(VM&, Object& home_object, Value) const = 0; virtual Optional private_bound_identifier() const { return {}; } @@ -1355,7 +1355,7 @@ public: virtual ElementKind class_element_kind() const override { return ElementKind::Method; } virtual void dump(int indent) const override; - virtual ThrowCompletionOr class_element_evaluation(VM&, Object& home_object) const override; + virtual ThrowCompletionOr class_element_evaluation(VM&, Object& home_object, Value property_key) const override; virtual Optional private_bound_identifier() const override; private: @@ -1382,7 +1382,7 @@ public: virtual ElementKind class_element_kind() const override { return ElementKind::Field; } virtual void dump(int indent) const override; - virtual ThrowCompletionOr class_element_evaluation(VM&, Object& home_object) const override; + virtual ThrowCompletionOr class_element_evaluation(VM&, Object& home_object, Value property_key) const override; virtual Optional private_bound_identifier() const override; private: @@ -1401,7 +1401,7 @@ public: } virtual ElementKind class_element_kind() const override { return ElementKind::StaticInitializer; } - virtual ThrowCompletionOr class_element_evaluation(VM&, Object& home_object) const override; + virtual ThrowCompletionOr class_element_evaluation(VM&, Object& home_object, Value property_key) const override; virtual void dump(int indent) const override; @@ -1446,7 +1446,7 @@ public: bool has_name() const { return m_name; } - ThrowCompletionOr create_class_constructor(VM&, Environment* class_environment, Environment* environment, Value super_class, Optional const& binding_name = {}, DeprecatedFlyString const& class_name = {}) const; + ThrowCompletionOr create_class_constructor(VM&, Environment* class_environment, Environment* environment, Value super_class, ReadonlySpan element_keys, Optional 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 47a0efbe121..88dc36a633f 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -2757,8 +2757,35 @@ Bytecode::CodeGenerationErrorOr> ClassExpression::genera if (m_super_class) super_class = TRY(m_super_class->generate_bytecode(generator)).value(); + generator.emit(); + + for (auto const& element : m_elements) { + auto opt_private_name = element->private_bound_identifier(); + if (opt_private_name.has_value()) { + generator.emit(generator.intern_identifier(*opt_private_name)); + } + } + + Vector> elements; + for (auto const& element : m_elements) { + Optional key; + if (is(*element)) { + auto const& class_method = static_cast(*element); + if (!is(class_method.key())) + key = TRY(class_method.key().generate_bytecode(generator)); + } else if (is(*element)) { + auto const& class_field = static_cast(*element); + if (!is(class_field.key())) + key = TRY(class_field.key().generate_bytecode(generator)); + } + + elements.append({ key }); + } + auto dst = choose_dst(generator, preferred_dst); - generator.emit(dst, super_class.has_value() ? super_class->operand() : Optional {}, *this, lhs_name); + generator.emit_with_extra_slots>(elements.size(), dst, super_class.has_value() ? super_class->operand() : Optional {}, *this, lhs_name, elements); + + generator.emit(); return dst; } diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h index 0ed93cdc97b..1dcdab6f2fd 100644 --- a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h @@ -666,7 +666,7 @@ inline ThrowCompletionOr create_variable(VM& vm, DeprecatedFlyString const return verify_cast(vm.variable_environment())->create_global_var_binding(name, false); } -inline ThrowCompletionOr new_class(VM& vm, Value super_class, ClassExpression const& class_expression, Optional const& lhs_name) +inline ThrowCompletionOr new_class(VM& vm, Value super_class, ClassExpression const& class_expression, Optional const& lhs_name, ReadonlySpan element_keys) { auto& interpreter = vm.bytecode_interpreter(); auto name = class_expression.name(); @@ -684,7 +684,7 @@ inline ThrowCompletionOr new_class(VM& vm, Value supe class_name = name.is_null() ? ""sv : name; } - return TRY(class_expression.create_class_constructor(vm, class_environment, vm.lexical_environment(), super_class, binding_name, class_name)); + return TRY(class_expression.create_class_constructor(vm, class_environment, vm.lexical_environment(), super_class, element_keys, binding_name, class_name)); } // 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index 41281fb06ef..d02b08fc818 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -15,6 +15,7 @@ #define ENUMERATE_BYTECODE_OPS(O) \ O(Add) \ + O(AddPrivateName) \ O(ArrayAppend) \ O(AsyncIteratorClose) \ O(Await) \ @@ -31,6 +32,7 @@ O(CopyObjectExcludingProperties) \ O(CreateLexicalEnvironment) \ O(CreateVariableEnvironment) \ + O(CreatePrivateEnvironment) \ O(CreateVariable) \ O(CreateRestParams) \ O(CreateArguments) \ @@ -88,6 +90,7 @@ O(JumpUndefined) \ O(LeaveFinally) \ O(LeaveLexicalEnvironment) \ + O(LeavePrivateEnvironment) \ O(LeaveUnwindContext) \ O(LeftShift) \ O(LessThan) \ diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index d39b0cbd158..3d3c844d62c 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -547,6 +547,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point) } HANDLE_INSTRUCTION(Add); + HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(AddPrivateName); HANDLE_INSTRUCTION(ArrayAppend); HANDLE_INSTRUCTION(AsyncIteratorClose); HANDLE_INSTRUCTION(BitwiseAnd); @@ -561,6 +562,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point) HANDLE_INSTRUCTION(CopyObjectExcludingProperties); HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreateLexicalEnvironment); HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreateVariableEnvironment); + HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(CreatePrivateEnvironment); HANDLE_INSTRUCTION(CreateVariable); HANDLE_INSTRUCTION(CreateRestParams); HANDLE_INSTRUCTION(CreateArguments); @@ -601,6 +603,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point) HANDLE_INSTRUCTION(IteratorToArray); HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(LeaveFinally); HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(LeaveLexicalEnvironment); + HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(LeavePrivateEnvironment); HANDLE_INSTRUCTION_WITHOUT_EXCEPTION_CHECK(LeaveUnwindContext); HANDLE_INSTRUCTION(LeftShift); HANDLE_INSTRUCTION(LessThan); @@ -1140,6 +1143,12 @@ void NewPrimitiveArray::execute_impl(Bytecode::Interpreter& interpreter) const interpreter.set(dst(), array); } +void AddPrivateName::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto const& name = interpreter.current_executable().get_identifier(m_name); + interpreter.vm().running_execution_context().private_environment->add_private_name(name); +} + ThrowCompletionOr ArrayAppend::execute_impl(Bytecode::Interpreter& interpreter) const { return append(interpreter.vm(), interpreter.get(dst()), interpreter.get(src()), m_is_spread); @@ -1266,6 +1275,13 @@ void CreateLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) running_execution_context.saved_lexical_environments.append(make_and_swap_envs(running_execution_context.lexical_environment)); } +void CreatePrivateEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& running_execution_context = interpreter.vm().running_execution_context(); + auto outer_private_environment = running_execution_context.private_environment; + running_execution_context.private_environment = new_private_environment(interpreter.vm(), outer_private_environment); +} + ThrowCompletionOr CreateVariableEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const { auto& running_execution_context = interpreter.vm().running_execution_context(); @@ -1754,6 +1770,12 @@ void LeaveLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) c running_execution_context.lexical_environment = running_execution_context.saved_lexical_environments.take_last(); } +void LeavePrivateEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& running_execution_context = interpreter.vm().running_execution_context(); + running_execution_context.private_environment = running_execution_context.private_environment->outer_environment(); +} + void LeaveUnwindContext::execute_impl(Bytecode::Interpreter& interpreter) const { interpreter.leave_unwind_context(); @@ -1923,7 +1945,14 @@ ThrowCompletionOr NewClass::execute_impl(Bytecode::Interpreter& interprete Value super_class; if (m_super_class.has_value()) super_class = interpreter.get(m_super_class.value()); - interpreter.set(dst(), TRY(new_class(interpreter.vm(), super_class, m_class_expression, m_lhs_name))); + Vector element_keys; + for (size_t i = 0; i < m_element_keys_count; ++i) { + Value element_key; + if (m_element_keys[i].has_value()) + element_key = interpreter.get(m_element_keys[i].value()); + element_keys.append(element_key); + } + interpreter.set(dst(), TRY(new_class(interpreter.vm(), super_class, m_class_expression, m_lhs_name, element_keys))); return {}; } @@ -1969,6 +1998,11 @@ ByteString NewPrimitiveArray::to_byte_string_impl(Bytecode::Executable const& ex format_value_list("elements"sv, elements())); } +ByteString AddPrivateName::to_byte_string_impl(Bytecode::Executable const& executable) const +{ + return ByteString::formatted("AddPrivateName {}"sv, executable.identifier_table->get(m_name)); +} + ByteString ArrayAppend::to_byte_string_impl(Bytecode::Executable const& executable) const { return ByteString::formatted("Append {}, {}{}", @@ -2052,6 +2086,11 @@ ByteString CreateLexicalEnvironment::to_byte_string_impl(Bytecode::Executable co return "CreateLexicalEnvironment"sv; } +ByteString CreatePrivateEnvironment::to_byte_string_impl(Bytecode::Executable const&) const +{ + return "CreatePrivateEnvironment"sv; +} + ByteString CreateVariableEnvironment::to_byte_string_impl(Bytecode::Executable const&) const { return "CreateVariableEnvironment"sv; @@ -2421,6 +2460,11 @@ ByteString LeaveLexicalEnvironment::to_byte_string_impl(Bytecode::Executable con return "LeaveLexicalEnvironment"sv; } +ByteString LeavePrivateEnvironment::to_byte_string_impl(Bytecode::Executable const&) const +{ + return "LeavePrivateEnvironment"sv; +} + ByteString LeaveUnwindContext::to_byte_string_impl(Bytecode::Executable const&) const { return "LeaveUnwindContext"; diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 970d2c2bd99..1819efa5eda 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -350,6 +350,21 @@ private: Value m_elements[]; }; +class AddPrivateName final : public Instruction { +public: + explicit AddPrivateName(IdentifierTableIndex name) + : Instruction(Type::AddPrivateName) + , m_name(name) + { + } + + void execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; + +private: + IdentifierTableIndex m_name; +}; + class ArrayAppend final : public Instruction { public: ArrayAppend(Operand dst, Operand src, bool is_spread) @@ -471,6 +486,17 @@ private: u32 m_capacity { 0 }; }; +class CreatePrivateEnvironment final : public Instruction { +public: + explicit CreatePrivateEnvironment() + : Instruction(Type::CreatePrivateEnvironment) + { + } + + void execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; +}; + class EnterObjectEnvironment final : public Instruction { public: explicit EnterObjectEnvironment(Operand object) @@ -1471,13 +1497,25 @@ private: class NewClass final : public Instruction { public: - explicit NewClass(Operand dst, Optional super_class, ClassExpression const& class_expression, Optional lhs_name) + static constexpr bool IsVariableLength = true; + + explicit NewClass(Operand dst, Optional super_class, ClassExpression const& class_expression, Optional lhs_name, ReadonlySpan> elements_keys) : Instruction(Type::NewClass) , m_dst(dst) , m_super_class(super_class) , m_class_expression(class_expression) , m_lhs_name(lhs_name) + , m_element_keys_count(elements_keys.size()) { + for (size_t i = 0; i < m_element_keys_count; i++) { + if (elements_keys[i].has_value()) + m_element_keys[i] = elements_keys[i]->operand(); + } + } + + size_t length_impl() const + { + return round_up_to_power_of_two(alignof(void*), sizeof(*this) + sizeof(Optional) * m_element_keys_count); } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; @@ -1493,6 +1531,8 @@ private: Optional m_super_class; ClassExpression const& m_class_expression; Optional m_lhs_name; + size_t m_element_keys_count { 0 }; + Optional m_element_keys[]; }; class NewFunction final : public Instruction { @@ -1760,6 +1800,17 @@ public: ByteString to_byte_string_impl(Bytecode::Executable const&) const; }; +class LeavePrivateEnvironment final : public Instruction { +public: + LeavePrivateEnvironment() + : Instruction(Type::LeavePrivateEnvironment) + { + } + + void execute_impl(Bytecode::Interpreter&) const; + ByteString to_byte_string_impl(Bytecode::Executable const&) const; +}; + class LeaveUnwindContext final : public Instruction { public: LeaveUnwindContext() diff --git a/Userland/Libraries/LibJS/Runtime/PrivateEnvironment.cpp b/Userland/Libraries/LibJS/Runtime/PrivateEnvironment.cpp index 4258354f519..e00a476719e 100644 --- a/Userland/Libraries/LibJS/Runtime/PrivateEnvironment.cpp +++ b/Userland/Libraries/LibJS/Runtime/PrivateEnvironment.cpp @@ -34,7 +34,7 @@ PrivateName PrivateEnvironment::resolve_private_identifier(DeprecatedFlyString c return m_outer_environment->resolve_private_identifier(identifier); } -void PrivateEnvironment::add_private_name(Badge, DeprecatedFlyString description) +void PrivateEnvironment::add_private_name(DeprecatedFlyString description) { if (!find_private_name(description).is_end()) return; diff --git a/Userland/Libraries/LibJS/Runtime/PrivateEnvironment.h b/Userland/Libraries/LibJS/Runtime/PrivateEnvironment.h index b450c245551..17c93c35a27 100644 --- a/Userland/Libraries/LibJS/Runtime/PrivateEnvironment.h +++ b/Userland/Libraries/LibJS/Runtime/PrivateEnvironment.h @@ -35,7 +35,10 @@ class PrivateEnvironment : public Cell { public: PrivateName resolve_private_identifier(DeprecatedFlyString const& identifier) const; - void add_private_name(Badge, DeprecatedFlyString description); + void add_private_name(DeprecatedFlyString description); + + PrivateEnvironment* outer_environment() { return m_outer_environment; } + PrivateEnvironment const* outer_environment() const { return m_outer_environment; } private: explicit PrivateEnvironment(PrivateEnvironment* parent);