Skip to content

Commit cbce220

Browse files
l46kokcopybara-github
authored andcommitted
Fix parsed-only evaluation for enum identifiers with containers
PiperOrigin-RevId: 872631218
1 parent bff6518 commit cbce220

File tree

2 files changed

+78
-28
lines changed

2 files changed

+78
-28
lines changed

runtime/src/main/java/dev/cel/runtime/planner/NamespacedAttribute.java

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import dev.cel.common.values.CelValueConverter;
2626
import dev.cel.runtime.GlobalResolver;
2727
import java.util.NoSuchElementException;
28+
import org.jspecify.annotations.Nullable;
2829

2930
@Immutable
3031
final class NamespacedAttribute implements Attribute {
@@ -34,6 +35,14 @@ final class NamespacedAttribute implements Attribute {
3435
private final CelValueConverter celValueConverter;
3536
private final CelTypeProvider typeProvider;
3637

38+
ImmutableList<Qualifier> qualifiers() {
39+
return qualifiers;
40+
}
41+
42+
ImmutableSet<String> candidateVariableNames() {
43+
return namespacedNames;
44+
}
45+
3746
@Override
3847
public Object resolve(GlobalResolver ctx, ExecutionFrame frame) {
3948
GlobalResolver inputVars = ctx;
@@ -59,41 +68,56 @@ public Object resolve(GlobalResolver ctx, ExecutionFrame frame) {
5968
}
6069
}
6170

62-
CelType type = typeProvider.findType(name).orElse(null);
63-
if (type != null) {
64-
if (qualifiers.isEmpty()) {
65-
// Resolution of a fully qualified type name: foo.bar.baz
66-
return TypeType.create(type);
67-
} else {
68-
// This is potentially a fully qualified reference to an enum value
69-
if (type instanceof EnumType && qualifiers.size() == 1) {
70-
EnumType enumType = (EnumType) type;
71-
String strQualifier = (String) qualifiers.get(0).value();
72-
return enumType
73-
.findNumberByName(strQualifier)
74-
.orElseThrow(
75-
() ->
76-
new NoSuchElementException(
77-
String.format(
78-
"Field %s was not found on enum %s",
79-
enumType.name(), strQualifier)));
80-
}
81-
}
82-
83-
throw new IllegalStateException(
84-
"Unexpected type resolution when there were remaining qualifiers: " + type.name());
71+
// Attempt to resolve the qualify type name if the name is not a variable identifier
72+
value = findIdent(name);
73+
if (value != null) {
74+
return value;
8575
}
8676
}
8777

8878
return MissingAttribute.newMissingAttribute(namespacedNames);
8979
}
9080

91-
ImmutableList<Qualifier> qualifiers() {
92-
return qualifiers;
81+
private @Nullable Object findIdent(String name) {
82+
CelType type = typeProvider.findType(name).orElse(null);
83+
// If the name resolves directly, this is a fully qualified type name
84+
// (ex: 'int' or 'google.protobuf.Timestamp')
85+
if (type != null) {
86+
if (qualifiers.isEmpty()) {
87+
// Resolution of a fully qualified type name: foo.bar.baz
88+
return TypeType.create(type);
89+
}
90+
91+
throw new IllegalStateException(
92+
"Unexpected type resolution when there were remaining qualifiers: " + type.name());
93+
}
94+
95+
// The name itself could be a fully qualified reference to an enum value
96+
// (e.g: my.enum_type.BAR)
97+
int lastDotIndex = name.lastIndexOf('.');
98+
if (lastDotIndex > 0) {
99+
String enumTypeName = name.substring(0, lastDotIndex);
100+
String enumValueQualifier = name.substring(lastDotIndex + 1);
101+
102+
return typeProvider
103+
.findType(enumTypeName)
104+
.filter(EnumType.class::isInstance)
105+
.map(EnumType.class::cast)
106+
.map(enumType -> getEnumValue(enumType, enumValueQualifier))
107+
.orElse(null);
108+
}
109+
110+
return null;
93111
}
94112

95-
ImmutableSet<String> candidateVariableNames() {
96-
return namespacedNames;
113+
private static Long getEnumValue(EnumType enumType, String field) {
114+
return enumType
115+
.findNumberByName(field)
116+
.map(Integer::longValue)
117+
.orElseThrow(
118+
() ->
119+
new NoSuchElementException(
120+
String.format("Field %s was not found on enum %s", enumType.name(), field)));
97121
}
98122

99123
private GlobalResolver unwrapToNonLocal(GlobalResolver resolver) {

runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,33 @@ public void plan_ident_enum() throws Exception {
277277

278278
Object result = program.eval();
279279

280-
assertThat(result).isEqualTo(1);
280+
assertThat(result).isEqualTo(1L);
281+
}
282+
283+
@Test
284+
public void plan_ident_enumContainer() throws Exception {
285+
CelContainer container = CelContainer.ofName(GlobalEnum.getDescriptor().getFullName());
286+
CelCompiler compiler =
287+
CelCompilerFactory.standardCelCompilerBuilder()
288+
.addMessageTypes(TestAllTypes.getDescriptor())
289+
.setContainer(container)
290+
.build();
291+
CelAbstractSyntaxTree ast = compile(compiler, GlobalEnum.GAR.name());
292+
ProgramPlanner planner =
293+
ProgramPlanner.newPlanner(
294+
TYPE_PROVIDER,
295+
VALUE_PROVIDER,
296+
newDispatcher(),
297+
CEL_VALUE_CONVERTER,
298+
container,
299+
CEL_OPTIONS,
300+
ImmutableSet.of());
301+
302+
Program program = planner.plan(ast);
303+
304+
Object result = program.eval();
305+
306+
assertThat(result).isEqualTo(1L);
281307
}
282308

283309
@Test

0 commit comments

Comments
 (0)