diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index f8cb53abbc..f06f6e70ce 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -137,6 +137,46 @@ void Generator::end_breakable_scope() end_boundary(BlockBoundaryType::Break); } +CodeGenerationErrorOr Generator::emit_super_reference(MemberExpression const& expression) +{ + VERIFY(is(expression.object())); + + // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation + // 1. Let env be GetThisEnvironment(). + // 2. Let actualThis be ? env.GetThisBinding(). + auto actual_this_register = allocate_register(); + emit(); + emit(actual_this_register); + + Optional computed_property_value_register; + + if (expression.is_computed()) { + // SuperProperty : super [ Expression ] + // 3. Let propertyNameReference be ? Evaluation of Expression. + // 4. Let propertyNameValue be ? GetValue(propertyNameReference). + TRY(expression.property().generate_bytecode(*this)); + computed_property_value_register = allocate_register(); + emit(*computed_property_value_register); + } + + // 5/7. Return ? MakeSuperPropertyReference(actualThis, propertyKey, strict). + + // https://tc39.es/ecma262/#sec-makesuperpropertyreference + // 1. Let env be GetThisEnvironment(). + // 2. Assert: env.HasSuperBinding() is true. + // 3. Let baseValue be ? env.GetSuperBase(). + auto super_base_register = allocate_register(); + emit(); + emit(super_base_register); + + // 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }. + return ReferenceRegisters { + .base = super_base_register, + .referenced_name = move(computed_property_value_register), + .this_value = actual_this_register, + }; +} + CodeGenerationErrorOr Generator::emit_load_from_reference(JS::ASTNode const& node) { if (is(node)) { @@ -149,43 +189,17 @@ CodeGenerationErrorOr Generator::emit_load_from_reference(JS::ASTNode cons // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation if (is(expression.object())) { - // 1. Let env be GetThisEnvironment(). - // 2. Let actualThis be ? env.GetThisBinding(). - auto this_register = allocate_register(); - emit(); - emit(this_register); + auto super_reference = TRY(emit_super_reference(expression)); - Optional computed_property_value_register; - - if (expression.is_computed()) { - // SuperProperty : super [ Expression ] - // 3. Let propertyNameReference be ? Evaluation of Expression. - // 4. Let propertyNameValue be ? GetValue(propertyNameReference). - TRY(expression.property().generate_bytecode(*this)); - computed_property_value_register = allocate_register(); - emit(*computed_property_value_register); - } - - // 5/7. Return ? MakeSuperPropertyReference(actualThis, propertyKey, strict). - - // https://tc39.es/ecma262/#sec-makesuperpropertyreference - // 1. Let env be GetThisEnvironment(). - // 2. Assert: env.HasSuperBinding() is true. - // 3. Let baseValue be ? env.GetSuperBase(). - emit(); - - // 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }. - if (computed_property_value_register.has_value()) { + if (super_reference.referenced_name.has_value()) { // 5. Let propertyKey be ? ToPropertyKey(propertyNameValue). // FIXME: This does ToPropertyKey out of order, which is observable by Symbol.toPrimitive! - auto super_base_register = allocate_register(); - emit(super_base_register); - emit(*computed_property_value_register); - emit(super_base_register, this_register); + emit(*super_reference.referenced_name); + emit(super_reference.base, super_reference.this_value); } else { // 3. Let propertyKey be StringValue of IdentifierName. auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); - emit(identifier_table_ref, this_register); + emit(identifier_table_ref, super_reference.this_value); } } else { TRY(expression.object().generate_bytecode(*this)); @@ -230,44 +244,18 @@ CodeGenerationErrorOr Generator::emit_store_to_reference(JS::ASTNode const // https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation if (is(expression.object())) { - // 1. Let env be GetThisEnvironment(). - // 2. Let actualThis be ? env.GetThisBinding(). - auto this_register = allocate_register(); - emit(); - emit(this_register); - - Optional computed_property_value_register; - - if (expression.is_computed()) { - // SuperProperty : super [ Expression ] - // 3. Let propertyNameReference be ? Evaluation of Expression. - // 4. Let propertyNameValue be ? GetValue(propertyNameReference). - TRY(expression.property().generate_bytecode(*this)); - computed_property_value_register = allocate_register(); - emit(*computed_property_value_register); - } - - // 5/7. Return ? MakeSuperPropertyReference(actualThis, propertyKey, strict). - - // https://tc39.es/ecma262/#sec-makesuperpropertyreference - // 1. Let env be GetThisEnvironment(). - // 2. Assert: env.HasSuperBinding() is true. - // 3. Let baseValue be ? env.GetSuperBase(). - auto super_base_register = allocate_register(); - emit(); - emit(super_base_register); - + auto super_reference = TRY(emit_super_reference(expression)); emit(value_reg); // 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[ThisValue]]: actualThis }. - if (computed_property_value_register.has_value()) { + if (super_reference.referenced_name.has_value()) { // 5. Let propertyKey be ? ToPropertyKey(propertyNameValue). // FIXME: This does ToPropertyKey out of order, which is observable by Symbol.toPrimitive! - emit(super_base_register, *computed_property_value_register, this_register); + emit(super_reference.base, *super_reference.referenced_name, super_reference.this_value); } else { // 3. Let propertyKey be StringValue of IdentifierName. auto identifier_table_ref = intern_identifier(verify_cast(expression.property()).string()); - emit(super_base_register, this_register, identifier_table_ref); + emit(super_reference.base, super_reference.this_value, identifier_table_ref); } } else { TRY(expression.object().generate_bytecode(*this)); diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.h b/Userland/Libraries/LibJS/Bytecode/Generator.h index f2edae60f8..af3beb59c5 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.h +++ b/Userland/Libraries/LibJS/Bytecode/Generator.h @@ -83,6 +83,13 @@ public: CodeGenerationErrorOr emit_store_to_reference(JS::ASTNode const&); CodeGenerationErrorOr emit_delete_reference(JS::ASTNode const&); + struct ReferenceRegisters { + Register base; // [[Base]] + Optional referenced_name; // [[ReferencedName]] + Register this_value; // [[ThisValue]] + }; + CodeGenerationErrorOr emit_super_reference(MemberExpression const&); + void emit_set_variable(JS::Identifier const& identifier, Bytecode::Op::SetVariable::InitializationMode initialization_mode = Bytecode::Op::SetVariable::InitializationMode::Set, Bytecode::Op::EnvironmentMode mode = Bytecode::Op::EnvironmentMode::Lexical); void push_home_object(Register);