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
11 changes: 11 additions & 0 deletions runtime/planner/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
load("@rules_java//java:defs.bzl", "java_library")

package(
default_applicable_licenses = ["//:license"],
default_visibility = ["//:internal"],
)

java_library(
name = "program_planner",
exports = ["//runtime/src/main/java/dev/cel/runtime/planner:program_planner"],
)
58 changes: 58 additions & 0 deletions runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
load("@rules_java//java:defs.bzl", "java_library")

package(
default_applicable_licenses = ["//:license"],
default_visibility = [
"//runtime/planner:__pkg__",
],
)

java_library(
name = "program_planner",
srcs = ["ProgramPlanner.java"],
tags = [
],
deps = [
":eval_const",
":planned_program",
"//:auto_value",
"//common:cel_ast",
"//common/annotations",
"//common/ast",
"//common/types:type_providers",
"//runtime:evaluation_exception",
"//runtime:evaluation_exception_builder",
"//runtime:interpretable",
"//runtime:program",
"@maven//:com_google_code_findbugs_annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
],
)

java_library(
name = "planned_program",
srcs = ["PlannedProgram.java"],
deps = [
"//:auto_value",
"//runtime:evaluation_exception",
"//runtime:function_resolver",
"//runtime:interpretable",
"//runtime:program",
"@maven//:com_google_errorprone_error_prone_annotations",
],
)

java_library(
name = "eval_const",
srcs = ["EvalConstant.java"],
deps = [
"//common/values",
"//common/values:cel_byte_string",
"//runtime:evaluation_listener",
"//runtime:function_resolver",
"//runtime:interpretable",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
],
)
116 changes: 116 additions & 0 deletions runtime/src/main/java/dev/cel/runtime/planner/EvalConstant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2025 Google LLC
//
// 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
//
// https://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 dev.cel.runtime.planner;

import com.google.common.primitives.UnsignedLong;
import com.google.errorprone.annotations.Immutable;
import dev.cel.common.values.CelByteString;
import dev.cel.common.values.NullValue;
import dev.cel.runtime.CelEvaluationListener;
import dev.cel.runtime.CelFunctionResolver;
import dev.cel.runtime.GlobalResolver;
import dev.cel.runtime.Interpretable;

@Immutable
final class EvalConstant implements Interpretable {

// Pre-allocation of common constants
private static final EvalConstant NULL_VALUE = new EvalConstant(NullValue.NULL_VALUE);
private static final EvalConstant TRUE = new EvalConstant(true);
private static final EvalConstant FALSE = new EvalConstant(false);
private static final EvalConstant ZERO = new EvalConstant(0L);
private static final EvalConstant ONE = new EvalConstant(1L);
private static final EvalConstant UNSIGNED_ZERO = new EvalConstant(UnsignedLong.ZERO);
private static final EvalConstant UNSIGNED_ONE = new EvalConstant(UnsignedLong.ONE);
private static final EvalConstant EMPTY_STRING = new EvalConstant("");
private static final EvalConstant EMPTY_BYTES = new EvalConstant(CelByteString.EMPTY);

@SuppressWarnings("Immutable") // Known CEL constants that aren't mutated are stored
private final Object constant;

@Override
public Object eval(GlobalResolver resolver) {
return constant;
}

@Override
public Object eval(GlobalResolver resolver, CelEvaluationListener listener) {
return constant;
}

@Override
public Object eval(GlobalResolver resolver, CelFunctionResolver lateBoundFunctionResolver) {
return constant;
}

@Override
public Object eval(
GlobalResolver resolver,
CelFunctionResolver lateBoundFunctionResolver,
CelEvaluationListener listener) {
return constant;
}

static EvalConstant create(boolean value) {
return value ? TRUE : FALSE;
}

static EvalConstant create(String value) {
if (value.isEmpty()) {
return EMPTY_STRING;
}

return new EvalConstant(value);
}

static EvalConstant create(long value) {
if (value == 0L) {
return ZERO;
} else if (value == 1L) {
return ONE;
}

return new EvalConstant(Long.valueOf(value));
}

static EvalConstant create(double value) {
return new EvalConstant(Double.valueOf(value));
}

static EvalConstant create(UnsignedLong unsignedLong) {
if (unsignedLong.longValue() == 0L) {
return UNSIGNED_ZERO;
} else if (unsignedLong.longValue() == 1L) {
return UNSIGNED_ONE;
}

return new EvalConstant(unsignedLong);
}

static EvalConstant create(NullValue unused) {
return NULL_VALUE;
}

static EvalConstant create(CelByteString byteString) {
if (byteString.isEmpty()) {
return EMPTY_BYTES;
}
return new EvalConstant(byteString);
}

private EvalConstant(Object constant) {
this.constant = constant;
}
}
50 changes: 50 additions & 0 deletions runtime/src/main/java/dev/cel/runtime/planner/PlannedProgram.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2025 Google LLC
//
// 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
//
// https://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 dev.cel.runtime.planner;

import com.google.auto.value.AutoValue;
import com.google.errorprone.annotations.Immutable;
import dev.cel.runtime.CelEvaluationException;
import dev.cel.runtime.CelFunctionResolver;
import dev.cel.runtime.GlobalResolver;
import dev.cel.runtime.Interpretable;
import dev.cel.runtime.Program;
import java.util.Map;

@Immutable
@AutoValue
abstract class PlannedProgram implements Program {
abstract Interpretable interpretable();

@Override
public Object eval() throws CelEvaluationException {
return interpretable().eval(GlobalResolver.EMPTY);
}

@Override
public Object eval(Map<String, ?> mapValue) throws CelEvaluationException {
throw new UnsupportedOperationException("Not yet implemented");
}

@Override
public Object eval(Map<String, ?> mapValue, CelFunctionResolver lateBoundFunctionResolver)
throws CelEvaluationException {
throw new UnsupportedOperationException("Late bound functions not supported yet");
}

static Program create(Interpretable interpretable) {
return new AutoValue_PlannedProgram(interpretable);
}
}
103 changes: 103 additions & 0 deletions runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2025 Google LLC
//
// 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
//
// https://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 dev.cel.runtime.planner;

import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableMap;
import javax.annotation.concurrent.ThreadSafe;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.annotations.Internal;
import dev.cel.common.ast.CelConstant;
import dev.cel.common.ast.CelExpr;
import dev.cel.common.ast.CelReference;
import dev.cel.common.types.CelType;
import dev.cel.runtime.CelEvaluationException;
import dev.cel.runtime.CelEvaluationExceptionBuilder;
import dev.cel.runtime.Interpretable;
import dev.cel.runtime.Program;

/**
* {@code ProgramPlanner} resolves functions, types, and identifiers at plan time given a
* parsed-only or a type-checked expression.
*/
@ThreadSafe
@Internal
public final class ProgramPlanner {

/**
* Plans a {@link Program} from the provided parsed-only or type-checked {@link
* CelAbstractSyntaxTree}.
*/
public Program plan(CelAbstractSyntaxTree ast) throws CelEvaluationException {
Interpretable plannedInterpretable;
try {
plannedInterpretable = plan(ast.getExpr(), PlannerContext.create(ast));
} catch (RuntimeException e) {
throw CelEvaluationExceptionBuilder.newBuilder(e.getMessage()).setCause(e).build();
}

return PlannedProgram.create(plannedInterpretable);
}

private Interpretable plan(CelExpr celExpr, PlannerContext unused) {
switch (celExpr.getKind()) {
case CONSTANT:
return planConstant(celExpr.constant());
case NOT_SET:
throw new UnsupportedOperationException("Unsupported kind: " + celExpr.getKind());
default:
throw new IllegalArgumentException("Not yet implemented kind: " + celExpr.getKind());
}
}

private Interpretable planConstant(CelConstant celConstant) {
switch (celConstant.getKind()) {
case NULL_VALUE:
return EvalConstant.create(celConstant.nullValue());
case BOOLEAN_VALUE:
return EvalConstant.create(celConstant.booleanValue());
case INT64_VALUE:
return EvalConstant.create(celConstant.int64Value());
case UINT64_VALUE:
return EvalConstant.create(celConstant.uint64Value());
case DOUBLE_VALUE:
return EvalConstant.create(celConstant.doubleValue());
case STRING_VALUE:
return EvalConstant.create(celConstant.stringValue());
case BYTES_VALUE:
return EvalConstant.create(celConstant.bytesValue());
default:
throw new IllegalStateException("Unsupported kind: " + celConstant.getKind());
}
}

@AutoValue
abstract static class PlannerContext {

abstract ImmutableMap<Long, CelReference> referenceMap();

abstract ImmutableMap<Long, CelType> typeMap();

private static PlannerContext create(CelAbstractSyntaxTree ast) {
return new AutoValue_ProgramPlanner_PlannerContext(ast.getReferenceMap(), ast.getTypeMap());
}
}

public static ProgramPlanner newPlanner() {
return new ProgramPlanner();
}

private ProgramPlanner() {}
}
42 changes: 42 additions & 0 deletions runtime/src/test/java/dev/cel/runtime/planner/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
load("@rules_java//java:defs.bzl", "java_library")
load("//:testing.bzl", "junit4_test_suites")

package(
default_applicable_licenses = ["//:license"],
default_testonly = True,
)

java_library(
name = "tests",
testonly = 1,
srcs = glob(
["*.java"],
),
deps = [
"//:java_truth",
"//common:cel_ast",
"//common:cel_source",
"//common/ast",
"//common/values",
"//common/values:cel_byte_string",
"//compiler",
"//compiler:compiler_builder",
"//runtime",
"//runtime:program",
"//runtime/planner:program_planner",
"@maven//:com_google_guava_guava",
"@maven//:com_google_testparameterinjector_test_parameter_injector",
"@maven//:junit_junit",
],
)

junit4_test_suites(
name = "test_suites",
sizes = [
"small",
],
src_dir = "src/test/java",
deps = [
":tests",
],
)
Loading
Loading