Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions runtime/src/main/java/dev/cel/runtime/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ java_library(
tags = [
],
deps = [
":base",
":evaluation_exception",
":evaluation_exception_builder",
":function_overload",
":resolved_overload",
":resolved_overload_internal",
"//:auto_value",
Expand All @@ -138,9 +138,9 @@ cel_android_library(
srcs = DISPATCHER_SOURCES,
visibility = ["//visibility:private"],
deps = [
":base_android",
":evaluation_exception",
":evaluation_exception_builder",
":function_overload_android",
":function_resolver_android",
":resolved_overload_android",
":resolved_overload_internal_android",
Expand Down Expand Up @@ -725,7 +725,6 @@ java_library(
],
deps = [
":function_overload",
"//common/annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
],
Expand All @@ -738,7 +737,6 @@ cel_android_library(
],
deps = [
":function_overload_android",
"//common/annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven_android//:com_google_guava_guava",
],
Expand Down
8 changes: 5 additions & 3 deletions runtime/src/main/java/dev/cel/runtime/CelFunctionBinding.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.Immutable;
import dev.cel.common.annotations.Internal;

/**
* Binding consisting of an overload id, a Java-native argument signature, and an overload
Expand All @@ -36,7 +35,7 @@
*
* <p>Examples: string_startsWith_string, mathMax_list, lessThan_money_money
*/
@Internal

@Immutable
public interface CelFunctionBinding {
String getOverloadId();
Expand All @@ -45,6 +44,8 @@ public interface CelFunctionBinding {

CelFunctionOverload getDefinition();

boolean isStrict();

/** Create a unary function binding from the {@code overloadId}, {@code arg}, and {@code impl}. */
@SuppressWarnings("unchecked")
static <T> CelFunctionBinding from(
Expand All @@ -66,6 +67,7 @@ static <T1, T2> CelFunctionBinding from(
/** Create a function binding from the {@code overloadId}, {@code argTypes}, and {@code impl}. */
static CelFunctionBinding from(
String overloadId, Iterable<Class<?>> argTypes, CelFunctionOverload impl) {
return new FunctionBindingImpl(overloadId, ImmutableList.copyOf(argTypes), impl);
return new FunctionBindingImpl(
overloadId, ImmutableList.copyOf(argTypes), impl, /* isStrict= */ true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ private static ResolvedOverload createResolvedOverload(CelFunctionBinding bindin
return CelResolvedOverload.of(
binding.getOverloadId(),
(args) -> binding.getDefinition().apply(args),
binding.isStrict(),
binding.getArgTypes());
}
}
29 changes: 25 additions & 4 deletions runtime/src/main/java/dev/cel/runtime/CelResolvedOverload.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ abstract class CelResolvedOverload implements ResolvedOverload {
@Override
public abstract ImmutableList<Class<?>> getParameterTypes();

/* Denotes whether an overload is strict.
*
* <p>A strict function will not be invoked if any of its arguments are an error or unknown value.
* The runtime automatically propagates the error or unknown instead.
*
* <p>A non-strict function will be invoked even if its arguments contain errors or unknowns. The
* function's implementation is then responsible for handling these values. This is primarily used
* for short-circuiting logical operators (e.g., `||`, `&&`) and comprehension's
* internal @not_strictly_false function.
*
* <p>In a vast majority of cases, this should be set to true.
*/
@Override
public abstract boolean isStrict();

/** The function definition. */
@Override
public abstract CelFunctionOverload getDefinition();
Expand All @@ -43,16 +58,22 @@ abstract class CelResolvedOverload implements ResolvedOverload {
* Creates a new resolved overload from the given overload id, parameter types, and definition.
*/
public static CelResolvedOverload of(
String overloadId, CelFunctionOverload definition, Class<?>... parameterTypes) {
return of(overloadId, definition, ImmutableList.copyOf(parameterTypes));
String overloadId,
CelFunctionOverload definition,
boolean isStrict,
Class<?>... parameterTypes) {
return of(overloadId, definition, isStrict, ImmutableList.copyOf(parameterTypes));
}

/**
* Creates a new resolved overload from the given overload id, parameter types, and definition.
*/
public static CelResolvedOverload of(
String overloadId, CelFunctionOverload definition, List<Class<?>> parameterTypes) {
String overloadId,
CelFunctionOverload definition,
boolean isStrict,
List<Class<?>> parameterTypes) {
return new AutoValue_CelResolvedOverload(
overloadId, ImmutableList.copyOf(parameterTypes), definition);
overloadId, ImmutableList.copyOf(parameterTypes), isStrict, definition);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ public CelRuntimeLegacyImpl build() {
.forEach(
(String overloadId, CelFunctionBinding func) ->
dispatcher.add(
overloadId, func.getArgTypes(), (args) -> func.getDefinition().apply(args)));
overloadId, func.getArgTypes(), func.isStrict(), func.getDefinition()));

RuntimeTypeProvider runtimeTypeProvider;

Expand Down
29 changes: 3 additions & 26 deletions runtime/src/main/java/dev/cel/runtime/DefaultDispatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,40 +32,17 @@
* <p>Should be final, do not mock; mocking {@link Dispatcher} instead.
*/
@ThreadSafe
final class DefaultDispatcher implements Dispatcher, Registrar {
final class DefaultDispatcher implements Dispatcher {
public static DefaultDispatcher create() {
return new DefaultDispatcher();
}

@GuardedBy("this")
private final Map<String, ResolvedOverload> overloads = new HashMap<>();

@Override
@SuppressWarnings("unchecked")
public synchronized <T> void add(
String overloadId, Class<T> argType, final Registrar.UnaryFunction<T> function) {
overloads.put(
overloadId,
CelResolvedOverload.of(overloadId, args -> function.apply((T) args[0]), argType));
}

@Override
@SuppressWarnings("unchecked")
public synchronized <T1, T2> void add(
String overloadId,
Class<T1> argType1,
Class<T2> argType2,
final Registrar.BinaryFunction<T1, T2> function) {
overloads.put(
overloadId,
CelResolvedOverload.of(
overloadId, args -> function.apply((T1) args[0], (T2) args[1]), argType1, argType2));
}

@Override
public synchronized void add(
String overloadId, List<Class<?>> argTypes, Registrar.Function function) {
overloads.put(overloadId, CelResolvedOverload.of(overloadId, function, argTypes));
String overloadId, List<Class<?>> argTypes, boolean isStrict, CelFunctionOverload overload) {
overloads.put(overloadId, CelResolvedOverload.of(overloadId, overload, isStrict, argTypes));
}

@Override
Expand Down
13 changes: 12 additions & 1 deletion runtime/src/main/java/dev/cel/runtime/FunctionBindingImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ final class FunctionBindingImpl implements CelFunctionBinding {

private final CelFunctionOverload definition;

private final boolean isStrict;

@Override
public String getOverloadId() {
return overloadId;
Expand All @@ -41,10 +43,19 @@ public CelFunctionOverload getDefinition() {
return definition;
}

@Override
public boolean isStrict() {
return isStrict;
}

FunctionBindingImpl(
String overloadId, ImmutableList<Class<?>> argTypes, CelFunctionOverload definition) {
String overloadId,
ImmutableList<Class<?>> argTypes,
CelFunctionOverload definition,
boolean isStrict) {
this.overloadId = overloadId;
this.argTypes = argTypes;
this.definition = definition;
this.isStrict = isStrict;
}
}
5 changes: 4 additions & 1 deletion runtime/src/main/java/dev/cel/runtime/LiteRuntimeImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,10 @@ public CelLiteRuntime build() {
.forEach(
(String overloadId, CelFunctionBinding func) ->
dispatcher.add(
overloadId, func.getArgTypes(), (args) -> func.getDefinition().apply(args)));
overloadId,
func.getArgTypes(),
func.isStrict(),
(args) -> func.getDefinition().apply(args)));

Interpreter interpreter =
new DefaultInterpreter(
Expand Down
6 changes: 3 additions & 3 deletions runtime/src/main/java/dev/cel/runtime/Registrar.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
package dev.cel.runtime;

import com.google.errorprone.annotations.Immutable;
import dev.cel.common.annotations.Internal;
import java.util.List;

/**
* An object which registers the functions that a {@link Dispatcher} calls.
*
* <p>CEL Library Internals. Do Not Use.
* @deprecated Do not use. This interface exists solely for legacy async stack compatibility
* reasons.
*/
@Internal
@Deprecated
public interface Registrar {

/** Interface describing the general signature of all CEL custom function implementations. */
Expand Down
15 changes: 15 additions & 0 deletions runtime/src/main/java/dev/cel/runtime/ResolvedOverload.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ interface ResolvedOverload {
/** The function definition. */
CelFunctionOverload getDefinition();

/**
* Denotes whether an overload is strict.
*
* <p>A strict function will not be invoked if any of its arguments are an error or unknown value.
* The runtime automatically propagates the error or unknown instead.
*
* <p>A non-strict function will be invoked even if its arguments contain errors or unknowns. The
* function's implementation is then responsible for handling these values. This is primarily used
* for short-circuiting logical operators (e.g., `||`, `&&`) and comprehension's
* internal @not_strictly_false function.
*
* <p>In a vast majority of cases, a function should be kept strict.
*/
boolean isStrict();

/**
* Returns true if the overload's expected argument types match the types of the given arguments.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ CelResolvedOverload getIncrementIntOverload() {
Long arg = (Long) args[0];
return arg + 1;
},
/* isStrict= */ true,
Long.class);
}

Expand All @@ -43,14 +44,15 @@ public void canHandle_matchingTypes_returnsTrue() {
@Test
public void canHandle_nullMessageType_returnsTrue() {
CelResolvedOverload overload =
CelResolvedOverload.of("identity", (args) -> args[0], TestAllTypes.class);
CelResolvedOverload.of(
"identity", (args) -> args[0], /* isStrict= */ true, TestAllTypes.class);
assertThat(overload.canHandle(new Object[] {null})).isTrue();
}

@Test
public void canHandle_nullPrimitive_returnsFalse() {
CelResolvedOverload overload =
CelResolvedOverload.of("identity", (args) -> args[0], Long.class);
CelResolvedOverload.of("identity", (args) -> args[0], /* isStrict= */ true, Long.class);
assertThat(overload.canHandle(new Object[] {null})).isFalse();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ public final class DefaultDispatcherTest {
public void setup() {
overloads = new HashMap<>();
overloads.put(
"overload_1", CelResolvedOverload.of("overload_1", args -> (Long) args[0] + 1, Long.class));
"overload_1",
CelResolvedOverload.of(
"overload_1", args -> (Long) args[0] + 1, /* isStrict= */ true, Long.class));
overloads.put(
"overload_2", CelResolvedOverload.of("overload_2", args -> (Long) args[0] + 2, Long.class));
"overload_2",
CelResolvedOverload.of(
"overload_2", args -> (Long) args[0] + 2, /* isStrict= */ true, Long.class));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

import com.google.common.collect.ImmutableList;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelFunctionDecl;
Expand Down Expand Up @@ -74,7 +75,11 @@ public Object adapt(String messageName, Object message) {
};
CelAbstractSyntaxTree ast = celCompiler.compile("[1].all(x, [2].all(y, error()))").getAst();
DefaultDispatcher dispatcher = DefaultDispatcher.create();
dispatcher.add("error", long.class, (args) -> new IllegalArgumentException("Always throws"));
dispatcher.add(
"error",
ImmutableList.of(long.class),
/* isStrict= */ true,
(args) -> new IllegalArgumentException("Always throws"));
DefaultInterpreter defaultInterpreter =
new DefaultInterpreter(new TypeResolver(), emptyProvider, dispatcher, CelOptions.DEFAULT);
DefaultInterpretable interpretable =
Expand Down
Loading