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
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,8 +64,10 @@ java_library(
"//common:source",
"//common/types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
],
Expand Down
161 changes: 122 additions & 39 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,9 @@
import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
Expand All@@ -35,8 +37,11 @@
import dev.cel.common.types.OptionalType;
import dev.cel.common.types.SimpleType;
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;

Expand All@@ -47,6 +52,18 @@
@AutoValue
public abstract class CelEnvironment {

@VisibleForTesting
static final ImmutableMap<String, CanonicalCelExtension> CEL_EXTENSION_CONFIG_MAP =
ImmutableMap.of(
"bindings", CanonicalCelExtension.BINDINGS,
"encoders", CanonicalCelExtension.ENCODERS,
"lists", CanonicalCelExtension.LISTS,
"math", CanonicalCelExtension.MATH,
"optional", CanonicalCelExtension.OPTIONAL,
"protos", CanonicalCelExtension.PROTOS,
"sets", CanonicalCelExtension.SETS,
"strings", CanonicalCelExtension.STRINGS);

/** Environment source in textual format (ex: textproto, YAML). */
public abstract Optional<Source> source();

Expand DownExpand Up@@ -126,12 +143,14 @@ public static Builder newBuilder() {
.setFunctions(ImmutableSet.of());
}

/** Extends the provided {@code cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
try {
CelTypeProvider celTypeProvider = cel.getTypeProvider();
CelBuilder celBuilder =
cel.toCelBuilder()
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setTypeProvider(celTypeProvider)
.setContainer(container())
.addVarDeclarations(
Expand All@@ -144,53 +163,58 @@ public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException
.collect(toImmutableList()));

if (!container().isEmpty()) {
celBuilder.setContainer(container());
compilerBuilder.setContainer(container());
}

addAllExtensions(celBuilder, celOptions);
addAllCompilerExtensions(compilerBuilder, celOptions);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

/** Extends the provided {@link Cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
try {
// Casting is necessary to only extend the compiler here
CelCompiler celCompiler = extend((CelCompiler) cel, celOptions);

CelRuntimeBuilder celRuntimeBuilder = cel.toRuntimeBuilder();
addAllRuntimeExtensions(celRuntimeBuilder, celOptions);

return celBuilder.build();
return CelFactory.combine(celCompiler, celRuntimeBuilder.build());
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

private void addAllExtensions(CelBuilder celBuilder, CelOptions celOptions) {
private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
switch (extensionConfig.name()) {
case "bindings":
celBuilder.addCompilerLibraries(CelExtensions.bindings());
break;
case "encoders":
celBuilder.addCompilerLibraries(CelExtensions.encoders());
celBuilder.addRuntimeLibraries(CelExtensions.encoders());
break;
case "math":
celBuilder.addCompilerLibraries(CelExtensions.math(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.math(celOptions));
break;
case "optional":
celBuilder.addCompilerLibraries(CelOptionalLibrary.INSTANCE);
celBuilder.addRuntimeLibraries(CelOptionalLibrary.INSTANCE);
break;
case "protos":
celBuilder.addCompilerLibraries(CelExtensions.protos());
break;
case "strings":
celBuilder.addCompilerLibraries(CelExtensions.strings());
celBuilder.addRuntimeLibraries(CelExtensions.strings());
break;
case "sets":
celBuilder.addCompilerLibraries(CelExtensions.sets(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.sets(celOptions));
break;
default:
throw new IllegalArgumentException("Unrecognized extension: " + extensionConfig.name());
}
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
}
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
throw new IllegalArgumentException("Unrecognized extension: " + extensionName);
}

return extension;
}

/** Represents a policy variable declaration. */
@AutoValue
public abstract static class VariableDecl {
Expand DownExpand Up@@ -529,4 +553,63 @@ public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}
}

@VisibleForTesting
enum CanonicalCelExtension {
BINDINGS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.bindings())),
PROTOS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.protos())),
ENCODERS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.encoders()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.encoders())),
MATH(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.math(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.math(options))),
OPTIONAL(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelOptionalLibrary.INSTANCE),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelOptionalLibrary.INSTANCE)),
STRINGS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.strings()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.strings())),
SETS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.sets(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.sets(options))),
LISTS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.lists()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.lists()));

@SuppressWarnings("ImmutableEnumChecker")
private final CompilerExtensionApplier compilerExtensionApplier;

@SuppressWarnings("ImmutableEnumChecker")
private final RuntimeExtensionApplier runtimeExtensionApplier;

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
}

void addCompilerExtension(CelCompilerBuilder compilerBuilder, CelOptions options) {
compilerExtensionApplier.apply(compilerBuilder, options);
}

void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
runtimeExtensionApplier.apply(runtimeBuilder, options);
}

CanonicalCelExtension(CompilerExtensionApplier compilerExtensionApplier) {
this(
compilerExtensionApplier,
(runtimeBuilder, options) -> {}); // no-op. Not all extensions augment the runtime.
}

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,11 @@
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;

final class CelEnvironmentYamlParser {
/**
* CelEnvironmentYamlParser intakes a YAML document that describes the structure of a CEL
* environment, parses it then creates a {@link CelEnvironment}.
*/
public final class CelEnvironmentYamlParser {
// Sentinel values to be returned for various declarations when parsing failure is encountered.
private static final TypeDecl ERROR_TYPE_DECL = TypeDecl.create(ERROR);
private static final VariableDecl ERROR_VARIABLE_DECL =
Expand Down
2 changes: 1 addition & 1 deletion bundle/src/test/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ java_library(
testonly = True,
srcs = glob(["*Test.java"]),
resources = [
"//testing/environment:environment_yaml_files",
"//testing/environment:extended_env",
],
deps = [
"//:java_truth",
Expand Down
33 changes: 33 additions & 0 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,12 @@

import static com.google.common.truth.Truth.assertThat;

import com.google.common.collect.ImmutableSet;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import dev.cel.bundle.CelEnvironment.CanonicalCelExtension;
import dev.cel.bundle.CelEnvironment.ExtensionConfig;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import org.junit.Test;
import org.junit.runner.RunWith;

Expand All@@ -35,4 +40,32 @@ public void newBuilder_defaults() {
assertThat(environment.variables()).isEmpty();
assertThat(environment.functions()).isEmpty();
}

@Test
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().setExtensions(extensionConfigs).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast =
cel.compile(
"cel.bind(x, 10, math.greatest([1,x])) < int(' 11 '.trim()) &&"
+ " optional.none().orValue(true) && [].flatten() == []")
.getAst();
boolean result = (boolean) cel.createProgram(ast).eval();

assertThat(extensionConfigs.size()).isEqualTo(CelEnvironment.CEL_EXTENSION_CONFIG_MAP.size());
assertThat(extensionConfigs.size()).isEqualTo(CanonicalCelExtension.values().length);
assertThat(result).isTrue();
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -74,6 +74,7 @@ public void environment_setExtensions() throws Exception {
"extensions:\n"
+ " - name: 'bindings'\n"
+ " - name: 'encoders'\n"
+ " - name: 'lists'\n"
+ " - name: 'math'\n"
+ " - name: 'optional'\n"
+ " - name: 'protos'\n"
Expand All@@ -91,6 +92,7 @@ public void environment_setExtensions() throws Exception {
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
Expand DownExpand Up@@ -307,6 +309,31 @@ public void environment_setMessageVariable() throws Exception {
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_setContainer() throws Exception {
String yamlConfig =
"container: google.rpc.context\n"
+ "variables:\n"
+ "- name: 'request'\n"
+ " type:\n"
+ " type_name: 'google.rpc.context.AttributeContext.Request'";

CelEnvironment environment = ENVIRONMENT_PARSER.parse(yamlConfig);

assertThat(environment)
.isEqualTo(
CelEnvironment.newBuilder()
.setContainer("google.rpc.context")
.setSource(environment.source().get())
.setVariables(
ImmutableSet.of(
VariableDecl.create(
"request",
TypeDecl.create("google.rpc.context.AttributeContext.Request"))))
.build());
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_withInlinedVariableDecl() throws Exception {
String yamlConfig =
Expand DownExpand Up@@ -675,8 +702,7 @@ private enum EnvironmentYamlResourceTestCase {
private final String yamlFileContent;
private final CelEnvironment expectedEnvironment;

EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment)
throws RuntimeException {
EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment) {
try {
this.yamlFileContent = readFile(yamlResourcePath);
} catch (IOException e) {
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/main/java/dev/cel/compiler/tools/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,14 +15,14 @@ java_binary(
main_class = "dev.cel.compiler.tools.CelCompilerTool",
neverlink = 1,
deps = [
"//bundle:environment",
"//bundle:environment_exception",
"//bundle:environment_yaml_parser",
"//common",
"//common:compiler_common",
"//common:options",
"//common:proto_ast",
"//compiler",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"@cel_spec//proto/cel/expr:checked_java_proto",
"@maven//:com_google_guava_guava",
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,8 +64,10 @@ java_library(
"//common:source",
"//common/types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
],
Expand Down
161 changes: 122 additions & 39 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,9 @@
import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
Expand All@@ -35,8 +37,11 @@
import dev.cel.common.types.OptionalType;
import dev.cel.common.types.SimpleType;
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;

Expand All@@ -47,6 +52,18 @@
@AutoValue
public abstract class CelEnvironment {

@VisibleForTesting
static final ImmutableMap<String, CanonicalCelExtension> CEL_EXTENSION_CONFIG_MAP =
ImmutableMap.of(
"bindings", CanonicalCelExtension.BINDINGS,
"encoders", CanonicalCelExtension.ENCODERS,
"lists", CanonicalCelExtension.LISTS,
"math", CanonicalCelExtension.MATH,
"optional", CanonicalCelExtension.OPTIONAL,
"protos", CanonicalCelExtension.PROTOS,
"sets", CanonicalCelExtension.SETS,
"strings", CanonicalCelExtension.STRINGS);

/** Environment source in textual format (ex: textproto, YAML). */
public abstract Optional<Source> source();

Expand DownExpand Up@@ -126,12 +143,14 @@ public static Builder newBuilder() {
.setFunctions(ImmutableSet.of());
}

/** Extends the provided {@code cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
try {
CelTypeProvider celTypeProvider = cel.getTypeProvider();
CelBuilder celBuilder =
cel.toCelBuilder()
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setTypeProvider(celTypeProvider)
.setContainer(container())
.addVarDeclarations(
Expand All@@ -144,53 +163,58 @@ public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException
.collect(toImmutableList()));

if (!container().isEmpty()) {
celBuilder.setContainer(container());
compilerBuilder.setContainer(container());
}

addAllExtensions(celBuilder, celOptions);
addAllCompilerExtensions(compilerBuilder, celOptions);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

/** Extends the provided {@link Cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
try {
// Casting is necessary to only extend the compiler here
CelCompiler celCompiler = extend((CelCompiler) cel, celOptions);

CelRuntimeBuilder celRuntimeBuilder = cel.toRuntimeBuilder();
addAllRuntimeExtensions(celRuntimeBuilder, celOptions);

return celBuilder.build();
return CelFactory.combine(celCompiler, celRuntimeBuilder.build());
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

private void addAllExtensions(CelBuilder celBuilder, CelOptions celOptions) {
private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
switch (extensionConfig.name()) {
case "bindings":
celBuilder.addCompilerLibraries(CelExtensions.bindings());
break;
case "encoders":
celBuilder.addCompilerLibraries(CelExtensions.encoders());
celBuilder.addRuntimeLibraries(CelExtensions.encoders());
break;
case "math":
celBuilder.addCompilerLibraries(CelExtensions.math(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.math(celOptions));
break;
case "optional":
celBuilder.addCompilerLibraries(CelOptionalLibrary.INSTANCE);
celBuilder.addRuntimeLibraries(CelOptionalLibrary.INSTANCE);
break;
case "protos":
celBuilder.addCompilerLibraries(CelExtensions.protos());
break;
case "strings":
celBuilder.addCompilerLibraries(CelExtensions.strings());
celBuilder.addRuntimeLibraries(CelExtensions.strings());
break;
case "sets":
celBuilder.addCompilerLibraries(CelExtensions.sets(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.sets(celOptions));
break;
default:
throw new IllegalArgumentException("Unrecognized extension: " + extensionConfig.name());
}
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
}
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
throw new IllegalArgumentException("Unrecognized extension: " + extensionName);
}

return extension;
}

/** Represents a policy variable declaration. */
@AutoValue
public abstract static class VariableDecl {
Expand DownExpand Up@@ -529,4 +553,63 @@ public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}
}

@VisibleForTesting
enum CanonicalCelExtension {
BINDINGS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.bindings())),
PROTOS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.protos())),
ENCODERS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.encoders()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.encoders())),
MATH(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.math(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.math(options))),
OPTIONAL(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelOptionalLibrary.INSTANCE),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelOptionalLibrary.INSTANCE)),
STRINGS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.strings()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.strings())),
SETS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.sets(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.sets(options))),
LISTS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.lists()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.lists()));

@SuppressWarnings("ImmutableEnumChecker")
private final CompilerExtensionApplier compilerExtensionApplier;

@SuppressWarnings("ImmutableEnumChecker")
private final RuntimeExtensionApplier runtimeExtensionApplier;

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
}

void addCompilerExtension(CelCompilerBuilder compilerBuilder, CelOptions options) {
compilerExtensionApplier.apply(compilerBuilder, options);
}

void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
runtimeExtensionApplier.apply(runtimeBuilder, options);
}

CanonicalCelExtension(CompilerExtensionApplier compilerExtensionApplier) {
this(
compilerExtensionApplier,
(runtimeBuilder, options) -> {}); // no-op. Not all extensions augment the runtime.
}

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,11 @@
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;

final class CelEnvironmentYamlParser {
/**
* CelEnvironmentYamlParser intakes a YAML document that describes the structure of a CEL
* environment, parses it then creates a {@link CelEnvironment}.
*/
public final class CelEnvironmentYamlParser {
// Sentinel values to be returned for various declarations when parsing failure is encountered.
private static final TypeDecl ERROR_TYPE_DECL = TypeDecl.create(ERROR);
private static final VariableDecl ERROR_VARIABLE_DECL =
Expand Down
2 changes: 1 addition & 1 deletion bundle/src/test/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ java_library(
testonly = True,
srcs = glob(["*Test.java"]),
resources = [
"//testing/environment:environment_yaml_files",
"//testing/environment:extended_env",
],
deps = [
"//:java_truth",
Expand Down
33 changes: 33 additions & 0 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,12 @@

import static com.google.common.truth.Truth.assertThat;

import com.google.common.collect.ImmutableSet;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import dev.cel.bundle.CelEnvironment.CanonicalCelExtension;
import dev.cel.bundle.CelEnvironment.ExtensionConfig;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import org.junit.Test;
import org.junit.runner.RunWith;

Expand All@@ -35,4 +40,32 @@ public void newBuilder_defaults() {
assertThat(environment.variables()).isEmpty();
assertThat(environment.functions()).isEmpty();
}

@Test
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().setExtensions(extensionConfigs).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast =
cel.compile(
"cel.bind(x, 10, math.greatest([1,x])) < int(' 11 '.trim()) &&"
+ " optional.none().orValue(true) && [].flatten() == []")
.getAst();
boolean result = (boolean) cel.createProgram(ast).eval();

assertThat(extensionConfigs.size()).isEqualTo(CelEnvironment.CEL_EXTENSION_CONFIG_MAP.size());
assertThat(extensionConfigs.size()).isEqualTo(CanonicalCelExtension.values().length);
assertThat(result).isTrue();
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -74,6 +74,7 @@ public void environment_setExtensions() throws Exception {
"extensions:\n"
+ " - name: 'bindings'\n"
+ " - name: 'encoders'\n"
+ " - name: 'lists'\n"
+ " - name: 'math'\n"
+ " - name: 'optional'\n"
+ " - name: 'protos'\n"
Expand All@@ -91,6 +92,7 @@ public void environment_setExtensions() throws Exception {
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
Expand DownExpand Up@@ -307,6 +309,31 @@ public void environment_setMessageVariable() throws Exception {
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_setContainer() throws Exception {
String yamlConfig =
"container: google.rpc.context\n"
+ "variables:\n"
+ "- name: 'request'\n"
+ " type:\n"
+ " type_name: 'google.rpc.context.AttributeContext.Request'";

CelEnvironment environment = ENVIRONMENT_PARSER.parse(yamlConfig);

assertThat(environment)
.isEqualTo(
CelEnvironment.newBuilder()
.setContainer("google.rpc.context")
.setSource(environment.source().get())
.setVariables(
ImmutableSet.of(
VariableDecl.create(
"request",
TypeDecl.create("google.rpc.context.AttributeContext.Request"))))
.build());
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_withInlinedVariableDecl() throws Exception {
String yamlConfig =
Expand DownExpand Up@@ -675,8 +702,7 @@ private enum EnvironmentYamlResourceTestCase {
private final String yamlFileContent;
private final CelEnvironment expectedEnvironment;

EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment)
throws RuntimeException {
EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment) {
try {
this.yamlFileContent = readFile(yamlResourcePath);
} catch (IOException e) {
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/main/java/dev/cel/compiler/tools/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,14 +15,14 @@ java_binary(
main_class = "dev.cel.compiler.tools.CelCompilerTool",
neverlink = 1,
deps = [
"//bundle:environment",
"//bundle:environment_exception",
"//bundle:environment_yaml_parser",
"//common",
"//common:compiler_common",
"//common:options",
"//common:proto_ast",
"//compiler",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"@cel_spec//proto/cel/expr:checked_java_proto",
"@maven//:com_google_guava_guava",
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,8 +64,10 @@ java_library(
"//common:source",
"//common/types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
],
Expand Down
161 changes: 122 additions & 39 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,9 @@
import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
Expand All@@ -35,8 +37,11 @@
import dev.cel.common.types.OptionalType;
import dev.cel.common.types.SimpleType;
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;

Expand All@@ -47,6 +52,18 @@
@AutoValue
public abstract class CelEnvironment {

@VisibleForTesting
static final ImmutableMap<String, CanonicalCelExtension> CEL_EXTENSION_CONFIG_MAP =
ImmutableMap.of(
"bindings", CanonicalCelExtension.BINDINGS,
"encoders", CanonicalCelExtension.ENCODERS,
"lists", CanonicalCelExtension.LISTS,
"math", CanonicalCelExtension.MATH,
"optional", CanonicalCelExtension.OPTIONAL,
"protos", CanonicalCelExtension.PROTOS,
"sets", CanonicalCelExtension.SETS,
"strings", CanonicalCelExtension.STRINGS);

/** Environment source in textual format (ex: textproto, YAML). */
public abstract Optional<Source> source();

Expand DownExpand Up@@ -126,12 +143,14 @@ public static Builder newBuilder() {
.setFunctions(ImmutableSet.of());
}

/** Extends the provided {@code cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
try {
CelTypeProvider celTypeProvider = cel.getTypeProvider();
CelBuilder celBuilder =
cel.toCelBuilder()
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setTypeProvider(celTypeProvider)
.setContainer(container())
.addVarDeclarations(
Expand All@@ -144,53 +163,58 @@ public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException
.collect(toImmutableList()));

if (!container().isEmpty()) {
celBuilder.setContainer(container());
compilerBuilder.setContainer(container());
}

addAllExtensions(celBuilder, celOptions);
addAllCompilerExtensions(compilerBuilder, celOptions);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

/** Extends the provided {@link Cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
try {
// Casting is necessary to only extend the compiler here
CelCompiler celCompiler = extend((CelCompiler) cel, celOptions);

CelRuntimeBuilder celRuntimeBuilder = cel.toRuntimeBuilder();
addAllRuntimeExtensions(celRuntimeBuilder, celOptions);

return celBuilder.build();
return CelFactory.combine(celCompiler, celRuntimeBuilder.build());
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

private void addAllExtensions(CelBuilder celBuilder, CelOptions celOptions) {
private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
switch (extensionConfig.name()) {
case "bindings":
celBuilder.addCompilerLibraries(CelExtensions.bindings());
break;
case "encoders":
celBuilder.addCompilerLibraries(CelExtensions.encoders());
celBuilder.addRuntimeLibraries(CelExtensions.encoders());
break;
case "math":
celBuilder.addCompilerLibraries(CelExtensions.math(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.math(celOptions));
break;
case "optional":
celBuilder.addCompilerLibraries(CelOptionalLibrary.INSTANCE);
celBuilder.addRuntimeLibraries(CelOptionalLibrary.INSTANCE);
break;
case "protos":
celBuilder.addCompilerLibraries(CelExtensions.protos());
break;
case "strings":
celBuilder.addCompilerLibraries(CelExtensions.strings());
celBuilder.addRuntimeLibraries(CelExtensions.strings());
break;
case "sets":
celBuilder.addCompilerLibraries(CelExtensions.sets(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.sets(celOptions));
break;
default:
throw new IllegalArgumentException("Unrecognized extension: " + extensionConfig.name());
}
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
}
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
throw new IllegalArgumentException("Unrecognized extension: " + extensionName);
}

return extension;
}

/** Represents a policy variable declaration. */
@AutoValue
public abstract static class VariableDecl {
Expand DownExpand Up@@ -529,4 +553,63 @@ public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}
}

@VisibleForTesting
enum CanonicalCelExtension {
BINDINGS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.bindings())),
PROTOS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.protos())),
ENCODERS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.encoders()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.encoders())),
MATH(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.math(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.math(options))),
OPTIONAL(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelOptionalLibrary.INSTANCE),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelOptionalLibrary.INSTANCE)),
STRINGS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.strings()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.strings())),
SETS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.sets(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.sets(options))),
LISTS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.lists()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.lists()));

@SuppressWarnings("ImmutableEnumChecker")
private final CompilerExtensionApplier compilerExtensionApplier;

@SuppressWarnings("ImmutableEnumChecker")
private final RuntimeExtensionApplier runtimeExtensionApplier;

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
}

void addCompilerExtension(CelCompilerBuilder compilerBuilder, CelOptions options) {
compilerExtensionApplier.apply(compilerBuilder, options);
}

void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
runtimeExtensionApplier.apply(runtimeBuilder, options);
}

CanonicalCelExtension(CompilerExtensionApplier compilerExtensionApplier) {
this(
compilerExtensionApplier,
(runtimeBuilder, options) -> {}); // no-op. Not all extensions augment the runtime.
}

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,11 @@
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;

final class CelEnvironmentYamlParser {
/**
* CelEnvironmentYamlParser intakes a YAML document that describes the structure of a CEL
* environment, parses it then creates a {@link CelEnvironment}.
*/
public final class CelEnvironmentYamlParser {
// Sentinel values to be returned for various declarations when parsing failure is encountered.
private static final TypeDecl ERROR_TYPE_DECL = TypeDecl.create(ERROR);
private static final VariableDecl ERROR_VARIABLE_DECL =
Expand Down
2 changes: 1 addition & 1 deletion bundle/src/test/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ java_library(
testonly = True,
srcs = glob(["*Test.java"]),
resources = [
"//testing/environment:environment_yaml_files",
"//testing/environment:extended_env",
],
deps = [
"//:java_truth",
Expand Down
33 changes: 33 additions & 0 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,12 @@

import static com.google.common.truth.Truth.assertThat;

import com.google.common.collect.ImmutableSet;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import dev.cel.bundle.CelEnvironment.CanonicalCelExtension;
import dev.cel.bundle.CelEnvironment.ExtensionConfig;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import org.junit.Test;
import org.junit.runner.RunWith;

Expand All@@ -35,4 +40,32 @@ public void newBuilder_defaults() {
assertThat(environment.variables()).isEmpty();
assertThat(environment.functions()).isEmpty();
}

@Test
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().setExtensions(extensionConfigs).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast =
cel.compile(
"cel.bind(x, 10, math.greatest([1,x])) < int(' 11 '.trim()) &&"
+ " optional.none().orValue(true) && [].flatten() == []")
.getAst();
boolean result = (boolean) cel.createProgram(ast).eval();

assertThat(extensionConfigs.size()).isEqualTo(CelEnvironment.CEL_EXTENSION_CONFIG_MAP.size());
assertThat(extensionConfigs.size()).isEqualTo(CanonicalCelExtension.values().length);
assertThat(result).isTrue();
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -74,6 +74,7 @@ public void environment_setExtensions() throws Exception {
"extensions:\n"
+ " - name: 'bindings'\n"
+ " - name: 'encoders'\n"
+ " - name: 'lists'\n"
+ " - name: 'math'\n"
+ " - name: 'optional'\n"
+ " - name: 'protos'\n"
Expand All@@ -91,6 +92,7 @@ public void environment_setExtensions() throws Exception {
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
Expand DownExpand Up@@ -307,6 +309,31 @@ public void environment_setMessageVariable() throws Exception {
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_setContainer() throws Exception {
String yamlConfig =
"container: google.rpc.context\n"
+ "variables:\n"
+ "- name: 'request'\n"
+ " type:\n"
+ " type_name: 'google.rpc.context.AttributeContext.Request'";

CelEnvironment environment = ENVIRONMENT_PARSER.parse(yamlConfig);

assertThat(environment)
.isEqualTo(
CelEnvironment.newBuilder()
.setContainer("google.rpc.context")
.setSource(environment.source().get())
.setVariables(
ImmutableSet.of(
VariableDecl.create(
"request",
TypeDecl.create("google.rpc.context.AttributeContext.Request"))))
.build());
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_withInlinedVariableDecl() throws Exception {
String yamlConfig =
Expand DownExpand Up@@ -675,8 +702,7 @@ private enum EnvironmentYamlResourceTestCase {
private final String yamlFileContent;
private final CelEnvironment expectedEnvironment;

EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment)
throws RuntimeException {
EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment) {
try {
this.yamlFileContent = readFile(yamlResourcePath);
} catch (IOException e) {
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/main/java/dev/cel/compiler/tools/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,14 +15,14 @@ java_binary(
main_class = "dev.cel.compiler.tools.CelCompilerTool",
neverlink = 1,
deps = [
"//bundle:environment",
"//bundle:environment_exception",
"//bundle:environment_yaml_parser",
"//common",
"//common:compiler_common",
"//common:options",
"//common:proto_ast",
"//compiler",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"@cel_spec//proto/cel/expr:checked_java_proto",
"@maven//:com_google_guava_guava",
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,8 +64,10 @@ java_library(
"//common:source",
"//common/types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
],
Expand Down
161 changes: 122 additions & 39 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,9 @@
import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
Expand All@@ -35,8 +37,11 @@
import dev.cel.common.types.OptionalType;
import dev.cel.common.types.SimpleType;
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;

Expand All@@ -47,6 +52,18 @@
@AutoValue
public abstract class CelEnvironment {

@VisibleForTesting
static final ImmutableMap<String, CanonicalCelExtension> CEL_EXTENSION_CONFIG_MAP =
ImmutableMap.of(
"bindings", CanonicalCelExtension.BINDINGS,
"encoders", CanonicalCelExtension.ENCODERS,
"lists", CanonicalCelExtension.LISTS,
"math", CanonicalCelExtension.MATH,
"optional", CanonicalCelExtension.OPTIONAL,
"protos", CanonicalCelExtension.PROTOS,
"sets", CanonicalCelExtension.SETS,
"strings", CanonicalCelExtension.STRINGS);

/** Environment source in textual format (ex: textproto, YAML). */
public abstract Optional<Source> source();

Expand DownExpand Up@@ -126,12 +143,14 @@ public static Builder newBuilder() {
.setFunctions(ImmutableSet.of());
}

/** Extends the provided {@code cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
try {
CelTypeProvider celTypeProvider = cel.getTypeProvider();
CelBuilder celBuilder =
cel.toCelBuilder()
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setTypeProvider(celTypeProvider)
.setContainer(container())
.addVarDeclarations(
Expand All@@ -144,53 +163,58 @@ public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException
.collect(toImmutableList()));

if (!container().isEmpty()) {
celBuilder.setContainer(container());
compilerBuilder.setContainer(container());
}

addAllExtensions(celBuilder, celOptions);
addAllCompilerExtensions(compilerBuilder, celOptions);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

/** Extends the provided {@link Cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
try {
// Casting is necessary to only extend the compiler here
CelCompiler celCompiler = extend((CelCompiler) cel, celOptions);

CelRuntimeBuilder celRuntimeBuilder = cel.toRuntimeBuilder();
addAllRuntimeExtensions(celRuntimeBuilder, celOptions);

return celBuilder.build();
return CelFactory.combine(celCompiler, celRuntimeBuilder.build());
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

private void addAllExtensions(CelBuilder celBuilder, CelOptions celOptions) {
private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
switch (extensionConfig.name()) {
case "bindings":
celBuilder.addCompilerLibraries(CelExtensions.bindings());
break;
case "encoders":
celBuilder.addCompilerLibraries(CelExtensions.encoders());
celBuilder.addRuntimeLibraries(CelExtensions.encoders());
break;
case "math":
celBuilder.addCompilerLibraries(CelExtensions.math(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.math(celOptions));
break;
case "optional":
celBuilder.addCompilerLibraries(CelOptionalLibrary.INSTANCE);
celBuilder.addRuntimeLibraries(CelOptionalLibrary.INSTANCE);
break;
case "protos":
celBuilder.addCompilerLibraries(CelExtensions.protos());
break;
case "strings":
celBuilder.addCompilerLibraries(CelExtensions.strings());
celBuilder.addRuntimeLibraries(CelExtensions.strings());
break;
case "sets":
celBuilder.addCompilerLibraries(CelExtensions.sets(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.sets(celOptions));
break;
default:
throw new IllegalArgumentException("Unrecognized extension: " + extensionConfig.name());
}
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
}
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
throw new IllegalArgumentException("Unrecognized extension: " + extensionName);
}

return extension;
}

/** Represents a policy variable declaration. */
@AutoValue
public abstract static class VariableDecl {
Expand DownExpand Up@@ -529,4 +553,63 @@ public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}
}

@VisibleForTesting
enum CanonicalCelExtension {
BINDINGS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.bindings())),
PROTOS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.protos())),
ENCODERS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.encoders()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.encoders())),
MATH(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.math(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.math(options))),
OPTIONAL(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelOptionalLibrary.INSTANCE),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelOptionalLibrary.INSTANCE)),
STRINGS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.strings()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.strings())),
SETS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.sets(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.sets(options))),
LISTS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.lists()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.lists()));

@SuppressWarnings("ImmutableEnumChecker")
private final CompilerExtensionApplier compilerExtensionApplier;

@SuppressWarnings("ImmutableEnumChecker")
private final RuntimeExtensionApplier runtimeExtensionApplier;

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
}

void addCompilerExtension(CelCompilerBuilder compilerBuilder, CelOptions options) {
compilerExtensionApplier.apply(compilerBuilder, options);
}

void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
runtimeExtensionApplier.apply(runtimeBuilder, options);
}

CanonicalCelExtension(CompilerExtensionApplier compilerExtensionApplier) {
this(
compilerExtensionApplier,
(runtimeBuilder, options) -> {}); // no-op. Not all extensions augment the runtime.
}

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,11 @@
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;

final class CelEnvironmentYamlParser {
/**
* CelEnvironmentYamlParser intakes a YAML document that describes the structure of a CEL
* environment, parses it then creates a {@link CelEnvironment}.
*/
public final class CelEnvironmentYamlParser {
// Sentinel values to be returned for various declarations when parsing failure is encountered.
private static final TypeDecl ERROR_TYPE_DECL = TypeDecl.create(ERROR);
private static final VariableDecl ERROR_VARIABLE_DECL =
Expand Down
2 changes: 1 addition & 1 deletion bundle/src/test/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ java_library(
testonly = True,
srcs = glob(["*Test.java"]),
resources = [
"//testing/environment:environment_yaml_files",
"//testing/environment:extended_env",
],
deps = [
"//:java_truth",
Expand Down
33 changes: 33 additions & 0 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,12 @@

import static com.google.common.truth.Truth.assertThat;

import com.google.common.collect.ImmutableSet;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import dev.cel.bundle.CelEnvironment.CanonicalCelExtension;
import dev.cel.bundle.CelEnvironment.ExtensionConfig;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import org.junit.Test;
import org.junit.runner.RunWith;

Expand All@@ -35,4 +40,32 @@ public void newBuilder_defaults() {
assertThat(environment.variables()).isEmpty();
assertThat(environment.functions()).isEmpty();
}

@Test
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().setExtensions(extensionConfigs).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast =
cel.compile(
"cel.bind(x, 10, math.greatest([1,x])) < int(' 11 '.trim()) &&"
+ " optional.none().orValue(true) && [].flatten() == []")
.getAst();
boolean result = (boolean) cel.createProgram(ast).eval();

assertThat(extensionConfigs.size()).isEqualTo(CelEnvironment.CEL_EXTENSION_CONFIG_MAP.size());
assertThat(extensionConfigs.size()).isEqualTo(CanonicalCelExtension.values().length);
assertThat(result).isTrue();
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -74,6 +74,7 @@ public void environment_setExtensions() throws Exception {
"extensions:\n"
+ " - name: 'bindings'\n"
+ " - name: 'encoders'\n"
+ " - name: 'lists'\n"
+ " - name: 'math'\n"
+ " - name: 'optional'\n"
+ " - name: 'protos'\n"
Expand All@@ -91,6 +92,7 @@ public void environment_setExtensions() throws Exception {
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
Expand DownExpand Up@@ -307,6 +309,31 @@ public void environment_setMessageVariable() throws Exception {
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_setContainer() throws Exception {
String yamlConfig =
"container: google.rpc.context\n"
+ "variables:\n"
+ "- name: 'request'\n"
+ " type:\n"
+ " type_name: 'google.rpc.context.AttributeContext.Request'";

CelEnvironment environment = ENVIRONMENT_PARSER.parse(yamlConfig);

assertThat(environment)
.isEqualTo(
CelEnvironment.newBuilder()
.setContainer("google.rpc.context")
.setSource(environment.source().get())
.setVariables(
ImmutableSet.of(
VariableDecl.create(
"request",
TypeDecl.create("google.rpc.context.AttributeContext.Request"))))
.build());
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_withInlinedVariableDecl() throws Exception {
String yamlConfig =
Expand DownExpand Up@@ -675,8 +702,7 @@ private enum EnvironmentYamlResourceTestCase {
private final String yamlFileContent;
private final CelEnvironment expectedEnvironment;

EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment)
throws RuntimeException {
EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment) {
try {
this.yamlFileContent = readFile(yamlResourcePath);
} catch (IOException e) {
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/main/java/dev/cel/compiler/tools/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,14 +15,14 @@ java_binary(
main_class = "dev.cel.compiler.tools.CelCompilerTool",
neverlink = 1,
deps = [
"//bundle:environment",
"//bundle:environment_exception",
"//bundle:environment_yaml_parser",
"//common",
"//common:compiler_common",
"//common:options",
"//common:proto_ast",
"//compiler",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"@cel_spec//proto/cel/expr:checked_java_proto",
"@maven//:com_google_guava_guava",
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,8 +64,10 @@ java_library(
"//common:source",
"//common/types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
],
Expand Down
161 changes: 122 additions & 39 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,9 @@
import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
Expand All@@ -35,8 +37,11 @@
import dev.cel.common.types.OptionalType;
import dev.cel.common.types.SimpleType;
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;

Expand All@@ -47,6 +52,18 @@
@AutoValue
public abstract class CelEnvironment {

@VisibleForTesting
static final ImmutableMap<String, CanonicalCelExtension> CEL_EXTENSION_CONFIG_MAP =
ImmutableMap.of(
"bindings", CanonicalCelExtension.BINDINGS,
"encoders", CanonicalCelExtension.ENCODERS,
"lists", CanonicalCelExtension.LISTS,
"math", CanonicalCelExtension.MATH,
"optional", CanonicalCelExtension.OPTIONAL,
"protos", CanonicalCelExtension.PROTOS,
"sets", CanonicalCelExtension.SETS,
"strings", CanonicalCelExtension.STRINGS);

/** Environment source in textual format (ex: textproto, YAML). */
public abstract Optional<Source> source();

Expand DownExpand Up@@ -126,12 +143,14 @@ public static Builder newBuilder() {
.setFunctions(ImmutableSet.of());
}

/** Extends the provided {@code cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
try {
CelTypeProvider celTypeProvider = cel.getTypeProvider();
CelBuilder celBuilder =
cel.toCelBuilder()
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setTypeProvider(celTypeProvider)
.setContainer(container())
.addVarDeclarations(
Expand All@@ -144,53 +163,58 @@ public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException
.collect(toImmutableList()));

if (!container().isEmpty()) {
celBuilder.setContainer(container());
compilerBuilder.setContainer(container());
}

addAllExtensions(celBuilder, celOptions);
addAllCompilerExtensions(compilerBuilder, celOptions);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

/** Extends the provided {@link Cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
try {
// Casting is necessary to only extend the compiler here
CelCompiler celCompiler = extend((CelCompiler) cel, celOptions);

CelRuntimeBuilder celRuntimeBuilder = cel.toRuntimeBuilder();
addAllRuntimeExtensions(celRuntimeBuilder, celOptions);

return celBuilder.build();
return CelFactory.combine(celCompiler, celRuntimeBuilder.build());
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

private void addAllExtensions(CelBuilder celBuilder, CelOptions celOptions) {
private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
switch (extensionConfig.name()) {
case "bindings":
celBuilder.addCompilerLibraries(CelExtensions.bindings());
break;
case "encoders":
celBuilder.addCompilerLibraries(CelExtensions.encoders());
celBuilder.addRuntimeLibraries(CelExtensions.encoders());
break;
case "math":
celBuilder.addCompilerLibraries(CelExtensions.math(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.math(celOptions));
break;
case "optional":
celBuilder.addCompilerLibraries(CelOptionalLibrary.INSTANCE);
celBuilder.addRuntimeLibraries(CelOptionalLibrary.INSTANCE);
break;
case "protos":
celBuilder.addCompilerLibraries(CelExtensions.protos());
break;
case "strings":
celBuilder.addCompilerLibraries(CelExtensions.strings());
celBuilder.addRuntimeLibraries(CelExtensions.strings());
break;
case "sets":
celBuilder.addCompilerLibraries(CelExtensions.sets(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.sets(celOptions));
break;
default:
throw new IllegalArgumentException("Unrecognized extension: " + extensionConfig.name());
}
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
}
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
throw new IllegalArgumentException("Unrecognized extension: " + extensionName);
}

return extension;
}

/** Represents a policy variable declaration. */
@AutoValue
public abstract static class VariableDecl {
Expand DownExpand Up@@ -529,4 +553,63 @@ public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}
}

@VisibleForTesting
enum CanonicalCelExtension {
BINDINGS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.bindings())),
PROTOS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.protos())),
ENCODERS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.encoders()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.encoders())),
MATH(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.math(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.math(options))),
OPTIONAL(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelOptionalLibrary.INSTANCE),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelOptionalLibrary.INSTANCE)),
STRINGS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.strings()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.strings())),
SETS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.sets(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.sets(options))),
LISTS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.lists()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.lists()));

@SuppressWarnings("ImmutableEnumChecker")
private final CompilerExtensionApplier compilerExtensionApplier;

@SuppressWarnings("ImmutableEnumChecker")
private final RuntimeExtensionApplier runtimeExtensionApplier;

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
}

void addCompilerExtension(CelCompilerBuilder compilerBuilder, CelOptions options) {
compilerExtensionApplier.apply(compilerBuilder, options);
}

void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
runtimeExtensionApplier.apply(runtimeBuilder, options);
}

CanonicalCelExtension(CompilerExtensionApplier compilerExtensionApplier) {
this(
compilerExtensionApplier,
(runtimeBuilder, options) -> {}); // no-op. Not all extensions augment the runtime.
}

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,11 @@
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;

final class CelEnvironmentYamlParser {
/**
* CelEnvironmentYamlParser intakes a YAML document that describes the structure of a CEL
* environment, parses it then creates a {@link CelEnvironment}.
*/
public final class CelEnvironmentYamlParser {
// Sentinel values to be returned for various declarations when parsing failure is encountered.
private static final TypeDecl ERROR_TYPE_DECL = TypeDecl.create(ERROR);
private static final VariableDecl ERROR_VARIABLE_DECL =
Expand Down
2 changes: 1 addition & 1 deletion bundle/src/test/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ java_library(
testonly = True,
srcs = glob(["*Test.java"]),
resources = [
"//testing/environment:environment_yaml_files",
"//testing/environment:extended_env",
],
deps = [
"//:java_truth",
Expand Down
33 changes: 33 additions & 0 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,12 @@

import static com.google.common.truth.Truth.assertThat;

import com.google.common.collect.ImmutableSet;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import dev.cel.bundle.CelEnvironment.CanonicalCelExtension;
import dev.cel.bundle.CelEnvironment.ExtensionConfig;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import org.junit.Test;
import org.junit.runner.RunWith;

Expand All@@ -35,4 +40,32 @@ public void newBuilder_defaults() {
assertThat(environment.variables()).isEmpty();
assertThat(environment.functions()).isEmpty();
}

@Test
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().setExtensions(extensionConfigs).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast =
cel.compile(
"cel.bind(x, 10, math.greatest([1,x])) < int(' 11 '.trim()) &&"
+ " optional.none().orValue(true) && [].flatten() == []")
.getAst();
boolean result = (boolean) cel.createProgram(ast).eval();

assertThat(extensionConfigs.size()).isEqualTo(CelEnvironment.CEL_EXTENSION_CONFIG_MAP.size());
assertThat(extensionConfigs.size()).isEqualTo(CanonicalCelExtension.values().length);
assertThat(result).isTrue();
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -74,6 +74,7 @@ public void environment_setExtensions() throws Exception {
"extensions:\n"
+ " - name: 'bindings'\n"
+ " - name: 'encoders'\n"
+ " - name: 'lists'\n"
+ " - name: 'math'\n"
+ " - name: 'optional'\n"
+ " - name: 'protos'\n"
Expand All@@ -91,6 +92,7 @@ public void environment_setExtensions() throws Exception {
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
Expand DownExpand Up@@ -307,6 +309,31 @@ public void environment_setMessageVariable() throws Exception {
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_setContainer() throws Exception {
String yamlConfig =
"container: google.rpc.context\n"
+ "variables:\n"
+ "- name: 'request'\n"
+ " type:\n"
+ " type_name: 'google.rpc.context.AttributeContext.Request'";

CelEnvironment environment = ENVIRONMENT_PARSER.parse(yamlConfig);

assertThat(environment)
.isEqualTo(
CelEnvironment.newBuilder()
.setContainer("google.rpc.context")
.setSource(environment.source().get())
.setVariables(
ImmutableSet.of(
VariableDecl.create(
"request",
TypeDecl.create("google.rpc.context.AttributeContext.Request"))))
.build());
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_withInlinedVariableDecl() throws Exception {
String yamlConfig =
Expand DownExpand Up@@ -675,8 +702,7 @@ private enum EnvironmentYamlResourceTestCase {
private final String yamlFileContent;
private final CelEnvironment expectedEnvironment;

EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment)
throws RuntimeException {
EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment) {
try {
this.yamlFileContent = readFile(yamlResourcePath);
} catch (IOException e) {
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/main/java/dev/cel/compiler/tools/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,14 +15,14 @@ java_binary(
main_class = "dev.cel.compiler.tools.CelCompilerTool",
neverlink = 1,
deps = [
"//bundle:environment",
"//bundle:environment_exception",
"//bundle:environment_yaml_parser",
"//common",
"//common:compiler_common",
"//common:options",
"//common:proto_ast",
"//compiler",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"@cel_spec//proto/cel/expr:checked_java_proto",
"@maven//:com_google_guava_guava",
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,8 +64,10 @@ java_library(
"//common:source",
"//common/types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
],
Expand Down
161 changes: 122 additions & 39 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,9 @@
import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
Expand All@@ -35,8 +37,11 @@
import dev.cel.common.types.OptionalType;
import dev.cel.common.types.SimpleType;
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;

Expand All@@ -47,6 +52,18 @@
@AutoValue
public abstract class CelEnvironment {

@VisibleForTesting
static final ImmutableMap<String, CanonicalCelExtension> CEL_EXTENSION_CONFIG_MAP =
ImmutableMap.of(
"bindings", CanonicalCelExtension.BINDINGS,
"encoders", CanonicalCelExtension.ENCODERS,
"lists", CanonicalCelExtension.LISTS,
"math", CanonicalCelExtension.MATH,
"optional", CanonicalCelExtension.OPTIONAL,
"protos", CanonicalCelExtension.PROTOS,
"sets", CanonicalCelExtension.SETS,
"strings", CanonicalCelExtension.STRINGS);

/** Environment source in textual format (ex: textproto, YAML). */
public abstract Optional<Source> source();

Expand DownExpand Up@@ -126,12 +143,14 @@ public static Builder newBuilder() {
.setFunctions(ImmutableSet.of());
}

/** Extends the provided {@code cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
try {
CelTypeProvider celTypeProvider = cel.getTypeProvider();
CelBuilder celBuilder =
cel.toCelBuilder()
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setTypeProvider(celTypeProvider)
.setContainer(container())
.addVarDeclarations(
Expand All@@ -144,53 +163,58 @@ public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException
.collect(toImmutableList()));

if (!container().isEmpty()) {
celBuilder.setContainer(container());
compilerBuilder.setContainer(container());
}

addAllExtensions(celBuilder, celOptions);
addAllCompilerExtensions(compilerBuilder, celOptions);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

/** Extends the provided {@link Cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
try {
// Casting is necessary to only extend the compiler here
CelCompiler celCompiler = extend((CelCompiler) cel, celOptions);

CelRuntimeBuilder celRuntimeBuilder = cel.toRuntimeBuilder();
addAllRuntimeExtensions(celRuntimeBuilder, celOptions);

return celBuilder.build();
return CelFactory.combine(celCompiler, celRuntimeBuilder.build());
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

private void addAllExtensions(CelBuilder celBuilder, CelOptions celOptions) {
private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
switch (extensionConfig.name()) {
case "bindings":
celBuilder.addCompilerLibraries(CelExtensions.bindings());
break;
case "encoders":
celBuilder.addCompilerLibraries(CelExtensions.encoders());
celBuilder.addRuntimeLibraries(CelExtensions.encoders());
break;
case "math":
celBuilder.addCompilerLibraries(CelExtensions.math(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.math(celOptions));
break;
case "optional":
celBuilder.addCompilerLibraries(CelOptionalLibrary.INSTANCE);
celBuilder.addRuntimeLibraries(CelOptionalLibrary.INSTANCE);
break;
case "protos":
celBuilder.addCompilerLibraries(CelExtensions.protos());
break;
case "strings":
celBuilder.addCompilerLibraries(CelExtensions.strings());
celBuilder.addRuntimeLibraries(CelExtensions.strings());
break;
case "sets":
celBuilder.addCompilerLibraries(CelExtensions.sets(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.sets(celOptions));
break;
default:
throw new IllegalArgumentException("Unrecognized extension: " + extensionConfig.name());
}
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
}
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
throw new IllegalArgumentException("Unrecognized extension: " + extensionName);
}

return extension;
}

/** Represents a policy variable declaration. */
@AutoValue
public abstract static class VariableDecl {
Expand DownExpand Up@@ -529,4 +553,63 @@ public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}
}

@VisibleForTesting
enum CanonicalCelExtension {
BINDINGS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.bindings())),
PROTOS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.protos())),
ENCODERS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.encoders()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.encoders())),
MATH(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.math(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.math(options))),
OPTIONAL(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelOptionalLibrary.INSTANCE),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelOptionalLibrary.INSTANCE)),
STRINGS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.strings()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.strings())),
SETS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.sets(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.sets(options))),
LISTS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.lists()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.lists()));

@SuppressWarnings("ImmutableEnumChecker")
private final CompilerExtensionApplier compilerExtensionApplier;

@SuppressWarnings("ImmutableEnumChecker")
private final RuntimeExtensionApplier runtimeExtensionApplier;

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
}

void addCompilerExtension(CelCompilerBuilder compilerBuilder, CelOptions options) {
compilerExtensionApplier.apply(compilerBuilder, options);
}

void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
runtimeExtensionApplier.apply(runtimeBuilder, options);
}

CanonicalCelExtension(CompilerExtensionApplier compilerExtensionApplier) {
this(
compilerExtensionApplier,
(runtimeBuilder, options) -> {}); // no-op. Not all extensions augment the runtime.
}

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,11 @@
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;

final class CelEnvironmentYamlParser {
/**
* CelEnvironmentYamlParser intakes a YAML document that describes the structure of a CEL
* environment, parses it then creates a {@link CelEnvironment}.
*/
public final class CelEnvironmentYamlParser {
// Sentinel values to be returned for various declarations when parsing failure is encountered.
private static final TypeDecl ERROR_TYPE_DECL = TypeDecl.create(ERROR);
private static final VariableDecl ERROR_VARIABLE_DECL =
Expand Down
2 changes: 1 addition & 1 deletion bundle/src/test/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ java_library(
testonly = True,
srcs = glob(["*Test.java"]),
resources = [
"//testing/environment:environment_yaml_files",
"//testing/environment:extended_env",
],
deps = [
"//:java_truth",
Expand Down
33 changes: 33 additions & 0 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,12 @@

import static com.google.common.truth.Truth.assertThat;

import com.google.common.collect.ImmutableSet;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import dev.cel.bundle.CelEnvironment.CanonicalCelExtension;
import dev.cel.bundle.CelEnvironment.ExtensionConfig;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import org.junit.Test;
import org.junit.runner.RunWith;

Expand All@@ -35,4 +40,32 @@ public void newBuilder_defaults() {
assertThat(environment.variables()).isEmpty();
assertThat(environment.functions()).isEmpty();
}

@Test
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().setExtensions(extensionConfigs).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast =
cel.compile(
"cel.bind(x, 10, math.greatest([1,x])) < int(' 11 '.trim()) &&"
+ " optional.none().orValue(true) && [].flatten() == []")
.getAst();
boolean result = (boolean) cel.createProgram(ast).eval();

assertThat(extensionConfigs.size()).isEqualTo(CelEnvironment.CEL_EXTENSION_CONFIG_MAP.size());
assertThat(extensionConfigs.size()).isEqualTo(CanonicalCelExtension.values().length);
assertThat(result).isTrue();
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -74,6 +74,7 @@ public void environment_setExtensions() throws Exception {
"extensions:\n"
+ " - name: 'bindings'\n"
+ " - name: 'encoders'\n"
+ " - name: 'lists'\n"
+ " - name: 'math'\n"
+ " - name: 'optional'\n"
+ " - name: 'protos'\n"
Expand All@@ -91,6 +92,7 @@ public void environment_setExtensions() throws Exception {
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
Expand DownExpand Up@@ -307,6 +309,31 @@ public void environment_setMessageVariable() throws Exception {
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_setContainer() throws Exception {
String yamlConfig =
"container: google.rpc.context\n"
+ "variables:\n"
+ "- name: 'request'\n"
+ " type:\n"
+ " type_name: 'google.rpc.context.AttributeContext.Request'";

CelEnvironment environment = ENVIRONMENT_PARSER.parse(yamlConfig);

assertThat(environment)
.isEqualTo(
CelEnvironment.newBuilder()
.setContainer("google.rpc.context")
.setSource(environment.source().get())
.setVariables(
ImmutableSet.of(
VariableDecl.create(
"request",
TypeDecl.create("google.rpc.context.AttributeContext.Request"))))
.build());
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_withInlinedVariableDecl() throws Exception {
String yamlConfig =
Expand DownExpand Up@@ -675,8 +702,7 @@ private enum EnvironmentYamlResourceTestCase {
private final String yamlFileContent;
private final CelEnvironment expectedEnvironment;

EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment)
throws RuntimeException {
EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment) {
try {
this.yamlFileContent = readFile(yamlResourcePath);
} catch (IOException e) {
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/main/java/dev/cel/compiler/tools/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,14 +15,14 @@ java_binary(
main_class = "dev.cel.compiler.tools.CelCompilerTool",
neverlink = 1,
deps = [
"//bundle:environment",
"//bundle:environment_exception",
"//bundle:environment_yaml_parser",
"//common",
"//common:compiler_common",
"//common:options",
"//common:proto_ast",
"//compiler",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"@cel_spec//proto/cel/expr:checked_java_proto",
"@maven//:com_google_guava_guava",
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,8 +64,10 @@ java_library(
"//common:source",
"//common/types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
],
Expand Down
161 changes: 122 additions & 39 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,9 @@
import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
Expand All@@ -35,8 +37,11 @@
import dev.cel.common.types.OptionalType;
import dev.cel.common.types.SimpleType;
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;

Expand All@@ -47,6 +52,18 @@
@AutoValue
public abstract class CelEnvironment {

@VisibleForTesting
static final ImmutableMap<String, CanonicalCelExtension> CEL_EXTENSION_CONFIG_MAP =
ImmutableMap.of(
"bindings", CanonicalCelExtension.BINDINGS,
"encoders", CanonicalCelExtension.ENCODERS,
"lists", CanonicalCelExtension.LISTS,
"math", CanonicalCelExtension.MATH,
"optional", CanonicalCelExtension.OPTIONAL,
"protos", CanonicalCelExtension.PROTOS,
"sets", CanonicalCelExtension.SETS,
"strings", CanonicalCelExtension.STRINGS);

/** Environment source in textual format (ex: textproto, YAML). */
public abstract Optional<Source> source();

Expand DownExpand Up@@ -126,12 +143,14 @@ public static Builder newBuilder() {
.setFunctions(ImmutableSet.of());
}

/** Extends the provided {@code cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
try {
CelTypeProvider celTypeProvider = cel.getTypeProvider();
CelBuilder celBuilder =
cel.toCelBuilder()
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setTypeProvider(celTypeProvider)
.setContainer(container())
.addVarDeclarations(
Expand All@@ -144,53 +163,58 @@ public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException
.collect(toImmutableList()));

if (!container().isEmpty()) {
celBuilder.setContainer(container());
compilerBuilder.setContainer(container());
}

addAllExtensions(celBuilder, celOptions);
addAllCompilerExtensions(compilerBuilder, celOptions);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

/** Extends the provided {@link Cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
try {
// Casting is necessary to only extend the compiler here
CelCompiler celCompiler = extend((CelCompiler) cel, celOptions);

CelRuntimeBuilder celRuntimeBuilder = cel.toRuntimeBuilder();
addAllRuntimeExtensions(celRuntimeBuilder, celOptions);

return celBuilder.build();
return CelFactory.combine(celCompiler, celRuntimeBuilder.build());
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

private void addAllExtensions(CelBuilder celBuilder, CelOptions celOptions) {
private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
switch (extensionConfig.name()) {
case "bindings":
celBuilder.addCompilerLibraries(CelExtensions.bindings());
break;
case "encoders":
celBuilder.addCompilerLibraries(CelExtensions.encoders());
celBuilder.addRuntimeLibraries(CelExtensions.encoders());
break;
case "math":
celBuilder.addCompilerLibraries(CelExtensions.math(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.math(celOptions));
break;
case "optional":
celBuilder.addCompilerLibraries(CelOptionalLibrary.INSTANCE);
celBuilder.addRuntimeLibraries(CelOptionalLibrary.INSTANCE);
break;
case "protos":
celBuilder.addCompilerLibraries(CelExtensions.protos());
break;
case "strings":
celBuilder.addCompilerLibraries(CelExtensions.strings());
celBuilder.addRuntimeLibraries(CelExtensions.strings());
break;
case "sets":
celBuilder.addCompilerLibraries(CelExtensions.sets(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.sets(celOptions));
break;
default:
throw new IllegalArgumentException("Unrecognized extension: " + extensionConfig.name());
}
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
}
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
throw new IllegalArgumentException("Unrecognized extension: " + extensionName);
}

return extension;
}

/** Represents a policy variable declaration. */
@AutoValue
public abstract static class VariableDecl {
Expand DownExpand Up@@ -529,4 +553,63 @@ public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}
}

@VisibleForTesting
enum CanonicalCelExtension {
BINDINGS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.bindings())),
PROTOS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.protos())),
ENCODERS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.encoders()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.encoders())),
MATH(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.math(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.math(options))),
OPTIONAL(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelOptionalLibrary.INSTANCE),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelOptionalLibrary.INSTANCE)),
STRINGS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.strings()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.strings())),
SETS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.sets(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.sets(options))),
LISTS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.lists()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.lists()));

@SuppressWarnings("ImmutableEnumChecker")
private final CompilerExtensionApplier compilerExtensionApplier;

@SuppressWarnings("ImmutableEnumChecker")
private final RuntimeExtensionApplier runtimeExtensionApplier;

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
}

void addCompilerExtension(CelCompilerBuilder compilerBuilder, CelOptions options) {
compilerExtensionApplier.apply(compilerBuilder, options);
}

void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
runtimeExtensionApplier.apply(runtimeBuilder, options);
}

CanonicalCelExtension(CompilerExtensionApplier compilerExtensionApplier) {
this(
compilerExtensionApplier,
(runtimeBuilder, options) -> {}); // no-op. Not all extensions augment the runtime.
}

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,11 @@
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;

final class CelEnvironmentYamlParser {
/**
* CelEnvironmentYamlParser intakes a YAML document that describes the structure of a CEL
* environment, parses it then creates a {@link CelEnvironment}.
*/
public final class CelEnvironmentYamlParser {
// Sentinel values to be returned for various declarations when parsing failure is encountered.
private static final TypeDecl ERROR_TYPE_DECL = TypeDecl.create(ERROR);
private static final VariableDecl ERROR_VARIABLE_DECL =
Expand Down
2 changes: 1 addition & 1 deletion bundle/src/test/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ java_library(
testonly = True,
srcs = glob(["*Test.java"]),
resources = [
"//testing/environment:environment_yaml_files",
"//testing/environment:extended_env",
],
deps = [
"//:java_truth",
Expand Down
33 changes: 33 additions & 0 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,12 @@

import static com.google.common.truth.Truth.assertThat;

import com.google.common.collect.ImmutableSet;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import dev.cel.bundle.CelEnvironment.CanonicalCelExtension;
import dev.cel.bundle.CelEnvironment.ExtensionConfig;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import org.junit.Test;
import org.junit.runner.RunWith;

Expand All@@ -35,4 +40,32 @@ public void newBuilder_defaults() {
assertThat(environment.variables()).isEmpty();
assertThat(environment.functions()).isEmpty();
}

@Test
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().setExtensions(extensionConfigs).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast =
cel.compile(
"cel.bind(x, 10, math.greatest([1,x])) < int(' 11 '.trim()) &&"
+ " optional.none().orValue(true) && [].flatten() == []")
.getAst();
boolean result = (boolean) cel.createProgram(ast).eval();

assertThat(extensionConfigs.size()).isEqualTo(CelEnvironment.CEL_EXTENSION_CONFIG_MAP.size());
assertThat(extensionConfigs.size()).isEqualTo(CanonicalCelExtension.values().length);
assertThat(result).isTrue();
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -74,6 +74,7 @@ public void environment_setExtensions() throws Exception {
"extensions:\n"
+ " - name: 'bindings'\n"
+ " - name: 'encoders'\n"
+ " - name: 'lists'\n"
+ " - name: 'math'\n"
+ " - name: 'optional'\n"
+ " - name: 'protos'\n"
Expand All@@ -91,6 +92,7 @@ public void environment_setExtensions() throws Exception {
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
Expand DownExpand Up@@ -307,6 +309,31 @@ public void environment_setMessageVariable() throws Exception {
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_setContainer() throws Exception {
String yamlConfig =
"container: google.rpc.context\n"
+ "variables:\n"
+ "- name: 'request'\n"
+ " type:\n"
+ " type_name: 'google.rpc.context.AttributeContext.Request'";

CelEnvironment environment = ENVIRONMENT_PARSER.parse(yamlConfig);

assertThat(environment)
.isEqualTo(
CelEnvironment.newBuilder()
.setContainer("google.rpc.context")
.setSource(environment.source().get())
.setVariables(
ImmutableSet.of(
VariableDecl.create(
"request",
TypeDecl.create("google.rpc.context.AttributeContext.Request"))))
.build());
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_withInlinedVariableDecl() throws Exception {
String yamlConfig =
Expand DownExpand Up@@ -675,8 +702,7 @@ private enum EnvironmentYamlResourceTestCase {
private final String yamlFileContent;
private final CelEnvironment expectedEnvironment;

EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment)
throws RuntimeException {
EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment) {
try {
this.yamlFileContent = readFile(yamlResourcePath);
} catch (IOException e) {
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/main/java/dev/cel/compiler/tools/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,14 +15,14 @@ java_binary(
main_class = "dev.cel.compiler.tools.CelCompilerTool",
neverlink = 1,
deps = [
"//bundle:environment",
"//bundle:environment_exception",
"//bundle:environment_yaml_parser",
"//common",
"//common:compiler_common",
"//common:options",
"//common:proto_ast",
"//compiler",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"@cel_spec//proto/cel/expr:checked_java_proto",
"@maven//:com_google_guava_guava",
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,8 +64,10 @@ java_library(
"//common:source",
"//common/types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
],
Expand Down
161 changes: 122 additions & 39 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,9 @@
import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
Expand All@@ -35,8 +37,11 @@
import dev.cel.common.types.OptionalType;
import dev.cel.common.types.SimpleType;
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;

Expand All@@ -47,6 +52,18 @@
@AutoValue
public abstract class CelEnvironment {

@VisibleForTesting
static final ImmutableMap<String, CanonicalCelExtension> CEL_EXTENSION_CONFIG_MAP =
ImmutableMap.of(
"bindings", CanonicalCelExtension.BINDINGS,
"encoders", CanonicalCelExtension.ENCODERS,
"lists", CanonicalCelExtension.LISTS,
"math", CanonicalCelExtension.MATH,
"optional", CanonicalCelExtension.OPTIONAL,
"protos", CanonicalCelExtension.PROTOS,
"sets", CanonicalCelExtension.SETS,
"strings", CanonicalCelExtension.STRINGS);

/** Environment source in textual format (ex: textproto, YAML). */
public abstract Optional<Source> source();

Expand DownExpand Up@@ -126,12 +143,14 @@ public static Builder newBuilder() {
.setFunctions(ImmutableSet.of());
}

/** Extends the provided {@code cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
try {
CelTypeProvider celTypeProvider = cel.getTypeProvider();
CelBuilder celBuilder =
cel.toCelBuilder()
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setTypeProvider(celTypeProvider)
.setContainer(container())
.addVarDeclarations(
Expand All@@ -144,53 +163,58 @@ public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException
.collect(toImmutableList()));

if (!container().isEmpty()) {
celBuilder.setContainer(container());
compilerBuilder.setContainer(container());
}

addAllExtensions(celBuilder, celOptions);
addAllCompilerExtensions(compilerBuilder, celOptions);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

/** Extends the provided {@link Cel} environment with this configuration. */
public Cel extend(Cel cel, CelOptions celOptions) throws CelEnvironmentException {
try {
// Casting is necessary to only extend the compiler here
CelCompiler celCompiler = extend((CelCompiler) cel, celOptions);

CelRuntimeBuilder celRuntimeBuilder = cel.toRuntimeBuilder();
addAllRuntimeExtensions(celRuntimeBuilder, celOptions);

return celBuilder.build();
return CelFactory.combine(celCompiler, celRuntimeBuilder.build());
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
}
}

private void addAllExtensions(CelBuilder celBuilder, CelOptions celOptions) {
private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
switch (extensionConfig.name()) {
case "bindings":
celBuilder.addCompilerLibraries(CelExtensions.bindings());
break;
case "encoders":
celBuilder.addCompilerLibraries(CelExtensions.encoders());
celBuilder.addRuntimeLibraries(CelExtensions.encoders());
break;
case "math":
celBuilder.addCompilerLibraries(CelExtensions.math(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.math(celOptions));
break;
case "optional":
celBuilder.addCompilerLibraries(CelOptionalLibrary.INSTANCE);
celBuilder.addRuntimeLibraries(CelOptionalLibrary.INSTANCE);
break;
case "protos":
celBuilder.addCompilerLibraries(CelExtensions.protos());
break;
case "strings":
celBuilder.addCompilerLibraries(CelExtensions.strings());
celBuilder.addRuntimeLibraries(CelExtensions.strings());
break;
case "sets":
celBuilder.addCompilerLibraries(CelExtensions.sets(celOptions));
celBuilder.addRuntimeLibraries(CelExtensions.sets(celOptions));
break;
default:
throw new IllegalArgumentException("Unrecognized extension: " + extensionConfig.name());
}
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
}
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
throw new IllegalArgumentException("Unrecognized extension: " + extensionName);
}

return extension;
}

/** Represents a policy variable declaration. */
@AutoValue
public abstract static class VariableDecl {
Expand DownExpand Up@@ -529,4 +553,63 @@ public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}
}

@VisibleForTesting
enum CanonicalCelExtension {
BINDINGS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.bindings())),
PROTOS((compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.protos())),
ENCODERS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.encoders()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.encoders())),
MATH(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.math(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.math(options))),
OPTIONAL(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelOptionalLibrary.INSTANCE),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelOptionalLibrary.INSTANCE)),
STRINGS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.strings()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.strings())),
SETS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.sets(options)),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.sets(options))),
LISTS(
(compilerBuilder, options) -> compilerBuilder.addLibraries(CelExtensions.lists()),
(runtimeBuilder, options) -> runtimeBuilder.addLibraries(CelExtensions.lists()));

@SuppressWarnings("ImmutableEnumChecker")
private final CompilerExtensionApplier compilerExtensionApplier;

@SuppressWarnings("ImmutableEnumChecker")
private final RuntimeExtensionApplier runtimeExtensionApplier;

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
}

void addCompilerExtension(CelCompilerBuilder compilerBuilder, CelOptions options) {
compilerExtensionApplier.apply(compilerBuilder, options);
}

void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
runtimeExtensionApplier.apply(runtimeBuilder, options);
}

CanonicalCelExtension(CompilerExtensionApplier compilerExtensionApplier) {
this(
compilerExtensionApplier,
(runtimeBuilder, options) -> {}); // no-op. Not all extensions augment the runtime.
}

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,11 @@
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;

final class CelEnvironmentYamlParser {
/**
* CelEnvironmentYamlParser intakes a YAML document that describes the structure of a CEL
* environment, parses it then creates a {@link CelEnvironment}.
*/
public final class CelEnvironmentYamlParser {
// Sentinel values to be returned for various declarations when parsing failure is encountered.
private static final TypeDecl ERROR_TYPE_DECL = TypeDecl.create(ERROR);
private static final VariableDecl ERROR_VARIABLE_DECL =
Expand Down
2 changes: 1 addition & 1 deletion bundle/src/test/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ java_library(
testonly = True,
srcs = glob(["*Test.java"]),
resources = [
"//testing/environment:environment_yaml_files",
"//testing/environment:extended_env",
],
deps = [
"//:java_truth",
Expand Down
33 changes: 33 additions & 0 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,12 @@

import static com.google.common.truth.Truth.assertThat;

import com.google.common.collect.ImmutableSet;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import dev.cel.bundle.CelEnvironment.CanonicalCelExtension;
import dev.cel.bundle.CelEnvironment.ExtensionConfig;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import org.junit.Test;
import org.junit.runner.RunWith;

Expand All@@ -35,4 +40,32 @@ public void newBuilder_defaults() {
assertThat(environment.variables()).isEmpty();
assertThat(environment.functions()).isEmpty();
}

@Test
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().setExtensions(extensionConfigs).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast =
cel.compile(
"cel.bind(x, 10, math.greatest([1,x])) < int(' 11 '.trim()) &&"
+ " optional.none().orValue(true) && [].flatten() == []")
.getAst();
boolean result = (boolean) cel.createProgram(ast).eval();

assertThat(extensionConfigs.size()).isEqualTo(CelEnvironment.CEL_EXTENSION_CONFIG_MAP.size());
assertThat(extensionConfigs.size()).isEqualTo(CanonicalCelExtension.values().length);
assertThat(result).isTrue();
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -74,6 +74,7 @@ public void environment_setExtensions() throws Exception {
"extensions:\n"
+ " - name: 'bindings'\n"
+ " - name: 'encoders'\n"
+ " - name: 'lists'\n"
+ " - name: 'math'\n"
+ " - name: 'optional'\n"
+ " - name: 'protos'\n"
Expand All@@ -91,6 +92,7 @@ public void environment_setExtensions() throws Exception {
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
Expand DownExpand Up@@ -307,6 +309,31 @@ public void environment_setMessageVariable() throws Exception {
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_setContainer() throws Exception {
String yamlConfig =
"container: google.rpc.context\n"
+ "variables:\n"
+ "- name: 'request'\n"
+ " type:\n"
+ " type_name: 'google.rpc.context.AttributeContext.Request'";

CelEnvironment environment = ENVIRONMENT_PARSER.parse(yamlConfig);

assertThat(environment)
.isEqualTo(
CelEnvironment.newBuilder()
.setContainer("google.rpc.context")
.setSource(environment.source().get())
.setVariables(
ImmutableSet.of(
VariableDecl.create(
"request",
TypeDecl.create("google.rpc.context.AttributeContext.Request"))))
.build());
assertThat(environment.extend(CEL_WITH_MESSAGE_TYPES, CelOptions.DEFAULT)).isNotNull();
}

@Test
public void environment_withInlinedVariableDecl() throws Exception {
String yamlConfig =
Expand DownExpand Up@@ -675,8 +702,7 @@ private enum EnvironmentYamlResourceTestCase {
private final String yamlFileContent;
private final CelEnvironment expectedEnvironment;

EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment)
throws RuntimeException {
EnvironmentYamlResourceTestCase(String yamlResourcePath, CelEnvironment expectedEnvironment) {
try {
this.yamlFileContent = readFile(yamlResourcePath);
} catch (IOException e) {
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/main/java/dev/cel/compiler/tools/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,14 +15,14 @@ java_binary(
main_class = "dev.cel.compiler.tools.CelCompilerTool",
neverlink = 1,
deps = [
"//bundle:environment",
"//bundle:environment_exception",
"//bundle:environment_yaml_parser",
"//common",
"//common:compiler_common",
"//common:options",
"//common:proto_ast",
"//compiler",
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"@cel_spec//proto/cel/expr:checked_java_proto",
"@maven//:com_google_guava_guava",
Expand Down
Loading