4343import dev .cel .common .types .CelTypes ;
4444import dev .cel .common .values .CelValueProvider ;
4545import dev .cel .common .values .ProtoMessageValueProvider ;
46+ import dev .cel .runtime .CelStandardFunctions .StandardFunction .Overload .Comparison ;
47+ import dev .cel .runtime .CelStandardFunctions .StandardFunction .Overload .Conversions ;
4648import java .util .Arrays ;
4749import java .util .HashMap ;
4850import 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