Skip to content

Commit a46b835

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

File tree

9 files changed

+96
-59
lines changed

9 files changed

+96
-59
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,7 @@ java_library(
790790
name = "function_overload",
791791
srcs = [
792792
"CelFunctionOverload.java",
793+
"OptimizedFunctionOverload.java",
793794
],
794795
tags = [
795796
],
@@ -805,6 +806,7 @@ cel_android_library(
805806
name = "function_overload_android",
806807
srcs = [
807808
"CelFunctionOverload.java",
809+
"OptimizedFunctionOverload.java",
808810
],
809811
deps = [
810812
":evaluation_exception",
@@ -1306,9 +1308,12 @@ java_library(
13061308
tags = [
13071309
],
13081310
deps = [
1311+
":evaluation_exception",
1312+
":function_binding",
13091313
":function_overload",
13101314
"//:auto_value",
13111315
"//common/annotations",
1316+
"//common/exceptions:overload_not_found",
13121317
"@maven//:com_google_errorprone_error_prone_annotations",
13131318
"@maven//:com_google_guava_guava",
13141319
],
@@ -1320,9 +1325,12 @@ cel_android_library(
13201325
tags = [
13211326
],
13221327
deps = [
1328+
":evaluation_exception",
1329+
":function_binding_android",
13231330
":function_overload_android",
13241331
"//:auto_value",
13251332
"//common/annotations",
1333+
"//common/exceptions:overload_not_found",
13261334
"@maven//:com_google_errorprone_error_prone_annotations",
13271335
"@maven_android//:com_google_guava_guava",
13281336
],

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: 39 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,35 @@ public abstract class CelResolvedOverload {
5253
/** The function definition. */
5354
public abstract CelFunctionOverload getDefinition();
5455

56+
/** Fast-path execution optimizations safely hidden from the public API. */
57+
@Internal
58+
abstract OptimizedFunctionOverload getOptimizedDefinition();
59+
60+
public Object invoke(Object[] args) throws CelEvaluationException {
61+
// Note: canHandle check is handled separately in DynamicDispatchOverload
62+
if (isDynamicDispatch()
63+
|| CelFunctionOverload.canHandle(args, getParameterTypes(), isStrict())) {
64+
return getDefinition().apply(args);
65+
}
66+
throw new CelOverloadNotFoundException(getOverloadId());
67+
}
68+
69+
public Object invoke(Object arg) throws CelEvaluationException {
70+
if (isDynamicDispatch()
71+
|| CelFunctionOverload.canHandle(arg, getParameterTypes(), isStrict())) {
72+
return getOptimizedDefinition().apply(arg);
73+
}
74+
throw new CelOverloadNotFoundException(getOverloadId());
75+
}
76+
77+
public Object invoke(Object arg1, Object arg2) throws CelEvaluationException {
78+
if (isDynamicDispatch()
79+
|| CelFunctionOverload.canHandle(arg1, arg2, getParameterTypes(), isStrict())) {
80+
return getOptimizedDefinition().apply(arg1, arg2);
81+
}
82+
throw new CelOverloadNotFoundException(getOverloadId());
83+
}
84+
5585
/**
5686
* Creates a new resolved overload from the given overload id, parameter types, and definition.
5787
*/
@@ -71,8 +101,12 @@ public static CelResolvedOverload of(
71101
CelFunctionOverload definition,
72102
boolean isStrict,
73103
List<Class<?>> parameterTypes) {
104+
OptimizedFunctionOverload optimizedDef =
105+
(definition instanceof OptimizedFunctionOverload)
106+
? (OptimizedFunctionOverload) definition
107+
: definition::apply;
74108
return new AutoValue_CelResolvedOverload(
75-
overloadId, ImmutableList.copyOf(parameterTypes), isStrict, definition);
109+
overloadId, ImmutableList.copyOf(parameterTypes), isStrict, definition, optimizedDef);
76110
}
77111

78112
/**
@@ -81,4 +115,8 @@ public static CelResolvedOverload of(
81115
boolean canHandle(Object[] arguments) {
82116
return CelFunctionOverload.canHandle(arguments, getParameterTypes(), isStrict());
83117
}
118+
119+
private boolean isDynamicDispatch() {
120+
return getDefinition() instanceof FunctionBindingImpl.DynamicDispatchOverload;
121+
}
84122
}

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 2024 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)