Skip to content

Commit 9e1b5ee

Browse files
l46kokcopybara-github
authored andcommitted
Fix CelValue adaptation for lists/maps
PiperOrigin-RevId: 876477992
1 parent 5103b30 commit 9e1b5ee

File tree

10 files changed

+92
-60
lines changed

10 files changed

+92
-60
lines changed

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

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,39 @@ public static CelValueConverter getDefaultInstance() {
4141
return DEFAULT_INSTANCE;
4242
}
4343

44-
/** Adapts a {@link CelValue} to a plain old Java Object. */
45-
public Object unwrap(CelValue celValue) {
46-
Preconditions.checkNotNull(celValue);
44+
/**
45+
* Unwraps the {@code value} into its plain old Java Object representation.
46+
*
47+
* <p>The value may be a {@link CelValue}, a {@link Collection} or a {@link Map}.
48+
*/
49+
public Object maybeUnwrap(Object value) {
50+
if (value instanceof CelValue) {
51+
return unwrap((CelValue) value);
52+
}
4753

48-
if (celValue instanceof OptionalValue) {
49-
OptionalValue<Object, Object> optionalValue = (OptionalValue<Object, Object>) celValue;
50-
if (optionalValue.isZeroValue()) {
51-
return Optional.empty();
54+
if (value instanceof Collection) {
55+
Collection<Object> collection = (Collection<Object>) value;
56+
ImmutableList.Builder<Object> builder =
57+
ImmutableList.builderWithExpectedSize(collection.size());
58+
for (Object element : collection) {
59+
builder.add(maybeUnwrap(element));
5260
}
5361

54-
return Optional.of(optionalValue.value());
62+
return builder.build();
5563
}
5664

57-
if (celValue instanceof ErrorValue) {
58-
return celValue;
65+
if (value instanceof Map) {
66+
Map<Object, Object> map = (Map<Object, Object>) value;
67+
ImmutableMap.Builder<Object, Object> builder =
68+
ImmutableMap.builderWithExpectedSize(map.size());
69+
for (Map.Entry<Object, Object> entry : map.entrySet()) {
70+
builder.put(maybeUnwrap(entry.getKey()), maybeUnwrap(entry.getValue()));
71+
}
72+
73+
return builder.buildOrThrow();
5974
}
6075

61-
return celValue.value();
76+
return value;
6277
}
6378

6479
/**
@@ -101,6 +116,26 @@ protected Object normalizePrimitive(Object value) {
101116
return value;
102117
}
103118

119+
/** Adapts a {@link CelValue} to a plain old Java Object. */
120+
private static Object unwrap(CelValue celValue) {
121+
Preconditions.checkNotNull(celValue);
122+
123+
if (celValue instanceof OptionalValue) {
124+
OptionalValue<Object, Object> optionalValue = (OptionalValue<Object, Object>) celValue;
125+
if (optionalValue.isZeroValue()) {
126+
return Optional.empty();
127+
}
128+
129+
return Optional.of(optionalValue.value());
130+
}
131+
132+
if (celValue instanceof ErrorValue) {
133+
return celValue;
134+
}
135+
136+
return celValue.value();
137+
}
138+
104139
private ImmutableList<Object> toListValue(Collection<Object> iterable) {
105140
Preconditions.checkNotNull(iterable);
106141

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,16 @@ public void toRuntimeValue_optionalValue() {
3737
@Test
3838
@SuppressWarnings("unchecked") // Test only
3939
public void unwrap_optionalValue() {
40-
Optional<Long> result = (Optional<Long>) CEL_VALUE_CONVERTER.unwrap(OptionalValue.create(2L));
40+
Optional<Long> result =
41+
(Optional<Long>) CEL_VALUE_CONVERTER.maybeUnwrap(OptionalValue.create(2L));
4142

4243
assertThat(result).isEqualTo(Optional.of(2L));
4344
}
4445

4546
@Test
4647
@SuppressWarnings("unchecked") // Test only
4748
public void unwrap_emptyOptionalValue() {
48-
Optional<Long> result = (Optional<Long>) CEL_VALUE_CONVERTER.unwrap(OptionalValue.EMPTY);
49+
Optional<Long> result = (Optional<Long>) CEL_VALUE_CONVERTER.maybeUnwrap(OptionalValue.EMPTY);
4950

5051
assertThat(result).isEqualTo(Optional.empty());
5152
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class ProtoCelValueConverterTest {
3535

3636
@Test
3737
public void unwrap_nullValue() {
38-
NullValue nullValue = (NullValue) PROTO_CEL_VALUE_CONVERTER.unwrap(NullValue.NULL_VALUE);
38+
NullValue nullValue = (NullValue) PROTO_CEL_VALUE_CONVERTER.maybeUnwrap(NullValue.NULL_VALUE);
3939

4040
// Note: No conversion is attempted. We're using dev.cel.common.values.NullValue.NULL_VALUE as
4141
// the

conformance/src/test/java/dev/cel/conformance/BUILD.bazel

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,12 +176,6 @@ _TESTS_TO_SKIP_PLANNER = [
176176

177177
# Skip until fixed.
178178
"parse/receiver_function_names",
179-
"proto2/extensions_get/package_scoped_test_all_types_ext",
180-
"proto2/extensions_get/package_scoped_repeated_test_all_types",
181-
"proto2/extensions_get/message_scoped_nested_ext",
182-
"proto2/extensions_get/message_scoped_repeated_test_all_types",
183-
"proto2_ext/get_ext/package_scoped_repeated_test_all_types",
184-
"proto2_ext/get_ext/message_scoped_repeated_test_all_types",
185179

186180
# TODO: Fix null assignment to a field
187181
"proto2/set_null/single_message",

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

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import dev.cel.common.exceptions.CelAttributeNotFoundException;
2323
import dev.cel.common.values.BaseProtoCelValueConverter;
2424
import dev.cel.common.values.BaseProtoMessageValueProvider;
25-
import dev.cel.common.values.CelValue;
2625
import dev.cel.common.values.CelValueProvider;
2726
import dev.cel.common.values.CombinedCelValueProvider;
2827
import dev.cel.common.values.SelectableValue;
@@ -64,7 +63,7 @@ static CelValueRuntimeTypeProvider newInstance(CelValueProvider valueProvider) {
6463

6564
@Override
6665
public Object createMessage(String messageName, Map<String, Object> values) {
67-
return maybeUnwrapCelValue(
66+
return protoCelValueConverter.maybeUnwrap(
6867
valueProvider
6968
.newValue(messageName, values)
7069
.orElseThrow(
@@ -87,7 +86,7 @@ public Object selectField(Object message, String fieldName) {
8786
SelectableValue<String> selectableValue = getSelectableValueOrThrow(message, fieldName);
8887
Object value = selectableValue.select(fieldName);
8988

90-
return maybeUnwrapCelValue(value);
89+
return protoCelValueConverter.maybeUnwrap(value);
9190
}
9291

9392
@Override
@@ -120,24 +119,12 @@ public Object adapt(String messageName, Object message) {
120119
}
121120

122121
if (message instanceof MessageLite) {
123-
return maybeUnwrapCelValue(protoCelValueConverter.toRuntimeValue(message));
122+
return protoCelValueConverter.maybeUnwrap(protoCelValueConverter.toRuntimeValue(message));
124123
}
125124

126125
return message;
127126
}
128127

129-
/**
130-
* DefaultInterpreter cannot handle CelValue and instead expects plain Java objects.
131-
*
132-
* <p>This will become unnecessary once we introduce a rewrite of a Cel runtime.
133-
*/
134-
private Object maybeUnwrapCelValue(Object object) {
135-
if (object instanceof CelValue) {
136-
return protoCelValueConverter.unwrap((CelValue) object);
137-
}
138-
return object;
139-
}
140-
141128
private static void throwInvalidFieldSelection(String fieldName) {
142129
throw CelAttributeNotFoundException.forFieldResolution(fieldName);
143130
}

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ java_library(
128128
"//common/types",
129129
"//common/types:type_providers",
130130
"//common/values",
131-
"//common/values:cel_value",
132131
"//runtime:interpretable",
133132
"@maven//:com_google_errorprone_error_prone_annotations",
134133
"@maven//:com_google_guava_guava",
@@ -380,7 +379,6 @@ java_library(
380379
"//common:error_codes",
381380
"//common/exceptions:runtime_exception",
382381
"//common/values",
383-
"//common/values:cel_value",
384382
"//runtime:evaluation_exception",
385383
"//runtime:interpretable",
386384
"//runtime:resolved_overload",

runtime/src/main/java/dev/cel/runtime/planner/EvalHelpers.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import com.google.common.base.Joiner;
1818
import dev.cel.common.CelErrorCode;
1919
import dev.cel.common.exceptions.CelRuntimeException;
20-
import dev.cel.common.values.CelValue;
2120
import dev.cel.common.values.CelValueConverter;
2221
import dev.cel.common.values.ErrorValue;
2322
import dev.cel.runtime.CelEvaluationException;
@@ -56,23 +55,21 @@ static Object evalStrictly(
5655
}
5756
}
5857

59-
static Object dispatch(CelResolvedOverload overload, CelValueConverter valueConverter, Object[] args) throws CelEvaluationException {
58+
static Object dispatch(
59+
CelResolvedOverload overload, CelValueConverter valueConverter, Object[] args)
60+
throws CelEvaluationException {
6061
try {
6162
Object result = overload.getDefinition().apply(args);
62-
Object runtimeValue = valueConverter.toRuntimeValue(result);
63-
if (runtimeValue instanceof CelValue) {
64-
return valueConverter.unwrap((CelValue) runtimeValue);
65-
}
66-
67-
return runtimeValue;
63+
return valueConverter.maybeUnwrap(valueConverter.toRuntimeValue(result));
6864
} catch (CelRuntimeException e) {
6965
// Function dispatch failure that's already been handled -- just propagate.
7066
throw e;
7167
} catch (RuntimeException e) {
7268
// Unexpected function dispatch failure.
73-
throw new IllegalArgumentException(String.format(
74-
"Function '%s' failed with arg(s) '%s'",
75-
overload.getOverloadId(), Joiner.on(", ").join(args)),
69+
throw new IllegalArgumentException(
70+
String.format(
71+
"Function '%s' failed with arg(s) '%s'",
72+
overload.getOverloadId(), Joiner.on(", ").join(args)),
7673
e);
7774
}
7875
}

runtime/src/main/java/dev/cel/runtime/planner/NamespacedAttribute.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import dev.cel.common.types.EnumType;
2323
import dev.cel.common.types.SimpleType;
2424
import dev.cel.common.types.TypeType;
25-
import dev.cel.common.values.CelValue;
2625
import dev.cel.common.values.CelValueConverter;
2726
import dev.cel.runtime.GlobalResolver;
2827
import java.util.NoSuchElementException;
@@ -148,11 +147,7 @@ private static Object applyQualifiers(
148147
obj = qualifier.qualify(obj);
149148
}
150149

151-
if (obj instanceof CelValue) {
152-
obj = celValueConverter.unwrap((CelValue) obj);
153-
}
154-
155-
return obj;
150+
return celValueConverter.maybeUnwrap(obj);
156151
}
157152

158153
static NamespacedAttribute create(

runtime/src/main/java/dev/cel/runtime/planner/RelativeAttribute.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import com.google.common.collect.ImmutableList;
1818
import com.google.errorprone.annotations.Immutable;
19-
import dev.cel.common.values.CelValue;
2019
import dev.cel.common.values.CelValueConverter;
2120
import dev.cel.runtime.GlobalResolver;
2221

@@ -41,10 +40,7 @@ public Object resolve(GlobalResolver ctx, ExecutionFrame frame) {
4140
}
4241

4342
// TODO: Handle unknowns
44-
if (obj instanceof CelValue) {
45-
obj = celValueConverter.unwrap((CelValue) obj);
46-
}
47-
return obj;
43+
return celValueConverter.maybeUnwrap(obj);
4844
}
4945

5046
@Override

runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,35 @@ public void plan_ident_variable() throws Exception {
316316
assertThat(result).isEqualTo(1);
317317
}
318318

319+
@Test
320+
public void plan_ident_variableWithStructInList() throws Exception {
321+
CelAbstractSyntaxTree ast = compile("dyn_var");
322+
Program program = PLANNER.plan(ast);
323+
324+
Object result =
325+
program.eval(
326+
ImmutableMap.of(
327+
"dyn_var", ImmutableList.of(TestAllTypes.newBuilder().setSingleInt32(42).build())));
328+
329+
assertThat(result)
330+
.isEqualTo(ImmutableList.of(TestAllTypes.newBuilder().setSingleInt32(42).build()));
331+
}
332+
333+
@Test
334+
public void plan_ident_variableWithStructInMap() throws Exception {
335+
CelAbstractSyntaxTree ast = compile("dyn_var");
336+
Program program = PLANNER.plan(ast);
337+
338+
Object result =
339+
program.eval(
340+
ImmutableMap.of(
341+
"dyn_var",
342+
ImmutableMap.of("foo", TestAllTypes.newBuilder().setSingleInt32(42).build())));
343+
344+
assertThat(result)
345+
.isEqualTo(ImmutableMap.of("foo", TestAllTypes.newBuilder().setSingleInt32(42).build()));
346+
}
347+
319348
@Test
320349
public void planIdent_typeLiteral(@TestParameter TypeLiteralTestCase testCase) throws Exception {
321350
CelAbstractSyntaxTree ast = compile(testCase.expression);

0 commit comments

Comments
 (0)