mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-29 07:48:47 +00:00
LibJS/Bytecode: Leave GlobalDeclarationInstantiation in C++
Don't try to implement this AO in bytecode. Instead, the bytecode Interpreter class now has a run() API with the same inputs as the AST interpreter. It sets up the necessary environments etc, including invoking the GlobalDeclarationInstantiation AO.
This commit is contained in:
parent
32d9c8e3ca
commit
d063f35afd
Notes:
sideshowbarker
2024-07-18 01:43:16 +09:00
Author: https://github.com/awesomekling
Commit: d063f35afd
Pull-request: https://github.com/SerenityOS/serenity/pull/19408
Issue: https://github.com/SerenityOS/serenity/issues/15210
12 changed files with 172 additions and 284 deletions
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
#include <LibJS/AST.h>
|
||||
#include <LibJS/Bytecode/BasicBlock.h>
|
||||
#include <LibJS/Bytecode/Instruction.h>
|
||||
#include <LibJS/Bytecode/Interpreter.h>
|
||||
|
@ -39,6 +40,141 @@ Interpreter::~Interpreter()
|
|||
s_current = nullptr;
|
||||
}
|
||||
|
||||
// 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation
|
||||
ThrowCompletionOr<Value> Interpreter::run(Script& script_record, JS::GCPtr<Environment> lexical_environment_override)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 1. Let globalEnv be scriptRecord.[[Realm]].[[GlobalEnv]].
|
||||
auto& global_environment = script_record.realm().global_environment();
|
||||
|
||||
// 2. Let scriptContext be a new ECMAScript code execution context.
|
||||
ExecutionContext script_context(vm.heap());
|
||||
|
||||
// 3. Set the Function of scriptContext to null.
|
||||
// NOTE: This was done during execution context construction.
|
||||
|
||||
// 4. Set the Realm of scriptContext to scriptRecord.[[Realm]].
|
||||
script_context.realm = &script_record.realm();
|
||||
|
||||
// 5. Set the ScriptOrModule of scriptContext to scriptRecord.
|
||||
script_context.script_or_module = NonnullGCPtr<Script>(script_record);
|
||||
|
||||
// 6. Set the VariableEnvironment of scriptContext to globalEnv.
|
||||
script_context.variable_environment = &global_environment;
|
||||
|
||||
// 7. Set the LexicalEnvironment of scriptContext to globalEnv.
|
||||
script_context.lexical_environment = &global_environment;
|
||||
|
||||
// Non-standard: Override the lexical environment if requested.
|
||||
if (lexical_environment_override)
|
||||
script_context.lexical_environment = lexical_environment_override;
|
||||
|
||||
// 8. Set the PrivateEnvironment of scriptContext to null.
|
||||
|
||||
// NOTE: This isn't in the spec, but we require it.
|
||||
script_context.is_strict_mode = script_record.parse_node().is_strict_mode();
|
||||
|
||||
// FIXME: 9. Suspend the currently running execution context.
|
||||
|
||||
// 10. Push scriptContext onto the execution context stack; scriptContext is now the running execution context.
|
||||
TRY(vm.push_execution_context(script_context, {}));
|
||||
|
||||
// 11. Let script be scriptRecord.[[ECMAScriptCode]].
|
||||
auto& script = script_record.parse_node();
|
||||
|
||||
// 12. Let result be Completion(GlobalDeclarationInstantiation(script, globalEnv)).
|
||||
auto instantiation_result = script.global_declaration_instantiation(vm, global_environment);
|
||||
Completion result = instantiation_result.is_throw_completion() ? instantiation_result.throw_completion() : normal_completion({});
|
||||
|
||||
// 13. If result.[[Type]] is normal, then
|
||||
if (result.type() == Completion::Type::Normal) {
|
||||
auto executable_result = JS::Bytecode::Generator::generate(script);
|
||||
|
||||
if (executable_result.is_error()) {
|
||||
if (auto error_string = executable_result.error().to_string(); error_string.is_error())
|
||||
result = vm.template throw_completion<JS::InternalError>(vm.error_message(JS::VM::ErrorMessage::OutOfMemory));
|
||||
else if (error_string = String::formatted("TODO({})", error_string.value()); error_string.is_error())
|
||||
result = vm.template throw_completion<JS::InternalError>(vm.error_message(JS::VM::ErrorMessage::OutOfMemory));
|
||||
else
|
||||
result = JS::throw_completion(JS::InternalError::create(realm(), error_string.release_value()));
|
||||
} else {
|
||||
auto executable = executable_result.release_value();
|
||||
|
||||
if (m_optimizations_enabled) {
|
||||
auto& passes = optimization_pipeline();
|
||||
passes.perform(*executable);
|
||||
}
|
||||
|
||||
if (g_dump_bytecode)
|
||||
executable->dump();
|
||||
|
||||
// a. Set result to the result of evaluating script.
|
||||
auto result_or_error = run_and_return_frame(*executable, nullptr);
|
||||
if (result_or_error.value.is_error())
|
||||
result = result_or_error.value.release_error();
|
||||
else
|
||||
result = result_or_error.frame->registers[0];
|
||||
}
|
||||
}
|
||||
|
||||
// 14. If result.[[Type]] is normal and result.[[Value]] is empty, then
|
||||
if (result.type() == Completion::Type::Normal && !result.value().has_value()) {
|
||||
// a. Set result to NormalCompletion(undefined).
|
||||
result = normal_completion(js_undefined());
|
||||
}
|
||||
|
||||
// FIXME: 15. Suspend scriptContext and remove it from the execution context stack.
|
||||
vm.pop_execution_context();
|
||||
|
||||
// 16. Assert: The execution context stack is not empty.
|
||||
VERIFY(!vm.execution_context_stack().is_empty());
|
||||
|
||||
// FIXME: 17. Resume the context that is now on the top of the execution context stack as the running execution context.
|
||||
|
||||
// At this point we may have already run any queued promise jobs via on_call_stack_emptied,
|
||||
// in which case this is a no-op.
|
||||
// FIXME: These three should be moved out of Interpreter::run and give the host an option to run these, as it's up to the host when these get run.
|
||||
// https://tc39.es/ecma262/#sec-jobs for jobs and https://tc39.es/ecma262/#_ref_3508 for ClearKeptObjects
|
||||
// finish_execution_generation is particularly an issue for LibWeb, as the HTML spec wants to run it specifically after performing a microtask checkpoint.
|
||||
// The promise and registry cleanup queues don't cause LibWeb an issue, as LibWeb overrides the hooks that push onto these queues.
|
||||
vm.run_queued_promise_jobs();
|
||||
|
||||
vm.run_queued_finalization_registry_cleanup_jobs();
|
||||
|
||||
vm.finish_execution_generation();
|
||||
|
||||
// 18. Return ? result.
|
||||
if (result.is_abrupt()) {
|
||||
VERIFY(result.type() == Completion::Type::Throw);
|
||||
return result.release_error();
|
||||
}
|
||||
|
||||
VERIFY(result.value().has_value());
|
||||
return *result.value();
|
||||
}
|
||||
|
||||
ThrowCompletionOr<Value> Interpreter::run(SourceTextModule& module)
|
||||
{
|
||||
// FIXME: This is not a entry point as defined in the spec, but is convenient.
|
||||
// To avoid work we use link_and_eval_module however that can already be
|
||||
// dangerous if the vm loaded other modules.
|
||||
auto& vm = this->vm();
|
||||
|
||||
TRY(vm.link_and_eval_module(Badge<Bytecode::Interpreter> {}, module));
|
||||
|
||||
vm.run_queued_promise_jobs();
|
||||
|
||||
vm.run_queued_finalization_registry_cleanup_jobs();
|
||||
|
||||
return js_undefined();
|
||||
}
|
||||
|
||||
void Interpreter::set_optimizations_enabled(bool enabled)
|
||||
{
|
||||
m_optimizations_enabled = enabled;
|
||||
}
|
||||
|
||||
Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& executable, BasicBlock const* entry_point, RegisterWindow* in_frame)
|
||||
{
|
||||
dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter will run unit {:p}", &executable);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue