LibWeb+LibJS: Compile fetched top-level JS off-thread

Split Rust program compilation so code generation and assembly finish
before the main thread materializes GC-backed executable objects. The
new CompiledProgram handle owns the parsed program, generator state, and
bytecode until C++ consumes it on the main thread.

Wire WebContent script fetching through that handle for classic scripts
and modules. Syntax-error paths still return ParsedProgram, so existing
error reporting stays in place. Successful fetches now do top-level
codegen on the thread pool before deferred_invoke hands control back to
the main thread.

Executable creation, SharedFunctionInstanceData materialization, module
metadata extraction, and declaration data extraction still run on the
main thread where VM and GC access is valid.
This commit is contained in:
Andreas Kling
2026-04-26 14:53:34 +02:00
committed by Andreas Kling
parent 0d120019df
commit 4a7dc45b3f
Notes: github-actions[bot] 2026-04-26 20:31:58 +00:00
12 changed files with 496 additions and 87 deletions

View File

@@ -83,6 +83,29 @@ Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse_f
module_result.executable.ptr(), module_result.tla_shared_data.ptr());
}
Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse_from_pre_compiled(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm& realm, Script::HostDefined* host_defined)
{
auto filename = source_code->filename();
auto rust_result = RustIntegration::materialize_compiled_module(compiled, move(source_code), realm);
// Always from the Rust pipeline, so the Optional must have a value.
VERIFY(rust_result.has_value());
if (rust_result->is_error())
return rust_result->release_error();
auto& module_result = rust_result->value();
Vector<FunctionToInitialize> functions_to_initialize;
functions_to_initialize.ensure_capacity(module_result.functions_to_initialize.size());
for (auto& f : module_result.functions_to_initialize)
functions_to_initialize.append({ *f.shared_data, move(f.name) });
return realm.heap().allocate<SourceTextModule>(
realm, filename, host_defined, module_result.has_top_level_await,
move(module_result.requested_modules), move(module_result.import_entries),
move(module_result.local_export_entries), move(module_result.indirect_export_entries),
move(module_result.star_export_entries), move(module_result.default_export_binding_name),
move(module_result.var_declared_names), move(module_result.lexical_bindings),
move(functions_to_initialize),
module_result.executable.ptr(), module_result.tla_shared_data.ptr());
}
// 16.2.1.7.1 ParseModule ( sourceText, realm, hostDefined ), https://tc39.es/ecma262/#sec-parsemodule
Result<GC::Ref<SourceTextModule>, Vector<ParserError>> SourceTextModule::parse(StringView source_text, Realm& realm, StringView filename, Script::HostDefined* host_defined)
{