2424import com .google .common .collect .ImmutableSet ;
2525import com .google .errorprone .annotations .CanIgnoreReturnValue ;
2626import dev .cel .bundle .Cel ;
27+ import dev .cel .bundle .CelBuilder ;
2728import dev .cel .common .CelAbstractSyntaxTree ;
2829import dev .cel .common .CelMutableAst ;
2930import dev .cel .common .CelSource ;
4243import dev .cel .common .navigation .CelNavigableMutableAst ;
4344import dev .cel .common .navigation .CelNavigableMutableExpr ;
4445import dev .cel .common .navigation .TraversalOrder ;
46+ import dev .cel .common .types .CelType ;
47+ import dev .cel .common .types .CelTypeProvider ;
4548import 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 ;
4653import dev .cel .extensions .CelOptionalLibrary .Function ;
4754import dev .cel .optimizer .AstMutator ;
4855import 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 ();
0 commit comments