mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-11 18:20:43 +09:00
LibJS: Avoid redundant ExecutionContext allocation for bound functions
Instead of creating a second ExecutionContext in BoundFunction.[[Call]], we now implement BoundFunction::get_stack_frame_size() and combine information from the target + the bound arguments list. This allows BoundFunction.[[Call]] to reuse the already-established ExecutionContext for the callee. 1.20x speedup on MicroBench/bound-call-04-args.js
This commit is contained in:
parent
dbece92637
commit
74f133293d
Notes:
github-actions[bot]
2025-05-07 11:21:37 +00:00
Author: https://github.com/awesomekling
Commit: 74f133293d
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4627
3 changed files with 31 additions and 13 deletions
|
@ -43,7 +43,7 @@ BoundFunction::BoundFunction(Realm& realm, FunctionObject& bound_target_function
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10.4.1.1 [[Call]] ( thisArgument, argumentsList ), https://tc39.es/ecma262/#sec-bound-function-exotic-objects-call-thisargument-argumentslist
|
// 10.4.1.1 [[Call]] ( thisArgument, argumentsList ), https://tc39.es/ecma262/#sec-bound-function-exotic-objects-call-thisargument-argumentslist
|
||||||
ThrowCompletionOr<Value> BoundFunction::internal_call(ExecutionContext& outer_context, [[maybe_unused]] Value this_argument)
|
ThrowCompletionOr<Value> BoundFunction::internal_call(ExecutionContext& callee_context, [[maybe_unused]] Value this_argument)
|
||||||
{
|
{
|
||||||
// 1. Let target be F.[[BoundTargetFunction]].
|
// 1. Let target be F.[[BoundTargetFunction]].
|
||||||
auto& target = *m_bound_target_function;
|
auto& target = *m_bound_target_function;
|
||||||
|
@ -55,24 +55,17 @@ ThrowCompletionOr<Value> BoundFunction::internal_call(ExecutionContext& outer_co
|
||||||
auto& bound_args = m_bound_arguments;
|
auto& bound_args = m_bound_arguments;
|
||||||
|
|
||||||
// 4. Let args be the list-concatenation of boundArgs and argumentsList.
|
// 4. Let args be the list-concatenation of boundArgs and argumentsList.
|
||||||
|
auto* argument_values = callee_context.arguments.data();
|
||||||
|
|
||||||
ExecutionContext* callee_context = nullptr;
|
for (ssize_t i = static_cast<ssize_t>(callee_context.arguments.size()) - 1; i >= static_cast<ssize_t>(bound_args.size()); --i)
|
||||||
size_t registers_and_constants_and_locals_count = 0;
|
argument_values[i] = argument_values[i - bound_args.size()];
|
||||||
size_t argument_count = bound_args.size() + outer_context.arguments.size();
|
|
||||||
TRY(target.get_stack_frame_size(registers_and_constants_and_locals_count, argument_count));
|
|
||||||
ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK(callee_context, registers_and_constants_and_locals_count, argument_count);
|
|
||||||
|
|
||||||
auto* argument_values = callee_context->arguments.data();
|
|
||||||
for (size_t i = 0; i < bound_args.size(); ++i)
|
for (size_t i = 0; i < bound_args.size(); ++i)
|
||||||
argument_values[i] = bound_args[i];
|
argument_values[i] = bound_args[i];
|
||||||
|
|
||||||
for (size_t i = 0; i < outer_context.arguments.size(); ++i)
|
callee_context.passed_argument_count += bound_args.size();
|
||||||
argument_values[bound_args.size() + i] = outer_context.arguments[i];
|
|
||||||
|
|
||||||
callee_context->passed_argument_count = bound_args.size() + outer_context.arguments.size();
|
|
||||||
|
|
||||||
// 5. Return ? Call(target, boundThis, args).
|
// 5. Return ? Call(target, boundThis, args).
|
||||||
return target.internal_call(*callee_context, bound_this);
|
return target.internal_call(callee_context, bound_this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10.4.1.2 [[Construct]] ( argumentsList, newTarget ), https://tc39.es/ecma262/#sec-bound-function-exotic-objects-construct-argumentslist-newtarget
|
// 10.4.1.2 [[Construct]] ( argumentsList, newTarget ), https://tc39.es/ecma262/#sec-bound-function-exotic-objects-construct-argumentslist-newtarget
|
||||||
|
@ -112,4 +105,11 @@ void BoundFunction::visit_edges(Visitor& visitor)
|
||||||
visitor.visit(m_bound_arguments);
|
visitor.visit(m_bound_arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> BoundFunction::get_stack_frame_size(size_t& registers_and_constants_and_locals_count, size_t& argument_count)
|
||||||
|
{
|
||||||
|
TRY(m_bound_target_function->get_stack_frame_size(registers_and_constants_and_locals_count, argument_count));
|
||||||
|
argument_count += m_bound_arguments.size();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ public:
|
||||||
private:
|
private:
|
||||||
BoundFunction(Realm&, FunctionObject& target_function, Value bound_this, Vector<Value> bound_arguments, Object* prototype);
|
BoundFunction(Realm&, FunctionObject& target_function, Value bound_this, Vector<Value> bound_arguments, Object* prototype);
|
||||||
|
|
||||||
|
ThrowCompletionOr<void> get_stack_frame_size(size_t& registers_and_constants_and_locals_count, size_t& argument_count) override;
|
||||||
virtual void visit_edges(Visitor&) override;
|
virtual void visit_edges(Visitor&) override;
|
||||||
|
|
||||||
GC::Ptr<FunctionObject> m_bound_target_function; // [[BoundTargetFunction]]
|
GC::Ptr<FunctionObject> m_bound_target_function; // [[BoundTargetFunction]]
|
||||||
|
|
17
Libraries/LibJS/Tests/regress/function-bind-arguments.js
Normal file
17
Libraries/LibJS/Tests/regress/function-bind-arguments.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
test("Some function.bind cases", () => {
|
||||||
|
function collectArguments() {
|
||||||
|
let a = [];
|
||||||
|
for (let i = 0; i < arguments.length; ++i) a.push(arguments[i]);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
let b = collectArguments.bind(null);
|
||||||
|
expect(b()).toEqual([]);
|
||||||
|
expect(b(3, 4)).toEqual([3, 4]);
|
||||||
|
expect(b(3, 4, 5, 6)).toEqual([3, 4, 5, 6]);
|
||||||
|
|
||||||
|
let b12 = collectArguments.bind(null, 1, 2);
|
||||||
|
expect(b12()).toEqual([1, 2]);
|
||||||
|
expect(b12(3, 4)).toEqual([1, 2, 3, 4]);
|
||||||
|
expect(b12(3, 4, 5, 6)).toEqual([1, 2, 3, 4, 5, 6]);
|
||||||
|
});
|
Loading…
Add table
Add a link
Reference in a new issue