Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions bundle/src/test/java/dev/cel/bundle/CelImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2119,17 +2119,11 @@ public void eval_withJsonFieldName_runtimeOptionDisabled_throws() throws Excepti
CelAbstractSyntaxTree ast = celCompiler.compile("file.int64CamelCaseJsonName").getAst();

CelEvaluationException e =
assertThrows(
CelEvaluationException.class,
() ->
celRuntime
.createProgram(ast)
.eval(ImmutableMap.of("file", SingleFile.getDefaultInstance())));
assertThrows(CelEvaluationException.class, () -> celRuntime.createProgram(ast));

assertThat(e)
.hasMessageThat()
.contains(
"field 'int64CamelCaseJsonName' is not declared in message"
+ " 'dev.cel.testing.testdata.SingleFile");
.contains("json_name extension requires CelOptions.enableJsonFieldNames(true)");
}

@Test
Expand Down
14 changes: 9 additions & 5 deletions common/src/main/java/dev/cel/common/CelSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -367,19 +367,19 @@ public CelSource build() {
public abstract static class Extension {

/** Identifier for the extension. Example: constant_folding */
abstract String id();
public abstract String id();

/**
* Version info. May be skipped if it isn't meaningful for the extension. (for example
* constant_folding might always be v0.0).
*/
abstract Version version();
public abstract Version version();

/**
* If set, the listed components must understand the extension for the expression to evaluate
* correctly.
*/
abstract ImmutableList<Component> affectedComponents();
public abstract ImmutableList<Component> affectedComponents();

/** Version of the extension */
@AutoValue
Expand All @@ -390,18 +390,20 @@ public abstract static class Version {
* Major version changes indicate different required support level from the required
* components.
*/
abstract long major();
public abstract long major();

/**
* Minor version changes must not change the observed behavior from existing implementations,
* but may be provided informational.
*/
abstract long minor();
public abstract long minor();

/** Create a new instance of Version with the provided major and minor values. */
public static Version of(long major, long minor) {
return new AutoValue_CelSource_Extension_Version(major, minor);
}

Version() {}
}

/** CEL component specifier. */
Expand All @@ -427,5 +429,7 @@ public static Extension create(String id, Version version, Iterable<Component> c
public static Extension create(String id, Version version, Component... components) {
return create(id, version, Arrays.asList(components));
}

Extension() {}
}
}
2 changes: 2 additions & 0 deletions conformance/src/test/java/dev/cel/conformance/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ java_library(
deps = [
"//:java_truth",
"//checker:checker_builder",
"//common:cel_ast",
"//common:cel_source",
"//common:compiler_common",
"//common:container",
"//common:options",
Expand Down
22 changes: 19 additions & 3 deletions conformance/src/test/java/dev/cel/conformance/ConformanceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@
import com.google.protobuf.TypeRegistry;
import dev.cel.checker.CelChecker;
import dev.cel.checker.CelCheckerBuilder;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelContainer;
import dev.cel.common.CelIssue;
import dev.cel.common.CelOptions;
import dev.cel.common.CelSource.Extension;
import dev.cel.common.CelSource.Extension.Component;
import dev.cel.common.CelSource.Extension.Version;
import dev.cel.common.CelValidationResult;
import dev.cel.common.CelVarDecl;
import dev.cel.common.ast.CelBlock;
Expand Down Expand Up @@ -227,7 +231,19 @@ public void evaluate() throws Throwable {
response = getChecker(test).check(response.getAst());
}
assertThat(response.getErrors()).isEmpty();
Type resultType = CelProtoTypes.celTypeToType(response.getAst().getResultType());
CelAbstractSyntaxTree ast = response.getAst();
if (ast.getExpr().callOrDefault().function().equals(CelBlock.FUNCTION_NAME)) {
ast =
CelAbstractSyntaxTree.newCheckedAst(
ast.getExpr(),
ast.getSource().toBuilder()
.addAllExtensions(
Extension.create("cel_block", Version.of(1, 1), Component.COMPONENT_RUNTIME))
.build(),
ast.getReferenceMap(),
ast.getTypeMap());
}
Type resultType = CelProtoTypes.celTypeToType(ast.getResultType());

if (test.getCheckOnly()) {
assertThat(test.hasTypedResult()).isTrue();
Expand All @@ -244,8 +260,8 @@ public void evaluate() throws Throwable {
ExprValue result = null;
CelEvaluationException error = null;
try {
Program program = runtime.createProgram(response.getAst());
result = toExprValue(program.eval(getBindings(test)), response.getAst().getResultType());
Program program = runtime.createProgram(ast);
result = toExprValue(program.eval(getBindings(test)), ast.getResultType());
} catch (CelEvaluationException e) {
error = e;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -827,10 +827,17 @@ private CelAbstractSyntaxTree compileUsingInternalFunctions(String expression, b
indexExpr.ident().setName(internalIdentName);
});

CelAbstractSyntaxTree parsedAst =
CelAbstractSyntaxTree.newParsedAst(
mutableAst.toParsedAst().getExpr(),
astToModify.getSource().toBuilder()
.addAllExtensions(
Extension.create("cel_block", Version.of(1L, 1L), Component.COMPONENT_RUNTIME))
.build());
if (parsedOnly) {
return mutableAst.toParsedAst();
return parsedAst;
}
return celForEvaluatingBlock.check(mutableAst.toParsedAst()).getAst();
return celForEvaluatingBlock.check(parsedAst).getAst();
}

private CelAbstractSyntaxTree compileUsingInternalFunctions(String expression)
Expand Down
8 changes: 2 additions & 6 deletions runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ java_library(
":planner_helpers",
"//:auto_value",
"//common:cel_ast",
"//common:cel_source",
"//common:container",
"//common:operator",
"//common:options",
Expand Down Expand Up @@ -555,16 +556,13 @@ java_library(
":planned_interpretable",
":planner_helpers",
"//common/ast",
"//common/types",
"//common/types:cel_types",
"//common/types:type_providers",
"//common/values",
"//common/values:cel_byte_string",
"//common/values:optimized_select_traversal",
"//common/values:select_field",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
"@maven//:org_jspecify_jspecify",
],
)

Expand Down Expand Up @@ -720,6 +718,7 @@ cel_android_library(
":planner_helpers_android",
"//:auto_value",
"//common:cel_ast_android",
"//common:cel_source_android",
"//common:container_android",
"//common:operator_android",
"//common:options",
Expand Down Expand Up @@ -1232,14 +1231,11 @@ cel_android_library(
":planner_helpers_android",
"//common/ast:ast_android",
"//common/types:cel_types_android",
"//common/types:type_providers_android",
"//common/types:types_android",
"//common/values:cel_byte_string",
"//common/values:optimized_select_traversal_android",
"//common/values:select_field_android",
"//common/values:values_android",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:org_jspecify_jspecify",
"@maven_android//:com_google_guava_guava",
],
)
Expand Down
70 changes: 57 additions & 13 deletions runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package dev.cel.runtime.planner;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.auto.value.AutoValue;
Expand All @@ -28,6 +29,9 @@
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelContainer;
import dev.cel.common.CelOptions;
import dev.cel.common.CelSource.Extension;
import dev.cel.common.CelSource.Extension.Component;
import dev.cel.common.CelSource.Extension.Version;
import dev.cel.common.Operator;
import dev.cel.common.annotations.Internal;
import dev.cel.common.ast.CelBlock;
Expand Down Expand Up @@ -60,6 +64,7 @@
import dev.cel.runtime.RuntimeEquality;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import org.jspecify.annotations.Nullable;
Expand All @@ -72,6 +77,15 @@
@Internal
public final class ProgramPlanner {
private static final String MAP_INSERT_FUNCTION = "cel.@mapInsert";
private static final String CEL_BLOCK_EXTENSION = "cel_block";
private static final String JSON_NAME_EXTENSION = "json_name";
private static final String SELECT_OPTIMIZATION_EXTENSION = "select_optimization";

private static final ImmutableMap<String, Version> SUPPORTED_RUNTIME_EXTENSIONS =
ImmutableMap.of(
CEL_BLOCK_EXTENSION, Version.of(1, 1),
JSON_NAME_EXTENSION, Version.of(1, 1),
SELECT_OPTIMIZATION_EXTENSION, Version.of(1, 0));

private final CelTypeProvider typeProvider;
private final CelValueProvider valueProvider;
Expand Down Expand Up @@ -102,7 +116,7 @@ public PlannedProgram plan(CelAbstractSyntaxTree ast) throws CelEvaluationExcept
ErrorMetadata errorMetadata =
ErrorMetadata.create(ast.getSource().getPositionsMap(), ast.getSource().getDescription());
try {
PlannerContext ctx = PlannerContext.create(ast);
PlannerContext ctx = PlannerContext.create(ast, options);
plannedInterpretable =
CelBlock.extract(ast)
.map(celBlock -> planBlock(celBlock, ctx))
Expand Down Expand Up @@ -248,6 +262,11 @@ private PlannedInterpretable planCall(CelExpr expr, PlannerContext ctx) {
// map key lookup on Map, and SelectableValue fallback).
if (functionName.equals(OptimizedSelectPlanner.CEL_ATTRIBUTE_FUNCTION_NAME)
|| functionName.equals(OptimizedSelectPlanner.CEL_HAS_FIELD_FUNCTION_NAME)) {
checkArgument(
ctx.runtimeExtensions.contains(SELECT_OPTIMIZATION_EXTENSION),
"%s requires the %s AST extension",
functionName,
SELECT_OPTIMIZATION_EXTENSION);
return optimizedSelectPlanner.plan(expr, functionName, operandExpr -> plan(operandExpr, ctx));
}

Expand Down Expand Up @@ -382,6 +401,11 @@ private PlannedInterpretable planCall(CelExpr expr, PlannerContext ctx) {
}

private PlannedInterpretable planBlock(CelBlock celBlock, PlannerContext ctx) {
checkArgument(
ctx.runtimeExtensions.contains(CEL_BLOCK_EXTENSION),
"%s requires the %s AST extension",
CelBlock.FUNCTION_NAME,
CEL_BLOCK_EXTENSION);
ImmutableList<CelExpr> indices = celBlock.indices();

PlannedInterpretable[] slotExprs = new PlannedInterpretable[indices.size()];
Expand Down Expand Up @@ -716,23 +740,20 @@ private static Builder newBuilder() {
}
}

static final class PlannerContext {
private static final class PlannerContext {
private final CelAbstractSyntaxTree ast;
private final HashMap<String, Integer> localVars = new HashMap<>();

CelAbstractSyntaxTree ast() {
return ast;
}
private final ImmutableSet<String> runtimeExtensions;
private final Map<String, Integer> localVars;

ImmutableMap<Long, CelReference> referenceMap() {
private ImmutableMap<Long, CelReference> referenceMap() {
return ast.getReferenceMap();
}

ImmutableMap<Long, CelType> typeMap() {
private ImmutableMap<Long, CelType> typeMap() {
return ast.getTypeMap();
}

boolean isChecked() {
private boolean isChecked() {
return ast.isChecked();
}

Expand Down Expand Up @@ -766,12 +787,35 @@ private boolean isLocalVar(String name) {
return localVars.containsKey(name);
}

static PlannerContext create(CelAbstractSyntaxTree ast) {
return new PlannerContext(ast);
private static PlannerContext create(CelAbstractSyntaxTree ast, CelOptions options) {
ImmutableSet.Builder<String> runtimeExtensions = ImmutableSet.builder();
for (Extension extension : ast.getSource().getExtensions()) {
if (!extension.affectedComponents().contains(Component.COMPONENT_RUNTIME)) {
continue;
}
Version version = extension.version();
Version supportedVersion = SUPPORTED_RUNTIME_EXTENSIONS.get(extension.id());
checkArgument(
supportedVersion != null
&& version.major() == supportedVersion.major()
&& version.minor() <= supportedVersion.minor(),
"unsupported CEL extension: %s@%s.%s",
extension.id(),
version.major(),
version.minor());
runtimeExtensions.add(extension.id());
}
ImmutableSet<String> extensions = runtimeExtensions.build();
checkArgument(
!extensions.contains(JSON_NAME_EXTENSION) || options.enableJsonFieldNames(),
"json_name extension requires CelOptions.enableJsonFieldNames(true)");
return new PlannerContext(ast, extensions);
}

private PlannerContext(CelAbstractSyntaxTree ast) {
private PlannerContext(CelAbstractSyntaxTree ast, ImmutableSet<String> runtimeExtensions) {
this.ast = checkNotNull(ast);
this.runtimeExtensions = runtimeExtensions;
this.localVars = new HashMap<>();
}
}

Expand Down
2 changes: 2 additions & 0 deletions runtime/src/test/java/dev/cel/runtime/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ java_library(
"//common:container",
"//common:error_codes",
"//common:options",
"//common:proto_ast",
"//common:proto_v1alpha1_ast",
"//common/ast",
"//common/ast:cel_block",
Expand Down Expand Up @@ -111,6 +112,7 @@ java_library(
"//testing/protos:single_file_java_proto",
"//testing/protos:test_all_types_cel_java_proto2",
"//testing/protos:test_all_types_cel_java_proto3",
"@cel_spec//proto/cel/expr:checked_java_proto",
"@cel_spec//proto/cel/expr/conformance/proto2:test_all_types_java_proto",
"@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto",
"@com_google_googleapis//google/api/expr/v1alpha1:expr_java_proto",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertThrows;

import com.google.api.expr.v1alpha1.CheckedExpr;
import dev.cel.expr.CheckedExpr;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
Expand All @@ -40,7 +40,7 @@
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOptions;
import dev.cel.common.CelOverloadDecl;
import dev.cel.common.CelProtoV1Alpha1AbstractSyntaxTree;
import dev.cel.common.CelProtoAbstractSyntaxTree;
import dev.cel.common.ast.CelBlock;
import dev.cel.common.internal.ProtoTimeUtils;
import dev.cel.common.types.ListType;
Expand Down Expand Up @@ -1879,11 +1879,11 @@ public void checkedExprWireRoundTrip_preservesOptimizedSelectsUnderVersionSkew()
.getAst();
CelAbstractSyntaxTree optimizedAst = serverOptimizer.optimize(ast);
byte[] serializedCheckedExpr =
CelProtoV1Alpha1AbstractSyntaxTree.fromCelAst(optimizedAst).toCheckedExpr().toByteArray();
CelProtoAbstractSyntaxTree.fromCelAst(optimizedAst).toCheckedExpr().toByteArray();
CheckedExpr deserializedCheckedExpr =
CheckedExpr.parseFrom(serializedCheckedExpr, ExtensionRegistryLite.getEmptyRegistry());
CelAbstractSyntaxTree deserializedAst =
CelProtoV1Alpha1AbstractSyntaxTree.fromCheckedExpr(deserializedCheckedExpr).getAst();
CelProtoAbstractSyntaxTree.fromCheckedExpr(deserializedCheckedExpr).getAst();

Object result =
clientRuntime
Expand Down
Loading
Loading