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
49 changes: 32 additions & 17 deletions extensions/src/main/java/dev/cel/extensions/CelOptionalLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,26 @@
public final class CelOptionalLibrary implements CelCompilerLibrary, CelRuntimeLibrary {
public static final CelOptionalLibrary INSTANCE = new CelOptionalLibrary();

private static final String VALUE_FUNCTION = "value";
private static final String HAS_VALUE_FUNCTION = "hasValue";
private static final String OPTIONAL_NONE_FUNCTION = "optional.none";
private static final String OPTIONAL_OF_FUNCTION = "optional.of";
private static final String OPTIONAL_OF_NON_ZERO_VALUE_FUNCTION = "optional.ofNonZeroValue";
/** Enumerations of function names used for supporting optionals. */
public enum Function {
VALUE("value"),
HAS_VALUE("hasValue"),
OPTIONAL_NONE("optional.none"),
OPTIONAL_OF("optional.of"),
OPTIONAL_OF_NON_ZERO_VALUE("optional.ofNonZeroValue"),
OR("or"),
OR_VALUE("orValue");
private final String functionName;

public String getFunction() {
return functionName;
}

Function(String functionName) {
this.functionName = functionName;
}
}

private static final String UNUSED_ITER_VAR = "#unused";

@Override
Expand All @@ -80,20 +95,20 @@ public void setCheckerOptions(CelCheckerBuilder checkerBuilder) {
MapType mapTypeKv = MapType.create(paramTypeK, paramTypeV);
checkerBuilder.addFunctionDeclarations(
CelFunctionDecl.newFunctionDeclaration(
OPTIONAL_OF_FUNCTION,
Function.OPTIONAL_OF.getFunction(),
CelOverloadDecl.newGlobalOverload("optional_of", optionalTypeV, paramTypeV)),
CelFunctionDecl.newFunctionDeclaration(
OPTIONAL_OF_NON_ZERO_VALUE_FUNCTION,
Function.OPTIONAL_OF_NON_ZERO_VALUE.getFunction(),
CelOverloadDecl.newGlobalOverload(
"optional_ofNonZeroValue", optionalTypeV, paramTypeV)),
CelFunctionDecl.newFunctionDeclaration(
OPTIONAL_NONE_FUNCTION,
Function.OPTIONAL_NONE.getFunction(),
CelOverloadDecl.newGlobalOverload("optional_none", optionalTypeV)),
CelFunctionDecl.newFunctionDeclaration(
VALUE_FUNCTION,
Function.VALUE.getFunction(),
CelOverloadDecl.newMemberOverload("optional_value", paramTypeV, optionalTypeV)),
CelFunctionDecl.newFunctionDeclaration(
HAS_VALUE_FUNCTION,
Function.HAS_VALUE.getFunction(),
CelOverloadDecl.newMemberOverload("optional_hasValue", SimpleType.BOOL, optionalTypeV)),
// Note: Implementation of "or" and "orValue" are special-cased inside the interpreter.
// Hence, their bindings are not provided here.
Expand Down Expand Up @@ -218,18 +233,18 @@ private static Optional<CelExpr> expandOptMap(
return Optional.of(
exprFactory.newGlobalCall(
Operator.CONDITIONAL.getFunction(),
exprFactory.newReceiverCall(HAS_VALUE_FUNCTION, target),
exprFactory.newReceiverCall(Function.HAS_VALUE.getFunction(), target),
exprFactory.newGlobalCall(
OPTIONAL_OF_FUNCTION,
Function.OPTIONAL_OF.getFunction(),
exprFactory.fold(
UNUSED_ITER_VAR,
exprFactory.newList(),
varName,
exprFactory.newReceiverCall(VALUE_FUNCTION, target),
exprFactory.newReceiverCall(Function.VALUE.getFunction(), target),
exprFactory.newBoolLiteral(true),
exprFactory.newIdentifier(varName),
mapExpr)),
exprFactory.newGlobalCall(OPTIONAL_NONE_FUNCTION)));
exprFactory.newGlobalCall(Function.OPTIONAL_NONE.getFunction())));
}

private static Optional<CelExpr> expandOptFlatMap(
Expand All @@ -252,16 +267,16 @@ private static Optional<CelExpr> expandOptFlatMap(
return Optional.of(
exprFactory.newGlobalCall(
Operator.CONDITIONAL.getFunction(),
exprFactory.newReceiverCall(HAS_VALUE_FUNCTION, target),
exprFactory.newReceiverCall(Function.HAS_VALUE.getFunction(), target),
exprFactory.fold(
UNUSED_ITER_VAR,
exprFactory.newList(),
varName,
exprFactory.newReceiverCall(VALUE_FUNCTION, target),
exprFactory.newReceiverCall(Function.VALUE.getFunction(), target),
exprFactory.newBoolLiteral(true),
exprFactory.newIdentifier(varName),
mapExpr),
exprFactory.newGlobalCall(OPTIONAL_NONE_FUNCTION)));
exprFactory.newGlobalCall(Function.OPTIONAL_NONE.getFunction())));
}

private CelOptionalLibrary() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ java_library(
"//common/ast",
"//common/ast:expr_util",
"//common/navigation",
"//extensions:optional_library",
"//optimizer:ast_optimizer",
"//optimizer:mutable_ast",
"//optimizer:optimization_exception",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import dev.cel.common.ast.CelExprUtil;
import dev.cel.common.navigation.CelNavigableAst;
import dev.cel.common.navigation.CelNavigableExpr;
import dev.cel.extensions.CelOptionalLibrary.Function;
import dev.cel.optimizer.CelAstOptimizer;
import dev.cel.optimizer.CelOptimizationException;
import dev.cel.optimizer.MutableAst;
Expand Down Expand Up @@ -68,10 +69,9 @@ public static ConstantFoldingOptimizer newInstance(

// Use optional.of and optional.none as sentinel function names for folding optional calls.
// TODO: Leverage CelValue representation of Optionals instead when available.
private static final String OPTIONAL_OF_FUNCTION = "optional.of";
private static final String OPTIONAL_NONE_FUNCTION = "optional.none";
private static final CelExpr OPTIONAL_NONE_EXPR =
CelExpr.ofCallExpr(0, Optional.empty(), OPTIONAL_NONE_FUNCTION, ImmutableList.of());
CelExpr.ofCallExpr(
0, Optional.empty(), Function.OPTIONAL_NONE.getFunction(), ImmutableList.of());

private final ConstantFoldingOptions constantFoldingOptions;
private final MutableAst mutableAst;
Expand Down Expand Up @@ -130,8 +130,8 @@ private static boolean canFold(CelNavigableExpr navigableExpr) {
String functionName = celCall.function();

// These are already folded or do not need to be folded.
if (functionName.equals(OPTIONAL_OF_FUNCTION)
|| functionName.equals(OPTIONAL_NONE_FUNCTION)) {
if (functionName.equals(Function.OPTIONAL_OF.getFunction())
|| functionName.equals(Function.OPTIONAL_NONE.getFunction())) {
return false;
}

Expand Down Expand Up @@ -264,13 +264,13 @@ private Optional<CelExpr> maybeAdaptEvaluatedResult(Object result) {
private Optional<CelAbstractSyntaxTree> maybeRewriteOptional(
Optional<?> optResult, CelAbstractSyntaxTree ast, CelExpr expr) {
if (!optResult.isPresent()) {
if (!expr.callOrDefault().function().equals(OPTIONAL_NONE_FUNCTION)) {
if (!expr.callOrDefault().function().equals(Function.OPTIONAL_NONE.getFunction())) {
// An empty optional value was encountered. Rewrite the tree with optional.none call.
// This is to account for other optional functions returning an empty optional value
// e.g: optional.ofNonZeroValue(0)
return Optional.of(mutableAst.replaceSubtree(ast, OPTIONAL_NONE_EXPR, expr.id()));
}
} else if (!expr.callOrDefault().function().equals(OPTIONAL_OF_FUNCTION)) {
} else if (!expr.callOrDefault().function().equals(Function.OPTIONAL_OF.getFunction())) {
Object unwrappedResult = optResult.get();
if (!CelConstant.isConstantValue(unwrappedResult)) {
// Evaluated result is not a constant. Leave the optional as is.
Expand All @@ -281,7 +281,7 @@ private Optional<CelAbstractSyntaxTree> maybeRewriteOptional(
CelExpr.newBuilder()
.setCall(
CelCall.newBuilder()
.setFunction(OPTIONAL_OF_FUNCTION)
.setFunction(Function.OPTIONAL_OF.getFunction())
.addArgs(
CelExpr.newBuilder()
.setConstant(CelConstant.ofObjectValue(unwrappedResult))
Expand Down Expand Up @@ -444,12 +444,12 @@ private CelAbstractSyntaxTree pruneOptionalListElements(CelAbstractSyntaxTree as

if (element.exprKind().getKind().equals(Kind.CALL)) {
CelCall call = element.call();
if (call.function().equals(OPTIONAL_NONE_FUNCTION)) {
if (call.function().equals(Function.OPTIONAL_NONE.getFunction())) {
// Skip optional.none.
// Skipping causes the list to get smaller.
newOptIndex--;
continue;
} else if (call.function().equals(OPTIONAL_OF_FUNCTION)) {
} else if (call.function().equals(Function.OPTIONAL_OF.getFunction())) {
CelExpr arg = call.args().get(0);
if (arg.exprKind().getKind().equals(Kind.CONSTANT)) {
updatedElemBuilder.add(call.args().get(0));
Expand Down Expand Up @@ -491,11 +491,11 @@ private CelAbstractSyntaxTree pruneOptionalMapElements(CelAbstractSyntaxTree ast
}

CelCall call = value.call();
if (call.function().equals(OPTIONAL_NONE_FUNCTION)) {
if (call.function().equals(Function.OPTIONAL_NONE.getFunction())) {
// Skip the element. This is resolving an optional.none: ex {?1: optional.none()}.
modified = true;
continue;
} else if (call.function().equals(OPTIONAL_OF_FUNCTION)) {
} else if (call.function().equals(Function.OPTIONAL_OF.getFunction())) {
CelExpr arg = call.args().get(0);
if (arg.exprKind().getKind().equals(Kind.CONSTANT)) {
modified = true;
Expand Down Expand Up @@ -537,11 +537,11 @@ private CelAbstractSyntaxTree pruneOptionalStructElements(
}

CelCall call = value.call();
if (call.function().equals(OPTIONAL_NONE_FUNCTION)) {
if (call.function().equals(Function.OPTIONAL_NONE.getFunction())) {
// Skip the element. This is resolving an optional.none: ex msg{?field: optional.none()}.
modified = true;
continue;
} else if (call.function().equals(OPTIONAL_OF_FUNCTION)) {
} else if (call.function().equals(Function.OPTIONAL_OF.getFunction())) {
CelExpr arg = call.args().get(0);
if (arg.exprKind().getKind().equals(Kind.CONSTANT)) {
modified = true;
Expand Down