|
17 | 17 | import static com.google.common.base.Preconditions.checkNotNull; |
18 | 18 | import static com.google.common.base.Preconditions.checkState; |
19 | 19 | import static com.google.common.collect.ImmutableList.toImmutableList; |
| 20 | +import static com.google.common.collect.ImmutableSet.toImmutableSet; |
20 | 21 |
|
21 | 22 | import com.google.auto.value.AutoValue; |
22 | 23 | import com.google.common.annotations.VisibleForTesting; |
|
25 | 26 | import com.google.common.collect.ImmutableSet; |
26 | 27 | import com.google.errorprone.annotations.CanIgnoreReturnValue; |
27 | 28 | import com.google.errorprone.annotations.CheckReturnValue; |
| 29 | +import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector; |
| 30 | +import dev.cel.checker.CelStandardDeclarations; |
| 31 | +import dev.cel.checker.CelStandardDeclarations.StandardFunction; |
| 32 | +import dev.cel.checker.CelStandardDeclarations.StandardOverload; |
28 | 33 | import dev.cel.common.CelFunctionDecl; |
29 | 34 | import dev.cel.common.CelOptions; |
30 | 35 | import dev.cel.common.CelOverloadDecl; |
|
41 | 46 | import dev.cel.compiler.CelCompilerBuilder; |
42 | 47 | import dev.cel.extensions.CelExtensions; |
43 | 48 | import dev.cel.extensions.CelOptionalLibrary; |
| 49 | +import dev.cel.parser.CelStandardMacro; |
44 | 50 | import dev.cel.runtime.CelRuntimeBuilder; |
45 | 51 | import java.util.Arrays; |
46 | 52 | import java.util.Optional; |
@@ -97,6 +103,9 @@ public abstract class CelEnvironment { |
97 | 103 | /** New function declarations to add in the compilation environment. */ |
98 | 104 | public abstract ImmutableSet<FunctionDecl> functions(); |
99 | 105 |
|
| 106 | + /** Standard library subset (which macros, functions to include/exclude) */ |
| 107 | + public abstract Optional<LibrarySubset> standardLibrarySubset(); |
| 108 | + |
100 | 109 | /** Builder for {@link CelEnvironment}. */ |
101 | 110 | @AutoValue.Builder |
102 | 111 | public abstract static class Builder { |
@@ -141,8 +150,28 @@ public Builder setFunctions(FunctionDecl... functions) { |
141 | 150 |
|
142 | 151 | public abstract Builder setFunctions(ImmutableSet<FunctionDecl> functions); |
143 | 152 |
|
| 153 | + public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset); |
| 154 | + |
| 155 | + abstract CelEnvironment autoBuild(); |
| 156 | + |
144 | 157 | @CheckReturnValue |
145 | | - public abstract CelEnvironment build(); |
| 158 | + public final CelEnvironment build() { |
| 159 | + CelEnvironment env = autoBuild(); |
| 160 | + LibrarySubset librarySubset = env.standardLibrarySubset().orElse(null); |
| 161 | + if (librarySubset != null) { |
| 162 | + if (!librarySubset.includedMacros().isEmpty() |
| 163 | + && !librarySubset.excludedMacros().isEmpty()) { |
| 164 | + throw new IllegalArgumentException( |
| 165 | + "Invalid subset: cannot both include and exclude macros"); |
| 166 | + } |
| 167 | + if (!librarySubset.includedFunctions().isEmpty() |
| 168 | + && !librarySubset.excludedFunctions().isEmpty()) { |
| 169 | + throw new IllegalArgumentException( |
| 170 | + "Invalid subset: cannot both include and exclude functions"); |
| 171 | + } |
| 172 | + } |
| 173 | + return env; |
| 174 | + } |
146 | 175 | } |
147 | 176 |
|
148 | 177 | /** Creates a new builder to construct a {@link CelEnvironment} instance. */ |
@@ -180,6 +209,8 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions) |
180 | 209 |
|
181 | 210 | addAllCompilerExtensions(compilerBuilder, celOptions); |
182 | 211 |
|
| 212 | + applyStandardLibrarySubset(compilerBuilder); |
| 213 | + |
183 | 214 | return compilerBuilder.build(); |
184 | 215 | } catch (RuntimeException e) { |
185 | 216 | throw new CelEnvironmentException(e.getMessage(), e); |
@@ -218,6 +249,67 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt |
218 | 249 | } |
219 | 250 | } |
220 | 251 |
|
| 252 | + private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) { |
| 253 | + if (!standardLibrarySubset().isPresent()) { |
| 254 | + return; |
| 255 | + } |
| 256 | + |
| 257 | + LibrarySubset librarySubset = standardLibrarySubset().get(); |
| 258 | + if (librarySubset.disabled()) { |
| 259 | + compilerBuilder.setStandardEnvironmentEnabled(false); |
| 260 | + return; |
| 261 | + } |
| 262 | + |
| 263 | + if (librarySubset.macrosDisabled()) { |
| 264 | + compilerBuilder.setStandardMacros(ImmutableList.of()); |
| 265 | + } else if (!librarySubset.includedMacros().isEmpty()) { |
| 266 | + compilerBuilder.setStandardMacros( |
| 267 | + librarySubset.includedMacros().stream() |
| 268 | + .map(CelEnvironment::getStandardMacroOrThrow) |
| 269 | + .collect(toImmutableSet())); |
| 270 | + } else if (!librarySubset.excludedMacros().isEmpty()) { |
| 271 | + ImmutableSet<CelStandardMacro> set = |
| 272 | + librarySubset.excludedMacros().stream() |
| 273 | + .map(CelEnvironment::getStandardMacroOrThrow) |
| 274 | + .collect(toImmutableSet()); |
| 275 | + compilerBuilder.setStandardMacros( |
| 276 | + CelStandardMacro.STANDARD_MACROS.stream() |
| 277 | + .filter(macro -> !set.contains(macro)) |
| 278 | + .collect(toImmutableSet())); |
| 279 | + } |
| 280 | + |
| 281 | + if (!librarySubset.includedFunctions().isEmpty()) { |
| 282 | + ImmutableSet<FunctionSelector> includedFunctions = librarySubset.includedFunctions(); |
| 283 | + compilerBuilder |
| 284 | + .setStandardEnvironmentEnabled(false) |
| 285 | + .setStandardDeclarations( |
| 286 | + CelStandardDeclarations.newBuilder() |
| 287 | + .filterFunctions( |
| 288 | + (function, overload) -> |
| 289 | + FunctionSelector.matchesAny(function, overload, includedFunctions)) |
| 290 | + .build()); |
| 291 | + } else if (!librarySubset.excludedFunctions().isEmpty()) { |
| 292 | + ImmutableSet<FunctionSelector> excludedFunctions = librarySubset.excludedFunctions(); |
| 293 | + compilerBuilder |
| 294 | + .setStandardEnvironmentEnabled(false) |
| 295 | + .setStandardDeclarations( |
| 296 | + CelStandardDeclarations.newBuilder() |
| 297 | + .filterFunctions( |
| 298 | + (function, overload) -> |
| 299 | + !FunctionSelector.matchesAny(function, overload, excludedFunctions)) |
| 300 | + .build()); |
| 301 | + } |
| 302 | + } |
| 303 | + |
| 304 | + private static CelStandardMacro getStandardMacroOrThrow(String macroName) { |
| 305 | + for (CelStandardMacro macro : CelStandardMacro.STANDARD_MACROS) { |
| 306 | + if (macro.getFunction().equals(macroName)) { |
| 307 | + return macro; |
| 308 | + } |
| 309 | + } |
| 310 | + throw new IllegalArgumentException("unrecognized standard macro `" + macroName + "'"); |
| 311 | + } |
| 312 | + |
221 | 313 | private static CanonicalCelExtension getExtensionOrThrow(String extensionName) { |
222 | 314 | CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName); |
223 | 315 | if (extension == null) { |
@@ -624,4 +716,179 @@ void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) { |
624 | 716 | this.runtimeExtensionApplier = runtimeExtensionApplier; |
625 | 717 | } |
626 | 718 | } |
| 719 | + |
| 720 | + /** |
| 721 | + * LibrarySubset indicates a subset of the macros and function supported by a subsettable |
| 722 | + * library. |
| 723 | + */ |
| 724 | + @AutoValue |
| 725 | + public abstract static class LibrarySubset { |
| 726 | + |
| 727 | + /** |
| 728 | + * Disabled indicates whether the library has been disabled, typically only used for |
| 729 | + * default-enabled libraries like stdlib. |
| 730 | + */ |
| 731 | + public abstract boolean disabled(); |
| 732 | + |
| 733 | + /** DisableMacros disables macros for the given library. */ |
| 734 | + public abstract boolean macrosDisabled(); |
| 735 | + |
| 736 | + /** IncludeMacros specifies a set of macro function names to include in the subset. */ |
| 737 | + public abstract ImmutableSet<String> includedMacros(); |
| 738 | + |
| 739 | + /** |
| 740 | + * ExcludeMacros specifies a set of macro function names to exclude from the subset. |
| 741 | + * |
| 742 | + * <p>Note: if IncludedMacros is non-empty, then ExcludedMacros is ignored. |
| 743 | + */ |
| 744 | + public abstract ImmutableSet<String> excludedMacros(); |
| 745 | + |
| 746 | + /** |
| 747 | + * IncludeFunctions specifies a set of functions to include in the subset. |
| 748 | + * |
| 749 | + * <p>Note: the overloads specified in the subset need only specify their ID. |
| 750 | + * <p>Note: if IncludedFunctions is non-empty, then ExcludedFunctions is ignored. |
| 751 | + */ |
| 752 | + public abstract ImmutableSet<FunctionSelector> includedFunctions(); |
| 753 | + |
| 754 | + /** |
| 755 | + * ExcludeFunctions specifies the set of functions to exclude from the subset. |
| 756 | + * |
| 757 | + * <p>Note: the overloads specified in the subset need only specify their ID. |
| 758 | + */ |
| 759 | + public abstract ImmutableSet<FunctionSelector> excludedFunctions(); |
| 760 | + |
| 761 | + public static Builder newBuilder() { |
| 762 | + return new AutoValue_CelEnvironment_LibrarySubset.Builder() |
| 763 | + .setMacrosDisabled(false) |
| 764 | + .setIncludedMacros(ImmutableSet.of()) |
| 765 | + .setExcludedMacros(ImmutableSet.of()) |
| 766 | + .setIncludedFunctions(ImmutableSet.of()) |
| 767 | + .setExcludedFunctions(ImmutableSet.of()); |
| 768 | + } |
| 769 | + |
| 770 | + /** Builder for {@link LibrarySubset}. */ |
| 771 | + @AutoValue.Builder |
| 772 | + public abstract static class Builder { |
| 773 | + |
| 774 | + public abstract Builder setDisabled(boolean disabled); |
| 775 | + |
| 776 | + public abstract Builder setMacrosDisabled(boolean disabled); |
| 777 | + |
| 778 | + public abstract Builder setIncludedMacros(ImmutableSet<String> includedMacros); |
| 779 | + |
| 780 | + public abstract Builder setExcludedMacros(ImmutableSet<String> excludedMacros); |
| 781 | + |
| 782 | + public abstract Builder setIncludedFunctions( |
| 783 | + ImmutableSet<FunctionSelector> includedFunctions); |
| 784 | + |
| 785 | + public abstract Builder setExcludedFunctions( |
| 786 | + ImmutableSet<FunctionSelector> excludedFunctions); |
| 787 | + |
| 788 | + @CheckReturnValue |
| 789 | + public abstract LibrarySubset build(); |
| 790 | + } |
| 791 | + |
| 792 | + /** |
| 793 | + * Represents a function selector, which can be used to configure included/excluded library |
| 794 | + * functions. |
| 795 | + */ |
| 796 | + @AutoValue |
| 797 | + public abstract static class FunctionSelector { |
| 798 | + |
| 799 | + public abstract String name(); |
| 800 | + |
| 801 | + public abstract ImmutableSet<OverloadSelector> overloads(); |
| 802 | + |
| 803 | + /** Builder for {@link FunctionSelector}. */ |
| 804 | + @AutoValue.Builder |
| 805 | + public abstract static class Builder implements RequiredFieldsChecker { |
| 806 | + |
| 807 | + public abstract Optional<String> name(); |
| 808 | + |
| 809 | + public abstract Builder setName(String name); |
| 810 | + |
| 811 | + public abstract Builder setOverloads(ImmutableSet<OverloadSelector> overloads); |
| 812 | + |
| 813 | + @Override |
| 814 | + public ImmutableList<RequiredField> requiredFields() { |
| 815 | + return ImmutableList.of(RequiredField.of("name", this::name)); |
| 816 | + } |
| 817 | + |
| 818 | + /** Builds a new instance of {@link FunctionSelector}. */ |
| 819 | + public abstract FunctionSelector build(); |
| 820 | + } |
| 821 | + |
| 822 | + /** Creates a new builder to construct a {@link FunctionSelector} instance. */ |
| 823 | + public static FunctionSelector.Builder newBuilder() { |
| 824 | + return new AutoValue_CelEnvironment_LibrarySubset_FunctionSelector.Builder() |
| 825 | + .setOverloads(ImmutableSet.of()); |
| 826 | + } |
| 827 | + |
| 828 | + public static FunctionSelector create(String name, ImmutableSet<String> overloads) { |
| 829 | + return newBuilder() |
| 830 | + .setName(name) |
| 831 | + .setOverloads( |
| 832 | + overloads.stream() |
| 833 | + .map(id -> OverloadSelector.newBuilder().setId(id).build()) |
| 834 | + .collect(toImmutableSet())) |
| 835 | + .build(); |
| 836 | + } |
| 837 | + |
| 838 | + private static boolean matchesAny( |
| 839 | + StandardFunction function, |
| 840 | + StandardOverload overload, |
| 841 | + ImmutableSet<FunctionSelector> selectors) { |
| 842 | + String functionName = function.functionDecl().name(); |
| 843 | + for (FunctionSelector functionSelector : selectors) { |
| 844 | + if (!functionSelector.name().equals(functionName)) { |
| 845 | + continue; |
| 846 | + } |
| 847 | + |
| 848 | + if (functionSelector.overloads().isEmpty()) { |
| 849 | + return true; |
| 850 | + } |
| 851 | + |
| 852 | + String overloadId = overload.celOverloadDecl().overloadId(); |
| 853 | + for (OverloadSelector overloadSelector : functionSelector.overloads()) { |
| 854 | + if (overloadSelector.id().equals(overloadId)) { |
| 855 | + return true; |
| 856 | + } |
| 857 | + } |
| 858 | + } |
| 859 | + return false; |
| 860 | + } |
| 861 | + } |
| 862 | + |
| 863 | + /** Represents an overload selector on a function selector. */ |
| 864 | + @AutoValue |
| 865 | + public abstract static class OverloadSelector { |
| 866 | + |
| 867 | + /** An overload ID. Required. Follows the same format as {@link OverloadDecl#id()} */ |
| 868 | + public abstract String id(); |
| 869 | + |
| 870 | + /** Builder for {@link OverloadSelector}. */ |
| 871 | + @AutoValue.Builder |
| 872 | + public abstract static class Builder implements RequiredFieldsChecker { |
| 873 | + |
| 874 | + public abstract Optional<String> id(); |
| 875 | + |
| 876 | + public abstract Builder setId(String overloadId); |
| 877 | + |
| 878 | + @Override |
| 879 | + public ImmutableList<RequiredField> requiredFields() { |
| 880 | + return ImmutableList.of(RequiredField.of("id", this::id)); |
| 881 | + } |
| 882 | + |
| 883 | + /** Builds a new instance of {@link OverloadSelector}. */ |
| 884 | + @CheckReturnValue |
| 885 | + public abstract OverloadSelector build(); |
| 886 | + } |
| 887 | + |
| 888 | + /** Creates a new builder to construct a {@link OverloadSelector} instance. */ |
| 889 | + public static OverloadSelector.Builder newBuilder() { |
| 890 | + return new AutoValue_CelEnvironment_LibrarySubset_OverloadSelector.Builder(); |
| 891 | + } |
| 892 | + } |
| 893 | + } |
627 | 894 | } |
0 commit comments