Skip to content

Commit 33a4cb0

Browse files
l46kokcopybara-github
authored andcommitted
Plan specialized calls for Logical OR/AND
PiperOrigin-RevId: 837218578
1 parent eb0273d commit 33a4cb0

File tree

8 files changed

+341
-2
lines changed

8 files changed

+341
-2
lines changed

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,21 @@ java_library(
1414
],
1515
deps = [
1616
":attribute",
17+
":eval_and",
1718
":eval_attribute",
1819
":eval_const",
1920
":eval_create_list",
2021
":eval_create_map",
2122
":eval_create_struct",
23+
":eval_or",
2224
":eval_unary",
2325
":eval_var_args_call",
2426
":eval_zero_arity",
2527
":planned_program",
2628
"//:auto_value",
2729
"//common:cel_ast",
2830
"//common:container",
31+
"//common:operator",
2932
"//common/annotations",
3033
"//common/ast",
3134
"//common/types",
@@ -48,6 +51,8 @@ java_library(
4851
srcs = ["PlannedProgram.java"],
4952
deps = [
5053
"//:auto_value",
54+
"//common:runtime_exception",
55+
"//common/values",
5156
"//runtime:activation",
5257
"//runtime:evaluation_exception",
5358
"//runtime:evaluation_exception_builder",
@@ -137,6 +142,32 @@ java_library(
137142
],
138143
)
139144

145+
java_library(
146+
name = "eval_or",
147+
srcs = ["EvalOr.java"],
148+
deps = [
149+
":eval_helpers",
150+
"//common/values",
151+
"//runtime:evaluation_listener",
152+
"//runtime:function_resolver",
153+
"//runtime:interpretable",
154+
"@maven//:com_google_guava_guava",
155+
],
156+
)
157+
158+
java_library(
159+
name = "eval_and",
160+
srcs = ["EvalAnd.java"],
161+
deps = [
162+
":eval_helpers",
163+
"//common/values",
164+
"//runtime:evaluation_listener",
165+
"//runtime:function_resolver",
166+
"//runtime:interpretable",
167+
"@maven//:com_google_guava_guava",
168+
],
169+
)
170+
140171
java_library(
141172
name = "eval_create_struct",
142173
srcs = ["EvalCreateStruct.java"],
@@ -178,3 +209,12 @@ java_library(
178209
"@maven//:com_google_guava_guava",
179210
],
180211
)
212+
213+
java_library(
214+
name = "eval_helpers",
215+
srcs = ["EvalHelpers.java"],
216+
deps = [
217+
"//common/values",
218+
"//runtime:interpretable",
219+
],
220+
)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2025 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.planner;
16+
17+
import static dev.cel.runtime.planner.EvalHelpers.evalNonstrictly;
18+
19+
import com.google.common.base.Preconditions;
20+
import dev.cel.common.values.ErrorValue;
21+
import dev.cel.runtime.CelEvaluationListener;
22+
import dev.cel.runtime.CelFunctionResolver;
23+
import dev.cel.runtime.GlobalResolver;
24+
import dev.cel.runtime.Interpretable;
25+
26+
final class EvalAnd implements Interpretable {
27+
28+
@SuppressWarnings("Immutable")
29+
private final Interpretable[] args;
30+
31+
@Override
32+
public Object eval(GlobalResolver resolver) {
33+
ErrorValue errorValue = null;
34+
for (Interpretable arg : args) {
35+
Object argVal = evalNonstrictly(arg, resolver);
36+
if (argVal instanceof Boolean) {
37+
// Short-circuit on false
38+
if (!((boolean) argVal)) {
39+
return false;
40+
}
41+
} else if (argVal instanceof ErrorValue) {
42+
errorValue = (ErrorValue) argVal;
43+
} else {
44+
// TODO: Handle unknowns
45+
throw new IllegalArgumentException(
46+
String.format("Expected boolean value, found: %s", argVal));
47+
}
48+
}
49+
50+
if (errorValue != null) {
51+
return errorValue;
52+
}
53+
54+
return true;
55+
}
56+
57+
@Override
58+
public Object eval(GlobalResolver resolver, CelEvaluationListener listener) {
59+
// TODO: Implement support
60+
throw new UnsupportedOperationException("Not yet supported");
61+
}
62+
63+
@Override
64+
public Object eval(GlobalResolver resolver, CelFunctionResolver lateBoundFunctionResolver) {
65+
// TODO: Implement support
66+
throw new UnsupportedOperationException("Not yet supported");
67+
}
68+
69+
@Override
70+
public Object eval(
71+
GlobalResolver resolver,
72+
CelFunctionResolver lateBoundFunctionResolver,
73+
CelEvaluationListener listener) {
74+
// TODO: Implement support
75+
throw new UnsupportedOperationException("Not yet supported");
76+
}
77+
78+
static EvalAnd create(Interpretable[] args) {
79+
return new EvalAnd(args);
80+
}
81+
82+
private EvalAnd(Interpretable[] args) {
83+
Preconditions.checkArgument(args.length == 2);
84+
this.args = args;
85+
}
86+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2025 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.planner;
16+
17+
import dev.cel.common.values.ErrorValue;
18+
import dev.cel.runtime.GlobalResolver;
19+
import dev.cel.runtime.Interpretable;
20+
21+
final class EvalHelpers {
22+
23+
static Object evalNonstrictly(Interpretable interpretable, GlobalResolver resolver) {
24+
try {
25+
return interpretable.eval(resolver);
26+
} catch (Exception e) {
27+
return ErrorValue.create(e);
28+
}
29+
}
30+
31+
private EvalHelpers() {}
32+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2025 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.planner;
16+
17+
import static dev.cel.runtime.planner.EvalHelpers.evalNonstrictly;
18+
19+
import com.google.common.base.Preconditions;
20+
import dev.cel.common.values.ErrorValue;
21+
import dev.cel.runtime.CelEvaluationListener;
22+
import dev.cel.runtime.CelFunctionResolver;
23+
import dev.cel.runtime.GlobalResolver;
24+
import dev.cel.runtime.Interpretable;
25+
26+
final class EvalOr implements Interpretable {
27+
28+
@SuppressWarnings("Immutable")
29+
private final Interpretable[] args;
30+
31+
@Override
32+
public Object eval(GlobalResolver resolver) {
33+
ErrorValue errorValue = null;
34+
for (Interpretable arg : args) {
35+
Object argVal = evalNonstrictly(arg, resolver);
36+
if (argVal instanceof Boolean) {
37+
// Short-circuit on true
38+
if (((boolean) argVal)) {
39+
return true;
40+
}
41+
} else if (argVal instanceof ErrorValue) {
42+
errorValue = (ErrorValue) argVal;
43+
} else {
44+
// TODO: Handle unknowns
45+
throw new IllegalArgumentException(
46+
String.format("Expected boolean value, found: %s", argVal));
47+
}
48+
}
49+
50+
if (errorValue != null) {
51+
return errorValue;
52+
}
53+
54+
return false;
55+
}
56+
57+
@Override
58+
public Object eval(GlobalResolver resolver, CelEvaluationListener listener) {
59+
// TODO: Implement support
60+
throw new UnsupportedOperationException("Not yet supported");
61+
}
62+
63+
@Override
64+
public Object eval(GlobalResolver resolver, CelFunctionResolver lateBoundFunctionResolver) {
65+
// TODO: Implement support
66+
throw new UnsupportedOperationException("Not yet supported");
67+
}
68+
69+
@Override
70+
public Object eval(
71+
GlobalResolver resolver,
72+
CelFunctionResolver lateBoundFunctionResolver,
73+
CelEvaluationListener listener) {
74+
// TODO: Implement support
75+
throw new UnsupportedOperationException("Not yet supported");
76+
}
77+
78+
static EvalOr create(Interpretable[] args) {
79+
return new EvalOr(args);
80+
}
81+
82+
private EvalOr(Interpretable[] args) {
83+
Preconditions.checkArgument(args.length == 2);
84+
this.args = args;
85+
}
86+
}

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
import com.google.auto.value.AutoValue;
1818
import com.google.errorprone.annotations.Immutable;
19+
import dev.cel.common.CelRuntimeException;
20+
import dev.cel.common.values.ErrorValue;
1921
import dev.cel.runtime.Activation;
2022
import dev.cel.runtime.CelEvaluationException;
2123
import dev.cel.runtime.CelEvaluationExceptionBuilder;
@@ -49,14 +51,28 @@ public Object eval(Map<String, ?> mapValue, CelFunctionResolver lateBoundFunctio
4951
private Object evalOrThrow(Interpretable interpretable, GlobalResolver resolver)
5052
throws CelEvaluationException {
5153
try {
52-
return interpretable.eval(resolver);
54+
Object evalResult = interpretable.eval(resolver);
55+
if (evalResult instanceof ErrorValue) {
56+
ErrorValue errorValue = (ErrorValue) evalResult;
57+
throw newCelEvaluationException(errorValue.value());
58+
}
59+
60+
return evalResult;
5361
} catch (RuntimeException e) {
5462
throw newCelEvaluationException(e);
5563
}
5664
}
5765

5866
private static CelEvaluationException newCelEvaluationException(Exception e) {
59-
return CelEvaluationExceptionBuilder.newBuilder(e.getMessage()).setCause(e).build();
67+
CelEvaluationExceptionBuilder builder;
68+
if (e instanceof CelRuntimeException) {
69+
// Preserve detailed error, including error codes if one exists.
70+
builder = CelEvaluationExceptionBuilder.newBuilder((CelRuntimeException) e);
71+
} else {
72+
builder = CelEvaluationExceptionBuilder.newBuilder(e.getMessage()).setCause(e);
73+
}
74+
75+
return builder.build();
6076
}
6177

6278
static Program create(Interpretable interpretable) {

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import javax.annotation.concurrent.ThreadSafe;
2222
import dev.cel.common.CelAbstractSyntaxTree;
2323
import dev.cel.common.CelContainer;
24+
import dev.cel.common.Operator;
2425
import dev.cel.common.annotations.Internal;
2526
import dev.cel.common.ast.CelConstant;
2627
import dev.cel.common.ast.CelExpr;
@@ -169,6 +170,17 @@ private Interpretable planCall(CelExpr expr, PlannerContext ctx) {
169170

170171
// TODO: Handle all specialized calls (logical operators, conditionals, equals etc)
171172
String functionName = resolvedFunction.functionName();
173+
Operator operator = Operator.findReverse(functionName).orElse(null);
174+
if (operator != null) {
175+
switch (operator) {
176+
case LOGICAL_OR:
177+
return EvalOr.create(evaluatedArgs);
178+
case LOGICAL_AND:
179+
return EvalAnd.create(evaluatedArgs);
180+
default:
181+
// fall-through
182+
}
183+
}
172184

173185
CelResolvedOverload resolvedOverload = null;
174186
if (resolvedFunction.overloadId().isPresent()) {

runtime/src/test/java/dev/cel/runtime/planner/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ java_library(
1818
"//common:cel_descriptor_util",
1919
"//common:cel_source",
2020
"//common:compiler_common",
21+
"//common:error_codes",
2122
"//common:operator",
2223
"//common:options",
2324
"//common/ast",

0 commit comments

Comments
 (0)