ladybird/Libraries/LibJS/SourceTextModule.h
Andreas Kling 6ecfcd3e68 LibWeb+LibJS: Cache decoded JS bytecode sidecars
Add a ref-counted decoded bytecode cache backing so bytecode cache
materialization can create fresh script or module records from a shared
decoded sidecar without passing around one-shot raw blob ownership.

Keep that backing in ExecutableBacking for records materialized from
bytecode cache sidecars, so the immutable decoded data stays alive for
as long as the installed record needs it.

Cover the shared backing path with a bytecode-cache test that
materializes and runs two scripts from one decoded backing.
2026-06-06 09:15:09 +02:00

107 lines
5.6 KiB
C++

/*
* Copyright (c) 2021-2023, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/NonnullRefPtr.h>
#include <LibJS/CyclicModule.h>
#include <LibJS/ExecutableBacking.h>
#include <LibJS/Export.h>
#include <LibJS/Forward.h>
#include <LibJS/ModuleEntry.h>
#include <LibJS/Runtime/ExecutionContext.h>
namespace JS {
namespace FFI {
struct ParsedProgram;
struct CompiledProgram;
struct DecodedBytecodeCacheBlob;
}
namespace RustIntegration {
class DecodedBytecodeCache;
}
// 16.2.1.6 Source Text Module Records, https://tc39.es/ecma262/#sec-source-text-module-records
class JS_API SourceTextModule final : public CyclicModule {
GC_CELL(SourceTextModule, CyclicModule);
GC_DECLARE_ALLOCATOR(SourceTextModule);
public:
virtual ~SourceTextModule() override;
static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> parse(StringView source_text, Realm&, StringView filename = {}, Script::HostDefined* host_defined = nullptr);
static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> parse_from_pre_parsed(FFI::ParsedProgram* parsed, NonnullRefPtr<SourceCode const> source_code, Realm&, Script::HostDefined* host_defined = nullptr);
static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> parse_from_pre_compiled(FFI::CompiledProgram* compiled, NonnullRefPtr<SourceCode const> source_code, Realm&, Script::HostDefined* host_defined = nullptr);
static Result<GC::Ref<SourceTextModule>, Vector<ParserError>> parse_from_bytecode_cache(NonnullRefPtr<RustIntegration::DecodedBytecodeCache>, NonnullRefPtr<SourceCode const> source_code, Realm&, Script::HostDefined* host_defined = nullptr);
virtual Vector<Utf16FlyString> get_exported_names(VM& vm, GC::RootHashTable<GC::Ref<Module const>>& export_star_set) override;
virtual ResolvedBinding resolve_export(VM& vm, Utf16FlyString const& export_name, Vector<ResolvedBinding> resolve_set = {}) override;
Object* import_meta() { return m_import_meta; }
void set_import_meta(Badge<VM>, Object* import_meta) { m_import_meta = import_meta; }
// Pre-computed module declaration instantiation data.
// These are extracted from the AST at construction time so that
// initialize_environment() can run without walking the AST.
struct FunctionToInitialize {
GC::Ref<SharedFunctionInstanceData> shared_data;
Utf16FlyString name;
};
struct LexicalBinding {
Utf16FlyString name;
bool is_constant { false };
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; }
ExecutableBacking const& executable_backing() const { return m_executable_backing; }
[[nodiscard]] bool can_generate_bytecode_cache() const;
[[nodiscard]] bool can_install_generated_bytecode_cache() const;
void begin_bytecode_cache_generation();
void finish_bytecode_cache_generation_without_install();
bool try_install_bytecode_cache(NonnullRefPtr<RustIntegration::DecodedBytecodeCache>, NonnullRefPtr<SourceCode const> source_code);
void install_generated_bytecode_cache(NonnullRefPtr<RustIntegration::DecodedBytecodeCache>, NonnullRefPtr<SourceCode const> source_code);
protected:
virtual ThrowCompletionOr<void> initialize_environment(VM& vm) override;
virtual ThrowCompletionOr<void> execute_module(VM& vm, GC::Ptr<PromiseCapability> capability) override;
private:
SourceTextModule(Realm&, StringView filename, Script::HostDefined* host_defined, bool has_top_level_await, Vector<ModuleRequest> requested_modules, Vector<ImportEntry> import_entries, Vector<ExportEntry> local_export_entries, Vector<ExportEntry> indirect_export_entries, Vector<ExportEntry> star_export_entries, Optional<Utf16FlyString> default_export_binding_name, Vector<Utf16FlyString> var_declared_names, Vector<LexicalBinding> lexical_bindings, Vector<FunctionToInitialize> functions_to_initialize, Vector<GC::Root<SharedFunctionInstanceData>> shared_function_data, GC::Ptr<Bytecode::Executable> executable, GC::Ptr<SharedFunctionInstanceData> tla_shared_data, ExecutableBacking);
virtual void visit_edges(Cell::Visitor&) override;
virtual size_t external_memory_size() const override;
Vector<SharedFunctionInstanceData*> collect_shared_function_data();
void complete_bytecode_cache_install(GC::Ptr<Bytecode::Executable>, GC::Ptr<Bytecode::Executable> top_level_await_executable, NonnullRefPtr<RustIntegration::DecodedBytecodeCache>);
void verify_executable_backing_invariants();
NonnullOwnPtr<ExecutionContext> m_execution_context; // [[Context]]
GC::Ptr<Object> m_import_meta; // [[ImportMeta]]
Vector<ImportEntry> m_import_entries; // [[ImportEntries]]
Vector<ExportEntry> m_local_export_entries; // [[LocalExportEntries]]
Vector<ExportEntry> m_indirect_export_entries; // [[IndirectExportEntries]]
Vector<ExportEntry> m_star_export_entries; // [[StarExportEntries]]
Vector<Utf16FlyString> m_var_declared_names;
Vector<LexicalBinding> m_lexical_bindings;
Vector<FunctionToInitialize> m_functions_to_initialize;
Optional<Utf16FlyString> m_default_export_binding_name;
SharedFunctionInstanceDataList m_shared_function_data;
GC::Ptr<Bytecode::Executable> m_executable;
GC::Ptr<SharedFunctionInstanceData> m_tla_shared_data;
ExecutableBacking m_executable_backing;
};
}