Skip to content

Commit 0706aa2

Browse files
l46kokcopybara-github
authored andcommitted
No public description
PiperOrigin-RevId: 996749917
1 parent eb9055b commit 0706aa2

3 files changed

Lines changed: 101 additions & 146 deletions

File tree

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,6 @@ cel_android_library(
372372
"//common/values:cel_byte_string",
373373
"//protobuf:cel_lite_descriptor",
374374
"@maven//:com_google_errorprone_error_prone_annotations",
375-
"@maven//:com_google_guava_guava",
376375
"@maven//:org_jspecify_jspecify",
377376
"@maven_android//:com_google_guava_guava",
378377
"@maven_android//:com_google_protobuf_protobuf_javalite",

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

Lines changed: 64 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import static com.google.common.base.Preconditions.checkNotNull;
1818

19-
import com.google.common.annotations.VisibleForTesting;
2019
import com.google.common.base.Defaults;
2120
import com.google.common.collect.ImmutableList;
2221
import com.google.common.collect.ImmutableMap;
@@ -59,8 +58,6 @@
5958
@Immutable
6059
@Internal
6160
public final class ProtoLiteCelValueConverter extends BaseProtoCelValueConverter {
62-
private static final String MAP_KEY_FIELD_NAME = "key";
63-
private static final String MAP_VALUE_FIELD_NAME = "value";
6461
private static final int MAP_KEY_FIELD_NUMBER = 1;
6562
private static final int MAP_VALUE_FIELD_NUMBER = 2;
6663

@@ -316,19 +313,52 @@ private Map.Entry<Object, Object> readSingleMapEntry(
316313
*/
317314
@Nullable Object readSingleField(ByteString bytes, FieldLiteDescriptor fieldDescriptor)
318315
throws IOException {
316+
return scanField(bytes, fieldDescriptor, null, null);
317+
}
318+
319+
/** Scans for {@code fieldDescriptor}; null map entry descriptors are looked up in the pool. */
320+
private @Nullable Object scanField(
321+
ByteString bytes,
322+
FieldLiteDescriptor fieldDescriptor,
323+
@Nullable FieldLiteDescriptor keyDescriptor,
324+
@Nullable FieldLiteDescriptor valueDescriptor)
325+
throws IOException {
319326
CodedInputStream inputStream = bytes.newCodedInput();
320327
int targetFieldNumber = fieldDescriptor.getFieldNumber();
321328
Object fieldValue = null;
322329
for (int tag = inputStream.readTag(); tag != 0; tag = inputStream.readTag()) {
323-
int fieldNumber = WireFormat.getTagFieldNumber(tag);
324-
if (fieldNumber != targetFieldNumber) {
330+
if (WireFormat.getTagFieldNumber(tag) != targetFieldNumber) {
325331
skipWireField(tag, inputStream);
326332
continue;
327333
}
328334
int tagWireType = WireFormat.getTagWireType(tag);
329-
fieldValue = readFieldValue(tagWireType, inputStream, fieldDescriptor, fieldValue);
335+
switch (fieldDescriptor.getEncodingType()) {
336+
case SINGULAR:
337+
fieldValue = readSingularField(tagWireType, inputStream, fieldDescriptor, fieldValue);
338+
break;
339+
case LIST:
340+
fieldValue = readRepeatedField(tagWireType, inputStream, fieldDescriptor, fieldValue);
341+
break;
342+
case MAP:
343+
if (keyDescriptor == null) {
344+
// Looked up at the first entry since an absent map needs no entry descriptor.
345+
MessageLiteDescriptor entryDescriptor =
346+
descriptorPool.getDescriptorOrThrow(fieldDescriptor.getFieldProtoTypeName());
347+
keyDescriptor = entryDescriptor.getByFieldNumberOrThrow(MAP_KEY_FIELD_NUMBER);
348+
valueDescriptor = entryDescriptor.getByFieldNumberOrThrow(MAP_VALUE_FIELD_NUMBER);
349+
}
350+
fieldValue =
351+
readMapField(
352+
tagWireType,
353+
inputStream,
354+
fieldDescriptor,
355+
keyDescriptor,
356+
valueDescriptor,
357+
fieldValue);
358+
break;
359+
}
330360
}
331-
return fieldValue == null ? null : resolveFieldValue(finalizeFieldValue(fieldValue));
361+
return fieldValue == null ? null : finalizeFieldValue(fieldValue);
332362
}
333363

334364
boolean hasSingleField(ByteString bytes, FieldLiteDescriptor fieldDescriptor) throws IOException {
@@ -394,71 +424,37 @@ boolean hasFieldByNumber(ByteString bytes, SelectField field) throws IOException
394424

395425
private @Nullable Object readFieldByNumber(ByteString bytes, SelectField field)
396426
throws IOException {
397-
FieldLiteDescriptor fieldDescriptor = newFieldDescriptor(field);
398427
SelectField.MapEntrySpec mapEntrySpec = field.mapEntrySpec();
399428
if (mapEntrySpec == null) {
400-
return readSingleField(bytes, fieldDescriptor);
429+
return readSingleField(bytes, newFieldDescriptor(field));
401430
}
402431
// The map entry type has no descriptor either, so its key and value are described by the spec.
403-
FieldLiteDescriptor keyDescriptor =
432+
return scanField(
433+
bytes,
434+
newFieldDescriptor(field),
404435
newFieldDescriptor(
405-
MAP_KEY_FIELD_NUMBER,
406-
MAP_KEY_FIELD_NAME,
407-
EncodingType.SINGULAR,
408-
mapEntrySpec.keyTypeCode(),
409-
/* protoTypeName= */ "");
410-
FieldLiteDescriptor valueDescriptor =
436+
MAP_KEY_FIELD_NUMBER, "key", EncodingType.SINGULAR, mapEntrySpec.keyTypeCode(), ""),
411437
newFieldDescriptor(
412438
MAP_VALUE_FIELD_NUMBER,
413-
MAP_VALUE_FIELD_NAME,
439+
"value",
414440
EncodingType.SINGULAR,
415441
mapEntrySpec.valueTypeCode(),
416-
field.protoTypeName());
417-
CodedInputStream inputStream = bytes.newCodedInput();
418-
Map<Object, Object> mapValues = null;
419-
for (int tag = inputStream.readTag(); tag != 0; tag = inputStream.readTag()) {
420-
if (WireFormat.getTagFieldNumber(tag) != field.fieldNumber()) {
421-
skipWireField(tag, inputStream);
422-
continue;
423-
}
424-
mapValues =
425-
readMapField(
426-
WireFormat.getTagWireType(tag),
427-
inputStream,
428-
fieldDescriptor,
429-
keyDescriptor,
430-
valueDescriptor,
431-
mapValues);
432-
}
433-
return mapValues == null ? null : resolveFieldValue(finalizeFieldValue(mapValues));
442+
field.protoTypeName()));
434443
}
435444

436445
/** Describes {@code field} by the type information it carries. */
437446
private static FieldLiteDescriptor newFieldDescriptor(SelectField field) {
438-
if (field.mapEntrySpec() != null) {
439-
// SelectField doesn't name the map entry type; its protoTypeName() is the map's value type.
440-
return newFieldDescriptor(
441-
field.fieldNumber(),
442-
field.fieldName(),
443-
EncodingType.MAP,
444-
SelectField.MESSAGE_TYPE_CODE,
445-
/* protoTypeName= */ "");
446-
}
447-
if (field.typeCode() == SelectField.NO_TYPE_CODE) {
448-
// Only presence tests omit the type code. Their presence check doesn't depend on the type,
449-
// and the fields they navigate through are always messages.
450-
return newFieldDescriptor(
451-
field.fieldNumber(),
452-
field.fieldName(),
453-
EncodingType.SINGULAR,
454-
SelectField.MESSAGE_TYPE_CODE,
455-
/* protoTypeName= */ "");
456-
}
447+
// Only presence tests omit the type code: their presence check doesn't depend on the type, and
448+
// the fields they navigate through are messages, as are map entries.
449+
boolean isMap = field.mapEntrySpec() != null;
450+
boolean isMessage = isMap || field.typeCode() == SelectField.NO_TYPE_CODE;
457451
return newFieldDescriptor(
458452
field.fieldNumber(),
459453
field.fieldName(),
460-
field.defaultValue() instanceof List ? EncodingType.LIST : EncodingType.SINGULAR,
461-
field.typeCode(),
454+
isMap
455+
? EncodingType.MAP
456+
: (field.defaultValue() instanceof List ? EncodingType.LIST : EncodingType.SINGULAR),
457+
isMessage ? SelectField.MESSAGE_TYPE_CODE : field.typeCode(),
462458
field.protoTypeName());
463459
}
464460

@@ -480,59 +476,29 @@ private static FieldLiteDescriptor newFieldDescriptor(
480476
protoTypeName);
481477
}
482478

483-
/** Returns the CEL value of a scanned field, completing any conversion that was deferred. */
484-
private Object resolveFieldValue(Object fieldValue) {
485-
if (fieldValue instanceof DeferredConversion) {
486-
return toRuntimeValue(((DeferredConversion) fieldValue).value);
487-
}
488-
return fieldValue;
489-
}
490-
491479
/**
492480
* Converts a value accumulated while scanning a field into its final immutable form.
493481
*
494482
* <p>Repeated and map fields accumulate into mutable containers, which are copied into immutable
495483
* ones. Well-known types other than FieldMask (see {@link #isStructLike}) are kept as parsed
496-
* {@link MessageLite}s until the scan completes so that split occurrences can be merged, and
497-
* values holding them are wrapped in a {@link DeferredConversion}. All other messages are wrapped
498-
* as CEL values as soon as they are read.
484+
* {@link MessageLite}s until the scan completes, so that split occurrences can be merged and map
485+
* values replaced by a repeated key are never converted. All other messages are wrapped as CEL
486+
* values as soon as they are read.
499487
*/
500-
private static Object finalizeFieldValue(Object accumulatedValue) {
488+
private Object finalizeFieldValue(Object accumulatedValue) {
501489
if (accumulatedValue instanceof List) {
502490
ImmutableList<?> list = ImmutableList.copyOf((List<?>) accumulatedValue);
503-
return Iterables.any(list, MessageLite.class::isInstance)
504-
? new DeferredConversion(list)
505-
: list;
491+
return Iterables.any(list, MessageLite.class::isInstance) ? toRuntimeValue(list) : list;
506492
}
507493
if (accumulatedValue instanceof Map) {
508494
ImmutableMap<?, ?> map = ImmutableMap.copyOf((Map<?, ?>) accumulatedValue);
509-
return Iterables.any(map.values(), MessageLite.class::isInstance)
510-
? new DeferredConversion(map)
511-
: map;
495+
return Iterables.any(map.values(), MessageLite.class::isInstance) ? toRuntimeValue(map) : map;
512496
}
513497
return accumulatedValue instanceof MessageLite
514-
? new DeferredConversion(accumulatedValue)
498+
? toRuntimeValue(accumulatedValue)
515499
: accumulatedValue;
516500
}
517501

518-
private @Nullable Object readFieldValue(
519-
int tagWireType,
520-
CodedInputStream inputStream,
521-
FieldLiteDescriptor fieldDescriptor,
522-
@Nullable Object existingValue)
523-
throws IOException {
524-
EncodingType encodingType = fieldDescriptor.getEncodingType();
525-
switch (encodingType) {
526-
case SINGULAR:
527-
return readSingularField(tagWireType, inputStream, fieldDescriptor, existingValue);
528-
case LIST:
529-
return readRepeatedField(tagWireType, inputStream, fieldDescriptor, existingValue);
530-
case MAP:
531-
return readMapField(tagWireType, inputStream, fieldDescriptor, existingValue);
532-
}
533-
throw new IllegalStateException("Unexpected encoding type: " + encodingType);
534-
}
535-
536502
private Object readSingularField(
537503
int tagWireType,
538504
CodedInputStream inputStream,
@@ -589,23 +555,6 @@ private Object readSingularField(
589555
return repeatedValues;
590556
}
591557

592-
private Map<Object, Object> readMapField(
593-
int tagWireType,
594-
CodedInputStream inputStream,
595-
FieldLiteDescriptor fieldDescriptor,
596-
@Nullable Object existingValue)
597-
throws IOException {
598-
MessageLiteDescriptor entryDescriptor =
599-
descriptorPool.getDescriptorOrThrow(fieldDescriptor.getFieldProtoTypeName());
600-
return readMapField(
601-
tagWireType,
602-
inputStream,
603-
fieldDescriptor,
604-
entryDescriptor.getByFieldNameOrThrow(MAP_KEY_FIELD_NAME),
605-
entryDescriptor.getByFieldNameOrThrow(MAP_VALUE_FIELD_NAME),
606-
existingValue);
607-
}
608-
609558
// Safe because MAP fields only ever store a LinkedHashMap as their accumulated value.
610559
@SuppressWarnings("unchecked")
611560
private Map<Object, Object> readMapField(
@@ -654,34 +603,13 @@ private static boolean isPackable(FieldLiteDescriptor fieldDescriptor) {
654603
return fieldDescriptor.getProtoFieldType().toWireFormatFieldType().isPackable();
655604
}
656605

657-
@VisibleForTesting
658-
static void skipWireField(int tag, CodedInputStream inputStream) throws IOException {
606+
private static void skipWireField(int tag, CodedInputStream inputStream) throws IOException {
659607
int tagWireType = WireFormat.getTagWireType(tag);
660-
switch (tagWireType) {
661-
case WireFormat.WIRETYPE_VARINT:
662-
case WireFormat.WIRETYPE_FIXED64:
663-
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
664-
case WireFormat.WIRETYPE_FIXED32:
665-
inputStream.skipField(tag);
666-
return;
667-
case WireFormat.WIRETYPE_START_GROUP:
668-
case WireFormat.WIRETYPE_END_GROUP:
669-
throw new UnsupportedOperationException("Groups are not supported");
670-
default:
671-
throw new IllegalArgumentException("Unknown wire type: " + tagWireType);
672-
}
673-
}
674-
675-
/**
676-
* A field value holding well-known type messages, whose conversion to CEL values is deferred
677-
* until {@link #resolveFieldValue}.
678-
*/
679-
private static final class DeferredConversion {
680-
private final Object value;
681-
682-
private DeferredConversion(Object value) {
683-
this.value = checkNotNull(value);
608+
if (tagWireType == WireFormat.WIRETYPE_START_GROUP
609+
|| tagWireType == WireFormat.WIRETYPE_END_GROUP) {
610+
throw new UnsupportedOperationException("Groups are not supported");
684611
}
612+
inputStream.skipField(tag);
685613
}
686614

687615
private ProtoLiteCelValueConverter(CelLiteDescriptorPool celLiteDescriptorPool) {

‎common/src/test/java/dev/cel/common/values/ProtoLiteCelValueConverterTest.java‎

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import com.google.protobuf.Timestamp;
3939
import com.google.protobuf.UInt32Value;
4040
import com.google.protobuf.UInt64Value;
41-
import com.google.protobuf.WireFormat;
4241
import com.google.testing.junit.testparameterinjector.TestParameter;
4342
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
4443
import dev.cel.common.internal.CelLiteDescriptorPool;
@@ -539,6 +538,29 @@ public void readSingleField_skipsOtherFieldsAndDecodesTarget(
539538
assertThat(result).isEqualTo(testCase.expected);
540539
}
541540

541+
@Test
542+
public void readSingleField_repeatedMapKey_convertsOnlyFinalValue() throws Exception {
543+
// The first value is out of Timestamp's range, so converting it would throw.
544+
ByteString bytes =
545+
TestAllTypes.newBuilder()
546+
.putMapStringTimestamp("k", Timestamp.newBuilder().setSeconds(1L << 60).build())
547+
.build()
548+
.toByteString()
549+
.concat(
550+
TestAllTypes.newBuilder()
551+
.putMapStringTimestamp("k", Timestamp.newBuilder().setSeconds(100).build())
552+
.build()
553+
.toByteString());
554+
FieldLiteDescriptor fd =
555+
fieldDescriptor(
556+
"cel.expr.conformance.proto3.TestAllTypes",
557+
TestAllTypes.MAP_STRING_TIMESTAMP_FIELD_NUMBER);
558+
559+
Object result = PROTO_LITE_CEL_VALUE_CONVERTER.readSingleField(bytes, fd);
560+
561+
assertThat(result).isEqualTo(ImmutableMap.of("k", Instant.ofEpochSecond(100)));
562+
}
563+
542564
@Test
543565
public void hasSingleField_emptyBytes_returnsFalse() throws Exception {
544566
FieldLiteDescriptor singleInt64Fd =
@@ -593,15 +615,21 @@ public void hasSingleField_emptyPackedFollowedByPopulatedPacked_returnsTrue() th
593615
assertThat(result).isTrue();
594616
}
595617

618+
// Wire types 3 and 4 are START_GROUP and END_GROUP.
596619
@Test
597-
public void skipWireField_groupWireType_throwsUnsupportedOperationException() {
598-
int startGroupTag = (1 << 3) | WireFormat.WIRETYPE_START_GROUP;
599-
600-
assertThrows(
601-
UnsupportedOperationException.class,
602-
() ->
603-
ProtoLiteCelValueConverter.skipWireField(
604-
startGroupTag, ByteString.EMPTY.newCodedInput()));
620+
public void readSingleField_groupInSkippedField_throwsUnsupportedOperationException(
621+
@TestParameter({"3", "4"}) int groupWireType) {
622+
ByteString bytes = ByteString.copyFrom(new byte[] {(byte) ((1 << 3) | groupWireType)});
623+
FieldLiteDescriptor fd =
624+
fieldDescriptor(
625+
"cel.expr.conformance.proto3.TestAllTypes", TestAllTypes.SINGLE_STRING_FIELD_NUMBER);
626+
627+
UnsupportedOperationException e =
628+
assertThrows(
629+
UnsupportedOperationException.class,
630+
() -> PROTO_LITE_CEL_VALUE_CONVERTER.readSingleField(bytes, fd));
631+
632+
assertThat(e).hasMessageThat().isEqualTo("Groups are not supported");
605633
}
606634

607635
private static FieldLiteDescriptor fieldDescriptor(String messageName, int fieldNumber) {

0 commit comments

Comments
 (0)