Skip to content

Commit 8b12b47

Browse files
l46kokcopybara-github
authored andcommitted
Make OpaqueValue subclassable and resolve type in TypeResolver
PiperOrigin-RevId: 932674585
1 parent 6e267b4 commit 8b12b47

8 files changed

Lines changed: 218 additions & 7 deletions

File tree

bundle/src/main/java/dev/cel/bundle/CelImpl.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,9 @@ public Builder setTypeProvider(TypeProvider typeProvider) {
327327
@Override
328328
public CelBuilder setTypeProvider(CelTypeProvider celTypeProvider) {
329329
compilerBuilder.setTypeProvider(celTypeProvider);
330+
if (runtimeBuilder instanceof CelRuntimeImpl.Builder) {
331+
runtimeBuilder.setTypeProvider(celTypeProvider);
332+
}
330333
return this;
331334
}
332335

common/src/main/java/dev/cel/common/values/OpaqueValue.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
package dev.cel.common.values;
1616

1717
import com.google.auto.value.AutoValue;
18+
import com.google.errorprone.annotations.Immutable;
1819
import dev.cel.common.types.OpaqueType;
1920

2021
/** OpaqueValue is the value representation of OpaqueType. */
21-
@AutoValue
22-
@AutoValue.CopyAnnotations
22+
@Immutable
2323
@SuppressWarnings("Immutable") // Java Object is mutable.
2424
public abstract class OpaqueValue extends CelValue {
2525

@@ -32,6 +32,12 @@ public boolean isZeroValue() {
3232
public abstract OpaqueType celType();
3333

3434
public static OpaqueValue create(String name, Object value) {
35-
return new AutoValue_OpaqueValue(value, OpaqueType.create(name));
35+
return new AutoValue_OpaqueValue_AutoOpaqueValue(value, OpaqueType.create(name));
3636
}
37+
38+
@AutoValue
39+
@AutoValue.CopyAnnotations
40+
@Immutable
41+
@SuppressWarnings("Immutable") // Java Object is mutable.
42+
abstract static class AutoOpaqueValue extends OpaqueValue {}
3743
}

common/src/test/java/dev/cel/common/values/OpaqueValueTest.java

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,17 @@
1717
import static com.google.common.truth.Truth.assertThat;
1818
import static org.junit.Assert.assertThrows;
1919

20+
import com.google.common.collect.ImmutableList;
21+
import com.google.common.collect.ImmutableMap;
22+
import com.google.errorprone.annotations.Immutable;
23+
import dev.cel.bundle.Cel;
24+
import dev.cel.bundle.CelFactory;
25+
import dev.cel.common.CelAbstractSyntaxTree;
26+
import dev.cel.common.types.CelType;
27+
import dev.cel.common.types.CelTypeProvider;
2028
import dev.cel.common.types.OpaqueType;
29+
import java.util.Map;
30+
import java.util.Optional;
2131
import org.junit.Test;
2232
import org.junit.runner.RunWith;
2333
import org.junit.runners.JUnit4;
@@ -37,4 +47,144 @@ public void opaqueValue_construct() {
3747
public void create_nullValue_throws() {
3848
assertThrows(NullPointerException.class, () -> OpaqueValue.create("opaque_type_name", null));
3949
}
50+
51+
private static final OpaqueType CUSTOM_OPAQUE_TYPE = OpaqueType.create("custom_opaque");
52+
53+
private static final CelTypeProvider CUSTOM_OPAQUE_TYPE_PROVIDER =
54+
new CelTypeProvider() {
55+
@Override
56+
public ImmutableList<CelType> types() {
57+
return ImmutableList.of(CUSTOM_OPAQUE_TYPE);
58+
}
59+
60+
@Override
61+
public Optional<CelType> findType(String typeName) {
62+
return typeName.equals(CUSTOM_OPAQUE_TYPE.name())
63+
? Optional.of(CUSTOM_OPAQUE_TYPE)
64+
: Optional.empty();
65+
}
66+
};
67+
68+
private static final CelValueProvider CUSTOM_OPAQUE_VALUE_PROVIDER =
69+
new CelValueProvider() {
70+
@Override
71+
public Optional<Object> newValue(String structType, Map<String, Object> fields) {
72+
return Optional.empty();
73+
}
74+
75+
@Override
76+
public CelValueConverter celValueConverter() {
77+
return new CelValueConverter() {
78+
@Override
79+
public Object toRuntimeValue(Object value) {
80+
if (value instanceof CustomOpaqueObject) {
81+
return new CelCustomOpaqueValue((CustomOpaqueObject) value);
82+
}
83+
return super.toRuntimeValue(value);
84+
}
85+
};
86+
}
87+
};
88+
89+
private static final CelValueProvider CUSTOM_OPAQUE_VALUE_PROVIDER_USING_CREATE =
90+
new CelValueProvider() {
91+
@Override
92+
public Optional<Object> newValue(String structType, Map<String, Object> fields) {
93+
return Optional.empty();
94+
}
95+
96+
@Override
97+
public CelValueConverter celValueConverter() {
98+
return new CelValueConverter() {
99+
@Override
100+
public Object toRuntimeValue(Object value) {
101+
if (value instanceof CustomOpaqueObject) {
102+
return OpaqueValue.create(CUSTOM_OPAQUE_TYPE.name(), value);
103+
}
104+
return super.toRuntimeValue(value);
105+
}
106+
};
107+
}
108+
};
109+
110+
@Immutable
111+
private static class CustomOpaqueObject {
112+
private final String value;
113+
114+
CustomOpaqueObject(String value) {
115+
this.value = value;
116+
}
117+
118+
String getValue() {
119+
return value;
120+
}
121+
}
122+
123+
private static class CelCustomOpaqueValue extends OpaqueValue {
124+
private final CustomOpaqueObject obj;
125+
126+
CelCustomOpaqueValue(CustomOpaqueObject obj) {
127+
this.obj = obj;
128+
}
129+
130+
@Override
131+
public CustomOpaqueObject value() {
132+
return obj;
133+
}
134+
135+
@Override
136+
public OpaqueType celType() {
137+
return CUSTOM_OPAQUE_TYPE;
138+
}
139+
}
140+
141+
@Test
142+
public void evaluate_customOpaqueValue_asVariable() throws Exception {
143+
Cel cel =
144+
CelFactory.plannerCelBuilder()
145+
.addVar("a", CUSTOM_OPAQUE_TYPE)
146+
.setTypeProvider(CUSTOM_OPAQUE_TYPE_PROVIDER)
147+
.setValueProvider(CUSTOM_OPAQUE_VALUE_PROVIDER)
148+
.build();
149+
CelAbstractSyntaxTree ast = cel.compile("a").getAst();
150+
151+
CustomOpaqueObject rawValue = new CustomOpaqueObject("hello");
152+
Object result = cel.createProgram(ast).eval(ImmutableMap.of("a", rawValue));
153+
154+
assertThat(result).isInstanceOf(CustomOpaqueObject.class);
155+
assertThat(((CustomOpaqueObject) result).getValue()).isEqualTo("hello");
156+
}
157+
158+
@Test
159+
public void evaluate_typeOfCustomOpaqueValue() throws Exception {
160+
Cel cel =
161+
CelFactory.plannerCelBuilder()
162+
.addVar("a", CUSTOM_OPAQUE_TYPE)
163+
.setTypeProvider(CUSTOM_OPAQUE_TYPE_PROVIDER)
164+
.setValueProvider(CUSTOM_OPAQUE_VALUE_PROVIDER)
165+
.build();
166+
CelAbstractSyntaxTree ast = cel.compile("type(a) == custom_opaque").getAst();
167+
168+
CustomOpaqueObject rawValue = new CustomOpaqueObject("hello");
169+
Object result = cel.createProgram(ast).eval(ImmutableMap.of("a", rawValue));
170+
171+
assertThat(result).isEqualTo(true);
172+
}
173+
174+
@Test
175+
public void evaluate_typeOfCustomOpaqueValue_usingCreate() throws Exception {
176+
Cel cel =
177+
CelFactory.plannerCelBuilder()
178+
.addVar("a", CUSTOM_OPAQUE_TYPE)
179+
.setTypeProvider(CUSTOM_OPAQUE_TYPE_PROVIDER)
180+
.setValueProvider(CUSTOM_OPAQUE_VALUE_PROVIDER_USING_CREATE)
181+
.build();
182+
CelAbstractSyntaxTree ast = cel.compile("type(a) == custom_opaque").getAst();
183+
184+
CustomOpaqueObject rawValue = new CustomOpaqueObject("hello");
185+
Object result = cel.createProgram(ast).eval(ImmutableMap.of("a", rawValue));
186+
187+
assertThat(result).isEqualTo(true);
188+
}
40189
}
190+

common/src/test/java/dev/cel/common/values/StructValueTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,22 @@ public void celTypeTest() {
128128
assertThat(value.celType()).isEqualTo(CUSTOM_STRUCT_TYPE);
129129
}
130130

131+
@Test
132+
public void evaluate_typeOfCustomStruct() throws Exception {
133+
Cel cel =
134+
CelFactory.plannerCelBuilder()
135+
.setOptions(CelOptions.current().enableHeterogeneousNumericComparisons(true).build())
136+
.addVar("a", CUSTOM_STRUCT_TYPE)
137+
.setTypeProvider(CUSTOM_STRUCT_TYPE_PROVIDER)
138+
.setValueProvider(CUSTOM_STRUCT_VALUE_PROVIDER)
139+
.build();
140+
CelAbstractSyntaxTree ast = cel.compile("type(a) == custom_struct").getAst();
141+
142+
Object result = cel.createProgram(ast).eval(ImmutableMap.of("a", new CelCustomStructValue(20)));
143+
144+
assertThat(result).isEqualTo(true);
145+
}
146+
131147
@Test
132148
public void evaluate_usingCustomClass_createNewStruct() throws Exception {
133149
Cel cel =

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ java_library(
213213
"//common/types:type_providers",
214214
"//common/values",
215215
"//common/values:cel_byte_string",
216+
"//common/values:cel_value",
216217
"@maven//:com_google_errorprone_error_prone_annotations",
217218
"@maven//:com_google_guava_guava",
218219
"@maven//:com_google_protobuf_protobuf_java",
@@ -229,6 +230,7 @@ cel_android_library(
229230
"//common/types:type_providers_android",
230231
"//common/types:types_android",
231232
"//common/values:cel_byte_string",
233+
"//common/values:cel_value_android",
232234
"//common/values:values_android",
233235
"@maven//:com_google_errorprone_error_prone_annotations",
234236
"@maven//:com_google_guava_guava",
@@ -247,6 +249,7 @@ java_library(
247249
"//common/annotations",
248250
"//common/types",
249251
"//common/types:type_providers",
252+
"//common/values",
250253
"@maven//:com_google_errorprone_error_prone_annotations",
251254
"@maven//:com_google_guava_guava",
252255
"@maven//:com_google_protobuf_protobuf_java",

runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ public CelRuntime build() {
521521
}
522522

523523
DescriptorTypeResolver descriptorTypeResolver =
524-
DescriptorTypeResolver.create(combinedTypeProvider);
524+
DescriptorTypeResolver.create(combinedTypeProvider, celValueConverter);
525525
TypeFunction typeFunction = TypeFunction.create(descriptorTypeResolver);
526526

527527
mutableFunctionBindings.putAll(functionBindings());

runtime/src/main/java/dev/cel/runtime/DescriptorTypeResolver.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import dev.cel.common.types.CelTypeProvider;
2424
import dev.cel.common.types.StructTypeReference;
2525
import dev.cel.common.types.TypeType;
26+
import dev.cel.common.values.CelValueConverter;
2627
import java.util.NoSuchElementException;
2728
import java.util.Optional;
2829
import org.jspecify.annotations.Nullable;
@@ -56,6 +57,11 @@ public static DescriptorTypeResolver create(CelTypeProvider typeProvider) {
5657
return new DescriptorTypeResolver(typeProvider);
5758
}
5859

60+
public static DescriptorTypeResolver create(
61+
CelTypeProvider typeProvider, CelValueConverter celValueConverter) {
62+
return new DescriptorTypeResolver(typeProvider, celValueConverter);
63+
}
64+
5965
@Override
6066
public TypeType resolveObjectType(Object obj, CelType typeCheckedType) {
6167
checkNotNull(obj);
@@ -82,10 +88,16 @@ public TypeType resolveObjectType(Object obj, CelType typeCheckedType) {
8288
}
8389

8490
private DescriptorTypeResolver() {
85-
this(null);
91+
this(null, CelValueConverter.getDefaultInstance());
8692
}
8793

8894
private DescriptorTypeResolver(@Nullable CelTypeProvider typeProvider) {
95+
this(typeProvider, CelValueConverter.getDefaultInstance());
96+
}
97+
98+
private DescriptorTypeResolver(
99+
@Nullable CelTypeProvider typeProvider, CelValueConverter celValueConverter) {
100+
super(celValueConverter);
89101
this.typeProvider = typeProvider;
90102
}
91103
}

runtime/src/main/java/dev/cel/runtime/TypeResolver.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import dev.cel.common.types.StructTypeReference;
3737
import dev.cel.common.types.TypeType;
3838
import dev.cel.common.values.CelByteString;
39+
import dev.cel.common.values.CelValue;
40+
import dev.cel.common.values.CelValueConverter;
3941
import java.time.Instant;
4042
import java.util.ArrayList;
4143
import java.util.Collection;
@@ -53,8 +55,14 @@
5355
@Internal
5456
public class TypeResolver {
5557

58+
private final CelValueConverter celValueConverter;
59+
5660
static TypeResolver create() {
57-
return new TypeResolver();
61+
return new TypeResolver(CelValueConverter.getDefaultInstance());
62+
}
63+
64+
static TypeResolver create(CelValueConverter celValueConverter) {
65+
return new TypeResolver(celValueConverter);
5866
}
5967

6068
// Sentinel runtime value representing the special "type" ident. This ensures following to be
@@ -147,6 +155,13 @@ public TypeType resolveObjectType(Object obj, CelType typeCheckedType) {
147155
return wellKnownTypeType.get();
148156
}
149157

158+
if (celValueConverter != null) {
159+
Object celVal = celValueConverter.toRuntimeValue(obj);
160+
if (celVal instanceof CelValue) {
161+
return TypeType.create(((CelValue) celVal).celType());
162+
}
163+
}
164+
150165
if (obj instanceof MessageLiteOrBuilder) {
151166
// TODO: Replace with CelLiteDescriptor
152167
throw new UnsupportedOperationException("Not implemented yet");
@@ -193,5 +208,11 @@ private static CelType adaptStructType(StructType typeOfType) {
193208
return newTypeOfType;
194209
}
195210

196-
protected TypeResolver() {}
211+
protected TypeResolver() {
212+
this(CelValueConverter.getDefaultInstance());
213+
}
214+
215+
protected TypeResolver(CelValueConverter celValueConverter) {
216+
this.celValueConverter = checkNotNull(celValueConverter);
217+
}
197218
}

0 commit comments

Comments
 (0)