diff --git a/Libraries/LibJS/Bytecode/Interpreter.cpp b/Libraries/LibJS/Bytecode/Interpreter.cpp index dc820e62d2..a94210d557 100644 --- a/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -311,8 +311,8 @@ NEVER_INLINE Interpreter::HandleExceptionResponse Interpreter::handle_exception( auto& handler = handlers->handler_offset; auto& finalizer = handlers->finalizer_offset; - VERIFY(!running_execution_context().unwind_contexts.is_empty()); - auto& unwind_context = running_execution_context().unwind_contexts.last(); + auto& unwind_contexts = running_execution_context().ensure_rare_data()->unwind_contexts; + auto& unwind_context = unwind_contexts.last(); VERIFY(unwind_context.executable == ¤t_executable()); if (handler.has_value()) { @@ -485,8 +485,8 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point) do_return(saved_return_value()); if (auto handlers = executable.exception_handlers_for_offset(program_counter); handlers.has_value()) { if (auto finalizer = handlers.value().finalizer_offset; finalizer.has_value()) { - VERIFY(!running_execution_context.unwind_contexts.is_empty()); - auto& unwind_context = running_execution_context.unwind_contexts.last(); + auto& unwind_contexts = running_execution_context.ensure_rare_data()->unwind_contexts; + auto& unwind_context = unwind_contexts.last(); VERIFY(unwind_context.executable == ¤t_executable()); reg(Register::saved_return_value()) = reg(Register::return_value()); reg(Register::return_value()) = js_special_empty_value(); @@ -497,7 +497,7 @@ FLATTEN_ON_CLANG void Interpreter::run_bytecode(size_t entry_point) } return; } - auto const old_scheduled_jump = running_execution_context.previously_scheduled_jumps.take_last(); + auto const old_scheduled_jump = running_execution_context.ensure_rare_data()->previously_scheduled_jumps.take_last(); if (m_running_execution_context->scheduled_jump.has_value()) { program_counter = m_running_execution_context->scheduled_jump.value(); m_running_execution_context->scheduled_jump = {}; @@ -762,23 +762,23 @@ ThrowCompletionOr Interpreter::run_executable(ExecutionContext& context, void Interpreter::enter_unwind_context() { - running_execution_context().unwind_contexts.empend( + running_execution_context().ensure_rare_data()->unwind_contexts.empend( current_executable(), running_execution_context().lexical_environment); - running_execution_context().previously_scheduled_jumps.append(m_running_execution_context->scheduled_jump); + running_execution_context().rare_data()->previously_scheduled_jumps.append(m_running_execution_context->scheduled_jump); m_running_execution_context->scheduled_jump = {}; } void Interpreter::leave_unwind_context() { - running_execution_context().unwind_contexts.take_last(); + running_execution_context().rare_data()->unwind_contexts.take_last(); } void Interpreter::catch_exception(Operand dst) { set(dst, reg(Register::exception())); reg(Register::exception()) = js_special_empty_value(); - auto& context = running_execution_context().unwind_contexts.last(); + auto& context = running_execution_context().rare_data()->unwind_contexts.last(); VERIFY(!context.handler_called); VERIFY(context.executable == ¤t_executable()); context.handler_called = true; @@ -787,19 +787,19 @@ void Interpreter::catch_exception(Operand dst) void Interpreter::restore_scheduled_jump() { - m_running_execution_context->scheduled_jump = running_execution_context().previously_scheduled_jumps.take_last(); + m_running_execution_context->scheduled_jump = running_execution_context().rare_data()->previously_scheduled_jumps.take_last(); } void Interpreter::leave_finally() { reg(Register::exception()) = js_special_empty_value(); - m_running_execution_context->scheduled_jump = running_execution_context().previously_scheduled_jumps.take_last(); + m_running_execution_context->scheduled_jump = running_execution_context().rare_data()->previously_scheduled_jumps.take_last(); } void Interpreter::enter_object_environment(Object& object) { auto& old_environment = running_execution_context().lexical_environment; - running_execution_context().saved_lexical_environments.append(old_environment); + running_execution_context().ensure_rare_data()->saved_lexical_environments.append(old_environment); running_execution_context().lexical_environment = new_object_environment(object, true, old_environment); } @@ -1593,7 +1593,7 @@ inline ThrowCompletionOr new_class(VM& vm, Value supe // NOTE: NewClass expects classEnv to be active lexical environment auto* class_environment = vm.lexical_environment(); - vm.running_execution_context().lexical_environment = vm.running_execution_context().saved_lexical_environments.take_last(); + vm.running_execution_context().lexical_environment = vm.running_execution_context().rare_data()->saved_lexical_environments.take_last(); Optional binding_name; Utf16FlyString class_name; @@ -2416,7 +2416,7 @@ void CreateLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) return environment; }; auto& running_execution_context = interpreter.running_execution_context(); - running_execution_context.saved_lexical_environments.append(make_and_swap_envs(running_execution_context.lexical_environment)); + running_execution_context.ensure_rare_data()->saved_lexical_environments.append(make_and_swap_envs(running_execution_context.lexical_environment)); if (m_dst.has_value()) interpreter.set(*m_dst, running_execution_context.lexical_environment); } @@ -3161,7 +3161,7 @@ ThrowCompletionOr ThrowIfTDZ::execute_impl(Bytecode::Interpreter& interpre void LeaveLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const { auto& running_execution_context = interpreter.running_execution_context(); - running_execution_context.lexical_environment = running_execution_context.saved_lexical_environments.take_last(); + running_execution_context.lexical_environment = running_execution_context.rare_data()->saved_lexical_environments.take_last(); } void LeavePrivateEnvironment::execute_impl(Bytecode::Interpreter& interpreter) const diff --git a/Libraries/LibJS/Runtime/ExecutionContext.cpp b/Libraries/LibJS/Runtime/ExecutionContext.cpp index 51ce3fc11b..35cc57f65e 100644 --- a/Libraries/LibJS/Runtime/ExecutionContext.cpp +++ b/Libraries/LibJS/Runtime/ExecutionContext.cpp @@ -15,6 +15,7 @@ namespace JS { GC_DEFINE_ALLOCATOR(CachedSourceRange); +GC_DEFINE_ALLOCATOR(ExecutionContextRareData); class ExecutionContextAllocator { public: @@ -110,10 +111,6 @@ ExecutionContext::ExecutionContext(u32 registers_and_constants_and_locals_count, arguments = { registers_and_constants_and_locals_and_arguments + registers_and_constants_and_locals_count, arguments_count }; } -ExecutionContext::~ExecutionContext() -{ -} - NonnullOwnPtr ExecutionContext::copy() const { auto copy = create(registers_and_constants_and_locals_and_arguments_count, arguments.size()); @@ -127,9 +124,12 @@ NonnullOwnPtr ExecutionContext::copy() const copy->this_value = this_value; copy->executable = executable; copy->passed_argument_count = passed_argument_count; - copy->unwind_contexts = unwind_contexts; - copy->saved_lexical_environments = saved_lexical_environments; - copy->previously_scheduled_jumps = previously_scheduled_jumps; + if (m_rare_data) { + auto copy_rare_data = copy->ensure_rare_data(); + copy_rare_data->unwind_contexts = m_rare_data->unwind_contexts; + copy_rare_data->saved_lexical_environments = m_rare_data->saved_lexical_environments; + copy_rare_data->previously_scheduled_jumps = m_rare_data->previously_scheduled_jumps; + } copy->registers_and_constants_and_locals_and_arguments_count = registers_and_constants_and_locals_and_arguments_count; for (size_t i = 0; i < registers_and_constants_and_locals_and_arguments_count; ++i) copy->registers_and_constants_and_locals_and_arguments()[i] = registers_and_constants_and_locals_and_arguments()[i]; @@ -146,14 +146,11 @@ void ExecutionContext::visit_edges(Cell::Visitor& visitor) visitor.visit(private_environment); visitor.visit(context_owner); visitor.visit(cached_source_range); + visitor.visit(m_rare_data); if (this_value.has_value()) visitor.visit(*this_value); visitor.visit(executable); visitor.visit(registers_and_constants_and_locals_and_arguments_span()); - for (auto& context : unwind_contexts) { - visitor.visit(context.lexical_environment); - } - visitor.visit(saved_lexical_environments); script_or_module.visit( [](Empty) {}, [&](auto& script_or_module) { @@ -161,4 +158,21 @@ void ExecutionContext::visit_edges(Cell::Visitor& visitor) }); } +void ExecutionContextRareData::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + for (auto& context : unwind_contexts) { + visitor.visit(context.lexical_environment); + } + visitor.visit(saved_lexical_environments); +} + +GC::Ref ExecutionContext::ensure_rare_data() +{ + if (!m_rare_data) { + m_rare_data = executable->heap().allocate(); + } + return *m_rare_data; +} + } diff --git a/Libraries/LibJS/Runtime/ExecutionContext.h b/Libraries/LibJS/Runtime/ExecutionContext.h index 9018f6cbf0..8163ae98e0 100644 --- a/Libraries/LibJS/Runtime/ExecutionContext.h +++ b/Libraries/LibJS/Runtime/ExecutionContext.h @@ -36,12 +36,25 @@ public: Variant source_range; }; +class ExecutionContextRareData final : public GC::Cell { + GC_CELL(ExecutionContextRareData, GC::Cell); + GC_DECLARE_ALLOCATOR(ExecutionContextRareData); + +public: + Vector unwind_contexts; + Vector> previously_scheduled_jumps; + Vector> saved_lexical_environments; + +private: + virtual void visit_edges(Cell::Visitor&) override; +}; + // 9.4 Execution Contexts, https://tc39.es/ecma262/#sec-execution-contexts struct JS_API ExecutionContext { static NonnullOwnPtr create(u32 registers_and_constants_and_locals_count, u32 arguments_count); [[nodiscard]] NonnullOwnPtr copy() const; - ~ExecutionContext(); + ~ExecutionContext() = default; void visit_edges(Cell::Visitor&); @@ -51,6 +64,9 @@ private: public: ExecutionContext(u32 registers_and_constants_and_locals_count, u32 arguments_count); + GC::Ptr rare_data() const { return m_rare_data; } + GC::Ref ensure_rare_data(); + void operator delete(void* ptr); GC::Ptr function; // [[Function]] @@ -105,9 +121,9 @@ public: Span arguments; - Vector unwind_contexts; - Vector> previously_scheduled_jumps; - Vector> saved_lexical_environments; + // NOTE: Rarely used data members go here to keep the size of ExecutionContext down, + // and to avoid needing an ExecutionContext destructor in the common case. + GC::Ptr m_rare_data; u32 passed_argument_count { 0 }; @@ -122,21 +138,19 @@ private: u32 registers_and_constants_and_locals_and_arguments_count { 0 }; }; -#define ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK_WITHOUT_CLEARING_ARGS(execution_context, \ - registers_and_constants_and_locals_count, \ - arguments_count) \ - auto execution_context_size = sizeof(JS::ExecutionContext) \ - + (((registers_and_constants_and_locals_count) + (arguments_count)) \ - * sizeof(JS::Value)); \ - \ - void* execution_context_memory = alloca(execution_context_size); \ - \ - execution_context = new (execution_context_memory) \ - JS::ExecutionContext((registers_and_constants_and_locals_count), (arguments_count)); \ - \ - ScopeGuard run_execution_context_destructor([execution_context] { \ - execution_context->~ExecutionContext(); \ - }) +static_assert(IsTriviallyDestructible); + +#define ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK_WITHOUT_CLEARING_ARGS(execution_context, \ + registers_and_constants_and_locals_count, \ + arguments_count) \ + auto execution_context_size = sizeof(JS::ExecutionContext) \ + + (((registers_and_constants_and_locals_count) + (arguments_count)) \ + * sizeof(JS::Value)); \ + \ + void* execution_context_memory = alloca(execution_context_size); \ + \ + execution_context = new (execution_context_memory) \ + JS::ExecutionContext((registers_and_constants_and_locals_count), (arguments_count)); #define ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK(execution_context, registers_and_constants_and_locals_count, \ arguments_count) \