1818
1919import com .google .auto .value .AutoValue ;
2020import com .google .auto .value .extension .memoized .Memoized ;
21- import com .google .common .collect .ImmutableListMultimap ;
2221import com .google .common .collect .ImmutableMap ;
2322import com .google .errorprone .annotations .Immutable ;
23+ import com .google .protobuf .ByteString ;
2424import com .google .protobuf .MessageLite ;
2525import dev .cel .common .types .CelType ;
2626import dev .cel .common .types .StructTypeReference ;
27- import dev .cel .common .values .ProtoLiteCelValueConverter .MessageFields ;
2827import dev .cel .protobuf .CelLiteDescriptor .FieldLiteDescriptor ;
2928import java .io .IOException ;
29+ import java .util .Objects ;
3030import java .util .Optional ;
3131import org .jspecify .annotations .Nullable ;
3232
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
5560abstract 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 () {}
0 commit comments