Skip to content

Commit fa0f36a

Browse files
l46kokcopybara-github
authored andcommitted
Add ProtoWrapperValue
PiperOrigin-RevId: 586717232
1 parent aa6875c commit fa0f36a

File tree

9 files changed

+271
-26
lines changed

9 files changed

+271
-26
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ java_library(
181181
],
182182
deps = [
183183
"//common/annotations",
184+
"@maven//:com_google_guava_guava",
184185
"@maven//:com_google_protobuf_protobuf_java",
185186
],
186187
)

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

+4-9
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import static com.google.common.base.Preconditions.checkNotNull;
1818
import static com.google.common.collect.ImmutableMap.toImmutableMap;
19-
import static java.util.Arrays.stream;
2019

2120
import dev.cel.expr.ExprValue;
2221
import com.google.common.collect.ImmutableList;
@@ -60,7 +59,6 @@
6059
import java.util.List;
6160
import java.util.Map;
6261
import java.util.Optional;
63-
import java.util.function.Function;
6462
import org.jspecify.nullness.Nullable;
6563

6664
/**
@@ -139,10 +137,6 @@ public final class ProtoAdapter {
139137
public static final BidiConverter<Number, Number> DOUBLE_CONVERTER =
140138
BidiConverter.of(Number::doubleValue, Number::floatValue);
141139

142-
private static final ImmutableMap<String, WellKnownProto> WELL_KNOWN_PROTOS =
143-
stream(WellKnownProto.values())
144-
.collect(toImmutableMap(WellKnownProto::typeName, Function.identity()));
145-
146140
private final DynamicProto dynamicProto;
147141
private final boolean enableUnsignedLongs;
148142

@@ -163,7 +157,8 @@ public Object adaptProtoToValue(MessageOrBuilder proto) {
163157
}
164158
// If the proto is not a well-known type, then the input Message is what's expected as the
165159
// output return value.
166-
WellKnownProto wellKnownProto = WELL_KNOWN_PROTOS.get(typeName(proto.getDescriptorForType()));
160+
WellKnownProto wellKnownProto =
161+
WellKnownProto.getByDescriptorName(typeName(proto.getDescriptorForType()));
167162
if (wellKnownProto == null) {
168163
return proto;
169164
}
@@ -328,7 +323,7 @@ private BidiConverter fieldToValueConverter(FieldDescriptor fieldDescriptor) {
328323
* considered, such as a packing an {@code google.protobuf.StringValue} into a {@code Any} value.
329324
*/
330325
public Optional<Message> adaptValueToProto(Object value, String protoTypeName) {
331-
WellKnownProto wellKnownProto = WELL_KNOWN_PROTOS.get(protoTypeName);
326+
WellKnownProto wellKnownProto = WellKnownProto.getByDescriptorName(protoTypeName);
332327
if (wellKnownProto == null) {
333328
if (value instanceof Message) {
334329
return Optional.of((Message) value);
@@ -644,7 +639,7 @@ private static boolean isWrapperType(FieldDescriptor fieldDescriptor) {
644639
return false;
645640
}
646641
String fieldTypeName = fieldDescriptor.getMessageType().getFullName();
647-
WellKnownProto wellKnownProto = WELL_KNOWN_PROTOS.get(fieldTypeName);
642+
WellKnownProto wellKnownProto = WellKnownProto.getByDescriptorName(fieldTypeName);
648643
return wellKnownProto != null && wellKnownProto.isWrapperType();
649644
}
650645

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

+18-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414

1515
package dev.cel.common.internal;
1616

17+
import static com.google.common.collect.ImmutableMap.toImmutableMap;
18+
import static java.util.Arrays.stream;
19+
20+
import com.google.common.collect.ImmutableMap;
1721
import com.google.protobuf.Any;
1822
import com.google.protobuf.BoolValue;
1923
import com.google.protobuf.BytesValue;
@@ -31,6 +35,7 @@
3135
import com.google.protobuf.UInt64Value;
3236
import com.google.protobuf.Value;
3337
import dev.cel.common.annotations.Internal;
38+
import java.util.function.Function;
3439

3540
/**
3641
* WellKnownProto types used throughout CEL. These types are specially handled to ensure that
@@ -58,6 +63,14 @@ public enum WellKnownProto {
5863
private final Descriptor descriptor;
5964
private final boolean isWrapperType;
6065

66+
private static final ImmutableMap<String, WellKnownProto> WELL_KNOWN_PROTO_MAP;
67+
68+
static {
69+
WELL_KNOWN_PROTO_MAP =
70+
stream(WellKnownProto.values())
71+
.collect(toImmutableMap(WellKnownProto::typeName, Function.identity()));
72+
}
73+
6174
WellKnownProto(Descriptor descriptor) {
6275
this(descriptor, /* isWrapperType= */ false);
6376
}
@@ -75,7 +88,11 @@ public String typeName() {
7588
return descriptor.getFullName();
7689
}
7790

78-
boolean isWrapperType() {
91+
public boolean isWrapperType() {
7992
return isWrapperType;
8093
}
94+
95+
public static WellKnownProto getByDescriptorName(String name) {
96+
return WELL_KNOWN_PROTO_MAP.get(name);
97+
}
8198
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ CEL_VALUES_SOURCES = [
3535
PROTO_MESSAGE_VALUE_SOURCES = [
3636
"ProtoCelValueConverter.java",
3737
"ProtoMessageValue.java",
38+
"ProtoWrapperValue.java",
3839
]
3940

4041
java_library(

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

+1-7
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@
1818
import static com.google.common.collect.ImmutableMap.toImmutableMap;
1919
import static com.google.common.math.LongMath.checkedAdd;
2020
import static com.google.common.math.LongMath.checkedSubtract;
21-
import static java.util.Arrays.stream;
2221

23-
import com.google.common.collect.ImmutableMap;
2422
import com.google.common.primitives.UnsignedLong;
2523
import com.google.errorprone.annotations.Immutable;
2624
import com.google.protobuf.Any;
@@ -56,7 +54,6 @@
5654
import java.util.HashMap;
5755
import java.util.List;
5856
import java.util.Map;
59-
import java.util.function.Function;
6057

6158
/**
6259
* {@code CelValueConverter} handles bidirectional conversion between native Java and protobuf
@@ -70,9 +67,6 @@
7067
@Immutable
7168
@Internal
7269
public final class ProtoCelValueConverter extends CelValueConverter {
73-
private static final ImmutableMap<String, WellKnownProto> WELL_KNOWN_PROTOS =
74-
stream(WellKnownProto.values())
75-
.collect(toImmutableMap(WellKnownProto::typeName, Function.identity()));
7670
private final CelDescriptorPool celDescriptorPool;
7771
private final DynamicProto dynamicProto;
7872

@@ -90,7 +84,7 @@ public CelValue fromProtoMessageToCelValue(MessageOrBuilder message) {
9084
}
9185

9286
WellKnownProto wellKnownProto =
93-
WELL_KNOWN_PROTOS.get(message.getDescriptorForType().getFullName());
87+
WellKnownProto.getByDescriptorName(message.getDescriptorForType().getFullName());
9488
if (wellKnownProto == null) {
9589
return ProtoMessageValue.create((Message) message, celDescriptorPool, this);
9690
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.common.values;
16+
17+
import com.google.auto.value.AutoValue;
18+
import com.google.errorprone.annotations.Immutable;
19+
import com.google.protobuf.ByteString;
20+
import com.google.protobuf.FloatValue;
21+
import com.google.protobuf.Int32Value;
22+
import com.google.protobuf.Int64Value;
23+
import com.google.protobuf.MessageOrBuilder;
24+
import com.google.protobuf.UInt32Value;
25+
import com.google.protobuf.UInt64Value;
26+
import dev.cel.common.internal.WellKnownProto;
27+
import dev.cel.common.types.NullableType;
28+
29+
/** ProtoWrapperValue represents a */
30+
@Immutable
31+
@AutoValue
32+
public abstract class ProtoWrapperValue extends StructValue {
33+
34+
@Override
35+
public abstract CelValue value();
36+
37+
abstract WellKnownProto wellKnownProto();
38+
39+
/**
40+
* Retrieves the underlying value being held in the wrapper. For example, if this is a
41+
* `google.protobuf.IntValue', a Java long is returned.
42+
*/
43+
public Object nativeValue() {
44+
if (wellKnownProto().equals(WellKnownProto.BYTES_VALUE)) {
45+
// Return the proto ByteString as the underlying primitive value rather than a mutable byte
46+
// array.
47+
return ByteString.copyFrom(((BytesValue) value()).value().toByteArray());
48+
}
49+
return value().value();
50+
}
51+
52+
@Override
53+
public abstract NullableType celType();
54+
55+
@Override
56+
public boolean isZeroValue() {
57+
return value().isZeroValue();
58+
}
59+
60+
@Override
61+
public CelValue select(String fieldName) {
62+
throw new UnsupportedOperationException("Wrappers do not support field selection");
63+
}
64+
65+
@Override
66+
public boolean hasField(String fieldName) {
67+
throw new UnsupportedOperationException("Wrappers do not support presence tests");
68+
}
69+
70+
public static ProtoWrapperValue create(
71+
MessageOrBuilder wrapperMessage, boolean enableUnsignedLongs) {
72+
WellKnownProto wellKnownProto = getWellKnownProtoFromWrapperName(wrapperMessage);
73+
CelValue celValue = newCelValueFromWrapper(wrapperMessage, wellKnownProto, enableUnsignedLongs);
74+
NullableType nullableType = NullableType.create(celValue.celType());
75+
return new AutoValue_ProtoWrapperValue(celValue, wellKnownProto, nullableType);
76+
}
77+
78+
private static CelValue newCelValueFromWrapper(
79+
MessageOrBuilder message, WellKnownProto wellKnownProto, boolean enableUnsignedLongs) {
80+
switch (wellKnownProto) {
81+
case BOOL_VALUE:
82+
return BoolValue.create(((com.google.protobuf.BoolValue) message).getValue());
83+
case BYTES_VALUE:
84+
return BytesValue.create(
85+
CelByteString.of(((com.google.protobuf.BytesValue) message).getValue().toByteArray()));
86+
case DOUBLE_VALUE:
87+
return DoubleValue.create(((com.google.protobuf.DoubleValue) message).getValue());
88+
case FLOAT_VALUE:
89+
return DoubleValue.create(((FloatValue) message).getValue());
90+
case INT32_VALUE:
91+
return IntValue.create(((Int32Value) message).getValue());
92+
case INT64_VALUE:
93+
return IntValue.create(((Int64Value) message).getValue());
94+
case STRING_VALUE:
95+
return StringValue.create(((com.google.protobuf.StringValue) message).getValue());
96+
case UINT32_VALUE:
97+
return UintValue.create(((UInt32Value) message).getValue(), enableUnsignedLongs);
98+
case UINT64_VALUE:
99+
return UintValue.create(((UInt64Value) message).getValue(), enableUnsignedLongs);
100+
default:
101+
throw new IllegalArgumentException(
102+
"Should only be called for wrapper types. Got: " + wellKnownProto.name());
103+
}
104+
}
105+
106+
private static WellKnownProto getWellKnownProtoFromWrapperName(MessageOrBuilder message) {
107+
WellKnownProto wellKnownProto =
108+
WellKnownProto.getByDescriptorName(message.getDescriptorForType().getFullName());
109+
if (!wellKnownProto.isWrapperType()) {
110+
throw new IllegalArgumentException("Expected a wrapper type. Got: " + wellKnownProto.name());
111+
}
112+
113+
return wellKnownProto;
114+
}
115+
}

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

+16-8
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,25 @@
2121
import dev.cel.common.types.SimpleType;
2222

2323
/**
24-
* UintValue represents CelValue for unsigned longs, leveraging Guava's implementation of {@link
25-
* UnsignedLong}.
26-
*
27-
* <p>TODO: Look into potentially accepting a primitive `long` to avoid boxing/unboxing
28-
* when the interpreter is augmented to work directly on CelValue.
24+
* UintValue represents CelValue for unsigned longs. This either leverages Guava's implementation of
25+
* {@link UnsignedLong}, or just holds a primitive long.
2926
*/
30-
@AutoValue
3127
@Immutable
28+
@AutoValue
29+
@AutoValue.CopyAnnotations
30+
@SuppressWarnings("Immutable") // value is either a boxed long or an immutable UnsignedLong.
3231
public abstract class UintValue extends CelValue {
3332

3433
@Override
35-
public abstract UnsignedLong value();
34+
public abstract Number value();
3635

3736
@Override
3837
public boolean isZeroValue() {
39-
return UnsignedLong.ZERO.equals(value());
38+
if (value() instanceof UnsignedLong) {
39+
return UnsignedLong.ZERO.equals(value());
40+
} else {
41+
return value().longValue() == 0;
42+
}
4043
}
4144

4245
@Override
@@ -47,4 +50,9 @@ public CelType celType() {
4750
public static UintValue create(UnsignedLong value) {
4851
return new AutoValue_UintValue(value);
4952
}
53+
54+
public static UintValue create(long value, boolean enableUnsignedLongs) {
55+
Number unsignedLong = enableUnsignedLongs ? UnsignedLong.fromLongBits(value) : value;
56+
return new AutoValue_UintValue(unsignedLong);
57+
}
5058
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public void hasField_extensionField_throwsWhenDescriptorMissing() {
172172
}
173173

174174
private enum SelectFieldTestCase {
175-
// // Primitives
175+
// Primitives
176176
BOOL("single_bool", BoolValue.create(true)),
177177
INT32("single_int32", IntValue.create(4L)),
178178
INT64("single_int64", IntValue.create(5L)),

0 commit comments

Comments
 (0)