Skip to content

Commit fc72489

Browse files
l46kokcopybara-github
authored andcommitted
Add WireMessageLite for evaluating raw messages, decode registered unknown submessage fields into MessageLite
PiperOrigin-RevId: 992414157
1 parent dab6df3 commit fc72489

9 files changed

Lines changed: 567 additions & 198 deletions

File tree

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ java_library(
320320
"ProtoLiteCelValueConverter.java",
321321
"ProtoMessageLiteValue.java",
322322
"RawProtoMessageLiteValue.java",
323+
"WireMessageLite.java",
323324
],
324325
tags = [
325326
],
@@ -350,6 +351,7 @@ cel_android_library(
350351
"ProtoLiteCelValueConverter.java",
351352
"ProtoMessageLiteValue.java",
352353
"RawProtoMessageLiteValue.java",
354+
"WireMessageLite.java",
353355
],
354356
tags = [
355357
],

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

Lines changed: 16 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import com.google.protobuf.CodedInputStream;
3030
import com.google.protobuf.ExtensionRegistryLite;
3131
import com.google.protobuf.MessageLite;
32-
import com.google.protobuf.MessageLiteOrBuilder;
3332
import com.google.protobuf.WireFormat;
3433
import dev.cel.common.annotations.Internal;
3534
import dev.cel.common.internal.CelLiteDescriptorPool;
@@ -45,7 +44,6 @@
4544
import java.util.LinkedHashMap;
4645
import java.util.List;
4746
import java.util.Map;
48-
import java.util.NoSuchElementException;
4947
import java.util.Optional;
5048
import java.util.TreeMap;
5149

@@ -174,31 +172,21 @@ Optional<FieldLiteDescriptor> findFieldDescriptor(String protoTypeName, int fiel
174172
.flatMap(desc -> desc.findByFieldNumber(fieldNumber));
175173
}
176174

177-
Optional<Object> tryDecodeWellKnownProto(ByteString bytes, String protoTypeName) {
178-
Optional<WellKnownProto> wellKnownProto = WellKnownProto.getByTypeName(protoTypeName);
179-
if (!wellKnownProto.isPresent()) {
180-
return Optional.empty();
181-
}
182-
175+
Optional<Object> tryDecodeProtoMessage(ByteString bytes, String protoTypeName) {
183176
return descriptorPool
184177
.findDescriptor(protoTypeName)
185-
.map(
186-
descriptor ->
187-
decodeWellKnownProto(bytes, protoTypeName, descriptor, wellKnownProto.get()));
178+
.map(descriptor -> decodeProtoMessage(bytes, protoTypeName, descriptor));
188179
}
189180

190-
private Object decodeWellKnownProto(
191-
ByteString bytes,
192-
String protoTypeName,
193-
MessageLiteDescriptor descriptor,
194-
WellKnownProto wellKnownProto) {
181+
private Object decodeProtoMessage(
182+
ByteString bytes, String protoTypeName, MessageLiteDescriptor descriptor) {
195183
try {
196-
MessageLite.Builder builder = descriptor.newMessageBuilder();
197-
builder.mergeFrom(bytes, ExtensionRegistryLite.getEmptyRegistry());
198-
return fromWellKnownProto(builder.build(), wellKnownProto);
184+
MessageLite.Builder builder =
185+
descriptor.newMessageBuilder().mergeFrom(bytes, ExtensionRegistryLite.getEmptyRegistry());
186+
return toRuntimeValue(builder.build(), descriptor);
199187
} catch (IOException e) {
200188
throw new IllegalArgumentException(
201-
"Failed to decode well-known proto of type: " + protoTypeName, e);
189+
"Failed to decode proto message of type: " + protoTypeName, e);
202190
}
203191
}
204192

@@ -212,35 +200,21 @@ public Object toRuntimeValue(Object value) {
212200
if (descriptor == null) {
213201
return RawProtoMessageLiteValue.create(msg.toByteString(), this);
214202
}
215-
WellKnownProto wellKnownProto =
216-
WellKnownProto.getByTypeName(descriptor.getProtoTypeName()).orElse(null);
217-
218-
if (wellKnownProto == null) {
219-
return ProtoMessageLiteValue.create(msg, descriptor.getProtoTypeName(), this);
220-
}
221-
222-
return fromWellKnownProto(msg, wellKnownProto);
203+
return toRuntimeValue(msg, descriptor);
223204
}
224205

225206
return super.toRuntimeValue(value);
226207
}
227208

228-
@Override
229-
protected Object fromWellKnownProto(MessageLiteOrBuilder msg, WellKnownProto wellKnownProto) {
230-
if (wellKnownProto == WellKnownProto.FIELD_MASK) {
231-
MessageLite message = (MessageLite) msg;
232-
MessageLiteDescriptor descriptor =
233-
descriptorPool
234-
.findDescriptor(message)
235-
.orElseThrow(
236-
() ->
237-
new NoSuchElementException(
238-
"Could not find a descriptor for message of type: "
239-
+ message.getClass().getName()));
240-
return ProtoMessageLiteValue.create(message, descriptor.getProtoTypeName(), this);
209+
private Object toRuntimeValue(MessageLite msg, MessageLiteDescriptor descriptor) {
210+
WellKnownProto wellKnownProto =
211+
WellKnownProto.getByTypeName(descriptor.getProtoTypeName()).orElse(null);
212+
213+
if (wellKnownProto == null || wellKnownProto == WellKnownProto.FIELD_MASK) {
214+
return ProtoMessageLiteValue.create(msg, descriptor.getProtoTypeName(), this);
241215
}
242216

243-
return super.fromWellKnownProto(msg, wellKnownProto);
217+
return fromWellKnownProto(msg, wellKnownProto);
244218
}
245219

246220
private Object getDefaultValue(FieldLiteDescriptor fieldDescriptor) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
*/
5353
@AutoValue
5454
@Immutable
55-
public abstract class ProtoMessageLiteValue extends StructValue<String, MessageLite>
55+
abstract class ProtoMessageLiteValue extends StructValue<String, MessageLite>
5656
implements OptimizedSelectable {
5757

5858
@Override
@@ -142,7 +142,7 @@ public Optional<Object> findByFieldNumber(SelectField field) {
142142
.orElse(null);
143143
}
144144

145-
public static ProtoMessageLiteValue create(
145+
static ProtoMessageLiteValue create(
146146
MessageLite value, String typeName, ProtoLiteCelValueConverter protoLiteCelValueConverter) {
147147
checkNotNull(value);
148148
checkNotNull(typeName);

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

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import com.google.protobuf.ByteString;
3131
import com.google.protobuf.CodedInputStream;
3232
import com.google.protobuf.WireFormat;
33-
import dev.cel.common.annotations.Internal;
3433
import dev.cel.common.exceptions.CelAttributeNotFoundException;
3534
import dev.cel.common.types.CelType;
3635
import dev.cel.common.types.StructTypeReference;
@@ -39,6 +38,7 @@
3938
import java.util.AbstractMap;
4039
import java.util.ArrayList;
4140
import java.util.List;
41+
import java.util.Locale;
4242
import java.util.Map;
4343
import java.util.Optional;
4444
import java.util.TreeMap;
@@ -49,38 +49,63 @@
4949
* client-server version skew issues where newer fields or submessages lack generated classes and
5050
* descriptors in the evaluation environment.
5151
*
52-
* <p>Rather than requiring compiled {@link MessageLite} classes or runtime schema descriptors, this
52+
* <p>Rather than requiring compiled {@code MessageLite} classes or runtime schema descriptors, this
5353
* value encapsulates the raw wire-format {@link ByteString} payload and performs classless,
5454
* reflection-free field traversal directly over wire tags via {@link CodedInputStream}.
5555
*/
5656
@AutoValue
5757
@AutoValue.CopyAnnotations
5858
@Immutable
5959
@SuppressWarnings("Immutable") // Immutable wire fields
60-
@Internal
61-
public abstract class RawProtoMessageLiteValue extends StructValue<String, RawProtoMessageLiteValue>
62-
implements OptimizedSelectable {
60+
abstract class RawProtoMessageLiteValue extends StructValue<String, WireMessageLite>
61+
implements OptimizedSelectable, WireMessageLite {
6362

6463
private static final String UNKNOWN_MESSAGE_TYPE_NAME = "cel.@unknownMessage";
6564
private static final int MAP_KEY_FIELD_NUMBER = 1;
6665
private static final int MAP_VALUE_FIELD_NUMBER = 2;
6766

68-
abstract ByteString rawWireBytes();
67+
@Override
68+
public abstract ByteString toByteString();
6969

7070
@Override
7171
public abstract CelType celType();
7272

7373
abstract ProtoLiteCelValueConverter protoLiteCelValueConverter();
7474

7575
@Override
76-
public RawProtoMessageLiteValue value() {
76+
public String protoTypeName() {
77+
return celType().name();
78+
}
79+
80+
@Override
81+
public WireMessageLite value() {
7782
return this;
7883
}
7984

85+
@Override
86+
public final boolean equals(Object other) {
87+
// TODO: Support message equality
88+
throw new UnsupportedOperationException("Message equality is not supported");
89+
}
90+
91+
@Override
92+
public final int hashCode() {
93+
throw new UnsupportedOperationException("Message equality is not supported");
94+
}
95+
96+
@Override
97+
public final String toString() {
98+
return String.format(
99+
Locale.US,
100+
"WireMessageLite{protoTypeName=%s, size=%d}",
101+
protoTypeName(),
102+
toByteString().size());
103+
}
104+
80105
@Memoized
81106
ImmutableListMultimap<Integer, Object> unknownFields() {
82107
try {
83-
CodedInputStream inputStream = rawWireBytes().newCodedInput();
108+
CodedInputStream inputStream = toByteString().newCodedInput();
84109
Multimap<Integer, Object> fields = Multimaps.newMultimap(new TreeMap<>(), ArrayList::new);
85110
for (int tag = inputStream.readTag(); tag != 0; tag = inputStream.readTag()) {
86111
int tagWireType = WireFormat.getTagWireType(tag);
@@ -96,7 +121,7 @@ ImmutableListMultimap<Integer, Object> unknownFields() {
96121

97122
@Override
98123
public boolean isZeroValue() {
99-
return rawWireBytes().isEmpty();
124+
return toByteString().isEmpty();
100125
}
101126

102127
/**
@@ -169,27 +194,15 @@ private static Object decodeWireField(
169194
}
170195

171196
boolean isRepeated = field.defaultValue() instanceof List;
172-
String protoTypeName = resolveProtoTypeName(field);
173-
174-
return decodeWireEntries(unknowns, typeCode, protoTypeName, isRepeated, converter);
175-
}
176197

177-
/**
178-
* Resolves the protobuf message type name for a field from the optimizer metadata in {@link
179-
* SelectField}, or {@link #UNKNOWN_MESSAGE_TYPE_NAME} if unspecified.
180-
*/
181-
private static String resolveProtoTypeName(SelectField field) {
182-
if (!field.protoTypeName().isEmpty()) {
183-
return field.protoTypeName();
184-
}
185-
return UNKNOWN_MESSAGE_TYPE_NAME;
198+
return decodeWireEntries(unknowns, typeCode, field.protoTypeName(), isRepeated, converter);
186199
}
187200

188201
private static Object resolveDefault(SelectField field, ProtoLiteCelValueConverter converter) {
189202
if (field.defaultValue() != null) {
190203
return field.defaultValue();
191204
}
192-
return create(ByteString.EMPTY, resolveProtoTypeName(field), converter);
205+
return decodeMessageValue(ByteString.EMPTY, field.protoTypeName(), converter);
193206
}
194207

195208
/**
@@ -419,9 +432,7 @@ static Object decodeWireValue(
419432
throw new UnsupportedOperationException("Groups are not supported");
420433
case MESSAGE:
421434
ByteString msgBytes = requireType(raw, ByteString.class, fieldType);
422-
return converter
423-
.tryDecodeWellKnownProto(msgBytes, protoTypeName)
424-
.orElseGet(() -> create(msgBytes, protoTypeName, converter));
435+
return decodeMessageValue(msgBytes, protoTypeName, converter);
425436
case BYTES:
426437
return CelByteString.of(requireType(raw, ByteString.class, fieldType).toByteArray());
427438
case UINT32:
@@ -437,6 +448,13 @@ static Object decodeWireValue(
437448
throw new IllegalArgumentException("Unsupported proto field type: " + fieldType);
438449
}
439450

451+
private static Object decodeMessageValue(
452+
ByteString msgBytes, String protoTypeName, ProtoLiteCelValueConverter converter) {
453+
return converter
454+
.tryDecodeProtoMessage(msgBytes, protoTypeName)
455+
.orElseGet(() -> create(msgBytes, protoTypeName, converter));
456+
}
457+
440458
private static <T> T requireType(
441459
Object raw, Class<T> expectedType, WireFormat.FieldType fieldType) {
442460
if (!expectedType.isInstance(raw)) {
@@ -509,12 +527,12 @@ private static ImmutableList<Object> decodePacked(
509527
}
510528
}
511529

512-
public static RawProtoMessageLiteValue create(
530+
static RawProtoMessageLiteValue create(
513531
ByteString rawWireBytes, ProtoLiteCelValueConverter protoLiteCelValueConverter) {
514532
return create(rawWireBytes, "", protoLiteCelValueConverter);
515533
}
516534

517-
public static RawProtoMessageLiteValue create(
535+
static RawProtoMessageLiteValue create(
518536
ByteString rawWireBytes,
519537
String protoTypeName,
520538
ProtoLiteCelValueConverter protoLiteCelValueConverter) {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.common.values;
16+
17+
import com.google.errorprone.annotations.Immutable;
18+
import com.google.protobuf.ByteString;
19+
import dev.cel.common.annotations.Beta;
20+
21+
/**
22+
* Represents a protobuf message evaluation result in {@code CelLiteRuntime} when no {@code
23+
* CelLiteDescriptor} is registered for the message type.
24+
*
25+
* <p>When a message-typed expression is evaluated in {@code CelLiteRuntime}:
26+
*
27+
* <ul>
28+
* <li>If a {@code CelLiteDescriptor} is registered for the message type, evaluation produces a
29+
* {@code MessageLite} instance.
30+
* <li>Otherwise, evaluation produces a {@code WireMessageLite} carrying the message's protobuf
31+
* type name and wire-encoded payload.
32+
* </ul>
33+
*/
34+
@Immutable
35+
@Beta
36+
public interface WireMessageLite {
37+
38+
/**
39+
* Returns the fully-qualified protobuf message type name (e.g. {@code
40+
* "cel.expr.conformance.proto3.TestAllTypes.NestedMessage"}), or {@code "cel.@unknownMessage"} if
41+
* the message type name is not known at runtime.
42+
*/
43+
String protoTypeName();
44+
45+
/** Serializes the message to a {@link ByteString} in protobuf wire format. */
46+
ByteString toByteString();
47+
}

0 commit comments

Comments
 (0)