Skip to content

Commit f10f91c

Browse files
l46kokcopybara-github
authored andcommitted
Inline presence tests for literals with zero value tests
PiperOrigin-RevId: 869463359
1 parent 21fbb54 commit f10f91c

File tree

2 files changed

+105
-15
lines changed

2 files changed

+105
-15
lines changed

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

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.google.auto.value.AutoValue;
2020
import com.google.common.collect.ImmutableList;
21+
import com.google.common.primitives.UnsignedLong;
2122
import dev.cel.bundle.Cel;
2223
import dev.cel.common.CelAbstractSyntaxTree;
2324
import dev.cel.common.CelMutableAst;
@@ -27,6 +28,7 @@
2728
import dev.cel.common.ast.CelMutableExpr;
2829
import dev.cel.common.ast.CelMutableExpr.CelMutableCall;
2930
import dev.cel.common.ast.CelMutableExpr.CelMutableComprehension;
31+
import dev.cel.common.ast.CelMutableExpr.CelMutableStruct;
3032
import dev.cel.common.navigation.CelNavigableMutableAst;
3133
import dev.cel.common.navigation.CelNavigableMutableExpr;
3234
import dev.cel.common.types.CelKind;
@@ -35,6 +37,7 @@
3537
import dev.cel.common.values.NullValue;
3638
import dev.cel.optimizer.AstMutator;
3739
import dev.cel.optimizer.CelAstOptimizer;
40+
import java.util.ArrayList;
3841
import java.util.NoSuchElementException;
3942
import java.util.Optional;
4043
import java.util.stream.Stream;
@@ -106,26 +109,66 @@ private static CelMutableExpr rewritePresenceExpr(
106109
.ast()
107110
.getType(replacementExpr.id())
108111
.orElseThrow(() -> new NoSuchElementException("Type is not present."));
112+
109113
if (isSizerType(replacementType)) {
110114
// has(X) -> X.size() != 0
111-
return CelMutableExpr.ofCall(
112-
CelMutableCall.create(
113-
Operator.NOT_EQUALS.getFunction(),
114-
CelMutableExpr.ofCall(CelMutableCall.create(replacementExpr, "size")),
115-
CelMutableExpr.ofConstant(CelConstant.ofValue(0))));
116-
} else if (replacementType.isAssignableFrom(SimpleType.NULL_TYPE)) {
115+
return createNotEquals(
116+
CelMutableExpr.ofCall(CelMutableCall.create(replacementExpr, "size")),
117+
CelMutableExpr.ofConstant(CelConstant.ofValue(0)));
118+
}
119+
120+
if (replacementType.isAssignableFrom(SimpleType.NULL_TYPE)) {
117121
// has(X) -> X != null
118122
// This covers well-known wrapper types
119-
return CelMutableExpr.ofCall(
120-
CelMutableCall.create(
121-
Operator.NOT_EQUALS.getFunction(),
122-
replacementExpr,
123-
CelMutableExpr.ofConstant(CelConstant.ofValue(NullValue.NULL_VALUE))));
123+
124+
return createNotEquals(
125+
replacementExpr, CelMutableExpr.ofConstant(CelConstant.ofValue(NullValue.NULL_VALUE)));
124126
}
125127

126-
throw new IllegalArgumentException(
127-
String.format(
128-
"Unable to inline expression type %s into presence test", replacementType.name()));
128+
return getZeroValueExpr(replacementType, replacementExpr)
129+
.map(zeroValue -> createNotEquals(replacementExpr, zeroValue))
130+
.orElseThrow(
131+
() ->
132+
new IllegalArgumentException(
133+
String.format(
134+
"Unable to inline expression type %s into presence test",
135+
replacementType.name())));
136+
}
137+
138+
private static Optional<CelMutableExpr> getZeroValueExpr(
139+
CelType type, CelMutableExpr replacementExpr) {
140+
switch (type.kind()) {
141+
case BOOL:
142+
return Optional.of(CelMutableExpr.ofConstant(CelConstant.ofValue(false)));
143+
case DOUBLE:
144+
return Optional.of(CelMutableExpr.ofConstant(CelConstant.ofValue(0.0d)));
145+
case INT:
146+
return Optional.of(CelMutableExpr.ofConstant(CelConstant.ofValue(0)));
147+
case UINT:
148+
return Optional.of(CelMutableExpr.ofConstant(CelConstant.ofValue(UnsignedLong.ZERO)));
149+
case TIMESTAMP:
150+
return Optional.of(
151+
CelMutableExpr.ofCall(
152+
CelMutableCall.create(
153+
"timestamp", CelMutableExpr.ofConstant(CelConstant.ofValue(0)))));
154+
case DURATION:
155+
return Optional.of(
156+
CelMutableExpr.ofCall(
157+
CelMutableCall.create(
158+
"duration", CelMutableExpr.ofConstant(CelConstant.ofValue("0")))));
159+
case STRUCT:
160+
return Optional.of(
161+
CelMutableExpr.ofStruct(
162+
CelMutableStruct.create(
163+
replacementExpr.struct().messageName(), new ArrayList<>())));
164+
default:
165+
return Optional.empty();
166+
}
167+
}
168+
169+
private static CelMutableExpr createNotEquals(CelMutableExpr left, CelMutableExpr right) {
170+
return CelMutableExpr.ofCall(
171+
CelMutableCall.create(Operator.NOT_EQUALS.getFunction(), left, right));
129172
}
130173

131174
private static boolean isSizerType(CelType type) {

optimizer/src/test/java/dev/cel/optimizer/optimizers/InliningOptimizerTest.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ public class InliningOptimizerTest {
5858
"child",
5959
StructTypeReference.create(TestAllTypes.NestedMessage.getDescriptor().getFullName()))
6060
.addVar("shadowed_ident", SimpleType.INT)
61-
.setOptions(CelOptions.current().populateMacroCalls(true).build())
61+
.setOptions(
62+
CelOptions.current().populateMacroCalls(true).enableTimestampEpoch(true).build())
6263
.build();
6364

6465
@Test
@@ -129,6 +130,52 @@ private enum SuccessTestCase {
129130
/* inlineVarName= */ "msg.single_any.processing_purpose",
130131
/* replacementExpr= */ "[1, 2, 3]",
131132
/* expected= */ "[1, 2, 3].size() != 0"),
133+
PRESENCE_WITH_INT_LITERAL_REWRITE(
134+
/* source= */ "has(msg.single_any.processing_purpose)",
135+
/* inlineVarName= */ "msg.single_any.processing_purpose",
136+
/* replacementExpr= */ "1",
137+
/* expected= */ "1 != 0"),
138+
PRESENCE_WITH_UINT_LITERAL_REWRITE(
139+
/* source= */ "has(msg.single_any.processing_purpose)",
140+
/* inlineVarName= */ "msg.single_any.processing_purpose",
141+
/* replacementExpr= */ "1u",
142+
/* expected= */ "1u != 0u"),
143+
PRESENCE_WITH_DOUBLE_LITERAL_REWRITE(
144+
/* source= */ "has(msg.single_any.processing_purpose)",
145+
/* inlineVarName= */ "msg.single_any.processing_purpose",
146+
/* replacementExpr= */ "1.5",
147+
/* expected= */ "1.5 != 0.0"),
148+
PRESENCE_WITH_BOOL_LITERAL_REWRITE(
149+
/* source= */ "has(msg.single_any.processing_purpose)",
150+
/* inlineVarName= */ "msg.single_any.processing_purpose",
151+
/* replacementExpr= */ "true",
152+
/* expected= */ "true != false"),
153+
PRESENCE_WITH_STRING_LITERAL_REWRITE(
154+
/* source= */ "has(msg.single_any.processing_purpose)",
155+
/* inlineVarName= */ "msg.single_any.processing_purpose",
156+
/* replacementExpr= */ "'foo'",
157+
/* expected= */ "\"foo\".size() != 0"),
158+
PRESENCE_WITH_BYTES_LITERAL_REWRITE(
159+
/* source= */ "has(msg.single_any.processing_purpose)",
160+
/* inlineVarName= */ "msg.single_any.processing_purpose",
161+
/* replacementExpr= */ "b'abc'",
162+
/* expected= */ "b\"\\141\\142\\143\".size() != 0"),
163+
PRESENCE_WITH_TIMESTAMP_LITERAL_REWRITE(
164+
/* source= */ "has(msg.single_any.processing_purpose)",
165+
/* inlineVarName= */ "msg.single_any.processing_purpose",
166+
/* replacementExpr= */ "timestamp(1)",
167+
/* expected= */ "timestamp(1) != timestamp(0)"),
168+
PRESENCE_WITH_DURATION_LITERAL_REWRITE(
169+
/* source= */ "has(msg.single_any.processing_purpose)",
170+
/* inlineVarName= */ "msg.single_any.processing_purpose",
171+
/* replacementExpr= */ "duration('1h')",
172+
/* expected= */ "duration(\"1h\") != duration(\"0\")"),
173+
PRESENCE_WITH_PROTOBUF_MESSAGE_REWRITE(
174+
/* source= */ "has(msg.single_any.processing_purpose)",
175+
/* inlineVarName= */ "msg.single_any.processing_purpose",
176+
/* replacementExpr= */ "cel.expr.conformance.proto3.TestAllTypes{single_int64: 1}",
177+
/* expected= */ "cel.expr.conformance.proto3.TestAllTypes{single_int64: 1} !="
178+
+ " cel.expr.conformance.proto3.TestAllTypes{}"),
132179
NESTED_SELECT(
133180
/* source= */ "msg.standalone_message.bb",
134181
/* inlineVarName= */ "msg.standalone_message",

0 commit comments

Comments
 (0)