Skip to content

Commit 476b9df

Browse files
committed
TypeChecker Refactor
1 parent ff8cd30 commit 476b9df

12 files changed

Lines changed: 205 additions & 237 deletions

File tree

.bazelversion

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
9.0.0

bundle/src/test/java/dev/cel/bundle/CelImplTest.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,14 +1001,12 @@ public void program_protoActivation() throws Exception {
10011001
}
10021002

10031003
@Test
1004-
@TestParameters("{resolveTypeDependencies: false}")
1005-
@TestParameters("{resolveTypeDependencies: true}")
1006-
public void program_enumTypeDirectResolution(boolean resolveTypeDependencies) throws Exception {
1004+
public void program_enumTypeDirectResolution() throws Exception {
10071005
Cel cel =
10081006
standardCelBuilderWithMacros()
10091007
.addFileTypes(StandaloneGlobalEnum.getDescriptor().getFile())
10101008
.setOptions(
1011-
CelOptions.current().resolveTypeDependencies(resolveTypeDependencies).build())
1009+
CelOptions.current().resolveTypeDependencies(true).build())
10121010
.setContainer(
10131011
CelContainer.ofName("dev.cel.testing.testdata.proto3.StandaloneGlobalEnum"))
10141012
.setResultType(SimpleType.BOOL)

checker/src/main/java/dev/cel/checker/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ CHECKER_LEGACY_ENV_SOURCES = [
3232
"InferenceContext.java",
3333
"TypeFormatter.java",
3434
"TypeProvider.java",
35+
"TypeProviderLegacyImpl.java",
3536
"Types.java",
3637
]
3738

@@ -190,6 +191,7 @@ java_library(
190191
"//common/types",
191192
"//common/types:cel_proto_types",
192193
"//common/types:cel_types",
194+
"//common/types:message_type_provider",
193195
"//common/types:type_providers",
194196
"//parser:macro",
195197
"@cel_spec//proto/cel/expr:checked_java_proto",

checker/src/main/java/dev/cel/checker/CelCheckerLegacyImpl.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,11 @@ public void accept(EnvVisitor envVisitor) {
163163
private Env getEnv(Errors errors) {
164164
Env env;
165165
if (standardEnvironmentEnabled) {
166-
env = Env.standard(errors, typeProvider, celOptions);
166+
env = Env.standard(errors, celTypeProvider, celOptions);
167167
} else if (overriddenStandardDeclarations != null) {
168-
env = Env.standard(overriddenStandardDeclarations, errors, typeProvider, celOptions);
168+
env = Env.standard(overriddenStandardDeclarations, errors, celTypeProvider, celOptions);
169169
} else {
170-
env = Env.unconfigured(errors, typeProvider, celOptions);
170+
env = Env.unconfigured(errors, celTypeProvider, celOptions);
171171
}
172172
identDeclarations.forEach(env::add);
173173
functionDeclarations.forEach(env::add);
@@ -483,11 +483,11 @@ public CelCheckerLegacyImpl build() {
483483
messageTypeProvider = protoTypeMaskTypeProvider;
484484
}
485485

486-
TypeProvider legacyProvider = new TypeProviderLegacyImpl(messageTypeProvider);
487486
if (customTypeProvider != null) {
488-
legacyProvider =
489-
new TypeProvider.CombinedTypeProvider(
490-
ImmutableList.of(customTypeProvider, legacyProvider));
487+
messageTypeProvider = new CelTypeProvider.CombinedCelTypeProvider(
488+
messageTypeProvider,
489+
new TypeProviderLegacyImpl(customTypeProvider)
490+
);
491491
}
492492

493493
return new CelCheckerLegacyImpl(
@@ -496,7 +496,7 @@ public CelCheckerLegacyImpl build() {
496496
identDeclarationSet,
497497
functionDeclarations.build(),
498498
Optional.fromNullable(expectedResultType),
499-
legacyProvider,
499+
customTypeProvider,
500500
messageTypeProvider,
501501
standardEnvironmentEnabled,
502502
standardDeclarations,

checker/src/main/java/dev/cel/checker/DescriptorTypeProvider.java

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414

1515
package dev.cel.checker;
1616

17+
import com.google.common.collect.ImmutableCollection;
18+
import dev.cel.common.types.CelProtoTypes;
19+
import dev.cel.common.types.CelType;
20+
import dev.cel.common.types.ProtoMessageType;
21+
import dev.cel.common.types.TypeType;
1722
import dev.cel.expr.Type;
1823
import com.google.auto.value.AutoValue;
1924
import com.google.common.base.Ascii;
@@ -40,20 +45,19 @@
4045
import java.util.HashSet;
4146
import java.util.List;
4247
import java.util.Map;
48+
import java.util.Optional;
4349
import java.util.Set;
4450
import org.jspecify.annotations.Nullable;
4551

4652
/**
4753
* The {@code DescriptorTypeProvider} provides type information for one or more {@link Descriptor}
4854
* instances of proto messages.
4955
*
50-
* <p>TODO: Unify implementation across the runtime (i.e: DescriptorMessageProvider)
51-
* and the compilation. This class can likely be eliminated as part of the work.
52-
*
5356
* <p>CEL Library Internals. Do Not Use.
5457
*/
5558
@Immutable
5659
@Internal
60+
@Deprecated
5761
public class DescriptorTypeProvider implements TypeProvider {
5862

5963
@SuppressWarnings("Immutable")
@@ -86,6 +90,43 @@ public DescriptorTypeProvider(Iterable<Descriptor> descriptors) {
8690
return typeDef != null ? Types.create(Types.createMessage(typeDef.name())) : null;
8791
}
8892

93+
@Override
94+
public Optional<TypeType> lookupCelType(String typeName) {
95+
TypeDef typeDef = lookupMessageTypeDef(typeName);
96+
if (typeDef == null) {
97+
return Optional.empty();
98+
}
99+
100+
ImmutableSet.Builder<String> fieldsBuilder = ImmutableSet.builder();
101+
for (FieldDef fieldDef : typeDef.fields()) {
102+
fieldsBuilder.add(fieldDef.name());
103+
}
104+
105+
@SuppressWarnings("Immutable") // Legacy type defs
106+
ProtoMessageType protoMessageType = ProtoMessageType.create(
107+
typeName,
108+
fieldsBuilder.build(),
109+
fieldName -> {
110+
FieldDef fieldDef = typeDef.lookupField(fieldName);
111+
if (fieldDef == null) {
112+
return Optional.empty();
113+
}
114+
115+
Type type = fieldDefToType(fieldDef);
116+
return Optional.of(CelProtoTypes.typeToCelType(type));
117+
},
118+
extensionFieldName -> {
119+
ExtensionFieldType extensionFieldType = symbolTable.lookupExtension(extensionFieldName);
120+
if (extensionFieldType == null) {
121+
return Optional.empty();
122+
}
123+
124+
return Optional.of(extensionFieldType.fieldType().celType());
125+
}
126+
);
127+
128+
return Optional.of(TypeType.create(protoMessageType));
129+
}
89130
@Override
90131
public @Nullable Integer lookupEnumValue(String enumName) {
91132
int dot = enumName.lastIndexOf('.');
@@ -339,6 +380,8 @@ private TypeDef buildTypeDef(EnumDescriptor descriptor, Map<String, TypeDef> typ
339380

340381
/** Value object for a proto-based primitive, message, or enum definition. */
341382
@AutoValue
383+
@AutoValue.CopyAnnotations
384+
@SuppressWarnings("Immutable")
342385
protected abstract static class TypeDef {
343386

344387
/** The qualified name of the message or enum. */
@@ -434,12 +477,28 @@ static TypeDef ofEnum(String name, Iterable<EnumValueDef> enumValues) {
434477
}
435478
}
436479

480+
@Override
481+
public ImmutableCollection<CelType> types() {
482+
ImmutableList.Builder<CelType> typesBuilder = ImmutableList.builder();
483+
for (TypeDef typeDef : symbolTable.typeMap.values()) {
484+
TypeType typeType = lookupCelType(typeDef.name()).orElse(null);
485+
if (typeType == null) {
486+
continue;
487+
}
488+
489+
typesBuilder.add(typeType.type());
490+
}
491+
492+
return typesBuilder.build();
493+
}
494+
437495
/**
438496
* Value object for a proto-based field definition.
439497
*
440498
* <p>Only one of the {@link #type} or {@link #mapEntryType} may be set.
441499
*/
442500
@AutoValue
501+
@AutoValue.CopyAnnotations
443502
protected abstract static class FieldDef {
444503

445504
/** The field name. */

checker/src/main/java/dev/cel/checker/Env.java

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@
1414

1515
package dev.cel.checker;
1616

17+
import dev.cel.common.types.EnumType;
18+
import dev.cel.common.types.TypeType;
1719
import dev.cel.expr.Constant;
1820
import dev.cel.expr.Decl;
1921
import dev.cel.expr.Decl.FunctionDecl.Overload;
2022
import dev.cel.expr.Expr;
2123
import dev.cel.expr.Type;
22-
import com.google.common.annotations.VisibleForTesting;
2324
import com.google.common.base.Preconditions;
2425
import com.google.common.collect.ImmutableList;
2526
import com.google.common.collect.ImmutableMap;
@@ -43,7 +44,9 @@
4344
import dev.cel.common.types.CelKind;
4445
import dev.cel.common.types.CelProtoTypes;
4546
import dev.cel.common.types.CelType;
47+
import dev.cel.common.types.CelTypeProvider;
4648
import dev.cel.common.types.CelTypes;
49+
import dev.cel.common.types.ProtoMessageTypeProvider;
4750
import dev.cel.common.types.SimpleType;
4851
import dev.cel.parser.CelStandardMacro;
4952
import java.util.ArrayList;
@@ -78,7 +81,7 @@ public class Env {
7881
CelFunctionDecl.newBuilder().setName("*error*").build();
7982

8083
/** Type provider responsible for resolving CEL message references to strong types. */
81-
private final TypeProvider typeProvider;
84+
private final CelTypeProvider typeProvider;
8285

8386
/**
8487
* Stack of declaration groups where each entry in stack represents a scope capable of hinding
@@ -105,7 +108,7 @@ public class Env {
105108
.build();
106109

107110
private Env(
108-
Errors errors, TypeProvider typeProvider, DeclGroup declGroup, CelOptions celOptions) {
111+
Errors errors, CelTypeProvider typeProvider, DeclGroup declGroup, CelOptions celOptions) {
109112
this.celOptions = celOptions;
110113
this.errors = Preconditions.checkNotNull(errors);
111114
this.typeProvider = Preconditions.checkNotNull(typeProvider);
@@ -118,27 +121,10 @@ private Env(
118121
*/
119122
@Deprecated
120123
public static Env unconfigured(Errors errors) {
121-
return unconfigured(errors, LEGACY_TYPE_CHECKER_OPTIONS);
124+
return unconfigured(errors, new ProtoMessageTypeProvider(), LEGACY_TYPE_CHECKER_OPTIONS);
122125
}
123126

124-
/**
125-
* Creates an unconfigured {@code Env} value without the standard CEL types, functions, and
126-
* operators with a reference to the configured {@code celOptions}.
127-
*/
128-
@VisibleForTesting
129-
static Env unconfigured(Errors errors, CelOptions celOptions) {
130-
return unconfigured(errors, new DescriptorTypeProvider(), celOptions);
131-
}
132-
133-
/**
134-
* Creates an unconfigured {@code Env} value without the standard CEL types, functions, and
135-
* operators using a custom {@code typeProvider}.
136-
*
137-
* @deprecated Do not use. This exists for compatibility reasons. Migrate to CEL-Java fluent APIs.
138-
* See {@code CelCompilerFactory}.
139-
*/
140-
@Deprecated
141-
public static Env unconfigured(Errors errors, TypeProvider typeProvider, CelOptions celOptions) {
127+
static Env unconfigured(Errors errors, CelTypeProvider typeProvider, CelOptions celOptions) {
142128
return new Env(errors, typeProvider, new DeclGroup(), celOptions);
143129
}
144130

@@ -148,7 +134,7 @@ public static Env unconfigured(Errors errors, TypeProvider typeProvider, CelOpti
148134
*/
149135
@Deprecated
150136
public static Env standard(Errors errors) {
151-
return standard(errors, new DescriptorTypeProvider());
137+
return standard(errors, new ProtoMessageTypeProvider(), LEGACY_TYPE_CHECKER_OPTIONS);
152138
}
153139

154140
/**
@@ -173,6 +159,11 @@ public static Env standard(Errors errors, TypeProvider typeProvider) {
173159
*/
174160
@Deprecated
175161
public static Env standard(Errors errors, TypeProvider typeProvider, CelOptions celOptions) {
162+
CelTypeProvider adapted = new TypeProviderLegacyImpl(typeProvider);
163+
return standard(errors, adapted, celOptions);
164+
}
165+
166+
static Env standard(Errors errors, CelTypeProvider typeProvider, CelOptions celOptions) {
176167
CelStandardDeclarations celStandardDeclaration =
177168
CelStandardDeclarations.newBuilder()
178169
.filterFunctions(
@@ -209,10 +200,10 @@ public static Env standard(Errors errors, TypeProvider typeProvider, CelOptions
209200
return standard(celStandardDeclaration, errors, typeProvider, celOptions);
210201
}
211202

212-
public static Env standard(
203+
static Env standard(
213204
CelStandardDeclarations celStandardDeclaration,
214205
Errors errors,
215-
TypeProvider typeProvider,
206+
CelTypeProvider typeProvider,
216207
CelOptions celOptions) {
217208
Env env = Env.unconfigured(errors, typeProvider, celOptions);
218209
// Isolate the standard declarations into their own scope for forward compatibility.
@@ -228,8 +219,8 @@ public Errors getErrorContext() {
228219
return errors;
229220
}
230221

231-
/** Returns the {@code TypeProvider}. */
232-
public TypeProvider getTypeProvider() {
222+
/** Returns the {@code CelTypeProvider}. */
223+
public CelTypeProvider getTypeProvider() {
233224
return typeProvider;
234225
}
235226

@@ -491,30 +482,47 @@ public Env add(String name, Type type) {
491482

492483
// Next try to import the name as a reference to a message type.
493484
// This is done via the type provider.
494-
Optional<CelType> type = typeProvider.lookupCelType(cand);
485+
Optional<CelType> type = typeProvider.findType(cand);
495486
if (type.isPresent()) {
496-
decl = CelIdentDecl.newIdentDeclaration(cand, type.get());
487+
decl = CelIdentDecl.newIdentDeclaration(cand, TypeType.create(type.get()));
497488
decls.get(0).putIdent(decl);
498489
return decl;
499490
}
500491

501492
// Next try to import this as an enum value by splitting the name in a type prefix and
502493
// the enum inside.
503-
Integer enumValue = typeProvider.lookupEnumValue(cand);
504-
if (enumValue != null) {
494+
Optional<Integer> enumValue = findEnumValue(cand);
495+
if (enumValue.isPresent()) {
505496
decl =
506497
CelIdentDecl.newBuilder()
507498
.setName(cand)
508499
.setType(SimpleType.INT)
509-
.setConstant(CelConstant.ofValue(enumValue))
510-
.build();
500+
.setConstant(CelConstant.ofValue(enumValue.get())).build();
511501

512502
decls.get(0).putIdent(decl);
513503
return decl;
514504
}
505+
515506
return null;
516507
}
517508

509+
private Optional<Integer> findEnumValue(String fullyQualifiedEnumName) {
510+
int dot = fullyQualifiedEnumName.lastIndexOf('.');
511+
if (dot <= 0) {
512+
return Optional.empty();
513+
}
514+
515+
String enumTypeName = fullyQualifiedEnumName.substring(0, dot);
516+
EnumType enumType = typeProvider.findType(enumTypeName).filter(t -> t instanceof EnumType)
517+
.map(EnumType.class::cast).orElse(null);
518+
if (enumType == null) {
519+
return Optional.empty();
520+
}
521+
522+
String enumValueName = fullyQualifiedEnumName.substring(dot + 1);
523+
return enumType.findNumberByName(enumValueName);
524+
}
525+
518526
/**
519527
* Lookup a local identifier by name. This searches only comprehension scopes, bypassing standard
520528
* environment or user-defined environment.

0 commit comments

Comments
 (0)