Skip to content

Commit 583fdb7

Browse files
l46kokcopybara-github
authored andcommitted
Implement iterative eval for program planner
PiperOrigin-RevId: 964335064
1 parent e5c1466 commit 583fdb7

12 files changed

Lines changed: 455 additions & 53 deletions

File tree

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.ArrayList;
2020
import java.util.Arrays;
2121
import java.util.Collection;
22+
import java.util.Collections;
2223
import java.util.HashSet;
2324
import java.util.Set;
2425
import org.jspecify.annotations.Nullable;
@@ -36,12 +37,12 @@ public final class AccumulatedUnknowns {
3637
private final Set<Long> exprIds;
3738
private final Set<CelAttribute> attributes;
3839

39-
Set<Long> exprIds() {
40-
return exprIds;
40+
public Set<Long> exprIds() {
41+
return Collections.unmodifiableSet(exprIds);
4142
}
4243

43-
Set<CelAttribute> attributes() {
44-
return attributes;
44+
public Set<CelAttribute> attributes() {
45+
return Collections.unmodifiableSet(attributes);
4546
}
4647

4748
/**

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,16 @@ public Object trace(PartialVars partialVars, CelEvaluationListener listener)
235235

236236
@Override
237237
public Object advanceEvaluation(UnknownContext context) throws CelEvaluationException {
238-
throw new UnsupportedOperationException("Unsupported operation.");
238+
PlannedProgram plannedProgram = (PlannedProgram) program;
239+
return plannedProgram.evalOrThrow(
240+
plannedProgram.interpretable(),
241+
context.variableResolver(),
242+
EMPTY_FUNCTION_RESOLVER,
243+
PartialVars.of(
244+
(name) -> Optional.ofNullable(context.variableResolver().resolve(name)),
245+
context.unresolvedAttributes()),
246+
context.createAttributeResolver(),
247+
/* listener= */ null);
239248
}
240249
};
241250
}

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

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,16 @@ public GlobalResolver variableResolver() {
107107
return variableResolver;
108108
}
109109

110+
/** Accessor for unresolved attribute patterns. */
111+
ImmutableList<CelAttributePattern> unresolvedAttributes() {
112+
return unresolvedAttributes;
113+
}
114+
115+
/** Accessor for resolved attribute values. */
116+
ImmutableMap<CelAttribute, Object> resolvedAttributes() {
117+
return resolvedAttributes;
118+
}
119+
110120
/**
111121
* Creates a new unknown context that is a copy of the current context with the provided
112122
* additional attribute values.
@@ -168,10 +178,27 @@ public Optional<Object> resolve(CelAttribute attribute) {
168178

169179
@Override
170180
public Optional<CelUnknownSet> maybePartialUnknown(CelAttribute attribute) {
171-
return unresolvedAttributes.stream()
172-
.filter(p -> p.isPartialMatch(attribute))
173-
.findFirst()
174-
.map(p -> CelUnknownSet.create(p.simplify(attribute)));
181+
if (attribute.equals(CelAttribute.EMPTY) || attribute.qualifiers().isEmpty()) {
182+
return Optional.empty();
183+
}
184+
Optional<CelUnknownSet> fromUnresolved =
185+
unresolvedAttributes.stream()
186+
.filter(p -> p.isPartialMatch(attribute))
187+
.findFirst()
188+
.map(p -> CelUnknownSet.create(p.simplify(attribute)));
189+
if (fromUnresolved.isPresent()) {
190+
return fromUnresolved;
191+
}
192+
for (CelAttribute resolved : resolvedAttributes.keySet()) {
193+
if (resolved.qualifiers().size() > attribute.qualifiers().size()
194+
&& resolved
195+
.qualifiers()
196+
.subList(0, attribute.qualifiers().size())
197+
.equals(attribute.qualifiers())) {
198+
return Optional.of(CelUnknownSet.create(attribute));
199+
}
200+
}
201+
return Optional.empty();
175202
}
176203
}
177204
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ java_library(
8989
"//runtime:partial_vars",
9090
"//runtime:program",
9191
"//runtime:resolved_overload",
92+
"//runtime:unknown_attributes",
9293
"//runtime:variable_resolver",
9394
"@maven//:com_google_errorprone_error_prone_annotations",
9495
"@maven//:org_jspecify_jspecify",
@@ -131,6 +132,7 @@ java_library(
131132
"//common/types:type_providers",
132133
"//common/values",
133134
"//runtime:accumulated_unknowns",
135+
"//runtime:evaluation_exception",
134136
"//runtime:interpretable",
135137
"//runtime:interpreter_util",
136138
"//runtime:partial_vars",
@@ -221,12 +223,17 @@ java_library(
221223
deps = [
222224
":eval_helpers",
223225
":planned_interpretable",
226+
"//common:operator",
224227
"//common/ast",
225228
"//common/values",
226229
"//runtime:accumulated_unknowns",
227230
"//runtime:evaluation_exception",
228231
"//runtime:interpretable",
232+
"//runtime:interpreter_util",
233+
"//runtime:partial_vars",
229234
"//runtime:resolved_overload",
235+
"//runtime:unknown_attributes",
236+
"@maven//:com_google_guava_guava",
230237
],
231238
)
232239

@@ -518,6 +525,7 @@ java_library(
518525
"//runtime:interpreter_util",
519526
"//runtime:partial_vars",
520527
"//runtime:resolved_overload",
528+
"//runtime:unknown_attributes",
521529
"@maven//:com_google_errorprone_error_prone_annotations",
522530
"@maven//:org_jspecify_jspecify",
523531
],
@@ -596,6 +604,7 @@ cel_android_library(
596604
"//runtime:evaluation_exception_builder",
597605
"//runtime:interpretable_android",
598606
"//runtime:resolved_overload_android",
607+
"//runtime:unknown_attributes_android",
599608
"//runtime:variable_resolver",
600609
"//runtime/src/main/java/dev/cel/runtime:evaluation_listener_android",
601610
"//runtime/src/main/java/dev/cel/runtime:function_resolver_android",
@@ -642,6 +651,7 @@ cel_android_library(
642651
"//common/types:type_providers_android",
643652
"//common/types:types_android",
644653
"//common/values:values_android",
654+
"//runtime:evaluation_exception",
645655
"//runtime:interpretable_android",
646656
"//runtime:unknown_attributes_android",
647657
"//runtime/src/main/java/dev/cel/runtime:accumulated_unknowns_android",
@@ -733,12 +743,17 @@ cel_android_library(
733743
deps = [
734744
":eval_helpers_android",
735745
":planned_interpretable_android",
746+
"//common:operator_android",
736747
"//common/ast:ast_android",
737748
"//common/values:values_android",
738749
"//runtime:evaluation_exception",
739750
"//runtime:interpretable_android",
751+
"//runtime:interpreter_util_android",
752+
"//runtime:partial_vars_android",
740753
"//runtime:resolved_overload_android",
754+
"//runtime:unknown_attributes_android",
741755
"//runtime/src/main/java/dev/cel/runtime:accumulated_unknowns_android",
756+
"@maven_android//:com_google_guava_guava",
742757
],
743758
)
744759

@@ -1023,6 +1038,7 @@ cel_android_library(
10231038
"//runtime:interpretable_android",
10241039
"//runtime:interpreter_util_android",
10251040
"//runtime:resolved_overload_android",
1041+
"//runtime:unknown_attributes_android",
10261042
"//runtime/src/main/java/dev/cel/runtime:function_resolver_android",
10271043
"//runtime/src/main/java/dev/cel/runtime:partial_vars_android",
10281044
"@maven//:com_google_errorprone_error_prone_annotations",

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

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,23 @@
1717
import static dev.cel.runtime.planner.EvalHelpers.evalNonstrictly;
1818
import static dev.cel.runtime.planner.EvalHelpers.evalStrictly;
1919

20+
import com.google.common.collect.ImmutableList;
21+
import com.google.common.primitives.UnsignedLong;
22+
import dev.cel.common.Operator;
2023
import dev.cel.common.ast.CelExpr;
2124
import dev.cel.common.values.CelValueConverter;
25+
import dev.cel.common.values.ErrorValue;
2226
import dev.cel.runtime.AccumulatedUnknowns;
27+
import dev.cel.runtime.CelAttribute;
28+
import dev.cel.runtime.CelAttributePattern;
29+
import dev.cel.runtime.CelAttributeResolver;
2330
import dev.cel.runtime.CelEvaluationException;
2431
import dev.cel.runtime.CelResolvedOverload;
2532
import dev.cel.runtime.GlobalResolver;
33+
import dev.cel.runtime.InterpreterUtil;
34+
import dev.cel.runtime.PartialVars;
35+
import java.util.Optional;
36+
import org.jspecify.annotations.Nullable;
2637

2738
final class EvalBinary extends PlannedInterpretable {
2839

@@ -39,7 +50,13 @@ Object evalInternal(GlobalResolver resolver, ExecutionFrame frame) throws CelEva
3950
isStrict ? evalStrictly(arg1, resolver, frame) : evalNonstrictly(arg1, resolver, frame);
4051
Object argVal2 =
4152
isStrict ? evalStrictly(arg2, resolver, frame) : evalNonstrictly(arg2, resolver, frame);
53+
4254
if (isStrict) {
55+
Object indexUnknownResult = maybeEvaluateIndexUnknown(argVal1, argVal2, frame);
56+
if (indexUnknownResult != null) {
57+
return indexUnknownResult;
58+
}
59+
4360
AccumulatedUnknowns unknowns = AccumulatedUnknowns.maybeMerge(null, argVal1);
4461
unknowns = AccumulatedUnknowns.maybeMerge(unknowns, argVal2);
4562
if (unknowns != null) {
@@ -51,6 +68,78 @@ Object evalInternal(GlobalResolver resolver, ExecutionFrame frame) throws CelEva
5168
functionName, resolvedOverload, celValueConverter, argVal1, argVal2);
5269
}
5370

71+
private @Nullable Object maybeEvaluateIndexUnknown(
72+
Object target, Object index, ExecutionFrame frame) throws CelEvaluationException {
73+
if (!Operator.INDEX.getFunction().equals(functionName)
74+
|| !(target instanceof AccumulatedUnknowns)
75+
|| target instanceof ErrorValue
76+
|| index instanceof AccumulatedUnknowns
77+
|| index instanceof ErrorValue) {
78+
return null;
79+
}
80+
81+
Optional<CelAttribute.Qualifier> optionalQualifier = toQualifier(index);
82+
if (!optionalQualifier.isPresent()) {
83+
return null;
84+
}
85+
CelAttribute.Qualifier qualifier = optionalQualifier.get();
86+
87+
AccumulatedUnknowns targetUnknowns = (AccumulatedUnknowns) target;
88+
CelAttributeResolver attributeResolver = frame.attributeResolver().orElse(null);
89+
PartialVars partialVars = frame.partialVars().orElse(null);
90+
91+
ImmutableList.Builder<CelAttribute> qualifiedAttrs = ImmutableList.builder();
92+
for (CelAttribute attr : targetUnknowns.attributes()) {
93+
CelAttribute qualifiedAttr = attr.qualify(qualifier);
94+
if (attributeResolver != null) {
95+
Optional<Object> resolved = attributeResolver.resolve(qualifiedAttr);
96+
if (resolved.isPresent()) {
97+
return adaptResolvedValue(resolved.get());
98+
}
99+
}
100+
qualifiedAttrs.add(simplifyAttribute(qualifiedAttr, attr, partialVars));
101+
}
102+
103+
return AccumulatedUnknowns.create(targetUnknowns.exprIds(), qualifiedAttrs.build());
104+
}
105+
106+
private static CelAttribute simplifyAttribute(
107+
CelAttribute qualifiedAttr, CelAttribute fallbackAttr, @Nullable PartialVars partialVars) {
108+
if (partialVars == null) {
109+
return qualifiedAttr;
110+
}
111+
for (CelAttributePattern pattern : partialVars.unknowns()) {
112+
if (pattern.isPartialMatch(qualifiedAttr)) {
113+
return pattern.simplify(qualifiedAttr);
114+
}
115+
}
116+
return fallbackAttr;
117+
}
118+
119+
private static Optional<CelAttribute.Qualifier> toQualifier(Object value) {
120+
if (value instanceof UnsignedLong) {
121+
return Optional.of(CelAttribute.Qualifier.ofUint((UnsignedLong) value));
122+
}
123+
if (value instanceof Long) {
124+
return Optional.of(CelAttribute.Qualifier.ofInt((Long) value));
125+
}
126+
if (value instanceof Integer) {
127+
return Optional.of(CelAttribute.Qualifier.ofInt(((Integer) value).longValue()));
128+
}
129+
if (value instanceof Boolean) {
130+
return Optional.of(CelAttribute.Qualifier.ofBool((Boolean) value));
131+
}
132+
if (value instanceof String) {
133+
return Optional.of(CelAttribute.Qualifier.ofString((String) value));
134+
}
135+
return Optional.empty();
136+
}
137+
138+
private static Object adaptResolvedValue(Object resolvedVal) throws CelEvaluationException {
139+
resolvedVal = InterpreterUtil.strict(resolvedVal);
140+
return InterpreterUtil.maybeAdaptToAccumulatedUnknowns(resolvedVal);
141+
}
142+
54143
static EvalBinary create(
55144
CelExpr expr,
56145
String functionName,

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import dev.cel.common.CelOptions;
1818
import dev.cel.common.exceptions.CelIterationLimitExceededException;
19+
import dev.cel.runtime.CelAttributeResolver;
1920
import dev.cel.runtime.CelEvaluationException;
2021
import dev.cel.runtime.CelEvaluationListener;
2122
import dev.cel.runtime.CelFunctionResolver;
@@ -31,6 +32,7 @@ final class ExecutionFrame {
3132
private final int comprehensionIterationLimit;
3233
private final CelFunctionResolver functionResolver;
3334
private final PartialVars partialVars;
35+
private final @Nullable CelAttributeResolver attributeResolver;
3436
private final @Nullable CelEvaluationListener listener;
3537
private int iterationCount;
3638
private BlockMemoizer blockMemoizer;
@@ -68,15 +70,33 @@ static ExecutionFrame create(
6870
CelFunctionResolver functionResolver,
6971
CelOptions celOptions,
7072
@Nullable PartialVars partialVars,
73+
@Nullable CelAttributeResolver attributeResolver,
7174
@Nullable CelEvaluationListener listener) {
7275
return new ExecutionFrame(
73-
functionResolver, celOptions.comprehensionMaxIterations(), partialVars, listener);
76+
functionResolver,
77+
celOptions.comprehensionMaxIterations(),
78+
partialVars,
79+
attributeResolver,
80+
listener);
81+
}
82+
83+
static ExecutionFrame create(
84+
CelFunctionResolver functionResolver,
85+
CelOptions celOptions,
86+
@Nullable PartialVars partialVars,
87+
@Nullable CelEvaluationListener listener) {
88+
return create(
89+
functionResolver, celOptions, partialVars, /* attributeResolver= */ null, listener);
7490
}
7591

7692
Optional<PartialVars> partialVars() {
7793
return Optional.ofNullable(partialVars);
7894
}
7995

96+
Optional<CelAttributeResolver> attributeResolver() {
97+
return Optional.ofNullable(attributeResolver);
98+
}
99+
80100
@Nullable CelEvaluationListener getListener() {
81101
return listener;
82102
}
@@ -85,10 +105,12 @@ private ExecutionFrame(
85105
CelFunctionResolver functionResolver,
86106
int limit,
87107
@Nullable PartialVars partialVars,
108+
@Nullable CelAttributeResolver attributeResolver,
88109
@Nullable CelEvaluationListener listener) {
89110
this.comprehensionIterationLimit = limit;
90111
this.functionResolver = functionResolver;
91112
this.partialVars = partialVars;
113+
this.attributeResolver = attributeResolver;
92114
this.listener = listener;
93115
}
94116
}

0 commit comments

Comments
 (0)