1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-09 09:34:57 +09:00

LibJS+LibWeb: Calculate count of regs+consts+locals before EC allocation

This is a preparation step before joining arguments vector into vector
of registers+constants+locals.
This commit is contained in:
Aliaksandr Kalenik 2025-04-22 21:49:41 +02:00 committed by Andreas Kling
parent fca1d33fec
commit 80a8040794
Notes: github-actions[bot] 2025-04-24 08:32:54 +00:00
14 changed files with 144 additions and 107 deletions

View file

@ -646,8 +646,23 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller,
// 19. If runningContext is not already suspended, suspend runningContext.
// NOTE: Done by the push on step 27.
// NOTE: Spec steps are rearranged in order to compute number of registers+constants+locals before construction of the execution context.
// 28. Let result be Completion(EvalDeclarationInstantiation(body, varEnv, lexEnv, privateEnv, strictEval)).
TRY(eval_declaration_instantiation(vm, program, variable_environment, lexical_environment, private_environment, strict_eval));
// 29. If result.[[Type]] is normal, then
// a. Set result to the result of evaluating body.
auto executable_result = Bytecode::Generator::generate_from_ast_node(vm, program, {});
if (executable_result.is_error())
return vm.throw_completion<InternalError>(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string()));
auto executable = executable_result.release_value();
executable->name = "eval"_fly_string;
if (Bytecode::g_dump_bytecode)
executable->dump();
// 20. Let evalContext be a new ECMAScript code execution context.
auto eval_context = ExecutionContext::create();
auto eval_context = ExecutionContext::create(executable->number_of_registers + executable->constants.size() + executable->local_variable_names.size());
// 21. Set evalContext's Function to null.
// NOTE: This was done in the construction of eval_context.
@ -680,21 +695,8 @@ ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller,
vm.pop_execution_context();
};
// 28. Let result be Completion(EvalDeclarationInstantiation(body, varEnv, lexEnv, privateEnv, strictEval)).
TRY(eval_declaration_instantiation(vm, program, variable_environment, lexical_environment, private_environment, strict_eval));
Optional<Value> eval_result;
// 29. If result.[[Type]] is normal, then
// a. Set result to the result of evaluating body.
auto executable_result = Bytecode::Generator::generate_from_ast_node(vm, program, {});
if (executable_result.is_error())
return vm.throw_completion<InternalError>(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string()));
auto executable = executable_result.release_value();
executable->name = "eval"_fly_string;
if (Bytecode::g_dump_bytecode)
executable->dump();
auto result_or_error = vm.bytecode_interpreter().run_executable(*executable, {});
if (result_or_error.value.is_error())
return result_or_error.value.release_error();

View file

@ -489,7 +489,18 @@ ThrowCompletionOr<Value> ECMAScriptFunctionObject::internal_call(Value this_argu
// 1. Let callerContext be the running execution context.
// NOTE: No-op, kept by the VM in its execution context stack.
auto callee_context = ExecutionContext::create();
if (!m_bytecode_executable) {
if (!ecmascript_code().bytecode_executable()) {
if (is_module_wrapper()) {
const_cast<Statement&>(ecmascript_code()).set_bytecode_executable(TRY(Bytecode::compile(vm, ecmascript_code(), kind(), name())));
} else {
const_cast<Statement&>(ecmascript_code()).set_bytecode_executable(TRY(Bytecode::compile(vm, *this)));
}
}
m_bytecode_executable = ecmascript_code().bytecode_executable();
}
auto callee_context = ExecutionContext::create(m_bytecode_executable->number_of_registers + m_bytecode_executable->constants.size() + m_bytecode_executable->local_variable_names.size());
// Non-standard
callee_context->arguments.ensure_capacity(max(arguments_list.size(), formal_parameters().size()));
@ -546,7 +557,18 @@ ThrowCompletionOr<GC::Ref<Object>> ECMAScriptFunctionObject::internal_construct(
{
auto& vm = this->vm();
auto callee_context = ExecutionContext::create();
if (!m_bytecode_executable) {
if (!ecmascript_code().bytecode_executable()) {
if (is_module_wrapper()) {
const_cast<Statement&>(ecmascript_code()).set_bytecode_executable(TRY(Bytecode::compile(vm, ecmascript_code(), kind(), name())));
} else {
const_cast<Statement&>(ecmascript_code()).set_bytecode_executable(TRY(Bytecode::compile(vm, *this)));
}
}
m_bytecode_executable = ecmascript_code().bytecode_executable();
}
auto callee_context = ExecutionContext::create(m_bytecode_executable->number_of_registers + m_bytecode_executable->constants.size() + m_bytecode_executable->local_variable_names.size());
// Non-standard
callee_context->arguments.ensure_capacity(max(arguments_list.size(), formal_parameters().size()));
@ -890,19 +912,6 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
auto& vm = this->vm();
auto& realm = *vm.current_realm();
if (!m_bytecode_executable) {
if (!ecmascript_code().bytecode_executable()) {
if (is_module_wrapper()) {
const_cast<Statement&>(ecmascript_code()).set_bytecode_executable(TRY(Bytecode::compile(vm, ecmascript_code(), kind(), name())));
} else {
const_cast<Statement&>(ecmascript_code()).set_bytecode_executable(TRY(Bytecode::compile(vm, *this)));
}
}
m_bytecode_executable = ecmascript_code().bytecode_executable();
}
vm.running_execution_context().registers_and_constants_and_locals.resize_with_default_value(local_variables_names().size() + m_bytecode_executable->number_of_registers + m_bytecode_executable->constants.size(), js_special_empty_value());
auto result_and_frame = vm.bytecode_interpreter().run_executable(*m_bytecode_executable, {});
if (result_and_frame.value.is_error())

View file

@ -33,9 +33,11 @@ private:
static NeverDestroyed<ExecutionContextAllocator> s_execution_context_allocator;
NonnullOwnPtr<ExecutionContext> ExecutionContext::create()
NonnullOwnPtr<ExecutionContext> ExecutionContext::create(u32 count)
{
return s_execution_context_allocator->allocate();
auto execution_context = s_execution_context_allocator->allocate();
execution_context->registers_and_constants_and_locals.resize_with_default_value(count, js_special_empty_value());
return execution_context;
}
void ExecutionContext::operator delete(void* ptr)
@ -53,7 +55,7 @@ ExecutionContext::~ExecutionContext()
NonnullOwnPtr<ExecutionContext> ExecutionContext::copy() const
{
auto copy = create();
auto copy = create(registers_and_constants_and_locals.size());
copy->function = function;
copy->realm = realm;
copy->script_or_module = script_or_module;

View file

@ -32,7 +32,7 @@ struct CachedSourceRange : public RefCounted<CachedSourceRange> {
// 9.4 Execution Contexts, https://tc39.es/ecma262/#sec-execution-contexts
struct ExecutionContext {
static NonnullOwnPtr<ExecutionContext> create();
static NonnullOwnPtr<ExecutionContext> create(u32);
[[nodiscard]] NonnullOwnPtr<ExecutionContext> copy() const;
~ExecutionContext();
@ -86,7 +86,12 @@ public:
bool is_strict_mode { false };
Vector<Value> arguments;
private:
friend class Bytecode::Interpreter;
Vector<Value> registers_and_constants_and_locals;
public:
Vector<Bytecode::UnwindInfo> unwind_contexts;
Vector<Optional<size_t>> previously_scheduled_jumps;
Vector<GC::Ptr<Environment>> saved_lexical_environments;

View file

@ -119,7 +119,7 @@ ThrowCompletionOr<Value> NativeFunction::internal_call(Value this_argument, Read
// 2. If callerContext is not already suspended, suspend callerContext.
// 3. Let calleeContext be a new execution context.
auto callee_context = ExecutionContext::create();
auto callee_context = ExecutionContext::create(0);
// 4. Set the Function of calleeContext to F.
callee_context->function = this;
@ -180,7 +180,7 @@ ThrowCompletionOr<GC::Ref<Object>> NativeFunction::internal_construct(ReadonlySp
// 2. If callerContext is not already suspended, suspend callerContext.
// 3. Let calleeContext be a new execution context.
auto callee_context = ExecutionContext::create();
auto callee_context = ExecutionContext::create(0);
// 4. Set the Function of calleeContext to F.
callee_context->function = this;

View file

@ -36,7 +36,7 @@ ThrowCompletionOr<NonnullOwnPtr<ExecutionContext>> Realm::initialize_host_define
// FIXME: 6. Set realm.[[TemplateMap]] to a new empty List.
// 7. Let newContext be a new execution context.
auto new_context = ExecutionContext::create();
auto new_context = ExecutionContext::create(0);
// 8. Set the Function of newContext to null.
new_context->function = nullptr;

View file

@ -127,8 +127,14 @@ ThrowCompletionOr<Value> perform_shadow_realm_eval(VM& vm, StringView source_tex
// 5. If runningContext is not already suspended, suspend runningContext.
// NOTE: This would be unused due to step 9 and is omitted for that reason.
auto maybe_executable = Bytecode::compile(vm, program, FunctionKind::Normal, "ShadowRealmEval"_fly_string);
if (maybe_executable.is_error())
return vm.throw_completion<TypeError>(ErrorType::ShadowRealmEvaluateAbruptCompletion);
auto executable = maybe_executable.release_value();
// 6. Let evalContext be GetShadowRealmContext(evalRealm, strictEval).
auto eval_context = get_shadow_realm_context(eval_realm, strict_eval);
u32 registers_and_constants_and_locals_count = executable->number_of_registers + executable->constants.size() + executable->local_variable_names.size();
auto eval_context = get_shadow_realm_context(eval_realm, strict_eval, registers_and_constants_and_locals_count);
// 7. Let lexEnv be evalContext's LexicalEnvironment.
auto lexical_environment = eval_context->lexical_environment;
@ -147,19 +153,12 @@ ThrowCompletionOr<Value> perform_shadow_realm_eval(VM& vm, StringView source_tex
// 11. If result.[[Type]] is normal, then
if (!eval_result.is_throw_completion()) {
// a. Set result to the result of evaluating body.
auto maybe_executable = Bytecode::compile(vm, program, FunctionKind::Normal, "ShadowRealmEval"_fly_string);
if (maybe_executable.is_error()) {
result = maybe_executable.release_error();
auto result_and_return_register = vm.bytecode_interpreter().run_executable(*executable, {});
if (result_and_return_register.value.is_error()) {
result = result_and_return_register.value.release_error();
} else {
auto executable = maybe_executable.release_value();
auto result_and_return_register = vm.bytecode_interpreter().run_executable(*executable, {});
if (result_and_return_register.value.is_error()) {
result = result_and_return_register.value.release_error();
} else {
// Resulting value is in the accumulator.
result = result_and_return_register.return_register_value.is_special_empty_value() ? js_undefined() : result_and_return_register.return_register_value;
}
// Resulting value is in the accumulator.
result = result_and_return_register.return_register_value.is_special_empty_value() ? js_undefined() : result_and_return_register.return_register_value;
}
}
@ -195,7 +194,7 @@ ThrowCompletionOr<Value> shadow_realm_import_value(VM& vm, String specifier_stri
auto& realm = *vm.current_realm();
// 1. Let evalContext be GetShadowRealmContext(evalRealm, true).
auto eval_context = get_shadow_realm_context(eval_realm, true);
auto eval_context = get_shadow_realm_context(eval_realm, true, 0);
// 2. Let innerCapability be ! NewPromiseCapability(%Promise%).
auto inner_capability = MUST(new_promise_capability(vm, realm.intrinsics().promise_constructor()));
@ -288,7 +287,7 @@ ThrowCompletionOr<Value> get_wrapped_value(VM& vm, Realm& caller_realm, Value va
}
// 3.1.7 GetShadowRealmContext ( shadowRealmRecord, strictEval ), https://tc39.es/proposal-shadowrealm/#sec-getshadowrealmcontext
NonnullOwnPtr<ExecutionContext> get_shadow_realm_context(Realm& shadow_realm, bool strict_eval)
NonnullOwnPtr<ExecutionContext> get_shadow_realm_context(Realm& shadow_realm, bool strict_eval, u32 registers_and_constants_and_locals_count)
{
// 1. Let lexEnv be NewDeclarativeEnvironment(shadowRealmRecord.[[GlobalEnv]]).
Environment* lexical_environment = new_declarative_environment(shadow_realm.global_environment()).ptr();
@ -301,7 +300,7 @@ NonnullOwnPtr<ExecutionContext> get_shadow_realm_context(Realm& shadow_realm, bo
variable_environment = lexical_environment;
// 4. Let context be a new ECMAScript code execution context.
auto context = ExecutionContext::create();
auto context = ExecutionContext::create(registers_and_constants_and_locals_count);
// 5. Set context's Function to null.
context->function = nullptr;

View file

@ -37,6 +37,6 @@ ThrowCompletionOr<void> copy_name_and_length(VM&, FunctionObject& function, Func
ThrowCompletionOr<Value> perform_shadow_realm_eval(VM&, StringView source_text, Realm& caller_realm, Realm& eval_realm);
ThrowCompletionOr<Value> shadow_realm_import_value(VM&, String specifier_string, String export_name_string, Realm& caller_realm, Realm& eval_realm);
ThrowCompletionOr<Value> get_wrapped_value(VM&, Realm& caller_realm, Value);
NonnullOwnPtr<ExecutionContext> get_shadow_realm_context(Realm& shadow_realm, bool strict_eval);
NonnullOwnPtr<ExecutionContext> get_shadow_realm_context(Realm& shadow_realm, bool strict_eval, u32 registers_and_constants_and_locals_count);
}

View file

@ -62,7 +62,7 @@ ThrowCompletionOr<Value> WrappedFunction::internal_call(Value this_argument, Rea
// NOTE: No-op, kept by the VM in its execution context stack.
// 2. Let calleeContext be PrepareForWrappedFunctionCall(F).
auto callee_context = ExecutionContext::create();
auto callee_context = ExecutionContext::create(0);
prepare_for_wrapped_function_call(*this, *callee_context);
// 3. Assert: calleeContext is now the running execution context.