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
82 changes: 48 additions & 34 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,10 +44,12 @@
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
import java.util.Optional;

Expand DownExpand Up@@ -237,15 +239,23 @@ private void addAllCompilerExtensions(
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
if (extension.compilerExtensionProvider() != null) {
CelCompilerLibrary celCompilerLibrary = extension.compilerExtensionProvider()
.getCelCompilerLibrary(celOptions, extensionConfig.version());
celCompilerBuilder.addLibraries(celCompilerLibrary);
}
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
if (extension.runtimeExtensionProvider() != null) {
CelRuntimeLibrary celRuntimeLibrary = extension.runtimeExtensionProvider()
.getCelRuntimeLibrary(celOptions, extensionConfig.version());
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
}

Expand DownExpand Up@@ -656,64 +666,68 @@ public static ExtensionConfig of(String name) {
public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}

/** Create a new extension config with the specified name and the latest version. */
public static ExtensionConfig latest(String name) {
return of(name, Integer.MAX_VALUE);
}
}

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

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

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

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
interface CompilerExtensionProvider {
CelCompilerLibrary getCelCompilerLibrary(CelOptions options, int version);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
interface RuntimeExtensionProvider {
CelRuntimeLibrary getCelRuntimeLibrary(CelOptions options, int version);
}

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

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

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

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
CompilerExtensionProvider compilerExtensionProvider,
RuntimeExtensionProvider runtimeExtensionProvider) {
this.compilerExtensionProvider = compilerExtensionProvider;
this.runtimeExtensionProvider = runtimeExtensionProvider;
}
}

Expand Down
65 changes: 57 additions & 8 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import dev.cel.common.CelValidationException;
import dev.cel.common.CelValidationResult;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerFactory;
Expand DownExpand Up@@ -52,14 +53,14 @@ public void newBuilder_defaults() {
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
ExtensionConfig.latest("bindings"),
ExtensionConfig.latest("encoders"),
ExtensionConfig.latest("lists"),
ExtensionConfig.latest("math"),
ExtensionConfig.latest("optional"),
ExtensionConfig.latest("protos"),
ExtensionConfig.latest("sets"),
ExtensionConfig.latest("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(extensionConfigs).build();

Expand All@@ -76,6 +77,54 @@ public void extend_allExtensions() throws Exception {
assertThat(result).isTrue();
}

@Test
public void extensionVersion_specific() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", 1)).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast1 = cel.compile("math.abs(-4)").getAst();
assertThat(cel.createProgram(ast1).eval()).isEqualTo(4);

// Version 1 of the 'math' extension does not include sqrt
assertThat(
assertThrows(
CelValidationException.class,
() -> {
cel.compile("math.sqrt(4)").getAst();
}))
.hasMessageThat()
.contains("undeclared reference to 'sqrt'");
}

@Test
public void extensionVersion_latest() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder()
.addExtensions(ExtensionConfig.latest("math"))
.build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast = cel.compile("math.sqrt(4)").getAst();
double result = (double) cel.createProgram(ast).eval();
assertThat(result).isEqualTo(2.0);
}

@Test
public void extensionVersion_unsupportedVersion_throws() {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", -5)).build();

assertThat(
assertThrows(
CelEnvironmentException.class,
() -> {
environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
}))
.hasMessageThat()
.contains("Unsupported 'math' extension version -5");
}

@Test
public void stdlibSubset_bothIncludeExcludeSet_throws() {
assertThat(
Expand Down
13 changes: 11 additions & 2 deletions extensions/src/main/java/dev/cel/extensions/CelExtensions.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -103,13 +103,22 @@ public static CelProtoExtensions protos() {
*
* <p>This will include all functions denoted in {@link CelMathExtensions.Function}, including any
* future additions. To expose only a subset of these, use {@link #math(CelOptions,
* CelMathExtensions.Function...)} instead.
* CelMathExtensions.Function...)} or {@link #math(CelOptions,int)} instead.
*
* @param celOptions CelOptions to configure CelMathExtension with. This should be the same
* options object used to configure the compilation/runtime environments.
*/
public static CelMathExtensions math(CelOptions celOptions) {
return new CelMathExtensions(celOptions);
return new CelMathExtensions(celOptions, Integer.MAX_VALUE);
}

/**
* Returns the specified version of the 'math' extension.
*
* <p>Refer to README.md for functions available in each version.
*/
public static CelMathExtensions math(CelOptions celOptions, int version) {
return new CelMathExtensions(celOptions, version);
}

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,8 @@ final class CelMathExtensions implements CelCompilerLibrary, CelRuntimeLibrary {
private static final String MATH_BIT_LEFT_SHIFT_FUNCTION = "math.bitShiftLeft";
private static final String MATH_BIT_RIGHT_SHIFT_FUNCTION = "math.bitShiftRight";

private static final String MATH_SQRT_FUNCTION = "math.sqrt";

private static final int MAX_BIT_SHIFT = 63;

/**
Expand DownExpand Up@@ -614,7 +616,80 @@ enum Function {
"math_bitShiftRight_uint_int",
UnsignedLong.class,
Long.class,
CelMathExtensions::uintBitShiftRight)));
CelMathExtensions::uintBitShiftRight))),
SQRT(
CelFunctionDecl.newFunctionDeclaration(
MATH_SQRT_FUNCTION,
CelOverloadDecl.newGlobalOverload(
"math_sqrt_double",
"Computes square root of the double value.",
SimpleType.DOUBLE,
SimpleType.DOUBLE),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_int",
"Computes square root of the int value.",
SimpleType.DOUBLE,
SimpleType.INT),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_uint",
"Computes square root of the unsigned value.",
SimpleType.DOUBLE,
SimpleType.UINT)),
ImmutableSet.of(
CelFunctionBinding.from(
"math_sqrt_double", Double.class, CelMathExtensions::sqrtDouble),
CelFunctionBinding.from(
"math_sqrt_int", Long.class, CelMathExtensions::sqrtInt),
CelFunctionBinding.from(
"math_sqrt_uint", UnsignedLong.class, CelMathExtensions::sqrtUint)));

private static final ImmutableSet<Function> VERSION_0 = ImmutableSet.of(
MIN,
MAX);

private static final ImmutableSet<Function> VERSION_1 =
ImmutableSet.<Function>builder()
.addAll(VERSION_0)
.add(
CEIL,
FLOOR,
ROUND,
TRUNC,
ISINF,
ISNAN,
ISFINITE,
ABS,
SIGN,
BITAND,
BITOR,
BITXOR,
BITNOT,
BITSHIFTLEFT,
BITSHIFTRIGHT)
.build();

private static final ImmutableSet<Function> VERSION_2 =
ImmutableSet.<Function>builder()
.addAll(VERSION_1)
.add(SQRT)
.build();

private static final ImmutableSet<Function> VERSION_LATEST = VERSION_2;

private static ImmutableSet<Function> byVersion(int version) {
switch (version) {
case 0:
return Function.VERSION_0;
case 1:
return Function.VERSION_1;
case 2:
return Function.VERSION_2;
case Integer.MAX_VALUE:
return Function.VERSION_LATEST;
default:
throw new IllegalArgumentException("Unsupported 'math' extension version " + version);
}
}

private final CelFunctionDecl functionDecl;
private final ImmutableSet<CelFunctionBinding> functionBindings;
Expand DownExpand Up@@ -644,8 +719,8 @@ String getFunction() {
private final boolean enableUnsignedLongs;
private final ImmutableSet<Function> functions;

CelMathExtensions(CelOptions celOptions) {
this(celOptions, ImmutableSet.copyOf(Function.values()));
CelMathExtensions(CelOptions celOptions, int version) {
this(celOptions, Function.byVersion(version));
}

CelMathExtensions(CelOptions celOptions, Set<Function> functions) {
Expand DownExpand Up@@ -880,6 +955,18 @@ private static UnsignedLong uintBitShiftRight(UnsignedLong value, long shiftAmou
return UnsignedLong.fromLongBits(value.longValue() >>> shiftAmount);
}

private static Double sqrtDouble(double x) {
return Math.sqrt(x);
}

private static Double sqrtInt(Long x) {
return sqrtDouble(x.doubleValue());
}

private static Double sqrtUint(UnsignedLong x) {
return sqrtDouble(x.doubleValue());
}

private static Comparable minList(List<Comparable> list) {
if (list.isEmpty()) {
throw new IllegalStateException("math.@min(list) argument must not be empty");
Expand Down
17 changes: 17 additions & 0 deletions extensions/src/main/java/dev/cel/extensions/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -334,6 +334,23 @@ Examples:
math.isFinite(0.0/0.0) // returns false
math.isFinite(1.2) // returns true

### Math.sqrt

Introduced at version: 2

Returns the square root of the numeric type provided as input. If the value is
NaN, the output is NaN. If the input is negative, the output is NaN.

math.sqrt(<double>) -> <double>
math.sqrt(<int>) -> <double>
math.sqrt(<uint>) -> <double>

Examples:

math.sqrt(81.0) // returns 9.0
math.sqrt(4) // returns 2.0
math.sqrt(-4) // returns NaN

## Protos

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 48 additions & 34 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,10 +44,12 @@
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
import java.util.Optional;

Expand DownExpand Up@@ -237,15 +239,23 @@ private void addAllCompilerExtensions(
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
if (extension.compilerExtensionProvider() != null) {
CelCompilerLibrary celCompilerLibrary = extension.compilerExtensionProvider()
.getCelCompilerLibrary(celOptions, extensionConfig.version());
celCompilerBuilder.addLibraries(celCompilerLibrary);
}
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
if (extension.runtimeExtensionProvider() != null) {
CelRuntimeLibrary celRuntimeLibrary = extension.runtimeExtensionProvider()
.getCelRuntimeLibrary(celOptions, extensionConfig.version());
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
}

Expand DownExpand Up@@ -656,64 +666,68 @@ public static ExtensionConfig of(String name) {
public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}

/** Create a new extension config with the specified name and the latest version. */
public static ExtensionConfig latest(String name) {
return of(name, Integer.MAX_VALUE);
}
}

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

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

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

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
interface CompilerExtensionProvider {
CelCompilerLibrary getCelCompilerLibrary(CelOptions options, int version);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
interface RuntimeExtensionProvider {
CelRuntimeLibrary getCelRuntimeLibrary(CelOptions options, int version);
}

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

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

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

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
CompilerExtensionProvider compilerExtensionProvider,
RuntimeExtensionProvider runtimeExtensionProvider) {
this.compilerExtensionProvider = compilerExtensionProvider;
this.runtimeExtensionProvider = runtimeExtensionProvider;
}
}

Expand Down
65 changes: 57 additions & 8 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import dev.cel.common.CelValidationException;
import dev.cel.common.CelValidationResult;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerFactory;
Expand DownExpand Up@@ -52,14 +53,14 @@ public void newBuilder_defaults() {
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
ExtensionConfig.latest("bindings"),
ExtensionConfig.latest("encoders"),
ExtensionConfig.latest("lists"),
ExtensionConfig.latest("math"),
ExtensionConfig.latest("optional"),
ExtensionConfig.latest("protos"),
ExtensionConfig.latest("sets"),
ExtensionConfig.latest("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(extensionConfigs).build();

Expand All@@ -76,6 +77,54 @@ public void extend_allExtensions() throws Exception {
assertThat(result).isTrue();
}

@Test
public void extensionVersion_specific() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", 1)).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast1 = cel.compile("math.abs(-4)").getAst();
assertThat(cel.createProgram(ast1).eval()).isEqualTo(4);

// Version 1 of the 'math' extension does not include sqrt
assertThat(
assertThrows(
CelValidationException.class,
() -> {
cel.compile("math.sqrt(4)").getAst();
}))
.hasMessageThat()
.contains("undeclared reference to 'sqrt'");
}

@Test
public void extensionVersion_latest() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder()
.addExtensions(ExtensionConfig.latest("math"))
.build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast = cel.compile("math.sqrt(4)").getAst();
double result = (double) cel.createProgram(ast).eval();
assertThat(result).isEqualTo(2.0);
}

@Test
public void extensionVersion_unsupportedVersion_throws() {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", -5)).build();

assertThat(
assertThrows(
CelEnvironmentException.class,
() -> {
environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
}))
.hasMessageThat()
.contains("Unsupported 'math' extension version -5");
}

@Test
public void stdlibSubset_bothIncludeExcludeSet_throws() {
assertThat(
Expand Down
13 changes: 11 additions & 2 deletions extensions/src/main/java/dev/cel/extensions/CelExtensions.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -103,13 +103,22 @@ public static CelProtoExtensions protos() {
*
* <p>This will include all functions denoted in {@link CelMathExtensions.Function}, including any
* future additions. To expose only a subset of these, use {@link #math(CelOptions,
* CelMathExtensions.Function...)} instead.
* CelMathExtensions.Function...)} or {@link #math(CelOptions,int)} instead.
*
* @param celOptions CelOptions to configure CelMathExtension with. This should be the same
* options object used to configure the compilation/runtime environments.
*/
public static CelMathExtensions math(CelOptions celOptions) {
return new CelMathExtensions(celOptions);
return new CelMathExtensions(celOptions, Integer.MAX_VALUE);
}

/**
* Returns the specified version of the 'math' extension.
*
* <p>Refer to README.md for functions available in each version.
*/
public static CelMathExtensions math(CelOptions celOptions, int version) {
return new CelMathExtensions(celOptions, version);
}

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,8 @@ final class CelMathExtensions implements CelCompilerLibrary, CelRuntimeLibrary {
private static final String MATH_BIT_LEFT_SHIFT_FUNCTION = "math.bitShiftLeft";
private static final String MATH_BIT_RIGHT_SHIFT_FUNCTION = "math.bitShiftRight";

private static final String MATH_SQRT_FUNCTION = "math.sqrt";

private static final int MAX_BIT_SHIFT = 63;

/**
Expand DownExpand Up@@ -614,7 +616,80 @@ enum Function {
"math_bitShiftRight_uint_int",
UnsignedLong.class,
Long.class,
CelMathExtensions::uintBitShiftRight)));
CelMathExtensions::uintBitShiftRight))),
SQRT(
CelFunctionDecl.newFunctionDeclaration(
MATH_SQRT_FUNCTION,
CelOverloadDecl.newGlobalOverload(
"math_sqrt_double",
"Computes square root of the double value.",
SimpleType.DOUBLE,
SimpleType.DOUBLE),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_int",
"Computes square root of the int value.",
SimpleType.DOUBLE,
SimpleType.INT),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_uint",
"Computes square root of the unsigned value.",
SimpleType.DOUBLE,
SimpleType.UINT)),
ImmutableSet.of(
CelFunctionBinding.from(
"math_sqrt_double", Double.class, CelMathExtensions::sqrtDouble),
CelFunctionBinding.from(
"math_sqrt_int", Long.class, CelMathExtensions::sqrtInt),
CelFunctionBinding.from(
"math_sqrt_uint", UnsignedLong.class, CelMathExtensions::sqrtUint)));

private static final ImmutableSet<Function> VERSION_0 = ImmutableSet.of(
MIN,
MAX);

private static final ImmutableSet<Function> VERSION_1 =
ImmutableSet.<Function>builder()
.addAll(VERSION_0)
.add(
CEIL,
FLOOR,
ROUND,
TRUNC,
ISINF,
ISNAN,
ISFINITE,
ABS,
SIGN,
BITAND,
BITOR,
BITXOR,
BITNOT,
BITSHIFTLEFT,
BITSHIFTRIGHT)
.build();

private static final ImmutableSet<Function> VERSION_2 =
ImmutableSet.<Function>builder()
.addAll(VERSION_1)
.add(SQRT)
.build();

private static final ImmutableSet<Function> VERSION_LATEST = VERSION_2;

private static ImmutableSet<Function> byVersion(int version) {
switch (version) {
case 0:
return Function.VERSION_0;
case 1:
return Function.VERSION_1;
case 2:
return Function.VERSION_2;
case Integer.MAX_VALUE:
return Function.VERSION_LATEST;
default:
throw new IllegalArgumentException("Unsupported 'math' extension version " + version);
}
}

private final CelFunctionDecl functionDecl;
private final ImmutableSet<CelFunctionBinding> functionBindings;
Expand DownExpand Up@@ -644,8 +719,8 @@ String getFunction() {
private final boolean enableUnsignedLongs;
private final ImmutableSet<Function> functions;

CelMathExtensions(CelOptions celOptions) {
this(celOptions, ImmutableSet.copyOf(Function.values()));
CelMathExtensions(CelOptions celOptions, int version) {
this(celOptions, Function.byVersion(version));
}

CelMathExtensions(CelOptions celOptions, Set<Function> functions) {
Expand DownExpand Up@@ -880,6 +955,18 @@ private static UnsignedLong uintBitShiftRight(UnsignedLong value, long shiftAmou
return UnsignedLong.fromLongBits(value.longValue() >>> shiftAmount);
}

private static Double sqrtDouble(double x) {
return Math.sqrt(x);
}

private static Double sqrtInt(Long x) {
return sqrtDouble(x.doubleValue());
}

private static Double sqrtUint(UnsignedLong x) {
return sqrtDouble(x.doubleValue());
}

private static Comparable minList(List<Comparable> list) {
if (list.isEmpty()) {
throw new IllegalStateException("math.@min(list) argument must not be empty");
Expand Down
17 changes: 17 additions & 0 deletions extensions/src/main/java/dev/cel/extensions/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -334,6 +334,23 @@ Examples:
math.isFinite(0.0/0.0) // returns false
math.isFinite(1.2) // returns true

### Math.sqrt

Introduced at version: 2

Returns the square root of the numeric type provided as input. If the value is
NaN, the output is NaN. If the input is negative, the output is NaN.

math.sqrt(<double>) -> <double>
math.sqrt(<int>) -> <double>
math.sqrt(<uint>) -> <double>

Examples:

math.sqrt(81.0) // returns 9.0
math.sqrt(4) // returns 2.0
math.sqrt(-4) // returns NaN

## Protos

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 48 additions & 34 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,10 +44,12 @@
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
import java.util.Optional;

Expand DownExpand Up@@ -237,15 +239,23 @@ private void addAllCompilerExtensions(
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
if (extension.compilerExtensionProvider() != null) {
CelCompilerLibrary celCompilerLibrary = extension.compilerExtensionProvider()
.getCelCompilerLibrary(celOptions, extensionConfig.version());
celCompilerBuilder.addLibraries(celCompilerLibrary);
}
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
if (extension.runtimeExtensionProvider() != null) {
CelRuntimeLibrary celRuntimeLibrary = extension.runtimeExtensionProvider()
.getCelRuntimeLibrary(celOptions, extensionConfig.version());
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
}

Expand DownExpand Up@@ -656,64 +666,68 @@ public static ExtensionConfig of(String name) {
public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}

/** Create a new extension config with the specified name and the latest version. */
public static ExtensionConfig latest(String name) {
return of(name, Integer.MAX_VALUE);
}
}

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

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

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

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
interface CompilerExtensionProvider {
CelCompilerLibrary getCelCompilerLibrary(CelOptions options, int version);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
interface RuntimeExtensionProvider {
CelRuntimeLibrary getCelRuntimeLibrary(CelOptions options, int version);
}

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

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

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

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
CompilerExtensionProvider compilerExtensionProvider,
RuntimeExtensionProvider runtimeExtensionProvider) {
this.compilerExtensionProvider = compilerExtensionProvider;
this.runtimeExtensionProvider = runtimeExtensionProvider;
}
}

Expand Down
65 changes: 57 additions & 8 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import dev.cel.common.CelValidationException;
import dev.cel.common.CelValidationResult;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerFactory;
Expand DownExpand Up@@ -52,14 +53,14 @@ public void newBuilder_defaults() {
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
ExtensionConfig.latest("bindings"),
ExtensionConfig.latest("encoders"),
ExtensionConfig.latest("lists"),
ExtensionConfig.latest("math"),
ExtensionConfig.latest("optional"),
ExtensionConfig.latest("protos"),
ExtensionConfig.latest("sets"),
ExtensionConfig.latest("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(extensionConfigs).build();

Expand All@@ -76,6 +77,54 @@ public void extend_allExtensions() throws Exception {
assertThat(result).isTrue();
}

@Test
public void extensionVersion_specific() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", 1)).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast1 = cel.compile("math.abs(-4)").getAst();
assertThat(cel.createProgram(ast1).eval()).isEqualTo(4);

// Version 1 of the 'math' extension does not include sqrt
assertThat(
assertThrows(
CelValidationException.class,
() -> {
cel.compile("math.sqrt(4)").getAst();
}))
.hasMessageThat()
.contains("undeclared reference to 'sqrt'");
}

@Test
public void extensionVersion_latest() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder()
.addExtensions(ExtensionConfig.latest("math"))
.build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast = cel.compile("math.sqrt(4)").getAst();
double result = (double) cel.createProgram(ast).eval();
assertThat(result).isEqualTo(2.0);
}

@Test
public void extensionVersion_unsupportedVersion_throws() {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", -5)).build();

assertThat(
assertThrows(
CelEnvironmentException.class,
() -> {
environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
}))
.hasMessageThat()
.contains("Unsupported 'math' extension version -5");
}

@Test
public void stdlibSubset_bothIncludeExcludeSet_throws() {
assertThat(
Expand Down
13 changes: 11 additions & 2 deletions extensions/src/main/java/dev/cel/extensions/CelExtensions.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -103,13 +103,22 @@ public static CelProtoExtensions protos() {
*
* <p>This will include all functions denoted in {@link CelMathExtensions.Function}, including any
* future additions. To expose only a subset of these, use {@link #math(CelOptions,
* CelMathExtensions.Function...)} instead.
* CelMathExtensions.Function...)} or {@link #math(CelOptions,int)} instead.
*
* @param celOptions CelOptions to configure CelMathExtension with. This should be the same
* options object used to configure the compilation/runtime environments.
*/
public static CelMathExtensions math(CelOptions celOptions) {
return new CelMathExtensions(celOptions);
return new CelMathExtensions(celOptions, Integer.MAX_VALUE);
}

/**
* Returns the specified version of the 'math' extension.
*
* <p>Refer to README.md for functions available in each version.
*/
public static CelMathExtensions math(CelOptions celOptions, int version) {
return new CelMathExtensions(celOptions, version);
}

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,8 @@ final class CelMathExtensions implements CelCompilerLibrary, CelRuntimeLibrary {
private static final String MATH_BIT_LEFT_SHIFT_FUNCTION = "math.bitShiftLeft";
private static final String MATH_BIT_RIGHT_SHIFT_FUNCTION = "math.bitShiftRight";

private static final String MATH_SQRT_FUNCTION = "math.sqrt";

private static final int MAX_BIT_SHIFT = 63;

/**
Expand DownExpand Up@@ -614,7 +616,80 @@ enum Function {
"math_bitShiftRight_uint_int",
UnsignedLong.class,
Long.class,
CelMathExtensions::uintBitShiftRight)));
CelMathExtensions::uintBitShiftRight))),
SQRT(
CelFunctionDecl.newFunctionDeclaration(
MATH_SQRT_FUNCTION,
CelOverloadDecl.newGlobalOverload(
"math_sqrt_double",
"Computes square root of the double value.",
SimpleType.DOUBLE,
SimpleType.DOUBLE),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_int",
"Computes square root of the int value.",
SimpleType.DOUBLE,
SimpleType.INT),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_uint",
"Computes square root of the unsigned value.",
SimpleType.DOUBLE,
SimpleType.UINT)),
ImmutableSet.of(
CelFunctionBinding.from(
"math_sqrt_double", Double.class, CelMathExtensions::sqrtDouble),
CelFunctionBinding.from(
"math_sqrt_int", Long.class, CelMathExtensions::sqrtInt),
CelFunctionBinding.from(
"math_sqrt_uint", UnsignedLong.class, CelMathExtensions::sqrtUint)));

private static final ImmutableSet<Function> VERSION_0 = ImmutableSet.of(
MIN,
MAX);

private static final ImmutableSet<Function> VERSION_1 =
ImmutableSet.<Function>builder()
.addAll(VERSION_0)
.add(
CEIL,
FLOOR,
ROUND,
TRUNC,
ISINF,
ISNAN,
ISFINITE,
ABS,
SIGN,
BITAND,
BITOR,
BITXOR,
BITNOT,
BITSHIFTLEFT,
BITSHIFTRIGHT)
.build();

private static final ImmutableSet<Function> VERSION_2 =
ImmutableSet.<Function>builder()
.addAll(VERSION_1)
.add(SQRT)
.build();

private static final ImmutableSet<Function> VERSION_LATEST = VERSION_2;

private static ImmutableSet<Function> byVersion(int version) {
switch (version) {
case 0:
return Function.VERSION_0;
case 1:
return Function.VERSION_1;
case 2:
return Function.VERSION_2;
case Integer.MAX_VALUE:
return Function.VERSION_LATEST;
default:
throw new IllegalArgumentException("Unsupported 'math' extension version " + version);
}
}

private final CelFunctionDecl functionDecl;
private final ImmutableSet<CelFunctionBinding> functionBindings;
Expand DownExpand Up@@ -644,8 +719,8 @@ String getFunction() {
private final boolean enableUnsignedLongs;
private final ImmutableSet<Function> functions;

CelMathExtensions(CelOptions celOptions) {
this(celOptions, ImmutableSet.copyOf(Function.values()));
CelMathExtensions(CelOptions celOptions, int version) {
this(celOptions, Function.byVersion(version));
}

CelMathExtensions(CelOptions celOptions, Set<Function> functions) {
Expand DownExpand Up@@ -880,6 +955,18 @@ private static UnsignedLong uintBitShiftRight(UnsignedLong value, long shiftAmou
return UnsignedLong.fromLongBits(value.longValue() >>> shiftAmount);
}

private static Double sqrtDouble(double x) {
return Math.sqrt(x);
}

private static Double sqrtInt(Long x) {
return sqrtDouble(x.doubleValue());
}

private static Double sqrtUint(UnsignedLong x) {
return sqrtDouble(x.doubleValue());
}

private static Comparable minList(List<Comparable> list) {
if (list.isEmpty()) {
throw new IllegalStateException("math.@min(list) argument must not be empty");
Expand Down
17 changes: 17 additions & 0 deletions extensions/src/main/java/dev/cel/extensions/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -334,6 +334,23 @@ Examples:
math.isFinite(0.0/0.0) // returns false
math.isFinite(1.2) // returns true

### Math.sqrt

Introduced at version: 2

Returns the square root of the numeric type provided as input. If the value is
NaN, the output is NaN. If the input is negative, the output is NaN.

math.sqrt(<double>) -> <double>
math.sqrt(<int>) -> <double>
math.sqrt(<uint>) -> <double>

Examples:

math.sqrt(81.0) // returns 9.0
math.sqrt(4) // returns 2.0
math.sqrt(-4) // returns NaN

## Protos

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 48 additions & 34 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,10 +44,12 @@
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
import java.util.Optional;

Expand DownExpand Up@@ -237,15 +239,23 @@ private void addAllCompilerExtensions(
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
if (extension.compilerExtensionProvider() != null) {
CelCompilerLibrary celCompilerLibrary = extension.compilerExtensionProvider()
.getCelCompilerLibrary(celOptions, extensionConfig.version());
celCompilerBuilder.addLibraries(celCompilerLibrary);
}
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
if (extension.runtimeExtensionProvider() != null) {
CelRuntimeLibrary celRuntimeLibrary = extension.runtimeExtensionProvider()
.getCelRuntimeLibrary(celOptions, extensionConfig.version());
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
}

Expand DownExpand Up@@ -656,64 +666,68 @@ public static ExtensionConfig of(String name) {
public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}

/** Create a new extension config with the specified name and the latest version. */
public static ExtensionConfig latest(String name) {
return of(name, Integer.MAX_VALUE);
}
}

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

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

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

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
interface CompilerExtensionProvider {
CelCompilerLibrary getCelCompilerLibrary(CelOptions options, int version);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
interface RuntimeExtensionProvider {
CelRuntimeLibrary getCelRuntimeLibrary(CelOptions options, int version);
}

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

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

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

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
CompilerExtensionProvider compilerExtensionProvider,
RuntimeExtensionProvider runtimeExtensionProvider) {
this.compilerExtensionProvider = compilerExtensionProvider;
this.runtimeExtensionProvider = runtimeExtensionProvider;
}
}

Expand Down
65 changes: 57 additions & 8 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import dev.cel.common.CelValidationException;
import dev.cel.common.CelValidationResult;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerFactory;
Expand DownExpand Up@@ -52,14 +53,14 @@ public void newBuilder_defaults() {
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
ExtensionConfig.latest("bindings"),
ExtensionConfig.latest("encoders"),
ExtensionConfig.latest("lists"),
ExtensionConfig.latest("math"),
ExtensionConfig.latest("optional"),
ExtensionConfig.latest("protos"),
ExtensionConfig.latest("sets"),
ExtensionConfig.latest("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(extensionConfigs).build();

Expand All@@ -76,6 +77,54 @@ public void extend_allExtensions() throws Exception {
assertThat(result).isTrue();
}

@Test
public void extensionVersion_specific() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", 1)).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast1 = cel.compile("math.abs(-4)").getAst();
assertThat(cel.createProgram(ast1).eval()).isEqualTo(4);

// Version 1 of the 'math' extension does not include sqrt
assertThat(
assertThrows(
CelValidationException.class,
() -> {
cel.compile("math.sqrt(4)").getAst();
}))
.hasMessageThat()
.contains("undeclared reference to 'sqrt'");
}

@Test
public void extensionVersion_latest() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder()
.addExtensions(ExtensionConfig.latest("math"))
.build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast = cel.compile("math.sqrt(4)").getAst();
double result = (double) cel.createProgram(ast).eval();
assertThat(result).isEqualTo(2.0);
}

@Test
public void extensionVersion_unsupportedVersion_throws() {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", -5)).build();

assertThat(
assertThrows(
CelEnvironmentException.class,
() -> {
environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
}))
.hasMessageThat()
.contains("Unsupported 'math' extension version -5");
}

@Test
public void stdlibSubset_bothIncludeExcludeSet_throws() {
assertThat(
Expand Down
13 changes: 11 additions & 2 deletions extensions/src/main/java/dev/cel/extensions/CelExtensions.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -103,13 +103,22 @@ public static CelProtoExtensions protos() {
*
* <p>This will include all functions denoted in {@link CelMathExtensions.Function}, including any
* future additions. To expose only a subset of these, use {@link #math(CelOptions,
* CelMathExtensions.Function...)} instead.
* CelMathExtensions.Function...)} or {@link #math(CelOptions,int)} instead.
*
* @param celOptions CelOptions to configure CelMathExtension with. This should be the same
* options object used to configure the compilation/runtime environments.
*/
public static CelMathExtensions math(CelOptions celOptions) {
return new CelMathExtensions(celOptions);
return new CelMathExtensions(celOptions, Integer.MAX_VALUE);
}

/**
* Returns the specified version of the 'math' extension.
*
* <p>Refer to README.md for functions available in each version.
*/
public static CelMathExtensions math(CelOptions celOptions, int version) {
return new CelMathExtensions(celOptions, version);
}

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,8 @@ final class CelMathExtensions implements CelCompilerLibrary, CelRuntimeLibrary {
private static final String MATH_BIT_LEFT_SHIFT_FUNCTION = "math.bitShiftLeft";
private static final String MATH_BIT_RIGHT_SHIFT_FUNCTION = "math.bitShiftRight";

private static final String MATH_SQRT_FUNCTION = "math.sqrt";

private static final int MAX_BIT_SHIFT = 63;

/**
Expand DownExpand Up@@ -614,7 +616,80 @@ enum Function {
"math_bitShiftRight_uint_int",
UnsignedLong.class,
Long.class,
CelMathExtensions::uintBitShiftRight)));
CelMathExtensions::uintBitShiftRight))),
SQRT(
CelFunctionDecl.newFunctionDeclaration(
MATH_SQRT_FUNCTION,
CelOverloadDecl.newGlobalOverload(
"math_sqrt_double",
"Computes square root of the double value.",
SimpleType.DOUBLE,
SimpleType.DOUBLE),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_int",
"Computes square root of the int value.",
SimpleType.DOUBLE,
SimpleType.INT),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_uint",
"Computes square root of the unsigned value.",
SimpleType.DOUBLE,
SimpleType.UINT)),
ImmutableSet.of(
CelFunctionBinding.from(
"math_sqrt_double", Double.class, CelMathExtensions::sqrtDouble),
CelFunctionBinding.from(
"math_sqrt_int", Long.class, CelMathExtensions::sqrtInt),
CelFunctionBinding.from(
"math_sqrt_uint", UnsignedLong.class, CelMathExtensions::sqrtUint)));

private static final ImmutableSet<Function> VERSION_0 = ImmutableSet.of(
MIN,
MAX);

private static final ImmutableSet<Function> VERSION_1 =
ImmutableSet.<Function>builder()
.addAll(VERSION_0)
.add(
CEIL,
FLOOR,
ROUND,
TRUNC,
ISINF,
ISNAN,
ISFINITE,
ABS,
SIGN,
BITAND,
BITOR,
BITXOR,
BITNOT,
BITSHIFTLEFT,
BITSHIFTRIGHT)
.build();

private static final ImmutableSet<Function> VERSION_2 =
ImmutableSet.<Function>builder()
.addAll(VERSION_1)
.add(SQRT)
.build();

private static final ImmutableSet<Function> VERSION_LATEST = VERSION_2;

private static ImmutableSet<Function> byVersion(int version) {
switch (version) {
case 0:
return Function.VERSION_0;
case 1:
return Function.VERSION_1;
case 2:
return Function.VERSION_2;
case Integer.MAX_VALUE:
return Function.VERSION_LATEST;
default:
throw new IllegalArgumentException("Unsupported 'math' extension version " + version);
}
}

private final CelFunctionDecl functionDecl;
private final ImmutableSet<CelFunctionBinding> functionBindings;
Expand DownExpand Up@@ -644,8 +719,8 @@ String getFunction() {
private final boolean enableUnsignedLongs;
private final ImmutableSet<Function> functions;

CelMathExtensions(CelOptions celOptions) {
this(celOptions, ImmutableSet.copyOf(Function.values()));
CelMathExtensions(CelOptions celOptions, int version) {
this(celOptions, Function.byVersion(version));
}

CelMathExtensions(CelOptions celOptions, Set<Function> functions) {
Expand DownExpand Up@@ -880,6 +955,18 @@ private static UnsignedLong uintBitShiftRight(UnsignedLong value, long shiftAmou
return UnsignedLong.fromLongBits(value.longValue() >>> shiftAmount);
}

private static Double sqrtDouble(double x) {
return Math.sqrt(x);
}

private static Double sqrtInt(Long x) {
return sqrtDouble(x.doubleValue());
}

private static Double sqrtUint(UnsignedLong x) {
return sqrtDouble(x.doubleValue());
}

private static Comparable minList(List<Comparable> list) {
if (list.isEmpty()) {
throw new IllegalStateException("math.@min(list) argument must not be empty");
Expand Down
17 changes: 17 additions & 0 deletions extensions/src/main/java/dev/cel/extensions/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -334,6 +334,23 @@ Examples:
math.isFinite(0.0/0.0) // returns false
math.isFinite(1.2) // returns true

### Math.sqrt

Introduced at version: 2

Returns the square root of the numeric type provided as input. If the value is
NaN, the output is NaN. If the input is negative, the output is NaN.

math.sqrt(<double>) -> <double>
math.sqrt(<int>) -> <double>
math.sqrt(<uint>) -> <double>

Examples:

math.sqrt(81.0) // returns 9.0
math.sqrt(4) // returns 2.0
math.sqrt(-4) // returns NaN

## Protos

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 48 additions & 34 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,10 +44,12 @@
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
import java.util.Optional;

Expand DownExpand Up@@ -237,15 +239,23 @@ private void addAllCompilerExtensions(
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
if (extension.compilerExtensionProvider() != null) {
CelCompilerLibrary celCompilerLibrary = extension.compilerExtensionProvider()
.getCelCompilerLibrary(celOptions, extensionConfig.version());
celCompilerBuilder.addLibraries(celCompilerLibrary);
}
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
if (extension.runtimeExtensionProvider() != null) {
CelRuntimeLibrary celRuntimeLibrary = extension.runtimeExtensionProvider()
.getCelRuntimeLibrary(celOptions, extensionConfig.version());
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
}

Expand DownExpand Up@@ -656,64 +666,68 @@ public static ExtensionConfig of(String name) {
public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}

/** Create a new extension config with the specified name and the latest version. */
public static ExtensionConfig latest(String name) {
return of(name, Integer.MAX_VALUE);
}
}

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

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

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

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
interface CompilerExtensionProvider {
CelCompilerLibrary getCelCompilerLibrary(CelOptions options, int version);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
interface RuntimeExtensionProvider {
CelRuntimeLibrary getCelRuntimeLibrary(CelOptions options, int version);
}

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

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

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

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
CompilerExtensionProvider compilerExtensionProvider,
RuntimeExtensionProvider runtimeExtensionProvider) {
this.compilerExtensionProvider = compilerExtensionProvider;
this.runtimeExtensionProvider = runtimeExtensionProvider;
}
}

Expand Down
65 changes: 57 additions & 8 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import dev.cel.common.CelValidationException;
import dev.cel.common.CelValidationResult;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerFactory;
Expand DownExpand Up@@ -52,14 +53,14 @@ public void newBuilder_defaults() {
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
ExtensionConfig.latest("bindings"),
ExtensionConfig.latest("encoders"),
ExtensionConfig.latest("lists"),
ExtensionConfig.latest("math"),
ExtensionConfig.latest("optional"),
ExtensionConfig.latest("protos"),
ExtensionConfig.latest("sets"),
ExtensionConfig.latest("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(extensionConfigs).build();

Expand All@@ -76,6 +77,54 @@ public void extend_allExtensions() throws Exception {
assertThat(result).isTrue();
}

@Test
public void extensionVersion_specific() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", 1)).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast1 = cel.compile("math.abs(-4)").getAst();
assertThat(cel.createProgram(ast1).eval()).isEqualTo(4);

// Version 1 of the 'math' extension does not include sqrt
assertThat(
assertThrows(
CelValidationException.class,
() -> {
cel.compile("math.sqrt(4)").getAst();
}))
.hasMessageThat()
.contains("undeclared reference to 'sqrt'");
}

@Test
public void extensionVersion_latest() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder()
.addExtensions(ExtensionConfig.latest("math"))
.build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast = cel.compile("math.sqrt(4)").getAst();
double result = (double) cel.createProgram(ast).eval();
assertThat(result).isEqualTo(2.0);
}

@Test
public void extensionVersion_unsupportedVersion_throws() {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", -5)).build();

assertThat(
assertThrows(
CelEnvironmentException.class,
() -> {
environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
}))
.hasMessageThat()
.contains("Unsupported 'math' extension version -5");
}

@Test
public void stdlibSubset_bothIncludeExcludeSet_throws() {
assertThat(
Expand Down
13 changes: 11 additions & 2 deletions extensions/src/main/java/dev/cel/extensions/CelExtensions.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -103,13 +103,22 @@ public static CelProtoExtensions protos() {
*
* <p>This will include all functions denoted in {@link CelMathExtensions.Function}, including any
* future additions. To expose only a subset of these, use {@link #math(CelOptions,
* CelMathExtensions.Function...)} instead.
* CelMathExtensions.Function...)} or {@link #math(CelOptions,int)} instead.
*
* @param celOptions CelOptions to configure CelMathExtension with. This should be the same
* options object used to configure the compilation/runtime environments.
*/
public static CelMathExtensions math(CelOptions celOptions) {
return new CelMathExtensions(celOptions);
return new CelMathExtensions(celOptions, Integer.MAX_VALUE);
}

/**
* Returns the specified version of the 'math' extension.
*
* <p>Refer to README.md for functions available in each version.
*/
public static CelMathExtensions math(CelOptions celOptions, int version) {
return new CelMathExtensions(celOptions, version);
}

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,8 @@ final class CelMathExtensions implements CelCompilerLibrary, CelRuntimeLibrary {
private static final String MATH_BIT_LEFT_SHIFT_FUNCTION = "math.bitShiftLeft";
private static final String MATH_BIT_RIGHT_SHIFT_FUNCTION = "math.bitShiftRight";

private static final String MATH_SQRT_FUNCTION = "math.sqrt";

private static final int MAX_BIT_SHIFT = 63;

/**
Expand DownExpand Up@@ -614,7 +616,80 @@ enum Function {
"math_bitShiftRight_uint_int",
UnsignedLong.class,
Long.class,
CelMathExtensions::uintBitShiftRight)));
CelMathExtensions::uintBitShiftRight))),
SQRT(
CelFunctionDecl.newFunctionDeclaration(
MATH_SQRT_FUNCTION,
CelOverloadDecl.newGlobalOverload(
"math_sqrt_double",
"Computes square root of the double value.",
SimpleType.DOUBLE,
SimpleType.DOUBLE),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_int",
"Computes square root of the int value.",
SimpleType.DOUBLE,
SimpleType.INT),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_uint",
"Computes square root of the unsigned value.",
SimpleType.DOUBLE,
SimpleType.UINT)),
ImmutableSet.of(
CelFunctionBinding.from(
"math_sqrt_double", Double.class, CelMathExtensions::sqrtDouble),
CelFunctionBinding.from(
"math_sqrt_int", Long.class, CelMathExtensions::sqrtInt),
CelFunctionBinding.from(
"math_sqrt_uint", UnsignedLong.class, CelMathExtensions::sqrtUint)));

private static final ImmutableSet<Function> VERSION_0 = ImmutableSet.of(
MIN,
MAX);

private static final ImmutableSet<Function> VERSION_1 =
ImmutableSet.<Function>builder()
.addAll(VERSION_0)
.add(
CEIL,
FLOOR,
ROUND,
TRUNC,
ISINF,
ISNAN,
ISFINITE,
ABS,
SIGN,
BITAND,
BITOR,
BITXOR,
BITNOT,
BITSHIFTLEFT,
BITSHIFTRIGHT)
.build();

private static final ImmutableSet<Function> VERSION_2 =
ImmutableSet.<Function>builder()
.addAll(VERSION_1)
.add(SQRT)
.build();

private static final ImmutableSet<Function> VERSION_LATEST = VERSION_2;

private static ImmutableSet<Function> byVersion(int version) {
switch (version) {
case 0:
return Function.VERSION_0;
case 1:
return Function.VERSION_1;
case 2:
return Function.VERSION_2;
case Integer.MAX_VALUE:
return Function.VERSION_LATEST;
default:
throw new IllegalArgumentException("Unsupported 'math' extension version " + version);
}
}

private final CelFunctionDecl functionDecl;
private final ImmutableSet<CelFunctionBinding> functionBindings;
Expand DownExpand Up@@ -644,8 +719,8 @@ String getFunction() {
private final boolean enableUnsignedLongs;
private final ImmutableSet<Function> functions;

CelMathExtensions(CelOptions celOptions) {
this(celOptions, ImmutableSet.copyOf(Function.values()));
CelMathExtensions(CelOptions celOptions, int version) {
this(celOptions, Function.byVersion(version));
}

CelMathExtensions(CelOptions celOptions, Set<Function> functions) {
Expand DownExpand Up@@ -880,6 +955,18 @@ private static UnsignedLong uintBitShiftRight(UnsignedLong value, long shiftAmou
return UnsignedLong.fromLongBits(value.longValue() >>> shiftAmount);
}

private static Double sqrtDouble(double x) {
return Math.sqrt(x);
}

private static Double sqrtInt(Long x) {
return sqrtDouble(x.doubleValue());
}

private static Double sqrtUint(UnsignedLong x) {
return sqrtDouble(x.doubleValue());
}

private static Comparable minList(List<Comparable> list) {
if (list.isEmpty()) {
throw new IllegalStateException("math.@min(list) argument must not be empty");
Expand Down
17 changes: 17 additions & 0 deletions extensions/src/main/java/dev/cel/extensions/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -334,6 +334,23 @@ Examples:
math.isFinite(0.0/0.0) // returns false
math.isFinite(1.2) // returns true

### Math.sqrt

Introduced at version: 2

Returns the square root of the numeric type provided as input. If the value is
NaN, the output is NaN. If the input is negative, the output is NaN.

math.sqrt(<double>) -> <double>
math.sqrt(<int>) -> <double>
math.sqrt(<uint>) -> <double>

Examples:

math.sqrt(81.0) // returns 9.0
math.sqrt(4) // returns 2.0
math.sqrt(-4) // returns NaN

## Protos

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 48 additions & 34 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,10 +44,12 @@
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
import java.util.Optional;

Expand DownExpand Up@@ -237,15 +239,23 @@ private void addAllCompilerExtensions(
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
if (extension.compilerExtensionProvider() != null) {
CelCompilerLibrary celCompilerLibrary = extension.compilerExtensionProvider()
.getCelCompilerLibrary(celOptions, extensionConfig.version());
celCompilerBuilder.addLibraries(celCompilerLibrary);
}
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
if (extension.runtimeExtensionProvider() != null) {
CelRuntimeLibrary celRuntimeLibrary = extension.runtimeExtensionProvider()
.getCelRuntimeLibrary(celOptions, extensionConfig.version());
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
}

Expand DownExpand Up@@ -656,64 +666,68 @@ public static ExtensionConfig of(String name) {
public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}

/** Create a new extension config with the specified name and the latest version. */
public static ExtensionConfig latest(String name) {
return of(name, Integer.MAX_VALUE);
}
}

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

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

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

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
interface CompilerExtensionProvider {
CelCompilerLibrary getCelCompilerLibrary(CelOptions options, int version);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
interface RuntimeExtensionProvider {
CelRuntimeLibrary getCelRuntimeLibrary(CelOptions options, int version);
}

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

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

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

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
CompilerExtensionProvider compilerExtensionProvider,
RuntimeExtensionProvider runtimeExtensionProvider) {
this.compilerExtensionProvider = compilerExtensionProvider;
this.runtimeExtensionProvider = runtimeExtensionProvider;
}
}

Expand Down
65 changes: 57 additions & 8 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import dev.cel.common.CelValidationException;
import dev.cel.common.CelValidationResult;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerFactory;
Expand DownExpand Up@@ -52,14 +53,14 @@ public void newBuilder_defaults() {
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
ExtensionConfig.latest("bindings"),
ExtensionConfig.latest("encoders"),
ExtensionConfig.latest("lists"),
ExtensionConfig.latest("math"),
ExtensionConfig.latest("optional"),
ExtensionConfig.latest("protos"),
ExtensionConfig.latest("sets"),
ExtensionConfig.latest("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(extensionConfigs).build();

Expand All@@ -76,6 +77,54 @@ public void extend_allExtensions() throws Exception {
assertThat(result).isTrue();
}

@Test
public void extensionVersion_specific() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", 1)).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast1 = cel.compile("math.abs(-4)").getAst();
assertThat(cel.createProgram(ast1).eval()).isEqualTo(4);

// Version 1 of the 'math' extension does not include sqrt
assertThat(
assertThrows(
CelValidationException.class,
() -> {
cel.compile("math.sqrt(4)").getAst();
}))
.hasMessageThat()
.contains("undeclared reference to 'sqrt'");
}

@Test
public void extensionVersion_latest() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder()
.addExtensions(ExtensionConfig.latest("math"))
.build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast = cel.compile("math.sqrt(4)").getAst();
double result = (double) cel.createProgram(ast).eval();
assertThat(result).isEqualTo(2.0);
}

@Test
public void extensionVersion_unsupportedVersion_throws() {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", -5)).build();

assertThat(
assertThrows(
CelEnvironmentException.class,
() -> {
environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
}))
.hasMessageThat()
.contains("Unsupported 'math' extension version -5");
}

@Test
public void stdlibSubset_bothIncludeExcludeSet_throws() {
assertThat(
Expand Down
13 changes: 11 additions & 2 deletions extensions/src/main/java/dev/cel/extensions/CelExtensions.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -103,13 +103,22 @@ public static CelProtoExtensions protos() {
*
* <p>This will include all functions denoted in {@link CelMathExtensions.Function}, including any
* future additions. To expose only a subset of these, use {@link #math(CelOptions,
* CelMathExtensions.Function...)} instead.
* CelMathExtensions.Function...)} or {@link #math(CelOptions,int)} instead.
*
* @param celOptions CelOptions to configure CelMathExtension with. This should be the same
* options object used to configure the compilation/runtime environments.
*/
public static CelMathExtensions math(CelOptions celOptions) {
return new CelMathExtensions(celOptions);
return new CelMathExtensions(celOptions, Integer.MAX_VALUE);
}

/**
* Returns the specified version of the 'math' extension.
*
* <p>Refer to README.md for functions available in each version.
*/
public static CelMathExtensions math(CelOptions celOptions, int version) {
return new CelMathExtensions(celOptions, version);
}

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,8 @@ final class CelMathExtensions implements CelCompilerLibrary, CelRuntimeLibrary {
private static final String MATH_BIT_LEFT_SHIFT_FUNCTION = "math.bitShiftLeft";
private static final String MATH_BIT_RIGHT_SHIFT_FUNCTION = "math.bitShiftRight";

private static final String MATH_SQRT_FUNCTION = "math.sqrt";

private static final int MAX_BIT_SHIFT = 63;

/**
Expand DownExpand Up@@ -614,7 +616,80 @@ enum Function {
"math_bitShiftRight_uint_int",
UnsignedLong.class,
Long.class,
CelMathExtensions::uintBitShiftRight)));
CelMathExtensions::uintBitShiftRight))),
SQRT(
CelFunctionDecl.newFunctionDeclaration(
MATH_SQRT_FUNCTION,
CelOverloadDecl.newGlobalOverload(
"math_sqrt_double",
"Computes square root of the double value.",
SimpleType.DOUBLE,
SimpleType.DOUBLE),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_int",
"Computes square root of the int value.",
SimpleType.DOUBLE,
SimpleType.INT),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_uint",
"Computes square root of the unsigned value.",
SimpleType.DOUBLE,
SimpleType.UINT)),
ImmutableSet.of(
CelFunctionBinding.from(
"math_sqrt_double", Double.class, CelMathExtensions::sqrtDouble),
CelFunctionBinding.from(
"math_sqrt_int", Long.class, CelMathExtensions::sqrtInt),
CelFunctionBinding.from(
"math_sqrt_uint", UnsignedLong.class, CelMathExtensions::sqrtUint)));

private static final ImmutableSet<Function> VERSION_0 = ImmutableSet.of(
MIN,
MAX);

private static final ImmutableSet<Function> VERSION_1 =
ImmutableSet.<Function>builder()
.addAll(VERSION_0)
.add(
CEIL,
FLOOR,
ROUND,
TRUNC,
ISINF,
ISNAN,
ISFINITE,
ABS,
SIGN,
BITAND,
BITOR,
BITXOR,
BITNOT,
BITSHIFTLEFT,
BITSHIFTRIGHT)
.build();

private static final ImmutableSet<Function> VERSION_2 =
ImmutableSet.<Function>builder()
.addAll(VERSION_1)
.add(SQRT)
.build();

private static final ImmutableSet<Function> VERSION_LATEST = VERSION_2;

private static ImmutableSet<Function> byVersion(int version) {
switch (version) {
case 0:
return Function.VERSION_0;
case 1:
return Function.VERSION_1;
case 2:
return Function.VERSION_2;
case Integer.MAX_VALUE:
return Function.VERSION_LATEST;
default:
throw new IllegalArgumentException("Unsupported 'math' extension version " + version);
}
}

private final CelFunctionDecl functionDecl;
private final ImmutableSet<CelFunctionBinding> functionBindings;
Expand DownExpand Up@@ -644,8 +719,8 @@ String getFunction() {
private final boolean enableUnsignedLongs;
private final ImmutableSet<Function> functions;

CelMathExtensions(CelOptions celOptions) {
this(celOptions, ImmutableSet.copyOf(Function.values()));
CelMathExtensions(CelOptions celOptions, int version) {
this(celOptions, Function.byVersion(version));
}

CelMathExtensions(CelOptions celOptions, Set<Function> functions) {
Expand DownExpand Up@@ -880,6 +955,18 @@ private static UnsignedLong uintBitShiftRight(UnsignedLong value, long shiftAmou
return UnsignedLong.fromLongBits(value.longValue() >>> shiftAmount);
}

private static Double sqrtDouble(double x) {
return Math.sqrt(x);
}

private static Double sqrtInt(Long x) {
return sqrtDouble(x.doubleValue());
}

private static Double sqrtUint(UnsignedLong x) {
return sqrtDouble(x.doubleValue());
}

private static Comparable minList(List<Comparable> list) {
if (list.isEmpty()) {
throw new IllegalStateException("math.@min(list) argument must not be empty");
Expand Down
17 changes: 17 additions & 0 deletions extensions/src/main/java/dev/cel/extensions/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -334,6 +334,23 @@ Examples:
math.isFinite(0.0/0.0) // returns false
math.isFinite(1.2) // returns true

### Math.sqrt

Introduced at version: 2

Returns the square root of the numeric type provided as input. If the value is
NaN, the output is NaN. If the input is negative, the output is NaN.

math.sqrt(<double>) -> <double>
math.sqrt(<int>) -> <double>
math.sqrt(<uint>) -> <double>

Examples:

math.sqrt(81.0) // returns 9.0
math.sqrt(4) // returns 2.0
math.sqrt(-4) // returns NaN

## Protos

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 48 additions & 34 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,10 +44,12 @@
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
import java.util.Optional;

Expand DownExpand Up@@ -237,15 +239,23 @@ private void addAllCompilerExtensions(
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
if (extension.compilerExtensionProvider() != null) {
CelCompilerLibrary celCompilerLibrary = extension.compilerExtensionProvider()
.getCelCompilerLibrary(celOptions, extensionConfig.version());
celCompilerBuilder.addLibraries(celCompilerLibrary);
}
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
if (extension.runtimeExtensionProvider() != null) {
CelRuntimeLibrary celRuntimeLibrary = extension.runtimeExtensionProvider()
.getCelRuntimeLibrary(celOptions, extensionConfig.version());
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
}

Expand DownExpand Up@@ -656,64 +666,68 @@ public static ExtensionConfig of(String name) {
public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}

/** Create a new extension config with the specified name and the latest version. */
public static ExtensionConfig latest(String name) {
return of(name, Integer.MAX_VALUE);
}
}

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

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

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

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
interface CompilerExtensionProvider {
CelCompilerLibrary getCelCompilerLibrary(CelOptions options, int version);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
interface RuntimeExtensionProvider {
CelRuntimeLibrary getCelRuntimeLibrary(CelOptions options, int version);
}

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

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

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

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
CompilerExtensionProvider compilerExtensionProvider,
RuntimeExtensionProvider runtimeExtensionProvider) {
this.compilerExtensionProvider = compilerExtensionProvider;
this.runtimeExtensionProvider = runtimeExtensionProvider;
}
}

Expand Down
65 changes: 57 additions & 8 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import dev.cel.common.CelValidationException;
import dev.cel.common.CelValidationResult;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerFactory;
Expand DownExpand Up@@ -52,14 +53,14 @@ public void newBuilder_defaults() {
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
ExtensionConfig.latest("bindings"),
ExtensionConfig.latest("encoders"),
ExtensionConfig.latest("lists"),
ExtensionConfig.latest("math"),
ExtensionConfig.latest("optional"),
ExtensionConfig.latest("protos"),
ExtensionConfig.latest("sets"),
ExtensionConfig.latest("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(extensionConfigs).build();

Expand All@@ -76,6 +77,54 @@ public void extend_allExtensions() throws Exception {
assertThat(result).isTrue();
}

@Test
public void extensionVersion_specific() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", 1)).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast1 = cel.compile("math.abs(-4)").getAst();
assertThat(cel.createProgram(ast1).eval()).isEqualTo(4);

// Version 1 of the 'math' extension does not include sqrt
assertThat(
assertThrows(
CelValidationException.class,
() -> {
cel.compile("math.sqrt(4)").getAst();
}))
.hasMessageThat()
.contains("undeclared reference to 'sqrt'");
}

@Test
public void extensionVersion_latest() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder()
.addExtensions(ExtensionConfig.latest("math"))
.build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast = cel.compile("math.sqrt(4)").getAst();
double result = (double) cel.createProgram(ast).eval();
assertThat(result).isEqualTo(2.0);
}

@Test
public void extensionVersion_unsupportedVersion_throws() {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", -5)).build();

assertThat(
assertThrows(
CelEnvironmentException.class,
() -> {
environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
}))
.hasMessageThat()
.contains("Unsupported 'math' extension version -5");
}

@Test
public void stdlibSubset_bothIncludeExcludeSet_throws() {
assertThat(
Expand Down
13 changes: 11 additions & 2 deletions extensions/src/main/java/dev/cel/extensions/CelExtensions.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -103,13 +103,22 @@ public static CelProtoExtensions protos() {
*
* <p>This will include all functions denoted in {@link CelMathExtensions.Function}, including any
* future additions. To expose only a subset of these, use {@link #math(CelOptions,
* CelMathExtensions.Function...)} instead.
* CelMathExtensions.Function...)} or {@link #math(CelOptions,int)} instead.
*
* @param celOptions CelOptions to configure CelMathExtension with. This should be the same
* options object used to configure the compilation/runtime environments.
*/
public static CelMathExtensions math(CelOptions celOptions) {
return new CelMathExtensions(celOptions);
return new CelMathExtensions(celOptions, Integer.MAX_VALUE);
}

/**
* Returns the specified version of the 'math' extension.
*
* <p>Refer to README.md for functions available in each version.
*/
public static CelMathExtensions math(CelOptions celOptions, int version) {
return new CelMathExtensions(celOptions, version);
}

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,8 @@ final class CelMathExtensions implements CelCompilerLibrary, CelRuntimeLibrary {
private static final String MATH_BIT_LEFT_SHIFT_FUNCTION = "math.bitShiftLeft";
private static final String MATH_BIT_RIGHT_SHIFT_FUNCTION = "math.bitShiftRight";

private static final String MATH_SQRT_FUNCTION = "math.sqrt";

private static final int MAX_BIT_SHIFT = 63;

/**
Expand DownExpand Up@@ -614,7 +616,80 @@ enum Function {
"math_bitShiftRight_uint_int",
UnsignedLong.class,
Long.class,
CelMathExtensions::uintBitShiftRight)));
CelMathExtensions::uintBitShiftRight))),
SQRT(
CelFunctionDecl.newFunctionDeclaration(
MATH_SQRT_FUNCTION,
CelOverloadDecl.newGlobalOverload(
"math_sqrt_double",
"Computes square root of the double value.",
SimpleType.DOUBLE,
SimpleType.DOUBLE),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_int",
"Computes square root of the int value.",
SimpleType.DOUBLE,
SimpleType.INT),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_uint",
"Computes square root of the unsigned value.",
SimpleType.DOUBLE,
SimpleType.UINT)),
ImmutableSet.of(
CelFunctionBinding.from(
"math_sqrt_double", Double.class, CelMathExtensions::sqrtDouble),
CelFunctionBinding.from(
"math_sqrt_int", Long.class, CelMathExtensions::sqrtInt),
CelFunctionBinding.from(
"math_sqrt_uint", UnsignedLong.class, CelMathExtensions::sqrtUint)));

private static final ImmutableSet<Function> VERSION_0 = ImmutableSet.of(
MIN,
MAX);

private static final ImmutableSet<Function> VERSION_1 =
ImmutableSet.<Function>builder()
.addAll(VERSION_0)
.add(
CEIL,
FLOOR,
ROUND,
TRUNC,
ISINF,
ISNAN,
ISFINITE,
ABS,
SIGN,
BITAND,
BITOR,
BITXOR,
BITNOT,
BITSHIFTLEFT,
BITSHIFTRIGHT)
.build();

private static final ImmutableSet<Function> VERSION_2 =
ImmutableSet.<Function>builder()
.addAll(VERSION_1)
.add(SQRT)
.build();

private static final ImmutableSet<Function> VERSION_LATEST = VERSION_2;

private static ImmutableSet<Function> byVersion(int version) {
switch (version) {
case 0:
return Function.VERSION_0;
case 1:
return Function.VERSION_1;
case 2:
return Function.VERSION_2;
case Integer.MAX_VALUE:
return Function.VERSION_LATEST;
default:
throw new IllegalArgumentException("Unsupported 'math' extension version " + version);
}
}

private final CelFunctionDecl functionDecl;
private final ImmutableSet<CelFunctionBinding> functionBindings;
Expand DownExpand Up@@ -644,8 +719,8 @@ String getFunction() {
private final boolean enableUnsignedLongs;
private final ImmutableSet<Function> functions;

CelMathExtensions(CelOptions celOptions) {
this(celOptions, ImmutableSet.copyOf(Function.values()));
CelMathExtensions(CelOptions celOptions, int version) {
this(celOptions, Function.byVersion(version));
}

CelMathExtensions(CelOptions celOptions, Set<Function> functions) {
Expand DownExpand Up@@ -880,6 +955,18 @@ private static UnsignedLong uintBitShiftRight(UnsignedLong value, long shiftAmou
return UnsignedLong.fromLongBits(value.longValue() >>> shiftAmount);
}

private static Double sqrtDouble(double x) {
return Math.sqrt(x);
}

private static Double sqrtInt(Long x) {
return sqrtDouble(x.doubleValue());
}

private static Double sqrtUint(UnsignedLong x) {
return sqrtDouble(x.doubleValue());
}

private static Comparable minList(List<Comparable> list) {
if (list.isEmpty()) {
throw new IllegalStateException("math.@min(list) argument must not be empty");
Expand Down
17 changes: 17 additions & 0 deletions extensions/src/main/java/dev/cel/extensions/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -334,6 +334,23 @@ Examples:
math.isFinite(0.0/0.0) // returns false
math.isFinite(1.2) // returns true

### Math.sqrt

Introduced at version: 2

Returns the square root of the numeric type provided as input. If the value is
NaN, the output is NaN. If the input is negative, the output is NaN.

math.sqrt(<double>) -> <double>
math.sqrt(<int>) -> <double>
math.sqrt(<uint>) -> <double>

Examples:

math.sqrt(81.0) // returns 9.0
math.sqrt(4) // returns 2.0
math.sqrt(-4) // returns NaN

## Protos

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 48 additions & 34 deletions bundle/src/main/java/dev/cel/bundle/CelEnvironment.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,10 +44,12 @@
import dev.cel.common.types.TypeParamType;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.compiler.CelCompilerLibrary;
import dev.cel.extensions.CelExtensions;
import dev.cel.extensions.CelOptionalLibrary;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelRuntimeBuilder;
import dev.cel.runtime.CelRuntimeLibrary;
import java.util.Arrays;
import java.util.Optional;

Expand DownExpand Up@@ -237,15 +239,23 @@ private void addAllCompilerExtensions(
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addCompilerExtension(celCompilerBuilder, celOptions);
if (extension.compilerExtensionProvider() != null) {
CelCompilerLibrary celCompilerLibrary = extension.compilerExtensionProvider()
.getCelCompilerLibrary(celOptions, extensionConfig.version());
celCompilerBuilder.addLibraries(celCompilerLibrary);
}
}
}

private void addAllRuntimeExtensions(CelRuntimeBuilder celRuntimeBuilder, CelOptions celOptions) {
// TODO: Add capability to accept user defined exceptions
for (ExtensionConfig extensionConfig : extensions()) {
CanonicalCelExtension extension = getExtensionOrThrow(extensionConfig.name());
extension.addRuntimeExtension(celRuntimeBuilder, celOptions);
if (extension.runtimeExtensionProvider() != null) {
CelRuntimeLibrary celRuntimeLibrary = extension.runtimeExtensionProvider()
.getCelRuntimeLibrary(celOptions, extensionConfig.version());
celRuntimeBuilder.addLibraries(celRuntimeLibrary);
}
}
}

Expand DownExpand Up@@ -656,64 +666,68 @@ public static ExtensionConfig of(String name) {
public static ExtensionConfig of(String name, int version) {
return newBuilder().setName(name).setVersion(version).build();
}

/** Create a new extension config with the specified name and the latest version. */
public static ExtensionConfig latest(String name) {
return of(name, Integer.MAX_VALUE);
}
}

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

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

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

interface CompilerExtensionApplier {
void apply(CelCompilerBuilder compilerBuilder, CelOptions options);
interface CompilerExtensionProvider {
CelCompilerLibrary getCelCompilerLibrary(CelOptions options, int version);
}

interface RuntimeExtensionApplier {
void apply(CelRuntimeBuilder runtimeBuilder, CelOptions options);
interface RuntimeExtensionProvider {
CelRuntimeLibrary getCelRuntimeLibrary(CelOptions options, int version);
}

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

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

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

CanonicalCelExtension(
CompilerExtensionApplier compilerExtensionApplier,
RuntimeExtensionApplier runtimeExtensionApplier) {
this.compilerExtensionApplier = compilerExtensionApplier;
this.runtimeExtensionApplier = runtimeExtensionApplier;
CompilerExtensionProvider compilerExtensionProvider,
RuntimeExtensionProvider runtimeExtensionProvider) {
this.compilerExtensionProvider = compilerExtensionProvider;
this.runtimeExtensionProvider = runtimeExtensionProvider;
}
}

Expand Down
65 changes: 57 additions & 8 deletions bundle/src/test/java/dev/cel/bundle/CelEnvironmentTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,6 +25,7 @@
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import dev.cel.common.CelValidationException;
import dev.cel.common.CelValidationResult;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerFactory;
Expand DownExpand Up@@ -52,14 +53,14 @@ public void newBuilder_defaults() {
public void extend_allExtensions() throws Exception {
ImmutableSet<ExtensionConfig> extensionConfigs =
ImmutableSet.of(
ExtensionConfig.of("bindings"),
ExtensionConfig.of("encoders"),
ExtensionConfig.of("lists"),
ExtensionConfig.of("math"),
ExtensionConfig.of("optional"),
ExtensionConfig.of("protos"),
ExtensionConfig.of("sets"),
ExtensionConfig.of("strings"));
ExtensionConfig.latest("bindings"),
ExtensionConfig.latest("encoders"),
ExtensionConfig.latest("lists"),
ExtensionConfig.latest("math"),
ExtensionConfig.latest("optional"),
ExtensionConfig.latest("protos"),
ExtensionConfig.latest("sets"),
ExtensionConfig.latest("strings"));
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(extensionConfigs).build();

Expand All@@ -76,6 +77,54 @@ public void extend_allExtensions() throws Exception {
assertThat(result).isTrue();
}

@Test
public void extensionVersion_specific() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", 1)).build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast1 = cel.compile("math.abs(-4)").getAst();
assertThat(cel.createProgram(ast1).eval()).isEqualTo(4);

// Version 1 of the 'math' extension does not include sqrt
assertThat(
assertThrows(
CelValidationException.class,
() -> {
cel.compile("math.sqrt(4)").getAst();
}))
.hasMessageThat()
.contains("undeclared reference to 'sqrt'");
}

@Test
public void extensionVersion_latest() throws Exception {
CelEnvironment environment =
CelEnvironment.newBuilder()
.addExtensions(ExtensionConfig.latest("math"))
.build();

Cel cel = environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
CelAbstractSyntaxTree ast = cel.compile("math.sqrt(4)").getAst();
double result = (double) cel.createProgram(ast).eval();
assertThat(result).isEqualTo(2.0);
}

@Test
public void extensionVersion_unsupportedVersion_throws() {
CelEnvironment environment =
CelEnvironment.newBuilder().addExtensions(ExtensionConfig.of("math", -5)).build();

assertThat(
assertThrows(
CelEnvironmentException.class,
() -> {
environment.extend(CelFactory.standardCelBuilder().build(), CelOptions.DEFAULT);
}))
.hasMessageThat()
.contains("Unsupported 'math' extension version -5");
}

@Test
public void stdlibSubset_bothIncludeExcludeSet_throws() {
assertThat(
Expand Down
13 changes: 11 additions & 2 deletions extensions/src/main/java/dev/cel/extensions/CelExtensions.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -103,13 +103,22 @@ public static CelProtoExtensions protos() {
*
* <p>This will include all functions denoted in {@link CelMathExtensions.Function}, including any
* future additions. To expose only a subset of these, use {@link #math(CelOptions,
* CelMathExtensions.Function...)} instead.
* CelMathExtensions.Function...)} or {@link #math(CelOptions,int)} instead.
*
* @param celOptions CelOptions to configure CelMathExtension with. This should be the same
* options object used to configure the compilation/runtime environments.
*/
public static CelMathExtensions math(CelOptions celOptions) {
return new CelMathExtensions(celOptions);
return new CelMathExtensions(celOptions, Integer.MAX_VALUE);
}

/**
* Returns the specified version of the 'math' extension.
*
* <p>Refer to README.md for functions available in each version.
*/
public static CelMathExtensions math(CelOptions celOptions, int version) {
return new CelMathExtensions(celOptions, version);
}

/**
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -89,6 +89,8 @@ final class CelMathExtensions implements CelCompilerLibrary, CelRuntimeLibrary {
private static final String MATH_BIT_LEFT_SHIFT_FUNCTION = "math.bitShiftLeft";
private static final String MATH_BIT_RIGHT_SHIFT_FUNCTION = "math.bitShiftRight";

private static final String MATH_SQRT_FUNCTION = "math.sqrt";

private static final int MAX_BIT_SHIFT = 63;

/**
Expand DownExpand Up@@ -614,7 +616,80 @@ enum Function {
"math_bitShiftRight_uint_int",
UnsignedLong.class,
Long.class,
CelMathExtensions::uintBitShiftRight)));
CelMathExtensions::uintBitShiftRight))),
SQRT(
CelFunctionDecl.newFunctionDeclaration(
MATH_SQRT_FUNCTION,
CelOverloadDecl.newGlobalOverload(
"math_sqrt_double",
"Computes square root of the double value.",
SimpleType.DOUBLE,
SimpleType.DOUBLE),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_int",
"Computes square root of the int value.",
SimpleType.DOUBLE,
SimpleType.INT),
CelOverloadDecl.newGlobalOverload(
"math_sqrt_uint",
"Computes square root of the unsigned value.",
SimpleType.DOUBLE,
SimpleType.UINT)),
ImmutableSet.of(
CelFunctionBinding.from(
"math_sqrt_double", Double.class, CelMathExtensions::sqrtDouble),
CelFunctionBinding.from(
"math_sqrt_int", Long.class, CelMathExtensions::sqrtInt),
CelFunctionBinding.from(
"math_sqrt_uint", UnsignedLong.class, CelMathExtensions::sqrtUint)));

private static final ImmutableSet<Function> VERSION_0 = ImmutableSet.of(
MIN,
MAX);

private static final ImmutableSet<Function> VERSION_1 =
ImmutableSet.<Function>builder()
.addAll(VERSION_0)
.add(
CEIL,
FLOOR,
ROUND,
TRUNC,
ISINF,
ISNAN,
ISFINITE,
ABS,
SIGN,
BITAND,
BITOR,
BITXOR,
BITNOT,
BITSHIFTLEFT,
BITSHIFTRIGHT)
.build();

private static final ImmutableSet<Function> VERSION_2 =
ImmutableSet.<Function>builder()
.addAll(VERSION_1)
.add(SQRT)
.build();

private static final ImmutableSet<Function> VERSION_LATEST = VERSION_2;

private static ImmutableSet<Function> byVersion(int version) {
switch (version) {
case 0:
return Function.VERSION_0;
case 1:
return Function.VERSION_1;
case 2:
return Function.VERSION_2;
case Integer.MAX_VALUE:
return Function.VERSION_LATEST;
default:
throw new IllegalArgumentException("Unsupported 'math' extension version " + version);
}
}

private final CelFunctionDecl functionDecl;
private final ImmutableSet<CelFunctionBinding> functionBindings;
Expand DownExpand Up@@ -644,8 +719,8 @@ String getFunction() {
private final boolean enableUnsignedLongs;
private final ImmutableSet<Function> functions;

CelMathExtensions(CelOptions celOptions) {
this(celOptions, ImmutableSet.copyOf(Function.values()));
CelMathExtensions(CelOptions celOptions, int version) {
this(celOptions, Function.byVersion(version));
}

CelMathExtensions(CelOptions celOptions, Set<Function> functions) {
Expand DownExpand Up@@ -880,6 +955,18 @@ private static UnsignedLong uintBitShiftRight(UnsignedLong value, long shiftAmou
return UnsignedLong.fromLongBits(value.longValue() >>> shiftAmount);
}

private static Double sqrtDouble(double x) {
return Math.sqrt(x);
}

private static Double sqrtInt(Long x) {
return sqrtDouble(x.doubleValue());
}

private static Double sqrtUint(UnsignedLong x) {
return sqrtDouble(x.doubleValue());
}

private static Comparable minList(List<Comparable> list) {
if (list.isEmpty()) {
throw new IllegalStateException("math.@min(list) argument must not be empty");
Expand Down
17 changes: 17 additions & 0 deletions extensions/src/main/java/dev/cel/extensions/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -334,6 +334,23 @@ Examples:
math.isFinite(0.0/0.0) // returns false
math.isFinite(1.2) // returns true

### Math.sqrt

Introduced at version: 2

Returns the square root of the numeric type provided as input. If the value is
NaN, the output is NaN. If the input is negative, the output is NaN.

math.sqrt(<double>) -> <double>
math.sqrt(<int>) -> <double>
math.sqrt(<uint>) -> <double>

Examples:

math.sqrt(81.0) // returns 9.0
math.sqrt(4) // returns 2.0
math.sqrt(-4) // returns NaN

## Protos

Extended macros and functions for proto manipulation.
Expand Down
Loading