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
40 changes: 40 additions & 0 deletions checker/src/test/java/dev/cel/checker/CelOverloadDeclTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@

import dev.cel.expr.Decl.FunctionDecl.Overload;
import com.google.common.collect.ImmutableList;
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOverloadDecl;
import dev.cel.common.CelProtoDeclConverter;
import dev.cel.common.types.CelProtoTypes;
import dev.cel.common.types.ListType;
import dev.cel.common.types.SimpleType;
import dev.cel.common.types.TypeParamType;
import org.junit.Test;
Expand Down Expand Up @@ -105,4 +107,42 @@ public void setParameterTypes_doesNotDedupe() {
.containsExactly(SimpleType.STRING, SimpleType.STRING, SimpleType.STRING, SimpleType.INT)
.inOrder();
}

@Test
public void newFunctionDeclaration_populatesEmptyOverloadIds() {
CelOverloadDecl globalOverload =
newGlobalOverload(SimpleType.INT, SimpleType.INT, SimpleType.INT);
CelOverloadDecl globalListOverload =
newGlobalOverload(SimpleType.STRING, ImmutableList.of(SimpleType.INT));
CelOverloadDecl memberOverload =
newMemberOverload(
SimpleType.BOOL, ListType.create(TypeParamType.create("T")), TypeParamType.create("T"));
CelOverloadDecl memberListOverload =
newMemberOverload(SimpleType.INT, ImmutableList.of(SimpleType.STRING));
CelOverloadDecl explicitOverload =
newGlobalOverload("custom_id", SimpleType.STRING, SimpleType.STRING);

assertThat(globalOverload.overloadId()).isEmpty();
assertThat(globalListOverload.overloadId()).isEmpty();
assertThat(memberOverload.overloadId()).isEmpty();
assertThat(memberListOverload.overloadId()).isEmpty();

CelFunctionDecl functionDecl =
CelFunctionDecl.newFunctionDeclaration(
"myFunc",
globalOverload,
globalListOverload,
memberOverload,
memberListOverload,
explicitOverload);

assertThat(functionDecl.overloads().stream().map(CelOverloadDecl::overloadId))
.containsExactly(
"myFunc(int,int)",
"myFunc(int)",
"list<~T>.myFunc(~T)",
"string.myFunc()",
"custom_id")
.inOrder();
}
}
12 changes: 12 additions & 0 deletions common/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ cel_android_library(
exports = ["//common/src/main/java/dev/cel/common:cel_overload_decl_android"],
)

java_library(
name = "cel_signature_utils",
visibility = ["//:internal"],
exports = ["//common/src/main/java/dev/cel/common:cel_signature_utils"],
)

cel_android_library(
name = "cel_signature_utils_android",
visibility = ["//:internal"],
exports = ["//common/src/main/java/dev/cel/common:cel_signature_utils_android"],
)

java_library(
name = "cel_proto_decl_converter",
exports = ["//common/src/main/java/dev/cel/common:cel_proto_decl_converter"],
Expand Down
28 changes: 28 additions & 0 deletions common/src/main/java/dev/cel/common/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ java_library(
],
deps = [
":cel_overload_decl",
":cel_signature_utils",
"//:auto_value",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
Expand All @@ -76,12 +77,39 @@ cel_android_library(
],
deps = [
":cel_overload_decl_android",
":cel_signature_utils_android",
"//:auto_value",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven_android//:com_google_guava_guava",
],
)

java_library(
name = "cel_signature_utils",
srcs = ["CelSignatureUtils.java"],
tags = [
],
deps = [
"//common/annotations",
"//common/types",
"//common/types:type_providers",
"@maven//:com_google_guava_guava",
],
)

cel_android_library(
name = "cel_signature_utils_android",
srcs = ["CelSignatureUtils.java"],
tags = [
],
deps = [
"//common/annotations",
"//common/types:type_providers_android",
"//common/types:types_android",
"@maven_android//:com_google_guava_guava",
],
)

java_library(
name = "cel_overload_decl",
srcs = ["CelOverloadDecl.java"],
Expand Down
48 changes: 46 additions & 2 deletions common/src/main/java/dev/cel/common/CelFunctionDecl.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package dev.cel.common;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.auto.value.AutoValue;
Expand All @@ -22,6 +23,8 @@
import com.google.errorprone.annotations.CheckReturnValue;
import com.google.errorprone.annotations.Immutable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/** Abstract representation of a CEL Function declaration. */
@AutoValue
Expand Down Expand Up @@ -70,9 +73,50 @@ public Builder addOverloads(Iterable<CelOverloadDecl> overloads) {
return this;
}

/** Builds a new instance of {@link CelFunctionDecl} */
@CheckReturnValue
public abstract CelFunctionDecl build();
abstract CelFunctionDecl autoBuild();

/**
* Builds a new instance of {@link CelFunctionDecl}.
*
* <p>Any {@link CelOverloadDecl} with an empty {@link CelOverloadDecl#overloadId()} will have
* its ID automatically populated with its canonical overload signature.
*/
@CheckReturnValue
public CelFunctionDecl build() {
ImmutableSet<CelOverloadDecl> currentOverloads = overloads();
boolean needsOverloadId = false;
for (CelOverloadDecl overload : currentOverloads) {
if (overload.overloadId().isEmpty()) {
needsOverloadId = true;
break;
}
}
if (!needsOverloadId) {
return autoBuild();
}
String functionName = name();
ImmutableSet.Builder<CelOverloadDecl> updatedOverloads =
ImmutableSet.builderWithExpectedSize(currentOverloads.size());
Set<String> seenOverloadIds = new HashSet<>();
for (CelOverloadDecl overload : currentOverloads) {
String overloadId = overload.overloadId();
if (overloadId.isEmpty()) {
overloadId =
CelSignatureUtils.makeOverloadSignature(
functionName, overload.parameterTypes(), overload.isInstanceFunction());
updatedOverloads.add(overload.toBuilder().setOverloadId(overloadId).build());
} else {
updatedOverloads.add(overload);
}
checkArgument(
seenOverloadIds.add(overloadId), "Duplicate overload signature: %s", overloadId);
}
return new AutoValue_CelFunctionDecl.Builder()
.setName(functionName)
.setOverloads(updatedOverloads.build())
.autoBuild();
}
}

public abstract Builder toBuilder();
Expand Down
79 changes: 77 additions & 2 deletions common/src/main/java/dev/cel/common/CelOverloadDecl.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@
@AutoValue
@Immutable
public abstract class CelOverloadDecl {
/** Required. Globally unique overload name. */
/**
* Globally unique overload name. If empty when building {@link CelFunctionDecl}, an overload ID
* is automatically generated from the function name and parameter types.
*/
public abstract String overloadId();

/**
Expand Down Expand Up @@ -79,7 +82,7 @@ public abstract class CelOverloadDecl {

/** Create a new builder to construct a {@link CelOverloadDecl} instance */
public static Builder newBuilder() {
return new AutoValue_CelOverloadDecl.Builder().setDoc("");
return new AutoValue_CelOverloadDecl.Builder().setOverloadId("").setDoc("");
}

/** Builder for configuring the {@link CelOverloadDecl}. */
Expand Down Expand Up @@ -153,6 +156,44 @@ public CelOverloadDecl build() {
}
}

/**
* Helper method for declaring a member function overload (e.g. {@code x.f(y, ...)}) with an
* automatically generated overload ID.
*
* <p>The returned declaration has an empty {@link #overloadId()}, which is automatically
* populated with the canonical signature (e.g. {@code "target.func(arg1,arg2)"}) when built into
* a {@link CelFunctionDecl} via {@link CelFunctionDecl.Builder#build()} or {@link
* CelFunctionDecl#newFunctionDeclaration}.
*
* @param resultType the return type of the overload
* @param paramTypes the parameter types of the overload, where the first element ({@code
* paramTypes[0]}) is the receiver (target) type {@code x} and any subsequent elements are the
* method argument types {@code y, ...}
*/
@CheckReturnValue
public static CelOverloadDecl newMemberOverload(CelType resultType, CelType... paramTypes) {
return newMemberOverload("", resultType, Arrays.asList(paramTypes));
}

/**
* Helper method for declaring a member function overload (e.g. {@code x.f(y, ...)}) with an
* automatically generated overload ID.
*
* <p>The returned declaration has an empty {@link #overloadId()}, which is automatically
* populated with the canonical signature (e.g. {@code "target.func(arg1,arg2)"}) when built into
* a {@link CelFunctionDecl} via {@link CelFunctionDecl.Builder#build()} or {@link
* CelFunctionDecl#newFunctionDeclaration}.
*
* @param resultType the return type of the overload
* @param paramTypes the parameter types of the overload, where the first element ({@code
* paramTypes.get(0)}) is the receiver (target) type {@code x} and any subsequent elements are
* the method argument types {@code y, ...}
*/
@CheckReturnValue
public static CelOverloadDecl newMemberOverload(CelType resultType, List<CelType> paramTypes) {
return newMemberOverload("", /* doc= */ "", resultType, paramTypes);
}

/** Helper method for declaring a member function overload */
@CheckReturnValue
public static CelOverloadDecl newMemberOverload(
Expand Down Expand Up @@ -181,6 +222,40 @@ public static CelOverloadDecl newMemberOverload(
return newOverload(overloadId, doc, resultType, paramTypes, /* isInstanceFunction= */ true);
}

/**
* Helper method for declaring a global function overload (e.g. {@code f(x, ...)}) with an
* automatically generated overload ID.
*
* <p>The returned declaration has an empty {@link #overloadId()}, which is automatically
* populated with the canonical signature (e.g. {@code "func(arg1,arg2)"}) when built into a
* {@link CelFunctionDecl} via {@link CelFunctionDecl.Builder#build()} or {@link
* CelFunctionDecl#newFunctionDeclaration}.
*
* @param resultType the return type of the overload
* @param paramTypes the argument types of the overload
*/
@CheckReturnValue
public static CelOverloadDecl newGlobalOverload(CelType resultType, CelType... paramTypes) {
return newGlobalOverload("", resultType, Arrays.asList(paramTypes));
}

/**
* Helper method for declaring a global function overload (e.g. {@code f(x, ...)}) with an
* automatically generated overload ID.
*
* <p>The returned declaration has an empty {@link #overloadId()}, which is automatically
* populated with the canonical signature (e.g. {@code "func(arg1,arg2)"}) when built into a
* {@link CelFunctionDecl} via {@link CelFunctionDecl.Builder#build()} or {@link
* CelFunctionDecl#newFunctionDeclaration}.
*
* @param resultType the return type of the overload
* @param paramTypes the argument types of the overload
*/
@CheckReturnValue
public static CelOverloadDecl newGlobalOverload(CelType resultType, List<CelType> paramTypes) {
return newGlobalOverload("", /* doc= */ "", resultType, paramTypes);
}

/** Helper method for declaring a global function overload */
@CheckReturnValue
public static CelOverloadDecl newGlobalOverload(
Expand Down
Loading
Loading