Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
619f86c
Implement CEL optional conformance support
snazy Jul 17, 2026
003b61f
Code style and Java language level adoptions
snazy Jul 17, 2026
422dff3
Fix AstPruner / struct-pruning
snazy Jul 17, 2026
2f64300
Optimize protobuf and Jackson field access
snazy Jul 21, 2026
c64fe14
Centralize native value conversion
snazy Jul 21, 2026
3da9bcd
Specialize higher-arity overload evaluation
snazy Jul 21, 2026
ddcb5ac
Reduce allocations in CEL collections and protobuf access
snazy Jul 21, 2026
1245353
Prepare evaluator fast paths
snazy Jul 22, 2026
549b524
Add native-value planned evaluation
snazy Jul 22, 2026
aa3b21f
Extend native evaluation for lists, maps, and fields
snazy Jul 24, 2026
8110414
Optimize native planner aggregate and map consumers
snazy Jul 25, 2026
b381c67
Add representative performance benchmarks
snazy Jul 25, 2026
0e4bdd6
Plan exact Jackson object-valued map indexing
snazy Jul 27, 2026
2d28ef4
Plan constant regular expressions
snazy Jul 27, 2026
c530085
Reduce nested-quantifier allocation
snazy Jul 27, 2026
7fa317f
Add comparison benchmarks
snazy Jul 27, 2026
ee26759
Stabilize compilation and registry behavior
snazy Jul 27, 2026
30285a3
Update protobuf build metadata
snazy Jul 27, 2026
2433119
Reject unsupported Jackson map keys
snazy Jul 27, 2026
b0a7b5a
Support repeated protobuf message fields
snazy Jul 27, 2026
bd593b5
Clarify benchmark lifecycle compilation
snazy Jul 27, 2026
d623e4c
Protect source line offsets from mutation
snazy Jul 27, 2026
ae260e5
Update release support and protobuf compatibility
snazy Jul 27, 2026
124c928
Align regular expressions with the CEL specification
snazy Jul 27, 2026
15abd71
Extend Jackson type registration and array support
snazy Jul 27, 2026
22715cc
Fix core value lifecycle and collection behavior
snazy Jul 28, 2026
d8077c6
Fix compiler and interpreter state handling
snazy Jul 28, 2026
ceec767
Refresh API documentation and guides
snazy Jul 28, 2026
b3ebcb4
Package CEL-Java sources in cel-standalone
snazy Jul 28, 2026
dada5fe
Add resource limits
snazy Aug 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
544 changes: 110 additions & 434 deletions README.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Security Policy

The project is in a very early stage.

## Supported Versions

None, yet.
Only the latest published release receives security fixes. Older releases are not supported.

Reports affecting older releases are still welcome, but any fix will target the latest release.

## Reporting a Vulnerability

Expand Down
46 changes: 46 additions & 0 deletions benchmarks/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2026 The Authors of CEL-Java
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
`java-library`
alias(libs.plugins.jmh)
id("cel-conventions")
}

configurations.all {
exclude(group = "org.projectnessie.cel", module = "cel-generated-pb")
}

dependencies {
implementation(project(":cel-core"))
implementation(project(":cel-jackson3"))
implementation(project(":cel-generated-pb3"))
implementation(testFixtures(project(":cel-generated-pb3")))
implementation(libs.protobuf.java3)

testImplementation(platform(libs.junit.bom))
testImplementation(libs.bundles.junit.testing)
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")

jmhImplementation(libs.jmh.core)
jmhAnnotationProcessor(libs.jmh.generator.annprocess)
jmhImplementation(libs.cel.expr.java)
}

jmh { jmhVersion.set(libs.versions.jmh.get()) }

tasks.named("assemble") { dependsOn(tasks.named("jmhJar")) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
/*
* Copyright (C) 2026 The Authors of CEL-Java
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.projectnessie.cel;

import static org.projectnessie.cel.benchmark.RealWorldWorkloads.DAPR_IMPORTANT_HIT;
import static org.projectnessie.cel.benchmark.RealWorldWorkloads.DRA_UNRELATED_32;
import static org.projectnessie.cel.benchmark.RealWorldWorkloads.FLUX_READY_ALL_32;
import static org.projectnessie.cel.benchmark.RealWorldWorkloads.GATEWAY_HOSTNAME_16;
import static org.projectnessie.cel.benchmark.RealWorldWorkloads.GATEWAY_UNIQUE_16;
import static org.projectnessie.cel.benchmark.RealWorldWorkloads.IAM_PREFIX_HIT;
import static org.projectnessie.cel.benchmark.RealWorldWorkloads.NESSIE_DEFAULT_ALLOW;
import static org.projectnessie.cel.benchmark.RealWorldWorkloads.NESSIE_ROLES_MISS;
import static org.projectnessie.cel.benchmark.RealWorldWorkloads.ORG_BINDINGS_ALL_8_X_8;
import static org.projectnessie.cel.benchmark.RealWorldWorkloads.ORG_MEMBERS_ALL_32;
import static org.projectnessie.cel.benchmark.RealWorldWorkloads.POLARIS_CUTOFF_STOP;
import static org.projectnessie.cel.benchmark.RealWorldWorkloads.PROTOVALIDATE_NOTIFICATION_NONE;

import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Message;
import dev.cel.common.CelValidationException;
import dev.cel.common.types.CelType;
import dev.cel.common.types.ListType;
import dev.cel.common.types.SimpleType;
import dev.cel.common.types.StructTypeReference;
import dev.cel.compiler.CelCompiler;
import dev.cel.compiler.CelCompilerBuilder;
import dev.cel.compiler.CelCompilerFactory;
import dev.cel.parser.CelStandardMacro;
import dev.cel.runtime.CelEvaluationException;
import dev.cel.runtime.CelRuntime;
import dev.cel.runtime.CelRuntimeFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
import org.projectnessie.cel.benchmark.PreparedFixture;
import org.projectnessie.cel.benchmark.RealWorldHostFixtures;
import org.projectnessie.cel.benchmark.RealWorldProtoFixtures;
import org.projectnessie.cel.benchmark.RealWorldWorkloads;
import org.projectnessie.cel.benchmark.RealWorldWorkloads.Family;
import org.projectnessie.cel.benchmark.RealWorldWorkloads.Representation;
import org.projectnessie.cel.benchmark.RealWorldWorkloads.Scenario;

/**
* Compares Projectnessie CEL-Java with the independent {@code dev.cel} Java implementation.
*
* <p>The {@code dev.cel} implementation is maintained in the <a
* href="https://github.com/cel-expr/cel-java">{@code cel-expr/cel-java}</a> repository, formerly
* {@code google/cel-java}. It is not an upstream project from which Projectnessie CEL-Java is
* derived. Its exact release is pinned by the {@code celExprJava} entry in the Gradle version
* catalog.
*
* <h2>Measured boundary</h2>
*
* <p>This benchmark measures warm, single-threaded, complete-program evaluation. Each state
* constructs, checks, and plans its programs once during trial setup. Each benchmark invocation
* then:
*
* <ul>
* <li>evaluates the same CEL source expression or ordered expression sequence,
* <li>uses the same activation and, for Protobuf scenarios, the same generated message instance,
* <li>short-circuits an expression sequence after the first {@code true} result, and
* <li>materializes the result as a Java {@link Boolean}.
* </ul>
*
* <p>Checking, planning, environment construction, JVM startup, concurrency scaling, and
* application tail latency are outside the measured boundary.
*
* <h2>Compared configurations</h2>
*
* <p>Projectnessie is represented by two configurations. Both use {@link EvalOption#OptOptimize}.
* The {@code projectnessieExactNative*} methods additionally use exact input adapters and native
* planning. This is an opt-in fast path whose availability depends on the registered input types
* and expression shape. The {@code projectnessieGeneral*} methods use the general value-adaptation
* path and are included to make the contribution and scope of exact/native evaluation visible.
*
* <p>The {@code devCel*} methods use {@link CelCompilerFactory#standardCelCompilerBuilder()} with
* the standard macros and {@link CelRuntimeFactory#plannerRuntimeBuilder()}. No separate {@code
* dev.cel} AST optimization pass is applied. This represents the documented compiler and planner
* runtime path, not every configuration or feature offered by that implementation.
*
* <h2>Workload and dependency limitations</h2>
*
* <p>The scenarios are a deliberately bounded common subset of {@link RealWorldWorkloads}. They
* cover scalar and list host values, a dynamic map, constant regular expressions, and generated
* Protobuf fields, maps, repeated messages, presence, and nested comprehensions. Jackson scenarios
* are excluded because there is no equivalent {@code dev.cel} Jackson integration in this harness.
* The subset must not be treated as a general ranking across all CEL features, input systems,
* diagnostics, extensions, or application workloads.
*
* <p>Both implementations run in one JMH classpath and therefore use the Protobuf runtime version
* pinned by this project. This keeps the generated messages and runtime representation identical,
* but it does not reproduce the exact transitive dependency graph declared by the selected {@code
* dev.cel} release.
*
* <h2>Correctness and interpretation</h2>
*
* <p>Trial setup evaluates every compared configuration and rejects a scenario unless all results
* match its expected value. This is a semantic guard for the selected inputs, not a conformance
* test. Benchmark results should always identify the two library revisions, JDK, hardware, JMH
* configuration, selected scenarios, and compared modes. Results from this class establish only the
* relative cost of the documented warm-evaluation boundary.
*/
@Warmup(iterations = 3, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Fork(3)
@Threads(1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class DevCelRealWorldComparisonBench {
@State(Scope.Benchmark)
public static class HostState {
@Param({POLARIS_CUTOFF_STOP, NESSIE_DEFAULT_ALLOW, NESSIE_ROLES_MISS, DAPR_IMPORTANT_HIT})
public String scenarioId;

RealWorldProgramSet projectnessie;
DevCelPrograms devCel;

@Setup
public void setup() throws CelValidationException, CelEvaluationException {
Scenario scenario = RealWorldWorkloads.scenario(scenarioId);
PreparedFixture fixture = RealWorldHostFixtures.prepare(scenario);
projectnessie = RealWorldProgramSet.host(scenario, fixture);
devCel = DevCelPrograms.host(scenario, fixture);
verify(scenario, projectnessie.exactNative(), projectnessie.general(), devCel.evaluate());
}
}

@State(Scope.Benchmark)
public static class ProtoState {
@Param({
IAM_PREFIX_HIT,
DRA_UNRELATED_32,
ORG_MEMBERS_ALL_32,
ORG_BINDINGS_ALL_8_X_8,
GATEWAY_HOSTNAME_16,
GATEWAY_UNIQUE_16,
FLUX_READY_ALL_32,
PROTOVALIDATE_NOTIFICATION_NONE
})
public String scenarioId;

RealWorldProgramSet projectnessie;
DevCelPrograms devCel;

@Setup
public void setup() throws CelValidationException, CelEvaluationException {
Scenario scenario = RealWorldWorkloads.scenario(scenarioId);
PreparedFixture fixture = RealWorldProtoFixtures.prepare(scenario);
Message[] messageTypes = RealWorldProtoFixtures.registeredTypes(scenario.family());
projectnessie = RealWorldProgramSet.protobuf(scenario, fixture, messageTypes);
devCel = DevCelPrograms.protobuf(scenario, fixture, messageTypes);
verify(scenario, projectnessie.exactNative(), projectnessie.general(), devCel.evaluate());
}
}

@Benchmark
public Boolean projectnessieExactNativeHost(HostState state) {
return state.projectnessie.exactNative();
}

@Benchmark
public Boolean projectnessieGeneralHost(HostState state) {
return state.projectnessie.general();
}

@Benchmark
public Boolean devCelHost(HostState state) throws CelEvaluationException {
return state.devCel.evaluate();
}

@Benchmark
public Boolean projectnessieExactNativeProto(ProtoState state) {
return state.projectnessie.exactNative();
}

@Benchmark
public Boolean projectnessieGeneralProto(ProtoState state) {
return state.projectnessie.general();
}

@Benchmark
public Boolean devCelProto(ProtoState state) throws CelEvaluationException {
return state.devCel.evaluate();
}

private static void verify(
Scenario scenario,
Boolean projectnessieExactNative,
Boolean projectnessieGeneral,
Boolean devCel) {
if (projectnessieExactNative != scenario.expected()
|| projectnessieGeneral != scenario.expected()
|| devCel != scenario.expected()) {
throw new IllegalStateException(
scenario.id()
+ ": expected="
+ scenario.expected()
+ ", projectnessieExactNative="
+ projectnessieExactNative
+ ", projectnessieGeneral="
+ projectnessieGeneral
+ ", devCel="
+ devCel);
}
}

private record DevCelPrograms(List<CelRuntime.Program> programs, Map<String, Object> activation) {
private DevCelPrograms(List<CelRuntime.Program> programs, Map<String, Object> activation) {
this.programs = List.copyOf(programs);
this.activation = activation;
}

static DevCelPrograms host(Scenario scenario, PreparedFixture fixture)
throws CelValidationException, CelEvaluationException {
CelCompilerBuilder compiler = standardCompiler();
addHostVariables(compiler, scenario.family());
return create(
compiler.build(),
CelRuntimeFactory.plannerRuntimeBuilder().build(),
scenario.expressions(Representation.HOST),
fixture.activation());
}

static DevCelPrograms protobuf(
Scenario scenario, PreparedFixture fixture, Message[] messageTypes)
throws CelValidationException, CelEvaluationException {
List<Descriptor> descriptors =
Arrays.stream(messageTypes).map(Message::getDescriptorForType).toList();
Descriptor rootDescriptor = messageTypes[0].getDescriptorForType();
CelType rootType = StructTypeReference.create(rootDescriptor.getFullName());
CelCompilerBuilder compiler =
standardCompiler()
.addMessageTypes(descriptors)
.addVar(variableName(scenario.family()), variableType(scenario.family(), rootType));
CelRuntime runtime =
CelRuntimeFactory.plannerRuntimeBuilder().addMessageTypes(descriptors).build();
return create(
compiler.build(),
runtime,
scenario.expressions(Representation.PROTOBUF),
fixture.activation());
}

private static CelCompilerBuilder standardCompiler() {
return CelCompilerFactory.standardCelCompilerBuilder()
.setStandardMacros(CelStandardMacro.STANDARD_MACROS);
}

private static DevCelPrograms create(
CelCompiler compiler,
CelRuntime runtime,
List<String> expressions,
Map<String, Object> activation)
throws CelValidationException, CelEvaluationException {
List<CelRuntime.Program> programs = new ArrayList<>(expressions.size());
for (String expression : expressions) {
programs.add(runtime.createProgram(compiler.compile(expression).getAst()));
}
return new DevCelPrograms(programs, activation);
}

Boolean evaluate() throws CelEvaluationException {
for (CelRuntime.Program program : programs) {
if ((Boolean) program.eval(activation)) {
return true;
}
}
return false;
}

private static void addHostVariables(CelCompilerBuilder compiler, Family family) {
switch (family) {
case POLARIS ->
compiler
.addVar("ref", SimpleType.STRING)
.addVar("commits", SimpleType.INT)
.addVar("ageMinutes", SimpleType.INT)
.addVar("ageHours", SimpleType.INT)
.addVar("ageDays", SimpleType.INT);
case NESSIE ->
compiler
.addVar("op", SimpleType.STRING)
.addVar("role", SimpleType.STRING)
.addVar("roles", ListType.create(SimpleType.STRING))
.addVar("ref", SimpleType.STRING)
.addVar("path", SimpleType.STRING)
.addVar("contentType", SimpleType.STRING);
case DAPR_HOST -> compiler.addVar("event", SimpleType.DYN);
default -> throw new IllegalArgumentException("Unsupported host family " + family);
}
}

private static String variableName(Family family) {
return switch (family) {
case IAM, ORGANIZATION, FLUX -> "resource";
case GATEWAY -> "self";
case DRA -> "device";
case DAPR_TYPED -> "event";
case PROTOVALIDATE_BOOKING, PROTOVALIDATE_INTERVAL, PROTOVALIDATE_NOTIFICATION -> "this";
default -> throw new IllegalArgumentException("Unsupported protobuf family " + family);
};
}

private static CelType variableType(Family family, CelType rootType) {
return family == Family.GATEWAY ? ListType.create(rootType) : rootType;
}
}
}
Loading
Loading