mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-06-24 18:30:23 +00:00
Keep fully compiled function bytecode in its Rust-side form until the function is called for the first time. This covers decoded disk cache records and freshly precompiled bytecode, so startup avoids eagerly allocating every nested function executable. Validate cached function bytecode before accepting a cache entry. This keeps the existing failure behavior for corrupt on-disk cache data. Add coverage for bytecode-cache and freshly precompiled functions to assert that nested executables stay absent after script materialization, then appear after the function is called.
127 lines
4.6 KiB
C++
127 lines
4.6 KiB
C++
/*
|
|
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibJS/Runtime/ExternalMemory.h>
|
|
#include <LibJS/Runtime/SharedFunctionInstanceData.h>
|
|
#include <LibJS/RustIntegration.h>
|
|
|
|
namespace JS {
|
|
|
|
GC_DEFINE_ALLOCATOR(SharedFunctionInstanceData);
|
|
|
|
SharedFunctionInstanceData::SharedFunctionInstanceData(
|
|
VM&,
|
|
FunctionKind kind,
|
|
Utf16FlyString name,
|
|
i32 function_length,
|
|
u32 formal_parameter_count,
|
|
bool strict,
|
|
bool is_arrow_function,
|
|
bool has_simple_parameter_list,
|
|
Vector<Utf16FlyString> parameter_names_for_mapped_arguments,
|
|
void* rust_function_ast)
|
|
: m_name(move(name))
|
|
, m_function_length(function_length)
|
|
, m_formal_parameter_count(formal_parameter_count)
|
|
, m_parameter_names_for_mapped_arguments(move(parameter_names_for_mapped_arguments))
|
|
, m_kind(kind)
|
|
, m_strict(strict)
|
|
, m_is_arrow_function(is_arrow_function)
|
|
, m_has_simple_parameter_list(has_simple_parameter_list)
|
|
, m_rust_function_ast(rust_function_ast)
|
|
, m_use_rust_compilation(true)
|
|
{
|
|
if (m_is_arrow_function)
|
|
m_this_mode = ThisMode::Lexical;
|
|
else if (m_strict)
|
|
m_this_mode = ThisMode::Strict;
|
|
else
|
|
m_this_mode = ThisMode::Global;
|
|
|
|
update_can_inline_call();
|
|
}
|
|
|
|
void SharedFunctionInstanceData::visit_edges(Visitor& visitor)
|
|
{
|
|
Base::visit_edges(visitor);
|
|
visitor.visit(m_executable);
|
|
for (auto& function : m_functions_to_initialize)
|
|
visitor.visit(function.shared_data);
|
|
m_class_field_initializer_name.visit([&](PropertyKey const& key) { key.visit_edges(visitor); }, [](auto&) {});
|
|
}
|
|
|
|
size_t SharedFunctionInstanceData::external_memory_size() const
|
|
{
|
|
size_t size = utf16_string_external_memory_size(m_source_text_owner);
|
|
size = saturating_add_external_memory_size(size, vector_external_memory_size(m_local_variables_names));
|
|
size = saturating_add_external_memory_size(size, vector_external_memory_size(m_parameter_names_for_mapped_arguments));
|
|
size = saturating_add_external_memory_size(size, hash_map_external_memory_size(m_parameter_names));
|
|
size = saturating_add_external_memory_size(size, vector_external_memory_size(m_functions_to_initialize));
|
|
size = saturating_add_external_memory_size(size, vector_external_memory_size(m_var_names_to_initialize_binding));
|
|
size = saturating_add_external_memory_size(size, vector_external_memory_size(m_function_names_to_initialize_binding));
|
|
size = saturating_add_external_memory_size(size, vector_external_memory_size(m_lexical_bindings));
|
|
return size;
|
|
}
|
|
|
|
SharedFunctionInstanceData::~SharedFunctionInstanceData() = default;
|
|
|
|
void SharedFunctionInstanceData::set_executable(GC::Ptr<Bytecode::Executable> executable)
|
|
{
|
|
m_executable = executable;
|
|
update_can_inline_call();
|
|
}
|
|
|
|
void SharedFunctionInstanceData::set_is_class_constructor()
|
|
{
|
|
m_is_class_constructor = true;
|
|
update_can_inline_call();
|
|
}
|
|
|
|
void SharedFunctionInstanceData::update_asm_call_metadata()
|
|
{
|
|
m_asm_call_metadata = m_formal_parameter_count;
|
|
if (m_can_inline_call)
|
|
m_asm_call_metadata |= asm_call_metadata_can_inline_call;
|
|
if (m_function_environment_needed || m_this_value_needs_environment_resolution)
|
|
m_asm_call_metadata |= asm_call_metadata_needs_environment_or_this_value_resolution;
|
|
if (m_uses_this)
|
|
m_asm_call_metadata |= asm_call_metadata_uses_this;
|
|
if (m_strict)
|
|
m_asm_call_metadata |= asm_call_metadata_strict;
|
|
}
|
|
|
|
void SharedFunctionInstanceData::finalize()
|
|
{
|
|
Base::finalize();
|
|
RustIntegration::free_function_ast(m_rust_function_ast);
|
|
m_rust_function_ast = nullptr;
|
|
RustIntegration::free_cached_bytecode_executable(m_cached_bytecode_executable);
|
|
m_cached_bytecode_executable = nullptr;
|
|
RustIntegration::free_precompiled_bytecode_executable(m_precompiled_bytecode_executable);
|
|
m_precompiled_bytecode_executable = nullptr;
|
|
}
|
|
|
|
void SharedFunctionInstanceData::clear_compile_inputs()
|
|
{
|
|
VERIFY(m_executable);
|
|
m_functions_to_initialize.clear();
|
|
m_var_names_to_initialize_binding.clear();
|
|
m_lexical_bindings.clear();
|
|
RustIntegration::free_function_ast(m_rust_function_ast);
|
|
m_rust_function_ast = nullptr;
|
|
RustIntegration::free_cached_bytecode_executable(m_cached_bytecode_executable);
|
|
m_cached_bytecode_executable = nullptr;
|
|
RustIntegration::free_precompiled_bytecode_executable(m_precompiled_bytecode_executable);
|
|
m_precompiled_bytecode_executable = nullptr;
|
|
}
|
|
|
|
void SharedFunctionInstanceData::update_can_inline_call()
|
|
{
|
|
m_can_inline_call = m_executable && m_kind == FunctionKind::Normal && !m_is_class_constructor;
|
|
update_asm_call_metadata();
|
|
}
|
|
|
|
}
|