From cc2961ff69fd712dc895879fa885b1578d077cad Mon Sep 17 00:00:00 2001 From: Sokwhan Huh Date: Tue, 24 Jun 2025 10:34:40 -0700 Subject: [PATCH] Make CelRuntime.Program an interface PiperOrigin-RevId: 775294514 --- .../src/main/java/dev/cel/runtime/BUILD.bazel | 1 + .../main/java/dev/cel/runtime/CelRuntime.java | 142 ++------------ .../dev/cel/runtime/CelRuntimeLegacyImpl.java | 2 +- .../java/dev/cel/runtime/ProgramImpl.java | 177 ++++++++++++++++++ 4 files changed, 195 insertions(+), 127 deletions(-) create mode 100644 runtime/src/main/java/dev/cel/runtime/ProgramImpl.java diff --git a/runtime/src/main/java/dev/cel/runtime/BUILD.bazel b/runtime/src/main/java/dev/cel/runtime/BUILD.bazel index 754b360ae..7e8222570 100644 --- a/runtime/src/main/java/dev/cel/runtime/BUILD.bazel +++ b/runtime/src/main/java/dev/cel/runtime/BUILD.bazel @@ -459,6 +459,7 @@ RUNTIME_SOURCES = [ "CelRuntimeLibrary.java", "CelVariableResolver.java", "HierarchicalVariableResolver.java", + "ProgramImpl.java", "UnknownContext.java", ] diff --git a/runtime/src/main/java/dev/cel/runtime/CelRuntime.java b/runtime/src/main/java/dev/cel/runtime/CelRuntime.java index 95b4d7ca5..d730701e7 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelRuntime.java +++ b/runtime/src/main/java/dev/cel/runtime/CelRuntime.java @@ -14,8 +14,6 @@ package dev.cel.runtime; -import com.google.auto.value.AutoValue; -import com.google.common.base.Preconditions; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Immutable; import javax.annotation.concurrent.ThreadSafe; @@ -23,7 +21,6 @@ import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelOptions; import java.util.Map; -import java.util.Optional; /** * The CelRuntime creates executable {@code Program} instances from {@code CelAbstractSyntaxTree} @@ -39,112 +36,69 @@ public interface CelRuntime { CelRuntimeBuilder toRuntimeBuilder(); /** Creates an evaluable {@code Program} instance which is thread-safe and immutable. */ - @AutoValue @Immutable - abstract class Program implements CelLiteRuntime.Program { - - @Override - public Object eval() throws CelEvaluationException { - return evalInternal(Activation.EMPTY); - } - - @Override - public Object eval(Map mapValue) throws CelEvaluationException { - return evalInternal(Activation.copyOf(mapValue)); - } + interface Program extends CelLiteRuntime.Program { /** Evaluate the expression using {@code message} fields as the source of input variables. */ - public Object eval(Message message) throws CelEvaluationException { - return evalInternal(ProtoMessageActivationFactory.fromProto(message, getOptions())); - } + Object eval(Message message) throws CelEvaluationException; /** Evaluate a compiled program with a custom variable {@code resolver}. */ - public Object eval(CelVariableResolver resolver) throws CelEvaluationException { - return evalInternal((name) -> resolver.find(name).orElse(null)); - } + Object eval(CelVariableResolver resolver) throws CelEvaluationException; /** * Evaluate a compiled program with a custom variable {@code resolver} and late-bound functions * {@code lateBoundFunctionResolver}. */ - public Object eval(CelVariableResolver resolver, CelFunctionResolver lateBoundFunctionResolver) - throws CelEvaluationException { - return evalInternal( - (name) -> resolver.find(name).orElse(null), - lateBoundFunctionResolver, - CelEvaluationListener.noOpListener()); - } - - @Override - public Object eval(Map mapValue, CelFunctionResolver lateBoundFunctionResolver) - throws CelEvaluationException { - return evalInternal( - Activation.copyOf(mapValue), - lateBoundFunctionResolver, - CelEvaluationListener.noOpListener()); - } + Object eval(CelVariableResolver resolver, CelFunctionResolver lateBoundFunctionResolver) + throws CelEvaluationException; /** * Trace evaluates a compiled program without any variables and invokes the listener as * evaluation progresses through the AST. */ - public Object trace(CelEvaluationListener listener) throws CelEvaluationException { - return evalInternal(Activation.EMPTY, listener); - } + Object trace(CelEvaluationListener listener) throws CelEvaluationException; /** * Trace evaluates a compiled program using a {@code mapValue} as the source of input variables. * The listener is invoked as evaluation progresses through the AST. */ - public Object trace(Map mapValue, CelEvaluationListener listener) - throws CelEvaluationException { - return evalInternal(Activation.copyOf(mapValue), listener); - } + Object trace(Map mapValue, CelEvaluationListener listener) + throws CelEvaluationException; /** * Trace evaluates a compiled program using {@code message} fields as the source of input * variables. The listener is invoked as evaluation progresses through the AST. */ - public Object trace(Message message, CelEvaluationListener listener) - throws CelEvaluationException { - return evalInternal(ProtoMessageActivationFactory.fromProto(message, getOptions()), listener); - } + Object trace(Message message, CelEvaluationListener listener) throws CelEvaluationException; /** * Trace evaluates a compiled program using a custom variable {@code resolver}. The listener is * invoked as evaluation progresses through the AST. */ - public Object trace(CelVariableResolver resolver, CelEvaluationListener listener) - throws CelEvaluationException { - return evalInternal((name) -> resolver.find(name).orElse(null), listener); - } + Object trace(CelVariableResolver resolver, CelEvaluationListener listener) + throws CelEvaluationException; /** * Trace evaluates a compiled program using a custom variable {@code resolver} and late-bound * functions {@code lateBoundFunctionResolver}. The listener is invoked as evaluation progresses * through the AST. */ - public Object trace( + Object trace( CelVariableResolver resolver, CelFunctionResolver lateBoundFunctionResolver, CelEvaluationListener listener) - throws CelEvaluationException { - return evalInternal( - (name) -> resolver.find(name).orElse(null), lateBoundFunctionResolver, listener); - } + throws CelEvaluationException; /** * Trace evaluates a compiled program using a {@code mapValue} as the source of input variables * and late-bound functions {@code lateBoundFunctionResolver}. The listener is invoked as * evaluation progresses through the AST. */ - public Object trace( + Object trace( Map mapValue, CelFunctionResolver lateBoundFunctionResolver, CelEvaluationListener listener) - throws CelEvaluationException { - return evalInternal(Activation.copyOf(mapValue), lateBoundFunctionResolver, listener); - } + throws CelEvaluationException; /** * Advance evaluation based on the current unknown context. @@ -155,70 +109,6 @@ public Object trace( *

If no unknowns are declared in the context or {@link CelOptions#enableUnknownTracking() * UnknownTracking} is disabled, this is equivalent to eval. */ - public Object advanceEvaluation(UnknownContext context) throws CelEvaluationException { - return evalInternal(context, Optional.empty(), CelEvaluationListener.noOpListener()); - } - - private Object evalInternal(GlobalResolver resolver) throws CelEvaluationException { - return evalInternal( - UnknownContext.create(resolver), Optional.empty(), CelEvaluationListener.noOpListener()); - } - - private Object evalInternal(GlobalResolver resolver, CelEvaluationListener listener) - throws CelEvaluationException { - return evalInternal(UnknownContext.create(resolver), Optional.empty(), listener); - } - - private Object evalInternal( - GlobalResolver resolver, - CelFunctionResolver lateBoundFunctionResolver, - CelEvaluationListener listener) - throws CelEvaluationException { - return evalInternal( - UnknownContext.create(resolver), Optional.of(lateBoundFunctionResolver), listener); - } - - /** - * Evaluate an expr node with an UnknownContext (an activation annotated with which attributes - * are unknown). - */ - private Object evalInternal( - UnknownContext context, - Optional lateBoundFunctionResolver, - CelEvaluationListener listener) - throws CelEvaluationException { - Interpretable impl = getInterpretable(); - if (getOptions().enableUnknownTracking()) { - Preconditions.checkState( - impl instanceof UnknownTrackingInterpretable, - "Environment misconfigured. Requested unknown tracking without a compatible" - + " implementation."); - - UnknownTrackingInterpretable interpreter = (UnknownTrackingInterpretable) impl; - return interpreter.evalTrackingUnknowns( - RuntimeUnknownResolver.builder() - .setResolver(context.variableResolver()) - .setAttributeResolver(context.createAttributeResolver()) - .build(), - lateBoundFunctionResolver, - listener); - } else { - if (lateBoundFunctionResolver.isPresent()) { - return impl.eval(context.variableResolver(), lateBoundFunctionResolver.get(), listener); - } - return impl.eval(context.variableResolver(), listener); - } - } - - /** Get the underlying {@link Interpretable} for the {@code Program}. */ - abstract Interpretable getInterpretable(); - - /** Get the {@code CelOptions} configured for this program. */ - abstract CelOptions getOptions(); - - /** Instantiate a new {@code Program} from the input {@code interpretable}. */ - static Program from(Interpretable interpretable, CelOptions options) { - return new AutoValue_CelRuntime_Program(interpretable, options); - } + Object advanceEvaluation(UnknownContext context) throws CelEvaluationException; } } diff --git a/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java b/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java index c9a48a01c..2a6131723 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java +++ b/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java @@ -89,7 +89,7 @@ public final class CelRuntimeLegacyImpl implements CelRuntime { @Override public CelRuntime.Program createProgram(CelAbstractSyntaxTree ast) { checkState(ast.isChecked(), "programs must be created from checked expressions"); - return CelRuntime.Program.from(interpreter.createInterpretable(ast), options); + return ProgramImpl.from(interpreter.createInterpretable(ast), options); } @Override diff --git a/runtime/src/main/java/dev/cel/runtime/ProgramImpl.java b/runtime/src/main/java/dev/cel/runtime/ProgramImpl.java new file mode 100644 index 000000000..6f36027ab --- /dev/null +++ b/runtime/src/main/java/dev/cel/runtime/ProgramImpl.java @@ -0,0 +1,177 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dev.cel.runtime; + +import com.google.auto.value.AutoValue; +import com.google.common.base.Preconditions; +import com.google.errorprone.annotations.Immutable; +import com.google.protobuf.Message; +import dev.cel.common.CelOptions; +import dev.cel.runtime.CelRuntime.Program; +import java.util.Map; +import java.util.Optional; + +/** Internal implementation of a {@link CelRuntime.Program} */ +@AutoValue +@Immutable +abstract class ProgramImpl implements CelRuntime.Program { + + @Override + public Object eval() throws CelEvaluationException { + return evalInternal(Activation.EMPTY); + } + + @Override + public Object eval(Map mapValue) throws CelEvaluationException { + return evalInternal(Activation.copyOf(mapValue)); + } + + @Override + public Object eval(Message message) throws CelEvaluationException { + return evalInternal(ProtoMessageActivationFactory.fromProto(message, getOptions())); + } + + @Override + public Object eval(CelVariableResolver resolver) throws CelEvaluationException { + return evalInternal((name) -> resolver.find(name).orElse(null)); + } + + @Override + public Object eval(CelVariableResolver resolver, CelFunctionResolver lateBoundFunctionResolver) + throws CelEvaluationException { + return evalInternal( + (name) -> resolver.find(name).orElse(null), + lateBoundFunctionResolver, + CelEvaluationListener.noOpListener()); + } + + @Override + public Object eval(Map mapValue, CelFunctionResolver lateBoundFunctionResolver) + throws CelEvaluationException { + return evalInternal( + Activation.copyOf(mapValue), + lateBoundFunctionResolver, + CelEvaluationListener.noOpListener()); + } + + @Override + public Object trace(CelEvaluationListener listener) throws CelEvaluationException { + return evalInternal(Activation.EMPTY, listener); + } + + @Override + public Object trace(Map mapValue, CelEvaluationListener listener) + throws CelEvaluationException { + return evalInternal(Activation.copyOf(mapValue), listener); + } + + @Override + public Object trace(Message message, CelEvaluationListener listener) + throws CelEvaluationException { + return evalInternal(ProtoMessageActivationFactory.fromProto(message, getOptions()), listener); + } + + @Override + public Object trace(CelVariableResolver resolver, CelEvaluationListener listener) + throws CelEvaluationException { + return evalInternal((name) -> resolver.find(name).orElse(null), listener); + } + + @Override + public Object trace( + CelVariableResolver resolver, + CelFunctionResolver lateBoundFunctionResolver, + CelEvaluationListener listener) + throws CelEvaluationException { + return evalInternal( + (name) -> resolver.find(name).orElse(null), lateBoundFunctionResolver, listener); + } + + @Override + public Object trace( + Map mapValue, + CelFunctionResolver lateBoundFunctionResolver, + CelEvaluationListener listener) + throws CelEvaluationException { + return evalInternal(Activation.copyOf(mapValue), lateBoundFunctionResolver, listener); + } + + @Override + public Object advanceEvaluation(UnknownContext context) throws CelEvaluationException { + return evalInternal(context, Optional.empty(), CelEvaluationListener.noOpListener()); + } + + private Object evalInternal(GlobalResolver resolver) throws CelEvaluationException { + return evalInternal( + UnknownContext.create(resolver), Optional.empty(), CelEvaluationListener.noOpListener()); + } + + private Object evalInternal(GlobalResolver resolver, CelEvaluationListener listener) + throws CelEvaluationException { + return evalInternal(UnknownContext.create(resolver), Optional.empty(), listener); + } + + private Object evalInternal( + GlobalResolver resolver, + CelFunctionResolver lateBoundFunctionResolver, + CelEvaluationListener listener) + throws CelEvaluationException { + return evalInternal( + UnknownContext.create(resolver), Optional.of(lateBoundFunctionResolver), listener); + } + + /** + * Evaluate an expr node with an UnknownContext (an activation annotated with which attributes are + * unknown). + */ + private Object evalInternal( + UnknownContext context, + Optional lateBoundFunctionResolver, + CelEvaluationListener listener) + throws CelEvaluationException { + Interpretable impl = getInterpretable(); + if (getOptions().enableUnknownTracking()) { + Preconditions.checkState( + impl instanceof UnknownTrackingInterpretable, + "Environment misconfigured. Requested unknown tracking without a compatible" + + " implementation."); + + UnknownTrackingInterpretable interpreter = (UnknownTrackingInterpretable) impl; + return interpreter.evalTrackingUnknowns( + RuntimeUnknownResolver.builder() + .setResolver(context.variableResolver()) + .setAttributeResolver(context.createAttributeResolver()) + .build(), + lateBoundFunctionResolver, + listener); + } else { + if (lateBoundFunctionResolver.isPresent()) { + return impl.eval(context.variableResolver(), lateBoundFunctionResolver.get(), listener); + } + return impl.eval(context.variableResolver(), listener); + } + } + + /** Get the underlying {@link Interpretable} for the {@code Program}. */ + abstract Interpretable getInterpretable(); + + /** Get the {@code CelOptions} configured for this program. */ + abstract CelOptions getOptions(); + + /** Instantiate a new {@code Program} from the input {@code interpretable}. */ + static Program from(Interpretable interpretable, CelOptions options) { + return new AutoValue_ProgramImpl(interpretable, options); + } +}