Skip to content

Commit 347f943

Browse files
l46kokcopybara-github
authored andcommitted
Move fast-path unary/binary apply methods into an internal interface
PiperOrigin-RevId: 892616861
1 parent 8e9f1ac commit 347f943

File tree

9 files changed

+94
-61
lines changed

9 files changed

+94
-61
lines changed

runtime/src/main/java/dev/cel/runtime/BUILD.bazel

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ java_library(
129129
"//:auto_value",
130130
"//common:error_codes",
131131
"//common/annotations",
132-
"//common/exceptions:overload_not_found",
133132
"@maven//:com_google_code_findbugs_annotations",
134133
"@maven//:com_google_errorprone_error_prone_annotations",
135134
"@maven//:com_google_guava_guava",
@@ -151,7 +150,6 @@ cel_android_library(
151150
"//:auto_value",
152151
"//common:error_codes",
153152
"//common/annotations",
154-
"//common/exceptions:overload_not_found",
155153
"@maven//:com_google_code_findbugs_annotations",
156154
"@maven//:com_google_errorprone_error_prone_annotations",
157155
"@maven_android//:com_google_guava_guava",
@@ -790,6 +788,7 @@ java_library(
790788
name = "function_overload",
791789
srcs = [
792790
"CelFunctionOverload.java",
791+
"OptimizedFunctionOverload.java",
793792
],
794793
tags = [
795794
],
@@ -805,6 +804,7 @@ cel_android_library(
805804
name = "function_overload_android",
806805
srcs = [
807806
"CelFunctionOverload.java",
807+
"OptimizedFunctionOverload.java",
808808
],
809809
deps = [
810810
":evaluation_exception",
@@ -1306,9 +1306,12 @@ java_library(
13061306
tags = [
13071307
],
13081308
deps = [
1309+
":evaluation_exception",
1310+
":function_binding",
13091311
":function_overload",
13101312
"//:auto_value",
13111313
"//common/annotations",
1314+
"//common/exceptions:overload_not_found",
13121315
"@maven//:com_google_errorprone_error_prone_annotations",
13131316
"@maven//:com_google_guava_guava",
13141317
],
@@ -1320,9 +1323,12 @@ cel_android_library(
13201323
tags = [
13211324
],
13221325
deps = [
1326+
":evaluation_exception",
1327+
":function_binding_android",
13231328
":function_overload_android",
13241329
"//:auto_value",
13251330
"//common/annotations",
1331+
"//common/exceptions:overload_not_found",
13261332
"@maven//:com_google_errorprone_error_prone_annotations",
13271333
"@maven_android//:com_google_guava_guava",
13281334
],

runtime/src/main/java/dev/cel/runtime/CelFunctionBinding.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@ public interface CelFunctionBinding {
5151
boolean isStrict();
5252

5353
/** Create a unary function binding from the {@code overloadId}, {@code arg}, and {@code impl}. */
54-
@SuppressWarnings("unchecked")
54+
@SuppressWarnings("unchecked") // Safe from CelFunctionOverload.canHandle check before invocation
5555
static <T> CelFunctionBinding from(
5656
String overloadId, Class<T> arg, CelFunctionOverload.Unary<T> impl) {
5757
return from(
5858
overloadId,
5959
ImmutableList.of(arg),
60-
new CelFunctionOverload() {
60+
new OptimizedFunctionOverload() {
6161
@Override
6262
public Object apply(Object[] args) throws CelEvaluationException {
6363
return impl.apply((T) args[0]);
@@ -74,13 +74,13 @@ public Object apply(Object arg1) throws CelEvaluationException {
7474
* Create a binary function binding from the {@code overloadId}, {@code arg1}, {@code arg2}, and
7575
* {@code impl}.
7676
*/
77-
@SuppressWarnings("unchecked")
77+
@SuppressWarnings("unchecked") // Safe from CelFunctionOverload.canHandle check before invocation
7878
static <T1, T2> CelFunctionBinding from(
7979
String overloadId, Class<T1> arg1, Class<T2> arg2, CelFunctionOverload.Binary<T1, T2> impl) {
8080
return from(
8181
overloadId,
8282
ImmutableList.of(arg1, arg2),
83-
new CelFunctionOverload() {
83+
new OptimizedFunctionOverload() {
8484
@Override
8585
public Object apply(Object[] args) throws CelEvaluationException {
8686
return impl.apply((T1) args[0], (T2) args[1]);

runtime/src/main/java/dev/cel/runtime/CelFunctionOverload.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,6 @@ public interface CelFunctionOverload {
2626
/** Evaluate a set of arguments throwing a {@code CelException} on error. */
2727
Object apply(Object[] args) throws CelEvaluationException;
2828

29-
/** Fast-path for unary function execution to avoid Object[] allocation. */
30-
default Object apply(Object arg) throws CelEvaluationException {
31-
return apply(new Object[] {arg});
32-
}
33-
34-
/** Fast-path for binary function execution to avoid Object[] allocation. */
35-
default Object apply(Object arg1, Object arg2) throws CelEvaluationException {
36-
return apply(new Object[] {arg1, arg2});
37-
}
3829

3930
/**
4031
* Helper interface for describing unary functions where the type-parameter is used to improve

runtime/src/main/java/dev/cel/runtime/CelLateFunctionBindings.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public static CelLateFunctionBindings from(Collection<CelFunctionBinding> functi
6565
private static CelResolvedOverload createResolvedOverload(CelFunctionBinding binding) {
6666
return CelResolvedOverload.of(
6767
binding.getOverloadId(),
68-
(args) -> binding.getDefinition().apply(args),
68+
binding.getDefinition(),
6969
binding.isStrict(),
7070
binding.getArgTypes());
7171
}

runtime/src/main/java/dev/cel/runtime/CelResolvedOverload.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.google.common.collect.ImmutableList;
1919
import com.google.errorprone.annotations.Immutable;
2020
import dev.cel.common.annotations.Internal;
21+
import dev.cel.common.exceptions.CelOverloadNotFoundException;
2122
import java.util.List;
2223

2324
/**
@@ -52,6 +53,33 @@ public abstract class CelResolvedOverload {
5253
/** The function definition. */
5354
public abstract CelFunctionOverload getDefinition();
5455

56+
abstract OptimizedFunctionOverload getOptimizedDefinition();
57+
58+
public Object invoke(Object[] args) throws CelEvaluationException {
59+
// Note: canHandle check is handled separately in DynamicDispatchOverload
60+
if (isDynamicDispatch()
61+
|| CelFunctionOverload.canHandle(args, getParameterTypes(), isStrict())) {
62+
return getDefinition().apply(args);
63+
}
64+
throw new CelOverloadNotFoundException(getOverloadId());
65+
}
66+
67+
public Object invoke(Object arg) throws CelEvaluationException {
68+
if (isDynamicDispatch()
69+
|| CelFunctionOverload.canHandle(arg, getParameterTypes(), isStrict())) {
70+
return getOptimizedDefinition().apply(arg);
71+
}
72+
throw new CelOverloadNotFoundException(getOverloadId());
73+
}
74+
75+
public Object invoke(Object arg1, Object arg2) throws CelEvaluationException {
76+
if (isDynamicDispatch()
77+
|| CelFunctionOverload.canHandle(arg1, arg2, getParameterTypes(), isStrict())) {
78+
return getOptimizedDefinition().apply(arg1, arg2);
79+
}
80+
throw new CelOverloadNotFoundException(getOverloadId());
81+
}
82+
5583
/**
5684
* Creates a new resolved overload from the given overload id, parameter types, and definition.
5785
*/
@@ -71,8 +99,12 @@ public static CelResolvedOverload of(
7199
CelFunctionOverload definition,
72100
boolean isStrict,
73101
List<Class<?>> parameterTypes) {
102+
OptimizedFunctionOverload optimizedDef =
103+
(definition instanceof OptimizedFunctionOverload)
104+
? (OptimizedFunctionOverload) definition
105+
: definition::apply;
74106
return new AutoValue_CelResolvedOverload(
75-
overloadId, ImmutableList.copyOf(parameterTypes), isStrict, definition);
107+
overloadId, ImmutableList.copyOf(parameterTypes), isStrict, definition, optimizedDef);
76108
}
77109

78110
/**
@@ -81,4 +113,8 @@ public static CelResolvedOverload of(
81113
boolean canHandle(Object[] arguments) {
82114
return CelFunctionOverload.canHandle(arguments, getParameterTypes(), isStrict());
83115
}
116+
117+
private boolean isDynamicDispatch() {
118+
return getDefinition() instanceof FunctionBindingImpl.DynamicDispatchOverload;
119+
}
84120
}

runtime/src/main/java/dev/cel/runtime/DefaultDispatcher.java

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import com.google.errorprone.annotations.Immutable;
2727
import dev.cel.common.CelErrorCode;
2828
import dev.cel.common.annotations.Internal;
29-
import dev.cel.common.exceptions.CelOverloadNotFoundException;
3029
import dev.cel.runtime.FunctionBindingImpl.DynamicDispatchOverload;
3130
import java.util.ArrayList;
3231
import java.util.Collection;
@@ -202,46 +201,10 @@ public DefaultDispatcher build() {
202201
OverloadEntry overloadEntry = entry.getValue();
203202
CelFunctionOverload overloadImpl = overloadEntry.overload();
204203

205-
CelFunctionOverload guardedApply;
206-
if (overloadImpl instanceof DynamicDispatchOverload) {
207-
// Dynamic dispatcher already does its own internal canHandle checks
208-
guardedApply = overloadImpl;
209-
} else {
210-
boolean isStrict = overloadEntry.isStrict();
211-
ImmutableList<Class<?>> argTypes = overloadEntry.argTypes();
212-
213-
guardedApply =
214-
new CelFunctionOverload() {
215-
@Override
216-
public Object apply(Object[] args) throws CelEvaluationException {
217-
if (CelFunctionOverload.canHandle(args, argTypes, isStrict)) {
218-
return overloadImpl.apply(args);
219-
}
220-
throw new CelOverloadNotFoundException(overloadId);
221-
}
222-
223-
@Override
224-
public Object apply(Object arg) throws CelEvaluationException {
225-
if (CelFunctionOverload.canHandle(arg, argTypes, isStrict)) {
226-
return overloadImpl.apply(arg);
227-
}
228-
throw new CelOverloadNotFoundException(overloadId);
229-
}
230-
231-
@Override
232-
public Object apply(Object arg1, Object arg2) throws CelEvaluationException {
233-
if (CelFunctionOverload.canHandle(arg1, arg2, argTypes, isStrict)) {
234-
return overloadImpl.apply(arg1, arg2);
235-
}
236-
throw new CelOverloadNotFoundException(overloadId);
237-
}
238-
};
239-
}
240-
241204
resolvedOverloads.put(
242205
overloadId,
243206
CelResolvedOverload.of(
244-
overloadId, guardedApply, overloadEntry.isStrict(), overloadEntry.argTypes()));
207+
overloadId, overloadImpl, overloadEntry.isStrict(), overloadEntry.argTypes()));
245208
}
246209

247210
return new DefaultDispatcher(resolvedOverloads.buildOrThrow());

runtime/src/main/java/dev/cel/runtime/FunctionBindingImpl.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ private DynamicDispatchBinding(
126126
}
127127

128128
@Immutable
129-
static final class DynamicDispatchOverload implements CelFunctionOverload {
129+
static final class DynamicDispatchOverload implements OptimizedFunctionOverload {
130130
private final String functionName;
131131
private final ImmutableSet<CelFunctionBinding> overloadBindings;
132132

@@ -149,7 +149,8 @@ public Object apply(Object[] args) throws CelEvaluationException {
149149
public Object apply(Object arg) throws CelEvaluationException {
150150
for (CelFunctionBinding overload : overloadBindings) {
151151
if (CelFunctionOverload.canHandle(arg, overload.getArgTypes(), overload.isStrict())) {
152-
return overload.getDefinition().apply(arg);
152+
OptimizedFunctionOverload def = (OptimizedFunctionOverload) overload.getDefinition();
153+
return def.apply(arg);
153154
}
154155
}
155156
throw new CelOverloadNotFoundException(
@@ -164,7 +165,8 @@ public Object apply(Object arg1, Object arg2) throws CelEvaluationException {
164165
for (CelFunctionBinding overload : overloadBindings) {
165166
if (CelFunctionOverload.canHandle(
166167
arg1, arg2, overload.getArgTypes(), overload.isStrict())) {
167-
return overload.getDefinition().apply(arg1, arg2);
168+
OptimizedFunctionOverload def = (OptimizedFunctionOverload) overload.getDefinition();
169+
return def.apply(arg1, arg2);
168170
}
169171
}
170172
throw new CelOverloadNotFoundException(
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.runtime;
16+
17+
import com.google.errorprone.annotations.Immutable;
18+
19+
/**
20+
* Internal interface to support fast-path Unary and Binary evaluations, avoiding Object[]
21+
* allocation.
22+
*/
23+
@Immutable
24+
interface OptimizedFunctionOverload extends CelFunctionOverload {
25+
26+
/** Fast-path for unary function execution to avoid Object[] allocation. */
27+
default Object apply(Object arg) throws CelEvaluationException {
28+
return apply(new Object[] {arg});
29+
}
30+
31+
/** Fast-path for binary function execution to avoid Object[] allocation. */
32+
default Object apply(Object arg1, Object arg2) throws CelEvaluationException {
33+
return apply(new Object[] {arg1, arg2});
34+
}
35+
}

runtime/src/main/java/dev/cel/runtime/planner/EvalHelpers.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ static Object dispatch(
5959
CelResolvedOverload overload, CelValueConverter valueConverter, Object[] args)
6060
throws CelEvaluationException {
6161
try {
62-
Object result = overload.getDefinition().apply(args);
62+
Object result = overload.invoke(args);
6363
return valueConverter.maybeUnwrap(valueConverter.toRuntimeValue(result));
6464
} catch (RuntimeException e) {
6565
throw handleDispatchException(e, overload, args);
@@ -69,7 +69,7 @@ static Object dispatch(
6969
static Object dispatch(CelResolvedOverload overload, CelValueConverter valueConverter, Object arg)
7070
throws CelEvaluationException {
7171
try {
72-
Object result = overload.getDefinition().apply(arg);
72+
Object result = overload.invoke(arg);
7373
return valueConverter.maybeUnwrap(valueConverter.toRuntimeValue(result));
7474
} catch (RuntimeException e) {
7575
throw handleDispatchException(e, overload, arg);
@@ -80,7 +80,7 @@ static Object dispatch(
8080
CelResolvedOverload overload, CelValueConverter valueConverter, Object arg1, Object arg2)
8181
throws CelEvaluationException {
8282
try {
83-
Object result = overload.getDefinition().apply(arg1, arg2);
83+
Object result = overload.invoke(arg1, arg2);
8484
return valueConverter.maybeUnwrap(valueConverter.toRuntimeValue(result));
8585
} catch (RuntimeException e) {
8686
throw handleDispatchException(e, overload, arg1, arg2);

0 commit comments

Comments
 (0)