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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,6 +61,7 @@ java_library(
":required_fields_checker",
"//:auto_value",
"//bundle:cel",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common:source",
Expand All@@ -69,6 +70,7 @@ java_library(
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
Expand Down
269 changes: 268 additions & 1 deletion bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
Expand All@@ -25,6 +26,10 @@
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.checker.CelStandardDeclarations;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardOverload;
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOptions;
import dev.cel.common.CelOverloadDecl;
Expand All@@ -41,6 +46,7 @@
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;
Expand DownExpand Up@@ -97,6 +103,9 @@ public abstract class CelEnvironment {
/** New function declarations to add in the compilation environment. */
public abstract ImmutableSet<FunctionDecl> functions();

/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -141,8 +150,28 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setFunctions(ImmutableSet<FunctionDecl> functions);

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

abstract CelEnvironment autoBuild();

@CheckReturnValue
public abstract CelEnvironment build();
public final CelEnvironment build() {
CelEnvironment env = autoBuild();
LibrarySubset librarySubset = env.standardLibrarySubset().orElse(null);
if (librarySubset != null) {
if (!librarySubset.includedMacros().isEmpty()
&& !librarySubset.excludedMacros().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude macros");
}
if (!librarySubset.includedFunctions().isEmpty()
&& !librarySubset.excludedFunctions().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude functions");
}
}
return env;
}
}

/** Creates a new builder to construct a {@link CelEnvironment} instance. */
Expand DownExpand Up@@ -180,6 +209,8 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

addAllCompilerExtensions(compilerBuilder, celOptions);

applyStandardLibrarySubset(compilerBuilder);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
Expand DownExpand Up@@ -218,6 +249,67 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
}
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
if (!standardLibrarySubset().isPresent()) {
return;
}

LibrarySubset librarySubset = standardLibrarySubset().get();
if (librarySubset.disabled()) {
compilerBuilder.setStandardEnvironmentEnabled(false);
return;
}

if (librarySubset.macrosDisabled()) {
compilerBuilder.setStandardMacros(ImmutableList.of());
} else if (!librarySubset.includedMacros().isEmpty()) {
compilerBuilder.setStandardMacros(
librarySubset.includedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet()));
} else if (!librarySubset.excludedMacros().isEmpty()) {
ImmutableSet<CelStandardMacro> set =
librarySubset.excludedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet());
compilerBuilder.setStandardMacros(
CelStandardMacro.STANDARD_MACROS.stream()
.filter(macro -> !set.contains(macro))
.collect(toImmutableSet()));
}

if (!librarySubset.includedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> includedFunctions = librarySubset.includedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
FunctionSelector.matchesAny(function, overload, includedFunctions))
.build());
} else if (!librarySubset.excludedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> excludedFunctions = librarySubset.excludedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
!FunctionSelector.matchesAny(function, overload, excludedFunctions))
.build());
}
}

private static CelStandardMacro getStandardMacroOrThrow(String macroName) {
for (CelStandardMacro macro : CelStandardMacro.STANDARD_MACROS) {
if (macro.getFunction().equals(macroName)) {
return macro;
}
}
throw new IllegalArgumentException("unrecognized standard macro `" + macroName + "'");
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
Expand DownExpand Up@@ -624,4 +716,179 @@ void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}

/**
* LibrarySubset indicates a subset of the macros and function supported by a subsettable
* library.
*/
@AutoValue
public abstract static class LibrarySubset {

/**
* Disabled indicates whether the library has been disabled, typically only used for
* default-enabled libraries like stdlib.
*/
public abstract boolean disabled();

/** DisableMacros disables macros for the given library. */
public abstract boolean macrosDisabled();

/** IncludeMacros specifies a set of macro function names to include in the subset. */
public abstract ImmutableSet<String> includedMacros();

/**
* ExcludeMacros specifies a set of macro function names to exclude from the subset.
*
* <p>Note: if IncludedMacros is non-empty, then ExcludedMacros is ignored.
*/
public abstract ImmutableSet<String> excludedMacros();

/**
* IncludeFunctions specifies a set of functions to include in the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
* <p>Note: if IncludedFunctions is non-empty, then ExcludedFunctions is ignored.
*/
public abstract ImmutableSet<FunctionSelector> includedFunctions();

/**
* ExcludeFunctions specifies the set of functions to exclude from the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
*/
public abstract ImmutableSet<FunctionSelector> excludedFunctions();

public static Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset.Builder()
.setMacrosDisabled(false)
.setIncludedMacros(ImmutableSet.of())
.setExcludedMacros(ImmutableSet.of())
.setIncludedFunctions(ImmutableSet.of())
.setExcludedFunctions(ImmutableSet.of());
}

/** Builder for {@link LibrarySubset}. */
@AutoValue.Builder
public abstract static class Builder {

public abstract Builder setDisabled(boolean disabled);

public abstract Builder setMacrosDisabled(boolean disabled);

public abstract Builder setIncludedMacros(ImmutableSet<String> includedMacros);

public abstract Builder setExcludedMacros(ImmutableSet<String> excludedMacros);

public abstract Builder setIncludedFunctions(
ImmutableSet<FunctionSelector> includedFunctions);

public abstract Builder setExcludedFunctions(
ImmutableSet<FunctionSelector> excludedFunctions);

@CheckReturnValue
public abstract LibrarySubset build();
}

/**
* Represents a function selector, which can be used to configure included/excluded library
* functions.
*/
@AutoValue
public abstract static class FunctionSelector {

public abstract String name();

public abstract ImmutableSet<OverloadSelector> overloads();

/** Builder for {@link FunctionSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> name();

public abstract Builder setName(String name);

public abstract Builder setOverloads(ImmutableSet<OverloadSelector> overloads);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("name", this::name));
}

/** Builds a new instance of {@link FunctionSelector}. */
public abstract FunctionSelector build();
}

/** Creates a new builder to construct a {@link FunctionSelector} instance. */
public static FunctionSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_FunctionSelector.Builder()
.setOverloads(ImmutableSet.of());
}

public static FunctionSelector create(String name, ImmutableSet<String> overloads) {
return newBuilder()
.setName(name)
.setOverloads(
overloads.stream()
.map(id -> OverloadSelector.newBuilder().setId(id).build())
.collect(toImmutableSet()))
.build();
}

private static boolean matchesAny(
StandardFunction function,
StandardOverload overload,
ImmutableSet<FunctionSelector> selectors) {
String functionName = function.functionDecl().name();
for (FunctionSelector functionSelector : selectors) {
if (!functionSelector.name().equals(functionName)) {
continue;
}

if (functionSelector.overloads().isEmpty()) {
return true;
}

String overloadId = overload.celOverloadDecl().overloadId();
for (OverloadSelector overloadSelector : functionSelector.overloads()) {
if (overloadSelector.id().equals(overloadId)) {
return true;
}
}
}
return false;
}
}

/** Represents an overload selector on a function selector. */
@AutoValue
public abstract static class OverloadSelector {

/** An overload ID. Required. Follows the same format as {@link OverloadDecl#id()} */
public abstract String id();

/** Builder for {@link OverloadSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> id();

public abstract Builder setId(String overloadId);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("id", this::id));
}

/** Builds a new instance of {@link OverloadSelector}. */
@CheckReturnValue
public abstract OverloadSelector build();
}

/** Creates a new builder to construct a {@link OverloadSelector} instance. */
public static OverloadSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_OverloadSelector.Builder();
}
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,6 +61,7 @@ java_library(
":required_fields_checker",
"//:auto_value",
"//bundle:cel",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common:source",
Expand All@@ -69,6 +70,7 @@ java_library(
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
Expand Down
269 changes: 268 additions & 1 deletion bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
Expand All@@ -25,6 +26,10 @@
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.checker.CelStandardDeclarations;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardOverload;
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOptions;
import dev.cel.common.CelOverloadDecl;
Expand All@@ -41,6 +46,7 @@
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;
Expand DownExpand Up@@ -97,6 +103,9 @@ public abstract class CelEnvironment {
/** New function declarations to add in the compilation environment. */
public abstract ImmutableSet<FunctionDecl> functions();

/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -141,8 +150,28 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setFunctions(ImmutableSet<FunctionDecl> functions);

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

abstract CelEnvironment autoBuild();

@CheckReturnValue
public abstract CelEnvironment build();
public final CelEnvironment build() {
CelEnvironment env = autoBuild();
LibrarySubset librarySubset = env.standardLibrarySubset().orElse(null);
if (librarySubset != null) {
if (!librarySubset.includedMacros().isEmpty()
&& !librarySubset.excludedMacros().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude macros");
}
if (!librarySubset.includedFunctions().isEmpty()
&& !librarySubset.excludedFunctions().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude functions");
}
}
return env;
}
}

/** Creates a new builder to construct a {@link CelEnvironment} instance. */
Expand DownExpand Up@@ -180,6 +209,8 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

addAllCompilerExtensions(compilerBuilder, celOptions);

applyStandardLibrarySubset(compilerBuilder);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
Expand DownExpand Up@@ -218,6 +249,67 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
}
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
if (!standardLibrarySubset().isPresent()) {
return;
}

LibrarySubset librarySubset = standardLibrarySubset().get();
if (librarySubset.disabled()) {
compilerBuilder.setStandardEnvironmentEnabled(false);
return;
}

if (librarySubset.macrosDisabled()) {
compilerBuilder.setStandardMacros(ImmutableList.of());
} else if (!librarySubset.includedMacros().isEmpty()) {
compilerBuilder.setStandardMacros(
librarySubset.includedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet()));
} else if (!librarySubset.excludedMacros().isEmpty()) {
ImmutableSet<CelStandardMacro> set =
librarySubset.excludedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet());
compilerBuilder.setStandardMacros(
CelStandardMacro.STANDARD_MACROS.stream()
.filter(macro -> !set.contains(macro))
.collect(toImmutableSet()));
}

if (!librarySubset.includedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> includedFunctions = librarySubset.includedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
FunctionSelector.matchesAny(function, overload, includedFunctions))
.build());
} else if (!librarySubset.excludedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> excludedFunctions = librarySubset.excludedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
!FunctionSelector.matchesAny(function, overload, excludedFunctions))
.build());
}
}

private static CelStandardMacro getStandardMacroOrThrow(String macroName) {
for (CelStandardMacro macro : CelStandardMacro.STANDARD_MACROS) {
if (macro.getFunction().equals(macroName)) {
return macro;
}
}
throw new IllegalArgumentException("unrecognized standard macro `" + macroName + "'");
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
Expand DownExpand Up@@ -624,4 +716,179 @@ void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}

/**
* LibrarySubset indicates a subset of the macros and function supported by a subsettable
* library.
*/
@AutoValue
public abstract static class LibrarySubset {

/**
* Disabled indicates whether the library has been disabled, typically only used for
* default-enabled libraries like stdlib.
*/
public abstract boolean disabled();

/** DisableMacros disables macros for the given library. */
public abstract boolean macrosDisabled();

/** IncludeMacros specifies a set of macro function names to include in the subset. */
public abstract ImmutableSet<String> includedMacros();

/**
* ExcludeMacros specifies a set of macro function names to exclude from the subset.
*
* <p>Note: if IncludedMacros is non-empty, then ExcludedMacros is ignored.
*/
public abstract ImmutableSet<String> excludedMacros();

/**
* IncludeFunctions specifies a set of functions to include in the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
* <p>Note: if IncludedFunctions is non-empty, then ExcludedFunctions is ignored.
*/
public abstract ImmutableSet<FunctionSelector> includedFunctions();

/**
* ExcludeFunctions specifies the set of functions to exclude from the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
*/
public abstract ImmutableSet<FunctionSelector> excludedFunctions();

public static Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset.Builder()
.setMacrosDisabled(false)
.setIncludedMacros(ImmutableSet.of())
.setExcludedMacros(ImmutableSet.of())
.setIncludedFunctions(ImmutableSet.of())
.setExcludedFunctions(ImmutableSet.of());
}

/** Builder for {@link LibrarySubset}. */
@AutoValue.Builder
public abstract static class Builder {

public abstract Builder setDisabled(boolean disabled);

public abstract Builder setMacrosDisabled(boolean disabled);

public abstract Builder setIncludedMacros(ImmutableSet<String> includedMacros);

public abstract Builder setExcludedMacros(ImmutableSet<String> excludedMacros);

public abstract Builder setIncludedFunctions(
ImmutableSet<FunctionSelector> includedFunctions);

public abstract Builder setExcludedFunctions(
ImmutableSet<FunctionSelector> excludedFunctions);

@CheckReturnValue
public abstract LibrarySubset build();
}

/**
* Represents a function selector, which can be used to configure included/excluded library
* functions.
*/
@AutoValue
public abstract static class FunctionSelector {

public abstract String name();

public abstract ImmutableSet<OverloadSelector> overloads();

/** Builder for {@link FunctionSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> name();

public abstract Builder setName(String name);

public abstract Builder setOverloads(ImmutableSet<OverloadSelector> overloads);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("name", this::name));
}

/** Builds a new instance of {@link FunctionSelector}. */
public abstract FunctionSelector build();
}

/** Creates a new builder to construct a {@link FunctionSelector} instance. */
public static FunctionSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_FunctionSelector.Builder()
.setOverloads(ImmutableSet.of());
}

public static FunctionSelector create(String name, ImmutableSet<String> overloads) {
return newBuilder()
.setName(name)
.setOverloads(
overloads.stream()
.map(id -> OverloadSelector.newBuilder().setId(id).build())
.collect(toImmutableSet()))
.build();
}

private static boolean matchesAny(
StandardFunction function,
StandardOverload overload,
ImmutableSet<FunctionSelector> selectors) {
String functionName = function.functionDecl().name();
for (FunctionSelector functionSelector : selectors) {
if (!functionSelector.name().equals(functionName)) {
continue;
}

if (functionSelector.overloads().isEmpty()) {
return true;
}

String overloadId = overload.celOverloadDecl().overloadId();
for (OverloadSelector overloadSelector : functionSelector.overloads()) {
if (overloadSelector.id().equals(overloadId)) {
return true;
}
}
}
return false;
}
}

/** Represents an overload selector on a function selector. */
@AutoValue
public abstract static class OverloadSelector {

/** An overload ID. Required. Follows the same format as {@link OverloadDecl#id()} */
public abstract String id();

/** Builder for {@link OverloadSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> id();

public abstract Builder setId(String overloadId);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("id", this::id));
}

/** Builds a new instance of {@link OverloadSelector}. */
@CheckReturnValue
public abstract OverloadSelector build();
}

/** Creates a new builder to construct a {@link OverloadSelector} instance. */
public static OverloadSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_OverloadSelector.Builder();
}
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,6 +61,7 @@ java_library(
":required_fields_checker",
"//:auto_value",
"//bundle:cel",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common:source",
Expand All@@ -69,6 +70,7 @@ java_library(
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
Expand Down
269 changes: 268 additions & 1 deletion bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
Expand All@@ -25,6 +26,10 @@
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.checker.CelStandardDeclarations;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardOverload;
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOptions;
import dev.cel.common.CelOverloadDecl;
Expand All@@ -41,6 +46,7 @@
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;
Expand DownExpand Up@@ -97,6 +103,9 @@ public abstract class CelEnvironment {
/** New function declarations to add in the compilation environment. */
public abstract ImmutableSet<FunctionDecl> functions();

/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -141,8 +150,28 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setFunctions(ImmutableSet<FunctionDecl> functions);

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

abstract CelEnvironment autoBuild();

@CheckReturnValue
public abstract CelEnvironment build();
public final CelEnvironment build() {
CelEnvironment env = autoBuild();
LibrarySubset librarySubset = env.standardLibrarySubset().orElse(null);
if (librarySubset != null) {
if (!librarySubset.includedMacros().isEmpty()
&& !librarySubset.excludedMacros().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude macros");
}
if (!librarySubset.includedFunctions().isEmpty()
&& !librarySubset.excludedFunctions().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude functions");
}
}
return env;
}
}

/** Creates a new builder to construct a {@link CelEnvironment} instance. */
Expand DownExpand Up@@ -180,6 +209,8 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

addAllCompilerExtensions(compilerBuilder, celOptions);

applyStandardLibrarySubset(compilerBuilder);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
Expand DownExpand Up@@ -218,6 +249,67 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
}
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
if (!standardLibrarySubset().isPresent()) {
return;
}

LibrarySubset librarySubset = standardLibrarySubset().get();
if (librarySubset.disabled()) {
compilerBuilder.setStandardEnvironmentEnabled(false);
return;
}

if (librarySubset.macrosDisabled()) {
compilerBuilder.setStandardMacros(ImmutableList.of());
} else if (!librarySubset.includedMacros().isEmpty()) {
compilerBuilder.setStandardMacros(
librarySubset.includedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet()));
} else if (!librarySubset.excludedMacros().isEmpty()) {
ImmutableSet<CelStandardMacro> set =
librarySubset.excludedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet());
compilerBuilder.setStandardMacros(
CelStandardMacro.STANDARD_MACROS.stream()
.filter(macro -> !set.contains(macro))
.collect(toImmutableSet()));
}

if (!librarySubset.includedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> includedFunctions = librarySubset.includedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
FunctionSelector.matchesAny(function, overload, includedFunctions))
.build());
} else if (!librarySubset.excludedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> excludedFunctions = librarySubset.excludedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
!FunctionSelector.matchesAny(function, overload, excludedFunctions))
.build());
}
}

private static CelStandardMacro getStandardMacroOrThrow(String macroName) {
for (CelStandardMacro macro : CelStandardMacro.STANDARD_MACROS) {
if (macro.getFunction().equals(macroName)) {
return macro;
}
}
throw new IllegalArgumentException("unrecognized standard macro `" + macroName + "'");
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
Expand DownExpand Up@@ -624,4 +716,179 @@ void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}

/**
* LibrarySubset indicates a subset of the macros and function supported by a subsettable
* library.
*/
@AutoValue
public abstract static class LibrarySubset {

/**
* Disabled indicates whether the library has been disabled, typically only used for
* default-enabled libraries like stdlib.
*/
public abstract boolean disabled();

/** DisableMacros disables macros for the given library. */
public abstract boolean macrosDisabled();

/** IncludeMacros specifies a set of macro function names to include in the subset. */
public abstract ImmutableSet<String> includedMacros();

/**
* ExcludeMacros specifies a set of macro function names to exclude from the subset.
*
* <p>Note: if IncludedMacros is non-empty, then ExcludedMacros is ignored.
*/
public abstract ImmutableSet<String> excludedMacros();

/**
* IncludeFunctions specifies a set of functions to include in the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
* <p>Note: if IncludedFunctions is non-empty, then ExcludedFunctions is ignored.
*/
public abstract ImmutableSet<FunctionSelector> includedFunctions();

/**
* ExcludeFunctions specifies the set of functions to exclude from the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
*/
public abstract ImmutableSet<FunctionSelector> excludedFunctions();

public static Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset.Builder()
.setMacrosDisabled(false)
.setIncludedMacros(ImmutableSet.of())
.setExcludedMacros(ImmutableSet.of())
.setIncludedFunctions(ImmutableSet.of())
.setExcludedFunctions(ImmutableSet.of());
}

/** Builder for {@link LibrarySubset}. */
@AutoValue.Builder
public abstract static class Builder {

public abstract Builder setDisabled(boolean disabled);

public abstract Builder setMacrosDisabled(boolean disabled);

public abstract Builder setIncludedMacros(ImmutableSet<String> includedMacros);

public abstract Builder setExcludedMacros(ImmutableSet<String> excludedMacros);

public abstract Builder setIncludedFunctions(
ImmutableSet<FunctionSelector> includedFunctions);

public abstract Builder setExcludedFunctions(
ImmutableSet<FunctionSelector> excludedFunctions);

@CheckReturnValue
public abstract LibrarySubset build();
}

/**
* Represents a function selector, which can be used to configure included/excluded library
* functions.
*/
@AutoValue
public abstract static class FunctionSelector {

public abstract String name();

public abstract ImmutableSet<OverloadSelector> overloads();

/** Builder for {@link FunctionSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> name();

public abstract Builder setName(String name);

public abstract Builder setOverloads(ImmutableSet<OverloadSelector> overloads);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("name", this::name));
}

/** Builds a new instance of {@link FunctionSelector}. */
public abstract FunctionSelector build();
}

/** Creates a new builder to construct a {@link FunctionSelector} instance. */
public static FunctionSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_FunctionSelector.Builder()
.setOverloads(ImmutableSet.of());
}

public static FunctionSelector create(String name, ImmutableSet<String> overloads) {
return newBuilder()
.setName(name)
.setOverloads(
overloads.stream()
.map(id -> OverloadSelector.newBuilder().setId(id).build())
.collect(toImmutableSet()))
.build();
}

private static boolean matchesAny(
StandardFunction function,
StandardOverload overload,
ImmutableSet<FunctionSelector> selectors) {
String functionName = function.functionDecl().name();
for (FunctionSelector functionSelector : selectors) {
if (!functionSelector.name().equals(functionName)) {
continue;
}

if (functionSelector.overloads().isEmpty()) {
return true;
}

String overloadId = overload.celOverloadDecl().overloadId();
for (OverloadSelector overloadSelector : functionSelector.overloads()) {
if (overloadSelector.id().equals(overloadId)) {
return true;
}
}
}
return false;
}
}

/** Represents an overload selector on a function selector. */
@AutoValue
public abstract static class OverloadSelector {

/** An overload ID. Required. Follows the same format as {@link OverloadDecl#id()} */
public abstract String id();

/** Builder for {@link OverloadSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> id();

public abstract Builder setId(String overloadId);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("id", this::id));
}

/** Builds a new instance of {@link OverloadSelector}. */
@CheckReturnValue
public abstract OverloadSelector build();
}

/** Creates a new builder to construct a {@link OverloadSelector} instance. */
public static OverloadSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_OverloadSelector.Builder();
}
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,6 +61,7 @@ java_library(
":required_fields_checker",
"//:auto_value",
"//bundle:cel",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common:source",
Expand All@@ -69,6 +70,7 @@ java_library(
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
Expand Down
269 changes: 268 additions & 1 deletion bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
Expand All@@ -25,6 +26,10 @@
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.checker.CelStandardDeclarations;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardOverload;
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOptions;
import dev.cel.common.CelOverloadDecl;
Expand All@@ -41,6 +46,7 @@
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;
Expand DownExpand Up@@ -97,6 +103,9 @@ public abstract class CelEnvironment {
/** New function declarations to add in the compilation environment. */
public abstract ImmutableSet<FunctionDecl> functions();

/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -141,8 +150,28 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setFunctions(ImmutableSet<FunctionDecl> functions);

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

abstract CelEnvironment autoBuild();

@CheckReturnValue
public abstract CelEnvironment build();
public final CelEnvironment build() {
CelEnvironment env = autoBuild();
LibrarySubset librarySubset = env.standardLibrarySubset().orElse(null);
if (librarySubset != null) {
if (!librarySubset.includedMacros().isEmpty()
&& !librarySubset.excludedMacros().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude macros");
}
if (!librarySubset.includedFunctions().isEmpty()
&& !librarySubset.excludedFunctions().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude functions");
}
}
return env;
}
}

/** Creates a new builder to construct a {@link CelEnvironment} instance. */
Expand DownExpand Up@@ -180,6 +209,8 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

addAllCompilerExtensions(compilerBuilder, celOptions);

applyStandardLibrarySubset(compilerBuilder);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
Expand DownExpand Up@@ -218,6 +249,67 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
}
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
if (!standardLibrarySubset().isPresent()) {
return;
}

LibrarySubset librarySubset = standardLibrarySubset().get();
if (librarySubset.disabled()) {
compilerBuilder.setStandardEnvironmentEnabled(false);
return;
}

if (librarySubset.macrosDisabled()) {
compilerBuilder.setStandardMacros(ImmutableList.of());
} else if (!librarySubset.includedMacros().isEmpty()) {
compilerBuilder.setStandardMacros(
librarySubset.includedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet()));
} else if (!librarySubset.excludedMacros().isEmpty()) {
ImmutableSet<CelStandardMacro> set =
librarySubset.excludedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet());
compilerBuilder.setStandardMacros(
CelStandardMacro.STANDARD_MACROS.stream()
.filter(macro -> !set.contains(macro))
.collect(toImmutableSet()));
}

if (!librarySubset.includedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> includedFunctions = librarySubset.includedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
FunctionSelector.matchesAny(function, overload, includedFunctions))
.build());
} else if (!librarySubset.excludedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> excludedFunctions = librarySubset.excludedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
!FunctionSelector.matchesAny(function, overload, excludedFunctions))
.build());
}
}

private static CelStandardMacro getStandardMacroOrThrow(String macroName) {
for (CelStandardMacro macro : CelStandardMacro.STANDARD_MACROS) {
if (macro.getFunction().equals(macroName)) {
return macro;
}
}
throw new IllegalArgumentException("unrecognized standard macro `" + macroName + "'");
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
Expand DownExpand Up@@ -624,4 +716,179 @@ void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}

/**
* LibrarySubset indicates a subset of the macros and function supported by a subsettable
* library.
*/
@AutoValue
public abstract static class LibrarySubset {

/**
* Disabled indicates whether the library has been disabled, typically only used for
* default-enabled libraries like stdlib.
*/
public abstract boolean disabled();

/** DisableMacros disables macros for the given library. */
public abstract boolean macrosDisabled();

/** IncludeMacros specifies a set of macro function names to include in the subset. */
public abstract ImmutableSet<String> includedMacros();

/**
* ExcludeMacros specifies a set of macro function names to exclude from the subset.
*
* <p>Note: if IncludedMacros is non-empty, then ExcludedMacros is ignored.
*/
public abstract ImmutableSet<String> excludedMacros();

/**
* IncludeFunctions specifies a set of functions to include in the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
* <p>Note: if IncludedFunctions is non-empty, then ExcludedFunctions is ignored.
*/
public abstract ImmutableSet<FunctionSelector> includedFunctions();

/**
* ExcludeFunctions specifies the set of functions to exclude from the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
*/
public abstract ImmutableSet<FunctionSelector> excludedFunctions();

public static Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset.Builder()
.setMacrosDisabled(false)
.setIncludedMacros(ImmutableSet.of())
.setExcludedMacros(ImmutableSet.of())
.setIncludedFunctions(ImmutableSet.of())
.setExcludedFunctions(ImmutableSet.of());
}

/** Builder for {@link LibrarySubset}. */
@AutoValue.Builder
public abstract static class Builder {

public abstract Builder setDisabled(boolean disabled);

public abstract Builder setMacrosDisabled(boolean disabled);

public abstract Builder setIncludedMacros(ImmutableSet<String> includedMacros);

public abstract Builder setExcludedMacros(ImmutableSet<String> excludedMacros);

public abstract Builder setIncludedFunctions(
ImmutableSet<FunctionSelector> includedFunctions);

public abstract Builder setExcludedFunctions(
ImmutableSet<FunctionSelector> excludedFunctions);

@CheckReturnValue
public abstract LibrarySubset build();
}

/**
* Represents a function selector, which can be used to configure included/excluded library
* functions.
*/
@AutoValue
public abstract static class FunctionSelector {

public abstract String name();

public abstract ImmutableSet<OverloadSelector> overloads();

/** Builder for {@link FunctionSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> name();

public abstract Builder setName(String name);

public abstract Builder setOverloads(ImmutableSet<OverloadSelector> overloads);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("name", this::name));
}

/** Builds a new instance of {@link FunctionSelector}. */
public abstract FunctionSelector build();
}

/** Creates a new builder to construct a {@link FunctionSelector} instance. */
public static FunctionSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_FunctionSelector.Builder()
.setOverloads(ImmutableSet.of());
}

public static FunctionSelector create(String name, ImmutableSet<String> overloads) {
return newBuilder()
.setName(name)
.setOverloads(
overloads.stream()
.map(id -> OverloadSelector.newBuilder().setId(id).build())
.collect(toImmutableSet()))
.build();
}

private static boolean matchesAny(
StandardFunction function,
StandardOverload overload,
ImmutableSet<FunctionSelector> selectors) {
String functionName = function.functionDecl().name();
for (FunctionSelector functionSelector : selectors) {
if (!functionSelector.name().equals(functionName)) {
continue;
}

if (functionSelector.overloads().isEmpty()) {
return true;
}

String overloadId = overload.celOverloadDecl().overloadId();
for (OverloadSelector overloadSelector : functionSelector.overloads()) {
if (overloadSelector.id().equals(overloadId)) {
return true;
}
}
}
return false;
}
}

/** Represents an overload selector on a function selector. */
@AutoValue
public abstract static class OverloadSelector {

/** An overload ID. Required. Follows the same format as {@link OverloadDecl#id()} */
public abstract String id();

/** Builder for {@link OverloadSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> id();

public abstract Builder setId(String overloadId);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("id", this::id));
}

/** Builds a new instance of {@link OverloadSelector}. */
@CheckReturnValue
public abstract OverloadSelector build();
}

/** Creates a new builder to construct a {@link OverloadSelector} instance. */
public static OverloadSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_OverloadSelector.Builder();
}
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,6 +61,7 @@ java_library(
":required_fields_checker",
"//:auto_value",
"//bundle:cel",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common:source",
Expand All@@ -69,6 +70,7 @@ java_library(
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
Expand Down
269 changes: 268 additions & 1 deletion bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
Expand All@@ -25,6 +26,10 @@
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.checker.CelStandardDeclarations;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardOverload;
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOptions;
import dev.cel.common.CelOverloadDecl;
Expand All@@ -41,6 +46,7 @@
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;
Expand DownExpand Up@@ -97,6 +103,9 @@ public abstract class CelEnvironment {
/** New function declarations to add in the compilation environment. */
public abstract ImmutableSet<FunctionDecl> functions();

/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -141,8 +150,28 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setFunctions(ImmutableSet<FunctionDecl> functions);

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

abstract CelEnvironment autoBuild();

@CheckReturnValue
public abstract CelEnvironment build();
public final CelEnvironment build() {
CelEnvironment env = autoBuild();
LibrarySubset librarySubset = env.standardLibrarySubset().orElse(null);
if (librarySubset != null) {
if (!librarySubset.includedMacros().isEmpty()
&& !librarySubset.excludedMacros().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude macros");
}
if (!librarySubset.includedFunctions().isEmpty()
&& !librarySubset.excludedFunctions().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude functions");
}
}
return env;
}
}

/** Creates a new builder to construct a {@link CelEnvironment} instance. */
Expand DownExpand Up@@ -180,6 +209,8 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

addAllCompilerExtensions(compilerBuilder, celOptions);

applyStandardLibrarySubset(compilerBuilder);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
Expand DownExpand Up@@ -218,6 +249,67 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
}
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
if (!standardLibrarySubset().isPresent()) {
return;
}

LibrarySubset librarySubset = standardLibrarySubset().get();
if (librarySubset.disabled()) {
compilerBuilder.setStandardEnvironmentEnabled(false);
return;
}

if (librarySubset.macrosDisabled()) {
compilerBuilder.setStandardMacros(ImmutableList.of());
} else if (!librarySubset.includedMacros().isEmpty()) {
compilerBuilder.setStandardMacros(
librarySubset.includedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet()));
} else if (!librarySubset.excludedMacros().isEmpty()) {
ImmutableSet<CelStandardMacro> set =
librarySubset.excludedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet());
compilerBuilder.setStandardMacros(
CelStandardMacro.STANDARD_MACROS.stream()
.filter(macro -> !set.contains(macro))
.collect(toImmutableSet()));
}

if (!librarySubset.includedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> includedFunctions = librarySubset.includedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
FunctionSelector.matchesAny(function, overload, includedFunctions))
.build());
} else if (!librarySubset.excludedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> excludedFunctions = librarySubset.excludedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
!FunctionSelector.matchesAny(function, overload, excludedFunctions))
.build());
}
}

private static CelStandardMacro getStandardMacroOrThrow(String macroName) {
for (CelStandardMacro macro : CelStandardMacro.STANDARD_MACROS) {
if (macro.getFunction().equals(macroName)) {
return macro;
}
}
throw new IllegalArgumentException("unrecognized standard macro `" + macroName + "'");
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
Expand DownExpand Up@@ -624,4 +716,179 @@ void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}

/**
* LibrarySubset indicates a subset of the macros and function supported by a subsettable
* library.
*/
@AutoValue
public abstract static class LibrarySubset {

/**
* Disabled indicates whether the library has been disabled, typically only used for
* default-enabled libraries like stdlib.
*/
public abstract boolean disabled();

/** DisableMacros disables macros for the given library. */
public abstract boolean macrosDisabled();

/** IncludeMacros specifies a set of macro function names to include in the subset. */
public abstract ImmutableSet<String> includedMacros();

/**
* ExcludeMacros specifies a set of macro function names to exclude from the subset.
*
* <p>Note: if IncludedMacros is non-empty, then ExcludedMacros is ignored.
*/
public abstract ImmutableSet<String> excludedMacros();

/**
* IncludeFunctions specifies a set of functions to include in the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
* <p>Note: if IncludedFunctions is non-empty, then ExcludedFunctions is ignored.
*/
public abstract ImmutableSet<FunctionSelector> includedFunctions();

/**
* ExcludeFunctions specifies the set of functions to exclude from the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
*/
public abstract ImmutableSet<FunctionSelector> excludedFunctions();

public static Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset.Builder()
.setMacrosDisabled(false)
.setIncludedMacros(ImmutableSet.of())
.setExcludedMacros(ImmutableSet.of())
.setIncludedFunctions(ImmutableSet.of())
.setExcludedFunctions(ImmutableSet.of());
}

/** Builder for {@link LibrarySubset}. */
@AutoValue.Builder
public abstract static class Builder {

public abstract Builder setDisabled(boolean disabled);

public abstract Builder setMacrosDisabled(boolean disabled);

public abstract Builder setIncludedMacros(ImmutableSet<String> includedMacros);

public abstract Builder setExcludedMacros(ImmutableSet<String> excludedMacros);

public abstract Builder setIncludedFunctions(
ImmutableSet<FunctionSelector> includedFunctions);

public abstract Builder setExcludedFunctions(
ImmutableSet<FunctionSelector> excludedFunctions);

@CheckReturnValue
public abstract LibrarySubset build();
}

/**
* Represents a function selector, which can be used to configure included/excluded library
* functions.
*/
@AutoValue
public abstract static class FunctionSelector {

public abstract String name();

public abstract ImmutableSet<OverloadSelector> overloads();

/** Builder for {@link FunctionSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> name();

public abstract Builder setName(String name);

public abstract Builder setOverloads(ImmutableSet<OverloadSelector> overloads);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("name", this::name));
}

/** Builds a new instance of {@link FunctionSelector}. */
public abstract FunctionSelector build();
}

/** Creates a new builder to construct a {@link FunctionSelector} instance. */
public static FunctionSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_FunctionSelector.Builder()
.setOverloads(ImmutableSet.of());
}

public static FunctionSelector create(String name, ImmutableSet<String> overloads) {
return newBuilder()
.setName(name)
.setOverloads(
overloads.stream()
.map(id -> OverloadSelector.newBuilder().setId(id).build())
.collect(toImmutableSet()))
.build();
}

private static boolean matchesAny(
StandardFunction function,
StandardOverload overload,
ImmutableSet<FunctionSelector> selectors) {
String functionName = function.functionDecl().name();
for (FunctionSelector functionSelector : selectors) {
if (!functionSelector.name().equals(functionName)) {
continue;
}

if (functionSelector.overloads().isEmpty()) {
return true;
}

String overloadId = overload.celOverloadDecl().overloadId();
for (OverloadSelector overloadSelector : functionSelector.overloads()) {
if (overloadSelector.id().equals(overloadId)) {
return true;
}
}
}
return false;
}
}

/** Represents an overload selector on a function selector. */
@AutoValue
public abstract static class OverloadSelector {

/** An overload ID. Required. Follows the same format as {@link OverloadDecl#id()} */
public abstract String id();

/** Builder for {@link OverloadSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> id();

public abstract Builder setId(String overloadId);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("id", this::id));
}

/** Builds a new instance of {@link OverloadSelector}. */
@CheckReturnValue
public abstract OverloadSelector build();
}

/** Creates a new builder to construct a {@link OverloadSelector} instance. */
public static OverloadSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_OverloadSelector.Builder();
}
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,6 +61,7 @@ java_library(
":required_fields_checker",
"//:auto_value",
"//bundle:cel",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common:source",
Expand All@@ -69,6 +70,7 @@ java_library(
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
Expand Down
269 changes: 268 additions & 1 deletion bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
Expand All@@ -25,6 +26,10 @@
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.checker.CelStandardDeclarations;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardOverload;
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOptions;
import dev.cel.common.CelOverloadDecl;
Expand All@@ -41,6 +46,7 @@
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;
Expand DownExpand Up@@ -97,6 +103,9 @@ public abstract class CelEnvironment {
/** New function declarations to add in the compilation environment. */
public abstract ImmutableSet<FunctionDecl> functions();

/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -141,8 +150,28 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setFunctions(ImmutableSet<FunctionDecl> functions);

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

abstract CelEnvironment autoBuild();

@CheckReturnValue
public abstract CelEnvironment build();
public final CelEnvironment build() {
CelEnvironment env = autoBuild();
LibrarySubset librarySubset = env.standardLibrarySubset().orElse(null);
if (librarySubset != null) {
if (!librarySubset.includedMacros().isEmpty()
&& !librarySubset.excludedMacros().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude macros");
}
if (!librarySubset.includedFunctions().isEmpty()
&& !librarySubset.excludedFunctions().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude functions");
}
}
return env;
}
}

/** Creates a new builder to construct a {@link CelEnvironment} instance. */
Expand DownExpand Up@@ -180,6 +209,8 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

addAllCompilerExtensions(compilerBuilder, celOptions);

applyStandardLibrarySubset(compilerBuilder);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
Expand DownExpand Up@@ -218,6 +249,67 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
}
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
if (!standardLibrarySubset().isPresent()) {
return;
}

LibrarySubset librarySubset = standardLibrarySubset().get();
if (librarySubset.disabled()) {
compilerBuilder.setStandardEnvironmentEnabled(false);
return;
}

if (librarySubset.macrosDisabled()) {
compilerBuilder.setStandardMacros(ImmutableList.of());
} else if (!librarySubset.includedMacros().isEmpty()) {
compilerBuilder.setStandardMacros(
librarySubset.includedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet()));
} else if (!librarySubset.excludedMacros().isEmpty()) {
ImmutableSet<CelStandardMacro> set =
librarySubset.excludedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet());
compilerBuilder.setStandardMacros(
CelStandardMacro.STANDARD_MACROS.stream()
.filter(macro -> !set.contains(macro))
.collect(toImmutableSet()));
}

if (!librarySubset.includedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> includedFunctions = librarySubset.includedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
FunctionSelector.matchesAny(function, overload, includedFunctions))
.build());
} else if (!librarySubset.excludedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> excludedFunctions = librarySubset.excludedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
!FunctionSelector.matchesAny(function, overload, excludedFunctions))
.build());
}
}

private static CelStandardMacro getStandardMacroOrThrow(String macroName) {
for (CelStandardMacro macro : CelStandardMacro.STANDARD_MACROS) {
if (macro.getFunction().equals(macroName)) {
return macro;
}
}
throw new IllegalArgumentException("unrecognized standard macro `" + macroName + "'");
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
Expand DownExpand Up@@ -624,4 +716,179 @@ void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}

/**
* LibrarySubset indicates a subset of the macros and function supported by a subsettable
* library.
*/
@AutoValue
public abstract static class LibrarySubset {

/**
* Disabled indicates whether the library has been disabled, typically only used for
* default-enabled libraries like stdlib.
*/
public abstract boolean disabled();

/** DisableMacros disables macros for the given library. */
public abstract boolean macrosDisabled();

/** IncludeMacros specifies a set of macro function names to include in the subset. */
public abstract ImmutableSet<String> includedMacros();

/**
* ExcludeMacros specifies a set of macro function names to exclude from the subset.
*
* <p>Note: if IncludedMacros is non-empty, then ExcludedMacros is ignored.
*/
public abstract ImmutableSet<String> excludedMacros();

/**
* IncludeFunctions specifies a set of functions to include in the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
* <p>Note: if IncludedFunctions is non-empty, then ExcludedFunctions is ignored.
*/
public abstract ImmutableSet<FunctionSelector> includedFunctions();

/**
* ExcludeFunctions specifies the set of functions to exclude from the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
*/
public abstract ImmutableSet<FunctionSelector> excludedFunctions();

public static Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset.Builder()
.setMacrosDisabled(false)
.setIncludedMacros(ImmutableSet.of())
.setExcludedMacros(ImmutableSet.of())
.setIncludedFunctions(ImmutableSet.of())
.setExcludedFunctions(ImmutableSet.of());
}

/** Builder for {@link LibrarySubset}. */
@AutoValue.Builder
public abstract static class Builder {

public abstract Builder setDisabled(boolean disabled);

public abstract Builder setMacrosDisabled(boolean disabled);

public abstract Builder setIncludedMacros(ImmutableSet<String> includedMacros);

public abstract Builder setExcludedMacros(ImmutableSet<String> excludedMacros);

public abstract Builder setIncludedFunctions(
ImmutableSet<FunctionSelector> includedFunctions);

public abstract Builder setExcludedFunctions(
ImmutableSet<FunctionSelector> excludedFunctions);

@CheckReturnValue
public abstract LibrarySubset build();
}

/**
* Represents a function selector, which can be used to configure included/excluded library
* functions.
*/
@AutoValue
public abstract static class FunctionSelector {

public abstract String name();

public abstract ImmutableSet<OverloadSelector> overloads();

/** Builder for {@link FunctionSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> name();

public abstract Builder setName(String name);

public abstract Builder setOverloads(ImmutableSet<OverloadSelector> overloads);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("name", this::name));
}

/** Builds a new instance of {@link FunctionSelector}. */
public abstract FunctionSelector build();
}

/** Creates a new builder to construct a {@link FunctionSelector} instance. */
public static FunctionSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_FunctionSelector.Builder()
.setOverloads(ImmutableSet.of());
}

public static FunctionSelector create(String name, ImmutableSet<String> overloads) {
return newBuilder()
.setName(name)
.setOverloads(
overloads.stream()
.map(id -> OverloadSelector.newBuilder().setId(id).build())
.collect(toImmutableSet()))
.build();
}

private static boolean matchesAny(
StandardFunction function,
StandardOverload overload,
ImmutableSet<FunctionSelector> selectors) {
String functionName = function.functionDecl().name();
for (FunctionSelector functionSelector : selectors) {
if (!functionSelector.name().equals(functionName)) {
continue;
}

if (functionSelector.overloads().isEmpty()) {
return true;
}

String overloadId = overload.celOverloadDecl().overloadId();
for (OverloadSelector overloadSelector : functionSelector.overloads()) {
if (overloadSelector.id().equals(overloadId)) {
return true;
}
}
}
return false;
}
}

/** Represents an overload selector on a function selector. */
@AutoValue
public abstract static class OverloadSelector {

/** An overload ID. Required. Follows the same format as {@link OverloadDecl#id()} */
public abstract String id();

/** Builder for {@link OverloadSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> id();

public abstract Builder setId(String overloadId);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("id", this::id));
}

/** Builds a new instance of {@link OverloadSelector}. */
@CheckReturnValue
public abstract OverloadSelector build();
}

/** Creates a new builder to construct a {@link OverloadSelector} instance. */
public static OverloadSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_OverloadSelector.Builder();
}
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,6 +61,7 @@ java_library(
":required_fields_checker",
"//:auto_value",
"//bundle:cel",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common:source",
Expand All@@ -69,6 +70,7 @@ java_library(
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
Expand Down
269 changes: 268 additions & 1 deletion bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
Expand All@@ -25,6 +26,10 @@
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.checker.CelStandardDeclarations;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardOverload;
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOptions;
import dev.cel.common.CelOverloadDecl;
Expand All@@ -41,6 +46,7 @@
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;
Expand DownExpand Up@@ -97,6 +103,9 @@ public abstract class CelEnvironment {
/** New function declarations to add in the compilation environment. */
public abstract ImmutableSet<FunctionDecl> functions();

/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -141,8 +150,28 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setFunctions(ImmutableSet<FunctionDecl> functions);

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

abstract CelEnvironment autoBuild();

@CheckReturnValue
public abstract CelEnvironment build();
public final CelEnvironment build() {
CelEnvironment env = autoBuild();
LibrarySubset librarySubset = env.standardLibrarySubset().orElse(null);
if (librarySubset != null) {
if (!librarySubset.includedMacros().isEmpty()
&& !librarySubset.excludedMacros().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude macros");
}
if (!librarySubset.includedFunctions().isEmpty()
&& !librarySubset.excludedFunctions().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude functions");
}
}
return env;
}
}

/** Creates a new builder to construct a {@link CelEnvironment} instance. */
Expand DownExpand Up@@ -180,6 +209,8 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

addAllCompilerExtensions(compilerBuilder, celOptions);

applyStandardLibrarySubset(compilerBuilder);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
Expand DownExpand Up@@ -218,6 +249,67 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
}
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
if (!standardLibrarySubset().isPresent()) {
return;
}

LibrarySubset librarySubset = standardLibrarySubset().get();
if (librarySubset.disabled()) {
compilerBuilder.setStandardEnvironmentEnabled(false);
return;
}

if (librarySubset.macrosDisabled()) {
compilerBuilder.setStandardMacros(ImmutableList.of());
} else if (!librarySubset.includedMacros().isEmpty()) {
compilerBuilder.setStandardMacros(
librarySubset.includedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet()));
} else if (!librarySubset.excludedMacros().isEmpty()) {
ImmutableSet<CelStandardMacro> set =
librarySubset.excludedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet());
compilerBuilder.setStandardMacros(
CelStandardMacro.STANDARD_MACROS.stream()
.filter(macro -> !set.contains(macro))
.collect(toImmutableSet()));
}

if (!librarySubset.includedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> includedFunctions = librarySubset.includedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
FunctionSelector.matchesAny(function, overload, includedFunctions))
.build());
} else if (!librarySubset.excludedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> excludedFunctions = librarySubset.excludedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
!FunctionSelector.matchesAny(function, overload, excludedFunctions))
.build());
}
}

private static CelStandardMacro getStandardMacroOrThrow(String macroName) {
for (CelStandardMacro macro : CelStandardMacro.STANDARD_MACROS) {
if (macro.getFunction().equals(macroName)) {
return macro;
}
}
throw new IllegalArgumentException("unrecognized standard macro `" + macroName + "'");
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
Expand DownExpand Up@@ -624,4 +716,179 @@ void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}

/**
* LibrarySubset indicates a subset of the macros and function supported by a subsettable
* library.
*/
@AutoValue
public abstract static class LibrarySubset {

/**
* Disabled indicates whether the library has been disabled, typically only used for
* default-enabled libraries like stdlib.
*/
public abstract boolean disabled();

/** DisableMacros disables macros for the given library. */
public abstract boolean macrosDisabled();

/** IncludeMacros specifies a set of macro function names to include in the subset. */
public abstract ImmutableSet<String> includedMacros();

/**
* ExcludeMacros specifies a set of macro function names to exclude from the subset.
*
* <p>Note: if IncludedMacros is non-empty, then ExcludedMacros is ignored.
*/
public abstract ImmutableSet<String> excludedMacros();

/**
* IncludeFunctions specifies a set of functions to include in the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
* <p>Note: if IncludedFunctions is non-empty, then ExcludedFunctions is ignored.
*/
public abstract ImmutableSet<FunctionSelector> includedFunctions();

/**
* ExcludeFunctions specifies the set of functions to exclude from the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
*/
public abstract ImmutableSet<FunctionSelector> excludedFunctions();

public static Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset.Builder()
.setMacrosDisabled(false)
.setIncludedMacros(ImmutableSet.of())
.setExcludedMacros(ImmutableSet.of())
.setIncludedFunctions(ImmutableSet.of())
.setExcludedFunctions(ImmutableSet.of());
}

/** Builder for {@link LibrarySubset}. */
@AutoValue.Builder
public abstract static class Builder {

public abstract Builder setDisabled(boolean disabled);

public abstract Builder setMacrosDisabled(boolean disabled);

public abstract Builder setIncludedMacros(ImmutableSet<String> includedMacros);

public abstract Builder setExcludedMacros(ImmutableSet<String> excludedMacros);

public abstract Builder setIncludedFunctions(
ImmutableSet<FunctionSelector> includedFunctions);

public abstract Builder setExcludedFunctions(
ImmutableSet<FunctionSelector> excludedFunctions);

@CheckReturnValue
public abstract LibrarySubset build();
}

/**
* Represents a function selector, which can be used to configure included/excluded library
* functions.
*/
@AutoValue
public abstract static class FunctionSelector {

public abstract String name();

public abstract ImmutableSet<OverloadSelector> overloads();

/** Builder for {@link FunctionSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> name();

public abstract Builder setName(String name);

public abstract Builder setOverloads(ImmutableSet<OverloadSelector> overloads);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("name", this::name));
}

/** Builds a new instance of {@link FunctionSelector}. */
public abstract FunctionSelector build();
}

/** Creates a new builder to construct a {@link FunctionSelector} instance. */
public static FunctionSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_FunctionSelector.Builder()
.setOverloads(ImmutableSet.of());
}

public static FunctionSelector create(String name, ImmutableSet<String> overloads) {
return newBuilder()
.setName(name)
.setOverloads(
overloads.stream()
.map(id -> OverloadSelector.newBuilder().setId(id).build())
.collect(toImmutableSet()))
.build();
}

private static boolean matchesAny(
StandardFunction function,
StandardOverload overload,
ImmutableSet<FunctionSelector> selectors) {
String functionName = function.functionDecl().name();
for (FunctionSelector functionSelector : selectors) {
if (!functionSelector.name().equals(functionName)) {
continue;
}

if (functionSelector.overloads().isEmpty()) {
return true;
}

String overloadId = overload.celOverloadDecl().overloadId();
for (OverloadSelector overloadSelector : functionSelector.overloads()) {
if (overloadSelector.id().equals(overloadId)) {
return true;
}
}
}
return false;
}
}

/** Represents an overload selector on a function selector. */
@AutoValue
public abstract static class OverloadSelector {

/** An overload ID. Required. Follows the same format as {@link OverloadDecl#id()} */
public abstract String id();

/** Builder for {@link OverloadSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> id();

public abstract Builder setId(String overloadId);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("id", this::id));
}

/** Builds a new instance of {@link OverloadSelector}. */
@CheckReturnValue
public abstract OverloadSelector build();
}

/** Creates a new builder to construct a {@link OverloadSelector} instance. */
public static OverloadSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_OverloadSelector.Builder();
}
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bundle/src/main/java/dev/cel/bundle/BUILD.bazel
Original file line numberDiff line numberDiff line change
Expand Up@@ -61,6 +61,7 @@ java_library(
":required_fields_checker",
"//:auto_value",
"//bundle:cel",
"//checker:standard_decl",
"//common:compiler_common",
"//common:options",
"//common:source",
Expand All@@ -69,6 +70,7 @@ java_library(
"//compiler:compiler_builder",
"//extensions",
"//extensions:optional_library",
"//parser:macro",
"//runtime",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
Expand Down
269 changes: 268 additions & 1 deletion bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;

import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
Expand All@@ -25,6 +26,10 @@
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.checker.CelStandardDeclarations;
import dev.cel.checker.CelStandardDeclarations.StandardFunction;
import dev.cel.checker.CelStandardDeclarations.StandardOverload;
import dev.cel.common.CelFunctionDecl;
import dev.cel.common.CelOptions;
import dev.cel.common.CelOverloadDecl;
Expand All@@ -41,6 +46,7 @@
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import java.util.Arrays;
import java.util.Optional;
Expand DownExpand Up@@ -97,6 +103,9 @@ public abstract class CelEnvironment {
/** New function declarations to add in the compilation environment. */
public abstract ImmutableSet<FunctionDecl> functions();

/** Standard library subset (which macros, functions to include/exclude) */
public abstract Optional<LibrarySubset> standardLibrarySubset();

/** Builder for {@link CelEnvironment}. */
@AutoValue.Builder
public abstract static class Builder {
Expand DownExpand Up@@ -141,8 +150,28 @@ public Builder setFunctions(FunctionDecl... functions) {

public abstract Builder setFunctions(ImmutableSet<FunctionDecl> functions);

public abstract Builder setStandardLibrarySubset(LibrarySubset stdLibrarySubset);

abstract CelEnvironment autoBuild();

@CheckReturnValue
public abstract CelEnvironment build();
public final CelEnvironment build() {
CelEnvironment env = autoBuild();
LibrarySubset librarySubset = env.standardLibrarySubset().orElse(null);
if (librarySubset != null) {
if (!librarySubset.includedMacros().isEmpty()
&& !librarySubset.excludedMacros().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude macros");
}
if (!librarySubset.includedFunctions().isEmpty()
&& !librarySubset.excludedFunctions().isEmpty()) {
throw new IllegalArgumentException(
"Invalid subset: cannot both include and exclude functions");
}
}
return env;
}
}

/** Creates a new builder to construct a {@link CelEnvironment} instance. */
Expand DownExpand Up@@ -180,6 +209,8 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)

addAllCompilerExtensions(compilerBuilder, celOptions);

applyStandardLibrarySubset(compilerBuilder);

return compilerBuilder.build();
} catch (RuntimeException e) {
throw new CelEnvironmentException(e.getMessage(), e);
Expand DownExpand Up@@ -218,6 +249,67 @@ private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOpt
}
}

private void applyStandardLibrarySubset(CelCompilerBuilder compilerBuilder) {
if (!standardLibrarySubset().isPresent()) {
return;
}

LibrarySubset librarySubset = standardLibrarySubset().get();
if (librarySubset.disabled()) {
compilerBuilder.setStandardEnvironmentEnabled(false);
return;
}

if (librarySubset.macrosDisabled()) {
compilerBuilder.setStandardMacros(ImmutableList.of());
} else if (!librarySubset.includedMacros().isEmpty()) {
compilerBuilder.setStandardMacros(
librarySubset.includedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet()));
} else if (!librarySubset.excludedMacros().isEmpty()) {
ImmutableSet<CelStandardMacro> set =
librarySubset.excludedMacros().stream()
.map(CelEnvironment::getStandardMacroOrThrow)
.collect(toImmutableSet());
compilerBuilder.setStandardMacros(
CelStandardMacro.STANDARD_MACROS.stream()
.filter(macro -> !set.contains(macro))
.collect(toImmutableSet()));
}

if (!librarySubset.includedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> includedFunctions = librarySubset.includedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
FunctionSelector.matchesAny(function, overload, includedFunctions))
.build());
} else if (!librarySubset.excludedFunctions().isEmpty()) {
ImmutableSet<FunctionSelector> excludedFunctions = librarySubset.excludedFunctions();
compilerBuilder
.setStandardEnvironmentEnabled(false)
.setStandardDeclarations(
CelStandardDeclarations.newBuilder()
.filterFunctions(
(function, overload) ->
!FunctionSelector.matchesAny(function, overload, excludedFunctions))
.build());
}
}

private static CelStandardMacro getStandardMacroOrThrow(String macroName) {
for (CelStandardMacro macro : CelStandardMacro.STANDARD_MACROS) {
if (macro.getFunction().equals(macroName)) {
return macro;
}
}
throw new IllegalArgumentException("unrecognized standard macro `" + macroName + "'");
}

private static CanonicalCelExtension getExtensionOrThrow(String extensionName) {
CanonicalCelExtension extension = CEL_EXTENSION_CONFIG_MAP.get(extensionName);
if (extension == null) {
Expand DownExpand Up@@ -624,4 +716,179 @@ void addRuntimeExtension(CelRuntimeBuilder runtimeBuilder, CelOptions options) {
this.runtimeExtensionApplier = runtimeExtensionApplier;
}
}

/**
* LibrarySubset indicates a subset of the macros and function supported by a subsettable
* library.
*/
@AutoValue
public abstract static class LibrarySubset {

/**
* Disabled indicates whether the library has been disabled, typically only used for
* default-enabled libraries like stdlib.
*/
public abstract boolean disabled();

/** DisableMacros disables macros for the given library. */
public abstract boolean macrosDisabled();

/** IncludeMacros specifies a set of macro function names to include in the subset. */
public abstract ImmutableSet<String> includedMacros();

/**
* ExcludeMacros specifies a set of macro function names to exclude from the subset.
*
* <p>Note: if IncludedMacros is non-empty, then ExcludedMacros is ignored.
*/
public abstract ImmutableSet<String> excludedMacros();

/**
* IncludeFunctions specifies a set of functions to include in the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
* <p>Note: if IncludedFunctions is non-empty, then ExcludedFunctions is ignored.
*/
public abstract ImmutableSet<FunctionSelector> includedFunctions();

/**
* ExcludeFunctions specifies the set of functions to exclude from the subset.
*
* <p>Note: the overloads specified in the subset need only specify their ID.
*/
public abstract ImmutableSet<FunctionSelector> excludedFunctions();

public static Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset.Builder()
.setMacrosDisabled(false)
.setIncludedMacros(ImmutableSet.of())
.setExcludedMacros(ImmutableSet.of())
.setIncludedFunctions(ImmutableSet.of())
.setExcludedFunctions(ImmutableSet.of());
}

/** Builder for {@link LibrarySubset}. */
@AutoValue.Builder
public abstract static class Builder {

public abstract Builder setDisabled(boolean disabled);

public abstract Builder setMacrosDisabled(boolean disabled);

public abstract Builder setIncludedMacros(ImmutableSet<String> includedMacros);

public abstract Builder setExcludedMacros(ImmutableSet<String> excludedMacros);

public abstract Builder setIncludedFunctions(
ImmutableSet<FunctionSelector> includedFunctions);

public abstract Builder setExcludedFunctions(
ImmutableSet<FunctionSelector> excludedFunctions);

@CheckReturnValue
public abstract LibrarySubset build();
}

/**
* Represents a function selector, which can be used to configure included/excluded library
* functions.
*/
@AutoValue
public abstract static class FunctionSelector {

public abstract String name();

public abstract ImmutableSet<OverloadSelector> overloads();

/** Builder for {@link FunctionSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> name();

public abstract Builder setName(String name);

public abstract Builder setOverloads(ImmutableSet<OverloadSelector> overloads);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("name", this::name));
}

/** Builds a new instance of {@link FunctionSelector}. */
public abstract FunctionSelector build();
}

/** Creates a new builder to construct a {@link FunctionSelector} instance. */
public static FunctionSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_FunctionSelector.Builder()
.setOverloads(ImmutableSet.of());
}

public static FunctionSelector create(String name, ImmutableSet<String> overloads) {
return newBuilder()
.setName(name)
.setOverloads(
overloads.stream()
.map(id -> OverloadSelector.newBuilder().setId(id).build())
.collect(toImmutableSet()))
.build();
}

private static boolean matchesAny(
StandardFunction function,
StandardOverload overload,
ImmutableSet<FunctionSelector> selectors) {
String functionName = function.functionDecl().name();
for (FunctionSelector functionSelector : selectors) {
if (!functionSelector.name().equals(functionName)) {
continue;
}

if (functionSelector.overloads().isEmpty()) {
return true;
}

String overloadId = overload.celOverloadDecl().overloadId();
for (OverloadSelector overloadSelector : functionSelector.overloads()) {
if (overloadSelector.id().equals(overloadId)) {
return true;
}
}
}
return false;
}
}

/** Represents an overload selector on a function selector. */
@AutoValue
public abstract static class OverloadSelector {

/** An overload ID. Required. Follows the same format as {@link OverloadDecl#id()} */
public abstract String id();

/** Builder for {@link OverloadSelector}. */
@AutoValue.Builder
public abstract static class Builder implements RequiredFieldsChecker {

public abstract Optional<String> id();

public abstract Builder setId(String overloadId);

@Override
public ImmutableList<RequiredField> requiredFields() {
return ImmutableList.of(RequiredField.of("id", this::id));
}

/** Builds a new instance of {@link OverloadSelector}. */
@CheckReturnValue
public abstract OverloadSelector build();
}

/** Creates a new builder to construct a {@link OverloadSelector} instance. */
public static OverloadSelector.Builder newBuilder() {
return new AutoValue_CelEnvironment_LibrarySubset_OverloadSelector.Builder();
}
}
}
}
Loading