Skip to content

Commit 787a3c5

Browse files
jnthntatumcopybara-github
authored andcommitted
Add support for importing/exporting common limits to YAML environment configs.
PiperOrigin-RevId: 874676207
1 parent 4cf2b79 commit 787a3c5

14 files changed

Lines changed: 530 additions & 19 deletions

bundle/BUILD.bazel

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,5 @@ java_library(
2727

2828
java_library(
2929
name = "environment_exporter",
30-
visibility = ["//:internal"],
3130
exports = ["//bundle/src/main/java/dev/cel/bundle:environment_exporter"],
3231
)

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,15 @@ java_library(
126126
":environment",
127127
"//:auto_value",
128128
"//bundle:cel",
129+
"//checker:checker_builder",
129130
"//checker:standard_decl",
130131
"//common:compiler_common",
131132
"//common:options",
132133
"//common/internal:env_visitor",
133134
"//common/types:cel_proto_types",
134135
"//common/types:cel_types",
135136
"//common/types:type_providers",
137+
"//compiler:compiler_builder",
136138
"//extensions",
137139
"//extensions:extension_library",
138140
"//parser:macro",

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

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import dev.cel.compiler.CelCompilerLibrary;
4949
import dev.cel.extensions.CelExtensions;
5050
import dev.cel.parser.CelStandardMacro;
51+
import dev.cel.runtime.CelRuntime;
5152
import dev.cel.runtime.CelRuntimeBuilder;
5253
import dev.cel.runtime.CelRuntimeLibrary;
5354
import java.util.Arrays;
@@ -108,6 +109,12 @@ public abstract class CelEnvironment {
108109
/** Standard library subset (which macros, functions to include/exclude) */
109110
public abstract Optional<LibrarySubset> standardLibrarySubset();
110111

112+
/** Feature flags to enable in the environment. */
113+
public abstract ImmutableSet<FeatureFlag> features();
114+
115+
/** Limits to set in the environment. */
116+
public abstract ImmutableSet<Limit> limits();
117+
111118
/** Builder for {@link CelEnvironment}. */
112119
@AutoValue.Builder
113120
public abstract static class Builder {
@@ -159,6 +166,20 @@ public Builder setFunctions(FunctionDecl... functions) {
159166

160167
public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);
161168

169+
@CanIgnoreReturnValue
170+
public Builder setFeatures(FeatureFlag... featureFlags) {
171+
return setFeatures(ImmutableSet.copyOf(featureFlags));
172+
}
173+
174+
public abstract Builder setFeatures(ImmutableSet<FeatureFlag> featureFlags);
175+
176+
@CanIgnoreReturnValue
177+
public Builder setLimits(Limit... limits) {
178+
return setLimits(ImmutableSet.copyOf(limits));
179+
}
180+
181+
public abstract Builder setLimits(ImmutableSet<Limit> limits);
182+
162183
abstract CelEnvironment autoBuild();
163184

164185
@CheckReturnValue
@@ -188,18 +209,22 @@ public static Builder newBuilder() {
188209
.setDescription("")
189210
.setContainer(CelContainer.ofName(""))
190211
.setVariables(ImmutableSet.of())
191-
.setFunctions(ImmutableSet.of());
212+
.setFunctions(ImmutableSet.of())
213+
.setFeatures(ImmutableSet.of())
214+
.setLimits(ImmutableSet.of());
192215
}
193216

194217
/** Extends the provided {@link CelCompiler} environment with this configuration. */
195218
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
196219
throws CelEnvironmentException {
220+
celOptions = applyEnvironmentOptions(celOptions);
197221
try {
198222
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
199223
CelCompilerBuilder compilerBuilder =
200224
celCompiler
201225
.toCompilerBuilder()
202226
.setContainer(container())
227+
.setOptions(celOptions)
203228
.setTypeProvider(celTypeProvider)
204229
.addVarDeclarations(
205230
variables().stream()
@@ -222,19 +247,46 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
222247

223248
/** Extends the provided {@link Cel} environment with this configuration. */
224249
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
250+
celOptions = applyEnvironmentOptions(celOptions);
225251
try {
226252
// Casting is necessary to only extend the compiler here
227253
CelCompiler celCompiler = extend((CelCompiler) cel, celOptions);
228254

229-
CelRuntimeBuilder celRuntimeBuilder = cel.toRuntimeBuilder();
230-
addAllRuntimeExtensions(celRuntimeBuilder, celOptions);
255+
CelRuntime celRuntime = extendRuntime(cel, celOptions);
231256

232-
return CelFactory.combine(celCompiler, celRuntimeBuilder.build());
257+
return CelFactory.combine(celCompiler, celRuntime);
233258
} catch (RuntimeException e) {
234259
throw new CelEnvironmentException(e.getMessage(), e);
235260
}
236261
}
237262

263+
private CelOptions applyEnvironmentOptions(CelOptions celOptions) {
264+
CelOptions.Builder optionsBuilder = celOptions.toBuilder();
265+
for (FeatureFlag featureFlag : features()) {
266+
if (featureFlag.name().equals("cel.feature.macro_call_tracking")) {
267+
optionsBuilder.populateMacroCalls(featureFlag.enabled());
268+
} else if (featureFlag.name().equals("cel.feature.backtick_escape_syntax")) {
269+
optionsBuilder.enableQuotedIdentifierSyntax(featureFlag.enabled());
270+
} else if (featureFlag.name().equals("cel.feature.cross_type_numeric_comparisons")) {
271+
optionsBuilder.enableHeterogeneousNumericComparisons(featureFlag.enabled());
272+
} else {
273+
throw new IllegalArgumentException("Unknown feature flag: " + featureFlag.name());
274+
}
275+
}
276+
for (Limit limit : limits()) {
277+
if (limit.name().equals("cel.limit.expression_code_points")) {
278+
optionsBuilder.maxExpressionCodePointSize(limit.value());
279+
} else if (limit.name().equals("cel.limit.parse_error_recovery")) {
280+
optionsBuilder.maxParseErrorRecoveryLimit(limit.value());
281+
} else if (limit.name().equals("cel.limit.parse_recursion_depth")) {
282+
optionsBuilder.maxParseRecursionDepth(limit.value());
283+
} else {
284+
throw new IllegalArgumentException("Unknown limit: " + limit.name());
285+
}
286+
}
287+
return optionsBuilder.build();
288+
}
289+
238290
private void addAllCompilerExtensions(
239291
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
240292
// TODO: Add capability to accept user defined exceptions
@@ -250,7 +302,9 @@ private void addAllCompilerExtensions(
250302
}
251303
}
252304

253-
private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
305+
private CelRuntime extendRuntime(CelRuntime celRuntime, CelOptions celOptions) {
306+
CelRuntimeBuilder celRuntimeBuilder = celRuntime.toRuntimeBuilder();
307+
celRuntimeBuilder.setOptions(celOptions);
254308
// TODO: Add capability to accept user defined exceptions
255309
for (ExtensionConfig extensionConfig : extensions()) {
256310
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
@@ -262,6 +316,7 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
262316
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
263317
}
264318
}
319+
return celRuntimeBuilder.build();
265320
}
266321

267322
private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
@@ -625,6 +680,39 @@ public CelType toCelType(CelTypeProvider celTypeProvider) {
625680
}
626681
}
627682

683+
/** Represents a feature flag that can be enabled in the environment. */
684+
@AutoValue
685+
public abstract static class FeatureFlag {
686+
/** Normalized name of the feature flag. */
687+
public abstract String name();
688+
689+
/** Whether the feature is enabled or disabled. */
690+
public abstract boolean enabled();
691+
692+
public static FeatureFlag create(String name, boolean enabled) {
693+
return new AutoValue_CelEnvironment_FeatureFlag(name, enabled);
694+
}
695+
}
696+
697+
/**
698+
* Represents a configurable limit in the environment.
699+
*
700+
* <p>A negative value indicates no limit. If not specified, the limit should be set to the
701+
* library default.
702+
*/
703+
@AutoValue
704+
public abstract static class Limit {
705+
/** Normalized name of the limit (e.g. cel.limit.expression_code_points */
706+
public abstract String name();
707+
708+
/** The value of the limit, -1 means no limit. */
709+
public abstract int value();
710+
711+
public static Limit create(String name, int value) {
712+
return new AutoValue_CelEnvironment_Limit(name, value);
713+
}
714+
}
715+
628716
/**
629717
* Represents a configuration for a canonical CEL extension that can be enabled in the
630718
* environment.

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

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import dev.cel.expr.Decl.FunctionDecl;
2323
import dev.cel.expr.Decl.FunctionDecl.Overload;
2424
import com.google.auto.value.AutoValue;
25+
import com.google.common.base.Preconditions;
2526
import com.google.common.collect.ArrayListMultimap;
2627
import com.google.common.collect.ImmutableSet;
2728
import com.google.common.collect.ListMultimap;
@@ -30,6 +31,7 @@
3031
import dev.cel.bundle.CelEnvironment.LibrarySubset;
3132
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
3233
import dev.cel.bundle.CelEnvironment.OverloadDecl;
34+
import dev.cel.checker.CelCheckerBuilder;
3335
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
3436
import dev.cel.checker.CelStandardDeclarations.StandardIdentifier;
3537
import dev.cel.common.CelFunctionDecl;
@@ -41,6 +43,7 @@
4143
import dev.cel.common.types.CelProtoTypes;
4244
import dev.cel.common.types.CelType;
4345
import dev.cel.common.types.CelTypes;
46+
import dev.cel.compiler.CelCompiler;
4447
import dev.cel.extensions.CelExtensionLibrary;
4548
import dev.cel.extensions.CelExtensions;
4649
import dev.cel.parser.CelMacro;
@@ -161,9 +164,24 @@ public static CelEnvironmentExporter.Builder newBuilder() {
161164
* </ul>
162165
*/
163166
public CelEnvironment export(Cel cel) {
164-
CelEnvironment.Builder envBuilder =
165-
CelEnvironment.newBuilder().setContainer(cel.toCheckerBuilder().container());
167+
return export((CelCompiler) cel);
168+
}
166169

170+
/**
171+
* Exports a {@link CelEnvironment} that describes the configuration of the given {@link
172+
* CelCompiler} instance.
173+
*
174+
* <p>The exported environment includes:
175+
*
176+
* <ul>
177+
* <li>Standard library subset: functions and their overloads that are either included or
178+
* excluded from the standard library.
179+
* <li>Extension libraries: names and versions of the extension libraries that are used.
180+
* <li>Custom declarations: functions and variables that are not part of the standard library or
181+
* any of the extension libraries.
182+
* </ul>
183+
*/
184+
public CelEnvironment export(CelCompiler cel) {
167185
// Inventory is a full set of declarations and macros that are found in the configuration of
168186
// the supplied CEL instance.
169187
//
@@ -172,6 +190,14 @@ public CelEnvironment export(Cel cel) {
172190
//
173191
// Whatever is left will be included in the Environment as custom declarations.
174192

193+
// Checker builder is used to access some parts of the config not exposed in the EnvVisitable
194+
// interface.
195+
CelCheckerBuilder checkerBuilder = cel.toCheckerBuilder();
196+
197+
CelEnvironment.Builder envBuilder =
198+
CelEnvironment.newBuilder().setContainer(checkerBuilder.container());
199+
addOptions(envBuilder, checkerBuilder.options());
200+
175201
Set<Object> inventory = new HashSet<>();
176202
collectInventory(inventory, cel);
177203
addExtensionConfigsAndRemoveFromInventory(envBuilder, inventory);
@@ -180,11 +206,46 @@ public CelEnvironment export(Cel cel) {
180206
return envBuilder.build();
181207
}
182208

209+
private void addOptions(CelEnvironment.Builder envBuilder, CelOptions options) {
210+
// The set of features supported in the exported environment in Go is pretty limited right now.
211+
ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();
212+
if (options.enableHeterogeneousNumericComparisons()) {
213+
featureFlags.add(
214+
CelEnvironment.FeatureFlag.create("cel.feature.cross_type_numeric_comparisons", true));
215+
}
216+
if (options.enableQuotedIdentifierSyntax()) {
217+
featureFlags.add(
218+
CelEnvironment.FeatureFlag.create("cel.feature.backtick_escape_syntax", true));
219+
}
220+
if (options.populateMacroCalls()) {
221+
featureFlags.add(CelEnvironment.FeatureFlag.create("cel.feature.macro_call_tracking", true));
222+
}
223+
envBuilder.setFeatures(featureFlags.build());
224+
ImmutableSet.Builder<CelEnvironment.Limit> limits = ImmutableSet.builder();
225+
if (options.maxExpressionCodePointSize() != CelOptions.DEFAULT.maxExpressionCodePointSize()) {
226+
limits.add(
227+
CelEnvironment.Limit.create(
228+
"cel.limit.expression_code_points", options.maxExpressionCodePointSize()));
229+
}
230+
if (options.maxParseErrorRecoveryLimit() != CelOptions.DEFAULT.maxParseErrorRecoveryLimit()) {
231+
limits.add(
232+
CelEnvironment.Limit.create(
233+
"cel.limit.parse_error_recovery", options.maxParseErrorRecoveryLimit()));
234+
}
235+
if (options.maxParseRecursionDepth() != CelOptions.DEFAULT.maxParseRecursionDepth()) {
236+
limits.add(
237+
CelEnvironment.Limit.create(
238+
"cel.limit.parse_recursion_depth", options.maxParseRecursionDepth()));
239+
}
240+
envBuilder.setLimits(limits.build());
241+
}
242+
183243
/**
184244
* Collects all function overloads, variable declarations and macros from the given {@link Cel}
185245
* instance and stores them in a map.
186246
*/
187-
private void collectInventory(Set<Object> inventory, Cel cel) {
247+
private void collectInventory(Set<Object> inventory, CelCompiler cel) {
248+
Preconditions.checkArgument(cel instanceof EnvVisitable);
188249
((EnvVisitable) cel)
189250
.accept(
190251
new EnvVisitor() {

0 commit comments

Comments
 (0)