Skip to content

Commit fea0f28

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

9 files changed

Lines changed: 252 additions & 17 deletions

File tree

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import dev.cel.runtime.CelRuntimeLibrary;
4040
import dev.cel.runtime.CelStandardFunctions;
4141
import java.util.function.Function;
42+
import org.jspecify.annotations.Nullable;
4243

4344
/** Interface for building an instance of Cel. */
4445
public interface CelBuilder {
@@ -211,6 +212,9 @@ public interface CelBuilder {
211212
@CanIgnoreReturnValue
212213
CelBuilder setValueProvider(CelValueProvider celValueProvider);
213214

215+
/** Returns the configured {@link CelValueProvider}, or null if not set. */
216+
@Nullable CelValueProvider getValueProvider();
217+
214218
/**
215219
* Set the {@code typeProvider} for use with type-checking expressions.
216220
*

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import dev.cel.runtime.CelStandardFunctions;
6060
import java.util.Arrays;
6161
import java.util.function.Function;
62+
import org.jspecify.annotations.Nullable;
6263

6364
/**
6465
* Implementation of the synchronous CEL stack.
@@ -317,6 +318,11 @@ public CelBuilder setValueProvider(CelValueProvider celValueProvider) {
317318
return this;
318319
}
319320

321+
@Override
322+
public @Nullable CelValueProvider getValueProvider() {
323+
return runtimeBuilder.getValueProvider();
324+
}
325+
320326
@Override
321327
@Deprecated
322328
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:cel_value",
36+
"//common/values:cel_value_provider",
37+
"//common/values:proto_message_value",
3438
"//extensions:optional_library",
3539
"//optimizer:ast_optimizer",
3640
"//optimizer:mutable_ast",

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

Lines changed: 68 additions & 12 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.ProtoMessageValue;
4653
import dev.cel.extensions.CelOptionalLibrary.Function;
4754
import dev.cel.optimizer.AstMutator;
4855
import dev.cel.optimizer.CelAstOptimizer;
@@ -95,8 +102,15 @@ private static CelMutableExpr newOptionalNoneExpr() {
95102
@Override
96103
public OptimizationResult optimize(CelAbstractSyntaxTree ast, Cel cel)
97104
throws CelOptimizationException {
98-
// Override the environment's expected type to generally allow all subtrees to be folded.
99-
Cel optimizerEnv = cel.toCelBuilder().setResultType(SimpleType.DYN).build();
105+
CelBuilder builder = cel.toCelBuilder();
106+
CelValueProvider valueProvider;
107+
try {
108+
valueProvider = builder.getValueProvider();
109+
} catch (UnsupportedOperationException e) {
110+
// Legacy runtime does not support getValueProvider and may throw.
111+
valueProvider = null;
112+
}
113+
Cel optimizerEnv = builder.setResultType(SimpleType.DYN).build();
100114

101115
CelMutableAst mutableAst = CelMutableAst.fromCelAst(ast);
102116
int iterCount = 0;
@@ -123,7 +137,7 @@ public OptimizationResult optimize(CelAbstractSyntaxTree ast, Cel cel)
123137
if (!mutatedResult.isPresent()) {
124138
// Evaluate the call then fold
125139
try {
126-
mutatedResult = maybeFold(optimizerEnv, mutableAst, foldableExpr);
140+
mutatedResult = maybeFold(optimizerEnv, valueProvider, mutableAst, foldableExpr);
127141
} catch (CelEvaluationException e) {
128142
throw new CelOptimizationException(
129143
"Constant folding failure. Failed to evaluate subtree due to: " + e.getMessage(),
@@ -290,7 +304,10 @@ private static boolean isNestedComprehension(CelNavigableMutableExpr expr) {
290304
}
291305

292306
private Optional<CelMutableAst> maybeFold(
293-
Cel cel, CelMutableAst mutableAst, CelNavigableMutableExpr node)
307+
Cel cel,
308+
CelValueProvider valueProvider,
309+
CelMutableAst mutableAst,
310+
CelNavigableMutableExpr node)
294311
throws CelOptimizationException, CelEvaluationException {
295312
Object result;
296313
try {
@@ -305,25 +322,33 @@ private Optional<CelMutableAst> maybeFold(
305322
// ex2: optional.ofNonZeroValue(5) -> optional.of(5)
306323
if (result instanceof Optional<?>) {
307324
Optional<?> optResult = ((Optional<?>) result);
308-
return maybeRewriteOptional(optResult, mutableAst, node.expr());
325+
return maybeRewriteOptional(
326+
cel.getTypeProvider(), valueProvider, optResult, mutableAst, node.expr());
309327
}
310328

311-
CelMutableExpr adaptedResult = maybeAdaptEvaluatedResult(result).orElse(null);
329+
CelMutableExpr adaptedResult =
330+
maybeAdaptEvaluatedResult(cel.getTypeProvider(), valueProvider, result).orElse(null);
312331
if (adaptedResult == null) {
313332
return Optional.empty();
314333
}
315334

316335
return Optional.of(astMutator.replaceSubtree(mutableAst, adaptedResult, node.id()));
317336
}
318337

319-
private Optional<CelMutableExpr> maybeAdaptEvaluatedResult(Object result) {
338+
private Optional<CelMutableExpr> maybeAdaptEvaluatedResult(
339+
CelTypeProvider typeProvider, CelValueProvider valueProvider, Object result) {
340+
if (valueProvider != null && !(result instanceof CelValue)) {
341+
result = valueProvider.celValueConverter().toRuntimeValue(result);
342+
}
343+
320344
if (CelConstant.isConstantValue(result)) {
321345
return Optional.of(CelMutableExpr.ofConstant(CelConstant.ofObjectValue(result)));
322346
} else if (result instanceof Collection<?>) {
323347
Collection<?> collection = (Collection<?>) result;
324348
List<CelMutableExpr> listElements = new ArrayList<>();
325349
for (Object evaluatedElement : collection) {
326-
CelMutableExpr adaptedExpr = maybeAdaptEvaluatedResult(evaluatedElement).orElse(null);
350+
CelMutableExpr adaptedExpr =
351+
maybeAdaptEvaluatedResult(typeProvider, valueProvider, evaluatedElement).orElse(null);
327352
if (adaptedExpr == null) {
328353
return Optional.empty();
329354
}
@@ -335,11 +360,13 @@ private Optional<CelMutableExpr> maybeAdaptEvaluatedResult(Object result) {
335360
Map<?, ?> map = (Map<?, ?>) result;
336361
List<CelMutableMap.Entry> mapEntries = new ArrayList<>();
337362
for (Map.Entry<?, ?> entry : map.entrySet()) {
338-
CelMutableExpr adaptedKey = maybeAdaptEvaluatedResult(entry.getKey()).orElse(null);
363+
CelMutableExpr adaptedKey =
364+
maybeAdaptEvaluatedResult(typeProvider, valueProvider, entry.getKey()).orElse(null);
339365
if (adaptedKey == null) {
340366
return Optional.empty();
341367
}
342-
CelMutableExpr adaptedValue = maybeAdaptEvaluatedResult(entry.getValue()).orElse(null);
368+
CelMutableExpr adaptedValue =
369+
maybeAdaptEvaluatedResult(typeProvider, valueProvider, entry.getValue()).orElse(null);
343370
if (adaptedValue == null) {
344371
return Optional.empty();
345372
}
@@ -364,14 +391,42 @@ private Optional<CelMutableExpr> maybeAdaptEvaluatedResult(Object result) {
364391
CelMutableExpr.ofConstant(CelConstant.ofValue(timestampStrArg)));
365392

366393
return Optional.of(CelMutableExpr.ofCall(timestampCall));
394+
} else if (result instanceof ProtoMessageValue) {
395+
ProtoMessageValue structValue = (ProtoMessageValue) result;
396+
List<CelMutableStruct.Entry> structEntries = new ArrayList<>();
397+
398+
String typeName = structValue.celType().name();
399+
CelType optType = typeProvider.findType(typeName).orElse(null);
400+
if (!(optType instanceof StructType)) {
401+
return Optional.empty();
402+
}
403+
StructType structType = (StructType) optType;
404+
for (String fieldName : structType.fieldNames()) {
405+
Optional<?> fieldOpt = structValue.find(fieldName);
406+
if (!fieldOpt.isPresent()) {
407+
continue;
408+
}
409+
CelMutableExpr adaptedFieldExpr =
410+
maybeAdaptEvaluatedResult(typeProvider, valueProvider, fieldOpt.get()).orElse(null);
411+
if (adaptedFieldExpr == null) {
412+
return Optional.empty();
413+
}
414+
structEntries.add(CelMutableStruct.Entry.create(0, fieldName, adaptedFieldExpr));
415+
}
416+
return Optional.of(
417+
CelMutableExpr.ofStruct(CelMutableStruct.create(structType.name(), structEntries)));
367418
}
368419

369420
// Evaluated result cannot be folded (e.g: unknowns)
370421
return Optional.empty();
371422
}
372423

373424
private Optional<CelMutableAst> maybeRewriteOptional(
374-
Optional<?> optResult, CelMutableAst mutableAst, CelMutableExpr expr) {
425+
CelTypeProvider typeProvider,
426+
CelValueProvider valueProvider,
427+
Optional<?> optResult,
428+
CelMutableAst mutableAst,
429+
CelMutableExpr expr) {
375430
Object unwrappedResult = optResult.orElse(null);
376431
if (unwrappedResult == null) {
377432
if (isCallToFunction(expr, Function.OPTIONAL_NONE.getFunction())) {
@@ -387,7 +442,8 @@ private Optional<CelMutableAst> maybeRewriteOptional(
387442
return Optional.empty();
388443
}
389444

390-
CelMutableExpr adaptedResult = maybeAdaptEvaluatedResult(unwrappedResult).orElse(null);
445+
CelMutableExpr adaptedResult =
446+
maybeAdaptEvaluatedResult(typeProvider, valueProvider, unwrappedResult).orElse(null);
391447
if (adaptedResult == null) {
392448
// Evaluated result is not an adaptable constant. Leave the optional as is.
393449
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)