add invoke_single fast path for no-arg filter invocation, avoids splat alloc

This commit is contained in:
Tobi Lutke
2026-03-11 08:47:21 -04:00
parent 03a1977ffe
commit 526af22574
3 changed files with 21 additions and 1 deletions
+6
View File
@@ -109,6 +109,12 @@ module Liquid
strainer.invoke(method, *args).to_liquid
end
# Fast path for single-argument filter invocation (the most common case:
# {{ value | filter }}) — avoids *args splat allocation.
def invoke_single(method, input)
strainer.invoke_single(method, input).to_liquid
end
# Push new local scope on the stack. use <tt>Context#stack</tt> instead
def push(new_scope = {})
@scopes.unshift(new_scope)
+14
View File
@@ -58,5 +58,19 @@ module Liquid
rescue ::ArgumentError => e
raise Liquid::ArgumentError, e.message, e.backtrace
end
# Fast path for single-argument (no extra args) filter invocation.
# Avoids *args splat allocation for the common {{ value | filter }} case.
def invoke_single(method, input)
if self.class.invokable?(method)
send(method, input)
elsif @context.strict_filters
raise Liquid::UndefinedFilter, "undefined filter #{method}"
else
input
end
rescue ::ArgumentError => e
raise Liquid::ArgumentError, e.message, e.backtrace
end
end
end
+1 -1
View File
@@ -306,7 +306,7 @@ module Liquid
@filters.each do |filter_name, filter_args, filter_kwargs|
if filter_args.empty? && !filter_kwargs
obj = context.invoke(filter_name, obj)
obj = context.invoke_single(filter_name, obj)
else
filter_args = evaluate_filter_expressions(context, filter_args, filter_kwargs)
obj = context.invoke(filter_name, obj, *filter_args)