diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 23c5a17299..1222d22e17 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -1526,6 +1526,19 @@ Bytecode::CodeGenerationErrorOr CallExpression::generate_bytecode(Bytecode } else if (is(*m_callee)) { auto& optional_chain = static_cast(*m_callee); TRY(generate_optional_chain(generator, optional_chain, callee_reg, this_reg)); + } else if (is(*m_callee)) { + // If the callee is an identifier, we may need to extract a `this` value. + // This is important when we're inside a `with` statement and calling a method on + // the environment's binding object. + // NOTE: If the identifier refers to a known "local" or "global", we know it can't be + // a `with` binding, so we can skip this. + auto& identifier = static_cast(*m_callee); + if (!identifier.is_local() && !identifier.is_global()) { + generator.emit(generator.intern_identifier(identifier.string()), callee_reg, this_reg); + } else { + TRY(m_callee->generate_bytecode(generator)); + generator.emit(callee_reg); + } } else { // FIXME: this = global object in sloppy mode. TRY(m_callee->generate_bytecode(generator)); diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index 13d2db87f6..7855e723b5 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -10,110 +10,111 @@ #include #include -#define ENUMERATE_BYTECODE_OPS(O) \ - O(Add) \ - O(Append) \ - O(AsyncIteratorClose) \ - O(Await) \ - O(BitwiseAnd) \ - O(BitwiseNot) \ - O(BitwiseOr) \ - O(BitwiseXor) \ - O(BlockDeclarationInstantiation) \ - O(Call) \ - O(CallWithArgumentArray) \ - O(ConcatString) \ - O(ContinuePendingUnwind) \ - O(CopyObjectExcludingProperties) \ - O(CreateLexicalEnvironment) \ - O(CreateVariable) \ - O(Decrement) \ - O(DeleteById) \ - O(DeleteByIdWithThis) \ - O(DeleteByValue) \ - O(DeleteByValueWithThis) \ - O(DeleteVariable) \ - O(Div) \ - O(EnterUnwindContext) \ - O(EnterObjectEnvironment) \ - O(Exp) \ - O(GetById) \ - O(GetByIdWithThis) \ - O(GetByValue) \ - O(GetByValueWithThis) \ - O(GetIterator) \ - O(GetMethod) \ - O(GetNewTarget) \ - O(GetImportMeta) \ - O(GetObjectPropertyIterator) \ - O(GetPrivateById) \ - O(GetVariable) \ - O(GetGlobal) \ - O(GetLocal) \ - O(GreaterThan) \ - O(GreaterThanEquals) \ - O(HasPrivateId) \ - O(ImportCall) \ - O(In) \ - O(Increment) \ - O(InstanceOf) \ - O(IteratorClose) \ - O(IteratorNext) \ - O(IteratorResultDone) \ - O(IteratorResultValue) \ - O(IteratorToArray) \ - O(Jump) \ - O(JumpConditional) \ - O(JumpNullish) \ - O(JumpUndefined) \ - O(LeaveLexicalEnvironment) \ - O(LeaveUnwindContext) \ - O(LeftShift) \ - O(LessThan) \ - O(LessThanEquals) \ - O(Load) \ - O(LoadImmediate) \ - O(LooselyEquals) \ - O(LooselyInequals) \ - O(Mod) \ - O(Mul) \ - O(NewArray) \ - O(NewBigInt) \ - O(NewClass) \ - O(NewFunction) \ - O(NewObject) \ - O(NewRegExp) \ - O(NewString) \ - O(NewTypeError) \ - O(Not) \ - O(PushDeclarativeEnvironment) \ - O(PutById) \ - O(PutByIdWithThis) \ - O(PutByValue) \ - O(PutByValueWithThis) \ - O(PutPrivateById) \ - O(ResolveThisBinding) \ - O(ResolveSuperBase) \ - O(Return) \ - O(RightShift) \ - O(ScheduleJump) \ - O(SetVariable) \ - O(SetLocal) \ - O(Store) \ - O(StrictlyEquals) \ - O(StrictlyInequals) \ - O(Sub) \ - O(SuperCallWithArgumentArray) \ - O(Throw) \ - O(ThrowIfNotObject) \ - O(ThrowIfNullish) \ - O(ToNumeric) \ - O(Typeof) \ - O(TypeofVariable) \ - O(TypeofLocal) \ - O(UnaryMinus) \ - O(UnaryPlus) \ - O(UnsignedRightShift) \ +#define ENUMERATE_BYTECODE_OPS(O) \ + O(Add) \ + O(Append) \ + O(AsyncIteratorClose) \ + O(Await) \ + O(BitwiseAnd) \ + O(BitwiseNot) \ + O(BitwiseOr) \ + O(BitwiseXor) \ + O(BlockDeclarationInstantiation) \ + O(Call) \ + O(CallWithArgumentArray) \ + O(ConcatString) \ + O(ContinuePendingUnwind) \ + O(CopyObjectExcludingProperties) \ + O(CreateLexicalEnvironment) \ + O(CreateVariable) \ + O(Decrement) \ + O(DeleteById) \ + O(DeleteByIdWithThis) \ + O(DeleteByValue) \ + O(DeleteByValueWithThis) \ + O(DeleteVariable) \ + O(Div) \ + O(EnterUnwindContext) \ + O(EnterObjectEnvironment) \ + O(Exp) \ + O(GetById) \ + O(GetByIdWithThis) \ + O(GetByValue) \ + O(GetByValueWithThis) \ + O(GetCalleeAndThisFromEnvironment) \ + O(GetIterator) \ + O(GetMethod) \ + O(GetNewTarget) \ + O(GetImportMeta) \ + O(GetObjectPropertyIterator) \ + O(GetPrivateById) \ + O(GetVariable) \ + O(GetGlobal) \ + O(GetLocal) \ + O(GreaterThan) \ + O(GreaterThanEquals) \ + O(HasPrivateId) \ + O(ImportCall) \ + O(In) \ + O(Increment) \ + O(InstanceOf) \ + O(IteratorClose) \ + O(IteratorNext) \ + O(IteratorResultDone) \ + O(IteratorResultValue) \ + O(IteratorToArray) \ + O(Jump) \ + O(JumpConditional) \ + O(JumpNullish) \ + O(JumpUndefined) \ + O(LeaveLexicalEnvironment) \ + O(LeaveUnwindContext) \ + O(LeftShift) \ + O(LessThan) \ + O(LessThanEquals) \ + O(Load) \ + O(LoadImmediate) \ + O(LooselyEquals) \ + O(LooselyInequals) \ + O(Mod) \ + O(Mul) \ + O(NewArray) \ + O(NewBigInt) \ + O(NewClass) \ + O(NewFunction) \ + O(NewObject) \ + O(NewRegExp) \ + O(NewString) \ + O(NewTypeError) \ + O(Not) \ + O(PushDeclarativeEnvironment) \ + O(PutById) \ + O(PutByIdWithThis) \ + O(PutByValue) \ + O(PutByValueWithThis) \ + O(PutPrivateById) \ + O(ResolveThisBinding) \ + O(ResolveSuperBase) \ + O(Return) \ + O(RightShift) \ + O(ScheduleJump) \ + O(SetVariable) \ + O(SetLocal) \ + O(Store) \ + O(StrictlyEquals) \ + O(StrictlyInequals) \ + O(Sub) \ + O(SuperCallWithArgumentArray) \ + O(Throw) \ + O(ThrowIfNotObject) \ + O(ThrowIfNullish) \ + O(ToNumeric) \ + O(Typeof) \ + O(TypeofVariable) \ + O(TypeofLocal) \ + O(UnaryMinus) \ + O(UnaryPlus) \ + O(UnsignedRightShift) \ O(Yield) namespace JS::Bytecode { diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index 164ed5b829..53e0b3ea76 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -418,6 +418,46 @@ ThrowCompletionOr GetVariable::execute_impl(Bytecode::Interpreter& interpr return {}; } +ThrowCompletionOr GetCalleeAndThisFromEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + + auto get_reference = [&]() -> ThrowCompletionOr { + auto const& string = interpreter.current_executable().get_identifier(m_identifier); + if (m_cached_environment_coordinate.has_value()) { + auto environment = vm.running_execution_context().lexical_environment; + for (size_t i = 0; i < m_cached_environment_coordinate->hops; ++i) + environment = environment->outer_environment(); + VERIFY(environment); + VERIFY(environment->is_declarative_environment()); + if (!environment->is_permanently_screwed_by_eval()) { + return Reference { *environment, string, vm.in_strict_mode(), m_cached_environment_coordinate }; + } + m_cached_environment_coordinate = {}; + } + + auto reference = TRY(vm.resolve_binding(string)); + if (reference.environment_coordinate().has_value()) + m_cached_environment_coordinate = reference.environment_coordinate(); + return reference; + }; + auto reference = TRY(get_reference()); + interpreter.reg(m_callee_reg) = TRY(reference.get_value(vm)); + + Value this_value = js_undefined(); + if (reference.is_property_reference()) { + this_value = reference.get_this_value(); + } else { + if (reference.is_environment_reference()) { + if (auto base_object = reference.base_environment().with_base_object(); base_object != nullptr) + this_value = base_object; + } + } + interpreter.reg(m_this_reg) = this_value; + + return {}; +} + ThrowCompletionOr GetGlobal::execute_impl(Bytecode::Interpreter& interpreter) const { auto& vm = interpreter.vm(); @@ -1498,6 +1538,11 @@ DeprecatedString ConcatString::to_deprecated_string_impl(Bytecode::Executable co return DeprecatedString::formatted("ConcatString {}", m_lhs); } +DeprecatedString GetCalleeAndThisFromEnvironment::to_deprecated_string_impl(Bytecode::Executable const& executable) const +{ + return DeprecatedString::formatted("GetCalleeAndThisFromEnvironment {} -> callee: {}, this:{} ", executable.identifier_table->get(m_identifier), m_callee_reg, m_this_reg); +} + DeprecatedString GetVariable::to_deprecated_string_impl(Bytecode::Executable const& executable) const { return DeprecatedString::formatted("GetVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier)); diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 754870b9c0..f4508e92bd 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -441,6 +441,29 @@ private: size_t m_index; }; +class GetCalleeAndThisFromEnvironment final : public Instruction { +public: + explicit GetCalleeAndThisFromEnvironment(IdentifierTableIndex identifier, Register callee_reg, Register this_reg) + : Instruction(Type::GetCalleeAndThisFromEnvironment) + , m_identifier(identifier) + , m_callee_reg(callee_reg) + , m_this_reg(this_reg) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; + + IdentifierTableIndex identifier() const { return m_identifier; } + +private: + IdentifierTableIndex m_identifier; + Register m_callee_reg; + Register m_this_reg; + + Optional mutable m_cached_environment_coordinate; +}; + class GetVariable final : public Instruction { public: explicit GetVariable(IdentifierTableIndex identifier)