Skip to content

Commit 83f7b4e

Browse files
l46kokcopybara-github
authored andcommitted
Delete unknownFields and have readAllFields skip over unknown tags
PiperOrigin-RevId: 994561750
1 parent 82d934a commit 83f7b4e

9 files changed

Lines changed: 1210 additions & 401 deletions

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,9 @@ java_library(
280280
],
281281
deps = [
282282
":base_proto_cel_value_converter",
283+
":optimized_selectable",
283284
":preadapted_list",
285+
":select_field",
284286
":values",
285287
"//:auto_value",
286288
"//common:options",

‎common/src/main/java/dev/cel/common/values/ProtoLiteCelValueConverter.java‎

Lines changed: 275 additions & 146 deletions
Large diffs are not rendered by default.

‎common/src/main/java/dev/cel/common/values/ProtoMessageLiteValue.java‎

Lines changed: 108 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818

1919
import com.google.auto.value.AutoValue;
2020
import com.google.auto.value.extension.memoized.Memoized;
21-
import com.google.common.collect.ImmutableListMultimap;
2221
import com.google.common.collect.ImmutableMap;
2322
import com.google.errorprone.annotations.Immutable;
23+
import com.google.protobuf.ByteString;
2424
import com.google.protobuf.MessageLite;
2525
import dev.cel.common.types.CelType;
2626
import dev.cel.common.types.StructTypeReference;
27-
import dev.cel.common.values.ProtoLiteCelValueConverter.MessageFields;
2827
import dev.cel.protobuf.CelLiteDescriptor.FieldLiteDescriptor;
2928
import java.io.IOException;
29+
import java.util.Objects;
3030
import java.util.Optional;
3131
import org.jspecify.annotations.Nullable;
3232

@@ -38,53 +38,97 @@
3838
* <p>If the codebase has access to full protobuf messages with descriptors, use {@code
3939
* ProtoMessageValue} instead.
4040
*
41+
* <p>An instance is backed by either a materialized {@link #rawValue()} or unparsed {@link
42+
* #wireBytes()} (exactly one is non-null). Wire-backed instances decode selected fields directly
43+
* from {@link #wireBytes()} and lazily parse the full {@link MessageLite} only if {@link #value()}
44+
* is invoked.
45+
*
4146
* <p>Implements {@link OptimizedSelectable} so that select chains can address fields by number:
4247
*
4348
* <ul>
4449
* <li><b>Field renames:</b> If a protobuf field is renamed in schema after an AST was compiled,
4550
* resolving by {@link SelectField#fieldNumber()} maps the number to the runtime descriptor's
4651
* current field name, preventing {@code CelAttributeNotFoundException}.
4752
* <li><b>Version skew / unknown fields:</b> When evaluating payloads serialized by a newer binary
48-
* containing fields absent from the local {@code CelLiteDescriptor}, the unknown wire bytes
49-
* are preserved in {@link #unknownFields()} and decoded on demand using the compile-time wire
50-
* type and default metadata in {@link SelectField}.
53+
* containing fields absent from the local {@code CelLiteDescriptor}, the unknown fields are
54+
* decoded on demand directly from the message's wire bytes using the compile-time wire type
55+
* and default metadata in {@link SelectField}.
5156
* </ul>
5257
*/
5358
@AutoValue
5459
@Immutable
5560
abstract class ProtoMessageLiteValue extends StructValue<String, MessageLite>
5661
implements OptimizedSelectable {
5762

58-
@Override
59-
public abstract MessageLite value();
63+
// Populated when wrapping an already-materialized root MessageLite (e.g., from activation).
64+
abstract @Nullable MessageLite rawValue();
65+
66+
// Populated when slicing a nested submessage from parent wire bytes to avoid deserializing and
67+
// re-serializing intermediate hops; lazily parsed into a MessageLite only if value() is called.
68+
abstract @Nullable ByteString wireBytes();
6069

6170
@Override
6271
public abstract CelType celType();
6372

6473
abstract ProtoLiteCelValueConverter protoLiteCelValueConverter();
6574

6675
@Memoized
67-
MessageFields messageFields() {
68-
try {
69-
return protoLiteCelValueConverter().readMessageFields(value(), celType().name());
70-
} catch (IOException e) {
71-
throw new IllegalStateException("Unable to read message fields for " + celType().name(), e);
76+
@Override
77+
public MessageLite value() {
78+
MessageLite msg = rawValue();
79+
if (msg != null) {
80+
return msg;
7281
}
82+
return protoLiteCelValueConverter()
83+
.parseMessageLite(checkNotNull(wireBytes()), celType().name());
7384
}
7485

75-
ImmutableMap<String, Object> fieldValues() {
76-
return messageFields().values();
86+
@Memoized
87+
ByteString serializedRawValue() {
88+
return checkNotNull(rawValue()).toByteString();
7789
}
7890

79-
ImmutableListMultimap<Integer, Object> unknownFields() {
80-
return messageFields().unknowns();
91+
ByteString toByteString() {
92+
ByteString bytes = wireBytes();
93+
return bytes != null ? bytes : serializedRawValue();
94+
}
95+
96+
@Memoized
97+
ImmutableMap<String, Object> fieldValues() {
98+
try {
99+
return protoLiteCelValueConverter().readAllFields(toByteString(), celType().name());
100+
} catch (IOException e) {
101+
throw new IllegalArgumentException(
102+
"Failed to decode proto message of type: " + celType().name(), e);
103+
}
81104
}
82105

83106
@Override
84107
public boolean isZeroValue() {
108+
ByteString bytes = wireBytes();
109+
if (bytes != null && bytes.isEmpty()) {
110+
return true;
111+
}
85112
return value().getDefaultInstanceForType().equals(value());
86113
}
87114

115+
@Override
116+
public final boolean equals(Object other) {
117+
if (other == this) {
118+
return true;
119+
}
120+
if (!(other instanceof ProtoMessageLiteValue)) {
121+
return false;
122+
}
123+
ProtoMessageLiteValue that = (ProtoMessageLiteValue) other;
124+
return this.celType().equals(that.celType()) && this.value().equals(that.value());
125+
}
126+
127+
@Override
128+
public final int hashCode() {
129+
return Objects.hash(value(), celType());
130+
}
131+
88132
@Override
89133
public Object select(String field) {
90134
return find(field)
@@ -93,16 +137,15 @@ public Object select(String field) {
93137

94138
@Override
95139
public Optional<Object> find(String field) {
96-
Object fieldValue = fieldValues().get(field);
97-
return Optional.ofNullable(fieldValue)
98-
.map(value -> protoLiteCelValueConverter().toRuntimeValue(fieldValue));
140+
return Optional.ofNullable(fieldValues().get(field))
141+
.map(protoLiteCelValueConverter()::toRuntimeValue);
99142
}
100143

101144
@Override
102145
public Object selectByFieldNumber(SelectField field) {
103146
FieldLiteDescriptor fd = findFieldDescriptor(field);
104147
if (fd != null) {
105-
Object known = fieldValues().get(fd.getFieldName());
148+
Object known = readField(fd);
106149
if (known != null) {
107150
return protoLiteCelValueConverter().toRuntimeValue(known);
108151
}
@@ -112,28 +155,48 @@ public Object selectByFieldNumber(SelectField field) {
112155
return protoLiteCelValueConverter().getDefaultCelValue(fd);
113156
}
114157
return RawProtoMessageLiteValue.selectWireOrDefault(
115-
field, unknownFields().get(field.fieldNumber()), protoLiteCelValueConverter());
158+
field,
159+
RawProtoMessageLiteValue.readWireField(toByteString(), field.fieldNumber()),
160+
protoLiteCelValueConverter());
116161
}
117162

118163
@Override
119164
public boolean hasFieldByNumber(SelectField field) {
120165
FieldLiteDescriptor fd = findFieldDescriptor(field);
121166
if (fd != null) {
122-
return fieldValues().containsKey(fd.getFieldName());
167+
return hasField(fd);
123168
}
124-
return RawProtoMessageLiteValue.isPresentInWire(
125-
field, unknownFields().get(field.fieldNumber()));
169+
return RawProtoMessageLiteValue.isPresentInWire(toByteString(), field);
126170
}
127171

128172
@Override
129173
public Optional<Object> findByFieldNumber(SelectField field) {
130174
FieldLiteDescriptor fd = findFieldDescriptor(field);
131175
if (fd != null) {
132-
return Optional.ofNullable(fieldValues().get(fd.getFieldName()))
133-
.map(value -> protoLiteCelValueConverter().toRuntimeValue(value));
176+
return Optional.ofNullable(readField(fd)).map(protoLiteCelValueConverter()::toRuntimeValue);
134177
}
135178
return RawProtoMessageLiteValue.navigateWire(
136-
field, unknownFields().get(field.fieldNumber()), protoLiteCelValueConverter());
179+
field,
180+
RawProtoMessageLiteValue.readWireField(toByteString(), field.fieldNumber()),
181+
protoLiteCelValueConverter());
182+
}
183+
184+
private @Nullable Object readField(FieldLiteDescriptor fd) {
185+
try {
186+
return protoLiteCelValueConverter().readSingleField(toByteString(), fd);
187+
} catch (IOException e) {
188+
throw new IllegalArgumentException(
189+
"Failed to decode proto message of type: " + celType().name(), e);
190+
}
191+
}
192+
193+
private boolean hasField(FieldLiteDescriptor fd) {
194+
try {
195+
return protoLiteCelValueConverter().hasSingleField(toByteString(), fd);
196+
} catch (IOException e) {
197+
throw new IllegalArgumentException(
198+
"Failed to decode proto message of type: " + celType().name(), e);
199+
}
137200
}
138201

139202
private @Nullable FieldLiteDescriptor findFieldDescriptor(SelectField field) {
@@ -148,7 +211,24 @@ static ProtoMessageLiteValue create(
148211
checkNotNull(typeName);
149212
checkNotNull(protoLiteCelValueConverter);
150213
return new AutoValue_ProtoMessageLiteValue(
151-
value, StructTypeReference.create(typeName), protoLiteCelValueConverter);
214+
value,
215+
/* wireBytes= */ null,
216+
StructTypeReference.create(typeName),
217+
protoLiteCelValueConverter);
218+
}
219+
220+
static ProtoMessageLiteValue create(
221+
ByteString wireBytes,
222+
String typeName,
223+
ProtoLiteCelValueConverter protoLiteCelValueConverter) {
224+
checkNotNull(wireBytes);
225+
checkNotNull(typeName);
226+
checkNotNull(protoLiteCelValueConverter);
227+
return new AutoValue_ProtoMessageLiteValue(
228+
/* rawValue= */ null,
229+
wireBytes,
230+
StructTypeReference.create(typeName),
231+
protoLiteCelValueConverter);
152232
}
153233

154234
ProtoMessageLiteValue() {}

‎common/src/main/java/dev/cel/common/values/ProtoMessageValue.java‎

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414

1515
package dev.cel.common.values;
1616

17+
import static com.google.common.base.Preconditions.checkNotNull;
18+
1719
import com.google.auto.value.AutoValue;
18-
import com.google.common.base.Preconditions;
1920
import com.google.errorprone.annotations.Immutable;
2021
import com.google.protobuf.Descriptors.Descriptor;
2122
import com.google.protobuf.Descriptors.FieldDescriptor;
@@ -28,7 +29,8 @@
2829
/** ProtoMessageValue is a struct value with protobuf support. */
2930
@AutoValue
3031
@Immutable
31-
public abstract class ProtoMessageValue extends StructValue<String, Message> {
32+
public abstract class ProtoMessageValue extends StructValue<String, Message>
33+
implements OptimizedSelectable {
3234

3335
@Override
3436
public abstract Message value();
@@ -60,28 +62,38 @@ public Optional<Object> find(String field) {
6062
FieldDescriptor fieldDescriptor =
6163
findField(celDescriptorPool(), value().getDescriptorForType(), field);
6264

63-
// Selecting a field on a protobuf message yields a default value even if the field is not
64-
// declared. Therefore, we must exhaustively test whether they are actually declared.
65-
if (fieldDescriptor.isRepeated()) {
66-
if (value().getRepeatedFieldCount(fieldDescriptor) == 0) {
67-
return Optional.empty();
68-
}
69-
} else if (!value().hasField(fieldDescriptor)) {
70-
return Optional.empty();
71-
}
65+
return findFieldValue(fieldDescriptor);
66+
}
7267

73-
return Optional.of(
74-
protoCelValueConverter().fromProtoMessageFieldToCelValue(value(), fieldDescriptor));
68+
@Override
69+
public Object selectByFieldNumber(SelectField field) {
70+
FieldDescriptor fieldDescriptor = findFieldByNumber(value().getDescriptorForType(), field);
71+
72+
return protoCelValueConverter().fromProtoMessageFieldToCelValue(value(), fieldDescriptor);
73+
}
74+
75+
@Override
76+
public boolean hasFieldByNumber(SelectField field) {
77+
FieldDescriptor fieldDescriptor = findFieldByNumber(value().getDescriptorForType(), field);
78+
79+
return isFieldPresent(fieldDescriptor);
80+
}
81+
82+
@Override
83+
public Optional<Object> findByFieldNumber(SelectField field) {
84+
FieldDescriptor fieldDescriptor = findFieldByNumber(value().getDescriptorForType(), field);
85+
86+
return findFieldValue(fieldDescriptor);
7587
}
7688

7789
public static ProtoMessageValue create(
7890
Message value,
7991
CelDescriptorPool celDescriptorPool,
8092
ProtoCelValueConverter protoCelValueConverter,
8193
boolean enableJsonFieldNames) {
82-
Preconditions.checkNotNull(value);
83-
Preconditions.checkNotNull(celDescriptorPool);
84-
Preconditions.checkNotNull(protoCelValueConverter);
94+
checkNotNull(value);
95+
checkNotNull(celDescriptorPool);
96+
checkNotNull(protoCelValueConverter);
8597
return new AutoValue_ProtoMessageValue(
8698
value,
8799
StructTypeReference.create(value.getDescriptorForType().getFullName()),
@@ -90,6 +102,36 @@ public static ProtoMessageValue create(
90102
enableJsonFieldNames);
91103
}
92104

105+
private Optional<Object> findFieldValue(FieldDescriptor fieldDescriptor) {
106+
if (!isFieldPresent(fieldDescriptor)) {
107+
return Optional.empty();
108+
}
109+
110+
return Optional.of(
111+
protoCelValueConverter().fromProtoMessageFieldToCelValue(value(), fieldDescriptor));
112+
}
113+
114+
private boolean isFieldPresent(FieldDescriptor fieldDescriptor) {
115+
// Selecting a field on a protobuf message yields a default value even if the field is not
116+
// declared. Therefore, we must exhaustively test whether they are actually declared.
117+
if (fieldDescriptor.isRepeated()) {
118+
return value().getRepeatedFieldCount(fieldDescriptor) > 0;
119+
}
120+
return value().hasField(fieldDescriptor);
121+
}
122+
123+
private static FieldDescriptor findFieldByNumber(Descriptor descriptor, SelectField field) {
124+
FieldDescriptor fieldDescriptor = descriptor.findFieldByNumber(field.fieldNumber());
125+
if (fieldDescriptor != null) {
126+
return fieldDescriptor;
127+
}
128+
129+
throw new IllegalArgumentException(
130+
String.format(
131+
"field '%s' (number %d) is not declared in message '%s'",
132+
field.fieldName(), field.fieldNumber(), descriptor.getFullName()));
133+
}
134+
93135
private FieldDescriptor findField(
94136
CelDescriptorPool celDescriptorPool, Descriptor descriptor, String fieldName) {
95137
if (enableJsonFieldNames()) {
@@ -114,4 +156,6 @@ private FieldDescriptor findField(
114156
"field '%s' is not declared in message '%s'",
115157
fieldName, descriptor.getFullName())));
116158
}
159+
160+
ProtoMessageValue() {}
117161
}

0 commit comments

Comments
 (0)