diff --git a/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java b/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java index c3bff8dfd..8b7145201 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java +++ b/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java @@ -255,8 +255,8 @@ public Object trace(PartialVars partialVars, CelEvaluationListener listener) } @Override - public Object advanceEvaluation(UnknownContext context) { - throw new UnsupportedOperationException("Unsupported operation."); + public Object advanceEvaluation(UnknownContext context) throws CelEvaluationException { + return program.eval(context.toPartialVars()); } }; } diff --git a/runtime/src/main/java/dev/cel/runtime/UnknownContext.java b/runtime/src/main/java/dev/cel/runtime/UnknownContext.java index c494ff252..3c028fbca 100644 --- a/runtime/src/main/java/dev/cel/runtime/UnknownContext.java +++ b/runtime/src/main/java/dev/cel/runtime/UnknownContext.java @@ -107,6 +107,11 @@ public GlobalResolver variableResolver() { return variableResolver; } + PartialVars toPartialVars() { + return PartialVars.of( + name -> Optional.ofNullable(variableResolver.resolve(name)), unresolvedAttributes); + } + /** * Creates a new unknown context that is a copy of the current context with the provided * additional attribute values. diff --git a/runtime/src/test/java/dev/cel/runtime/CelRuntimeTest.java b/runtime/src/test/java/dev/cel/runtime/CelRuntimeTest.java index d7247f8a1..ba8326d27 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelRuntimeTest.java +++ b/runtime/src/test/java/dev/cel/runtime/CelRuntimeTest.java @@ -142,13 +142,18 @@ public void evaluate_v1alpha1CheckedExpr() throws Exception { public void advanceEvaluation_withUnknownTracking_noSelfReferenceInMerge(String expression) throws Exception { Cel cel = - CelFactory.standardCelBuilder() + runtimeFlavor + .builder() .setStandardMacros(CelStandardMacro.STANDARD_MACROS) .addCompilerLibraries(CelExtensions.bindings()) .setContainer(CelContainer.ofName("cel.expr.conformance.test")) .addVar("unknown_attr", SimpleType.INT) .addVar("my_list", ListType.create(SimpleType.INT)) - .setOptions(CelOptions.current().enableUnknownTracking(true).build()) + .setOptions( + CelOptions.current() + .enableHeterogeneousNumericComparisons(true) + .enableUnknownTracking(true) + .build()) .build(); CelUnknownSet result = @@ -170,6 +175,59 @@ public void advanceEvaluation_withUnknownTracking_noSelfReferenceInMerge(String assertThat(result.attributes()).containsExactly(CelAttribute.create("unknown_attr")); } + @Test + public void advanceEvaluation_withUnknownContext_tracksUnknowns() throws Exception { + Cel cel = + runtimeFlavor + .builder() + .setOptions( + CelOptions.current() + .enableHeterogeneousNumericComparisons(true) + .enableUnknownTracking(true) + .build()) + .setContainer(CelContainer.ofName("com.google")) + .addVar("com.google.a", SimpleType.BOOL) + .addVar("com.google.b", SimpleType.BOOL) + .setResultType(SimpleType.BOOL) + .build(); + CelRuntime.Program program = cel.createProgram(cel.compile("b ? a : false").getAst()); + UnknownContext context = + UnknownContext.create( + name -> name.equals("com.google.b") ? Optional.of(true) : Optional.empty(), + ImmutableList.of(CelAttributePattern.fromQualifiedIdentifier("com.google.a"))); + + CelUnknownSet unknownResult = (CelUnknownSet) program.advanceEvaluation(context); + + assertThat(unknownResult.attributes()) + .containsExactly(CelAttribute.fromQualifiedIdentifier("com.google.a")); + } + + @Test + public void advanceEvaluation_withResolvedUnknownContext_evaluatesResult() throws Exception { + Cel cel = + runtimeFlavor + .builder() + .setOptions( + CelOptions.current() + .enableHeterogeneousNumericComparisons(true) + .enableUnknownTracking(true) + .build()) + .setContainer(CelContainer.ofName("com.google")) + .addVar("com.google.a", SimpleType.BOOL) + .addVar("com.google.b", SimpleType.BOOL) + .setResultType(SimpleType.BOOL) + .build(); + CelRuntime.Program program = cel.createProgram(cel.compile("a || b").getAst()); + ImmutableMap vars = + ImmutableMap.of("com.google.a", true, "com.google.b", false); + UnknownContext context = + UnknownContext.create(name -> Optional.ofNullable(vars.get(name)), ImmutableList.of()); + + Object resolvedResult = program.advanceEvaluation(context); + + assertThat(resolvedResult).isEqualTo(true); + } + @Test public void newWellKnownTypeMessage_withDifferentDescriptorInstance() throws Exception { CelCompiler celCompiler = diff --git a/runtime/src/test/java/dev/cel/runtime/UnknownContextTest.java b/runtime/src/test/java/dev/cel/runtime/UnknownContextTest.java index 428fa6b26..0ab030c9b 100644 --- a/runtime/src/test/java/dev/cel/runtime/UnknownContextTest.java +++ b/runtime/src/test/java/dev/cel/runtime/UnknownContextTest.java @@ -167,4 +167,20 @@ public void withResolvedAttributes_attributeResolverPartialsShadowed() { CelAttribute.fromQualifiedIdentifier("qualified.Identifier.field2"))) .isEmpty(); } + + @Test + public void toPartialVars_preservesResolverAndUnknowns() { + CelAttributePattern pattern = + CelAttributePattern.fromQualifiedIdentifier("qualified.Identifier"); + UnknownContext context = + UnknownContext.create( + name -> name.equals("x") ? Optional.of("val") : Optional.empty(), + ImmutableList.of(pattern)); + + PartialVars partialVars = context.toPartialVars(); + + assertThat(partialVars.resolver().find("x")).hasValue("val"); + assertThat(partialVars.resolver().find("missing")).isEmpty(); + assertThat(partialVars.unknowns()).containsExactly(pattern); + } }