From e5c7d8407bd1ccef9a4ff296747c89e186808d88 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 23 Jun 2023 08:16:17 +0200 Subject: [PATCH] LibJS/Bytecode: Support private class fields This is accomplished with two new instructions: - GetPrivateById - PutPrivateById Looks like 1616 new passes on test262. :^) --- .../Libraries/LibJS/Bytecode/ASTCodegen.cpp | 5 +++ .../Libraries/LibJS/Bytecode/Generator.cpp | 7 ++++ .../Libraries/LibJS/Bytecode/Instruction.h | 2 + Userland/Libraries/LibJS/Bytecode/Op.cpp | 37 ++++++++++++++++ Userland/Libraries/LibJS/Bytecode/Op.h | 42 +++++++++++++++++++ 5 files changed, 93 insertions(+) diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index e7da966100b..cc8be8b26cf 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -382,6 +382,8 @@ Bytecode::CodeGenerationErrorOr AssignmentExpression::generate_bytecode(By // To be continued later with PutByValue. } else if (expression.property().is_identifier()) { // Do nothing, this will be handled by PutById later. + } else if (expression.property().is_private_identifier()) { + // Do nothing, this will be handled by PutPrivateById later. } else { return Bytecode::CodeGenerationError { &expression, @@ -415,6 +417,9 @@ Bytecode::CodeGenerationErrorOr AssignmentExpression::generate_bytecode(By } else if (expression.property().is_identifier()) { auto identifier_table_ref = generator.intern_identifier(verify_cast(expression.property()).string()); generator.emit(*base_object_register, identifier_table_ref); + } else if (expression.property().is_private_identifier()) { + auto identifier_table_ref = generator.intern_identifier(verify_cast(expression.property()).string()); + generator.emit(*base_object_register, identifier_table_ref); } else { return Bytecode::CodeGenerationError { &expression, diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index 8bb2d5443e1..46713e9f478 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -198,6 +198,9 @@ CodeGenerationErrorOr Generator::emit_load_from_reference(JS::ASTNode cons } else if (expression.property().is_identifier()) { auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); emit(identifier_table_ref); + } else if (expression.property().is_private_identifier()) { + auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); + emit(identifier_table_ref); } else { return CodeGenerationError { &expression, @@ -238,6 +241,10 @@ CodeGenerationErrorOr Generator::emit_store_to_reference(JS::ASTNode const emit(value_reg); auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); emit(object_reg, identifier_table_ref); + } else if (expression.property().is_private_identifier()) { + emit(value_reg); + auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); + emit(object_reg, identifier_table_ref); } else { return CodeGenerationError { &expression, diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index bc6a1373e43..76cd9716e11 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -38,6 +38,7 @@ O(GetMethod) \ O(GetNewTarget) \ O(GetObjectPropertyIterator) \ + O(GetPrivateById) \ O(GetVariable) \ O(GreaterThan) \ O(GreaterThanEquals) \ @@ -76,6 +77,7 @@ O(PushDeclarativeEnvironment) \ O(PutById) \ O(PutByValue) \ + O(PutPrivateById) \ O(ResolveThisBinding) \ O(ResolveSuperBase) \ O(Return) \ diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index b488e1349c8..3e0fac81f4c 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -512,6 +512,16 @@ ThrowCompletionOr GetById::execute_impl(Bytecode::Interpreter& interpreter return {}; } +ThrowCompletionOr GetPrivateById::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto const& name = interpreter.current_executable().get_identifier(m_property); + auto base_value = interpreter.accumulator(); + auto private_reference = make_private_reference(vm, base_value, name); + interpreter.accumulator() = TRY(private_reference.get_value(vm)); + return {}; +} + ThrowCompletionOr PutById::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); @@ -522,6 +532,17 @@ ThrowCompletionOr PutById::execute_impl(Bytecode::Interpreter& interpreter return put_by_property_key(vm, object, value, name, m_kind); } +ThrowCompletionOr PutPrivateById::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + // NOTE: Get the value from the accumulator before side effects have a chance to overwrite it. + auto value = interpreter.accumulator(); + auto object = TRY(interpreter.reg(m_base).to_object(vm)); + auto name = interpreter.current_executable().get_identifier(m_property); + auto private_reference = make_private_reference(vm, object, name); + return private_reference.put_value(vm, value); +} + ThrowCompletionOr DeleteById::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); @@ -1252,11 +1273,27 @@ DeprecatedString PutById::to_deprecated_string_impl(Bytecode::Executable const& return DeprecatedString::formatted("PutById kind:{} base:{}, property:{} ({})", kind, m_base, m_property, executable.identifier_table->get(m_property)); } +DeprecatedString PutPrivateById::to_deprecated_string_impl(Bytecode::Executable const& executable) const +{ + auto kind = m_kind == PropertyKind::Getter + ? "getter" + : m_kind == PropertyKind::Setter + ? "setter" + : "property"; + + return DeprecatedString::formatted("PutPrivateById kind:{} base:{}, property:{} ({})", kind, m_base, m_property, executable.identifier_table->get(m_property)); +} + DeprecatedString GetById::to_deprecated_string_impl(Bytecode::Executable const& executable) const { return DeprecatedString::formatted("GetById {} ({})", m_property, executable.identifier_table->get(m_property)); } +DeprecatedString GetPrivateById::to_deprecated_string_impl(Bytecode::Executable const& executable) const +{ + return DeprecatedString::formatted("GetPrivateById {} ({})", m_property, executable.identifier_table->get(m_property)); +} + DeprecatedString DeleteById::to_deprecated_string_impl(Bytecode::Executable const& executable) const { return DeprecatedString::formatted("DeleteById {} ({})", m_property, executable.identifier_table->get(m_property)); diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 83fb61dfca6..5326d3de2c8 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -517,6 +517,23 @@ private: IdentifierTableIndex m_property; }; +class GetPrivateById final : public Instruction { +public: + explicit GetPrivateById(IdentifierTableIndex property) + : Instruction(Type::GetPrivateById) + , m_property(property) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } + void replace_references_impl(Register, Register) { } + +private: + IdentifierTableIndex m_property; +}; + enum class PropertyKind { Getter, Setter, @@ -550,6 +567,31 @@ private: PropertyKind m_kind; }; +class PutPrivateById final : public Instruction { +public: + explicit PutPrivateById(Register base, IdentifierTableIndex property, PropertyKind kind = PropertyKind::KeyValue) + : Instruction(Type::PutPrivateById) + , m_base(base) + , m_property(property) + , m_kind(kind) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } + void replace_references_impl(Register from, Register to) + { + if (m_base == from) + m_base = to; + } + +private: + Register m_base; + IdentifierTableIndex m_property; + PropertyKind m_kind; +}; + class DeleteById final : public Instruction { public: explicit DeleteById(IdentifierTableIndex property)