Skip to content
Open
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
10 changes: 10 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package(
CEL_SOURCES = [
"Cel.java",
"CelBuilder.java",
"CelFunction.java",
]

java_library(
Expand All @@ -19,9 +20,12 @@ java_library(
tags = [
],
deps = [
"//:auto_value",
"//checker:checker_legacy_environment",
"//checker:proto_type_mask",
"//checker:standard_decl",
"//common:cel_function_decl",
"//common:cel_overload_decl",
"//common:compiler_common",
"//common:container",
"//common:options",
Expand All @@ -35,6 +39,7 @@ java_library(
"@cel_spec//proto/cel/expr:checked_java_proto",
"@maven//:com_google_code_findbugs_annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
"@maven//:com_google_protobuf_protobuf_java",
],
)
Expand Down Expand Up @@ -110,12 +115,14 @@ java_library(
"//checker:standard_decl",
"//common:cel_function_decl",
"//common:cel_overload_decl",
"//common:cel_signature_utils",
"//common:cel_var_decl",
"//common:container",
"//common:options",
"//common:source",
"//common/formats:parser_context",
"//common/types",
"//common/types:cel_types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
Expand Down Expand Up @@ -147,13 +154,16 @@ java_library(
deps = [
":environment",
":environment_exception",
"//common:cel_signature_utils",
"//common:compiler_common",
"//common:container",
"//common/formats:file_source",
"//common/formats:parser_context",
"//common/formats:value_string",
"//common/formats:yaml_helper",
"//common/formats:yaml_parser_context_impl",
"//common/internal",
"//common/types:type_providers",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
"@maven//:org_jspecify_jspecify",
Expand Down
14 changes: 14 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/CelBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,20 @@ public interface CelBuilder {
@CanIgnoreReturnValue
CelBuilder addFunctionDeclarations(Iterable<CelFunctionDecl> celFunctionDecls);

/**
* Add one or more {@link CelFunction} definitions (combining compile-time declarations and
* runtime bindings) to the CEL environment.
*/
@CanIgnoreReturnValue
CelBuilder addFunctions(CelFunction... functions);

/**
* Add a collection of {@link CelFunction} definitions (combining compile-time declarations and
* runtime bindings) to the CEL environment.
*/
@CanIgnoreReturnValue
CelBuilder addFunctions(Iterable<CelFunction> functions);

/** Add variable declaration {@code CelVarDecl} to the CEL environment. */
@CanIgnoreReturnValue
CelBuilder addVarDeclarations(CelVarDecl... celVarDecls);
Expand Down
161 changes: 136 additions & 25 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,17 @@
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOptions;
import dev.cel.common.CelOverloadDecl;
import dev.cel.common.CelSignatureUtils;
import dev.cel.common.CelVarDecl;
import dev.cel.common.Source;
import dev.cel.common.types.CelKind;
import dev.cel.common.types.CelType;
import dev.cel.common.types.CelTypeProvider;
import dev.cel.common.types.CelTypes;
import dev.cel.common.types.ListType;
import dev.cel.common.types.MapType;
import dev.cel.common.types.NullableType;
import dev.cel.common.types.OpaqueType;
import dev.cel.common.types.OptionalType;
import dev.cel.common.types.SimpleType;
import dev.cel.common.types.TypeParamType;
Expand All @@ -55,6 +60,7 @@
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.ObjIntConsumer;

/**
Expand Down Expand Up @@ -545,8 +551,12 @@ public CelFunctionDecl toCelFunctionDecl(CelTypeProvider celTypeProvider) {
public abstract static class OverloadDecl {

/**
* A unique overload ID. Required. This should follow the typical naming convention used in CEL
* (e.g: targetType_func_argType1_argType...)
* A unique overload ID. Required.
*
* <p>This can follow the typical naming convention used in CEL (e.g. {@code
* targetType_func_argType1_argType...}) or use a canonical overload signature (e.g. {@code
* targetType.func(argType1,argType2)}). When parsed from YAML with a {@code signature} field
* and no explicit {@code id}, this defaults to the {@code signature} string.
*/
public abstract String id();

Expand Down Expand Up @@ -709,59 +719,133 @@ static TypeDecl ofTypeParam(String typeParamName) {
return newBuilder().setName(typeParamName).setIsTypeParam(true).build();
}

/** Converts a {@link CelType} into a {@link TypeDecl}. */
static TypeDecl fromCelType(CelType type) {
if (type.kind() == CelKind.ERROR
|| type.kind() == CelKind.UNSPECIFIED
|| (type.kind() == CelKind.OPAQUE && type.name().equals("function"))) {
throw new IllegalArgumentException("Unsupported type: " + type.name());
}
if (type instanceof NullableType) {
switch (((NullableType) type).targetType().kind()) {
case BOOL:
return TypeDecl.create(CelTypes.BOOL_WRAPPER_MESSAGE);
case BYTES:
return TypeDecl.create(CelTypes.BYTES_WRAPPER_MESSAGE);
case DOUBLE:
return TypeDecl.create(CelTypes.DOUBLE_WRAPPER_MESSAGE);
case INT:
return TypeDecl.create(CelTypes.INT64_WRAPPER_MESSAGE);
case STRING:
return TypeDecl.create(CelTypes.STRING_WRAPPER_MESSAGE);
case UINT:
return TypeDecl.create(CelTypes.UINT64_WRAPPER_MESSAGE);
default:
throw new IllegalArgumentException("Unsupported wrapper type: " + type.name());
}
}
return TypeDecl.newBuilder()
.setName(type.name())
.setIsTypeParam(type.kind() == CelKind.TYPE_PARAM)
.addParams(
type.parameters().stream().map(TypeDecl::fromCelType).collect(toImmutableList()))
.build();
}

public static TypeDecl.Builder newBuilder() {
return new AutoValue_CelEnvironment_TypeDecl.Builder().setIsTypeParam(false);
}

/** Converts this type declaration into a {@link CelType}. */
public CelType toCelType(CelTypeProvider celTypeProvider) {
return toCelType(celTypeProvider::findType);
}

CelType toCelType() {
return toCelType(typeName -> Optional.of(OpaqueType.create(typeName)));
}

private CelType toCelType(Function<String, Optional<CelType>> typeFinder) {
if (isTypeParam()) {
if (!params().isEmpty()) {
throw new IllegalArgumentException(
"Type param has unexpected param count: " + params().size());
}
return TypeParamType.create(name());
}
switch (name()) {
case "*error*":
throw new IllegalArgumentException("Unsupported type name: " + name());
case "list":
if (params().size() != 1) {
throw new IllegalArgumentException(
"List type has unexpected param count: " + params().size());
}

CelType elementType = params().get(0).toCelType(celTypeProvider);
CelType elementType = params().get(0).toCelType(typeFinder);
return ListType.create(elementType);
case "map":
if (params().size() != 2) {
throw new IllegalArgumentException(
"Map type has unexpected param count: " + params().size());
}

CelType keyType = params().get(0).toCelType(celTypeProvider);
CelType valueType = params().get(1).toCelType(celTypeProvider);
CelType keyType = params().get(0).toCelType(typeFinder);
CelType valueType = params().get(1).toCelType(typeFinder);
return MapType.create(keyType, valueType);
case "type":
checkState(
params().size() == 1, "Expected 1 parameter for type, got %s", params().size());
return TypeType.create(params().get(0).toCelType(celTypeProvider));
return TypeType.create(params().get(0).toCelType(typeFinder));
case "dyn":
return SimpleType.DYN;
case "null":
return SimpleType.NULL_TYPE;
case "any":
return SimpleType.ANY;
case "timestamp":
return SimpleType.TIMESTAMP;
case "duration":
return SimpleType.DURATION;
case "bool_wrapper":
return NullableType.create(SimpleType.BOOL);
case "bytes_wrapper":
return NullableType.create(SimpleType.BYTES);
case "double_wrapper":
return NullableType.create(SimpleType.DOUBLE);
case "int_wrapper":
return NullableType.create(SimpleType.INT);
case "string_wrapper":
return NullableType.create(SimpleType.STRING);
case "uint_wrapper":
return NullableType.create(SimpleType.UINT);
case OptionalType.NAME:
checkState(
params().size() == 1,
"Optional type must have exactly 1 parameter. Found %s",
params().size());
return OptionalType.create(params().get(0).toCelType(typeFinder));
default:
if (isTypeParam()) {
return TypeParamType.create(name());
}

if (name().equals("dyn")) {
return SimpleType.DYN;
}

CelType simpleType = SimpleType.findByName(name()).orElse(null);
if (simpleType != null) {
return simpleType;
}

if (name().equals(OptionalType.NAME)) {
checkState(
params().size() == 1,
"Optional type must have exactly 1 parameter. Found %s",
params().size());
return OptionalType.create(params().get(0).toCelType(celTypeProvider));
CelType wellKnownType = CelTypes.getWellKnownCelType(name()).orElse(null);
if (wellKnownType != null) {
return wellKnownType;
}

return celTypeProvider
.findType(name())
.orElseThrow(() -> new IllegalArgumentException("Undefined type name: " + name()));
CelType resolvedType =
typeFinder
.apply(name())
.orElseThrow(
() -> new IllegalArgumentException("Undefined type name: " + name()));
if (!params().isEmpty()) {
return resolvedType.withParameters(
params().stream().map(p -> p.toCelType(typeFinder)).collect(toImmutableList()));
}
return resolvedType;
}
}
}
Expand Down Expand Up @@ -1057,6 +1141,15 @@ public static FunctionSelector.Builder newBuilder() {
.setOverloads(ImmutableSet.of());
}

/**
* Creates a new {@link FunctionSelector} matching the given function {@code name} and {@code
* overloads}.
*
* @param name the function name to match
* @param overloads the overload IDs or canonical overload signatures (e.g. {@code
* "add_bytes"} or {@code "_+_(bytes,bytes)"}) to match, or empty to match all overloads
* of the function
*/
public static FunctionSelector create(String name, ImmutableSet<String> overloads) {
return newBuilder()
.setName(name)
Expand All @@ -1072,6 +1165,7 @@ private static boolean matchesAny(
StandardOverload overload,
ImmutableSet<FunctionSelector> selectors) {
String functionName = function.functionDecl().name();
String overloadSignature = null;
for (FunctionSelector functionSelector : selectors) {
if (!functionSelector.name().equals(functionName)) {
continue;
Expand All @@ -1081,12 +1175,26 @@ private static boolean matchesAny(
return true;
}

String overloadId = overload.celOverloadDecl().overloadId();
CelOverloadDecl celOverload = overload.celOverloadDecl();
String overloadId = celOverload.overloadId();
for (OverloadSelector overloadSelector : functionSelector.overloads()) {
if (overloadSelector.id().equals(overloadId)) {
return true;
}
}

if (overloadSignature == null) {
overloadSignature =
CelSignatureUtils.makeOverloadSignature(
functionName, celOverload.parameterTypes(), celOverload.isInstanceFunction());
}
if (!overloadSignature.equals(overloadId)) {
for (OverloadSelector overloadSelector : functionSelector.overloads()) {
if (overloadSelector.id().equals(overloadSignature)) {
return true;
}
}
}
}
return false;
}
Expand All @@ -1096,7 +1204,10 @@ private static boolean matchesAny(
@AutoValue
public abstract static class OverloadSelector {

/** An overload ID. Required. Follows the same format as {@link OverloadDecl#id()} */
/**
* An overload ID or canonical overload signature (e.g. {@code "add_bytes"} or {@code
* "_+_(bytes,bytes)"}). Required. Follows the same format as {@link OverloadDecl#id()}.
*/
public abstract String id();

/** Builder for {@link OverloadSelector}. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
import dev.cel.common.CelVarDecl;
import dev.cel.common.internal.EnvVisitable;
import dev.cel.common.internal.EnvVisitor;
import dev.cel.common.types.CelKind;
import dev.cel.common.types.CelType;
import dev.cel.compiler.CelCompiler;
import dev.cel.extensions.CelExtensionLibrary;
Expand Down Expand Up @@ -492,12 +491,7 @@ private CelEnvironment.OverloadDecl toCelEnvOverloadDecl(CelOverloadDecl overloa
}

private CelEnvironment.TypeDecl toCelEnvTypeDecl(CelType type) {
return CelEnvironment.TypeDecl.newBuilder()
.setName(type.name())
.setIsTypeParam(type.kind() == CelKind.TYPE_PARAM)
.addParams(
type.parameters().stream().map(this::toCelEnvTypeDecl).collect(toImmutableList()))
.build();
return CelEnvironment.TypeDecl.fromCelType(type);
}

/** Wrapper for CelOverloadDecl, associating it with the corresponding function name. */
Expand Down
Loading
Loading