Skip to content

Commit 66bfaf9

Browse files
l46kokcopybara-github
authored andcommitted
Read in-memory lite messages through generated getters
PiperOrigin-RevId: 999013976
1 parent cb53abe commit 66bfaf9

19 files changed

Lines changed: 823 additions & 81 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
@@ -329,6 +329,7 @@ java_library(
329329
deps = [
330330
":base_proto_cel_value_converter",
331331
":optimized_selectable",
332+
":preadapted_list",
332333
":select_field",
333334
":values",
334335
"//:auto_value",
@@ -360,6 +361,7 @@ cel_android_library(
360361
deps = [
361362
":base_proto_cel_value_converter_android",
362363
":optimized_selectable_android",
364+
":preadapted_list_android",
363365
":select_field_android",
364366
":values_android",
365367
"//:auto_value",

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

Lines changed: 137 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.protobuf.ByteString;
2626
import com.google.protobuf.CodedInputStream;
2727
import com.google.protobuf.ExtensionRegistryLite;
28+
import com.google.protobuf.Internal.EnumLite;
2829
import com.google.protobuf.InvalidProtocolBufferException;
2930
import com.google.protobuf.MessageLite;
3031
import com.google.protobuf.WireFormat;
@@ -163,16 +164,28 @@ Object getDefaultCelValue(FieldLiteDescriptor fieldDescriptor) {
163164
return toRuntimeValue(getDefaultValue(fieldDescriptor));
164165
}
165166

166-
private Optional<FieldLiteDescriptor> findFieldDescriptor(String protoTypeName, int fieldNumber) {
167-
return descriptorPool
168-
.findDescriptor(protoTypeName)
169-
.flatMap(desc -> desc.findByFieldNumber(fieldNumber));
167+
// A selected field's default value: the one in the SelectField, else its type's.
168+
Object getDefaultCelValue(SelectField field, FieldLiteDescriptor fieldDescriptor) {
169+
return field.defaultValue() != null
170+
? field.defaultValue()
171+
: getDefaultCelValue(fieldDescriptor);
172+
}
173+
174+
@Nullable FieldLiteDescriptor findFieldDescriptor(String protoTypeName, int fieldNumber) {
175+
MessageLiteDescriptor descriptor = descriptorPool.findDescriptor(protoTypeName).orElse(null);
176+
return descriptor == null ? null : descriptor.findByFieldNumber(fieldNumber);
170177
}
171178

172-
Optional<FieldLiteDescriptor> findFieldDescriptor(String protoTypeName, String fieldName) {
179+
@Nullable FieldLiteDescriptor findFieldDescriptor(String protoTypeName, String fieldName) {
180+
MessageLiteDescriptor descriptor = descriptorPool.findDescriptor(protoTypeName).orElse(null);
181+
return descriptor == null ? null : descriptor.findByFieldName(fieldName);
182+
}
183+
184+
@Nullable MessageLiteDescriptor findDescriptorWithGetters(String protoTypeName) {
173185
return descriptorPool
174186
.findDescriptor(protoTypeName)
175-
.flatMap(desc -> desc.findByFieldName(fieldName));
187+
.filter(MessageLiteDescriptor::hasGetters)
188+
.orElse(null);
176189
}
177190

178191
MessageLite parseMessageLite(ByteString bytes, String protoTypeName) {
@@ -316,15 +329,8 @@ private Map.Entry<Object, Object> readSingleMapEntry(
316329
return scanField(bytes, protoTypeName, fieldDescriptor, null, null);
317330
}
318331

319-
/** Reads {@code field}, by its own type information if {@code fieldDescriptor} is null. */
320-
private @Nullable Object readField(
321-
ByteString bytes,
322-
String protoTypeName,
323-
@Nullable FieldLiteDescriptor fieldDescriptor,
324-
SelectField field) {
325-
if (fieldDescriptor != null) {
326-
return readField(bytes, protoTypeName, fieldDescriptor);
327-
}
332+
/** Reads {@code field} using the type information it carries. */
333+
private @Nullable Object readField(ByteString bytes, String protoTypeName, SelectField field) {
328334
SelectField.MapEntrySpec mapEntrySpec = field.mapEntrySpec();
329335
if (mapEntrySpec == null) {
330336
return readField(bytes, protoTypeName, newFieldDescriptor(field));
@@ -355,6 +361,9 @@ private Map.Entry<Object, Object> readSingleMapEntry(
355361
FieldLiteDescriptor fieldDescriptor,
356362
@Nullable FieldLiteDescriptor keyDescriptor,
357363
@Nullable FieldLiteDescriptor valueDescriptor) {
364+
if (fieldDescriptor.getProtoFieldType().equals(FieldLiteDescriptor.Type.GROUP)) {
365+
throw new UnsupportedOperationException("Groups are not supported");
366+
}
358367
CodedInputStream inputStream = bytes.newCodedInput();
359368
// Submessages alias bytes; unwrapping parses or copies them, so no view outlives evaluation.
360369
inputStream.enableAliasing(true);
@@ -400,40 +409,31 @@ private Map.Entry<Object, Object> readSingleMapEntry(
400409
}
401410

402411
/**
403-
* Selects {@code field} from {@code bytes}, decoding it by the type information in {@code field}
404-
* for fields missing from the descriptor pool. Returns the field's default value if it's absent.
412+
* Selects {@code field}, which its message's descriptor doesn't have, from {@code bytes} by the
413+
* type information it carries. Returns the field's default value if it's absent.
405414
*/
406415
Object selectByFieldNumber(ByteString bytes, String protoTypeName, SelectField field) {
407-
FieldLiteDescriptor fieldDescriptor =
408-
findFieldDescriptor(protoTypeName, field.fieldNumber()).orElse(null);
409-
Object fieldValue = readField(bytes, protoTypeName, fieldDescriptor, field);
410-
if (fieldValue != null) {
411-
return fieldValue;
412-
}
413-
if (field.defaultValue() != null) {
414-
return field.defaultValue();
415-
}
416-
return getDefaultCelValue(
417-
fieldDescriptor != null ? fieldDescriptor : newFieldDescriptor(field));
416+
Object fieldValue = readField(bytes, protoTypeName, field);
417+
return fieldValue != null ? fieldValue : getDefaultCelValue(field, newFieldDescriptor(field));
418418
}
419419

420420
/**
421-
* Finds {@code field} in {@code bytes}, decoding it by the type information in {@code field} for
422-
* fields missing from the descriptor pool. A field without a type code is decoded as a message.
421+
* Finds {@code field}, which its message's descriptor doesn't have, in {@code bytes} by the type
422+
* information it carries. A field without a type code is decoded as a message.
423423
*/
424424
Optional<Object> findByFieldNumber(ByteString bytes, String protoTypeName, SelectField field) {
425-
FieldLiteDescriptor fieldDescriptor =
426-
findFieldDescriptor(protoTypeName, field.fieldNumber()).orElse(null);
427-
return Optional.ofNullable(readField(bytes, protoTypeName, fieldDescriptor, field));
425+
return Optional.ofNullable(readField(bytes, protoTypeName, field));
428426
}
429427

430428
/** Returns whether {@code field} is present in {@code bytes}. */
431429
boolean hasFieldByNumber(ByteString bytes, String protoTypeName, SelectField field) {
432-
FieldLiteDescriptor fieldDescriptor =
433-
findFieldDescriptor(protoTypeName, field.fieldNumber()).orElse(null);
430+
FieldLiteDescriptor fieldDescriptor = findFieldDescriptor(protoTypeName, field.fieldNumber());
434431
if (fieldDescriptor == null) {
435432
fieldDescriptor = newFieldDescriptor(field);
436433
}
434+
if (fieldDescriptor.getProtoFieldType().equals(FieldLiteDescriptor.Type.GROUP)) {
435+
throw new UnsupportedOperationException("Groups are not supported");
436+
}
437437
boolean isPackableList =
438438
fieldDescriptor.getEncodingType().equals(EncodingType.LIST) && isPackable(fieldDescriptor);
439439
CodedInputStream inputStream = bytes.newCodedInput();
@@ -464,6 +464,108 @@ boolean hasFieldByNumber(ByteString bytes, String protoTypeName, SelectField fie
464464
return false;
465465
}
466466

467+
/**
468+
* Reads {@code fieldDescriptor} of {@code message} with its generated getter rather than from
469+
* wire bytes: its CEL value, or null if it's absent.
470+
*/
471+
@Nullable Object readFieldByGetter(
472+
MessageLite message, MessageLiteDescriptor descriptor, FieldLiteDescriptor fieldDescriptor) {
473+
Object value = readGetter(message, descriptor, fieldDescriptor.getFieldNumber());
474+
if (value == null) {
475+
return null;
476+
}
477+
switch (fieldDescriptor.getEncodingType()) {
478+
case LIST:
479+
List<?> list = (List<?>) value;
480+
switch (fieldDescriptor.getProtoFieldType()) {
481+
case INT64:
482+
case SINT64:
483+
case SFIXED64:
484+
case DOUBLE:
485+
case BOOL:
486+
case STRING:
487+
// These elements are their own CEL values, so the immutable list needn't be copied.
488+
return CelPreAdaptedList.wrap(list);
489+
default:
490+
break;
491+
}
492+
ImmutableList.Builder<Object> listBuilder =
493+
ImmutableList.builderWithExpectedSize(list.size());
494+
for (int i = 0; i < list.size(); i++) {
495+
listBuilder.add(fromGetterValue(list.get(i), fieldDescriptor));
496+
}
497+
return listBuilder.build();
498+
case MAP:
499+
Map<?, ?> map = (Map<?, ?>) value;
500+
MessageLiteDescriptor entryDescriptor =
501+
descriptorPool.getDescriptorOrThrow(fieldDescriptor.getFieldProtoTypeName());
502+
FieldLiteDescriptor keyDescriptor =
503+
entryDescriptor.getByFieldNumberOrThrow(MAP_KEY_FIELD_NUMBER);
504+
FieldLiteDescriptor valueDescriptor =
505+
entryDescriptor.getByFieldNumberOrThrow(MAP_VALUE_FIELD_NUMBER);
506+
ImmutableMap.Builder<Object, Object> mapBuilder =
507+
ImmutableMap.builderWithExpectedSize(map.size());
508+
for (Map.Entry<?, ?> entry : map.entrySet()) {
509+
mapBuilder.put(
510+
fromGetterValue(entry.getKey(), keyDescriptor),
511+
fromGetterValue(entry.getValue(), valueDescriptor));
512+
}
513+
return mapBuilder.buildOrThrow();
514+
case SINGULAR:
515+
return fromGetterValue(value, fieldDescriptor);
516+
}
517+
throw new IllegalStateException(
518+
"Unexpected encoding type: " + fieldDescriptor.getEncodingType());
519+
}
520+
521+
/** Like {@link #hasFieldByNumber}, through the field's generated getter. */
522+
boolean hasByGetter(MessageLite message, MessageLiteDescriptor descriptor, SelectField field) {
523+
return readGetter(message, descriptor, field.fieldNumber()) != null;
524+
}
525+
526+
// A field's value as its getter returns it, or null if it's unset or an empty repeated or map.
527+
private static @Nullable Object readGetter(
528+
MessageLite message, MessageLiteDescriptor descriptor, int fieldNumber) {
529+
Object value = descriptor.getFieldValue(message, fieldNumber);
530+
boolean isEmpty =
531+
(value instanceof List && ((List<?>) value).isEmpty())
532+
|| (value instanceof Map && ((Map<?, ?>) value).isEmpty());
533+
return isEmpty ? null : value;
534+
}
535+
536+
// Converts a field's (or element's) Java value as returned by its getter to its CEL value.
537+
private Object fromGetterValue(Object value, FieldLiteDescriptor fieldDescriptor) {
538+
switch (fieldDescriptor.getProtoFieldType()) {
539+
case UINT32:
540+
case FIXED32:
541+
return UnsignedLong.fromLongBits(Integer.toUnsignedLong((Integer) value));
542+
case UINT64:
543+
case FIXED64:
544+
return UnsignedLong.fromLongBits((Long) value);
545+
case ENUM:
546+
// Open enums are read as their numbers; closed ones as enum constants.
547+
return value instanceof Integer
548+
? ((Integer) value).longValue()
549+
: (long) ((EnumLite) value).getNumber();
550+
case MESSAGE:
551+
MessageLite message = (MessageLite) value;
552+
String protoTypeName = fieldDescriptor.getFieldProtoTypeName();
553+
MessageLiteDescriptor descriptor =
554+
descriptorPool.findDescriptor(protoTypeName).orElse(null);
555+
if (descriptor == null) {
556+
// Without a descriptor, only this submessage is serialized, rather than its whole root.
557+
return RawProtoMessageLiteValue.create(message.toByteString(), protoTypeName, this);
558+
}
559+
if (descriptorPool.findDescriptor(message).orElse(null) != descriptor) {
560+
// The pool decodes this type into another class, such as an older version of it.
561+
return readMessageField(message.toByteString(), protoTypeName);
562+
}
563+
return toRuntimeValue(message, descriptor);
564+
default:
565+
return super.toRuntimeValue(value);
566+
}
567+
}
568+
467569
/** Describes {@code field} by the type information it carries, built once and kept on it. */
468570
private static FieldLiteDescriptor newFieldDescriptor(SelectField field) {
469571
Object cached = field.liteFieldDescriptor();

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

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import dev.cel.common.types.CelType;
2626
import dev.cel.common.types.StructTypeReference;
2727
import dev.cel.protobuf.CelLiteDescriptor.FieldLiteDescriptor;
28+
import dev.cel.protobuf.CelLiteDescriptor.MessageLiteDescriptor;
2829
import java.util.Objects;
2930
import java.util.Optional;
3031
import org.jspecify.annotations.Nullable;
@@ -40,7 +41,8 @@
4041
* <p>An instance is backed by either a materialized {@link #rawValue()} or unparsed {@link
4142
* #wireBytes()} (exactly one is non-null). Wire-backed instances decode selected fields directly
4243
* from {@link #wireBytes()} and lazily parse the full {@link MessageLite} only if {@link #value()}
43-
* is invoked.
44+
* is invoked. Materialized instances read fields through their descriptor's generated getters if it
45+
* has them, and otherwise, as for fields it doesn't know, decode them from their serialized bytes.
4446
*
4547
* <p>Implements {@link OptimizedSelectable} so that select chains can address fields by number:
4648
*
@@ -120,7 +122,7 @@ public final int hashCode() {
120122

121123
@Override
122124
public Object select(String field) {
123-
FieldLiteDescriptor fd = findFieldDescriptor(field).orElse(null);
125+
FieldLiteDescriptor fd = findFieldDescriptor(field);
124126
if (fd == null) {
125127
throw CelAttributeNotFoundException.of(
126128
String.format("field '%s' is not declared in message '%s'", field, celType().name()));
@@ -131,35 +133,65 @@ public Object select(String field) {
131133

132134
@Override
133135
public Optional<Object> find(String field) {
134-
FieldLiteDescriptor fd = findFieldDescriptor(field).orElse(null);
135136
// Per SelectableValue#find, a field that doesn't exist is reported as absent.
136-
return Optional.ofNullable(fd).map(value -> readField(value));
137+
return Optional.ofNullable(findFieldDescriptor(field)).map(this::readField);
137138
}
138139

139140
@Override
140141
public Object selectByFieldNumber(SelectField field) {
142+
FieldLiteDescriptor fd = findFieldDescriptor(field.fieldNumber());
143+
if (fd != null) {
144+
Object value = readField(fd);
145+
return value != null ? value : protoLiteCelValueConverter().getDefaultCelValue(field, fd);
146+
}
141147
return protoLiteCelValueConverter()
142148
.selectByFieldNumber(toByteString(), celType().name(), field);
143149
}
144150

145151
@Override
146152
public boolean hasFieldByNumber(SelectField field) {
153+
MessageLite message = rawValue();
154+
if (message != null && findFieldDescriptor(field.fieldNumber()) != null) {
155+
MessageLiteDescriptor descriptor = findDescriptorWithGetters();
156+
if (descriptor != null) {
157+
return protoLiteCelValueConverter().hasByGetter(message, descriptor, field);
158+
}
159+
}
147160
return protoLiteCelValueConverter().hasFieldByNumber(toByteString(), celType().name(), field);
148161
}
149162

150163
@Override
151164
public Optional<Object> findByFieldNumber(SelectField field) {
165+
FieldLiteDescriptor fd = findFieldDescriptor(field.fieldNumber());
166+
if (fd != null) {
167+
return Optional.ofNullable(readField(fd));
168+
}
152169
return protoLiteCelValueConverter().findByFieldNumber(toByteString(), celType().name(), field);
153170
}
154171

155172
private @Nullable Object readField(FieldLiteDescriptor fd) {
173+
MessageLite message = rawValue();
174+
if (message != null) {
175+
MessageLiteDescriptor descriptor = findDescriptorWithGetters();
176+
if (descriptor != null) {
177+
return protoLiteCelValueConverter().readFieldByGetter(message, descriptor, fd);
178+
}
179+
}
156180
return protoLiteCelValueConverter().readField(toByteString(), celType().name(), fd);
157181
}
158182

159-
private Optional<FieldLiteDescriptor> findFieldDescriptor(String fieldName) {
183+
private @Nullable FieldLiteDescriptor findFieldDescriptor(String fieldName) {
160184
return protoLiteCelValueConverter().findFieldDescriptor(celType().name(), fieldName);
161185
}
162186

187+
private @Nullable FieldLiteDescriptor findFieldDescriptor(int fieldNumber) {
188+
return protoLiteCelValueConverter().findFieldDescriptor(celType().name(), fieldNumber);
189+
}
190+
191+
private @Nullable MessageLiteDescriptor findDescriptorWithGetters() {
192+
return protoLiteCelValueConverter().findDescriptorWithGetters(celType().name());
193+
}
194+
163195
static ProtoMessageLiteValue create(
164196
MessageLite value, String typeName, ProtoLiteCelValueConverter protoLiteCelValueConverter) {
165197
checkNotNull(value);

0 commit comments

Comments
 (0)