Skip to content

Commit f96d9fc

Browse files
l46kokcopybara-github
authored andcommitted
Introduce OptimizedSelectable interface and general traversal logic on
PiperOrigin-RevId: 986267092
1 parent b4977d9 commit f96d9fc

8 files changed

Lines changed: 871 additions & 0 deletions

File tree

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

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,3 +434,85 @@ cel_android_library(
434434
"@maven//:com_google_errorprone_error_prone_annotations",
435435
],
436436
)
437+
438+
java_library(
439+
name = "select_field",
440+
srcs = ["SelectField.java"],
441+
tags = [
442+
],
443+
deps = [
444+
"//:auto_value",
445+
"//common/annotations",
446+
"@maven//:com_google_errorprone_error_prone_annotations",
447+
"@maven//:com_google_guava_guava",
448+
"@maven//:org_jspecify_jspecify",
449+
],
450+
)
451+
452+
cel_android_library(
453+
name = "select_field_android",
454+
srcs = ["SelectField.java"],
455+
tags = [
456+
],
457+
deps = [
458+
"//:auto_value",
459+
"//common/annotations",
460+
"@maven//:com_google_errorprone_error_prone_annotations",
461+
"@maven//:org_jspecify_jspecify",
462+
"@maven_android//:com_google_guava_guava",
463+
],
464+
)
465+
466+
java_library(
467+
name = "optimized_selectable",
468+
srcs = ["OptimizedSelectable.java"],
469+
tags = [
470+
],
471+
deps = [
472+
":select_field",
473+
"//common/annotations",
474+
"@maven//:com_google_errorprone_error_prone_annotations",
475+
],
476+
)
477+
478+
cel_android_library(
479+
name = "optimized_selectable_android",
480+
srcs = ["OptimizedSelectable.java"],
481+
tags = [
482+
],
483+
deps = [
484+
":select_field_android",
485+
"//common/annotations",
486+
"@maven//:com_google_errorprone_error_prone_annotations",
487+
],
488+
)
489+
490+
java_library(
491+
name = "optimized_select_traversal",
492+
srcs = ["OptimizedSelectTraversal.java"],
493+
tags = [
494+
],
495+
deps = [
496+
":optimized_selectable",
497+
":select_field",
498+
":values",
499+
"//common/annotations",
500+
"//common/exceptions:attribute_not_found",
501+
"@maven//:com_google_guava_guava",
502+
],
503+
)
504+
505+
cel_android_library(
506+
name = "optimized_select_traversal_android",
507+
srcs = ["OptimizedSelectTraversal.java"],
508+
tags = [
509+
],
510+
deps = [
511+
":optimized_selectable_android",
512+
":select_field_android",
513+
":values_android",
514+
"//common/annotations",
515+
"//common/exceptions:attribute_not_found",
516+
"@maven_android//:com_google_guava_guava",
517+
],
518+
)
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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.Optional;
21+
22+
/**
23+
* Walks a sequence of {@link SelectField} selections, dispatching each field over {@link
24+
* OptimizedSelectable} or {@link SelectableValue}.
25+
*
26+
* <p>CEL Library Internals. Do Not Use.
27+
*/
28+
@Internal
29+
public final class OptimizedSelectTraversal {
30+
31+
/**
32+
* Qualifies {@code target} through every field in {@code fields} and returns the terminal value.
33+
*/
34+
public static Object qualify(Object target, ImmutableList<SelectField> fields) {
35+
Object current = target;
36+
for (int i = 0; i < fields.size(); i++) {
37+
current = qualifyField(current, fields.get(i));
38+
}
39+
return current;
40+
}
41+
42+
/**
43+
* Presence tests the terminal field of {@code fields}, navigating through all preceding fields.
44+
*
45+
* <p>Absence of any intermediate field short-circuits to {@code false}.
46+
*/
47+
public static boolean hasField(Object target, ImmutableList<SelectField> fields) {
48+
if (fields.isEmpty()) {
49+
return false;
50+
}
51+
Object current = target;
52+
int terminalIndex = fields.size() - 1;
53+
for (int i = 0; i < terminalIndex; i++) {
54+
// Invariant: Select optimization is only applied to structs (proto messages). Maps and
55+
// repeated fields are excluded from optimized field selection, so an absent intermediate
56+
// field is guaranteed to be an unset message field rather than a missing map key or
57+
// invalid field access, safely short-circuiting to false.
58+
Optional<Object> next = navigateField(current, fields.get(i));
59+
if (!next.isPresent()) {
60+
return false;
61+
}
62+
current = next.get();
63+
}
64+
return hasTerminalField(current, fields.get(terminalIndex));
65+
}
66+
67+
// SelectableValue is only ever instantiated with String keys in the select path.
68+
@SuppressWarnings("unchecked")
69+
private static Object qualifyField(Object target, SelectField field) {
70+
if (target instanceof ErrorValue) {
71+
return target;
72+
}
73+
if (target instanceof OptimizedSelectable) {
74+
return ((OptimizedSelectable) target).selectByFieldNumber(field);
75+
}
76+
if (target instanceof SelectableValue) {
77+
SelectableValue<String> selectable = (SelectableValue<String>) target;
78+
if (field.defaultValue() != null) {
79+
return selectable
80+
.find(field.fieldName())
81+
.map(Object.class::cast)
82+
.orElse(field.defaultValue());
83+
}
84+
return selectable.select(field.fieldName());
85+
}
86+
throw CelAttributeNotFoundException.forFieldResolution(field.fieldName());
87+
}
88+
89+
// SelectableValue is only ever instantiated with String keys in the select path.
90+
@SuppressWarnings("unchecked")
91+
private static Optional<Object> navigateField(Object target, SelectField field) {
92+
if (target instanceof ErrorValue) {
93+
return Optional.of(target);
94+
}
95+
if (target instanceof OptimizedSelectable) {
96+
return ((OptimizedSelectable) target).findByFieldNumber(field);
97+
}
98+
if (target instanceof SelectableValue) {
99+
return ((SelectableValue<String>) target).find(field.fieldName()).map(Object.class::cast);
100+
}
101+
throw CelAttributeNotFoundException.forFieldResolution(field.fieldName());
102+
}
103+
104+
// SelectableValue is only ever instantiated with String keys in the select path.
105+
@SuppressWarnings("unchecked")
106+
private static boolean hasTerminalField(Object target, SelectField field) {
107+
if (target instanceof ErrorValue) {
108+
return false;
109+
}
110+
if (target instanceof OptimizedSelectable) {
111+
return ((OptimizedSelectable) target).hasFieldByNumber(field);
112+
}
113+
if (target instanceof SelectableValue) {
114+
return ((SelectableValue<String>) target).find(field.fieldName()).isPresent();
115+
}
116+
throw CelAttributeNotFoundException.forFieldResolution(field.fieldName());
117+
}
118+
119+
private OptimizedSelectTraversal() {}
120+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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 an optimized field selection within a selection chain rewritten by the select optimizer.
23+
*
24+
* <p>Implementations resolve individual field selections against themselves by protobuf field
25+
* number. Walking the chain across multiple fields and heterogeneous values belongs to {@link
26+
* OptimizedSelectTraversal}.
27+
*
28+
* <p>CEL Library Internals. Do Not Use.
29+
*/
30+
@Internal
31+
@Immutable
32+
public interface OptimizedSelectable {
33+
34+
/** Selects {@code field}, falling back to its default value or an empty submessage if absent. */
35+
Object selectByFieldNumber(SelectField field);
36+
37+
/** Returns whether {@code field} is present. */
38+
boolean hasFieldByNumber(SelectField field);
39+
40+
/**
41+
* Returns the value of the field at {@code field} (a scalar or submessage) for an intermediate
42+
* step of a presence test, or empty if absent.
43+
*/
44+
Optional<Object> findByFieldNumber(SelectField field);
45+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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 static com.google.common.base.Preconditions.checkArgument;
18+
import static com.google.common.base.Preconditions.checkNotNull;
19+
20+
import com.google.auto.value.AutoValue;
21+
import com.google.errorprone.annotations.Immutable;
22+
import dev.cel.common.annotations.Internal;
23+
import org.jspecify.annotations.Nullable;
24+
25+
/**
26+
* Represents a single field selection in an optimized selection chain.
27+
*
28+
* <p>CEL Library Internals. Do Not Use.
29+
*/
30+
@Internal
31+
@AutoValue
32+
@AutoValue.CopyAnnotations
33+
@Immutable
34+
@SuppressWarnings("Immutable") // Default value is an immutable CEL literal or null
35+
public abstract class SelectField {
36+
37+
private static final int MAX_FIELD_NUMBER = 536870911;
38+
39+
/** CEL-specific type code used to encode CEL maps on the wire. */
40+
public static final int CEL_MAP_TYPE_CODE = -1;
41+
42+
/** Sentinel for a presence-test qualifier, whose 2-tuple carries no type code. */
43+
public static final int NO_TYPE_CODE = 0;
44+
45+
/** Protobuf field type code for {@code TYPE_MESSAGE} ({@code FieldDescriptorProto.Type}). */
46+
public static final int MESSAGE_TYPE_CODE = 11;
47+
48+
// Mirrors FieldDescriptorProto.Type. Not validated against a protobuf enum because the :values
49+
// target is deliberately protobuf-free; keep in sync with CelLiteDescriptor.FieldLiteDescriptor.
50+
private static final int MIN_PROTO_TYPE_CODE = 1; // TYPE_DOUBLE
51+
private static final int MAX_PROTO_TYPE_CODE = 18; // TYPE_SINT64
52+
private static final int GROUP_PROTO_TYPE_CODE = 10; // Unsupported by CEL.
53+
54+
/** Protobuf field number of this hop. */
55+
public abstract int fieldNumber();
56+
57+
/** Protobuf field name or map key of this hop. */
58+
public abstract String fieldName();
59+
60+
/**
61+
* Protobuf wire type code (1..18, except 10), {@link #CEL_MAP_TYPE_CODE}, or {@link
62+
* #NO_TYPE_CODE}.
63+
*/
64+
public abstract int typeCode();
65+
66+
/**
67+
* Default value for this hop, or null if unspecified. When non-null, this must be an immutable
68+
* CEL literal value.
69+
*/
70+
public abstract @Nullable Object defaultValue();
71+
72+
/**
73+
* Creates a presence-test qualifier hop.
74+
*
75+
* @param fieldNumber Protobuf field number. Takes {@code long} for compatibility with CEL's int64
76+
* constant representations.
77+
* @param fieldName Protobuf field name.
78+
*/
79+
public static SelectField create(long fieldNumber, String fieldName) {
80+
checkArgument(
81+
fieldNumber >= 1 && fieldNumber <= MAX_FIELD_NUMBER,
82+
"Field number out of protobuf range: %s",
83+
fieldNumber);
84+
checkNotNull(fieldName);
85+
return new AutoValue_SelectField(
86+
(int) fieldNumber, fieldName, NO_TYPE_CODE, /* defaultValue= */ null);
87+
}
88+
89+
/**
90+
* Creates a fully-specified field selection hop with type code and optional default value.
91+
*
92+
* @param fieldNumber Protobuf field number. Takes {@code long} for compatibility with CEL's int64
93+
* constant representations.
94+
* @param fieldName Protobuf field name.
95+
* @param typeCode Protobuf wire type code or {@link #CEL_MAP_TYPE_CODE}. Takes {@code long} for
96+
* compatibility with CEL's int64 constant representations.
97+
* @param defaultValue Default value for the field, or null if unspecified.
98+
*/
99+
public static SelectField create(
100+
long fieldNumber, String fieldName, long typeCode, @Nullable Object defaultValue) {
101+
checkArgument(
102+
fieldNumber >= 1 && fieldNumber <= MAX_FIELD_NUMBER,
103+
"Field number out of protobuf range: %s",
104+
fieldNumber);
105+
checkNotNull(fieldName);
106+
checkArgument(isSupportedTypeCode(typeCode), "Invalid protobuf type code: %s", typeCode);
107+
return new AutoValue_SelectField((int) fieldNumber, fieldName, (int) typeCode, defaultValue);
108+
}
109+
110+
/**
111+
* Returns whether {@code typeCode} is a protobuf field type code CEL supports, or the {@link
112+
* #CEL_MAP_TYPE_CODE} sentinel.
113+
*/
114+
public static boolean isSupportedTypeCode(long typeCode) {
115+
if (typeCode == CEL_MAP_TYPE_CODE) {
116+
return true;
117+
}
118+
return typeCode >= MIN_PROTO_TYPE_CODE
119+
&& typeCode <= MAX_PROTO_TYPE_CODE
120+
&& typeCode != GROUP_PROTO_TYPE_CODE;
121+
}
122+
123+
SelectField() {}
124+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ java_library(
2929
"//common/values:cel_value_provider",
3030
"//common/values:combined_cel_value_converter",
3131
"//common/values:combined_cel_value_provider",
32+
"//common/values:optimized_select_traversal",
33+
"//common/values:optimized_selectable",
3234
"//common/values:proto_message_lite_value",
3335
"//common/values:proto_message_lite_value_provider",
3436
"//common/values:proto_message_value",
3537
"//common/values:proto_message_value_provider",
38+
"//common/values:select_field",
3639
"//protobuf:cel_lite_descriptor",
3740
"//testing/protos:test_all_types_cel_java_proto3",
3841
"@cel_spec//proto/cel/expr/conformance/proto2:test_all_types_java_proto",

0 commit comments

Comments
 (0)