Cache EnvironmentShape on SharedFunctionInstanceData after the first function environment creates its statically known bindings. Install the cached shape before later calls create bindings so those environments avoid building their own name vector and lookup map. Let DeclarativeEnvironment keep a local overlay for eval-created bindings, and keep the packed flag mirror per environment while the asm interpreter still reads flags directly from the environment. Add runtime coverage for repeated calls with an eval-created binding.
128 lines
4.6 KiB
C++
128 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);
|
|
visitor.visit(m_function_environment_shape);
|
|
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();
|
|
}
|
|
|
|
}
|