Skip to content

Commit 697f00f

Browse files
CEL Dev Teamcopybara-github
authored andcommitted
Add support for stdlib subsetting via CelEnvironment
PiperOrigin-RevId: 776693221
1 parent 52015ce commit 697f00f

13 files changed

Lines changed: 857 additions & 6 deletions

File tree

bundle/src/main/java/dev/cel/bundle/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ java_library(
6161
":required_fields_checker",
6262
"//:auto_value",
6363
"//bundle:cel",
64+
"//checker:standard_decl",
6465
"//common:compiler_common",
6566
"//common:options",
6667
"//common:source",
@@ -69,6 +70,7 @@ java_library(
6970
"//compiler:compiler_builder",
7071
"//extensions",
7172
"//extensions:optional_library",
73+
"//parser:macro",
7274
"//runtime",
7375
"@maven//:com_google_errorprone_error_prone_annotations",
7476
"@maven//:com_google_guava_guava",

bundle/src/main/java/dev/cel/bundle/CelEnvironment.java

Lines changed: 270 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import static com.google.common.base.Preconditions.checkNotNull;
1818
import static com.google.common.base.Preconditions.checkState;
1919
import static com.google.common.collect.ImmutableList.toImmutableList;
20+
import static com.google.common.collect.ImmutableSet.toImmutableSet;
2021

2122
import com.google.auto.value.AutoValue;
2223
import com.google.common.annotations.VisibleForTesting;
@@ -25,6 +26,10 @@
2526
import com.google.common.collect.ImmutableSet;
2627
import com.google.errorprone.annotations.CanIgnoreReturnValue;
2728
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;
2833
import dev.cel.common.CelFunctionDecl;
2934
import dev.cel.common.CelOptions;
3035
import dev.cel.common.CelOverloadDecl;
@@ -41,6 +46,7 @@
4146
import dev.cel.compiler.CelCompilerBuilder;
4247
import dev.cel.extensions.CelExtensions;
4348
import dev.cel.extensions.CelOptionalLibrary;
49+
import dev.cel.parser.CelStandardMacro;
4450
import dev.cel.runtime.CelRuntimeBuilder;
4551
import java.util.Arrays;
4652
import java.util.Optional;
@@ -97,6 +103,9 @@ public abstract class CelEnvironment {
97103
/** New function declarations to add in the compilation environment. */
98104
public abstract ImmutableSet<FunctionDecl> functions();
99105

106+
/** Standard library subset (which macros, functions to include/exclude) */
107+
public abstract Optional<LibrarySubset> standardLibrarySubset();
108+
100109
/** Builder for {@link CelEnvironment}. */
101110
@AutoValue.Builder
102111
public abstract static class Builder {
@@ -141,8 +150,28 @@ public Builder setFunctions(FunctionDecl... functions) {
141150

142151
public abstract Builder setFunctions(ImmutableSet<FunctionDecl> functions);
143152

153+
public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);
154+
155+
abstract CelEnvironment autoBuild();
156+
144157
@CheckReturnValue
145-
public abstract CelEnvironment build();
158+
public final CelEnvironment build() {
159+
CelEnvironment env = autoBuild();
160+
if (env.standardLibrarySubset().isPresent()) {
161+
LibrarySubset librarySubset = env.standardLibrarySubset().get();
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+
}
146175
}
147176

148177
/** Creates a new builder to construct a {@link CelEnvironment} instance. */
@@ -180,6 +209,8 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
180209

181210
addAllCompilerExtensions(compilerBuilder, celOptions);
182211

212+
applyStandardLibrarySubset(compilerBuilder, celTypeProvider);
213+
183214
return compilerBuilder.build();
184215
} catch (RuntimeException e) {
185216
throw new CelEnvironmentException(e.getMessage(), e);
@@ -218,6 +249,68 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
218249
}
219250
}
220251

252+
private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder,
253+
CelTypeProvider celTypeProvider) {
254+
if (standardLibrarySubset().isEmpty()) {
255+
return;
256+
}
257+
258+
LibrarySubset librarySubset = standardLibrarySubset().get();
259+
if (librarySubset.disabled()) {
260+
compilerBuilder.setStandardEnvironmentEnabled(false);
261+
return;
262+
}
263+
264+
if (librarySubset.macrosDisabled()) {
265+
compilerBuilder.setStandardMacros(ImmutableList.of());
266+
} else if (!librarySubset.includedMacros().isEmpty()) {
267+
compilerBuilder.setStandardMacros(
268+
librarySubset.includedMacros().stream()
269+
.map(CelEnvironment::getStandardMacroOrThrow)
270+
.collect(toImmutableSet()));
271+
} else if (!librarySubset.excludedMacros().isEmpty()) {
272+
ImmutableSet<CelStandardMacro> set =
273+
librarySubset.excludedMacros().stream()
274+
.map(CelEnvironment::getStandardMacroOrThrow)
275+
.collect(toImmutableSet());
276+
compilerBuilder.setStandardMacros(
277+
CelStandardMacro.STANDARD_MACROS.stream()
278+
.filter(macro -> !set.contains(macro))
279+
.collect(toImmutableSet()));
280+
}
281+
282+
if (!librarySubset.includedFunctions().isEmpty()) {
283+
ImmutableSet<FunctionSelector> includedFunctions = librarySubset.includedFunctions();
284+
compilerBuilder
285+
.setStandardEnvironmentEnabled(false)
286+
.setStandardDeclarations(
287+
CelStandardDeclarations.newBuilder()
288+
.filterFunctions(
289+
(function, overload) ->
290+
FunctionSelector.matchesAny(function, overload, includedFunctions))
291+
.build());
292+
} else if (!librarySubset.excludedFunctions().isEmpty()) {
293+
ImmutableSet<FunctionSelector> excludedFunctions = librarySubset.excludedFunctions();
294+
compilerBuilder
295+
.setStandardEnvironmentEnabled(false)
296+
.setStandardDeclarations(
297+
CelStandardDeclarations.newBuilder()
298+
.filterFunctions(
299+
(function, overload) ->
300+
!FunctionSelector.matchesAny(function, overload, excludedFunctions))
301+
.build());
302+
}
303+
}
304+
305+
private static CelStandardMacro getStandardMacroOrThrow(String macroName) {
306+
for (CelStandardMacro macro : CelStandardMacro.STANDARD_MACROS) {
307+
if (macro.getFunction().equals(macroName)) {
308+
return macro;
309+
}
310+
}
311+
throw new IllegalArgumentException("unrecognized standard macro `" + macroName + "'");
312+
}
313+
221314
private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
222315
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
223316
if (extension == null) {
@@ -624,4 +717,180 @@ void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
624717
this.runtimeExtensionApplier = runtimeExtensionApplier;
625718
}
626719
}
720+
721+
/**
722+
* LibrarySubset indicates a subset of the macros and function supported by a subsettable
723+
* library.
724+
*/
725+
@AutoValue
726+
public abstract static class LibrarySubset {
727+
728+
/**
729+
* Disabled indicates whether the library has been disabled, typically only used for
730+
* default-enabled libraries like stdlib.
731+
*/
732+
public abstract boolean disabled();
733+
734+
/** DisableMacros disables macros for the given library. */
735+
public abstract boolean macrosDisabled();
736+
737+
/** IncludeMacros specifies a set of macro function names to include in the subset. */
738+
public abstract ImmutableSet<String> includedMacros();
739+
740+
/**
741+
* ExcludeMacros specifies a set of macro function names to exclude from the subset.
742+
*
743+
* <p>Note: if IncludedMacros is non-empty, then ExcludedMacros is ignored.
744+
*/
745+
public abstract ImmutableSet<String> excludedMacros();
746+
747+
/**
748+
* IncludeFunctions specifies a set of functions to include in the subset.
749+
*
750+
* <p>Note: the overloads specified in the subset need only specify their ID.
751+
* <p>Note: if IncludedFunctions is non-empty, then ExcludedFunctions is ignored.
752+
*/
753+
public abstract ImmutableSet<FunctionSelector> includedFunctions();
754+
755+
/**
756+
* ExcludeFunctions specifies the set of functions to exclude from the subset.
757+
*
758+
* <p>Note: the overloads specified in the subset need only specify their ID.
759+
*/
760+
public abstract ImmutableSet<FunctionSelector> excludedFunctions();
761+
762+
public static Builder newBuilder() {
763+
return new AutoValue_CelEnvironment_LibrarySubset.Builder()
764+
.setDisabled(true)
765+
.setMacrosDisabled(false)
766+
.setIncludedMacros(ImmutableSet.of())
767+
.setExcludedMacros(ImmutableSet.of())
768+
.setIncludedFunctions(ImmutableSet.of())
769+
.setExcludedFunctions(ImmutableSet.of());
770+
}
771+
772+
/** Builder for {@link LibrarySubset}. */
773+
@AutoValue.Builder
774+
public abstract static class Builder {
775+
776+
public abstract Builder setDisabled(boolean disabled);
777+
778+
public abstract Builder setMacrosDisabled(boolean disabled);
779+
780+
public abstract Builder setIncludedMacros(ImmutableSet<String> includedMacros);
781+
782+
public abstract Builder setExcludedMacros(ImmutableSet<String> excludedMacros);
783+
784+
public abstract Builder setIncludedFunctions(
785+
ImmutableSet<FunctionSelector> includedFunctions);
786+
787+
public abstract Builder setExcludedFunctions(
788+
ImmutableSet<FunctionSelector> excludedFunctions);
789+
790+
@CheckReturnValue
791+
public abstract LibrarySubset build();
792+
}
793+
794+
/**
795+
* Represents a function selector, which can be used to configure included/excluded library
796+
* functions.
797+
*/
798+
@AutoValue
799+
public abstract static class FunctionSelector {
800+
801+
public abstract String name();
802+
803+
public abstract ImmutableSet<OverloadSelector> overloads();
804+
805+
/** Builder for {@link FunctionSelector}. */
806+
@AutoValue.Builder
807+
public abstract static class Builder implements RequiredFieldsChecker {
808+
809+
public abstract Optional<String> name();
810+
811+
public abstract Builder setName(String name);
812+
813+
public abstract Builder setOverloads(ImmutableSet<OverloadSelector> overloads);
814+
815+
@Override
816+
public ImmutableList<RequiredField> requiredFields() {
817+
return ImmutableList.of(RequiredField.of("name", this::name));
818+
}
819+
820+
/** Builds a new instance of {@link FunctionSelector}. */
821+
public abstract FunctionSelector build();
822+
}
823+
824+
/** Creates a new builder to construct a {@link FunctionSelector} instance. */
825+
public static FunctionSelector.Builder newBuilder() {
826+
return new AutoValue_CelEnvironment_LibrarySubset_FunctionSelector.Builder()
827+
.setOverloads(ImmutableSet.of());
828+
}
829+
830+
public static FunctionSelector create(String name, ImmutableSet<String> overloads) {
831+
return newBuilder()
832+
.setName(name)
833+
.setOverloads(
834+
overloads.stream()
835+
.map(id -> OverloadSelector.newBuilder().setId(id).build())
836+
.collect(toImmutableSet()))
837+
.build();
838+
}
839+
840+
public static boolean matchesAny(
841+
StandardFunction function,
842+
StandardOverload overload,
843+
ImmutableSet<FunctionSelector> selectors) {
844+
String functionName = function.functionDecl().name();
845+
for (FunctionSelector functionSelector : selectors) {
846+
if (!functionSelector.name().equals(functionName)) {
847+
continue;
848+
}
849+
850+
if (functionSelector.overloads().isEmpty()) {
851+
return true;
852+
}
853+
854+
String overloadId = overload.celOverloadDecl().overloadId();
855+
for (OverloadSelector overloadSelector : functionSelector.overloads()) {
856+
if (overloadSelector.id().equals(overloadId)) {
857+
return true;
858+
}
859+
}
860+
}
861+
return false;
862+
}
863+
}
864+
865+
/** Represents an overload selector on a function selector. */
866+
@AutoValue
867+
public abstract static class OverloadSelector {
868+
869+
/** An overload ID. Required. Follows the same format as {@link OverloadDecl#id()} */
870+
public abstract String id();
871+
872+
/** Builder for {@link OverloadSelector}. */
873+
@AutoValue.Builder
874+
public abstract static class Builder implements RequiredFieldsChecker {
875+
876+
public abstract Optional<String> id();
877+
878+
public abstract Builder setId(String overloadId);
879+
880+
@Override
881+
public ImmutableList<RequiredField> requiredFields() {
882+
return ImmutableList.of(RequiredField.of("id", this::id));
883+
}
884+
885+
/** Builds a new instance of {@link OverloadDecl}. */
886+
@CheckReturnValue
887+
public abstract OverloadSelector build();
888+
}
889+
890+
/** Creates a new builder to construct a {@link OverloadSelector} instance. */
891+
public static OverloadSelector.Builder newBuilder() {
892+
return new AutoValue_CelEnvironment_LibrarySubset_OverloadSelector.Builder();
893+
}
894+
}
895+
}
627896
}

0 commit comments

Comments
 (0)