Skip to content

Commit 5344bb0

Browse files
l46kokcopybara-github
authored andcommitted
Implement Native Extensions
Future iterations may introduce an annotation based field mapping (ex: `@CelName('foo')`. PiperOrigin-RevId: 904571869
1 parent f566450 commit 5344bb0

12 files changed

Lines changed: 2217 additions & 11 deletions

File tree

common/internal/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,8 @@ cel_android_library(
147147
name = "date_time_helpers_android",
148148
exports = ["//common/src/main/java/dev/cel/common/internal:date_time_helpers_android"],
149149
)
150+
151+
java_library(
152+
name = "reflection_util",
153+
exports = ["//common/src/main/java/dev/cel/common/internal:reflection_util"],
154+
)

common/src/main/java/dev/cel/common/internal/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,9 @@ java_library(
398398
java_library(
399399
name = "reflection_util",
400400
srcs = ["ReflectionUtil.java"],
401+
tags = [
402+
"alt_dep=//common/internal:reflection_util",
403+
],
401404
deps = [
402405
"//common/annotations",
403406
],

common/src/main/java/dev/cel/common/internal/ReflectionUtil.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
import dev.cel.common.annotations.Internal;
1818
import java.lang.reflect.InvocationTargetException;
1919
import java.lang.reflect.Method;
20+
import java.lang.reflect.ParameterizedType;
21+
import java.lang.reflect.Type;
22+
import java.lang.reflect.WildcardType;
23+
import java.util.List;
24+
import java.util.Map;
25+
import java.util.Optional;
2026

2127
/**
2228
* Utility class for invoking Java reflection.
@@ -48,5 +54,68 @@ public static Object invoke(Method method, Object object, Object... params) {
4854
}
4955
}
5056

57+
/**
58+
* Extracts the element type of a container type (List, Map, Optional, or Array). Returns the type
59+
* itself if it's not a container or if generic type info is missing.
60+
*/
61+
public static Class<?> getElementType(Class<?> type, Type genericType) {
62+
if (type.isArray()) {
63+
return type.getComponentType();
64+
}
65+
66+
if (genericType instanceof ParameterizedType) {
67+
Type[] args = ((ParameterizedType) genericType).getActualTypeArguments();
68+
int argIndex = -1;
69+
70+
if (type == Optional.class || List.class.isAssignableFrom(type)) {
71+
argIndex = 0;
72+
} else if (Map.class.isAssignableFrom(type)) {
73+
argIndex = 1;
74+
}
75+
76+
if (argIndex >= 0 && args.length > argIndex && args[argIndex] instanceof Class<?>) {
77+
return (Class<?>) args[argIndex];
78+
}
79+
}
80+
81+
return type;
82+
}
83+
84+
/**
85+
* Extracts the raw Class from a Type. Handles Class, ParameterizedType, and WildcardType (returns
86+
* upper bound). Returns Object.class as fallback.
87+
*/
88+
public static Class<?> getRawType(Type type) {
89+
if (type instanceof Class<?>) {
90+
return (Class<?>) type;
91+
}
92+
if (type instanceof ParameterizedType) {
93+
return (Class<?>) ((ParameterizedType) type).getRawType();
94+
}
95+
if (type instanceof WildcardType) {
96+
WildcardType wt = (WildcardType) type;
97+
Type[] upperBounds = wt.getUpperBounds();
98+
if (upperBounds.length > 0) {
99+
return getRawType(upperBounds[0]);
100+
}
101+
}
102+
return Object.class;
103+
}
104+
105+
/**
106+
* Extracts the actual type arguments from a ParameterizedType, if it has at least the expected
107+
* minimum number of arguments. Returns Optional.empty() if the type is not parameterized or has
108+
* fewer arguments than expected.
109+
*/
110+
public static Optional<Type[]> getTypeArguments(Type type, int minArgs) {
111+
if (type instanceof ParameterizedType) {
112+
Type[] args = ((ParameterizedType) type).getActualTypeArguments();
113+
if (args.length >= minArgs) {
114+
return Optional.of(args);
115+
}
116+
}
117+
return Optional.empty();
118+
}
119+
51120
private ReflectionUtil() {}
52121
}

extensions/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,8 @@ java_library(
5656
name = "comprehensions",
5757
exports = ["//extensions/src/main/java/dev/cel/extensions:comprehensions"],
5858
)
59+
60+
java_library(
61+
name = "native",
62+
exports = ["//extensions/src/main/java/dev/cel/extensions:native"],
63+
)

extensions/src/main/java/dev/cel/extensions/BUILD.bazel

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ java_library(
3434
":encoders",
3535
":lists",
3636
":math",
37+
":native",
3738
":optional_library",
3839
":protos",
3940
":regex",
@@ -318,3 +319,25 @@ java_library(
318319
"@maven//:com_google_guava_guava",
319320
],
320321
)
322+
323+
java_library(
324+
name = "native",
325+
srcs = ["CelNativeTypesExtensions.java"],
326+
tags = [
327+
],
328+
deps = [
329+
"//checker:checker_builder",
330+
"//common/exceptions:attribute_not_found",
331+
"//common/internal:reflection_util",
332+
"//common/types",
333+
"//common/types:type_providers",
334+
"//common/values",
335+
"//common/values:cel_byte_string",
336+
"//common/values:cel_value",
337+
"//common/values:cel_value_provider",
338+
"//compiler:compiler_builder",
339+
"//runtime",
340+
"@maven//:com_google_errorprone_error_prone_annotations",
341+
"@maven//:com_google_guava_guava",
342+
],
343+
)

extensions/src/main/java/dev/cel/extensions/CelExtensions.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
package dev.cel.extensions;
1616

1717
import static com.google.common.collect.ImmutableSet.toImmutableSet;
18-
import static java.util.Arrays.stream;
1918

2019
import com.google.common.collect.ImmutableSet;
2120
import com.google.common.collect.Streams;
2221
import com.google.errorprone.annotations.InlineMe;
2322
import dev.cel.common.CelOptions;
2423
import dev.cel.extensions.CelMathExtensions.Function;
24+
import java.util.EnumSet;
2525
import java.util.Set;
2626

2727
/**
@@ -350,6 +350,18 @@ public static CelComprehensionsExtensions comprehensions() {
350350
return COMPREHENSIONS_EXTENSIONS;
351351
}
352352

353+
/**
354+
* Extensions for supporting native Java types (POJOs) in CEL.
355+
*
356+
* <p>Refer to README.md for details on property discovery, type mapping, and limitations.
357+
*
358+
* <p>Note: Passing classes with unsupported types or anonymous/local classes will result in an
359+
* {@link IllegalArgumentException} when the runtime is built.
360+
*/
361+
public static CelNativeTypesExtensions nativeTypes(Class<?>... classes) {
362+
return CelNativeTypesExtensions.nativeTypes(classes);
363+
}
364+
353365
/**
354366
* Retrieves all function names used by every extension libraries.
355367
*
@@ -359,18 +371,17 @@ public static CelComprehensionsExtensions comprehensions() {
359371
*/
360372
public static ImmutableSet<String> getAllFunctionNames() {
361373
return Streams.concat(
362-
stream(CelMathExtensions.Function.values())
363-
.map(CelMathExtensions.Function::getFunction),
364-
stream(CelStringExtensions.Function.values())
374+
EnumSet.allOf(Function.class).stream().map(CelMathExtensions.Function::getFunction),
375+
EnumSet.allOf(CelStringExtensions.Function.class).stream()
365376
.map(CelStringExtensions.Function::getFunction),
366-
stream(SetsFunction.values()).map(SetsFunction::getFunction),
367-
stream(CelEncoderExtensions.Function.values())
377+
EnumSet.allOf(SetsFunction.class).stream().map(SetsFunction::getFunction),
378+
EnumSet.allOf(CelEncoderExtensions.Function.class).stream()
368379
.map(CelEncoderExtensions.Function::getFunction),
369-
stream(CelListsExtensions.Function.values())
380+
EnumSet.allOf(CelListsExtensions.Function.class).stream()
370381
.map(CelListsExtensions.Function::getFunction),
371-
stream(CelRegexExtensions.Function.values())
382+
EnumSet.allOf(CelRegexExtensions.Function.class).stream()
372383
.map(CelRegexExtensions.Function::getFunction),
373-
stream(CelComprehensionsExtensions.Function.values())
384+
EnumSet.allOf(CelComprehensionsExtensions.Function.class).stream()
374385
.map(CelComprehensionsExtensions.Function::getFunction))
375386
.collect(toImmutableSet());
376387
}

0 commit comments

Comments
 (0)