|
14 | 14 |
|
15 | 15 | package dev.cel.runtime.planner; |
16 | 16 |
|
| 17 | +import static com.google.common.base.Preconditions.checkArgument; |
17 | 18 | import static com.google.common.base.Preconditions.checkNotNull; |
18 | 19 |
|
19 | 20 | import com.google.auto.value.AutoValue; |
|
28 | 29 | import dev.cel.common.CelAbstractSyntaxTree; |
29 | 30 | import dev.cel.common.CelContainer; |
30 | 31 | import dev.cel.common.CelOptions; |
| 32 | +import dev.cel.common.CelSource.Extension; |
| 33 | +import dev.cel.common.CelSource.Extension.Component; |
| 34 | +import dev.cel.common.CelSource.Extension.Version; |
31 | 35 | import dev.cel.common.Operator; |
32 | 36 | import dev.cel.common.annotations.Internal; |
33 | 37 | import dev.cel.common.ast.CelBlock; |
|
60 | 64 | import dev.cel.runtime.RuntimeEquality; |
61 | 65 | import java.util.Arrays; |
62 | 66 | import java.util.HashMap; |
| 67 | +import java.util.Map; |
63 | 68 | import java.util.NoSuchElementException; |
64 | 69 | import java.util.Optional; |
65 | 70 | import org.jspecify.annotations.Nullable; |
|
72 | 77 | @Internal |
73 | 78 | public final class ProgramPlanner { |
74 | 79 | private static final String MAP_INSERT_FUNCTION = "cel.@mapInsert"; |
| 80 | + private static final String CEL_BLOCK_EXTENSION = "cel_block"; |
| 81 | + private static final String JSON_NAME_EXTENSION = "json_name"; |
| 82 | + private static final String SELECT_OPTIMIZATION_EXTENSION = "select_optimization"; |
| 83 | + |
| 84 | + private static final ImmutableMap<String, Version> SUPPORTED_RUNTIME_EXTENSIONS = |
| 85 | + ImmutableMap.of( |
| 86 | + CEL_BLOCK_EXTENSION, Version.of(1, 1), |
| 87 | + JSON_NAME_EXTENSION, Version.of(1, 1), |
| 88 | + SELECT_OPTIMIZATION_EXTENSION, Version.of(1, 0)); |
75 | 89 |
|
76 | 90 | private final CelTypeProvider typeProvider; |
77 | 91 | private final CelValueProvider valueProvider; |
@@ -102,7 +116,7 @@ public PlannedProgram plan(CelAbstractSyntaxTree ast) throws CelEvaluationExcept |
102 | 116 | ErrorMetadata errorMetadata = |
103 | 117 | ErrorMetadata.create(ast.getSource().getPositionsMap(), ast.getSource().getDescription()); |
104 | 118 | try { |
105 | | - PlannerContext ctx = PlannerContext.create(ast); |
| 119 | + PlannerContext ctx = PlannerContext.create(ast, options); |
106 | 120 | plannedInterpretable = |
107 | 121 | CelBlock.extract(ast) |
108 | 122 | .map(celBlock -> planBlock(celBlock, ctx)) |
@@ -248,6 +262,11 @@ private PlannedInterpretable planCall(CelExpr expr, PlannerContext ctx) { |
248 | 262 | // map key lookup on Map, and SelectableValue fallback). |
249 | 263 | if (functionName.equals(OptimizedSelectPlanner.CEL_ATTRIBUTE_FUNCTION_NAME) |
250 | 264 | || functionName.equals(OptimizedSelectPlanner.CEL_HAS_FIELD_FUNCTION_NAME)) { |
| 265 | + checkArgument( |
| 266 | + ctx.runtimeExtensions.contains(SELECT_OPTIMIZATION_EXTENSION), |
| 267 | + "%s requires the %s AST extension", |
| 268 | + functionName, |
| 269 | + SELECT_OPTIMIZATION_EXTENSION); |
251 | 270 | return optimizedSelectPlanner.plan(expr, functionName, operandExpr -> plan(operandExpr, ctx)); |
252 | 271 | } |
253 | 272 |
|
@@ -382,6 +401,11 @@ private PlannedInterpretable planCall(CelExpr expr, PlannerContext ctx) { |
382 | 401 | } |
383 | 402 |
|
384 | 403 | private PlannedInterpretable planBlock(CelBlock celBlock, PlannerContext ctx) { |
| 404 | + checkArgument( |
| 405 | + ctx.runtimeExtensions.contains(CEL_BLOCK_EXTENSION), |
| 406 | + "%s requires the %s AST extension", |
| 407 | + CelBlock.FUNCTION_NAME, |
| 408 | + CEL_BLOCK_EXTENSION); |
385 | 409 | ImmutableList<CelExpr> indices = celBlock.indices(); |
386 | 410 |
|
387 | 411 | PlannedInterpretable[] slotExprs = new PlannedInterpretable[indices.size()]; |
@@ -716,23 +740,20 @@ private static Builder newBuilder() { |
716 | 740 | } |
717 | 741 | } |
718 | 742 |
|
719 | | - static final class PlannerContext { |
| 743 | + private static final class PlannerContext { |
720 | 744 | private final CelAbstractSyntaxTree ast; |
721 | | - private final HashMap<String, Integer> localVars = new HashMap<>(); |
722 | | - |
723 | | - CelAbstractSyntaxTree ast() { |
724 | | - return ast; |
725 | | - } |
| 745 | + private final ImmutableSet<String> runtimeExtensions; |
| 746 | + private final Map<String, Integer> localVars; |
726 | 747 |
|
727 | | - ImmutableMap<Long, CelReference> referenceMap() { |
| 748 | + private ImmutableMap<Long, CelReference> referenceMap() { |
728 | 749 | return ast.getReferenceMap(); |
729 | 750 | } |
730 | 751 |
|
731 | | - ImmutableMap<Long, CelType> typeMap() { |
| 752 | + private ImmutableMap<Long, CelType> typeMap() { |
732 | 753 | return ast.getTypeMap(); |
733 | 754 | } |
734 | 755 |
|
735 | | - boolean isChecked() { |
| 756 | + private boolean isChecked() { |
736 | 757 | return ast.isChecked(); |
737 | 758 | } |
738 | 759 |
|
@@ -766,12 +787,35 @@ private boolean isLocalVar(String name) { |
766 | 787 | return localVars.containsKey(name); |
767 | 788 | } |
768 | 789 |
|
769 | | - static PlannerContext create(CelAbstractSyntaxTree ast) { |
770 | | - return new PlannerContext(ast); |
| 790 | + private static PlannerContext create(CelAbstractSyntaxTree ast, CelOptions options) { |
| 791 | + ImmutableSet.Builder<String> runtimeExtensions = ImmutableSet.builder(); |
| 792 | + for (Extension extension : ast.getSource().getExtensions()) { |
| 793 | + if (!extension.affectedComponents().contains(Component.COMPONENT_RUNTIME)) { |
| 794 | + continue; |
| 795 | + } |
| 796 | + Version version = extension.version(); |
| 797 | + Version supportedVersion = SUPPORTED_RUNTIME_EXTENSIONS.get(extension.id()); |
| 798 | + checkArgument( |
| 799 | + supportedVersion != null |
| 800 | + && version.major() == supportedVersion.major() |
| 801 | + && version.minor() <= supportedVersion.minor(), |
| 802 | + "unsupported CEL extension: %s@%s.%s", |
| 803 | + extension.id(), |
| 804 | + version.major(), |
| 805 | + version.minor()); |
| 806 | + runtimeExtensions.add(extension.id()); |
| 807 | + } |
| 808 | + ImmutableSet<String> extensions = runtimeExtensions.build(); |
| 809 | + checkArgument( |
| 810 | + !extensions.contains(JSON_NAME_EXTENSION) || options.enableJsonFieldNames(), |
| 811 | + "json_name extension requires CelOptions.enableJsonFieldNames(true)"); |
| 812 | + return new PlannerContext(ast, extensions); |
771 | 813 | } |
772 | 814 |
|
773 | | - private PlannerContext(CelAbstractSyntaxTree ast) { |
| 815 | + private PlannerContext(CelAbstractSyntaxTree ast, ImmutableSet<String> runtimeExtensions) { |
774 | 816 | this.ast = checkNotNull(ast); |
| 817 | + this.runtimeExtensions = runtimeExtensions; |
| 818 | + this.localVars = new HashMap<>(); |
775 | 819 | } |
776 | 820 | } |
777 | 821 |
|
|
0 commit comments