Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion bundle/src/main/java/dev/cel/bundle/Cel.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@

/** Cel interface for parse, type-check, and evaluation of CEL programs. */
@ThreadSafe
public interface Cel extends CelCompiler, CelRuntime {}
public interface Cel extends CelCompiler, CelRuntime {
CelBuilder toCelBuilder();
}
7 changes: 6 additions & 1 deletion bundle/src/main/java/dev/cel/bundle/CelFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
import dev.cel.checker.CelCheckerLegacyImpl;
import dev.cel.common.CelOptions;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerImpl;
import dev.cel.parser.CelParserImpl;
import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntimeLegacyImpl;

/** Helper class to configure the entire CEL stack in a common interface. */
public final class CelFactory {
Expand All @@ -33,7 +35,10 @@ private CelFactory() {}
* evaluation are enabled by default.
*/
public static CelBuilder standardCelBuilder() {
return CelImpl.newBuilder(CelParserImpl.newBuilder(), CelCheckerLegacyImpl.newBuilder())
return CelImpl.newBuilder(
CelCompilerImpl.newBuilder(
CelParserImpl.newBuilder(), CelCheckerLegacyImpl.newBuilder()),
CelRuntimeLegacyImpl.newBuilder())
.setOptions(CelOptions.current().build())
.setStandardEnvironmentEnabled(true);
}
Expand Down
38 changes: 31 additions & 7 deletions bundle/src/main/java/dev/cel/bundle/CelImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,13 @@
import dev.cel.common.values.CelValueProvider;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.compiler.CelCompilerImpl;
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.parser.CelMacro;
import dev.cel.parser.CelParserBuilder;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelEvaluationException;
import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLegacyImpl;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
import java.util.function.Function;
Expand Down Expand Up @@ -102,6 +100,31 @@ public void accept(EnvVisitor envVisitor) {
}
}

@Override
public CelBuilder toCelBuilder() {
return newBuilder(toCompilerBuilder(), toRuntimeBuilder());
}

@Override
public CelParserBuilder toParserBuilder() {
return compiler.get().toParserBuilder();
}

@Override
public CelCheckerBuilder toCheckerBuilder() {
return compiler.get().toCheckerBuilder();
}

@Override
public CelCompilerBuilder toCompilerBuilder() {
return compiler.get().toCompilerBuilder();
}

@Override
public CelRuntimeBuilder toRuntimeBuilder() {
return runtime.get().toRuntimeBuilder();
}

/** Combines a prebuilt {@link CelCompiler} and {@link CelRuntime} into {@link CelImpl}. */
static CelImpl combine(CelCompiler compiler, CelRuntime runtime) {
return new CelImpl(Suppliers.memoize(() -> compiler), Suppliers.memoize(() -> runtime));
Expand All @@ -112,8 +135,9 @@ static CelImpl combine(CelCompiler compiler, CelRuntime runtime) {
*
* <p>By default, {@link CelOptions#DEFAULT} are enabled, as is the CEL standard environment.
*/
static CelBuilder newBuilder(CelParserBuilder parserBuilder, CelCheckerBuilder checkerBuilder) {
return new CelImpl.Builder(parserBuilder, checkerBuilder);
static CelBuilder newBuilder(
CelCompilerBuilder compilerBuilder, CelRuntimeBuilder celRuntimeBuilder) {
return new CelImpl.Builder(compilerBuilder, celRuntimeBuilder);
}

/** Builder class for CelImpl instances. */
Expand All @@ -122,9 +146,9 @@ public static final class Builder implements CelBuilder {
private final CelCompilerBuilder compilerBuilder;
private final CelRuntimeBuilder runtimeBuilder;

private Builder(CelParserBuilder parserBuilder, CelCheckerBuilder checkerBuilder) {
this.compilerBuilder = CelCompilerImpl.newBuilder(parserBuilder, checkerBuilder);
this.runtimeBuilder = CelRuntimeLegacyImpl.newBuilder();
private Builder(CelCompilerBuilder celCompilerBuilder, CelRuntimeBuilder celRuntimeBuilder) {
this.compilerBuilder = celCompilerBuilder;
this.runtimeBuilder = celRuntimeBuilder;
}

@Override
Expand Down
2 changes: 2 additions & 0 deletions bundle/src/test/java/dev/cel/bundle/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ java_library(
"//:auto_value",
"//:java_truth",
"//bundle:cel",
"//checker",
"//checker:checker_legacy_environment",
"//checker:proto_type_mask",
"//common",
Expand All @@ -31,6 +32,7 @@ java_library(
"//common/types:type_providers",
"//compiler",
"//compiler:compiler_builder",
"//parser",
"//parser:macro",
"//runtime",
"//runtime:unknown_attributes",
Expand Down
25 changes: 25 additions & 0 deletions bundle/src/test/java/dev/cel/bundle/CelImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import com.google.testing.junit.testparameterinjector.TestParameter;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import com.google.testing.junit.testparameterinjector.TestParameters;
import dev.cel.checker.CelCheckerLegacyImpl;
import dev.cel.checker.DescriptorTypeProvider;
import dev.cel.checker.ProtoTypeMask;
import dev.cel.checker.TypeProvider;
Expand Down Expand Up @@ -77,6 +78,8 @@
import dev.cel.common.types.StructTypeReference;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerFactory;
import dev.cel.compiler.CelCompilerImpl;
import dev.cel.parser.CelParserImpl;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelAttribute;
import dev.cel.runtime.CelAttribute.Qualifier;
Expand All @@ -86,6 +89,7 @@
import dev.cel.runtime.CelRuntime.CelFunctionBinding;
import dev.cel.runtime.CelRuntime.Program;
import dev.cel.runtime.CelRuntimeFactory;
import dev.cel.runtime.CelRuntimeLegacyImpl;
import dev.cel.runtime.CelUnknownSet;
import dev.cel.runtime.CelVariableResolver;
import dev.cel.runtime.UnknownContext;
Expand Down Expand Up @@ -1812,6 +1816,27 @@ public boolean isAssignableFrom(CelType other) {
assertThat(result).isEqualTo("5");
}

@Test
public void toBuilder_isImmutable() {
CelBuilder celBuilder = CelFactory.standardCelBuilder();
CelImpl celImpl = (CelImpl) celBuilder.build();

CelImpl.Builder newCelBuilder = (CelImpl.Builder) celImpl.toCelBuilder();
CelParserImpl.Builder newParserBuilder = (CelParserImpl.Builder) celImpl.toParserBuilder();
CelCheckerLegacyImpl.Builder newCheckerBuilder =
(CelCheckerLegacyImpl.Builder) celImpl.toCheckerBuilder();
CelCompilerImpl.Builder newCompilerBuilder =
(CelCompilerImpl.Builder) celImpl.toCompilerBuilder();
CelRuntimeLegacyImpl.Builder newRuntimeBuilder =
(CelRuntimeLegacyImpl.Builder) celImpl.toRuntimeBuilder();

assertThat(newCelBuilder).isNotEqualTo(celBuilder);
assertThat(newParserBuilder).isNotEqualTo(celImpl.toParserBuilder());
assertThat(newCheckerBuilder).isNotEqualTo(celImpl.toCheckerBuilder());
assertThat(newCompilerBuilder).isNotEqualTo(celImpl.toCompilerBuilder());
assertThat(newRuntimeBuilder).isNotEqualTo(celImpl.toRuntimeBuilder());
}

private static TypeProvider aliasingProvider(ImmutableMap<String, Type> typeAliases) {
return new TypeProvider() {
@Override
Expand Down
2 changes: 2 additions & 0 deletions checker/src/main/java/dev/cel/checker/CelChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ public interface CelChecker {
* <p>Check validates the type-agreement of the parsed {@code CelAbstractSyntaxTree}.
*/
CelValidationResult check(CelAbstractSyntaxTree ast);

CelCheckerBuilder toCheckerBuilder();
}
99 changes: 84 additions & 15 deletions checker/src/main/java/dev/cel/checker/CelCheckerLegacyImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import dev.cel.expr.Decl;
import dev.cel.expr.Type;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
Expand Down Expand Up @@ -62,15 +63,19 @@ public final class CelCheckerLegacyImpl implements CelChecker, EnvVisitable {

private final CelOptions celOptions;
private final String container;
private final ImmutableList<CelIdentDecl> identDeclarations;
private final ImmutableList<CelFunctionDecl> functionDeclarations;
private final ImmutableSet<CelIdentDecl> identDeclarations;
private final ImmutableSet<CelFunctionDecl> functionDeclarations;
private final Optional<CelType> expectedResultType;

@SuppressWarnings("Immutable")
private final TypeProvider typeProvider;

private final boolean standardEnvironmentEnabled;

// Builder is mutable by design. APIs must make defensive copies in and out of this class.
@SuppressWarnings("Immutable")
private final Builder checkerBuilder;

@Override
public CelValidationResult check(CelAbstractSyntaxTree ast) {
CelSource source = ast.getSource();
Expand All @@ -88,6 +93,11 @@ public CelValidationResult check(CelAbstractSyntaxTree ast) {
return new CelValidationResult(checkedAst, ImmutableList.of());
}

@Override
public CelCheckerBuilder toCheckerBuilder() {
return new Builder(checkerBuilder);
}

@Override
public void accept(EnvVisitor envVisitor) {
Errors errors = new Errors("", "");
Expand Down Expand Up @@ -132,9 +142,9 @@ public static CelCheckerBuilder newBuilder() {
/** Builder class for the legacy {@code CelChecker} implementation. */
public static final class Builder implements CelCheckerBuilder {

private final ImmutableList.Builder<CelIdentDecl> identDeclarations;
private final ImmutableList.Builder<CelFunctionDecl> functionDeclarations;
private final ImmutableList.Builder<ProtoTypeMask> protoTypeMasks;
private final ImmutableSet.Builder<CelIdentDecl> identDeclarations;
private final ImmutableSet.Builder<CelFunctionDecl> functionDeclarations;
private final ImmutableSet.Builder<ProtoTypeMask> protoTypeMasks;
private final ImmutableSet.Builder<Descriptor> messageTypes;
private final ImmutableSet.Builder<FileDescriptor> fileTypes;
private final ImmutableSet.Builder<CelCheckerLibrary> celCheckerLibraries;
Expand Down Expand Up @@ -316,6 +326,38 @@ public CelCheckerBuilder addLibraries(Iterable<? extends CelCheckerLibrary> libr
return this;
}

// The following getters exist for asserting immutability for collections held by this builder,
// and shouldn't be exposed to the public.
@VisibleForTesting
ImmutableSet.Builder<CelFunctionDecl> getFunctionDecls() {
return this.functionDeclarations;
}

@VisibleForTesting
ImmutableSet.Builder<CelIdentDecl> getIdentDecls() {
return this.identDeclarations;
}

@VisibleForTesting
ImmutableSet.Builder<ProtoTypeMask> getProtoTypeMasks() {
return this.protoTypeMasks;
}

@VisibleForTesting
ImmutableSet.Builder<Descriptor> getMessageTypes() {
return this.messageTypes;
}

@VisibleForTesting
ImmutableSet.Builder<FileDescriptor> getFileTypes() {
return this.fileTypes;
}

@VisibleForTesting
ImmutableSet.Builder<CelCheckerLibrary> getCheckerLibraries() {
return this.celCheckerLibraries;
}

@Override
@CheckReturnValue
public CelCheckerLegacyImpl build() {
Expand Down Expand Up @@ -348,13 +390,13 @@ public CelCheckerLegacyImpl build() {
// Configure the declaration set, and possibly alter the type provider if ProtoDecl values
// are provided as they may prevent the use of certain field selection patterns against the
// proto.
ImmutableList<CelIdentDecl> identDeclarationSet = identDeclarations.build();
ImmutableList<ProtoTypeMask> protoTypeMaskSet = protoTypeMasks.build();
ImmutableSet<CelIdentDecl> identDeclarationSet = identDeclarations.build();
ImmutableSet<ProtoTypeMask> protoTypeMaskSet = protoTypeMasks.build();
if (!protoTypeMaskSet.isEmpty()) {
ProtoTypeMaskTypeProvider protoTypeMaskTypeProvider =
new ProtoTypeMaskTypeProvider(messageTypeProvider, protoTypeMaskSet);
identDeclarationSet =
ImmutableList.<CelIdentDecl>builder()
ImmutableSet.<CelIdentDecl>builder()
.addAll(identDeclarationSet)
.addAll(protoTypeMaskTypeProvider.computeDeclsFromProtoTypeMasks())
.build();
Expand All @@ -375,36 +417,63 @@ public CelCheckerLegacyImpl build() {
functionDeclarations.build(),
Optional.fromNullable(expectedResultType),
legacyProvider,
standardEnvironmentEnabled);
standardEnvironmentEnabled,
this);
}

private Builder() {
this.celOptions = CelOptions.newBuilder().build();
this.identDeclarations = ImmutableList.builder();
this.functionDeclarations = ImmutableList.builder();
this.identDeclarations = ImmutableSet.builder();
this.functionDeclarations = ImmutableSet.builder();
this.fileTypes = ImmutableSet.builder();
this.messageTypes = ImmutableSet.builder();
this.protoTypeMasks = ImmutableList.builder();
this.protoTypeMasks = ImmutableSet.builder();
this.celCheckerLibraries = ImmutableSet.builder();
this.container = "";
}

private Builder(Builder builder) {
// The following properties are either immutable or simple primitives, thus can be assigned
// directly.
this.celOptions = builder.celOptions;
this.celTypeProvider = builder.celTypeProvider;
this.container = builder.container;
this.customTypeProvider = builder.customTypeProvider;
this.expectedResultType = builder.expectedResultType;
this.standardEnvironmentEnabled = builder.standardEnvironmentEnabled;
// The following needs to be deep copied as they are collection builders
this.functionDeclarations = deepCopy(builder.functionDeclarations);
this.identDeclarations = deepCopy(builder.identDeclarations);
this.fileTypes = deepCopy(builder.fileTypes);
this.messageTypes = deepCopy(builder.messageTypes);
this.protoTypeMasks = deepCopy(builder.protoTypeMasks);
this.celCheckerLibraries = deepCopy(builder.celCheckerLibraries);
}

private static <T> ImmutableSet.Builder<T> deepCopy(ImmutableSet.Builder<T> builderToCopy) {
ImmutableSet.Builder<T> newBuilder = ImmutableSet.builder();
newBuilder.addAll(builderToCopy.build());
return newBuilder;
}
}

private CelCheckerLegacyImpl(
CelOptions celOptions,
String container,
ImmutableList<CelIdentDecl> identDeclarations,
ImmutableList<CelFunctionDecl> functionDeclarations,
ImmutableSet<CelIdentDecl> identDeclarations,
ImmutableSet<CelFunctionDecl> functionDeclarations,
Optional<CelType> expectedResultType,
TypeProvider typeProvider,
boolean standardEnvironmentEnabled) {
boolean standardEnvironmentEnabled,
Builder checkerBuilder) {
this.celOptions = celOptions;
this.container = container;
this.identDeclarations = identDeclarations;
this.functionDeclarations = functionDeclarations;
this.expectedResultType = expectedResultType;
this.typeProvider = typeProvider;
this.standardEnvironmentEnabled = standardEnvironmentEnabled;
this.checkerBuilder = new Builder(checkerBuilder);
}

private static ImmutableList<CelIssue> errorsToIssues(Errors errors) {
Expand Down
4 changes: 2 additions & 2 deletions checker/src/main/java/dev/cel/checker/Env.java
Original file line number Diff line number Diff line change
Expand Up @@ -958,15 +958,15 @@ private static CelFunctionDecl sanitizeFunction(CelFunctionDecl func) {
}

CelFunctionDecl.Builder funcBuilder = func.toBuilder();
ImmutableList.Builder<CelOverloadDecl> overloadsBuilder = new ImmutableList.Builder<>();
ImmutableSet.Builder<CelOverloadDecl> overloadsBuilder = new ImmutableSet.Builder<>();
for (CelOverloadDecl overloadDecl : funcBuilder.overloads()) {
CelOverloadDecl.Builder overloadBuilder = overloadDecl.toBuilder();
CelType resultType = overloadBuilder.build().resultType();
if (isWellKnownType(resultType)) {
overloadBuilder.setResultType(getWellKnownType(resultType));
}

ImmutableList.Builder<CelType> parameterTypeBuilder = ImmutableList.builder();
ImmutableSet.Builder<CelType> parameterTypeBuilder = ImmutableSet.builder();
for (CelType paramType : overloadBuilder.parameterTypes()) {
if (isWellKnownType(paramType)) {
parameterTypeBuilder.add(getWellKnownType(paramType));
Expand Down
Loading