From 49851f5bed92d2a4c2a2c1a0210b222d20cb2b20 Mon Sep 17 00:00:00 2001 From: Sokwhan Huh Date: Fri, 5 Jun 2026 12:00:40 -0700 Subject: [PATCH] Safely ignore enums in native type extensions Ref: https://github.com/google/cel-java/issues/1077 PiperOrigin-RevId: 927414521 --- .../extensions/CelNativeTypesExtensions.java | 8 ++++ .../main/java/dev/cel/extensions/README.md | 1 + .../CelNativeTypesExtensionsTest.java | 47 +++++++++++++++++-- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/extensions/src/main/java/dev/cel/extensions/CelNativeTypesExtensions.java b/extensions/src/main/java/dev/cel/extensions/CelNativeTypesExtensions.java index ae9483f7c..44fcdd1f6 100644 --- a/extensions/src/main/java/dev/cel/extensions/CelNativeTypesExtensions.java +++ b/extensions/src/main/java/dev/cel/extensions/CelNativeTypesExtensions.java @@ -82,6 +82,10 @@ public final class CelNativeTypesExtensions implements CelCompilerLibrary, CelRu private static final ImmutableSet OBJECT_METHOD_NAMES = stream(Object.class.getDeclaredMethods()).map(Method::getName).collect(toImmutableSet()); + // Set of all standard java.lang.Enum method names. + private static final ImmutableSet ENUM_METHOD_NAMES = + stream(Enum.class.getDeclaredMethods()).map(Method::getName).collect(toImmutableSet()); + private static final ImmutableMap, CelType> JAVA_TO_CEL_TYPE_MAP = ImmutableMap., CelType>builder() .put(boolean.class, SimpleType.BOOL) @@ -606,6 +610,10 @@ private static boolean isGetter(Method method) { if (OBJECT_METHOD_NAMES.contains(name)) { return false; } + if (Enum.class.isAssignableFrom(method.getDeclaringClass()) + && ENUM_METHOD_NAMES.contains(name)) { + return false; + } if (name.startsWith("get")) { return name.length() > 3; } diff --git a/extensions/src/main/java/dev/cel/extensions/README.md b/extensions/src/main/java/dev/cel/extensions/README.md index fcf019d15..5b75bb48d 100644 --- a/extensions/src/main/java/dev/cel/extensions/README.md +++ b/extensions/src/main/java/dev/cel/extensions/README.md @@ -1122,6 +1122,7 @@ The type-mapping between Java and CEL is as follows: * This is only supported for the planner runtime (e.g., `CelRuntimeFactory.plannerRuntimeBuilder()`). * Native Java arrays (except `byte[]`) are not supported. Use `java.util.List` instead. +* Java `enum` properties are not currently supported and will be safely ignored during scanning. * If there is a name collision with a Protobuf type, the protobuf type will take precedence. * Instantiating new struct values (e.g., `Account{id: 1234}`) requires the class to have a no-argument constructor (public, protected, package-private, or private). * Final fields are supported only in a **read-only** capacity; they cannot be populated when instantiating new struct values. diff --git a/extensions/src/test/java/dev/cel/extensions/CelNativeTypesExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelNativeTypesExtensionsTest.java index dcd3e811c..5485989af 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelNativeTypesExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelNativeTypesExtensionsTest.java @@ -89,7 +89,8 @@ public final class CelNativeTypesExtensionsTest { TestNestedSimplePojo.class, TestGetterFieldTypeMismatchPojo.class, TestAbstractPojo.class, - TestURLPojo.class); + TestURLPojo.class, + PojoWithEnum.class); private static final Cel CEL = CelFactory.plannerCelBuilder() @@ -564,16 +565,24 @@ public void nativeTypes_prefixLessGetter_success() throws Exception { .setContainer(CelContainer.ofName("dev.cel.extensions.CelNativeTypesExtensionsTest")) .addLibraries(extensions) .build(); - CelAbstractSyntaxTree ast = + CelAbstractSyntaxTree valueAst = celCompiler .compile( "dev.cel.extensions.CelNativeTypesExtensionsTest.TestPrefixLessGetterPojo{}.value") .getAst(); - CelRuntime.Program program = celRuntime.createProgram(ast); + CelAbstractSyntaxTree nameAst = + celCompiler + .compile( + "dev.cel.extensions.CelNativeTypesExtensionsTest.TestPrefixLessGetterPojo{}.name") + .getAst(); + CelRuntime.Program valueProgram = celRuntime.createProgram(valueAst); + CelRuntime.Program nameProgram = celRuntime.createProgram(nameAst); - Object result = program.eval(); + Object valueResult = valueProgram.eval(); + Object nameResult = nameProgram.eval(); - assertThat(result).isEqualTo("hello"); + assertThat(valueResult).isEqualTo("hello"); + assertThat(nameResult).isEqualTo("my_name"); } @Test @@ -1201,10 +1210,15 @@ public static final class TestPrivateFieldPojo { public static class TestPrefixLessGetterPojo { private String value = "hello"; + private String name = "my_name"; public String value() { return value; } + + public String name() { + return name; + } } public static class TestParentPojo { @@ -1346,4 +1360,27 @@ public String getMismatchField() { return "mismatch"; } } + + public enum TestEnum { + FOO, + BAR; + } + + public static class PojoWithEnum { + private TestEnum enumVal = TestEnum.FOO; + + public TestEnum getEnumVal() { + return enumVal; + } + + public void setEnumVal(TestEnum val) { + this.enumVal = val; + } + } + + @Test + public void nativeTypes_enumSafelyIgnored() throws Exception { + assertThat(eval("PojoWithEnum{}.enumVal")).isNotNull(); + } + }