Skip to content

Commit 9aeeccd

Browse files
l46kokcopybara-github
authored andcommitted
Implement OptimizedSelectable for ProtoMessageLiteValue
PiperOrigin-RevId: 988430966
1 parent 3030747 commit 9aeeccd

2 files changed

Lines changed: 392 additions & 9 deletions

File tree

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

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,21 @@
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;
1820
import com.google.auto.value.extension.memoized.Memoized;
19-
import com.google.common.base.Preconditions;
2021
import com.google.common.collect.ImmutableListMultimap;
2122
import com.google.common.collect.ImmutableMap;
2223
import com.google.errorprone.annotations.Immutable;
2324
import com.google.protobuf.MessageLite;
24-
import dev.cel.common.annotations.Internal;
2525
import dev.cel.common.types.CelType;
2626
import dev.cel.common.types.StructTypeReference;
2727
import dev.cel.common.values.ProtoLiteCelValueConverter.MessageFields;
28+
import dev.cel.protobuf.CelLiteDescriptor.FieldLiteDescriptor;
2829
import java.io.IOException;
2930
import java.util.Optional;
31+
import org.jspecify.annotations.Nullable;
3032

3133
/**
3234
* ProtoMessageLiteValue is a struct value with protobuf support for {@link MessageLite}.
@@ -35,10 +37,23 @@
3537
*
3638
* <p>If the codebase has access to full protobuf messages with descriptors, use {@code
3739
* ProtoMessageValue} instead.
40+
*
41+
* <p>Implements {@link OptimizedSelectable} so that select chains can address fields by number:
42+
*
43+
* <ul>
44+
* <li><b>Field renames:</b> If a protobuf field is renamed in schema after an AST was compiled,
45+
* resolving by {@link SelectField#fieldNumber()} maps the number to the runtime descriptor's
46+
* current field name, preventing {@code CelAttributeNotFoundException}.
47+
* <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}.
51+
* </ul>
3852
*/
3953
@AutoValue
4054
@Immutable
41-
public abstract class ProtoMessageLiteValue extends StructValue<String, MessageLite> {
55+
public abstract class ProtoMessageLiteValue extends StructValue<String, MessageLite>
56+
implements OptimizedSelectable {
4257

4358
@Override
4459
public abstract MessageLite value();
@@ -57,12 +72,11 @@ MessageFields messageFields() {
5772
}
5873
}
5974

60-
@Internal
61-
public ImmutableMap<String, Object> fieldValues() {
75+
ImmutableMap<String, Object> fieldValues() {
6276
return messageFields().values();
6377
}
6478

65-
public ImmutableListMultimap<Integer, Object> unknownFields() {
79+
ImmutableListMultimap<Integer, Object> unknownFields() {
6680
return messageFields().unknowns();
6781
}
6882

@@ -84,11 +98,59 @@ public Optional<Object> find(String field) {
8498
.map(value -> protoLiteCelValueConverter().toRuntimeValue(fieldValue));
8599
}
86100

101+
@Override
102+
public Object selectByFieldNumber(SelectField field) {
103+
FieldLiteDescriptor fd = findFieldDescriptor(field);
104+
Object known = findKnownFieldValue(fd);
105+
if (known != null) {
106+
return protoLiteCelValueConverter().toRuntimeValue(known);
107+
}
108+
return RawProtoMessageLiteValue.selectWireOrDefault(
109+
field, fd, unknownFields().get(field.fieldNumber()), protoLiteCelValueConverter());
110+
}
111+
112+
@Override
113+
public boolean hasFieldByNumber(SelectField field) {
114+
FieldLiteDescriptor fd = findFieldDescriptor(field);
115+
if (findKnownFieldValue(fd) != null) {
116+
return true;
117+
}
118+
return RawProtoMessageLiteValue.isPresentInWire(
119+
field, fd, unknownFields().get(field.fieldNumber()));
120+
}
121+
122+
@Override
123+
public Optional<Object> findByFieldNumber(SelectField field) {
124+
FieldLiteDescriptor fd = findFieldDescriptor(field);
125+
Object known = findKnownFieldValue(fd);
126+
if (known != null) {
127+
return Optional.of(protoLiteCelValueConverter().toRuntimeValue(known));
128+
}
129+
return RawProtoMessageLiteValue.navigateWire(
130+
field, fd, unknownFields().get(field.fieldNumber()), protoLiteCelValueConverter());
131+
}
132+
133+
private @Nullable FieldLiteDescriptor findFieldDescriptor(SelectField field) {
134+
return protoLiteCelValueConverter()
135+
.findFieldDescriptor(celType().name(), field.fieldNumber())
136+
.orElse(null);
137+
}
138+
139+
private @Nullable Object findKnownFieldValue(@Nullable FieldLiteDescriptor fieldDescriptor) {
140+
if (fieldDescriptor == null) {
141+
return null;
142+
}
143+
return fieldValues().get(fieldDescriptor.getFieldName());
144+
}
145+
87146
public static ProtoMessageLiteValue create(
88147
MessageLite value, String typeName, ProtoLiteCelValueConverter protoLiteCelValueConverter) {
89-
Preconditions.checkNotNull(value);
90-
Preconditions.checkNotNull(typeName);
148+
checkNotNull(value);
149+
checkNotNull(typeName);
150+
checkNotNull(protoLiteCelValueConverter);
91151
return new AutoValue_ProtoMessageLiteValue(
92152
value, StructTypeReference.create(typeName), protoLiteCelValueConverter);
93153
}
154+
155+
ProtoMessageLiteValue() {}
94156
}

0 commit comments

Comments
 (0)