From 66264f7c2a823a9be09e91abe9b75041005d7045 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Mon, 9 Aug 2021 17:09:48 -0400 Subject: [PATCH] LibJS: Change ExecutionContext's arguments list to a MarkedValueList The test262 tests under RegExp/property-escapes/generated will invoke Reflect.apply with up to 10,000 arguments at a time. In LibJS, when the call stack reached VM::call_internal, we transfer those arguments from a MarkedValueList to the execution context's arguments Vector. Because these types differ (MarkedValueList is a Vector), the arguments are copied rather than moved. By changing the arguments vector to a MarkedValueList, we can properly move the passed arguments over. This shaves about 2 seconds off the following test262 test (from 15sec): RegExp/property-escapes/generated/General_Category_-_Decimal_Number.js --- .../Libraries/LibJS/Bytecode/Interpreter.cpp | 2 +- Userland/Libraries/LibJS/Interpreter.cpp | 2 +- Userland/Libraries/LibJS/Runtime/VM.cpp | 31 ++++++++++--------- Userland/Libraries/LibJS/Runtime/VM.h | 7 ++++- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 9e6f04b26f..50d9f2a49b 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -44,7 +44,7 @@ Value Interpreter::run(Executable const& executable, BasicBlock const* entry_poi vm().set_last_value(Badge {}, {}); - ExecutionContext execution_context; + ExecutionContext execution_context(vm().heap()); if (vm().execution_context_stack().is_empty()) { execution_context.this_value = &global_object(); static FlyString global_execution_context_name = "(*BC* global execution context)"; diff --git a/Userland/Libraries/LibJS/Interpreter.cpp b/Userland/Libraries/LibJS/Interpreter.cpp index e5d800042f..41db16c75d 100644 --- a/Userland/Libraries/LibJS/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Interpreter.cpp @@ -45,7 +45,7 @@ void Interpreter::run(GlobalObject& global_object, const Program& program) vm.set_last_value(Badge {}, {}); - ExecutionContext execution_context; + ExecutionContext execution_context(heap()); execution_context.current_node = &program; execution_context.this_value = &global_object; static FlyString global_execution_context_name = "(global execution context)"; diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index 8e993bcd7f..048004e61d 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -442,6 +442,18 @@ Reference VM::resolve_binding(FlyString const& name, Environment* environment) return get_identifier_reference(environment, name, strict); } +static void append_bound_and_passed_arguments(MarkedValueList& arguments, Vector bound_arguments, Optional passed_arguments) +{ + arguments.ensure_capacity(bound_arguments.size()); + arguments.extend(move(bound_arguments)); + + if (passed_arguments.has_value()) { + auto arguments_list = move(passed_arguments.release_value().values()); + arguments.grow_capacity(arguments_list.size()); + arguments.extend(move(arguments_list)); + } +} + Value VM::construct(FunctionObject& function, FunctionObject& new_target, Optional arguments) { auto& global_object = function.global_object(); @@ -453,7 +465,7 @@ Value VM::construct(FunctionObject& function, FunctionObject& new_target, Option return {}; } - ExecutionContext callee_context; + ExecutionContext callee_context(heap()); prepare_for_ordinary_call(function, callee_context, &new_target); if (exception()) return {}; @@ -465,9 +477,7 @@ Value VM::construct(FunctionObject& function, FunctionObject& new_target, Option if (auto* interpreter = interpreter_if_exists()) callee_context.current_node = interpreter->current_node(); - callee_context.arguments = function.bound_arguments(); - if (arguments.has_value()) - callee_context.arguments.extend(arguments.value().values()); + append_bound_and_passed_arguments(callee_context.arguments, function.bound_arguments(), move(arguments)); if (auto* environment = callee_context.lexical_environment) { auto& function_environment = verify_cast(*environment); @@ -631,19 +641,14 @@ Value VM::call_internal(FunctionObject& function, Value this_value, Optional(function)) { auto& bound_function = static_cast(function); - auto bound_arguments = bound_function.bound_arguments(); - if (arguments.has_value()) - bound_arguments.extend(*arguments); MarkedValueList with_bound_arguments { heap() }; - with_bound_arguments.extend(bound_function.bound_arguments()); - if (arguments.has_value()) - with_bound_arguments.extend(*arguments); + append_bound_and_passed_arguments(with_bound_arguments, bound_function.bound_arguments(), move(arguments)); return call_internal(bound_function.target_function(), bound_function.bound_this(), move(with_bound_arguments)); } - ExecutionContext callee_context; + ExecutionContext callee_context(heap()); prepare_for_ordinary_call(function, callee_context, js_undefined()); if (exception()) return {}; @@ -656,9 +661,7 @@ Value VM::call_internal(FunctionObject& function, Value this_value, Optionalcurrent_node(); callee_context.this_value = function.bound_this().value_or(this_value); - callee_context.arguments = function.bound_arguments(); - if (arguments.has_value()) - callee_context.arguments.extend(arguments.value().values()); + append_bound_and_passed_arguments(callee_context.arguments, function.bound_arguments(), move(arguments)); if (callee_context.lexical_environment) ordinary_call_bind_this(function, callee_context, this_value); diff --git a/Userland/Libraries/LibJS/Runtime/VM.h b/Userland/Libraries/LibJS/Runtime/VM.h index d06d8b5d7f..5e4fc8a563 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.h +++ b/Userland/Libraries/LibJS/Runtime/VM.h @@ -43,11 +43,16 @@ struct ScopeFrame { }; struct ExecutionContext { + explicit ExecutionContext(Heap& heap) + : arguments(heap) + { + } + const ASTNode* current_node { nullptr }; FlyString function_name; FunctionObject* function { nullptr }; Value this_value; - Vector arguments; + MarkedValueList arguments; Object* arguments_object { nullptr }; Environment* lexical_environment { nullptr }; Environment* variable_environment { nullptr };