mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 22:02:44 +00:00 
			
		
		
		
	LibJS: Use correct this value when callee is a with binding
				
					
				
			If we're inside of a `with` statement scope, we have to take care to extract the correct `this` value for use in calls when calling a method on the binding object via an Identifier instead of a MemberExpression. This makes Vue.js work way better in the bytecode VM. :^) Also, 1 new pass on test262.
This commit is contained in:
		
							parent
							
								
									e61fdd1dc6
								
							
						
					
					
						commit
						e91bdedc93
					
				
					 4 changed files with 186 additions and 104 deletions
				
			
		|  | @ -1526,6 +1526,19 @@ Bytecode::CodeGenerationErrorOr<void> CallExpression::generate_bytecode(Bytecode | |||
|     } else if (is<OptionalChain>(*m_callee)) { | ||||
|         auto& optional_chain = static_cast<OptionalChain const&>(*m_callee); | ||||
|         TRY(generate_optional_chain(generator, optional_chain, callee_reg, this_reg)); | ||||
|     } else if (is<Identifier>(*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<Identifier const&>(*m_callee); | ||||
|         if (!identifier.is_local() && !identifier.is_global()) { | ||||
|             generator.emit<Bytecode::Op::GetCalleeAndThisFromEnvironment>(generator.intern_identifier(identifier.string()), callee_reg, this_reg); | ||||
|         } else { | ||||
|             TRY(m_callee->generate_bytecode(generator)); | ||||
|             generator.emit<Bytecode::Op::Store>(callee_reg); | ||||
|         } | ||||
|     } else { | ||||
|         // FIXME: this = global object in sloppy mode.
 | ||||
|         TRY(m_callee->generate_bytecode(generator)); | ||||
|  |  | |||
|  | @ -10,110 +10,111 @@ | |||
| #include <AK/Span.h> | ||||
| #include <LibJS/Forward.h> | ||||
| 
 | ||||
| #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 { | ||||
|  |  | |||
|  | @ -418,6 +418,46 @@ ThrowCompletionOr<void> GetVariable::execute_impl(Bytecode::Interpreter& interpr | |||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| ThrowCompletionOr<void> GetCalleeAndThisFromEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const | ||||
| { | ||||
|     auto& vm = interpreter.vm(); | ||||
| 
 | ||||
|     auto get_reference = [&]() -> ThrowCompletionOr<Reference> { | ||||
|         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<void> 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)); | ||||
|  |  | |||
|  | @ -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<void> 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<EnvironmentCoordinate> mutable m_cached_environment_coordinate; | ||||
| }; | ||||
| 
 | ||||
| class GetVariable final : public Instruction { | ||||
| public: | ||||
|     explicit GetVariable(IdentifierTableIndex identifier) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling