diff --git a/Libraries/LibJS/SourceTextModule.h b/Libraries/LibJS/SourceTextModule.h index 9baaa2f9703..b9c3e081106 100644 --- a/Libraries/LibJS/SourceTextModule.h +++ b/Libraries/LibJS/SourceTextModule.h @@ -53,6 +53,9 @@ public: i32 function_index { -1 }; // index into m_functions_to_initialize, -1 if not a function }; + Bytecode::Executable* cached_executable() const { return m_executable; } + SharedFunctionInstanceData* top_level_await_shared_data() const { return m_tla_shared_data; } + protected: virtual ThrowCompletionOr initialize_environment(VM& vm) override; virtual ThrowCompletionOr execute_module(VM& vm, GC::Ptr capability) override; diff --git a/Libraries/LibWeb/HTML/Scripting/Fetching.cpp b/Libraries/LibWeb/HTML/Scripting/Fetching.cpp index 5cc2fc2d6de..a570872ad27 100644 --- a/Libraries/LibWeb/HTML/Scripting/Fetching.cpp +++ b/Libraries/LibWeb/HTML/Scripting/Fetching.cpp @@ -10,7 +10,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -44,6 +46,87 @@ struct OffThreadCompiledProgram { JS::FFI::CompiledProgram* compiled { nullptr }; }; +static void compile_remaining_functions_off_thread(JS::Bytecode::Executable& executable, NonnullRefPtr source_code) +{ + Vector> shared_data_roots; + Vector function_asts; + + for (auto& shared_data : executable.shared_function_data) { + if (!shared_data || shared_data->m_executable || !shared_data->m_rust_function_ast) + continue; + + auto* cloned_ast = JS::RustIntegration::clone_function_ast(shared_data->m_rust_function_ast); + if (!cloned_ast) + continue; + + shared_data_roots.append(GC::make_root(*shared_data)); + function_asts.append(cloned_ast); + } + + if (function_asts.is_empty()) + return; + + auto length = source_code->length_in_code_units(); + auto* callback = new Function)>( + [shared_data_roots = move(shared_data_roots), source_code = move(source_code)](Vector compiled_functions) mutable { + VERIFY(compiled_functions.size() == shared_data_roots.size()); + auto& vm = Bindings::main_thread_vm(); + for (size_t i = 0; i < compiled_functions.size(); ++i) { + auto* compiled_function = compiled_functions[i]; + if (!compiled_function) + continue; + + auto& shared_data = *shared_data_roots[i]; + if (shared_data.m_executable) { + compile_remaining_functions_off_thread(*shared_data.m_executable, source_code); + JS::RustIntegration::free_compiled_function(compiled_function); + continue; + } + + JS::RustIntegration::materialize_compiled_function(compiled_function, vm, *source_code, shared_data); + } + }); + + auto event_loop_weak = Core::EventLoop::current_weak(); + + Threading::ThreadPool::the().submit([function_asts = move(function_asts), length, + callback, + event_loop_weak = move(event_loop_weak)]() mutable { + Vector compiled_functions; + compiled_functions.ensure_capacity(function_asts.size()); + for (auto* function_ast : function_asts) + compiled_functions.append(JS::RustIntegration::compile_function_off_thread(function_ast, length, false)); + + auto origin = event_loop_weak->take(); + if (!origin) + return; + origin->deferred_invoke([compiled_functions = move(compiled_functions), callback]() mutable { + (*callback)(move(compiled_functions)); + delete callback; + }); + }); +} + +static void compile_remaining_module_functions_off_thread(ModuleScript& module_script, NonnullRefPtr source_code) +{ + module_script.record().visit( + [](Empty) {}, + [](GC::Ref) {}, + [](GC::Ref) {}, + [source_code = move(source_code)](GC::Ref module) mutable { + if (auto* executable = module->cached_executable()) { + compile_remaining_functions_off_thread(*executable, source_code); + return; + } + + auto* top_level_await_shared_data = module->top_level_await_shared_data(); + if (!top_level_await_shared_data || !top_level_await_shared_data->m_executable) + return; + + compile_remaining_functions_off_thread(*top_level_await_shared_data->m_executable, source_code); + }); +} + // Submit parsing and top-level bytecode generation to the thread pool, then bounce back to the main thread via // deferred_invoke once the worker is done. Syntax errors still come back as a ParsedProgram so the main thread can // report them through the same Script/ModuleScript construction paths; successful programs come back as CompiledProgram @@ -448,9 +531,14 @@ void fetch_classic_script(GC::Ref element, URL::URL const& ur [response_url = move(response_url), response_url_string = move(response_url_string), muted_errors, on_complete_root = move(on_complete_root), settings_root = move(settings_root)](auto result, auto source_code) mutable { + auto source_code_for_background_compile = source_code; auto script = result.compiled ? ClassicScript::create_from_pre_compiled(move(response_url_string), move(source_code), *settings_root, move(response_url), result.compiled, muted_errors) : ClassicScript::create_from_pre_parsed(move(response_url_string), move(source_code), *settings_root, move(response_url), result.parsed, muted_errors); + if (auto* script_record = script->script_record()) { + if (auto* executable = script_record->cached_executable()) + compile_remaining_functions_off_thread(*executable, move(source_code_for_background_compile)); + } on_complete_root->function()(script); }); } else { @@ -811,9 +899,12 @@ void fetch_single_module_script(JS::Realm& realm, module_type_string = move(module_type_string), on_complete_root = move(on_complete_root), settings_root = move(settings_root)](auto result, auto source_code) mutable { + auto source_code_for_background_compile = source_code; auto module_script = result.compiled ? ModuleScript::create_from_pre_compiled(url_string, move(source_code), *settings_root, move(response_url), result.compiled).release_value_but_fixme_should_propagate_errors() : ModuleScript::create_from_pre_parsed(url_string, move(source_code), *settings_root, move(response_url), result.parsed).release_value_but_fixme_should_propagate_errors(); + if (module_script) + compile_remaining_module_functions_off_thread(*module_script, move(source_code_for_background_compile)); settings_root->module_map().set(url, module_type_string, { ModuleMap::EntryType::ModuleScript, module_script }); on_complete_root->function()(module_script); });