Skip to content

Commit 382100a

Browse files
l46kokcopybara-github
authored andcommitted
Introduce OptimizedSelectable interface and ProtoLite select optimization
PiperOrigin-RevId: 982681694
1 parent 612f7de commit 382100a

13 files changed

Lines changed: 2424 additions & 37 deletions

File tree

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ CEL_VALUES_SOURCES = [
1717
"ErrorValue.java",
1818
"NullValue.java",
1919
"OpaqueValue.java",
20+
"OptimizedSelectTraversal.java",
21+
"OptimizedSelectable.java",
2022
"OptionalValue.java",
23+
"SelectField.java",
2124
"SelectableValue.java",
2225
"StructValue.java",
2326
]
@@ -167,6 +170,7 @@ java_library(
167170
":preadapted_list",
168171
"//:auto_value",
169172
"//common/annotations",
173+
"//common/exceptions:attribute_not_found",
170174
"//common/types",
171175
"//common/types:type_providers",
172176
"@maven//:com_google_errorprone_error_prone_annotations",
@@ -218,6 +222,7 @@ cel_android_library(
218222
":preadapted_list_android",
219223
"//:auto_value",
220224
"//common/annotations",
225+
"//common/exceptions:attribute_not_found",
221226
"//common/types:type_providers_android",
222227
"//common/types:types_android",
223228
"@maven//:com_google_errorprone_error_prone_annotations",
@@ -317,6 +322,7 @@ java_library(
317322
srcs = [
318323
"ProtoLiteCelValueConverter.java",
319324
"ProtoMessageLiteValue.java",
325+
"RawProtoMessageLiteValue.java",
320326
],
321327
tags = [
322328
],
@@ -325,6 +331,7 @@ java_library(
325331
":values",
326332
"//:auto_value",
327333
"//common/annotations",
334+
"//common/exceptions:attribute_not_found",
328335
"//common/internal:cel_lite_descriptor_pool",
329336
"//common/internal:well_known_proto",
330337
"//common/types",
@@ -333,6 +340,7 @@ java_library(
333340
"//protobuf:cel_lite_descriptor",
334341
"@maven//:com_google_errorprone_error_prone_annotations",
335342
"@maven//:com_google_guava_guava",
343+
"@maven//:org_jspecify_jspecify",
336344
"@maven_android//:com_google_protobuf_protobuf_javalite",
337345
],
338346
)
@@ -342,6 +350,7 @@ cel_android_library(
342350
srcs = [
343351
"ProtoLiteCelValueConverter.java",
344352
"ProtoMessageLiteValue.java",
353+
"RawProtoMessageLiteValue.java",
345354
],
346355
tags = [
347356
],
@@ -350,6 +359,7 @@ cel_android_library(
350359
":values_android",
351360
"//:auto_value",
352361
"//common/annotations",
362+
"//common/exceptions:attribute_not_found",
353363
"//common/internal:cel_lite_descriptor_pool_android",
354364
"//common/internal:well_known_proto_android",
355365
"//common/types:type_providers_android",
@@ -358,6 +368,7 @@ cel_android_library(
358368
"//protobuf:cel_lite_descriptor",
359369
"@maven//:com_google_errorprone_error_prone_annotations",
360370
"@maven//:com_google_guava_guava",
371+
"@maven//:org_jspecify_jspecify",
361372
"@maven_android//:com_google_guava_guava",
362373
"@maven_android//:com_google_protobuf_protobuf_javalite",
363374
],
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
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.common.collect.ImmutableList;
18+
import dev.cel.common.annotations.Internal;
19+
import dev.cel.common.exceptions.CelAttributeNotFoundException;
20+
import java.util.Map;
21+
import java.util.Optional;
22+
import org.jspecify.annotations.Nullable;
23+
24+
/**
25+
* Walks a chain of {@link SelectField} hops, dispatching each hop over {@link OptimizedSelectable},
26+
* {@link SelectableValue} or {@link Map}.
27+
*
28+
* <p>CEL Library Internals. Do Not Use.
29+
*/
30+
@Internal
31+
public final class OptimizedSelectTraversal {
32+
33+
/**
34+
* Qualifies {@code target} through every hop in {@code fields} and returns the terminal value.
35+
*
36+
* @param celValueConverter Converter for hops not resolved by an {@link OptimizedSelectable};
37+
* superseded by {@link OptimizedSelectable#celValueConverter} once the chain crosses one.
38+
*/
39+
public static Object qualify(
40+
Object target, ImmutableList<SelectField> fields, CelValueConverter celValueConverter) {
41+
Object current = target;
42+
CelValueConverter converter = celValueConverter;
43+
for (int i = 0; i < fields.size(); i++) {
44+
converter = converterFor(current, converter);
45+
current = qualifyHop(current, fields.get(i), converter);
46+
}
47+
return current;
48+
}
49+
50+
/**
51+
* Presence tests the terminal hop of {@code fields}, navigating through all preceding hops.
52+
*
53+
* <p>Absence of any intermediate hop short-circuits to {@code false}.
54+
*/
55+
public static boolean hasField(
56+
Object target, ImmutableList<SelectField> fields, CelValueConverter celValueConverter) {
57+
if (fields.isEmpty()) {
58+
return false;
59+
}
60+
Object current = target;
61+
CelValueConverter converter = celValueConverter;
62+
int terminalIndex = fields.size() - 1;
63+
for (int i = 0; i < terminalIndex; i++) {
64+
converter = converterFor(current, converter);
65+
current = navigateHop(current, fields.get(i), converter);
66+
if (current == null) {
67+
return false;
68+
}
69+
}
70+
return hasTerminalHop(current, fields.get(terminalIndex));
71+
}
72+
73+
private static Object qualifyHop(
74+
Object target, SelectField field, CelValueConverter celValueConverter) {
75+
if (target instanceof OptimizedSelectable) {
76+
return ((OptimizedSelectable) target).selectField(field);
77+
}
78+
if (target instanceof SelectableValue) {
79+
Optional<Object> found =
80+
SelectField.findField((SelectableValue<?>) target, field.fieldName());
81+
if (found.isPresent()) {
82+
return SelectField.toStepTarget(found.get(), celValueConverter);
83+
}
84+
if (field.defaultValue() != null) {
85+
return field.defaultValue();
86+
}
87+
throw CelAttributeNotFoundException.forFieldResolution(field.fieldName());
88+
}
89+
if (target instanceof Map) {
90+
Map<?, ?> map = (Map<?, ?>) target;
91+
Object mapValue = map.get(field.fieldName());
92+
if (mapValue != null) {
93+
return SelectField.toStepTarget(mapValue, celValueConverter);
94+
}
95+
if (map.containsKey(field.fieldName())) {
96+
return NullValue.NULL_VALUE;
97+
}
98+
throw CelAttributeNotFoundException.forMissingMapKey(field.fieldName());
99+
}
100+
throw CelAttributeNotFoundException.forFieldResolution(field.fieldName());
101+
}
102+
103+
private static @Nullable Object navigateHop(
104+
Object target, SelectField field, CelValueConverter celValueConverter) {
105+
if (target instanceof OptimizedSelectable) {
106+
return ((OptimizedSelectable) target).navigateField(field).orElse(null);
107+
}
108+
if (target instanceof SelectableValue) {
109+
Optional<Object> found =
110+
SelectField.findField((SelectableValue<?>) target, field.fieldName());
111+
return found.isPresent() ? SelectField.toStepTarget(found.get(), celValueConverter) : null;
112+
}
113+
if (target instanceof Map) {
114+
Map<?, ?> map = (Map<?, ?>) target;
115+
Object mapValue = map.get(field.fieldName());
116+
if (mapValue != null) {
117+
return SelectField.toStepTarget(mapValue, celValueConverter);
118+
}
119+
return map.containsKey(field.fieldName()) ? NullValue.NULL_VALUE : null;
120+
}
121+
throw CelAttributeNotFoundException.forFieldResolution(field.fieldName());
122+
}
123+
124+
private static boolean hasTerminalHop(Object target, SelectField field) {
125+
if (target instanceof OptimizedSelectable) {
126+
return ((OptimizedSelectable) target).hasField(field);
127+
}
128+
if (target instanceof SelectableValue) {
129+
return SelectField.findField((SelectableValue<?>) target, field.fieldName()).isPresent();
130+
}
131+
if (target instanceof Map) {
132+
return ((Map<?, ?>) target).containsKey(field.fieldName());
133+
}
134+
throw CelAttributeNotFoundException.forFieldResolution(field.fieldName());
135+
}
136+
137+
/** Returns {@code target}'s own converter if it has one, otherwise {@code fallback}. */
138+
private static CelValueConverter converterFor(Object target, CelValueConverter fallback) {
139+
return target instanceof OptimizedSelectable
140+
? ((OptimizedSelectable) target).celValueConverter()
141+
: fallback;
142+
}
143+
144+
private OptimizedSelectTraversal() {}
145+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 dev.cel.common.annotations.Internal;
19+
import java.util.Optional;
20+
21+
/**
22+
* Resolves a single hop of an optimized selection chain, where a hop is one field selection within
23+
* a chain rewritten by the select optimizer ({@code a.b.c} has two hops, {@code .b} and {@code
24+
* .c}).
25+
*
26+
* <p>Implementations only resolve a hop against themselves. Walking the chain, including across
27+
* values that do not implement this interface, belongs to {@link OptimizedSelectTraversal}.
28+
*
29+
* <p>CEL Library Internals. Do Not Use.
30+
*/
31+
@Internal
32+
@Immutable
33+
public interface OptimizedSelectable {
34+
35+
/**
36+
* Returns the converter this value's fields were decoded with. {@link OptimizedSelectTraversal}
37+
* adopts it for the rest of the chain, so values produced downstream are adapted with the same
38+
* descriptor pool that produced them.
39+
*/
40+
CelValueConverter celValueConverter();
41+
42+
/** Selects {@code field}, falling back to its default value or an empty submessage if absent. */
43+
Object selectField(SelectField field);
44+
45+
/** Returns whether {@code field} is present. */
46+
boolean hasField(SelectField field);
47+
48+
/**
49+
* Returns the submessage at {@code field} for an intermediate hop of a presence test, or empty if
50+
* absent.
51+
*/
52+
Optional<Object> navigateField(SelectField field);
53+
}

0 commit comments

Comments
 (0)