Skip to content

Commit 9c3ec6e

Browse files
CEL Dev Teamcopybara-github
authored andcommitted
Port CEL-Go basic Protobuf constant folding syntax to CEL-Java
PiperOrigin-RevId: 944425297
1 parent ff7bccf commit 9c3ec6e

9 files changed

Lines changed: 362 additions & 12 deletions

File tree

bundle/src/main/java/dev/cel/bundle/CelBuilder.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ public interface CelBuilder {
211211
@CanIgnoreReturnValue
212212
CelBuilder setValueProvider(CelValueProvider celValueProvider);
213213

214+
/** Returns the configured {@link CelValueProvider}, or null if not set. */
215+
CelValueProvider getValueProvider();
216+
214217
/**
215218
* Set the {@code typeProvider} for use with type-checking expressions.
216219
*

bundle/src/main/java/dev/cel/bundle/CelImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,11 @@ public CelBuilder setValueProvider(CelValueProvider celValueProvider) {
317317
return this;
318318
}
319319

320+
@Override
321+
public CelValueProvider getValueProvider() {
322+
return runtimeBuilder.getValueProvider();
323+
}
324+
320325
@Override
321326
@Deprecated
322327
public Builder setTypeProvider(TypeProvider typeProvider) {

optimizer/src/main/java/dev/cel/optimizer/optimizers/BUILD.bazel

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ java_library(
3131
"//common/navigation:common",
3232
"//common/navigation:mutable_navigation",
3333
"//common/types",
34+
"//common/types:type_providers",
35+
"//common/values",
36+
"//common/values:cel_value",
37+
"//common/values:cel_value_provider",
3438
"//extensions:optional_library",
3539
"//optimizer:ast_optimizer",
3640
"//optimizer:mutable_ast",

optimizer/src/main/java/dev/cel/optimizer/optimizers/ConstantFoldingOptimizer.java

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.common.collect.ImmutableSet;
2525
import com.google.errorprone.annotations.CanIgnoreReturnValue;
2626
import dev.cel.bundle.Cel;
27+
import dev.cel.bundle.CelBuilder;
2728
import dev.cel.common.CelAbstractSyntaxTree;
2829
import dev.cel.common.CelMutableAst;
2930
import dev.cel.common.CelSource;
@@ -42,7 +43,13 @@
4243
import dev.cel.common.navigation.CelNavigableMutableAst;
4344
import dev.cel.common.navigation.CelNavigableMutableExpr;
4445
import dev.cel.common.navigation.TraversalOrder;
46+
import dev.cel.common.types.CelType;
47+
import dev.cel.common.types.CelTypeProvider;
4548
import dev.cel.common.types.SimpleType;
49+
import dev.cel.common.types.StructType;
50+
import dev.cel.common.values.CelValue;
51+
import dev.cel.common.values.CelValueProvider;
52+
import dev.cel.common.values.StructValue;
4653
import dev.cel.extensions.CelOptionalLibrary.Function;
4754
import dev.cel.optimizer.AstMutator;
4855
import dev.cel.optimizer.CelAstOptimizer;
@@ -59,6 +66,7 @@
5966
import java.util.List;
6067
import java.util.Map;
6168
import java.util.Optional;
69+
import org.jspecify.annotations.Nullable;
6270

6371
/**
6472
* Performs optimization for inlining constant scalar and aggregate literal values within function
@@ -95,8 +103,16 @@ private static CelMutableExpr newOptionalNoneExpr() {
95103
@Override
96104
public OptimizationResult optimize(CelAbstractSyntaxTree ast, Cel cel)
97105
throws CelOptimizationException {
106+
CelBuilder builder = cel.toCelBuilder();
107+
CelValueProvider valueProvider;
108+
try {
109+
valueProvider = builder.getValueProvider();
110+
} catch (UnsupportedOperationException e) {
111+
// Legacy runtime does not support getValueProvider and may throw.
112+
valueProvider = null;
113+
}
98114
// Override the environment's expected type to generally allow all subtrees to be folded.
99-
Cel optimizerEnv = cel.toCelBuilder().setResultType(SimpleType.DYN).build();
115+
Cel optimizerEnv = builder.setResultType(SimpleType.DYN).build();
100116

101117
CelMutableAst mutableAst = CelMutableAst.fromCelAst(ast);
102118
int iterCount = 0;
@@ -123,7 +139,7 @@ public OptimizationResult optimize(CelAbstractSyntaxTree ast, Cel cel)
123139
if (!mutatedResult.isPresent()) {
124140
// Evaluate the call then fold
125141
try {
126-
mutatedResult = maybeFold(optimizerEnv, mutableAst, foldableExpr);
142+
mutatedResult = maybeFold(optimizerEnv, valueProvider, mutableAst, foldableExpr);
127143
} catch (CelEvaluationException e) {
128144
throw new CelOptimizationException(
129145
"Constant folding failure. Failed to evaluate subtree due to: " + e.getMessage(),
@@ -290,7 +306,10 @@ private static boolean isNestedComprehension(CelNavigableMutableExpr expr) {
290306
}
291307

292308
private Optional<CelMutableAst> maybeFold(
293-
Cel cel, CelMutableAst mutableAst, CelNavigableMutableExpr node)
309+
Cel cel,
310+
CelValueProvider valueProvider,
311+
CelMutableAst mutableAst,
312+
CelNavigableMutableExpr node)
294313
throws CelOptimizationException, CelEvaluationException {
295314
Object result;
296315
try {
@@ -305,25 +324,33 @@ private Optional<CelMutableAst> maybeFold(
305324
// ex2: optional.ofNonZeroValue(5) -> optional.of(5)
306325
if (result instanceof Optional<?>) {
307326
Optional<?> optResult = ((Optional<?>) result);
308-
return maybeRewriteOptional(optResult, mutableAst, node.expr());
327+
return maybeRewriteOptional(
328+
cel.getTypeProvider(), valueProvider, optResult, mutableAst, node.expr());
309329
}
310330

311-
CelMutableExpr adaptedResult = maybeAdaptEvaluatedResult(result).orElse(null);
331+
CelMutableExpr adaptedResult =
332+
maybeAdaptEvaluatedResult(cel.getTypeProvider(), valueProvider, result).orElse(null);
312333
if (adaptedResult == null) {
313334
return Optional.empty();
314335
}
315336

316337
return Optional.of(astMutator.replaceSubtree(mutableAst, adaptedResult, node.id()));
317338
}
318339

319-
private Optional<CelMutableExpr> maybeAdaptEvaluatedResult(Object result) {
340+
private Optional<CelMutableExpr> maybeAdaptEvaluatedResult(
341+
CelTypeProvider typeProvider, @Nullable CelValueProvider valueProvider, Object result) {
342+
if (valueProvider != null && !(result instanceof CelValue)) {
343+
result = valueProvider.celValueConverter().toRuntimeValue(result);
344+
}
345+
320346
if (CelConstant.isConstantValue(result)) {
321347
return Optional.of(CelMutableExpr.ofConstant(CelConstant.ofObjectValue(result)));
322348
} else if (result instanceof Collection<?>) {
323349
Collection<?> collection = (Collection<?>) result;
324350
List<CelMutableExpr> listElements = new ArrayList<>();
325351
for (Object evaluatedElement : collection) {
326-
CelMutableExpr adaptedExpr = maybeAdaptEvaluatedResult(evaluatedElement).orElse(null);
352+
CelMutableExpr adaptedExpr =
353+
maybeAdaptEvaluatedResult(typeProvider, valueProvider, evaluatedElement).orElse(null);
327354
if (adaptedExpr == null) {
328355
return Optional.empty();
329356
}
@@ -335,11 +362,13 @@ private Optional<CelMutableExpr> maybeAdaptEvaluatedResult(Object result) {
335362
Map<?, ?> map = (Map<?, ?>) result;
336363
List<CelMutableMap.Entry> mapEntries = new ArrayList<>();
337364
for (Map.Entry<?, ?> entry : map.entrySet()) {
338-
CelMutableExpr adaptedKey = maybeAdaptEvaluatedResult(entry.getKey()).orElse(null);
365+
CelMutableExpr adaptedKey =
366+
maybeAdaptEvaluatedResult(typeProvider, valueProvider, entry.getKey()).orElse(null);
339367
if (adaptedKey == null) {
340368
return Optional.empty();
341369
}
342-
CelMutableExpr adaptedValue = maybeAdaptEvaluatedResult(entry.getValue()).orElse(null);
370+
CelMutableExpr adaptedValue =
371+
maybeAdaptEvaluatedResult(typeProvider, valueProvider, entry.getValue()).orElse(null);
343372
if (adaptedValue == null) {
344373
return Optional.empty();
345374
}
@@ -364,14 +393,43 @@ private Optional<CelMutableExpr> maybeAdaptEvaluatedResult(Object result) {
364393
CelMutableExpr.ofConstant(CelConstant.ofValue(timestampStrArg)));
365394

366395
return Optional.of(CelMutableExpr.ofCall(timestampCall));
396+
} else if (result instanceof StructValue) {
397+
@SuppressWarnings("unchecked") // Unchecked: StructValue only supports String keys.
398+
StructValue<String, ?> structValue = (StructValue<String, ?>) result;
399+
List<CelMutableStruct.Entry> structEntries = new ArrayList<>();
400+
401+
String typeName = structValue.celType().name();
402+
CelType optType = typeProvider.findType(typeName).orElse(null);
403+
if (!(optType instanceof StructType)) {
404+
return Optional.empty();
405+
}
406+
StructType structType = (StructType) optType;
407+
for (String fieldName : structType.fieldNames()) {
408+
Optional<?> fieldOpt = structValue.find(fieldName);
409+
if (!fieldOpt.isPresent()) {
410+
continue;
411+
}
412+
CelMutableExpr adaptedFieldExpr =
413+
maybeAdaptEvaluatedResult(typeProvider, valueProvider, fieldOpt.get()).orElse(null);
414+
if (adaptedFieldExpr == null) {
415+
return Optional.empty();
416+
}
417+
structEntries.add(CelMutableStruct.Entry.create(0, fieldName, adaptedFieldExpr));
418+
}
419+
return Optional.of(
420+
CelMutableExpr.ofStruct(CelMutableStruct.create(structType.name(), structEntries)));
367421
}
368422

369423
// Evaluated result cannot be folded (e.g: unknowns)
370424
return Optional.empty();
371425
}
372426

373427
private Optional<CelMutableAst> maybeRewriteOptional(
374-
Optional<?> optResult, CelMutableAst mutableAst, CelMutableExpr expr) {
428+
CelTypeProvider typeProvider,
429+
CelValueProvider valueProvider,
430+
Optional<?> optResult,
431+
CelMutableAst mutableAst,
432+
CelMutableExpr expr) {
375433
Object unwrappedResult = optResult.orElse(null);
376434
if (unwrappedResult == null) {
377435
if (isCallToFunction(expr, Function.OPTIONAL_NONE.getFunction())) {
@@ -387,7 +445,8 @@ private Optional<CelMutableAst> maybeRewriteOptional(
387445
return Optional.empty();
388446
}
389447

390-
CelMutableExpr adaptedResult = maybeAdaptEvaluatedResult(unwrappedResult).orElse(null);
448+
CelMutableExpr adaptedResult =
449+
maybeAdaptEvaluatedResult(typeProvider, valueProvider, unwrappedResult).orElse(null);
391450
if (adaptedResult == null) {
392451
// Evaluated result is not an adaptable constant. Leave the optional as is.
393452
return Optional.empty();

optimizer/src/test/java/dev/cel/optimizer/optimizers/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,10 @@ java_library(
4242
"@maven//:junit_junit",
4343
"@maven//:com_google_testparameterinjector_test_parameter_injector",
4444
"//:java_truth",
45+
"@cel_spec//proto/cel/expr/conformance/proto2:test_all_types_java_proto",
4546
"@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto",
4647
"@maven//:com_google_guava_guava",
48+
"@maven//:com_google_protobuf_protobuf_java",
4749
],
4850
)
4951

0 commit comments

Comments
 (0)