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
1 change: 0 additions & 1 deletion bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,5 @@ java_library(

java_library(
name = "environment_exporter",
visibility = ["//:internal"],
exports = ["//bundle/src/main/java/dev/cel/bundle:environment_exporter"],
)
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@@ -126,13 +126,15 @@ java_library(
":environment",
"//:auto_value",
"//bundle:cel",
"//checker:checker_builder",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common/internal:env_visitor",
"//common/types:cel_proto_types",
"//common/types:cel_types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:extension_library",
"//parser:macro",
Expand Down
57 changes: 52 additions & 5 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
Expand DownExpand Up@@ -108,6 +109,9 @@ public abstract class CelEnvironment {
/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Feature flags to enable in the environment. */
public abstract ImmutableSet<FeatureFlag> features();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -159,6 +163,13 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

@CanIgnoreReturnValue
public Builder setFeatures(FeatureFlag... featureFlags) {
return setFeatures(ImmutableSet.copyOf(featureFlags));
}

public abstract Builder setFeatures(ImmutableSet<FeatureFlag> macros);

abstract CelEnvironment autoBuild();

@CheckReturnValue
Expand DownExpand Up@@ -188,18 +199,21 @@ public static Builder newBuilder() {
.setDescription("")
.setContainer(CelContainer.ofName(""))
.setVariables(ImmutableSet.of())
.setFunctions(ImmutableSet.of());
.setFunctions(ImmutableSet.of())
.setFeatures(ImmutableSet.of());
}

/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
celOptions = applyFeatureFlags(celOptions);
try {
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setContainer(container())
.setOptions(celOptions)
.setTypeProvider(celTypeProvider)
.addVarDeclarations(
variables().stream()
Expand All@@ -222,19 +236,35 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

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

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

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

private CelOptions applyFeatureFlags(CelOptions celOptions) {
CelOptions.Builder optionsBuilder = celOptions.toBuilder();
for (FeatureFlag featureFlag : features()) {
if (featureFlag.name().equals("cel.feature.macro_call_tracking")) {
optionsBuilder.populateMacroCalls(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.backtick_escape_syntax")) {
optionsBuilder.enableQuotedIdentifierSyntax(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.cross_type_numeric_comparisons")) {
optionsBuilder.enableHeterogeneousNumericComparisons(featureFlag.enabled());
} else {
throw new IllegalArgumentException("Unknown feature flag: " + featureFlag.name());
}
}
return optionsBuilder.build();
}

private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
Expand All@@ -250,7 +280,9 @@ private void addAllCompilerExtensions(
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
private CelRuntime extendRuntime(CelRuntime celRuntime, CelOptions celOptions) {
CelRuntimeBuilder celRuntimeBuilder = celRuntime.toRuntimeBuilder();
celRuntimeBuilder.setOptions(celOptions);
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
Expand All@@ -262,6 +294,7 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
return celRuntimeBuilder.build();
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
Expand DownExpand Up@@ -625,6 +658,20 @@ public CelType toCelType(CelTypeProvider celTypeProvider) {
}
}

/** Represents a feature flag that can be enabled in the environment. */
@AutoValue
public abstract static class FeatureFlag {
/** Normalized name of the feature flag. */
public abstract String name();

/** Whether the feature is enabled or disabled. */
public abstract boolean enabled();

public static FeatureFlag create(String name, boolean enabled) {
return new AutoValue_CelEnvironment_FeatureFlag(name, enabled);
}
}

/**
* Represents a configuration for a canonical CEL extension that can be enabled in the
* environment.
Expand Down
50 changes: 47 additions & 3 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironmentExporter.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,7 @@
import dev.cel.expr.Decl.FunctionDecl;
import dev.cel.expr.Decl.FunctionDecl.Overload;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
Expand All@@ -30,6 +31,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.bundle.CelEnvironment.OverloadDecl;
import dev.cel.checker.CelCheckerBuilder;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardIdentifier;
import dev.cel.common.CelFunctionDecl;
Expand All@@ -41,6 +43,7 @@
import dev.cel.common.types.CelProtoTypes;
import dev.cel.common.types.CelType;
import dev.cel.common.types.CelTypes;
import dev.cel.compiler.CelCompiler;
import dev.cel.extensions.CelExtensionLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelMacro;
Expand DownExpand Up@@ -161,9 +164,24 @@ public static CelEnvironmentExporter.Builder newBuilder() {
* </ul>
*/
public CelEnvironment export(Cel cel) {
CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(cel.toCheckerBuilder().container());
return export((CelCompiler) cel);
}

/**
* Exports a {@link CelEnvironment} that describes the configuration of the given {@link
* CelCompiler} instance.
*
* <p>The exported environment includes:
*
* <ul>
* <li>Standard library subset: functions and their overloads that are either included or
* excluded from the standard library.
* <li>Extension libraries: names and versions of the extension libraries that are used.
* <li>Custom declarations: functions and variables that are not part of the standard library or
* any of the extension libraries.
* </ul>
*/
public CelEnvironment export(CelCompiler cel) {
// Inventory is a full set of declarations and macros that are found in the configuration of
// the supplied CEL instance.
//
Expand All@@ -172,6 +190,14 @@ public CelEnvironment export(Cel cel) {
//
// Whatever is left will be included in the Environment as custom declarations.

// Checker builder is used to access some parts of the config not exposed in the EnvVisitable
// interface.
CelCheckerBuilder checkerBuilder = cel.toCheckerBuilder();

CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(checkerBuilder.container());
addOptions(envBuilder, checkerBuilder.options());

Set<Object> inventory = new HashSet<>();
collectInventory(inventory, cel);
addExtensionConfigsAndRemoveFromInventory(envBuilder, inventory);
Expand All@@ -180,11 +206,29 @@ public CelEnvironment export(Cel cel) {
return envBuilder.build();
}

private void addOptions(CelEnvironment.Builder envBuilder, CelOptions options) {
// The set of features supported in the exported environment in Go is pretty limited right now.
ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();
if (options.enableHeterogeneousNumericComparisons()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.cross_type_numeric_comparisons", true));
}
if (options.enableQuotedIdentifierSyntax()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.backtick_escape_syntax", true));
}
if (options.populateMacroCalls()) {
featureFlags.add(CelEnvironment.FeatureFlag.create("cel.feature.macro_call_tracking", true));
}
envBuilder.setFeatures(featureFlags.build());
}

/**
* Collects all function overloads, variable declarations and macros from the given {@link Cel}
* instance and stores them in a map.
*/
private void collectInventory(Set<Object> inventory, Cel cel) {
private void collectInventory(Set<Object> inventory, CelCompiler cel) {
Preconditions.checkArgument(cel instanceof EnvVisitable);
((EnvVisitable) cel)
.accept(
new EnvVisitor() {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -143,6 +143,51 @@ private CelContainer parseContainer(ParserContext<Node> ctx, Node node) {
return builder.build();
}

private ImmutableSet<CelEnvironment.FeatureFlag> parseFeatures(
ParserContext<Node> ctx, Node node) {
long valueId = ctx.collectMetadata(node);
if (!validateYamlType(node, YamlNodeType.LIST, YamlNodeType.TEXT)) {
ctx.reportError(valueId, "Unsupported features format");
}

ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();

SequenceNode featureListNode = (SequenceNode) node;
for (Node featureMapNode : featureListNode.getValue()) {
long featureMapId = ctx.collectMetadata(featureMapNode);
if (!assertYamlType(ctx, featureMapId, featureMapNode, YamlNodeType.MAP)) {
continue;
}

MappingNode featureMap = (MappingNode) featureMapNode;
String name = "";
boolean enabled = true;
for (NodeTuple nodeTuple : featureMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "name":
name = newString(ctx, valueNode);
break;
case "enabled":
enabled = newBoolean(ctx, valueNode);
break;
default:
ctx.reportError(keyId, String.format("Unsupported feature tag: %s", keyName));
break;
}
}
if (name.isEmpty()) {
ctx.reportError(featureMapId, "Missing required attribute(s): name");
continue;
}
featureFlags.add(CelEnvironment.FeatureFlag.create(name, enabled));
}
return featureFlags.build();
}

private ImmutableSet<Alias> parseAliases(ParserContext<Node> ctx, Node node) {
ImmutableSet.Builder<Alias> aliasSetBuilder = ImmutableSet.builder();
long valueId = ctx.collectMetadata(node);
Expand DownExpand Up@@ -756,6 +801,9 @@ private CelEnvironment.Builder parseConfig(ParserContext<Node> ctx, Node node) {
case "stdlib":
builder.setStandardLibrarySubset(parseLibrarySubset(ctx, valueNode));
break;
case "features":
builder.setFeatures(parseFeatures(ctx, valueNode));
break;
default:
ctx.reportError(id, "Unknown config tag: " + fieldName);
// continue handling the rest of the nodes
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,6 +60,7 @@ private CelEnvironmentYamlSerializer() {
CelEnvironment.LibrarySubset.OverloadSelector.class, new RepresentOverloadSelector());
this.multiRepresenters.put(CelEnvironment.Alias.class, new RepresentAlias());
this.multiRepresenters.put(CelContainer.class, new RepresentContainer());
this.multiRepresenters.put(CelEnvironment.FeatureFlag.class, new RepresentFeatureFlag());
}

public static String toYaml(CelEnvironment environment) {
Expand DownExpand Up@@ -94,6 +95,9 @@ public Node representData(Object data) {
if (environment.standardLibrarySubset().isPresent()) {
configMap.put("stdlib", environment.standardLibrarySubset().get());
}
if (!environment.features().isEmpty()) {
configMap.put("features", environment.features().asList());
}
return represent(configMap.buildOrThrow());
}
}
Expand DownExpand Up@@ -258,4 +262,17 @@ public Node representData(Object data) {
return represent(ImmutableMap.<String, Object>of("id", overloadSelector.id()));
}
}

private final class RepresentFeatureFlag implements Represent {

@Override
public Node representData(Object data) {
CelEnvironment.FeatureFlag featureFlag = (CelEnvironment.FeatureFlag) data;
return represent(
ImmutableMap.builder()
.put("name", featureFlag.name())
.put("enabled", featureFlag.enabled())
.buildOrThrow());
}
}
}
Loading
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
1 change: 0 additions & 1 deletion bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,5 @@ java_library(

java_library(
name = "environment_exporter",
visibility = ["//:internal"],
exports = ["//bundle/src/main/java/dev/cel/bundle:environment_exporter"],
)
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@@ -126,13 +126,15 @@ java_library(
":environment",
"//:auto_value",
"//bundle:cel",
"//checker:checker_builder",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common/internal:env_visitor",
"//common/types:cel_proto_types",
"//common/types:cel_types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:extension_library",
"//parser:macro",
Expand Down
57 changes: 52 additions & 5 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
Expand DownExpand Up@@ -108,6 +109,9 @@ public abstract class CelEnvironment {
/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Feature flags to enable in the environment. */
public abstract ImmutableSet<FeatureFlag> features();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -159,6 +163,13 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

@CanIgnoreReturnValue
public Builder setFeatures(FeatureFlag... featureFlags) {
return setFeatures(ImmutableSet.copyOf(featureFlags));
}

public abstract Builder setFeatures(ImmutableSet<FeatureFlag> macros);

abstract CelEnvironment autoBuild();

@CheckReturnValue
Expand DownExpand Up@@ -188,18 +199,21 @@ public static Builder newBuilder() {
.setDescription("")
.setContainer(CelContainer.ofName(""))
.setVariables(ImmutableSet.of())
.setFunctions(ImmutableSet.of());
.setFunctions(ImmutableSet.of())
.setFeatures(ImmutableSet.of());
}

/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
celOptions = applyFeatureFlags(celOptions);
try {
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setContainer(container())
.setOptions(celOptions)
.setTypeProvider(celTypeProvider)
.addVarDeclarations(
variables().stream()
Expand All@@ -222,19 +236,35 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

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

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

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

private CelOptions applyFeatureFlags(CelOptions celOptions) {
CelOptions.Builder optionsBuilder = celOptions.toBuilder();
for (FeatureFlag featureFlag : features()) {
if (featureFlag.name().equals("cel.feature.macro_call_tracking")) {
optionsBuilder.populateMacroCalls(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.backtick_escape_syntax")) {
optionsBuilder.enableQuotedIdentifierSyntax(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.cross_type_numeric_comparisons")) {
optionsBuilder.enableHeterogeneousNumericComparisons(featureFlag.enabled());
} else {
throw new IllegalArgumentException("Unknown feature flag: " + featureFlag.name());
}
}
return optionsBuilder.build();
}

private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
Expand All@@ -250,7 +280,9 @@ private void addAllCompilerExtensions(
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
private CelRuntime extendRuntime(CelRuntime celRuntime, CelOptions celOptions) {
CelRuntimeBuilder celRuntimeBuilder = celRuntime.toRuntimeBuilder();
celRuntimeBuilder.setOptions(celOptions);
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
Expand All@@ -262,6 +294,7 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
return celRuntimeBuilder.build();
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
Expand DownExpand Up@@ -625,6 +658,20 @@ public CelType toCelType(CelTypeProvider celTypeProvider) {
}
}

/** Represents a feature flag that can be enabled in the environment. */
@AutoValue
public abstract static class FeatureFlag {
/** Normalized name of the feature flag. */
public abstract String name();

/** Whether the feature is enabled or disabled. */
public abstract boolean enabled();

public static FeatureFlag create(String name, boolean enabled) {
return new AutoValue_CelEnvironment_FeatureFlag(name, enabled);
}
}

/**
* Represents a configuration for a canonical CEL extension that can be enabled in the
* environment.
Expand Down
50 changes: 47 additions & 3 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironmentExporter.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,7 @@
import dev.cel.expr.Decl.FunctionDecl;
import dev.cel.expr.Decl.FunctionDecl.Overload;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
Expand All@@ -30,6 +31,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.bundle.CelEnvironment.OverloadDecl;
import dev.cel.checker.CelCheckerBuilder;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardIdentifier;
import dev.cel.common.CelFunctionDecl;
Expand All@@ -41,6 +43,7 @@
import dev.cel.common.types.CelProtoTypes;
import dev.cel.common.types.CelType;
import dev.cel.common.types.CelTypes;
import dev.cel.compiler.CelCompiler;
import dev.cel.extensions.CelExtensionLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelMacro;
Expand DownExpand Up@@ -161,9 +164,24 @@ public static CelEnvironmentExporter.Builder newBuilder() {
* </ul>
*/
public CelEnvironment export(Cel cel) {
CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(cel.toCheckerBuilder().container());
return export((CelCompiler) cel);
}

/**
* Exports a {@link CelEnvironment} that describes the configuration of the given {@link
* CelCompiler} instance.
*
* <p>The exported environment includes:
*
* <ul>
* <li>Standard library subset: functions and their overloads that are either included or
* excluded from the standard library.
* <li>Extension libraries: names and versions of the extension libraries that are used.
* <li>Custom declarations: functions and variables that are not part of the standard library or
* any of the extension libraries.
* </ul>
*/
public CelEnvironment export(CelCompiler cel) {
// Inventory is a full set of declarations and macros that are found in the configuration of
// the supplied CEL instance.
//
Expand All@@ -172,6 +190,14 @@ public CelEnvironment export(Cel cel) {
//
// Whatever is left will be included in the Environment as custom declarations.

// Checker builder is used to access some parts of the config not exposed in the EnvVisitable
// interface.
CelCheckerBuilder checkerBuilder = cel.toCheckerBuilder();

CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(checkerBuilder.container());
addOptions(envBuilder, checkerBuilder.options());

Set<Object> inventory = new HashSet<>();
collectInventory(inventory, cel);
addExtensionConfigsAndRemoveFromInventory(envBuilder, inventory);
Expand All@@ -180,11 +206,29 @@ public CelEnvironment export(Cel cel) {
return envBuilder.build();
}

private void addOptions(CelEnvironment.Builder envBuilder, CelOptions options) {
// The set of features supported in the exported environment in Go is pretty limited right now.
ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();
if (options.enableHeterogeneousNumericComparisons()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.cross_type_numeric_comparisons", true));
}
if (options.enableQuotedIdentifierSyntax()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.backtick_escape_syntax", true));
}
if (options.populateMacroCalls()) {
featureFlags.add(CelEnvironment.FeatureFlag.create("cel.feature.macro_call_tracking", true));
}
envBuilder.setFeatures(featureFlags.build());
}

/**
* Collects all function overloads, variable declarations and macros from the given {@link Cel}
* instance and stores them in a map.
*/
private void collectInventory(Set<Object> inventory, Cel cel) {
private void collectInventory(Set<Object> inventory, CelCompiler cel) {
Preconditions.checkArgument(cel instanceof EnvVisitable);
((EnvVisitable) cel)
.accept(
new EnvVisitor() {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -143,6 +143,51 @@ private CelContainer parseContainer(ParserContext<Node> ctx, Node node) {
return builder.build();
}

private ImmutableSet<CelEnvironment.FeatureFlag> parseFeatures(
ParserContext<Node> ctx, Node node) {
long valueId = ctx.collectMetadata(node);
if (!validateYamlType(node, YamlNodeType.LIST, YamlNodeType.TEXT)) {
ctx.reportError(valueId, "Unsupported features format");
}

ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();

SequenceNode featureListNode = (SequenceNode) node;
for (Node featureMapNode : featureListNode.getValue()) {
long featureMapId = ctx.collectMetadata(featureMapNode);
if (!assertYamlType(ctx, featureMapId, featureMapNode, YamlNodeType.MAP)) {
continue;
}

MappingNode featureMap = (MappingNode) featureMapNode;
String name = "";
boolean enabled = true;
for (NodeTuple nodeTuple : featureMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "name":
name = newString(ctx, valueNode);
break;
case "enabled":
enabled = newBoolean(ctx, valueNode);
break;
default:
ctx.reportError(keyId, String.format("Unsupported feature tag: %s", keyName));
break;
}
}
if (name.isEmpty()) {
ctx.reportError(featureMapId, "Missing required attribute(s): name");
continue;
}
featureFlags.add(CelEnvironment.FeatureFlag.create(name, enabled));
}
return featureFlags.build();
}

private ImmutableSet<Alias> parseAliases(ParserContext<Node> ctx, Node node) {
ImmutableSet.Builder<Alias> aliasSetBuilder = ImmutableSet.builder();
long valueId = ctx.collectMetadata(node);
Expand DownExpand Up@@ -756,6 +801,9 @@ private CelEnvironment.Builder parseConfig(ParserContext<Node> ctx, Node node) {
case "stdlib":
builder.setStandardLibrarySubset(parseLibrarySubset(ctx, valueNode));
break;
case "features":
builder.setFeatures(parseFeatures(ctx, valueNode));
break;
default:
ctx.reportError(id, "Unknown config tag: " + fieldName);
// continue handling the rest of the nodes
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,6 +60,7 @@ private CelEnvironmentYamlSerializer() {
CelEnvironment.LibrarySubset.OverloadSelector.class, new RepresentOverloadSelector());
this.multiRepresenters.put(CelEnvironment.Alias.class, new RepresentAlias());
this.multiRepresenters.put(CelContainer.class, new RepresentContainer());
this.multiRepresenters.put(CelEnvironment.FeatureFlag.class, new RepresentFeatureFlag());
}

public static String toYaml(CelEnvironment environment) {
Expand DownExpand Up@@ -94,6 +95,9 @@ public Node representData(Object data) {
if (environment.standardLibrarySubset().isPresent()) {
configMap.put("stdlib", environment.standardLibrarySubset().get());
}
if (!environment.features().isEmpty()) {
configMap.put("features", environment.features().asList());
}
return represent(configMap.buildOrThrow());
}
}
Expand DownExpand Up@@ -258,4 +262,17 @@ public Node representData(Object data) {
return represent(ImmutableMap.<String, Object>of("id", overloadSelector.id()));
}
}

private final class RepresentFeatureFlag implements Represent {

@Override
public Node representData(Object data) {
CelEnvironment.FeatureFlag featureFlag = (CelEnvironment.FeatureFlag) data;
return represent(
ImmutableMap.builder()
.put("name", featureFlag.name())
.put("enabled", featureFlag.enabled())
.buildOrThrow());
}
}
}
Loading
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
1 change: 0 additions & 1 deletion bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,5 @@ java_library(

java_library(
name = "environment_exporter",
visibility = ["//:internal"],
exports = ["//bundle/src/main/java/dev/cel/bundle:environment_exporter"],
)
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@@ -126,13 +126,15 @@ java_library(
":environment",
"//:auto_value",
"//bundle:cel",
"//checker:checker_builder",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common/internal:env_visitor",
"//common/types:cel_proto_types",
"//common/types:cel_types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:extension_library",
"//parser:macro",
Expand Down
57 changes: 52 additions & 5 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
Expand DownExpand Up@@ -108,6 +109,9 @@ public abstract class CelEnvironment {
/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Feature flags to enable in the environment. */
public abstract ImmutableSet<FeatureFlag> features();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -159,6 +163,13 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

@CanIgnoreReturnValue
public Builder setFeatures(FeatureFlag... featureFlags) {
return setFeatures(ImmutableSet.copyOf(featureFlags));
}

public abstract Builder setFeatures(ImmutableSet<FeatureFlag> macros);

abstract CelEnvironment autoBuild();

@CheckReturnValue
Expand DownExpand Up@@ -188,18 +199,21 @@ public static Builder newBuilder() {
.setDescription("")
.setContainer(CelContainer.ofName(""))
.setVariables(ImmutableSet.of())
.setFunctions(ImmutableSet.of());
.setFunctions(ImmutableSet.of())
.setFeatures(ImmutableSet.of());
}

/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
celOptions = applyFeatureFlags(celOptions);
try {
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setContainer(container())
.setOptions(celOptions)
.setTypeProvider(celTypeProvider)
.addVarDeclarations(
variables().stream()
Expand All@@ -222,19 +236,35 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

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

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

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

private CelOptions applyFeatureFlags(CelOptions celOptions) {
CelOptions.Builder optionsBuilder = celOptions.toBuilder();
for (FeatureFlag featureFlag : features()) {
if (featureFlag.name().equals("cel.feature.macro_call_tracking")) {
optionsBuilder.populateMacroCalls(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.backtick_escape_syntax")) {
optionsBuilder.enableQuotedIdentifierSyntax(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.cross_type_numeric_comparisons")) {
optionsBuilder.enableHeterogeneousNumericComparisons(featureFlag.enabled());
} else {
throw new IllegalArgumentException("Unknown feature flag: " + featureFlag.name());
}
}
return optionsBuilder.build();
}

private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
Expand All@@ -250,7 +280,9 @@ private void addAllCompilerExtensions(
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
private CelRuntime extendRuntime(CelRuntime celRuntime, CelOptions celOptions) {
CelRuntimeBuilder celRuntimeBuilder = celRuntime.toRuntimeBuilder();
celRuntimeBuilder.setOptions(celOptions);
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
Expand All@@ -262,6 +294,7 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
return celRuntimeBuilder.build();
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
Expand DownExpand Up@@ -625,6 +658,20 @@ public CelType toCelType(CelTypeProvider celTypeProvider) {
}
}

/** Represents a feature flag that can be enabled in the environment. */
@AutoValue
public abstract static class FeatureFlag {
/** Normalized name of the feature flag. */
public abstract String name();

/** Whether the feature is enabled or disabled. */
public abstract boolean enabled();

public static FeatureFlag create(String name, boolean enabled) {
return new AutoValue_CelEnvironment_FeatureFlag(name, enabled);
}
}

/**
* Represents a configuration for a canonical CEL extension that can be enabled in the
* environment.
Expand Down
50 changes: 47 additions & 3 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironmentExporter.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,7 @@
import dev.cel.expr.Decl.FunctionDecl;
import dev.cel.expr.Decl.FunctionDecl.Overload;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
Expand All@@ -30,6 +31,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.bundle.CelEnvironment.OverloadDecl;
import dev.cel.checker.CelCheckerBuilder;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardIdentifier;
import dev.cel.common.CelFunctionDecl;
Expand All@@ -41,6 +43,7 @@
import dev.cel.common.types.CelProtoTypes;
import dev.cel.common.types.CelType;
import dev.cel.common.types.CelTypes;
import dev.cel.compiler.CelCompiler;
import dev.cel.extensions.CelExtensionLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelMacro;
Expand DownExpand Up@@ -161,9 +164,24 @@ public static CelEnvironmentExporter.Builder newBuilder() {
* </ul>
*/
public CelEnvironment export(Cel cel) {
CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(cel.toCheckerBuilder().container());
return export((CelCompiler) cel);
}

/**
* Exports a {@link CelEnvironment} that describes the configuration of the given {@link
* CelCompiler} instance.
*
* <p>The exported environment includes:
*
* <ul>
* <li>Standard library subset: functions and their overloads that are either included or
* excluded from the standard library.
* <li>Extension libraries: names and versions of the extension libraries that are used.
* <li>Custom declarations: functions and variables that are not part of the standard library or
* any of the extension libraries.
* </ul>
*/
public CelEnvironment export(CelCompiler cel) {
// Inventory is a full set of declarations and macros that are found in the configuration of
// the supplied CEL instance.
//
Expand All@@ -172,6 +190,14 @@ public CelEnvironment export(Cel cel) {
//
// Whatever is left will be included in the Environment as custom declarations.

// Checker builder is used to access some parts of the config not exposed in the EnvVisitable
// interface.
CelCheckerBuilder checkerBuilder = cel.toCheckerBuilder();

CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(checkerBuilder.container());
addOptions(envBuilder, checkerBuilder.options());

Set<Object> inventory = new HashSet<>();
collectInventory(inventory, cel);
addExtensionConfigsAndRemoveFromInventory(envBuilder, inventory);
Expand All@@ -180,11 +206,29 @@ public CelEnvironment export(Cel cel) {
return envBuilder.build();
}

private void addOptions(CelEnvironment.Builder envBuilder, CelOptions options) {
// The set of features supported in the exported environment in Go is pretty limited right now.
ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();
if (options.enableHeterogeneousNumericComparisons()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.cross_type_numeric_comparisons", true));
}
if (options.enableQuotedIdentifierSyntax()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.backtick_escape_syntax", true));
}
if (options.populateMacroCalls()) {
featureFlags.add(CelEnvironment.FeatureFlag.create("cel.feature.macro_call_tracking", true));
}
envBuilder.setFeatures(featureFlags.build());
}

/**
* Collects all function overloads, variable declarations and macros from the given {@link Cel}
* instance and stores them in a map.
*/
private void collectInventory(Set<Object> inventory, Cel cel) {
private void collectInventory(Set<Object> inventory, CelCompiler cel) {
Preconditions.checkArgument(cel instanceof EnvVisitable);
((EnvVisitable) cel)
.accept(
new EnvVisitor() {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -143,6 +143,51 @@ private CelContainer parseContainer(ParserContext<Node> ctx, Node node) {
return builder.build();
}

private ImmutableSet<CelEnvironment.FeatureFlag> parseFeatures(
ParserContext<Node> ctx, Node node) {
long valueId = ctx.collectMetadata(node);
if (!validateYamlType(node, YamlNodeType.LIST, YamlNodeType.TEXT)) {
ctx.reportError(valueId, "Unsupported features format");
}

ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();

SequenceNode featureListNode = (SequenceNode) node;
for (Node featureMapNode : featureListNode.getValue()) {
long featureMapId = ctx.collectMetadata(featureMapNode);
if (!assertYamlType(ctx, featureMapId, featureMapNode, YamlNodeType.MAP)) {
continue;
}

MappingNode featureMap = (MappingNode) featureMapNode;
String name = "";
boolean enabled = true;
for (NodeTuple nodeTuple : featureMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "name":
name = newString(ctx, valueNode);
break;
case "enabled":
enabled = newBoolean(ctx, valueNode);
break;
default:
ctx.reportError(keyId, String.format("Unsupported feature tag: %s", keyName));
break;
}
}
if (name.isEmpty()) {
ctx.reportError(featureMapId, "Missing required attribute(s): name");
continue;
}
featureFlags.add(CelEnvironment.FeatureFlag.create(name, enabled));
}
return featureFlags.build();
}

private ImmutableSet<Alias> parseAliases(ParserContext<Node> ctx, Node node) {
ImmutableSet.Builder<Alias> aliasSetBuilder = ImmutableSet.builder();
long valueId = ctx.collectMetadata(node);
Expand DownExpand Up@@ -756,6 +801,9 @@ private CelEnvironment.Builder parseConfig(ParserContext<Node> ctx, Node node) {
case "stdlib":
builder.setStandardLibrarySubset(parseLibrarySubset(ctx, valueNode));
break;
case "features":
builder.setFeatures(parseFeatures(ctx, valueNode));
break;
default:
ctx.reportError(id, "Unknown config tag: " + fieldName);
// continue handling the rest of the nodes
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,6 +60,7 @@ private CelEnvironmentYamlSerializer() {
CelEnvironment.LibrarySubset.OverloadSelector.class, new RepresentOverloadSelector());
this.multiRepresenters.put(CelEnvironment.Alias.class, new RepresentAlias());
this.multiRepresenters.put(CelContainer.class, new RepresentContainer());
this.multiRepresenters.put(CelEnvironment.FeatureFlag.class, new RepresentFeatureFlag());
}

public static String toYaml(CelEnvironment environment) {
Expand DownExpand Up@@ -94,6 +95,9 @@ public Node representData(Object data) {
if (environment.standardLibrarySubset().isPresent()) {
configMap.put("stdlib", environment.standardLibrarySubset().get());
}
if (!environment.features().isEmpty()) {
configMap.put("features", environment.features().asList());
}
return represent(configMap.buildOrThrow());
}
}
Expand DownExpand Up@@ -258,4 +262,17 @@ public Node representData(Object data) {
return represent(ImmutableMap.<String, Object>of("id", overloadSelector.id()));
}
}

private final class RepresentFeatureFlag implements Represent {

@Override
public Node representData(Object data) {
CelEnvironment.FeatureFlag featureFlag = (CelEnvironment.FeatureFlag) data;
return represent(
ImmutableMap.builder()
.put("name", featureFlag.name())
.put("enabled", featureFlag.enabled())
.buildOrThrow());
}
}
}
Loading
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
1 change: 0 additions & 1 deletion bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,5 @@ java_library(

java_library(
name = "environment_exporter",
visibility = ["//:internal"],
exports = ["//bundle/src/main/java/dev/cel/bundle:environment_exporter"],
)
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@@ -126,13 +126,15 @@ java_library(
":environment",
"//:auto_value",
"//bundle:cel",
"//checker:checker_builder",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common/internal:env_visitor",
"//common/types:cel_proto_types",
"//common/types:cel_types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:extension_library",
"//parser:macro",
Expand Down
57 changes: 52 additions & 5 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
Expand DownExpand Up@@ -108,6 +109,9 @@ public abstract class CelEnvironment {
/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Feature flags to enable in the environment. */
public abstract ImmutableSet<FeatureFlag> features();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -159,6 +163,13 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

@CanIgnoreReturnValue
public Builder setFeatures(FeatureFlag... featureFlags) {
return setFeatures(ImmutableSet.copyOf(featureFlags));
}

public abstract Builder setFeatures(ImmutableSet<FeatureFlag> macros);

abstract CelEnvironment autoBuild();

@CheckReturnValue
Expand DownExpand Up@@ -188,18 +199,21 @@ public static Builder newBuilder() {
.setDescription("")
.setContainer(CelContainer.ofName(""))
.setVariables(ImmutableSet.of())
.setFunctions(ImmutableSet.of());
.setFunctions(ImmutableSet.of())
.setFeatures(ImmutableSet.of());
}

/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
celOptions = applyFeatureFlags(celOptions);
try {
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setContainer(container())
.setOptions(celOptions)
.setTypeProvider(celTypeProvider)
.addVarDeclarations(
variables().stream()
Expand All@@ -222,19 +236,35 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

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

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

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

private CelOptions applyFeatureFlags(CelOptions celOptions) {
CelOptions.Builder optionsBuilder = celOptions.toBuilder();
for (FeatureFlag featureFlag : features()) {
if (featureFlag.name().equals("cel.feature.macro_call_tracking")) {
optionsBuilder.populateMacroCalls(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.backtick_escape_syntax")) {
optionsBuilder.enableQuotedIdentifierSyntax(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.cross_type_numeric_comparisons")) {
optionsBuilder.enableHeterogeneousNumericComparisons(featureFlag.enabled());
} else {
throw new IllegalArgumentException("Unknown feature flag: " + featureFlag.name());
}
}
return optionsBuilder.build();
}

private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
Expand All@@ -250,7 +280,9 @@ private void addAllCompilerExtensions(
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
private CelRuntime extendRuntime(CelRuntime celRuntime, CelOptions celOptions) {
CelRuntimeBuilder celRuntimeBuilder = celRuntime.toRuntimeBuilder();
celRuntimeBuilder.setOptions(celOptions);
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
Expand All@@ -262,6 +294,7 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
return celRuntimeBuilder.build();
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
Expand DownExpand Up@@ -625,6 +658,20 @@ public CelType toCelType(CelTypeProvider celTypeProvider) {
}
}

/** Represents a feature flag that can be enabled in the environment. */
@AutoValue
public abstract static class FeatureFlag {
/** Normalized name of the feature flag. */
public abstract String name();

/** Whether the feature is enabled or disabled. */
public abstract boolean enabled();

public static FeatureFlag create(String name, boolean enabled) {
return new AutoValue_CelEnvironment_FeatureFlag(name, enabled);
}
}

/**
* Represents a configuration for a canonical CEL extension that can be enabled in the
* environment.
Expand Down
50 changes: 47 additions & 3 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironmentExporter.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,7 @@
import dev.cel.expr.Decl.FunctionDecl;
import dev.cel.expr.Decl.FunctionDecl.Overload;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
Expand All@@ -30,6 +31,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.bundle.CelEnvironment.OverloadDecl;
import dev.cel.checker.CelCheckerBuilder;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardIdentifier;
import dev.cel.common.CelFunctionDecl;
Expand All@@ -41,6 +43,7 @@
import dev.cel.common.types.CelProtoTypes;
import dev.cel.common.types.CelType;
import dev.cel.common.types.CelTypes;
import dev.cel.compiler.CelCompiler;
import dev.cel.extensions.CelExtensionLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelMacro;
Expand DownExpand Up@@ -161,9 +164,24 @@ public static CelEnvironmentExporter.Builder newBuilder() {
* </ul>
*/
public CelEnvironment export(Cel cel) {
CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(cel.toCheckerBuilder().container());
return export((CelCompiler) cel);
}

/**
* Exports a {@link CelEnvironment} that describes the configuration of the given {@link
* CelCompiler} instance.
*
* <p>The exported environment includes:
*
* <ul>
* <li>Standard library subset: functions and their overloads that are either included or
* excluded from the standard library.
* <li>Extension libraries: names and versions of the extension libraries that are used.
* <li>Custom declarations: functions and variables that are not part of the standard library or
* any of the extension libraries.
* </ul>
*/
public CelEnvironment export(CelCompiler cel) {
// Inventory is a full set of declarations and macros that are found in the configuration of
// the supplied CEL instance.
//
Expand All@@ -172,6 +190,14 @@ public CelEnvironment export(Cel cel) {
//
// Whatever is left will be included in the Environment as custom declarations.

// Checker builder is used to access some parts of the config not exposed in the EnvVisitable
// interface.
CelCheckerBuilder checkerBuilder = cel.toCheckerBuilder();

CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(checkerBuilder.container());
addOptions(envBuilder, checkerBuilder.options());

Set<Object> inventory = new HashSet<>();
collectInventory(inventory, cel);
addExtensionConfigsAndRemoveFromInventory(envBuilder, inventory);
Expand All@@ -180,11 +206,29 @@ public CelEnvironment export(Cel cel) {
return envBuilder.build();
}

private void addOptions(CelEnvironment.Builder envBuilder, CelOptions options) {
// The set of features supported in the exported environment in Go is pretty limited right now.
ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();
if (options.enableHeterogeneousNumericComparisons()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.cross_type_numeric_comparisons", true));
}
if (options.enableQuotedIdentifierSyntax()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.backtick_escape_syntax", true));
}
if (options.populateMacroCalls()) {
featureFlags.add(CelEnvironment.FeatureFlag.create("cel.feature.macro_call_tracking", true));
}
envBuilder.setFeatures(featureFlags.build());
}

/**
* Collects all function overloads, variable declarations and macros from the given {@link Cel}
* instance and stores them in a map.
*/
private void collectInventory(Set<Object> inventory, Cel cel) {
private void collectInventory(Set<Object> inventory, CelCompiler cel) {
Preconditions.checkArgument(cel instanceof EnvVisitable);
((EnvVisitable) cel)
.accept(
new EnvVisitor() {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -143,6 +143,51 @@ private CelContainer parseContainer(ParserContext<Node> ctx, Node node) {
return builder.build();
}

private ImmutableSet<CelEnvironment.FeatureFlag> parseFeatures(
ParserContext<Node> ctx, Node node) {
long valueId = ctx.collectMetadata(node);
if (!validateYamlType(node, YamlNodeType.LIST, YamlNodeType.TEXT)) {
ctx.reportError(valueId, "Unsupported features format");
}

ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();

SequenceNode featureListNode = (SequenceNode) node;
for (Node featureMapNode : featureListNode.getValue()) {
long featureMapId = ctx.collectMetadata(featureMapNode);
if (!assertYamlType(ctx, featureMapId, featureMapNode, YamlNodeType.MAP)) {
continue;
}

MappingNode featureMap = (MappingNode) featureMapNode;
String name = "";
boolean enabled = true;
for (NodeTuple nodeTuple : featureMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "name":
name = newString(ctx, valueNode);
break;
case "enabled":
enabled = newBoolean(ctx, valueNode);
break;
default:
ctx.reportError(keyId, String.format("Unsupported feature tag: %s", keyName));
break;
}
}
if (name.isEmpty()) {
ctx.reportError(featureMapId, "Missing required attribute(s): name");
continue;
}
featureFlags.add(CelEnvironment.FeatureFlag.create(name, enabled));
}
return featureFlags.build();
}

private ImmutableSet<Alias> parseAliases(ParserContext<Node> ctx, Node node) {
ImmutableSet.Builder<Alias> aliasSetBuilder = ImmutableSet.builder();
long valueId = ctx.collectMetadata(node);
Expand DownExpand Up@@ -756,6 +801,9 @@ private CelEnvironment.Builder parseConfig(ParserContext<Node> ctx, Node node) {
case "stdlib":
builder.setStandardLibrarySubset(parseLibrarySubset(ctx, valueNode));
break;
case "features":
builder.setFeatures(parseFeatures(ctx, valueNode));
break;
default:
ctx.reportError(id, "Unknown config tag: " + fieldName);
// continue handling the rest of the nodes
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,6 +60,7 @@ private CelEnvironmentYamlSerializer() {
CelEnvironment.LibrarySubset.OverloadSelector.class, new RepresentOverloadSelector());
this.multiRepresenters.put(CelEnvironment.Alias.class, new RepresentAlias());
this.multiRepresenters.put(CelContainer.class, new RepresentContainer());
this.multiRepresenters.put(CelEnvironment.FeatureFlag.class, new RepresentFeatureFlag());
}

public static String toYaml(CelEnvironment environment) {
Expand DownExpand Up@@ -94,6 +95,9 @@ public Node representData(Object data) {
if (environment.standardLibrarySubset().isPresent()) {
configMap.put("stdlib", environment.standardLibrarySubset().get());
}
if (!environment.features().isEmpty()) {
configMap.put("features", environment.features().asList());
}
return represent(configMap.buildOrThrow());
}
}
Expand DownExpand Up@@ -258,4 +262,17 @@ public Node representData(Object data) {
return represent(ImmutableMap.<String, Object>of("id", overloadSelector.id()));
}
}

private final class RepresentFeatureFlag implements Represent {

@Override
public Node representData(Object data) {
CelEnvironment.FeatureFlag featureFlag = (CelEnvironment.FeatureFlag) data;
return represent(
ImmutableMap.builder()
.put("name", featureFlag.name())
.put("enabled", featureFlag.enabled())
.buildOrThrow());
}
}
}
Loading
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
1 change: 0 additions & 1 deletion bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,5 @@ java_library(

java_library(
name = "environment_exporter",
visibility = ["//:internal"],
exports = ["//bundle/src/main/java/dev/cel/bundle:environment_exporter"],
)
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@@ -126,13 +126,15 @@ java_library(
":environment",
"//:auto_value",
"//bundle:cel",
"//checker:checker_builder",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common/internal:env_visitor",
"//common/types:cel_proto_types",
"//common/types:cel_types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:extension_library",
"//parser:macro",
Expand Down
57 changes: 52 additions & 5 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
Expand DownExpand Up@@ -108,6 +109,9 @@ public abstract class CelEnvironment {
/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Feature flags to enable in the environment. */
public abstract ImmutableSet<FeatureFlag> features();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -159,6 +163,13 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

@CanIgnoreReturnValue
public Builder setFeatures(FeatureFlag... featureFlags) {
return setFeatures(ImmutableSet.copyOf(featureFlags));
}

public abstract Builder setFeatures(ImmutableSet<FeatureFlag> macros);

abstract CelEnvironment autoBuild();

@CheckReturnValue
Expand DownExpand Up@@ -188,18 +199,21 @@ public static Builder newBuilder() {
.setDescription("")
.setContainer(CelContainer.ofName(""))
.setVariables(ImmutableSet.of())
.setFunctions(ImmutableSet.of());
.setFunctions(ImmutableSet.of())
.setFeatures(ImmutableSet.of());
}

/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
celOptions = applyFeatureFlags(celOptions);
try {
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setContainer(container())
.setOptions(celOptions)
.setTypeProvider(celTypeProvider)
.addVarDeclarations(
variables().stream()
Expand All@@ -222,19 +236,35 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

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

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

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

private CelOptions applyFeatureFlags(CelOptions celOptions) {
CelOptions.Builder optionsBuilder = celOptions.toBuilder();
for (FeatureFlag featureFlag : features()) {
if (featureFlag.name().equals("cel.feature.macro_call_tracking")) {
optionsBuilder.populateMacroCalls(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.backtick_escape_syntax")) {
optionsBuilder.enableQuotedIdentifierSyntax(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.cross_type_numeric_comparisons")) {
optionsBuilder.enableHeterogeneousNumericComparisons(featureFlag.enabled());
} else {
throw new IllegalArgumentException("Unknown feature flag: " + featureFlag.name());
}
}
return optionsBuilder.build();
}

private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
Expand All@@ -250,7 +280,9 @@ private void addAllCompilerExtensions(
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
private CelRuntime extendRuntime(CelRuntime celRuntime, CelOptions celOptions) {
CelRuntimeBuilder celRuntimeBuilder = celRuntime.toRuntimeBuilder();
celRuntimeBuilder.setOptions(celOptions);
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
Expand All@@ -262,6 +294,7 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
return celRuntimeBuilder.build();
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
Expand DownExpand Up@@ -625,6 +658,20 @@ public CelType toCelType(CelTypeProvider celTypeProvider) {
}
}

/** Represents a feature flag that can be enabled in the environment. */
@AutoValue
public abstract static class FeatureFlag {
/** Normalized name of the feature flag. */
public abstract String name();

/** Whether the feature is enabled or disabled. */
public abstract boolean enabled();

public static FeatureFlag create(String name, boolean enabled) {
return new AutoValue_CelEnvironment_FeatureFlag(name, enabled);
}
}

/**
* Represents a configuration for a canonical CEL extension that can be enabled in the
* environment.
Expand Down
50 changes: 47 additions & 3 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironmentExporter.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,7 @@
import dev.cel.expr.Decl.FunctionDecl;
import dev.cel.expr.Decl.FunctionDecl.Overload;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
Expand All@@ -30,6 +31,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.bundle.CelEnvironment.OverloadDecl;
import dev.cel.checker.CelCheckerBuilder;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardIdentifier;
import dev.cel.common.CelFunctionDecl;
Expand All@@ -41,6 +43,7 @@
import dev.cel.common.types.CelProtoTypes;
import dev.cel.common.types.CelType;
import dev.cel.common.types.CelTypes;
import dev.cel.compiler.CelCompiler;
import dev.cel.extensions.CelExtensionLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelMacro;
Expand DownExpand Up@@ -161,9 +164,24 @@ public static CelEnvironmentExporter.Builder newBuilder() {
* </ul>
*/
public CelEnvironment export(Cel cel) {
CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(cel.toCheckerBuilder().container());
return export((CelCompiler) cel);
}

/**
* Exports a {@link CelEnvironment} that describes the configuration of the given {@link
* CelCompiler} instance.
*
* <p>The exported environment includes:
*
* <ul>
* <li>Standard library subset: functions and their overloads that are either included or
* excluded from the standard library.
* <li>Extension libraries: names and versions of the extension libraries that are used.
* <li>Custom declarations: functions and variables that are not part of the standard library or
* any of the extension libraries.
* </ul>
*/
public CelEnvironment export(CelCompiler cel) {
// Inventory is a full set of declarations and macros that are found in the configuration of
// the supplied CEL instance.
//
Expand All@@ -172,6 +190,14 @@ public CelEnvironment export(Cel cel) {
//
// Whatever is left will be included in the Environment as custom declarations.

// Checker builder is used to access some parts of the config not exposed in the EnvVisitable
// interface.
CelCheckerBuilder checkerBuilder = cel.toCheckerBuilder();

CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(checkerBuilder.container());
addOptions(envBuilder, checkerBuilder.options());

Set<Object> inventory = new HashSet<>();
collectInventory(inventory, cel);
addExtensionConfigsAndRemoveFromInventory(envBuilder, inventory);
Expand All@@ -180,11 +206,29 @@ public CelEnvironment export(Cel cel) {
return envBuilder.build();
}

private void addOptions(CelEnvironment.Builder envBuilder, CelOptions options) {
// The set of features supported in the exported environment in Go is pretty limited right now.
ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();
if (options.enableHeterogeneousNumericComparisons()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.cross_type_numeric_comparisons", true));
}
if (options.enableQuotedIdentifierSyntax()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.backtick_escape_syntax", true));
}
if (options.populateMacroCalls()) {
featureFlags.add(CelEnvironment.FeatureFlag.create("cel.feature.macro_call_tracking", true));
}
envBuilder.setFeatures(featureFlags.build());
}

/**
* Collects all function overloads, variable declarations and macros from the given {@link Cel}
* instance and stores them in a map.
*/
private void collectInventory(Set<Object> inventory, Cel cel) {
private void collectInventory(Set<Object> inventory, CelCompiler cel) {
Preconditions.checkArgument(cel instanceof EnvVisitable);
((EnvVisitable) cel)
.accept(
new EnvVisitor() {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -143,6 +143,51 @@ private CelContainer parseContainer(ParserContext<Node> ctx, Node node) {
return builder.build();
}

private ImmutableSet<CelEnvironment.FeatureFlag> parseFeatures(
ParserContext<Node> ctx, Node node) {
long valueId = ctx.collectMetadata(node);
if (!validateYamlType(node, YamlNodeType.LIST, YamlNodeType.TEXT)) {
ctx.reportError(valueId, "Unsupported features format");
}

ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();

SequenceNode featureListNode = (SequenceNode) node;
for (Node featureMapNode : featureListNode.getValue()) {
long featureMapId = ctx.collectMetadata(featureMapNode);
if (!assertYamlType(ctx, featureMapId, featureMapNode, YamlNodeType.MAP)) {
continue;
}

MappingNode featureMap = (MappingNode) featureMapNode;
String name = "";
boolean enabled = true;
for (NodeTuple nodeTuple : featureMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "name":
name = newString(ctx, valueNode);
break;
case "enabled":
enabled = newBoolean(ctx, valueNode);
break;
default:
ctx.reportError(keyId, String.format("Unsupported feature tag: %s", keyName));
break;
}
}
if (name.isEmpty()) {
ctx.reportError(featureMapId, "Missing required attribute(s): name");
continue;
}
featureFlags.add(CelEnvironment.FeatureFlag.create(name, enabled));
}
return featureFlags.build();
}

private ImmutableSet<Alias> parseAliases(ParserContext<Node> ctx, Node node) {
ImmutableSet.Builder<Alias> aliasSetBuilder = ImmutableSet.builder();
long valueId = ctx.collectMetadata(node);
Expand DownExpand Up@@ -756,6 +801,9 @@ private CelEnvironment.Builder parseConfig(ParserContext<Node> ctx, Node node) {
case "stdlib":
builder.setStandardLibrarySubset(parseLibrarySubset(ctx, valueNode));
break;
case "features":
builder.setFeatures(parseFeatures(ctx, valueNode));
break;
default:
ctx.reportError(id, "Unknown config tag: " + fieldName);
// continue handling the rest of the nodes
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,6 +60,7 @@ private CelEnvironmentYamlSerializer() {
CelEnvironment.LibrarySubset.OverloadSelector.class, new RepresentOverloadSelector());
this.multiRepresenters.put(CelEnvironment.Alias.class, new RepresentAlias());
this.multiRepresenters.put(CelContainer.class, new RepresentContainer());
this.multiRepresenters.put(CelEnvironment.FeatureFlag.class, new RepresentFeatureFlag());
}

public static String toYaml(CelEnvironment environment) {
Expand DownExpand Up@@ -94,6 +95,9 @@ public Node representData(Object data) {
if (environment.standardLibrarySubset().isPresent()) {
configMap.put("stdlib", environment.standardLibrarySubset().get());
}
if (!environment.features().isEmpty()) {
configMap.put("features", environment.features().asList());
}
return represent(configMap.buildOrThrow());
}
}
Expand DownExpand Up@@ -258,4 +262,17 @@ public Node representData(Object data) {
return represent(ImmutableMap.<String, Object>of("id", overloadSelector.id()));
}
}

private final class RepresentFeatureFlag implements Represent {

@Override
public Node representData(Object data) {
CelEnvironment.FeatureFlag featureFlag = (CelEnvironment.FeatureFlag) data;
return represent(
ImmutableMap.builder()
.put("name", featureFlag.name())
.put("enabled", featureFlag.enabled())
.buildOrThrow());
}
}
}
Loading
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
1 change: 0 additions & 1 deletion bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,5 @@ java_library(

java_library(
name = "environment_exporter",
visibility = ["//:internal"],
exports = ["//bundle/src/main/java/dev/cel/bundle:environment_exporter"],
)
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@@ -126,13 +126,15 @@ java_library(
":environment",
"//:auto_value",
"//bundle:cel",
"//checker:checker_builder",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common/internal:env_visitor",
"//common/types:cel_proto_types",
"//common/types:cel_types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:extension_library",
"//parser:macro",
Expand Down
57 changes: 52 additions & 5 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
Expand DownExpand Up@@ -108,6 +109,9 @@ public abstract class CelEnvironment {
/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Feature flags to enable in the environment. */
public abstract ImmutableSet<FeatureFlag> features();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -159,6 +163,13 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

@CanIgnoreReturnValue
public Builder setFeatures(FeatureFlag... featureFlags) {
return setFeatures(ImmutableSet.copyOf(featureFlags));
}

public abstract Builder setFeatures(ImmutableSet<FeatureFlag> macros);

abstract CelEnvironment autoBuild();

@CheckReturnValue
Expand DownExpand Up@@ -188,18 +199,21 @@ public static Builder newBuilder() {
.setDescription("")
.setContainer(CelContainer.ofName(""))
.setVariables(ImmutableSet.of())
.setFunctions(ImmutableSet.of());
.setFunctions(ImmutableSet.of())
.setFeatures(ImmutableSet.of());
}

/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
celOptions = applyFeatureFlags(celOptions);
try {
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setContainer(container())
.setOptions(celOptions)
.setTypeProvider(celTypeProvider)
.addVarDeclarations(
variables().stream()
Expand All@@ -222,19 +236,35 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

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

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

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

private CelOptions applyFeatureFlags(CelOptions celOptions) {
CelOptions.Builder optionsBuilder = celOptions.toBuilder();
for (FeatureFlag featureFlag : features()) {
if (featureFlag.name().equals("cel.feature.macro_call_tracking")) {
optionsBuilder.populateMacroCalls(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.backtick_escape_syntax")) {
optionsBuilder.enableQuotedIdentifierSyntax(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.cross_type_numeric_comparisons")) {
optionsBuilder.enableHeterogeneousNumericComparisons(featureFlag.enabled());
} else {
throw new IllegalArgumentException("Unknown feature flag: " + featureFlag.name());
}
}
return optionsBuilder.build();
}

private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
Expand All@@ -250,7 +280,9 @@ private void addAllCompilerExtensions(
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
private CelRuntime extendRuntime(CelRuntime celRuntime, CelOptions celOptions) {
CelRuntimeBuilder celRuntimeBuilder = celRuntime.toRuntimeBuilder();
celRuntimeBuilder.setOptions(celOptions);
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
Expand All@@ -262,6 +294,7 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
return celRuntimeBuilder.build();
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
Expand DownExpand Up@@ -625,6 +658,20 @@ public CelType toCelType(CelTypeProvider celTypeProvider) {
}
}

/** Represents a feature flag that can be enabled in the environment. */
@AutoValue
public abstract static class FeatureFlag {
/** Normalized name of the feature flag. */
public abstract String name();

/** Whether the feature is enabled or disabled. */
public abstract boolean enabled();

public static FeatureFlag create(String name, boolean enabled) {
return new AutoValue_CelEnvironment_FeatureFlag(name, enabled);
}
}

/**
* Represents a configuration for a canonical CEL extension that can be enabled in the
* environment.
Expand Down
50 changes: 47 additions & 3 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironmentExporter.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,7 @@
import dev.cel.expr.Decl.FunctionDecl;
import dev.cel.expr.Decl.FunctionDecl.Overload;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
Expand All@@ -30,6 +31,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.bundle.CelEnvironment.OverloadDecl;
import dev.cel.checker.CelCheckerBuilder;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardIdentifier;
import dev.cel.common.CelFunctionDecl;
Expand All@@ -41,6 +43,7 @@
import dev.cel.common.types.CelProtoTypes;
import dev.cel.common.types.CelType;
import dev.cel.common.types.CelTypes;
import dev.cel.compiler.CelCompiler;
import dev.cel.extensions.CelExtensionLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelMacro;
Expand DownExpand Up@@ -161,9 +164,24 @@ public static CelEnvironmentExporter.Builder newBuilder() {
* </ul>
*/
public CelEnvironment export(Cel cel) {
CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(cel.toCheckerBuilder().container());
return export((CelCompiler) cel);
}

/**
* Exports a {@link CelEnvironment} that describes the configuration of the given {@link
* CelCompiler} instance.
*
* <p>The exported environment includes:
*
* <ul>
* <li>Standard library subset: functions and their overloads that are either included or
* excluded from the standard library.
* <li>Extension libraries: names and versions of the extension libraries that are used.
* <li>Custom declarations: functions and variables that are not part of the standard library or
* any of the extension libraries.
* </ul>
*/
public CelEnvironment export(CelCompiler cel) {
// Inventory is a full set of declarations and macros that are found in the configuration of
// the supplied CEL instance.
//
Expand All@@ -172,6 +190,14 @@ public CelEnvironment export(Cel cel) {
//
// Whatever is left will be included in the Environment as custom declarations.

// Checker builder is used to access some parts of the config not exposed in the EnvVisitable
// interface.
CelCheckerBuilder checkerBuilder = cel.toCheckerBuilder();

CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(checkerBuilder.container());
addOptions(envBuilder, checkerBuilder.options());

Set<Object> inventory = new HashSet<>();
collectInventory(inventory, cel);
addExtensionConfigsAndRemoveFromInventory(envBuilder, inventory);
Expand All@@ -180,11 +206,29 @@ public CelEnvironment export(Cel cel) {
return envBuilder.build();
}

private void addOptions(CelEnvironment.Builder envBuilder, CelOptions options) {
// The set of features supported in the exported environment in Go is pretty limited right now.
ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();
if (options.enableHeterogeneousNumericComparisons()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.cross_type_numeric_comparisons", true));
}
if (options.enableQuotedIdentifierSyntax()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.backtick_escape_syntax", true));
}
if (options.populateMacroCalls()) {
featureFlags.add(CelEnvironment.FeatureFlag.create("cel.feature.macro_call_tracking", true));
}
envBuilder.setFeatures(featureFlags.build());
}

/**
* Collects all function overloads, variable declarations and macros from the given {@link Cel}
* instance and stores them in a map.
*/
private void collectInventory(Set<Object> inventory, Cel cel) {
private void collectInventory(Set<Object> inventory, CelCompiler cel) {
Preconditions.checkArgument(cel instanceof EnvVisitable);
((EnvVisitable) cel)
.accept(
new EnvVisitor() {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -143,6 +143,51 @@ private CelContainer parseContainer(ParserContext<Node> ctx, Node node) {
return builder.build();
}

private ImmutableSet<CelEnvironment.FeatureFlag> parseFeatures(
ParserContext<Node> ctx, Node node) {
long valueId = ctx.collectMetadata(node);
if (!validateYamlType(node, YamlNodeType.LIST, YamlNodeType.TEXT)) {
ctx.reportError(valueId, "Unsupported features format");
}

ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();

SequenceNode featureListNode = (SequenceNode) node;
for (Node featureMapNode : featureListNode.getValue()) {
long featureMapId = ctx.collectMetadata(featureMapNode);
if (!assertYamlType(ctx, featureMapId, featureMapNode, YamlNodeType.MAP)) {
continue;
}

MappingNode featureMap = (MappingNode) featureMapNode;
String name = "";
boolean enabled = true;
for (NodeTuple nodeTuple : featureMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "name":
name = newString(ctx, valueNode);
break;
case "enabled":
enabled = newBoolean(ctx, valueNode);
break;
default:
ctx.reportError(keyId, String.format("Unsupported feature tag: %s", keyName));
break;
}
}
if (name.isEmpty()) {
ctx.reportError(featureMapId, "Missing required attribute(s): name");
continue;
}
featureFlags.add(CelEnvironment.FeatureFlag.create(name, enabled));
}
return featureFlags.build();
}

private ImmutableSet<Alias> parseAliases(ParserContext<Node> ctx, Node node) {
ImmutableSet.Builder<Alias> aliasSetBuilder = ImmutableSet.builder();
long valueId = ctx.collectMetadata(node);
Expand DownExpand Up@@ -756,6 +801,9 @@ private CelEnvironment.Builder parseConfig(ParserContext<Node> ctx, Node node) {
case "stdlib":
builder.setStandardLibrarySubset(parseLibrarySubset(ctx, valueNode));
break;
case "features":
builder.setFeatures(parseFeatures(ctx, valueNode));
break;
default:
ctx.reportError(id, "Unknown config tag: " + fieldName);
// continue handling the rest of the nodes
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,6 +60,7 @@ private CelEnvironmentYamlSerializer() {
CelEnvironment.LibrarySubset.OverloadSelector.class, new RepresentOverloadSelector());
this.multiRepresenters.put(CelEnvironment.Alias.class, new RepresentAlias());
this.multiRepresenters.put(CelContainer.class, new RepresentContainer());
this.multiRepresenters.put(CelEnvironment.FeatureFlag.class, new RepresentFeatureFlag());
}

public static String toYaml(CelEnvironment environment) {
Expand DownExpand Up@@ -94,6 +95,9 @@ public Node representData(Object data) {
if (environment.standardLibrarySubset().isPresent()) {
configMap.put("stdlib", environment.standardLibrarySubset().get());
}
if (!environment.features().isEmpty()) {
configMap.put("features", environment.features().asList());
}
return represent(configMap.buildOrThrow());
}
}
Expand DownExpand Up@@ -258,4 +262,17 @@ public Node representData(Object data) {
return represent(ImmutableMap.<String, Object>of("id", overloadSelector.id()));
}
}

private final class RepresentFeatureFlag implements Represent {

@Override
public Node representData(Object data) {
CelEnvironment.FeatureFlag featureFlag = (CelEnvironment.FeatureFlag) data;
return represent(
ImmutableMap.builder()
.put("name", featureFlag.name())
.put("enabled", featureFlag.enabled())
.buildOrThrow());
}
}
}
Loading
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
1 change: 0 additions & 1 deletion bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,5 @@ java_library(

java_library(
name = "environment_exporter",
visibility = ["//:internal"],
exports = ["//bundle/src/main/java/dev/cel/bundle:environment_exporter"],
)
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@@ -126,13 +126,15 @@ java_library(
":environment",
"//:auto_value",
"//bundle:cel",
"//checker:checker_builder",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common/internal:env_visitor",
"//common/types:cel_proto_types",
"//common/types:cel_types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:extension_library",
"//parser:macro",
Expand Down
57 changes: 52 additions & 5 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
Expand DownExpand Up@@ -108,6 +109,9 @@ public abstract class CelEnvironment {
/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Feature flags to enable in the environment. */
public abstract ImmutableSet<FeatureFlag> features();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -159,6 +163,13 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

@CanIgnoreReturnValue
public Builder setFeatures(FeatureFlag... featureFlags) {
return setFeatures(ImmutableSet.copyOf(featureFlags));
}

public abstract Builder setFeatures(ImmutableSet<FeatureFlag> macros);

abstract CelEnvironment autoBuild();

@CheckReturnValue
Expand DownExpand Up@@ -188,18 +199,21 @@ public static Builder newBuilder() {
.setDescription("")
.setContainer(CelContainer.ofName(""))
.setVariables(ImmutableSet.of())
.setFunctions(ImmutableSet.of());
.setFunctions(ImmutableSet.of())
.setFeatures(ImmutableSet.of());
}

/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
celOptions = applyFeatureFlags(celOptions);
try {
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setContainer(container())
.setOptions(celOptions)
.setTypeProvider(celTypeProvider)
.addVarDeclarations(
variables().stream()
Expand All@@ -222,19 +236,35 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

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

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

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

private CelOptions applyFeatureFlags(CelOptions celOptions) {
CelOptions.Builder optionsBuilder = celOptions.toBuilder();
for (FeatureFlag featureFlag : features()) {
if (featureFlag.name().equals("cel.feature.macro_call_tracking")) {
optionsBuilder.populateMacroCalls(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.backtick_escape_syntax")) {
optionsBuilder.enableQuotedIdentifierSyntax(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.cross_type_numeric_comparisons")) {
optionsBuilder.enableHeterogeneousNumericComparisons(featureFlag.enabled());
} else {
throw new IllegalArgumentException("Unknown feature flag: " + featureFlag.name());
}
}
return optionsBuilder.build();
}

private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
Expand All@@ -250,7 +280,9 @@ private void addAllCompilerExtensions(
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
private CelRuntime extendRuntime(CelRuntime celRuntime, CelOptions celOptions) {
CelRuntimeBuilder celRuntimeBuilder = celRuntime.toRuntimeBuilder();
celRuntimeBuilder.setOptions(celOptions);
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
Expand All@@ -262,6 +294,7 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
return celRuntimeBuilder.build();
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
Expand DownExpand Up@@ -625,6 +658,20 @@ public CelType toCelType(CelTypeProvider celTypeProvider) {
}
}

/** Represents a feature flag that can be enabled in the environment. */
@AutoValue
public abstract static class FeatureFlag {
/** Normalized name of the feature flag. */
public abstract String name();

/** Whether the feature is enabled or disabled. */
public abstract boolean enabled();

public static FeatureFlag create(String name, boolean enabled) {
return new AutoValue_CelEnvironment_FeatureFlag(name, enabled);
}
}

/**
* Represents a configuration for a canonical CEL extension that can be enabled in the
* environment.
Expand Down
50 changes: 47 additions & 3 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironmentExporter.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,7 @@
import dev.cel.expr.Decl.FunctionDecl;
import dev.cel.expr.Decl.FunctionDecl.Overload;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
Expand All@@ -30,6 +31,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.bundle.CelEnvironment.OverloadDecl;
import dev.cel.checker.CelCheckerBuilder;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardIdentifier;
import dev.cel.common.CelFunctionDecl;
Expand All@@ -41,6 +43,7 @@
import dev.cel.common.types.CelProtoTypes;
import dev.cel.common.types.CelType;
import dev.cel.common.types.CelTypes;
import dev.cel.compiler.CelCompiler;
import dev.cel.extensions.CelExtensionLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelMacro;
Expand DownExpand Up@@ -161,9 +164,24 @@ public static CelEnvironmentExporter.Builder newBuilder() {
* </ul>
*/
public CelEnvironment export(Cel cel) {
CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(cel.toCheckerBuilder().container());
return export((CelCompiler) cel);
}

/**
* Exports a {@link CelEnvironment} that describes the configuration of the given {@link
* CelCompiler} instance.
*
* <p>The exported environment includes:
*
* <ul>
* <li>Standard library subset: functions and their overloads that are either included or
* excluded from the standard library.
* <li>Extension libraries: names and versions of the extension libraries that are used.
* <li>Custom declarations: functions and variables that are not part of the standard library or
* any of the extension libraries.
* </ul>
*/
public CelEnvironment export(CelCompiler cel) {
// Inventory is a full set of declarations and macros that are found in the configuration of
// the supplied CEL instance.
//
Expand All@@ -172,6 +190,14 @@ public CelEnvironment export(Cel cel) {
//
// Whatever is left will be included in the Environment as custom declarations.

// Checker builder is used to access some parts of the config not exposed in the EnvVisitable
// interface.
CelCheckerBuilder checkerBuilder = cel.toCheckerBuilder();

CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(checkerBuilder.container());
addOptions(envBuilder, checkerBuilder.options());

Set<Object> inventory = new HashSet<>();
collectInventory(inventory, cel);
addExtensionConfigsAndRemoveFromInventory(envBuilder, inventory);
Expand All@@ -180,11 +206,29 @@ public CelEnvironment export(Cel cel) {
return envBuilder.build();
}

private void addOptions(CelEnvironment.Builder envBuilder, CelOptions options) {
// The set of features supported in the exported environment in Go is pretty limited right now.
ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();
if (options.enableHeterogeneousNumericComparisons()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.cross_type_numeric_comparisons", true));
}
if (options.enableQuotedIdentifierSyntax()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.backtick_escape_syntax", true));
}
if (options.populateMacroCalls()) {
featureFlags.add(CelEnvironment.FeatureFlag.create("cel.feature.macro_call_tracking", true));
}
envBuilder.setFeatures(featureFlags.build());
}

/**
* Collects all function overloads, variable declarations and macros from the given {@link Cel}
* instance and stores them in a map.
*/
private void collectInventory(Set<Object> inventory, Cel cel) {
private void collectInventory(Set<Object> inventory, CelCompiler cel) {
Preconditions.checkArgument(cel instanceof EnvVisitable);
((EnvVisitable) cel)
.accept(
new EnvVisitor() {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -143,6 +143,51 @@ private CelContainer parseContainer(ParserContext<Node> ctx, Node node) {
return builder.build();
}

private ImmutableSet<CelEnvironment.FeatureFlag> parseFeatures(
ParserContext<Node> ctx, Node node) {
long valueId = ctx.collectMetadata(node);
if (!validateYamlType(node, YamlNodeType.LIST, YamlNodeType.TEXT)) {
ctx.reportError(valueId, "Unsupported features format");
}

ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();

SequenceNode featureListNode = (SequenceNode) node;
for (Node featureMapNode : featureListNode.getValue()) {
long featureMapId = ctx.collectMetadata(featureMapNode);
if (!assertYamlType(ctx, featureMapId, featureMapNode, YamlNodeType.MAP)) {
continue;
}

MappingNode featureMap = (MappingNode) featureMapNode;
String name = "";
boolean enabled = true;
for (NodeTuple nodeTuple : featureMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "name":
name = newString(ctx, valueNode);
break;
case "enabled":
enabled = newBoolean(ctx, valueNode);
break;
default:
ctx.reportError(keyId, String.format("Unsupported feature tag: %s", keyName));
break;
}
}
if (name.isEmpty()) {
ctx.reportError(featureMapId, "Missing required attribute(s): name");
continue;
}
featureFlags.add(CelEnvironment.FeatureFlag.create(name, enabled));
}
return featureFlags.build();
}

private ImmutableSet<Alias> parseAliases(ParserContext<Node> ctx, Node node) {
ImmutableSet.Builder<Alias> aliasSetBuilder = ImmutableSet.builder();
long valueId = ctx.collectMetadata(node);
Expand DownExpand Up@@ -756,6 +801,9 @@ private CelEnvironment.Builder parseConfig(ParserContext<Node> ctx, Node node) {
case "stdlib":
builder.setStandardLibrarySubset(parseLibrarySubset(ctx, valueNode));
break;
case "features":
builder.setFeatures(parseFeatures(ctx, valueNode));
break;
default:
ctx.reportError(id, "Unknown config tag: " + fieldName);
// continue handling the rest of the nodes
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,6 +60,7 @@ private CelEnvironmentYamlSerializer() {
CelEnvironment.LibrarySubset.OverloadSelector.class, new RepresentOverloadSelector());
this.multiRepresenters.put(CelEnvironment.Alias.class, new RepresentAlias());
this.multiRepresenters.put(CelContainer.class, new RepresentContainer());
this.multiRepresenters.put(CelEnvironment.FeatureFlag.class, new RepresentFeatureFlag());
}

public static String toYaml(CelEnvironment environment) {
Expand DownExpand Up@@ -94,6 +95,9 @@ public Node representData(Object data) {
if (environment.standardLibrarySubset().isPresent()) {
configMap.put("stdlib", environment.standardLibrarySubset().get());
}
if (!environment.features().isEmpty()) {
configMap.put("features", environment.features().asList());
}
return represent(configMap.buildOrThrow());
}
}
Expand DownExpand Up@@ -258,4 +262,17 @@ public Node representData(Object data) {
return represent(ImmutableMap.<String, Object>of("id", overloadSelector.id()));
}
}

private final class RepresentFeatureFlag implements Represent {

@Override
public Node representData(Object data) {
CelEnvironment.FeatureFlag featureFlag = (CelEnvironment.FeatureFlag) data;
return represent(
ImmutableMap.builder()
.put("name", featureFlag.name())
.put("enabled", featureFlag.enabled())
.buildOrThrow());
}
}
}
Loading
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
1 change: 0 additions & 1 deletion bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,5 @@ java_library(

java_library(
name = "environment_exporter",
visibility = ["//:internal"],
exports = ["//bundle/src/main/java/dev/cel/bundle:environment_exporter"],
)
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@@ -126,13 +126,15 @@ java_library(
":environment",
"//:auto_value",
"//bundle:cel",
"//checker:checker_builder",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common/internal:env_visitor",
"//common/types:cel_proto_types",
"//common/types:cel_types",
"//common/types:type_providers",
"//compiler:compiler_builder",
"//extensions",
"//extensions:extension_library",
"//parser:macro",
Expand Down
57 changes: 52 additions & 5 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,6 +48,7 @@
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
Expand DownExpand Up@@ -108,6 +109,9 @@ public abstract class CelEnvironment {
/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Feature flags to enable in the environment. */
public abstract ImmutableSet<FeatureFlag> features();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -159,6 +163,13 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

@CanIgnoreReturnValue
public Builder setFeatures(FeatureFlag... featureFlags) {
return setFeatures(ImmutableSet.copyOf(featureFlags));
}

public abstract Builder setFeatures(ImmutableSet<FeatureFlag> macros);

abstract CelEnvironment autoBuild();

@CheckReturnValue
Expand DownExpand Up@@ -188,18 +199,21 @@ public static Builder newBuilder() {
.setDescription("")
.setContainer(CelContainer.ofName(""))
.setVariables(ImmutableSet.of())
.setFunctions(ImmutableSet.of());
.setFunctions(ImmutableSet.of())
.setFeatures(ImmutableSet.of());
}

/** Extends the provided {@link CelCompiler} environment with this configuration. */
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
throws CelEnvironmentException {
celOptions = applyFeatureFlags(celOptions);
try {
CelTypeProvider celTypeProvider = celCompiler.getTypeProvider();
CelCompilerBuilder compilerBuilder =
celCompiler
.toCompilerBuilder()
.setContainer(container())
.setOptions(celOptions)
.setTypeProvider(celTypeProvider)
.addVarDeclarations(
variables().stream()
Expand All@@ -222,19 +236,35 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

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

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

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

private CelOptions applyFeatureFlags(CelOptions celOptions) {
CelOptions.Builder optionsBuilder = celOptions.toBuilder();
for (FeatureFlag featureFlag : features()) {
if (featureFlag.name().equals("cel.feature.macro_call_tracking")) {
optionsBuilder.populateMacroCalls(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.backtick_escape_syntax")) {
optionsBuilder.enableQuotedIdentifierSyntax(featureFlag.enabled());
} else if (featureFlag.name().equals("cel.feature.cross_type_numeric_comparisons")) {
optionsBuilder.enableHeterogeneousNumericComparisons(featureFlag.enabled());
} else {
throw new IllegalArgumentException("Unknown feature flag: " + featureFlag.name());
}
}
return optionsBuilder.build();
}

private void addAllCompilerExtensions(
CelCompilerBuilder celCompilerBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
Expand All@@ -250,7 +280,9 @@ private void addAllCompilerExtensions(
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
private CelRuntime extendRuntime(CelRuntime celRuntime, CelOptions celOptions) {
CelRuntimeBuilder celRuntimeBuilder = celRuntime.toRuntimeBuilder();
celRuntimeBuilder.setOptions(celOptions);
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
Expand All@@ -262,6 +294,7 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
return celRuntimeBuilder.build();
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
Expand DownExpand Up@@ -625,6 +658,20 @@ public CelType toCelType(CelTypeProvider celTypeProvider) {
}
}

/** Represents a feature flag that can be enabled in the environment. */
@AutoValue
public abstract static class FeatureFlag {
/** Normalized name of the feature flag. */
public abstract String name();

/** Whether the feature is enabled or disabled. */
public abstract boolean enabled();

public static FeatureFlag create(String name, boolean enabled) {
return new AutoValue_CelEnvironment_FeatureFlag(name, enabled);
}
}

/**
* Represents a configuration for a canonical CEL extension that can be enabled in the
* environment.
Expand Down
50 changes: 47 additions & 3 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironmentExporter.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,7 @@
import dev.cel.expr.Decl.FunctionDecl;
import dev.cel.expr.Decl.FunctionDecl.Overload;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
Expand All@@ -30,6 +31,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.bundle.CelEnvironment.OverloadDecl;
import dev.cel.checker.CelCheckerBuilder;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardIdentifier;
import dev.cel.common.CelFunctionDecl;
Expand All@@ -41,6 +43,7 @@
import dev.cel.common.types.CelProtoTypes;
import dev.cel.common.types.CelType;
import dev.cel.common.types.CelTypes;
import dev.cel.compiler.CelCompiler;
import dev.cel.extensions.CelExtensionLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.parser.CelMacro;
Expand DownExpand Up@@ -161,9 +164,24 @@ public static CelEnvironmentExporter.Builder newBuilder() {
* </ul>
*/
public CelEnvironment export(Cel cel) {
CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(cel.toCheckerBuilder().container());
return export((CelCompiler) cel);
}

/**
* Exports a {@link CelEnvironment} that describes the configuration of the given {@link
* CelCompiler} instance.
*
* <p>The exported environment includes:
*
* <ul>
* <li>Standard library subset: functions and their overloads that are either included or
* excluded from the standard library.
* <li>Extension libraries: names and versions of the extension libraries that are used.
* <li>Custom declarations: functions and variables that are not part of the standard library or
* any of the extension libraries.
* </ul>
*/
public CelEnvironment export(CelCompiler cel) {
// Inventory is a full set of declarations and macros that are found in the configuration of
// the supplied CEL instance.
//
Expand All@@ -172,6 +190,14 @@ public CelEnvironment export(Cel cel) {
//
// Whatever is left will be included in the Environment as custom declarations.

// Checker builder is used to access some parts of the config not exposed in the EnvVisitable
// interface.
CelCheckerBuilder checkerBuilder = cel.toCheckerBuilder();

CelEnvironment.Builder envBuilder =
CelEnvironment.newBuilder().setContainer(checkerBuilder.container());
addOptions(envBuilder, checkerBuilder.options());

Set<Object> inventory = new HashSet<>();
collectInventory(inventory, cel);
addExtensionConfigsAndRemoveFromInventory(envBuilder, inventory);
Expand All@@ -180,11 +206,29 @@ public CelEnvironment export(Cel cel) {
return envBuilder.build();
}

private void addOptions(CelEnvironment.Builder envBuilder, CelOptions options) {
// The set of features supported in the exported environment in Go is pretty limited right now.
ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();
if (options.enableHeterogeneousNumericComparisons()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.cross_type_numeric_comparisons", true));
}
if (options.enableQuotedIdentifierSyntax()) {
featureFlags.add(
CelEnvironment.FeatureFlag.create("cel.feature.backtick_escape_syntax", true));
}
if (options.populateMacroCalls()) {
featureFlags.add(CelEnvironment.FeatureFlag.create("cel.feature.macro_call_tracking", true));
}
envBuilder.setFeatures(featureFlags.build());
}

/**
* Collects all function overloads, variable declarations and macros from the given {@link Cel}
* instance and stores them in a map.
*/
private void collectInventory(Set<Object> inventory, Cel cel) {
private void collectInventory(Set<Object> inventory, CelCompiler cel) {
Preconditions.checkArgument(cel instanceof EnvVisitable);
((EnvVisitable) cel)
.accept(
new EnvVisitor() {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -143,6 +143,51 @@ private CelContainer parseContainer(ParserContext<Node> ctx, Node node) {
return builder.build();
}

private ImmutableSet<CelEnvironment.FeatureFlag> parseFeatures(
ParserContext<Node> ctx, Node node) {
long valueId = ctx.collectMetadata(node);
if (!validateYamlType(node, YamlNodeType.LIST, YamlNodeType.TEXT)) {
ctx.reportError(valueId, "Unsupported features format");
}

ImmutableSet.Builder<CelEnvironment.FeatureFlag> featureFlags = ImmutableSet.builder();

SequenceNode featureListNode = (SequenceNode) node;
for (Node featureMapNode : featureListNode.getValue()) {
long featureMapId = ctx.collectMetadata(featureMapNode);
if (!assertYamlType(ctx, featureMapId, featureMapNode, YamlNodeType.MAP)) {
continue;
}

MappingNode featureMap = (MappingNode) featureMapNode;
String name = "";
boolean enabled = true;
for (NodeTuple nodeTuple : featureMap.getValue()) {
Node keyNode = nodeTuple.getKeyNode();
long keyId = ctx.collectMetadata(keyNode);
Node valueNode = nodeTuple.getValueNode();
String keyName = ((ScalarNode) keyNode).getValue();
switch (keyName) {
case "name":
name = newString(ctx, valueNode);
break;
case "enabled":
enabled = newBoolean(ctx, valueNode);
break;
default:
ctx.reportError(keyId, String.format("Unsupported feature tag: %s", keyName));
break;
}
}
if (name.isEmpty()) {
ctx.reportError(featureMapId, "Missing required attribute(s): name");
continue;
}
featureFlags.add(CelEnvironment.FeatureFlag.create(name, enabled));
}
return featureFlags.build();
}

private ImmutableSet<Alias> parseAliases(ParserContext<Node> ctx, Node node) {
ImmutableSet.Builder<Alias> aliasSetBuilder = ImmutableSet.builder();
long valueId = ctx.collectMetadata(node);
Expand DownExpand Up@@ -756,6 +801,9 @@ private CelEnvironment.Builder parseConfig(ParserContext<Node> ctx, Node node) {
case "stdlib":
builder.setStandardLibrarySubset(parseLibrarySubset(ctx, valueNode));
break;
case "features":
builder.setFeatures(parseFeatures(ctx, valueNode));
break;
default:
ctx.reportError(id, "Unknown config tag: " + fieldName);
// continue handling the rest of the nodes
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,6 +60,7 @@ private CelEnvironmentYamlSerializer() {
CelEnvironment.LibrarySubset.OverloadSelector.class, new RepresentOverloadSelector());
this.multiRepresenters.put(CelEnvironment.Alias.class, new RepresentAlias());
this.multiRepresenters.put(CelContainer.class, new RepresentContainer());
this.multiRepresenters.put(CelEnvironment.FeatureFlag.class, new RepresentFeatureFlag());
}

public static String toYaml(CelEnvironment environment) {
Expand DownExpand Up@@ -94,6 +95,9 @@ public Node representData(Object data) {
if (environment.standardLibrarySubset().isPresent()) {
configMap.put("stdlib", environment.standardLibrarySubset().get());
}
if (!environment.features().isEmpty()) {
configMap.put("features", environment.features().asList());
}
return represent(configMap.buildOrThrow());
}
}
Expand DownExpand Up@@ -258,4 +262,17 @@ public Node representData(Object data) {
return represent(ImmutableMap.<String, Object>of("id", overloadSelector.id()));
}
}

private final class RepresentFeatureFlag implements Represent {

@Override
public Node representData(Object data) {
CelEnvironment.FeatureFlag featureFlag = (CelEnvironment.FeatureFlag) data;
return represent(
ImmutableMap.builder()
.put("name", featureFlag.name())
.put("enabled", featureFlag.enabled())
.buildOrThrow());
}
}
}
Loading
Loading