From f89afe8e27701c19d781f1e64d39bcff7e9d198d Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 30 Aug 2025 16:26:25 +0200 Subject: [PATCH] LibJS: Allocate context up front in SuperCallWithArgumentArray This also removes the last user of Interpreter's argument buffer allocation API, which we've used to repeatedly shoot ourselves in the foot. Good-bye! --- Libraries/LibJS/Bytecode/Interpreter.cpp | 150 +++++++++++------------ Libraries/LibJS/Bytecode/Interpreter.h | 6 - 2 files changed, 73 insertions(+), 83 deletions(-) diff --git a/Libraries/LibJS/Bytecode/Interpreter.cpp b/Libraries/LibJS/Bytecode/Interpreter.cpp index 1a8f7d74cc5..85e300b1433 100644 --- a/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -1562,29 +1562,6 @@ inline Value new_regexp(VM& vm, ParsedRegex const& parsed_regex, Utf16String pat return regexp_object; } -// 13.3.8.1 https://tc39.es/ecma262/#sec-runtime-semantics-argumentlistevaluation -inline Span argument_list_evaluation(Interpreter& interpreter, Value arguments) -{ - // Note: Any spreading and actual evaluation is handled in preceding opcodes - // Note: The spec uses the concept of a list, while we create a temporary array - // in the preceding opcodes, so we have to convert in a manner that is not - // visible to the user - - auto& argument_array = arguments.as_array(); - auto array_length = argument_array.indexed_properties().array_like_size(); - - auto argument_values = interpreter.allocate_argument_values(array_length); - - for (size_t i = 0; i < array_length; ++i) { - if (auto maybe_value = argument_array.indexed_properties().get(i); maybe_value.has_value()) - argument_values[i] = maybe_value.release_value().value; - else - argument_values[i] = js_undefined(); - } - - return argument_values; -} - inline ThrowCompletionOr create_variable(VM& vm, Utf16FlyString const& name, Op::EnvironmentMode mode, bool is_global, bool is_immutable, bool is_strict) { if (mode == Op::EnvironmentMode::Lexical) { @@ -1631,59 +1608,6 @@ inline ThrowCompletionOr new_class(VM& vm, Value supe return TRY(class_expression.create_class_constructor(vm, class_environment, vm.lexical_environment(), super_class, element_keys, binding_name, class_name)); } -// 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation -inline ThrowCompletionOr> super_call_with_argument_array(Interpreter& interpreter, Value argument_array, bool is_synthetic) -{ - auto& vm = interpreter.vm(); - - // 1. Let newTarget be GetNewTarget(). - auto new_target = vm.get_new_target(); - - // 2. Assert: Type(newTarget) is Object. - VERIFY(new_target.is_object()); - - // 3. Let func be GetSuperConstructor(). - auto* func = get_super_constructor(vm); - - // 4. Let argList be ? ArgumentListEvaluation of Arguments. - Span arg_list; - if (is_synthetic) { - VERIFY(argument_array.is_object() && is(argument_array.as_object())); - auto const& array_value = static_cast(argument_array.as_object()); - auto length = MUST(length_of_array_like(vm, array_value)); - arg_list = interpreter.allocate_argument_values(length); - for (size_t i = 0; i < length; ++i) - arg_list[i] = array_value.get_without_side_effects(PropertyKey { i }); - } else { - arg_list = argument_list_evaluation(interpreter, argument_array); - } - - // 5. If IsConstructor(func) is false, throw a TypeError exception. - if (!Value(func).is_constructor()) - return vm.throw_completion(ErrorType::NotAConstructor, "Super constructor"); - - // 6. Let result be ? Construct(func, argList, newTarget). - auto result = TRY(construct(vm, static_cast(*func), arg_list, &new_target.as_function())); - - // 7. Let thisER be GetThisEnvironment(). - auto& this_environment = as(*get_this_environment(vm)); - - // 8. Perform ? thisER.BindThisValue(result). - TRY(this_environment.bind_this_value(vm, result)); - - // 9. Let F be thisER.[[FunctionObject]]. - auto& f = this_environment.function_object(); - - // 10. Assert: F is an ECMAScript function object. - // NOTE: This is implied by the strong C++ type. - - // 11. Perform ? InitializeInstanceElements(result, F). - TRY(result->initialize_instance_elements(f)); - - // 12. Return result. - return result; -} - inline ThrowCompletionOr> iterator_to_array(VM& vm, Value iterator) { auto& iterator_record = static_cast(iterator.as_cell()); @@ -2974,7 +2898,79 @@ ThrowCompletionOr CallConstructWithArgumentArray::execute_impl(Bytecode::I // 13.3.7.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation ThrowCompletionOr SuperCallWithArgumentArray::execute_impl(Bytecode::Interpreter& interpreter) const { - interpreter.set(dst(), TRY(super_call_with_argument_array(interpreter, interpreter.get(arguments()), m_is_synthetic))); + auto& vm = interpreter.vm(); + + // 1. Let newTarget be GetNewTarget(). + auto new_target = vm.get_new_target(); + + // 2. Assert: Type(newTarget) is Object. + VERIFY(new_target.is_object()); + + // 3. Let func be GetSuperConstructor(). + auto* func = get_super_constructor(vm); + + // NON-STANDARD: We're doing this step earlier to streamline control flow. + // 5. If IsConstructor(func) is false, throw a TypeError exception. + if (!Value(func).is_constructor()) + return vm.throw_completion(ErrorType::NotAConstructor, "Super constructor"); + + auto& function = static_cast(*func); + + // 4. Let argList be ? ArgumentListEvaluation of Arguments. + auto& argument_array = interpreter.get(m_arguments).as_array(); + size_t argument_array_length = 0; + + if (m_is_synthetic) { + argument_array_length = MUST(length_of_array_like(vm, argument_array)); + } else { + argument_array_length = argument_array.indexed_properties().array_like_size(); + } + + ExecutionContext* callee_context = nullptr; + size_t argument_count = argument_array_length; + size_t registers_and_constants_and_locals_count = 0; + TRY(function.get_stack_frame_size(registers_and_constants_and_locals_count, argument_count)); + ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK_WITHOUT_CLEARING_ARGS(callee_context, registers_and_constants_and_locals_count, max(argument_array_length, argument_count)); + + auto* callee_context_argument_values = callee_context->arguments.data(); + auto const callee_context_argument_count = callee_context->arguments.size(); + auto const insn_argument_count = argument_array_length; + + if (m_is_synthetic) { + for (size_t i = 0; i < insn_argument_count; ++i) + callee_context_argument_values[i] = argument_array.get_without_side_effects(PropertyKey { i }); + } else { + for (size_t i = 0; i < insn_argument_count; ++i) { + if (auto maybe_value = argument_array.indexed_properties().get(i); maybe_value.has_value()) + callee_context_argument_values[i] = maybe_value.release_value().value; + else + callee_context_argument_values[i] = js_undefined(); + } + } + for (size_t i = insn_argument_count; i < callee_context_argument_count; ++i) + callee_context_argument_values[i] = js_undefined(); + callee_context->passed_argument_count = insn_argument_count; + + // 6. Let result be ? Construct(func, argList, newTarget). + auto result = TRY(function.internal_construct(*callee_context, new_target.as_function())); + + // 7. Let thisER be GetThisEnvironment(). + auto& this_environment = as(*get_this_environment(vm)); + + // 8. Perform ? thisER.BindThisValue(result). + TRY(this_environment.bind_this_value(vm, result)); + + // 9. Let F be thisER.[[FunctionObject]]. + auto& f = this_environment.function_object(); + + // 10. Assert: F is an ECMAScript function object. + // NOTE: This is implied by the strong C++ type. + + // 11. Perform ? InitializeInstanceElements(result, F). + TRY(result->initialize_instance_elements(f)); + + // 12. Return result. + interpreter.set(m_dst, result); return {}; } diff --git a/Libraries/LibJS/Bytecode/Interpreter.h b/Libraries/LibJS/Bytecode/Interpreter.h index 766cf5a97f6..0b04ec9afdd 100644 --- a/Libraries/LibJS/Bytecode/Interpreter.h +++ b/Libraries/LibJS/Bytecode/Interpreter.h @@ -77,11 +77,6 @@ public: Executable& current_executable() { return *m_current_executable; } Executable const& current_executable() const { return *m_current_executable; } - Span allocate_argument_values(size_t argument_count) - { - m_argument_values_buffer.resize_and_keep_capacity(argument_count); - return m_argument_values_buffer.span(); - } ExecutionContext& running_execution_context() { return *m_running_execution_context; } @@ -101,7 +96,6 @@ private: GC::Ptr m_global_object { nullptr }; GC::Ptr m_global_declarative_environment { nullptr }; Span m_registers_and_constants_and_locals_arguments; - Vector m_argument_values_buffer; ExecutionContext* m_running_execution_context { nullptr }; };