Created
March 23, 2026 23:04
-
-
Save jdahlin/010f06b9077381ffac927b4ce3679b7a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/Libraries/LibJS/Bytecode/Interpreter.cpp b/Libraries/LibJS/Bytecode/Interpreter.cpp | |
| index 5b617d25d2..c87122aa21 100644 | |
| --- a/Libraries/LibJS/Bytecode/Interpreter.cpp | |
| +++ b/Libraries/LibJS/Bytecode/Interpreter.cpp | |
| @@ -158,6 +158,8 @@ ThrowCompletionOr<Value> Interpreter::run(Script& script_record, GC::Ptr<Environ | |
| // 9. Suspend the currently running execution context. | |
| // 10. Push scriptContext onto the execution context stack; scriptContext is now the running execution context. | |
| + if (executable) | |
| + script_context->initialize_constant_values(*executable); | |
| TRY(vm.push_execution_context(*script_context, {})); | |
| // 13. If result.[[Type]] is normal, then | |
| @@ -292,6 +294,8 @@ ExecutionContext* Interpreter::push_inline_frame( | |
| } | |
| callee_context->private_environment = callee_function.m_private_environment; | |
| + callee_context->initialize_constant_values(callee_executable); | |
| + | |
| // Fast-path push onto execution context stack (avoids Vector::append growth check preventing inlining). | |
| auto& ec_stack = vm().execution_context_stack(); | |
| if (ec_stack.size() < ec_stack.capacity()) [[likely]] | |
| @@ -309,12 +313,7 @@ ExecutionContext* Interpreter::push_inline_frame( | |
| // in cross-realm calls (e.g. iframe <-> parent). | |
| callee_context->executable = callee_executable; | |
| - // Copy constants (memcpy avoids aliasing issues with the scalar loop). | |
| - auto* values = callee_context->registers_and_constants_and_locals_and_arguments(); | |
| - if (auto count = callee_executable.constants.size()) | |
| - memcpy(values + callee_executable.registers_and_locals_count, | |
| - callee_executable.constants.data(), | |
| - count * sizeof(Value)); | |
| + auto* values = callee_context->registers_and_constants_and_locals_and_arguments_span().data(); | |
| // Set this value register. | |
| values[Register::this_value().index()] = callee_context->this_value.value_or(js_special_empty_value()); | |
| @@ -830,10 +829,10 @@ ThrowCompletionOr<Value> Interpreter::run_executable(ExecutionContext& context, | |
| // NOTE: This is how we "push" a new execution context onto the interpreter stack. | |
| TemporaryChange restore_running_execution_context { m_running_execution_context, &context }; | |
| - context.executable = executable; | |
| - | |
| VERIFY(executable.registers_and_locals_count + executable.constants.size() == executable.registers_and_locals_and_constants_count); | |
| VERIFY(executable.registers_and_locals_and_constants_count <= context.registers_and_constants_and_locals_and_arguments_span().size()); | |
| + ASSERT(!context.executable || context.executable == &executable); | |
| + context.executable = executable; | |
| // NOTE: We only copy the `this` value from ExecutionContext if it's not already set. | |
| // If we are re-entering an async/generator context, the `this` value | |
| @@ -842,12 +841,7 @@ ThrowCompletionOr<Value> Interpreter::run_executable(ExecutionContext& context, | |
| if (reg(Register::this_value()).is_special_empty_value()) | |
| reg(Register::this_value()) = context.this_value.value_or(js_special_empty_value()); | |
| - // NB: Layout is [registers | locals | constants | arguments], so constants start after registers+locals. | |
| - auto* values = context.registers_and_constants_and_locals_and_arguments(); | |
| - if (auto count = executable.constants.size()) | |
| - memcpy(values + executable.registers_and_locals_count, | |
| - executable.constants.data(), | |
| - count * sizeof(Value)); | |
| + auto* values = context.registers_and_constants_and_locals_and_arguments_span().data(); | |
| run_bytecode(entry_point); | |
| diff --git a/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Libraries/LibJS/Runtime/AbstractOperations.cpp | |
| index 3a5506ee63..d2e6e782dd 100644 | |
| --- a/Libraries/LibJS/Runtime/AbstractOperations.cpp | |
| +++ b/Libraries/LibJS/Runtime/AbstractOperations.cpp | |
| @@ -761,6 +761,7 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller, | |
| eval_context->private_environment = private_environment; | |
| // 29. Push evalContext onto the execution context stack; evalContext is now the running execution context. | |
| + eval_context->initialize_constant_values(*executable); | |
| TRY(vm.push_execution_context(*eval_context, {})); | |
| // NOTE: We use a ScopeGuard to automatically pop the execution context when any of the `TRY`s below return a throw completion. | |
| diff --git a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp | |
| index c81fed1c48..6ef70f26af 100644 | |
| --- a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp | |
| +++ b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp | |
| @@ -364,6 +364,7 @@ void ECMAScriptFunctionObject::prepare_for_ordinary_call(VM& vm, ExecutionContex | |
| // 11. If callerContext is not already suspended, suspend callerContext. | |
| // 12. Push calleeContext onto the execution context stack; calleeContext is now the running execution context. | |
| + callee_context.initialize_constant_values(*bytecode_executable()); | |
| // NOTE: We don't check for stack overflow here. The bytecode interpreter will do it anyway | |
| // when entering the function we're about to call. | |
| diff --git a/Libraries/LibJS/Runtime/ExecutionContext.cpp b/Libraries/LibJS/Runtime/ExecutionContext.cpp | |
| index 32f758c512..9d3bc13a01 100644 | |
| --- a/Libraries/LibJS/Runtime/ExecutionContext.cpp | |
| +++ b/Libraries/LibJS/Runtime/ExecutionContext.cpp | |
| @@ -118,13 +118,23 @@ NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const | |
| copy->this_value = this_value; | |
| copy->executable = executable; | |
| copy->passed_argument_count = passed_argument_count; | |
| - copy->registers_and_constants_and_locals_and_arguments_count = registers_and_constants_and_locals_and_arguments_count; | |
| for (size_t i = 0; i < registers_and_constants_and_locals_and_arguments_count; ++i) | |
| copy->registers_and_constants_and_locals_and_arguments()[i] = registers_and_constants_and_locals_and_arguments()[i]; | |
| - copy->argument_count = argument_count; | |
| return copy; | |
| } | |
| +void ExecutionContext::initialize_constant_values(Bytecode::Executable& executable_) | |
| +{ | |
| + executable = executable_; | |
| + VERIFY(executable_.registers_and_locals_and_constants_count + argument_count == registers_and_constants_and_locals_and_arguments_count); | |
| + if (auto count = executable_.constants.size()) { | |
| + auto* values = registers_and_constants_and_locals_and_arguments(); | |
| + memcpy(values + executable_.registers_and_locals_count, | |
| + executable_.constants.data(), | |
| + count * sizeof(Value)); | |
| + } | |
| +} | |
| + | |
| void ExecutionContext::visit_edges(Cell::Visitor& visitor) | |
| { | |
| visitor.visit(function); | |
| diff --git a/Libraries/LibJS/Runtime/ExecutionContext.h b/Libraries/LibJS/Runtime/ExecutionContext.h | |
| index 12ab059dbd..6c8d96d767 100644 | |
| --- a/Libraries/LibJS/Runtime/ExecutionContext.h | |
| +++ b/Libraries/LibJS/Runtime/ExecutionContext.h | |
| @@ -112,6 +112,8 @@ public: | |
| return registers_and_constants_and_locals_and_arguments() + (registers_and_constants_and_locals_and_arguments_count - argument_count); | |
| } | |
| + void initialize_constant_values(Bytecode::Executable&); | |
| + | |
| // Non-standard: Inline frame linkage for the bytecode interpreter. | |
| // When a JS-to-JS call is inlined in the dispatch loop, these fields | |
| // allow the Return handler to restore the caller's frame. | |
| diff --git a/Libraries/LibJS/Runtime/NativeFunction.cpp b/Libraries/LibJS/Runtime/NativeFunction.cpp | |
| index 659d9771c2..0f98a2e069 100644 | |
| --- a/Libraries/LibJS/Runtime/NativeFunction.cpp | |
| +++ b/Libraries/LibJS/Runtime/NativeFunction.cpp | |
| @@ -149,6 +149,8 @@ ThrowCompletionOr<Value> NativeFunction::internal_call(ExecutionContext& callee_ | |
| // </8.> -------------------------------------------------------------------------- | |
| // 9. Push calleeContext onto the execution context stack; calleeContext is now the running execution context. | |
| + if (is<NativeJavaScriptBackedFunction>(*this)) | |
| + callee_context.initialize_constant_values(as<NativeJavaScriptBackedFunction>(*this).bytecode_executable()); | |
| TRY(vm.push_execution_context(callee_context, {})); | |
| // 10. Let result be the Completion Record that is the result of evaluating F in a manner that conforms to the specification of F. thisArgument is the this value, argumentsList provides the named parameters, and the NewTarget value is undefined. | |
| @@ -200,6 +202,8 @@ ThrowCompletionOr<GC::Ref<Object>> NativeFunction::internal_construct(ExecutionC | |
| // </8.> -------------------------------------------------------------------------- | |
| // 9. Push calleeContext onto the execution context stack; calleeContext is now the running execution context. | |
| + if (is<NativeJavaScriptBackedFunction>(*this)) | |
| + callee_context.initialize_constant_values(as<NativeJavaScriptBackedFunction>(*this).bytecode_executable()); | |
| TRY(vm.push_execution_context(callee_context, {})); | |
| // 10. Let result be the Completion Record that is the result of evaluating F in a manner that conforms to the specification of F. The this value is uninitialized, argumentsList provides the named parameters, and newTarget provides the NewTarget value. | |
| diff --git a/Libraries/LibJS/Runtime/ShadowRealm.cpp b/Libraries/LibJS/Runtime/ShadowRealm.cpp | |
| index 2ee1644974..59ed666e38 100644 | |
| --- a/Libraries/LibJS/Runtime/ShadowRealm.cpp | |
| +++ b/Libraries/LibJS/Runtime/ShadowRealm.cpp | |
| @@ -146,6 +146,7 @@ ThrowCompletionOr<Value> perform_shadow_realm_eval(VM& vm, Value source, Realm& | |
| auto variable_environment = eval_context->variable_environment; | |
| // 9. Push evalContext onto the execution context stack; evalContext is now the running execution context. | |
| + eval_context->initialize_constant_values(*executable); | |
| TRY(vm.push_execution_context(*eval_context, {})); | |
| // 10. Let result be Completion(EvalDeclarationInstantiation(body, varEnv, lexEnv, null, strictEval)). | |
| diff --git a/Libraries/LibJS/SourceTextModule.cpp b/Libraries/LibJS/SourceTextModule.cpp | |
| index c96c3b98a0..f351738c9a 100644 | |
| --- a/Libraries/LibJS/SourceTextModule.cpp | |
| +++ b/Libraries/LibJS/SourceTextModule.cpp | |
| @@ -539,6 +539,8 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GC::Ptr<Promise | |
| // 8. Suspend the currently running execution context. | |
| // NOTE: Done by the push of execution context in steps below. | |
| + if (m_executable) | |
| + module_context->initialize_constant_values(*m_executable); | |
| // 9. If module.[[HasTLA]] is false, then | |
| if (!m_has_top_level_await) { |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment