Skip to content

Commit 0a7e83b

Browse files
l46kokcopybara-github
authored andcommitted
Add CelStandardFunctions to allow environment subsetting for the runtime
PiperOrigin-RevId: 677902385
1 parent c0dcb67 commit 0a7e83b

9 files changed

Lines changed: 2503 additions & 95 deletions

File tree

common/src/main/java/dev/cel/common/CelException.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ public CelException(String message, Throwable cause) {
2727
super(message, cause);
2828
}
2929

30+
public CelException(String message, CelErrorCode errorCode) {
31+
super(message);
32+
this.errorCode = errorCode;
33+
}
34+
3035
public CelException(String message, Throwable cause, CelErrorCode errorCode) {
3136
super(message, cause);
3237
this.errorCode = errorCode;

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ java_library(
4747
tags = [
4848
],
4949
deps = [
50-
":runtime_helper",
5150
"//:auto_value",
5251
"//common",
5352
"//common:error_codes",
@@ -61,13 +60,13 @@ java_library(
6160
"//common/internal:safe_string_formatter",
6261
"//common/types",
6362
"//common/types:type_providers",
63+
"//runtime:runtime_helper",
6464
"@cel_spec//proto/cel/expr:expr_java_proto",
6565
"@maven//:com_google_code_findbugs_annotations",
6666
"@maven//:com_google_errorprone_error_prone_annotations",
6767
"@maven//:com_google_guava_guava",
6868
"@maven//:com_google_protobuf_protobuf_java",
6969
"@maven//:com_google_protobuf_protobuf_java_util",
70-
"@maven//:com_google_re2j_re2j",
7170
"@maven//:org_jspecify_jspecify",
7271
],
7372
)
@@ -145,6 +144,7 @@ RUNTIME_SOURCES = [
145144
"CelRuntimeFactory.java",
146145
"CelRuntimeLegacyImpl.java",
147146
"CelRuntimeLibrary.java",
147+
"CelStandardFunctions.java",
148148
"CelVariableResolver.java",
149149
"HierarchicalVariableResolver.java",
150150
"UnknownContext.java",
@@ -157,6 +157,7 @@ java_library(
157157
],
158158
deps = [
159159
":evaluation_listener",
160+
":runtime_helper",
160161
":runtime_type_provider_legacy",
161162
":unknown_attributes",
162163
"//:auto_value",
@@ -165,6 +166,7 @@ java_library(
165166
"//common:options",
166167
"//common/annotations",
167168
"//common/internal:cel_descriptor_pools",
169+
"//common/internal:comparison_functions",
168170
"//common/internal:default_message_factory",
169171
"//common/internal:dynamic_proto",
170172
"//common/internal:proto_message_factory",
@@ -176,6 +178,8 @@ java_library(
176178
"@maven//:com_google_errorprone_error_prone_annotations",
177179
"@maven//:com_google_guava_guava",
178180
"@maven//:com_google_protobuf_protobuf_java",
181+
"@maven//:com_google_protobuf_protobuf_java_util",
182+
"@maven//:com_google_re2j_re2j",
179183
"@maven//:org_jspecify_jspecify",
180184
],
181185
)

runtime/src/main/java/dev/cel/runtime/CelEvaluationException.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,12 @@ public CelEvaluationException(String message, Throwable cause, CelErrorCode erro
3535
super(message, cause, errorCode);
3636
}
3737

38+
public CelEvaluationException(String message, CelErrorCode errorCode) {
39+
super(message, errorCode);
40+
}
41+
3842
CelEvaluationException(InterpreterException cause) {
39-
super(cause.getMessage(), cause.getCause());
43+
this(cause, cause.getErrorCode());
4044
}
4145

4246
CelEvaluationException(InterpreterException cause, CelErrorCode errorCode) {

runtime/src/main/java/dev/cel/runtime/CelRuntimeBuilder.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@ public interface CelRuntimeBuilder {
153153
@CanIgnoreReturnValue
154154
CelRuntimeBuilder setStandardEnvironmentEnabled(boolean value);
155155

156+
/**
157+
* Override the standard functions for the runtime. This can be used to subset the standard
158+
* environment to only expose the desired function overloads to the runtime. {@link
159+
* #setStandardEnvironmentEnabled(boolean)} must be set to false for this to take effect.
160+
*/
161+
@CanIgnoreReturnValue
162+
CelRuntimeBuilder setStandardFunctions(CelStandardFunctions standardFunctions);
163+
156164
/** Adds one or more libraries for runtime. */
157165
@CanIgnoreReturnValue
158166
CelRuntimeBuilder addLibraries(CelRuntimeLibrary... libraries);

runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java

Lines changed: 94 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
import dev.cel.common.types.CelTypes;
4444
import dev.cel.common.values.CelValueProvider;
4545
import dev.cel.common.values.ProtoMessageValueProvider;
46+
import dev.cel.runtime.CelStandardFunctions.StandardFunction.Overload.Comparison;
47+
import dev.cel.runtime.CelStandardFunctions.StandardFunction.Overload.Conversions;
4648
import java.util.Arrays;
4749
import java.util.HashMap;
4850
import java.util.Map;
@@ -87,7 +89,7 @@ public static CelRuntimeBuilder newBuilder() {
8789
public static final class Builder implements CelRuntimeBuilder {
8890

8991
private final ImmutableSet.Builder<FileDescriptor> fileTypes;
90-
private final HashMap<String, CelFunctionBinding> functionBindings;
92+
private final HashMap<String, CelFunctionBinding> customFunctionBindings;
9193
private final ImmutableSet.Builder<CelRuntimeLibrary> celRuntimeLibraries;
9294

9395
@SuppressWarnings("unused")
@@ -97,6 +99,7 @@ public static final class Builder implements CelRuntimeBuilder {
9799
private Function<String, Message.Builder> customTypeFactory;
98100
private ExtensionRegistry extensionRegistry;
99101
private CelValueProvider celValueProvider;
102+
private CelStandardFunctions overriddenStandardFunctions;
100103

101104
@Override
102105
public CelRuntimeBuilder setOptions(CelOptions options) {
@@ -111,7 +114,7 @@ public CelRuntimeBuilder addFunctionBindings(CelFunctionBinding... bindings) {
111114

112115
@Override
113116
public CelRuntimeBuilder addFunctionBindings(Iterable<CelFunctionBinding> bindings) {
114-
bindings.forEach(o -> functionBindings.putIfAbsent(o.getOverloadId(), o));
117+
bindings.forEach(o -> customFunctionBindings.putIfAbsent(o.getOverloadId(), o));
115118
return this;
116119
}
117120

@@ -160,6 +163,12 @@ public CelRuntimeBuilder setStandardEnvironmentEnabled(boolean value) {
160163
return this;
161164
}
162165

166+
@Override
167+
public CelRuntimeBuilder setStandardFunctions(CelStandardFunctions standardFunctions) {
168+
this.overriddenStandardFunctions = standardFunctions;
169+
return this;
170+
}
171+
163172
@Override
164173
public CelRuntimeBuilder addLibraries(CelRuntimeLibrary... libraries) {
165174
checkNotNull(libraries);
@@ -184,7 +193,7 @@ public CelRuntimeBuilder setExtensionRegistry(ExtensionRegistry extensionRegistr
184193
// and shouldn't be exposed to the public.
185194
@VisibleForTesting
186195
Map<String, CelFunctionBinding> getFunctionBindings() {
187-
return this.functionBindings;
196+
return this.customFunctionBindings;
188197
}
189198

190199
@VisibleForTesting
@@ -200,6 +209,11 @@ ImmutableSet.Builder<FileDescriptor> getFileTypes() {
200209
/** Build a new {@code CelRuntimeLegacyImpl} instance from the builder config. */
201210
@Override
202211
public CelRuntimeLegacyImpl build() {
212+
if (standardEnvironmentEnabled && overriddenStandardFunctions != null) {
213+
throw new IllegalArgumentException(
214+
"setStandardEnvironmentEnabled must be set to false to override standard function"
215+
+ " bindings.");
216+
}
203217
// Add libraries, such as extensions
204218
celRuntimeLibraries.build().forEach(celLibrary -> celLibrary.setRuntimeOptions(this));
205219

@@ -227,26 +241,33 @@ public CelRuntimeLegacyImpl build() {
227241

228242
DynamicProto dynamicProto = DynamicProto.create(runtimeTypeFactory);
229243

230-
DefaultDispatcher dispatcher =
231-
DefaultDispatcher.create(options, dynamicProto, standardEnvironmentEnabled);
232-
233-
ImmutableMap<String, CelFunctionBinding> functionBindingMap =
234-
ImmutableMap.copyOf(functionBindings);
235-
functionBindingMap.forEach(
236-
(String overloadId, CelFunctionBinding func) ->
237-
dispatcher.add(
238-
overloadId,
239-
func.getArgTypes(),
240-
(args) -> {
241-
try {
242-
return func.getDefinition().apply(args);
243-
} catch (CelEvaluationException e) {
244-
throw new InterpreterException.Builder(e.getMessage())
245-
.setCause(e)
246-
.setErrorCode(e.getErrorCode())
247-
.build();
248-
}
249-
}));
244+
ImmutableMap.Builder<String, CelFunctionBinding> functionBindingsBuilder =
245+
ImmutableMap.builder();
246+
for (CelFunctionBinding standardFunctionBinding : newStandardFunctionBindings(dynamicProto)) {
247+
functionBindingsBuilder.put(
248+
standardFunctionBinding.getOverloadId(), standardFunctionBinding);
249+
}
250+
251+
functionBindingsBuilder.putAll(customFunctionBindings);
252+
253+
DefaultDispatcher dispatcher = DefaultDispatcher.create();
254+
functionBindingsBuilder
255+
.buildOrThrow()
256+
.forEach(
257+
(String overloadId, CelFunctionBinding func) ->
258+
dispatcher.add(
259+
overloadId,
260+
func.getArgTypes(),
261+
(args) -> {
262+
try {
263+
return func.getDefinition().apply(args);
264+
} catch (CelEvaluationException e) {
265+
throw new InterpreterException.Builder(e.getMessage())
266+
.setCause(e)
267+
.setErrorCode(e.getErrorCode())
268+
.build();
269+
}
270+
}));
250271

251272
RuntimeTypeProvider runtimeTypeProvider;
252273

@@ -271,6 +292,54 @@ public CelRuntimeLegacyImpl build() {
271292
this);
272293
}
273294

295+
private ImmutableSet<CelFunctionBinding> newStandardFunctionBindings(
296+
DynamicProto dynamicProto) {
297+
CelStandardFunctions celStandardFunctions;
298+
if (standardEnvironmentEnabled) {
299+
celStandardFunctions =
300+
CelStandardFunctions.newBuilder()
301+
.filterFunctions(
302+
(standardFunction, standardOverload) -> {
303+
switch (standardFunction) {
304+
case INT:
305+
if (standardOverload.equals(Conversions.INT64_TO_INT64)) {
306+
// Note that we require UnsignedLong flag here to avoid ambiguous
307+
// overloads against "uint64_to_int64", because they both use the same
308+
// Java Long class. We skip adding this identity function if the flag is
309+
// disabled.
310+
return options.enableUnsignedLongs();
311+
}
312+
break;
313+
case TIMESTAMP:
314+
// TODO: Remove this flag guard once the feature has been
315+
// auto-enabled.
316+
if (standardOverload.equals(Conversions.INT64_TO_TIMESTAMP)) {
317+
return options.enableTimestampEpoch();
318+
}
319+
break;
320+
default:
321+
if (standardOverload instanceof Comparison
322+
&& !options.enableHeterogeneousNumericComparisons()) {
323+
Comparison comparison = (Comparison) standardOverload;
324+
if (comparison.isHeterogeneousComparison()) {
325+
return false;
326+
}
327+
}
328+
break;
329+
}
330+
331+
return true;
332+
})
333+
.build();
334+
} else if (overriddenStandardFunctions != null) {
335+
celStandardFunctions = overriddenStandardFunctions;
336+
} else {
337+
return ImmutableSet.of();
338+
}
339+
340+
return celStandardFunctions.newFunctionBindings(dynamicProto, options);
341+
}
342+
274343
private static CelDescriptorPool newDescriptorPool(
275344
CelDescriptors celDescriptors,
276345
ExtensionRegistry extensionRegistry) {
@@ -294,7 +363,7 @@ private static ProtoMessageFactory maybeCombineMessageFactory(
294363
private Builder() {
295364
this.options = CelOptions.newBuilder().build();
296365
this.fileTypes = ImmutableSet.builder();
297-
this.functionBindings = new HashMap<>();
366+
this.customFunctionBindings = new HashMap<>();
298367
this.celRuntimeLibraries = ImmutableSet.builder();
299368
this.extensionRegistry = ExtensionRegistry.getEmptyRegistry();
300369
this.customTypeFactory = null;
@@ -311,7 +380,7 @@ private Builder(Builder builder) {
311380
// The following needs to be deep copied as they are collection builders
312381
this.fileTypes = deepCopy(builder.fileTypes);
313382
this.celRuntimeLibraries = deepCopy(builder.celRuntimeLibraries);
314-
this.functionBindings = new HashMap<>(builder.functionBindings);
383+
this.customFunctionBindings = new HashMap<>(builder.customFunctionBindings);
315384
}
316385

317386
private static <T> ImmutableSet.Builder<T> deepCopy(ImmutableSet.Builder<T> builderToCopy) {

0 commit comments

Comments
 (0)