From 3725bd7247979692f7e4ec524420b4c47b9c270c Mon Sep 17 00:00:00 2001 From: Sokwhan Huh Date: Fri, 5 Dec 2025 16:21:12 -0800 Subject: [PATCH] Track source of evaluation errors in planner, surface error location PiperOrigin-RevId: 840916857 --- common/BUILD.bazel | 7 -- common/exceptions/BUILD.bazel | 48 +++++++++ .../src/main/java/dev/cel/common/BUILD.bazel | 19 +--- .../java/dev/cel/common/CelException.java | 5 - .../dev/cel/common/exceptions/BUILD.bazel | 99 +++++++++++++++++++ .../CelAttributeNotFoundException.java | 56 +++++++++++ .../exceptions/CelBadFormatException.java | 31 ++++++ .../exceptions/CelDivideByZeroException.java | 20 ++-- .../CelIndexOutOfBoundsException.java | 27 +++++ .../CelInvalidArgumentException.java | 27 +++++ .../CelNumericOverflowException.java | 34 +++++++ .../{ => exceptions}/CelRuntimeException.java | 13 ++- .../java/dev/cel/common/internal/BUILD.bazel | 13 +-- .../cel/common/internal/DateTimeHelpers.java | 7 +- .../dev/cel/common/internal/ProtoAdapter.java | 7 +- .../cel/common/internal/ProtoLiteAdapter.java | 11 +-- .../cel/common/values/CelValueConverter.java | 2 - .../dev/cel/common/values/ErrorValue.java | 6 +- .../common/values/CelValueConverterTest.java | 9 -- .../dev/cel/common/values/ErrorValueTest.java | 6 +- .../main/java/dev/cel/extensions/BUILD.bazel | 1 + .../dev/cel/extensions/CelMathExtensions.java | 9 +- .../test/java/dev/cel/extensions/BUILD.bazel | 3 +- .../CelComprehensionsExtensionsTest.java | 8 +- runtime/BUILD.bazel | 8 +- .../src/main/java/dev/cel/runtime/BUILD.bazel | 32 +++--- .../CelEvaluationExceptionBuilder.java | 15 ++- .../runtime/CelValueRuntimeTypeProvider.java | 15 +-- .../dev/cel/runtime/DefaultInterpreter.java | 2 +- .../runtime/DescriptorMessageProvider.java | 22 +---- .../java/dev/cel/runtime/RuntimeEquality.java | 7 +- .../java/dev/cel/runtime/RuntimeHelpers.java | 41 ++++---- .../java/dev/cel/runtime/planner/BUILD.bazel | 52 +++++++++- .../cel/runtime/planner/ErrorMetadata.java | 52 ++++++++++ .../java/dev/cel/runtime/planner/EvalAnd.java | 14 +-- .../cel/runtime/planner/EvalAttribute.java | 11 +-- .../cel/runtime/planner/EvalConditional.java | 9 +- .../dev/cel/runtime/planner/EvalConstant.java | 4 +- .../cel/runtime/planner/EvalCreateList.java | 13 ++- .../cel/runtime/planner/EvalCreateMap.java | 14 +-- .../cel/runtime/planner/EvalCreateStruct.java | 7 +- .../dev/cel/runtime/planner/EvalHelpers.java | 22 ++++- .../java/dev/cel/runtime/planner/EvalOr.java | 14 +-- .../dev/cel/runtime/planner/EvalUnary.java | 19 ++-- .../cel/runtime/planner/EvalVarArgsCall.java | 24 +++-- .../cel/runtime/planner/EvalZeroArity.java | 11 +-- .../runtime/planner/PlannedInterpretable.java | 31 ++++++ .../cel/runtime/planner/PlannedProgram.java | 25 ++--- .../cel/runtime/planner/ProgramPlanner.java | 53 +++++----- .../runtime/planner/StrictErrorException.java | 42 ++++++++ .../dev/cel/runtime/standard/AddOperator.java | 10 +- .../java/dev/cel/runtime/standard/BUILD.bazel | 98 ++++++------------ .../cel/runtime/standard/BoolFunction.java | 11 +-- .../cel/runtime/standard/DivideOperator.java | 6 +- .../cel/runtime/standard/DoubleFunction.java | 5 +- .../runtime/standard/DurationFunction.java | 7 +- .../dev/cel/runtime/standard/IntFunction.java | 18 ++-- .../cel/runtime/standard/MatchesFunction.java | 7 +- .../cel/runtime/standard/ModuloOperator.java | 6 +- .../runtime/standard/MultiplyOperator.java | 10 +- .../cel/runtime/standard/NegateOperator.java | 6 +- .../cel/runtime/standard/StringFunction.java | 15 +-- .../runtime/standard/SubtractOperator.java | 10 +- .../runtime/standard/TimestampFunction.java | 7 +- .../cel/runtime/standard/UintFunction.java | 26 ++--- .../src/test/java/dev/cel/runtime/BUILD.bazel | 5 +- .../CelEvaluationExceptionBuilderTest.java | 8 +- .../cel/runtime/CelRuntimeLegacyImplTest.java | 3 +- .../DescriptorMessageProviderTest.java | 8 +- .../ProtoMessageRuntimeEqualityTest.java | 2 +- .../ProtoMessageRuntimeHelpersTest.java | 21 ++-- .../java/dev/cel/runtime/planner/BUILD.bazel | 1 + .../runtime/planner/ProgramPlannerTest.java | 20 ++-- 73 files changed, 875 insertions(+), 462 deletions(-) create mode 100644 common/exceptions/BUILD.bazel create mode 100644 common/src/main/java/dev/cel/common/exceptions/BUILD.bazel create mode 100644 common/src/main/java/dev/cel/common/exceptions/CelAttributeNotFoundException.java create mode 100644 common/src/main/java/dev/cel/common/exceptions/CelBadFormatException.java rename runtime/src/main/java/dev/cel/runtime/standard/ArithmeticHelpers.java => common/src/main/java/dev/cel/common/exceptions/CelDivideByZeroException.java (59%) create mode 100644 common/src/main/java/dev/cel/common/exceptions/CelIndexOutOfBoundsException.java create mode 100644 common/src/main/java/dev/cel/common/exceptions/CelInvalidArgumentException.java create mode 100644 common/src/main/java/dev/cel/common/exceptions/CelNumericOverflowException.java rename common/src/main/java/dev/cel/common/{ => exceptions}/CelRuntimeException.java (80%) create mode 100644 runtime/src/main/java/dev/cel/runtime/planner/ErrorMetadata.java create mode 100644 runtime/src/main/java/dev/cel/runtime/planner/PlannedInterpretable.java create mode 100644 runtime/src/main/java/dev/cel/runtime/planner/StrictErrorException.java diff --git a/common/BUILD.bazel b/common/BUILD.bazel index 815d8a1da..75459597b 100644 --- a/common/BUILD.bazel +++ b/common/BUILD.bazel @@ -53,13 +53,6 @@ java_library( exports = ["//common/src/main/java/dev/cel/common:mutable_source"], ) -java_library( - name = "runtime_exception", - # used_by_android - visibility = ["//:internal"], - exports = ["//common/src/main/java/dev/cel/common:runtime_exception"], -) - java_library( name = "proto_json_adapter", exports = ["//common/src/main/java/dev/cel/common:proto_json_adapter"], diff --git a/common/exceptions/BUILD.bazel b/common/exceptions/BUILD.bazel new file mode 100644 index 000000000..7547ddc18 --- /dev/null +++ b/common/exceptions/BUILD.bazel @@ -0,0 +1,48 @@ +load("@rules_java//java:defs.bzl", "java_library") + +package( + default_applicable_licenses = ["//:license"], + default_visibility = ["//:internal"], +) + +java_library( + name = "runtime_exception", + # used_by_android + exports = ["//common/src/main/java/dev/cel/common/exceptions:runtime_exception"], +) + +java_library( + name = "attribute_not_found", + # used_by_android + exports = ["//common/src/main/java/dev/cel/common/exceptions:attribute_not_found"], +) + +java_library( + name = "divide_by_zero", + # used_by_android + exports = ["//common/src/main/java/dev/cel/common/exceptions:divide_by_zero"], +) + +java_library( + name = "index_out_of_bounds", + # used_by_android + exports = ["//common/src/main/java/dev/cel/common/exceptions:index_out_of_bounds"], +) + +java_library( + name = "bad_format", + # used_by_android + exports = ["//common/src/main/java/dev/cel/common/exceptions:bad_format"], +) + +java_library( + name = "numeric_overflow", + # used_by_android + exports = ["//common/src/main/java/dev/cel/common/exceptions:numeric_overflow"], +) + +java_library( + name = "invalid_argument", + # used_by_android + exports = ["//common/src/main/java/dev/cel/common/exceptions:invalid_argument"], +) diff --git a/common/src/main/java/dev/cel/common/BUILD.bazel b/common/src/main/java/dev/cel/common/BUILD.bazel index 7cd10f392..0050a2a7e 100644 --- a/common/src/main/java/dev/cel/common/BUILD.bazel +++ b/common/src/main/java/dev/cel/common/BUILD.bazel @@ -172,18 +172,6 @@ java_library( ], ) -java_library( - name = "runtime_exception", - srcs = ["CelRuntimeException.java"], - # used_by_android - tags = [ - ], - deps = [ - ":error_codes", - "//common/annotations", - ], -) - java_library( name = "mutable_ast", srcs = ["CelMutableAst.java"], @@ -205,7 +193,6 @@ java_library( ], deps = [ ":cel_source", - "//:auto_value", "//common/ast:mutable_expr", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", @@ -249,7 +236,6 @@ java_library( ":source", ":source_location", "//:auto_value", - "//common/annotations", "//common/ast", "//common/internal", "@maven//:com_google_errorprone_error_prone_annotations", @@ -360,8 +346,5 @@ java_library( srcs = ["Operator.java"], tags = [ ], - deps = [ - "//common/ast", - "@maven//:com_google_guava_guava", - ], + deps = ["@maven//:com_google_guava_guava"], ) diff --git a/common/src/main/java/dev/cel/common/CelException.java b/common/src/main/java/dev/cel/common/CelException.java index 9d80a9ba1..55c8623a4 100644 --- a/common/src/main/java/dev/cel/common/CelException.java +++ b/common/src/main/java/dev/cel/common/CelException.java @@ -27,11 +27,6 @@ public CelException(String message, Throwable cause) { super(message, cause); } - public CelException(String message, CelErrorCode errorCode) { - super(message); - this.errorCode = errorCode; - } - public CelException(String message, Throwable cause, CelErrorCode errorCode) { super(message, cause); this.errorCode = errorCode; diff --git a/common/src/main/java/dev/cel/common/exceptions/BUILD.bazel b/common/src/main/java/dev/cel/common/exceptions/BUILD.bazel new file mode 100644 index 000000000..b78034f6a --- /dev/null +++ b/common/src/main/java/dev/cel/common/exceptions/BUILD.bazel @@ -0,0 +1,99 @@ +load("@rules_java//java:defs.bzl", "java_library") + +package( + default_applicable_licenses = ["//:license"], + default_visibility = [ + "//common/exceptions:__pkg__", + "//publish:__pkg__", + ], +) + +java_library( + name = "runtime_exception", + srcs = ["CelRuntimeException.java"], + # used_by_android + tags = [ + ], + deps = [ + "//common:error_codes", + "//common/annotations", + ], +) + +java_library( + name = "attribute_not_found", + srcs = ["CelAttributeNotFoundException.java"], + # used_by_android + tags = [ + ], + deps = [ + "//common:error_codes", + "//common/annotations", + "//common/exceptions:runtime_exception", + ], +) + +java_library( + name = "divide_by_zero", + srcs = ["CelDivideByZeroException.java"], + # used_by_android + tags = [ + ], + deps = [ + "//common:error_codes", + "//common/annotations", + "//common/exceptions:runtime_exception", + ], +) + +java_library( + name = "index_out_of_bounds", + srcs = ["CelIndexOutOfBoundsException.java"], + # used_by_android + tags = [ + ], + deps = [ + "//common:error_codes", + "//common/annotations", + "//common/exceptions:runtime_exception", + ], +) + +java_library( + name = "bad_format", + srcs = ["CelBadFormatException.java"], + # used_by_android + tags = [ + ], + deps = [ + "//common:error_codes", + "//common/annotations", + "//common/exceptions:runtime_exception", + ], +) + +java_library( + name = "numeric_overflow", + srcs = ["CelNumericOverflowException.java"], + # used_by_android + tags = [ + ], + deps = [ + "//common:error_codes", + "//common/annotations", + "//common/exceptions:runtime_exception", + ], +) + +java_library( + name = "invalid_argument", + srcs = ["CelInvalidArgumentException.java"], + # used_by_android + tags = [ + ], + deps = [ + "//common:error_codes", + "//common/annotations", + "//common/exceptions:runtime_exception", + ], +) diff --git a/common/src/main/java/dev/cel/common/exceptions/CelAttributeNotFoundException.java b/common/src/main/java/dev/cel/common/exceptions/CelAttributeNotFoundException.java new file mode 100644 index 000000000..6204fd2fe --- /dev/null +++ b/common/src/main/java/dev/cel/common/exceptions/CelAttributeNotFoundException.java @@ -0,0 +1,56 @@ +// 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.common.exceptions; + +import dev.cel.common.CelErrorCode; +import dev.cel.common.annotations.Internal; +import java.util.Arrays; +import java.util.Collection; + +/** Indicates an attempt to access a map or object using an invalid attribute or key. */ +@Internal +public final class CelAttributeNotFoundException extends CelRuntimeException { + + public static CelAttributeNotFoundException of(String message) { + return new CelAttributeNotFoundException(message); + } + + public static CelAttributeNotFoundException forMissingMapKey(String key) { + return new CelAttributeNotFoundException(String.format("key '%s' is not present in map.", key)); + } + + public static CelAttributeNotFoundException forFieldResolution(String... fields) { + return forFieldResolution(Arrays.asList(fields)); + } + + public static CelAttributeNotFoundException forFieldResolution(Collection fields) { + return new CelAttributeNotFoundException(formatErrorMessage(fields)); + } + + private static String formatErrorMessage(Collection fields) { + String maybePlural = ""; + if (fields.size() > 1) { + maybePlural = "s"; + } + + return String.format( + "Error resolving field%s '%s'. Field selections must be performed on messages or maps.", + maybePlural, String.join(", ", fields)); + } + + private CelAttributeNotFoundException(String message) { + super(message, CelErrorCode.ATTRIBUTE_NOT_FOUND); + } +} diff --git a/common/src/main/java/dev/cel/common/exceptions/CelBadFormatException.java b/common/src/main/java/dev/cel/common/exceptions/CelBadFormatException.java new file mode 100644 index 000000000..ba4db602a --- /dev/null +++ b/common/src/main/java/dev/cel/common/exceptions/CelBadFormatException.java @@ -0,0 +1,31 @@ +// 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.common.exceptions; + +import dev.cel.common.CelErrorCode; +import dev.cel.common.annotations.Internal; + +/** Indicates that a data conversion failed due to a mismatch in the format specification. */ +@Internal +public final class CelBadFormatException extends CelRuntimeException { + + public CelBadFormatException(Throwable cause) { + super(cause, CelErrorCode.BAD_FORMAT); + } + + public CelBadFormatException(String errorMessage) { + super(errorMessage, CelErrorCode.BAD_FORMAT); + } +} diff --git a/runtime/src/main/java/dev/cel/runtime/standard/ArithmeticHelpers.java b/common/src/main/java/dev/cel/common/exceptions/CelDivideByZeroException.java similarity index 59% rename from runtime/src/main/java/dev/cel/runtime/standard/ArithmeticHelpers.java rename to common/src/main/java/dev/cel/common/exceptions/CelDivideByZeroException.java index 310e9401f..c507797c5 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/ArithmeticHelpers.java +++ b/common/src/main/java/dev/cel/common/exceptions/CelDivideByZeroException.java @@ -12,20 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -package dev.cel.runtime.standard; +package dev.cel.common.exceptions; import dev.cel.common.CelErrorCode; +import dev.cel.common.annotations.Internal; -final class ArithmeticHelpers { +/** Indicates that a division by zero occurred. */ +@Internal +public final class CelDivideByZeroException extends CelRuntimeException { - static CelErrorCode getArithmeticErrorCode(ArithmeticException e) { - String exceptionMessage = e.getMessage(); - // The two known cases for an arithmetic exception is divide by zero and overflow. - if (exceptionMessage.equals("/ by zero")) { - return CelErrorCode.DIVIDE_BY_ZERO; - } - return CelErrorCode.NUMERIC_OVERFLOW; + public CelDivideByZeroException() { + super("/ by zero", CelErrorCode.DIVIDE_BY_ZERO); } - private ArithmeticHelpers() {} + public CelDivideByZeroException(Throwable cause) { + super(cause, CelErrorCode.DIVIDE_BY_ZERO); + } } diff --git a/common/src/main/java/dev/cel/common/exceptions/CelIndexOutOfBoundsException.java b/common/src/main/java/dev/cel/common/exceptions/CelIndexOutOfBoundsException.java new file mode 100644 index 000000000..72a6cd1c0 --- /dev/null +++ b/common/src/main/java/dev/cel/common/exceptions/CelIndexOutOfBoundsException.java @@ -0,0 +1,27 @@ +// 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.common.exceptions; + +import dev.cel.common.CelErrorCode; +import dev.cel.common.annotations.Internal; + +/** Indicates that a list index access was attempted using an index that is out of bounds. */ +@Internal +public final class CelIndexOutOfBoundsException extends CelRuntimeException { + + public CelIndexOutOfBoundsException(Object index) { + super("Index out of bounds: " + index, CelErrorCode.INDEX_OUT_OF_BOUNDS); + } +} diff --git a/common/src/main/java/dev/cel/common/exceptions/CelInvalidArgumentException.java b/common/src/main/java/dev/cel/common/exceptions/CelInvalidArgumentException.java new file mode 100644 index 000000000..41358bb79 --- /dev/null +++ b/common/src/main/java/dev/cel/common/exceptions/CelInvalidArgumentException.java @@ -0,0 +1,27 @@ +// 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.common.exceptions; + +import dev.cel.common.CelErrorCode; +import dev.cel.common.annotations.Internal; + +/** Indicates that an invalid argument was supplied to a function. */ +@Internal +public final class CelInvalidArgumentException extends CelRuntimeException { + + public CelInvalidArgumentException(Throwable cause) { + super(cause, CelErrorCode.INVALID_ARGUMENT); + } +} diff --git a/common/src/main/java/dev/cel/common/exceptions/CelNumericOverflowException.java b/common/src/main/java/dev/cel/common/exceptions/CelNumericOverflowException.java new file mode 100644 index 000000000..78fbe807e --- /dev/null +++ b/common/src/main/java/dev/cel/common/exceptions/CelNumericOverflowException.java @@ -0,0 +1,34 @@ +// 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.common.exceptions; + +import dev.cel.common.CelErrorCode; +import dev.cel.common.annotations.Internal; + +/** + * Indicates that a numeric overflow occurred due to arithmetic operations or conversions resulting + * in a value outside the representable range. + */ +@Internal +public final class CelNumericOverflowException extends CelRuntimeException { + + public CelNumericOverflowException(String message) { + super(message, CelErrorCode.NUMERIC_OVERFLOW); + } + + public CelNumericOverflowException(Throwable cause) { + super(cause, CelErrorCode.NUMERIC_OVERFLOW); + } +} diff --git a/common/src/main/java/dev/cel/common/CelRuntimeException.java b/common/src/main/java/dev/cel/common/exceptions/CelRuntimeException.java similarity index 80% rename from common/src/main/java/dev/cel/common/CelRuntimeException.java rename to common/src/main/java/dev/cel/common/exceptions/CelRuntimeException.java index 6f194c474..c87e192bd 100644 --- a/common/src/main/java/dev/cel/common/CelRuntimeException.java +++ b/common/src/main/java/dev/cel/common/exceptions/CelRuntimeException.java @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -package dev.cel.common; +package dev.cel.common.exceptions; +import dev.cel.common.CelErrorCode; import dev.cel.common.annotations.Internal; /** @@ -21,14 +22,16 @@ * *

Note: This is not to be confused with the notion of CEL Runtime. Use {@code * CelEvaluationException} instead to signify an evaluation error. - * - *

TODO: Make this class abstract and define specific exception classes that - * corresponds to the CelErrorCode. */ @Internal -public class CelRuntimeException extends RuntimeException { +public abstract class CelRuntimeException extends RuntimeException { private final CelErrorCode errorCode; + CelRuntimeException(String errorMessage, CelErrorCode errorCode) { + super(errorMessage); + this.errorCode = errorCode; + } + public CelRuntimeException(Throwable cause, CelErrorCode errorCode) { super(cause); this.errorCode = errorCode; diff --git a/common/src/main/java/dev/cel/common/internal/BUILD.bazel b/common/src/main/java/dev/cel/common/internal/BUILD.bazel index 690b1cc75..c96a3dcfd 100644 --- a/common/src/main/java/dev/cel/common/internal/BUILD.bazel +++ b/common/src/main/java/dev/cel/common/internal/BUILD.bazel @@ -173,11 +173,9 @@ java_library( ":proto_lite_adapter", ":proto_message_factory", ":well_known_proto", - "//:auto_value", - "//common:error_codes", "//common:options", - "//common:runtime_exception", "//common/annotations", + "//common/exceptions:numeric_overflow", "//common/values", "//common/values:cel_byte_string", "@maven//:com_google_code_findbugs_annotations", @@ -194,11 +192,10 @@ java_library( ], deps = [ ":well_known_proto", - "//common:error_codes", "//common:options", "//common:proto_json_adapter", - "//common:runtime_exception", "//common/annotations", + "//common/exceptions:numeric_overflow", "//common/internal:proto_time_utils", "//common/values", "//common/values:cel_byte_string", @@ -440,9 +437,8 @@ java_library( tags = [ ], deps = [ - "//common:error_codes", - "//common:runtime_exception", "//common/annotations", + "//common/exceptions:bad_format", "@maven//:com_google_guava_guava", "@maven//:com_google_protobuf_protobuf_java", ], @@ -454,9 +450,8 @@ cel_android_library( tags = [ ], deps = [ - "//common:error_codes", - "//common:runtime_exception", "//common/annotations", + "//common/exceptions:bad_format", "@maven_android//:com_google_guava_guava", "@maven_android//:com_google_protobuf_protobuf_javalite", ], diff --git a/common/src/main/java/dev/cel/common/internal/DateTimeHelpers.java b/common/src/main/java/dev/cel/common/internal/DateTimeHelpers.java index 9abe39b4b..703fd2801 100644 --- a/common/src/main/java/dev/cel/common/internal/DateTimeHelpers.java +++ b/common/src/main/java/dev/cel/common/internal/DateTimeHelpers.java @@ -16,9 +16,8 @@ import com.google.common.base.Strings; import com.google.protobuf.Timestamp; -import dev.cel.common.CelErrorCode; -import dev.cel.common.CelRuntimeException; import dev.cel.common.annotations.Internal; +import dev.cel.common.exceptions.CelBadFormatException; import java.time.DateTimeException; import java.time.Duration; import java.time.Instant; @@ -184,7 +183,7 @@ private static ZoneId timeZone(String tz) { try { int ind = tz.indexOf(":"); if (ind == -1) { - throw new CelRuntimeException(e, CelErrorCode.BAD_FORMAT); + throw new CelBadFormatException(e); } int hourOffset = Integer.parseInt(tz.substring(0, ind)); @@ -199,7 +198,7 @@ private static ZoneId timeZone(String tz) { return ZoneId.of(formattedOffset); } catch (DateTimeException e2) { - throw new CelRuntimeException(e2, CelErrorCode.BAD_FORMAT); + throw new CelBadFormatException(e2); } } } diff --git a/common/src/main/java/dev/cel/common/internal/ProtoAdapter.java b/common/src/main/java/dev/cel/common/internal/ProtoAdapter.java index cd4be2f5e..3c3382ef2 100644 --- a/common/src/main/java/dev/cel/common/internal/ProtoAdapter.java +++ b/common/src/main/java/dev/cel/common/internal/ProtoAdapter.java @@ -31,10 +31,9 @@ import com.google.protobuf.MapEntry; import com.google.protobuf.Message; import com.google.protobuf.MessageOrBuilder; -import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; import dev.cel.common.annotations.Internal; +import dev.cel.common.exceptions.CelNumericOverflowException; import dev.cel.common.values.CelByteString; import dev.cel.common.values.NullValue; import java.util.ArrayList; @@ -361,7 +360,7 @@ private static int intCheckedCast(long value) { try { return Ints.checkedCast(value); } catch (IllegalArgumentException e) { - throw new CelRuntimeException(e, CelErrorCode.NUMERIC_OVERFLOW); + throw new CelNumericOverflowException(e); } } @@ -369,7 +368,7 @@ private static int unsignedIntCheckedCast(long value) { try { return UnsignedInts.checkedCast(value); } catch (IllegalArgumentException e) { - throw new CelRuntimeException(e, CelErrorCode.NUMERIC_OVERFLOW); + throw new CelNumericOverflowException(e); } } } diff --git a/common/src/main/java/dev/cel/common/internal/ProtoLiteAdapter.java b/common/src/main/java/dev/cel/common/internal/ProtoLiteAdapter.java index e05d92f68..3b13ecea1 100644 --- a/common/src/main/java/dev/cel/common/internal/ProtoLiteAdapter.java +++ b/common/src/main/java/dev/cel/common/internal/ProtoLiteAdapter.java @@ -42,11 +42,10 @@ import com.google.protobuf.UInt32Value; import com.google.protobuf.UInt64Value; import com.google.protobuf.Value; -import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; import dev.cel.common.CelProtoJsonAdapter; -import dev.cel.common.CelRuntimeException; import dev.cel.common.annotations.Internal; +import dev.cel.common.exceptions.CelNumericOverflowException; import dev.cel.common.values.CelByteString; import java.time.Instant; import java.util.Map; @@ -284,14 +283,14 @@ private Message adaptValueToUint32(Object value) { try { return UInt32Value.of(unsignedIntCheckedCast((Long) value)); } catch (IllegalArgumentException e) { - throw new CelRuntimeException(e, CelErrorCode.NUMERIC_OVERFLOW); + throw new CelNumericOverflowException(e); } } if (value instanceof UnsignedLong) { try { return UInt32Value.of(unsignedIntCheckedCast(((UnsignedLong) value).longValue())); } catch (IllegalArgumentException e) { - throw new CelRuntimeException(e, CelErrorCode.NUMERIC_OVERFLOW); + throw new CelNumericOverflowException(e); } } @@ -316,7 +315,7 @@ private static int intCheckedCast(long value) { try { return Ints.checkedCast(value); } catch (IllegalArgumentException e) { - throw new CelRuntimeException(e, CelErrorCode.NUMERIC_OVERFLOW); + throw new CelNumericOverflowException(e); } } @@ -324,7 +323,7 @@ private static int unsignedIntCheckedCast(long value) { try { return UnsignedInts.checkedCast(value); } catch (IllegalArgumentException e) { - throw new CelRuntimeException(e, CelErrorCode.NUMERIC_OVERFLOW); + throw new CelNumericOverflowException(e); } } diff --git a/common/src/main/java/dev/cel/common/values/CelValueConverter.java b/common/src/main/java/dev/cel/common/values/CelValueConverter.java index 3ab68c80e..b58790eaf 100644 --- a/common/src/main/java/dev/cel/common/values/CelValueConverter.java +++ b/common/src/main/java/dev/cel/common/values/CelValueConverter.java @@ -72,8 +72,6 @@ public Object toRuntimeValue(Object value) { .map(this::toRuntimeValue) .map(OptionalValue::create) .orElse(OptionalValue.EMPTY); - } else if (value instanceof Exception) { - return ErrorValue.create((Exception) value); } return normalizePrimitive(value); diff --git a/common/src/main/java/dev/cel/common/values/ErrorValue.java b/common/src/main/java/dev/cel/common/values/ErrorValue.java index 818f86850..6bc04cda4 100644 --- a/common/src/main/java/dev/cel/common/values/ErrorValue.java +++ b/common/src/main/java/dev/cel/common/values/ErrorValue.java @@ -33,6 +33,8 @@ "Immutable") // Exception is technically not immutable as the stacktrace is malleable. public abstract class ErrorValue extends CelValue { + public abstract long exprId(); + @Override public abstract Exception value(); @@ -46,7 +48,7 @@ public CelType celType() { return SimpleType.ERROR; } - public static ErrorValue create(Exception value) { - return new AutoValue_ErrorValue(value); + public static ErrorValue create(long exprId, Exception value) { + return new AutoValue_ErrorValue(exprId, value); } } diff --git a/common/src/test/java/dev/cel/common/values/CelValueConverterTest.java b/common/src/test/java/dev/cel/common/values/CelValueConverterTest.java index e4d767ef4..308d7b510 100644 --- a/common/src/test/java/dev/cel/common/values/CelValueConverterTest.java +++ b/common/src/test/java/dev/cel/common/values/CelValueConverterTest.java @@ -34,15 +34,6 @@ public void toRuntimeValue_optionalValue() { assertThat(optionalValue).isEqualTo(OptionalValue.create("test")); } - @Test - public void toRuntimeValue_errorValue() { - IllegalArgumentException e = new IllegalArgumentException("error"); - - ErrorValue errorValue = (ErrorValue) CEL_VALUE_CONVERTER.toRuntimeValue(e); - - assertThat(errorValue.value()).isEqualTo(e); - } - @Test @SuppressWarnings("unchecked") // Test only public void unwrap_optionalValue() { diff --git a/common/src/test/java/dev/cel/common/values/ErrorValueTest.java b/common/src/test/java/dev/cel/common/values/ErrorValueTest.java index 0db711f72..a6a4edb66 100644 --- a/common/src/test/java/dev/cel/common/values/ErrorValueTest.java +++ b/common/src/test/java/dev/cel/common/values/ErrorValueTest.java @@ -27,7 +27,7 @@ public class ErrorValueTest { @Test public void errorValue_construct() { IllegalArgumentException exception = new IllegalArgumentException("test"); - ErrorValue opaqueValue = ErrorValue.create(exception); + ErrorValue opaqueValue = ErrorValue.create(0L, exception); assertThat(opaqueValue.value()).isEqualTo(exception); assertThat(opaqueValue.isZeroValue()).isFalse(); @@ -35,12 +35,12 @@ public void errorValue_construct() { @Test public void create_nullValue_throws() { - assertThrows(NullPointerException.class, () -> ErrorValue.create(null)); + assertThrows(NullPointerException.class, () -> ErrorValue.create(0L, null)); } @Test public void celTypeTest() { - ErrorValue value = ErrorValue.create(new IllegalArgumentException("test")); + ErrorValue value = ErrorValue.create(0L, new IllegalArgumentException("test")); assertThat(value.celType()).isEqualTo(SimpleType.ERROR); } diff --git a/extensions/src/main/java/dev/cel/extensions/BUILD.bazel b/extensions/src/main/java/dev/cel/extensions/BUILD.bazel index 9b897cf84..ed2d19d6f 100644 --- a/extensions/src/main/java/dev/cel/extensions/BUILD.bazel +++ b/extensions/src/main/java/dev/cel/extensions/BUILD.bazel @@ -123,6 +123,7 @@ java_library( "//common:compiler_common", "//common:options", "//common/ast", + "//common/exceptions:numeric_overflow", "//common/internal:comparison_functions", "//common/types", "//compiler:compiler_builder", diff --git a/extensions/src/main/java/dev/cel/extensions/CelMathExtensions.java b/extensions/src/main/java/dev/cel/extensions/CelMathExtensions.java index 1e318aedc..e74177bf1 100644 --- a/extensions/src/main/java/dev/cel/extensions/CelMathExtensions.java +++ b/extensions/src/main/java/dev/cel/extensions/CelMathExtensions.java @@ -32,6 +32,7 @@ import dev.cel.common.ast.CelConstant; import dev.cel.common.ast.CelExpr; import dev.cel.common.ast.CelExpr.ExprKind.Kind; +import dev.cel.common.exceptions.CelNumericOverflowException; import dev.cel.common.internal.ComparisonFunctions; import dev.cel.common.types.ListType; import dev.cel.common.types.SimpleType; @@ -216,8 +217,7 @@ enum Function { "math_@max_int_double", Long.class, Double.class, CelMathExtensions::maxPair), CelFunctionBinding.from( "math_@max_double_int", Double.class, Long.class, CelMathExtensions::maxPair), - CelFunctionBinding.from( - "math_@max_list_dyn", List.class, CelMathExtensions::maxList)), + CelFunctionBinding.from("math_@max_list_dyn", List.class, CelMathExtensions::maxList)), ImmutableSet.of( CelFunctionBinding.from("math_@max_uint", Long.class, x -> x), CelFunctionBinding.from( @@ -640,8 +640,7 @@ enum Function { ImmutableSet.of( CelFunctionBinding.from( "math_sqrt_double", Double.class, CelMathExtensions::sqrtDouble), - CelFunctionBinding.from( - "math_sqrt_int", Long.class, CelMathExtensions::sqrtInt), + CelFunctionBinding.from("math_sqrt_int", Long.class, CelMathExtensions::sqrtInt), CelFunctionBinding.from( "math_sqrt_uint", UnsignedLong.class, CelMathExtensions::sqrtUint))); @@ -856,7 +855,7 @@ private static Comparable minPair(Comparable x, Comparable y) { private static long absExact(long x) { if (x == Long.MIN_VALUE) { // The only case where standard Math.abs overflows silently - throw new ArithmeticException("integer overflow"); + throw new CelNumericOverflowException("integer overflow"); } return Math.abs(x); } diff --git a/extensions/src/test/java/dev/cel/extensions/BUILD.bazel b/extensions/src/test/java/dev/cel/extensions/BUILD.bazel index 45d48aeca..d5155f662 100644 --- a/extensions/src/test/java/dev/cel/extensions/BUILD.bazel +++ b/extensions/src/test/java/dev/cel/extensions/BUILD.bazel @@ -14,7 +14,8 @@ java_library( "//common:compiler_common", "//common:container", "//common:options", - "//common/internal:proto_time_utils", + "//common/exceptions:divide_by_zero", + "//common/exceptions:index_out_of_bounds", "//common/types", "//common/types:type_providers", "//common/values", diff --git a/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java b/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java index 5318234b9..34696b688 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelComprehensionsExtensionsTest.java @@ -24,6 +24,8 @@ import dev.cel.common.CelFunctionDecl; import dev.cel.common.CelOptions; import dev.cel.common.CelValidationException; +import dev.cel.common.exceptions.CelDivideByZeroException; +import dev.cel.common.exceptions.CelIndexOutOfBoundsException; import dev.cel.common.types.SimpleType; import dev.cel.common.types.TypeParamType; import dev.cel.compiler.CelCompiler; @@ -347,13 +349,13 @@ public void twoVarComprehension_keyCollision_runtimeError(String expr, String er } @Test - public void twoVarComprehension_arithematicException_runtimeError() throws Exception { + public void twoVarComprehension_arithmeticException_runtimeError() throws Exception { CelAbstractSyntaxTree ast = CEL_COMPILER.compile("[0].all(i, k, i/k < k)").getAst(); CelEvaluationException e = assertThrows(CelEvaluationException.class, () -> CEL_RUNTIME.createProgram(ast).eval()); - assertThat(e).hasCauseThat().isInstanceOf(ArithmeticException.class); + assertThat(e).hasCauseThat().isInstanceOf(CelDivideByZeroException.class); assertThat(e).hasCauseThat().hasMessageThat().contains("/ by zero"); } @@ -364,7 +366,7 @@ public void twoVarComprehension_outOfBounds_runtimeError() throws Exception { CelEvaluationException e = assertThrows(CelEvaluationException.class, () -> CEL_RUNTIME.createProgram(ast).eval()); - assertThat(e).hasCauseThat().isInstanceOf(IndexOutOfBoundsException.class); + assertThat(e).hasCauseThat().isInstanceOf(CelIndexOutOfBoundsException.class); assertThat(e).hasCauseThat().hasMessageThat().contains("Index out of bounds: 1"); } } diff --git a/runtime/BUILD.bazel b/runtime/BUILD.bazel index f3b60d4d7..7760d96b8 100644 --- a/runtime/BUILD.bazel +++ b/runtime/BUILD.bazel @@ -11,10 +11,10 @@ java_library( exports = [ ":evaluation_exception", ":late_function_binding", + ":metadata", "//runtime/src/main/java/dev/cel/runtime", "//runtime/src/main/java/dev/cel/runtime:descriptor_message_provider", "//runtime/src/main/java/dev/cel/runtime:function_overload", - "//runtime/src/main/java/dev/cel/runtime:metadata", "//runtime/src/main/java/dev/cel/runtime:runtime_type_provider", ], ) @@ -249,3 +249,9 @@ cel_android_library( name = "program_android", exports = ["//runtime/src/main/java/dev/cel/runtime:program_android"], ) + +java_library( + name = "metadata", + visibility = ["//:internal"], + exports = ["//runtime/src/main/java/dev/cel/runtime:metadata"], +) diff --git a/runtime/src/main/java/dev/cel/runtime/BUILD.bazel b/runtime/src/main/java/dev/cel/runtime/BUILD.bazel index 847092cc3..178bb1728 100644 --- a/runtime/src/main/java/dev/cel/runtime/BUILD.bazel +++ b/runtime/src/main/java/dev/cel/runtime/BUILD.bazel @@ -98,10 +98,9 @@ java_library( ":runtime_type_provider", "//common:cel_descriptor_util", "//common:cel_descriptors", - "//common:error_codes", "//common:options", - "//common:runtime_exception", "//common/annotations", + "//common/exceptions:attribute_not_found", "//common/internal:cel_descriptor_pools", "//common/internal:default_message_factory", "//common/internal:dynamic_proto", @@ -298,9 +297,9 @@ java_library( "//common:cel_ast", "//common:error_codes", "//common:options", - "//common:runtime_exception", "//common/annotations", "//common/ast", + "//common/exceptions:runtime_exception", "//common/types", "//common/types:type_providers", "//common/values:cel_byte_string", @@ -308,7 +307,6 @@ java_library( "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", "@maven//:com_google_protobuf_protobuf_java", - "@maven//:org_jspecify_jspecify", ], ) @@ -337,9 +335,9 @@ cel_android_library( "//common:cel_ast_android", "//common:error_codes", "//common:options", - "//common:runtime_exception", "//common/annotations", "//common/ast:ast_android", + "//common/exceptions:runtime_exception", "//common/types:type_providers_android", "//common/types:types_android", "//common/values:cel_byte_string", @@ -360,10 +358,9 @@ java_library( ], deps = [ ":runtime_helpers", - "//common:error_codes", "//common:options", - "//common:runtime_exception", "//common/annotations", + "//common/exceptions:attribute_not_found", "//common/internal:comparison_functions", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", @@ -378,10 +375,9 @@ cel_android_library( ], deps = [ ":runtime_helpers_android", - "//common:error_codes", "//common:options", - "//common:runtime_exception", "//common/annotations", + "//common/exceptions:attribute_not_found", "//common/internal:comparison_functions_android", "@maven//:com_google_errorprone_error_prone_annotations", "@maven_android//:com_google_guava_guava", @@ -416,10 +412,11 @@ cel_android_library( ], deps = [ ":concatenated_list_view", - "//common:error_codes", "//common:options", - "//common:runtime_exception", "//common/annotations", + "//common/exceptions:divide_by_zero", + "//common/exceptions:index_out_of_bounds", + "//common/exceptions:numeric_overflow", "//common/internal:converter", "//common/values:values_android", "@maven//:com_google_errorprone_error_prone_annotations", @@ -439,10 +436,11 @@ java_library( ], deps = [ ":concatenated_list_view", - "//common:error_codes", "//common:options", - "//common:runtime_exception", "//common/annotations", + "//common/exceptions:divide_by_zero", + "//common/exceptions:index_out_of_bounds", + "//common/exceptions:numeric_overflow", "//common/internal:converter", "//common/values", "@maven//:com_google_errorprone_error_prone_annotations", @@ -558,8 +556,8 @@ java_library( ":evaluation_exception", ":metadata", "//common:error_codes", - "//common:runtime_exception", "//common/annotations", + "//common/exceptions:runtime_exception", "//common/internal:safe_string_formatter", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:org_jspecify_jspecify", @@ -1029,9 +1027,8 @@ java_library( deps = [ ":runtime_type_provider", ":unknown_attributes", - "//common:error_codes", - "//common:runtime_exception", "//common/annotations", + "//common/exceptions:attribute_not_found", "//common/values", "//common/values:base_proto_cel_value_converter", "//common/values:base_proto_message_value_provider", @@ -1050,9 +1047,8 @@ cel_android_library( deps = [ ":runtime_type_provider_android", ":unknown_attributes_android", - "//common:error_codes", - "//common:runtime_exception", "//common/annotations", + "//common/exceptions:attribute_not_found", "//common/values:base_proto_cel_value_converter_android", "//common/values:base_proto_message_value_provider_android", "//common/values:cel_value_android", diff --git a/runtime/src/main/java/dev/cel/runtime/CelEvaluationExceptionBuilder.java b/runtime/src/main/java/dev/cel/runtime/CelEvaluationExceptionBuilder.java index 6aaed4da7..986788bac 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelEvaluationExceptionBuilder.java +++ b/runtime/src/main/java/dev/cel/runtime/CelEvaluationExceptionBuilder.java @@ -16,8 +16,8 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import dev.cel.common.CelErrorCode; -import dev.cel.common.CelRuntimeException; import dev.cel.common.annotations.Internal; +import dev.cel.common.exceptions.CelRuntimeException; import dev.cel.common.internal.SafeStringFormatter; import org.jspecify.annotations.Nullable; @@ -83,10 +83,15 @@ public static CelEvaluationExceptionBuilder newBuilder(String message, Object... */ @Internal public static CelEvaluationExceptionBuilder newBuilder(CelRuntimeException celRuntimeException) { - Throwable cause = celRuntimeException.getCause(); - return new CelEvaluationExceptionBuilder(cause.getMessage()) - .setCause(cause) - .setErrorCode(celRuntimeException.getErrorCode()); + // Intercept the cause to prevent including the cause's class name in the exception message. + String message = + celRuntimeException.getCause() == null + ? celRuntimeException.getMessage() + : celRuntimeException.getCause().getMessage(); + + return new CelEvaluationExceptionBuilder(message) + .setErrorCode(celRuntimeException.getErrorCode()) + .setCause(celRuntimeException); } private CelEvaluationExceptionBuilder(String message) { diff --git a/runtime/src/main/java/dev/cel/runtime/CelValueRuntimeTypeProvider.java b/runtime/src/main/java/dev/cel/runtime/CelValueRuntimeTypeProvider.java index 989d3b7be..e071289ca 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelValueRuntimeTypeProvider.java +++ b/runtime/src/main/java/dev/cel/runtime/CelValueRuntimeTypeProvider.java @@ -18,9 +18,8 @@ import com.google.errorprone.annotations.Immutable; import com.google.protobuf.MessageLite; -import dev.cel.common.CelErrorCode; -import dev.cel.common.CelRuntimeException; import dev.cel.common.annotations.Internal; +import dev.cel.common.exceptions.CelAttributeNotFoundException; import dev.cel.common.values.BaseProtoCelValueConverter; import dev.cel.common.values.BaseProtoMessageValueProvider; import dev.cel.common.values.CelValue; @@ -82,9 +81,7 @@ public Object selectField(Object message, String fieldName) { return map.get(fieldName); } - throw new CelRuntimeException( - new IllegalArgumentException(String.format("key '%s' is not present in map.", fieldName)), - CelErrorCode.ATTRIBUTE_NOT_FOUND); + throw CelAttributeNotFoundException.forMissingMapKey(fieldName); } SelectableValue selectableValue = getSelectableValueOrThrow(message, fieldName); @@ -142,13 +139,7 @@ private Object maybeUnwrapCelValue(Object object) { } private static void throwInvalidFieldSelection(String fieldName) { - throw new CelRuntimeException( - new IllegalArgumentException( - String.format( - "Error resolving field '%s'. Field selections must be performed on messages or" - + " maps.", - fieldName)), - CelErrorCode.ATTRIBUTE_NOT_FOUND); + throw CelAttributeNotFoundException.forFieldResolution(fieldName); } private CelValueRuntimeTypeProvider( diff --git a/runtime/src/main/java/dev/cel/runtime/DefaultInterpreter.java b/runtime/src/main/java/dev/cel/runtime/DefaultInterpreter.java index c0a21cc3f..350163b14 100644 --- a/runtime/src/main/java/dev/cel/runtime/DefaultInterpreter.java +++ b/runtime/src/main/java/dev/cel/runtime/DefaultInterpreter.java @@ -31,7 +31,6 @@ import dev.cel.common.CelAbstractSyntaxTree; import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; import dev.cel.common.ast.CelConstant; import dev.cel.common.ast.CelExpr; import dev.cel.common.ast.CelExpr.CelCall; @@ -42,6 +41,7 @@ import dev.cel.common.ast.CelExpr.CelStruct; import dev.cel.common.ast.CelExpr.ExprKind; import dev.cel.common.ast.CelReference; +import dev.cel.common.exceptions.CelRuntimeException; import dev.cel.common.types.CelKind; import dev.cel.common.types.CelType; import dev.cel.common.types.TypeType; diff --git a/runtime/src/main/java/dev/cel/runtime/DescriptorMessageProvider.java b/runtime/src/main/java/dev/cel/runtime/DescriptorMessageProvider.java index ab6934e01..f2ea85b9b 100644 --- a/runtime/src/main/java/dev/cel/runtime/DescriptorMessageProvider.java +++ b/runtime/src/main/java/dev/cel/runtime/DescriptorMessageProvider.java @@ -21,10 +21,9 @@ import com.google.protobuf.Message; import com.google.protobuf.MessageOrBuilder; import com.google.protobuf.NullValue; -import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; import dev.cel.common.annotations.Internal; +import dev.cel.common.exceptions.CelAttributeNotFoundException; import dev.cel.common.internal.DynamicProto; import dev.cel.common.internal.ProtoAdapter; import dev.cel.common.internal.ProtoMessageFactory; @@ -79,10 +78,8 @@ public DescriptorMessageProvider(ProtoMessageFactory protoMessageFactory, CelOpt .newBuilder(messageName) .orElseThrow( () -> - new CelRuntimeException( - new IllegalArgumentException( - String.format("cannot resolve '%s' as a message", messageName)), - CelErrorCode.ATTRIBUTE_NOT_FOUND)); + CelAttributeNotFoundException.of( + String.format("cannot resolve '%s' as a message", messageName))); try { Descriptor descriptor = builder.getDescriptorForType(); @@ -122,10 +119,7 @@ public DescriptorMessageProvider(ProtoMessageFactory protoMessageFactory, CelOpt if (isOptionalMessage) { return Optional.empty(); } else { - throw new CelRuntimeException( - new IllegalArgumentException( - String.format("key '%s' is not present in map.", fieldName)), - CelErrorCode.ATTRIBUTE_NOT_FOUND); + throw CelAttributeNotFoundException.forMissingMapKey(fieldName); } } @@ -197,13 +191,7 @@ private FieldDescriptor findField(Descriptor descriptor, String fieldName) { private static MessageOrBuilder assertFullProtoMessage(Object candidate, String fieldName) { if (!(candidate instanceof MessageOrBuilder)) { // This can happen when the field selection is done on dyn, and it is not a message. - throw new CelRuntimeException( - new IllegalArgumentException( - String.format( - "Error resolving field '%s'. Field selections must be performed on messages or" - + " maps.", - fieldName)), - CelErrorCode.ATTRIBUTE_NOT_FOUND); + throw CelAttributeNotFoundException.forFieldResolution(fieldName); } return (MessageOrBuilder) candidate; } diff --git a/runtime/src/main/java/dev/cel/runtime/RuntimeEquality.java b/runtime/src/main/java/dev/cel/runtime/RuntimeEquality.java index d0b2fcd6b..2044a2518 100644 --- a/runtime/src/main/java/dev/cel/runtime/RuntimeEquality.java +++ b/runtime/src/main/java/dev/cel/runtime/RuntimeEquality.java @@ -17,10 +17,9 @@ import com.google.common.primitives.UnsignedLong; import com.google.errorprone.annotations.Immutable; import com.google.protobuf.MessageLiteOrBuilder; -import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; import dev.cel.common.annotations.Internal; +import dev.cel.common.exceptions.CelAttributeNotFoundException; import dev.cel.common.internal.ComparisonFunctions; import java.util.Iterator; import java.util.List; @@ -67,8 +66,8 @@ public B indexMap(Map map, A index) { if (value.isPresent()) { return (B) value.get(); } - throw new CelRuntimeException( - new IndexOutOfBoundsException(index.toString()), CelErrorCode.ATTRIBUTE_NOT_FOUND); + + throw CelAttributeNotFoundException.of(index.toString()); } /** Determine whether the {@code map} contains the given {@code key}. */ diff --git a/runtime/src/main/java/dev/cel/runtime/RuntimeHelpers.java b/runtime/src/main/java/dev/cel/runtime/RuntimeHelpers.java index 25f01ef59..0ee7824b7 100644 --- a/runtime/src/main/java/dev/cel/runtime/RuntimeHelpers.java +++ b/runtime/src/main/java/dev/cel/runtime/RuntimeHelpers.java @@ -23,10 +23,11 @@ import com.google.protobuf.Duration; import com.google.protobuf.MessageLiteOrBuilder; import com.google.re2j.Pattern; -import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; import dev.cel.common.annotations.Internal; +import dev.cel.common.exceptions.CelDivideByZeroException; +import dev.cel.common.exceptions.CelIndexOutOfBoundsException; +import dev.cel.common.exceptions.CelNumericOverflowException; import dev.cel.common.internal.Converter; import dev.cel.common.values.NullValue; import java.time.format.DateTimeParseException; @@ -116,17 +117,11 @@ public static A indexList(List list, Number index) { if (index instanceof Double) { return doubleToLongLossless(index.doubleValue()) .map(v -> indexList(list, v)) - .orElseThrow( - () -> - new CelRuntimeException( - new IndexOutOfBoundsException("Index out of bounds: " + index.doubleValue()), - CelErrorCode.INDEX_OUT_OF_BOUNDS)); + .orElseThrow(() -> new CelIndexOutOfBoundsException(index.doubleValue())); } int castIndex = Ints.checkedCast(index.longValue()); if (castIndex < 0 || castIndex >= list.size()) { - throw new CelRuntimeException( - new IndexOutOfBoundsException("Index out of bounds: " + castIndex), - CelErrorCode.INDEX_OUT_OF_BOUNDS); + throw new CelIndexOutOfBoundsException(castIndex); } return list.get(castIndex); } @@ -145,7 +140,7 @@ public static long int64Add(long x, long y, CelOptions celOptions) { public static long int64Divide(long x, long y, CelOptions celOptions) { if (celOptions.errorOnIntWrap() && x == Long.MIN_VALUE && y == -1) { - throw new ArithmeticException("most negative number wraps"); + throw new CelNumericOverflowException("most negative number wraps"); } return x / y; } @@ -186,13 +181,13 @@ public static long uint64Add(long x, long y, CelOptions celOptions) { if (celOptions.errorOnIntWrap()) { if (x < 0 && y < 0) { // Both numbers are in the upper half of the range, so it must overflow. - throw new ArithmeticException("range overflow on unsigned addition"); + throw new CelNumericOverflowException("range overflow on unsigned addition"); } long z = x + y; if ((x < 0 || y < 0) && z >= 0) { // Only one number is in the upper half of the range. It overflows if the result // is not in the upper half. - throw new ArithmeticException("range overflow on unsigned addition"); + throw new CelNumericOverflowException("range overflow on unsigned addition"); } return z; } @@ -201,7 +196,7 @@ public static long uint64Add(long x, long y, CelOptions celOptions) { public static UnsignedLong uint64Add(UnsignedLong x, UnsignedLong y) { if (x.compareTo(UnsignedLong.MAX_VALUE.minus(y)) > 0) { - throw new ArithmeticException("range overflow on unsigned addition"); + throw new CelNumericOverflowException("range overflow on unsigned addition"); } return x.plus(y); } @@ -228,7 +223,7 @@ public static long uint64Divide(long x, long y, CelOptions celOptions) { ? UnsignedLongs.divide(x, y) : UnsignedLong.valueOf(x).dividedBy(UnsignedLong.valueOf(y)).longValue(); } catch (ArithmeticException e) { - throw new CelRuntimeException(e, CelErrorCode.DIVIDE_BY_ZERO); + throw new CelDivideByZeroException(e); } } @@ -240,8 +235,7 @@ static long uint64Divide(long x, long y) { public static UnsignedLong uint64Divide(UnsignedLong x, UnsignedLong y) { if (y.equals(UnsignedLong.ZERO)) { - throw new CelRuntimeException( - new ArithmeticException("/ by zero"), CelErrorCode.DIVIDE_BY_ZERO); + throw new CelDivideByZeroException(); } return x.dividedBy(y); } @@ -252,14 +246,13 @@ public static long uint64Mod(long x, long y, CelOptions celOptions) { ? UnsignedLongs.remainder(x, y) : UnsignedLong.valueOf(x).mod(UnsignedLong.valueOf(y)).longValue(); } catch (ArithmeticException e) { - throw new CelRuntimeException(e, CelErrorCode.DIVIDE_BY_ZERO); + throw new CelDivideByZeroException(e); } } public static UnsignedLong uint64Mod(UnsignedLong x, UnsignedLong y) { if (y.equals(UnsignedLong.ZERO)) { - throw new CelRuntimeException( - new ArithmeticException("/ by zero"), CelErrorCode.DIVIDE_BY_ZERO); + throw new CelDivideByZeroException(); } return x.mod(y); } @@ -276,7 +269,7 @@ public static long uint64Multiply(long x, long y, CelOptions celOptions) { ? x * y : UnsignedLong.valueOf(x).times(UnsignedLong.valueOf(y)).longValue(); if (celOptions.errorOnIntWrap() && y != 0 && Long.divideUnsigned(z, y) != x) { - throw new ArithmeticException("multiply out of unsigned integer range"); + throw new CelNumericOverflowException("multiply out of unsigned integer range"); } return z; } @@ -289,7 +282,7 @@ static long uint64Multiply(long x, long y) { public static UnsignedLong uint64Multiply(UnsignedLong x, UnsignedLong y) { if (!y.equals(UnsignedLong.ZERO) && x.compareTo(UnsignedLong.MAX_VALUE.dividedBy(y)) > 0) { - throw new ArithmeticException("multiply out of unsigned integer range"); + throw new CelNumericOverflowException("multiply out of unsigned integer range"); } return x.times(y); } @@ -299,7 +292,7 @@ public static long uint64Subtract(long x, long y, CelOptions celOptions) { // Throw an overflow error if x < y, as unsigned longs. This happens if y has its high // bit set and x does not, or if they have the same high bit and x < y as signed longs. if ((x < 0 && y < 0 && x < y) || (x >= 0 && y >= 0 && x < y) || (x >= 0 && y < 0)) { - throw new ArithmeticException("unsigned subtraction underflow"); + throw new CelNumericOverflowException("unsigned subtraction underflow"); } // fallthrough } @@ -310,7 +303,7 @@ public static UnsignedLong uint64Subtract(UnsignedLong x, UnsignedLong y) { // Throw an overflow error if x < y, as unsigned longs. This happens if y has its high // bit set and x does not, or if they have the same high bit and x < y as signed longs. if (x.compareTo(y) < 0) { - throw new ArithmeticException("unsigned subtraction underflow"); + throw new CelNumericOverflowException("unsigned subtraction underflow"); } return x.minus(y); } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel b/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel index 3ad0c0173..a9a62b699 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel +++ b/runtime/src/main/java/dev/cel/runtime/planner/BUILD.bazel @@ -14,6 +14,7 @@ java_library( ], deps = [ ":attribute", + ":error_metadata", ":eval_and", ":eval_attribute", ":eval_conditional", @@ -25,6 +26,7 @@ java_library( ":eval_unary", ":eval_var_args_call", ":eval_zero_arity", + ":planned_interpretable", ":planned_program", "//:auto_value", "//common:cel_ast", @@ -51,8 +53,11 @@ java_library( name = "planned_program", srcs = ["PlannedProgram.java"], deps = [ + ":error_metadata", + ":planned_interpretable", + ":strict_error_exception", "//:auto_value", - "//common:runtime_exception", + "//common/exceptions:runtime_exception", "//common/values", "//runtime:activation", "//runtime:evaluation_exception", @@ -68,6 +73,7 @@ java_library( name = "eval_const", srcs = ["EvalConstant.java"], deps = [ + ":planned_interpretable", "//common/values", "//common/values:cel_byte_string", "//runtime:evaluation_listener", @@ -99,6 +105,7 @@ java_library( srcs = ["EvalAttribute.java"], deps = [ ":attribute", + ":planned_interpretable", "//runtime:evaluation_listener", "//runtime:function_resolver", "//runtime:interpretable", @@ -111,6 +118,7 @@ java_library( name = "eval_zero_arity", srcs = ["EvalZeroArity.java"], deps = [ + ":planned_interpretable", "//runtime:evaluation_exception", "//runtime:evaluation_listener", "//runtime:function_resolver", @@ -123,6 +131,8 @@ java_library( name = "eval_unary", srcs = ["EvalUnary.java"], deps = [ + ":eval_helpers", + ":planned_interpretable", "//runtime:evaluation_exception", "//runtime:evaluation_listener", "//runtime:function_resolver", @@ -135,6 +145,8 @@ java_library( name = "eval_var_args_call", srcs = ["EvalVarArgsCall.java"], deps = [ + ":eval_helpers", + ":planned_interpretable", "//runtime:evaluation_exception", "//runtime:evaluation_listener", "//runtime:function_resolver", @@ -148,6 +160,7 @@ java_library( srcs = ["EvalOr.java"], deps = [ ":eval_helpers", + ":planned_interpretable", "//common/values", "//runtime:evaluation_listener", "//runtime:function_resolver", @@ -161,6 +174,7 @@ java_library( srcs = ["EvalAnd.java"], deps = [ ":eval_helpers", + ":planned_interpretable", "//common/values", "//runtime:evaluation_listener", "//runtime:function_resolver", @@ -173,6 +187,7 @@ java_library( name = "eval_conditional", srcs = ["EvalConditional.java"], deps = [ + ":planned_interpretable", "//runtime:evaluation_exception", "//runtime:evaluation_listener", "//runtime:function_resolver", @@ -185,6 +200,7 @@ java_library( name = "eval_create_struct", srcs = ["EvalCreateStruct.java"], deps = [ + ":planned_interpretable", "//common/types", "//common/values", "//common/values:cel_value_provider", @@ -201,6 +217,7 @@ java_library( name = "eval_create_list", srcs = ["EvalCreateList.java"], deps = [ + ":planned_interpretable", "//runtime:evaluation_exception", "//runtime:evaluation_listener", "//runtime:function_resolver", @@ -214,6 +231,7 @@ java_library( name = "eval_create_map", srcs = ["EvalCreateMap.java"], deps = [ + ":planned_interpretable", "//runtime:evaluation_exception", "//runtime:evaluation_listener", "//runtime:function_resolver", @@ -227,7 +245,39 @@ java_library( name = "eval_helpers", srcs = ["EvalHelpers.java"], deps = [ + ":planned_interpretable", + ":strict_error_exception", + "//common:error_codes", + "//common/exceptions:runtime_exception", "//common/values", "//runtime:interpretable", ], ) + +java_library( + name = "strict_error_exception", + srcs = ["StrictErrorException.java"], + deps = [ + "//common:error_codes", + "//common/exceptions:runtime_exception", + ], +) + +java_library( + name = "error_metadata", + srcs = ["ErrorMetadata.java"], + deps = [ + "//runtime:metadata", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven//:com_google_guava_guava", + ], +) + +java_library( + name = "planned_interpretable", + srcs = ["PlannedInterpretable.java"], + deps = [ + "//runtime:interpretable", + "@maven//:com_google_errorprone_error_prone_annotations", + ], +) diff --git a/runtime/src/main/java/dev/cel/runtime/planner/ErrorMetadata.java b/runtime/src/main/java/dev/cel/runtime/planner/ErrorMetadata.java new file mode 100644 index 000000000..2358bd0f9 --- /dev/null +++ b/runtime/src/main/java/dev/cel/runtime/planner/ErrorMetadata.java @@ -0,0 +1,52 @@ +// 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 static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.Immutable; +import dev.cel.runtime.Metadata; + +@Immutable +final class ErrorMetadata implements Metadata { + + private final ImmutableMap exprIdToPositionMap; + private final String location; + + @Override + public String getLocation() { + return location; + } + + @Override + public int getPosition(long exprId) { + return exprIdToPositionMap.getOrDefault(exprId, 0); + } + + @Override + public boolean hasPosition(long exprId) { + return exprIdToPositionMap.containsKey(exprId); + } + + static ErrorMetadata create(ImmutableMap exprIdToPositionMap, String location) { + return new ErrorMetadata(exprIdToPositionMap, location); + } + + private ErrorMetadata(ImmutableMap exprIdToPositionMap, String location) { + this.exprIdToPositionMap = checkNotNull(exprIdToPositionMap); + this.location = checkNotNull(location); + } +} diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalAnd.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalAnd.java index 4bf9af517..a3a39ce8a 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalAnd.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalAnd.java @@ -21,17 +21,16 @@ import dev.cel.runtime.CelEvaluationListener; import dev.cel.runtime.CelFunctionResolver; import dev.cel.runtime.GlobalResolver; -import dev.cel.runtime.Interpretable; -final class EvalAnd implements Interpretable { +final class EvalAnd extends PlannedInterpretable { @SuppressWarnings("Immutable") - private final Interpretable[] args; + private final PlannedInterpretable[] args; @Override public Object eval(GlobalResolver resolver) { ErrorValue errorValue = null; - for (Interpretable arg : args) { + for (PlannedInterpretable arg : args) { Object argVal = evalNonstrictly(arg, resolver); if (argVal instanceof Boolean) { // Short-circuit on false @@ -75,11 +74,12 @@ public Object eval( throw new UnsupportedOperationException("Not yet supported"); } - static EvalAnd create(Interpretable[] args) { - return new EvalAnd(args); + static EvalAnd create(long exprId, PlannedInterpretable[] args) { + return new EvalAnd(exprId, args); } - private EvalAnd(Interpretable[] args) { + private EvalAnd(long exprId, PlannedInterpretable[] args) { + super(exprId); Preconditions.checkArgument(args.length == 2); this.args = args; } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalAttribute.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalAttribute.java index 23b7fa63c..6e20d4f59 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalAttribute.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalAttribute.java @@ -18,10 +18,9 @@ import dev.cel.runtime.CelEvaluationListener; import dev.cel.runtime.CelFunctionResolver; import dev.cel.runtime.GlobalResolver; -import dev.cel.runtime.Interpretable; @Immutable -final class EvalAttribute implements Interpretable { +final class EvalAttribute extends PlannedInterpretable { private final Attribute attr; @@ -47,15 +46,15 @@ public Object eval( GlobalResolver resolver, CelFunctionResolver lateBoundFunctionResolver, CelEvaluationListener listener) { - // TODO: Implement support throw new UnsupportedOperationException("Not yet supported"); } - static EvalAttribute create(Attribute attr) { - return new EvalAttribute(attr); + static EvalAttribute create(long exprId, Attribute attr) { + return new EvalAttribute(exprId, attr); } - private EvalAttribute(Attribute attr) { + private EvalAttribute(long exprId, Attribute attr) { + super(exprId); this.attr = attr; } } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalConditional.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalConditional.java index ca32b405c..4445d3e71 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalConditional.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalConditional.java @@ -21,7 +21,7 @@ import dev.cel.runtime.GlobalResolver; import dev.cel.runtime.Interpretable; -final class EvalConditional implements Interpretable { +final class EvalConditional extends PlannedInterpretable { @SuppressWarnings("Immutable") private final Interpretable[] args; @@ -67,11 +67,12 @@ public Object eval( throw new UnsupportedOperationException("Not yet supported"); } - static EvalConditional create(Interpretable[] args) { - return new EvalConditional(args); + static EvalConditional create(long exprId, Interpretable[] args) { + return new EvalConditional(exprId, args); } - private EvalConditional(Interpretable[] args) { + private EvalConditional(long exprId, Interpretable[] args) { + super(exprId); Preconditions.checkArgument(args.length == 3); this.args = args; } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalConstant.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalConstant.java index a1a2dc998..408d04046 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalConstant.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalConstant.java @@ -21,10 +21,9 @@ 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 { +final class EvalConstant extends PlannedInterpretable { // Pre-allocation of common constants private static final EvalConstant NULL_VALUE = new EvalConstant(NullValue.NULL_VALUE); @@ -115,6 +114,7 @@ static EvalConstant create(Object value) { } private EvalConstant(Object constant) { + super(/* exprId= */ -1); // It's not possible to throw while evaluating a constant this.constant = constant; } } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateList.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateList.java index 6a1917475..4ec275eef 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateList.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateList.java @@ -23,7 +23,7 @@ import dev.cel.runtime.Interpretable; @Immutable -final class EvalCreateList implements Interpretable { +final class EvalCreateList extends PlannedInterpretable { // Array contents are not mutated @SuppressWarnings("Immutable") @@ -51,17 +51,20 @@ public Object eval(GlobalResolver resolver, CelFunctionResolver lateBoundFunctio } @Override - public Object eval(GlobalResolver resolver, CelFunctionResolver lateBoundFunctionResolver, + public Object eval( + GlobalResolver resolver, + CelFunctionResolver lateBoundFunctionResolver, CelEvaluationListener listener) { // TODO: Implement support throw new UnsupportedOperationException("Not yet supported"); } - static EvalCreateList create(Interpretable[] values) { - return new EvalCreateList(values); + static EvalCreateList create(long exprId, Interpretable[] values) { + return new EvalCreateList(exprId, values); } - private EvalCreateList(Interpretable[] values) { + private EvalCreateList(long exprId, Interpretable[] values) { + super(exprId); this.values = values; } } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateMap.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateMap.java index a3fb2cb85..38d690303 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateMap.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateMap.java @@ -24,7 +24,7 @@ import dev.cel.runtime.Interpretable; @Immutable -final class EvalCreateMap implements Interpretable { +final class EvalCreateMap extends PlannedInterpretable { // Array contents are not mutated @SuppressWarnings("Immutable") @@ -58,18 +58,20 @@ public Object eval(GlobalResolver resolver, CelFunctionResolver lateBoundFunctio } @Override - public Object eval(GlobalResolver resolver, CelFunctionResolver lateBoundFunctionResolver, + public Object eval( + GlobalResolver resolver, + CelFunctionResolver lateBoundFunctionResolver, CelEvaluationListener listener) { // TODO: Implement support throw new UnsupportedOperationException("Not yet supported"); } - - static EvalCreateMap create(Interpretable[] keys, Interpretable[] values) { - return new EvalCreateMap(keys, values); + static EvalCreateMap create(long exprId, Interpretable[] keys, Interpretable[] values) { + return new EvalCreateMap(exprId, keys, values); } - private EvalCreateMap(Interpretable[] keys, Interpretable[] values) { + private EvalCreateMap(long exprId, Interpretable[] keys, Interpretable[] values) { + super(exprId); Preconditions.checkArgument(keys.length == values.length); this.keys = keys; this.values = values; diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateStruct.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateStruct.java index 5c1f1d77b..7553add80 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateStruct.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalCreateStruct.java @@ -28,7 +28,7 @@ import java.util.Map; @Immutable -final class EvalCreateStruct implements Interpretable { +final class EvalCreateStruct extends PlannedInterpretable { private final CelValueProvider valueProvider; private final StructType structType; @@ -84,18 +84,21 @@ public Object eval( } static EvalCreateStruct create( + long exprId, CelValueProvider valueProvider, StructType structType, String[] keys, Interpretable[] values) { - return new EvalCreateStruct(valueProvider, structType, keys, values); + return new EvalCreateStruct(exprId, valueProvider, structType, keys, values); } private EvalCreateStruct( + long exprId, CelValueProvider valueProvider, StructType structType, String[] keys, Interpretable[] values) { + super(exprId); this.valueProvider = valueProvider; this.structType = structType; this.keys = keys; diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalHelpers.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalHelpers.java index 82bd6124a..8dae6adfb 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalHelpers.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalHelpers.java @@ -14,17 +14,33 @@ package dev.cel.runtime.planner; +import dev.cel.common.CelErrorCode; +import dev.cel.common.exceptions.CelRuntimeException; import dev.cel.common.values.ErrorValue; import dev.cel.runtime.GlobalResolver; -import dev.cel.runtime.Interpretable; final class EvalHelpers { - static Object evalNonstrictly(Interpretable interpretable, GlobalResolver resolver) { + static Object evalNonstrictly(PlannedInterpretable interpretable, GlobalResolver resolver) { try { return interpretable.eval(resolver); + } catch (StrictErrorException e) { + // Intercept the strict exception to get a more localized expr ID for error reporting purposes + // Example: foo [1] && strict_err [2] -> ID 2 is propagated. + return ErrorValue.create(e.exprId(), e); } catch (Exception e) { - return ErrorValue.create(e); + return ErrorValue.create(interpretable.exprId(), e); + } + } + + static Object evalStrictly(PlannedInterpretable interpretable, GlobalResolver resolver) { + try { + return interpretable.eval(resolver); + } catch (CelRuntimeException e) { + throw new StrictErrorException(e, interpretable.exprId()); + } catch (Exception e) { + throw new StrictErrorException( + e.getCause(), CelErrorCode.INTERNAL_ERROR, interpretable.exprId()); } } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalOr.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalOr.java index afa02dfb8..f287bdd59 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalOr.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalOr.java @@ -21,17 +21,16 @@ import dev.cel.runtime.CelEvaluationListener; import dev.cel.runtime.CelFunctionResolver; import dev.cel.runtime.GlobalResolver; -import dev.cel.runtime.Interpretable; -final class EvalOr implements Interpretable { +final class EvalOr extends PlannedInterpretable { @SuppressWarnings("Immutable") - private final Interpretable[] args; + private final PlannedInterpretable[] args; @Override public Object eval(GlobalResolver resolver) { ErrorValue errorValue = null; - for (Interpretable arg : args) { + for (PlannedInterpretable arg : args) { Object argVal = evalNonstrictly(arg, resolver); if (argVal instanceof Boolean) { // Short-circuit on true @@ -75,11 +74,12 @@ public Object eval( throw new UnsupportedOperationException("Not yet supported"); } - static EvalOr create(Interpretable[] args) { - return new EvalOr(args); + static EvalOr create(long exprId, PlannedInterpretable[] args) { + return new EvalOr(exprId, args); } - private EvalOr(Interpretable[] args) { + private EvalOr(long exprId, PlannedInterpretable[] args) { + super(exprId); Preconditions.checkArgument(args.length == 2); this.args = args; } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalUnary.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalUnary.java index c6daff4b2..13b59d11e 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalUnary.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalUnary.java @@ -14,21 +14,24 @@ package dev.cel.runtime.planner; +import static dev.cel.runtime.planner.EvalHelpers.evalNonstrictly; +import static dev.cel.runtime.planner.EvalHelpers.evalStrictly; + import dev.cel.runtime.CelEvaluationException; import dev.cel.runtime.CelEvaluationListener; import dev.cel.runtime.CelFunctionResolver; import dev.cel.runtime.CelResolvedOverload; import dev.cel.runtime.GlobalResolver; -import dev.cel.runtime.Interpretable; -final class EvalUnary implements Interpretable { +final class EvalUnary extends PlannedInterpretable { private final CelResolvedOverload resolvedOverload; - private final Interpretable arg; + private final PlannedInterpretable arg; @Override public Object eval(GlobalResolver resolver) throws CelEvaluationException { - Object argVal = arg.eval(resolver); + Object argVal = + resolvedOverload.isStrict() ? evalStrictly(arg, resolver) : evalNonstrictly(arg, resolver); Object[] arguments = new Object[] {argVal}; return resolvedOverload.getDefinition().apply(arguments); @@ -55,11 +58,13 @@ public Object eval( throw new UnsupportedOperationException("Not yet supported"); } - static EvalUnary create(CelResolvedOverload resolvedOverload, Interpretable arg) { - return new EvalUnary(resolvedOverload, arg); + static EvalUnary create( + long exprId, CelResolvedOverload resolvedOverload, PlannedInterpretable arg) { + return new EvalUnary(exprId, resolvedOverload, arg); } - private EvalUnary(CelResolvedOverload resolvedOverload, Interpretable arg) { + private EvalUnary(long exprId, CelResolvedOverload resolvedOverload, PlannedInterpretable arg) { + super(exprId); this.resolvedOverload = resolvedOverload; this.arg = arg; } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalVarArgsCall.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalVarArgsCall.java index 48fc7ba04..a2a4c0acc 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalVarArgsCall.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalVarArgsCall.java @@ -14,25 +14,30 @@ package dev.cel.runtime.planner; +import static dev.cel.runtime.planner.EvalHelpers.evalNonstrictly; +import static dev.cel.runtime.planner.EvalHelpers.evalStrictly; + import dev.cel.runtime.CelEvaluationException; import dev.cel.runtime.CelEvaluationListener; import dev.cel.runtime.CelFunctionResolver; import dev.cel.runtime.CelResolvedOverload; import dev.cel.runtime.GlobalResolver; -import dev.cel.runtime.Interpretable; @SuppressWarnings("Immutable") -final class EvalVarArgsCall implements Interpretable { +final class EvalVarArgsCall extends PlannedInterpretable { private final CelResolvedOverload resolvedOverload; - private final Interpretable[] args; + private final PlannedInterpretable[] args; @Override public Object eval(GlobalResolver resolver) throws CelEvaluationException { Object[] argVals = new Object[args.length]; for (int i = 0; i < args.length; i++) { - Interpretable arg = args[i]; - argVals[i] = arg.eval(resolver); + PlannedInterpretable arg = args[i]; + argVals[i] = + resolvedOverload.isStrict() + ? evalStrictly(arg, resolver) + : evalNonstrictly(arg, resolver); } return resolvedOverload.getDefinition().apply(argVals); @@ -59,11 +64,14 @@ public Object eval( throw new UnsupportedOperationException("Not yet supported"); } - static EvalVarArgsCall create(CelResolvedOverload resolvedOverload, Interpretable[] args) { - return new EvalVarArgsCall(resolvedOverload, args); + static EvalVarArgsCall create( + long exprId, CelResolvedOverload resolvedOverload, PlannedInterpretable[] args) { + return new EvalVarArgsCall(exprId, resolvedOverload, args); } - private EvalVarArgsCall(CelResolvedOverload resolvedOverload, Interpretable[] args) { + private EvalVarArgsCall( + long exprId, CelResolvedOverload resolvedOverload, PlannedInterpretable[] args) { + super(exprId); this.resolvedOverload = resolvedOverload; this.args = args; } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/EvalZeroArity.java b/runtime/src/main/java/dev/cel/runtime/planner/EvalZeroArity.java index 813b84629..628e4a70f 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/EvalZeroArity.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/EvalZeroArity.java @@ -19,10 +19,8 @@ import dev.cel.runtime.CelFunctionResolver; import dev.cel.runtime.CelResolvedOverload; import dev.cel.runtime.GlobalResolver; -import dev.cel.runtime.Interpretable; - -final class EvalZeroArity implements Interpretable { +final class EvalZeroArity extends PlannedInterpretable { private static final Object[] EMPTY_ARRAY = new Object[0]; private final CelResolvedOverload resolvedOverload; @@ -53,11 +51,12 @@ public Object eval( throw new UnsupportedOperationException("Not yet supported"); } - static EvalZeroArity create(CelResolvedOverload resolvedOverload) { - return new EvalZeroArity(resolvedOverload); + static EvalZeroArity create(long exprId, CelResolvedOverload resolvedOverload) { + return new EvalZeroArity(exprId, resolvedOverload); } - private EvalZeroArity(CelResolvedOverload resolvedOverload) { + private EvalZeroArity(long exprId, CelResolvedOverload resolvedOverload) { + super(exprId); this.resolvedOverload = resolvedOverload; } } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/PlannedInterpretable.java b/runtime/src/main/java/dev/cel/runtime/planner/PlannedInterpretable.java new file mode 100644 index 000000000..87a1a7dc4 --- /dev/null +++ b/runtime/src/main/java/dev/cel/runtime/planner/PlannedInterpretable.java @@ -0,0 +1,31 @@ +// 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.errorprone.annotations.Immutable; +import dev.cel.runtime.Interpretable; + +@Immutable +abstract class PlannedInterpretable implements Interpretable { + private final long exprId; + + long exprId() { + return exprId; + } + + PlannedInterpretable(long exprId) { + this.exprId = exprId; + } +} diff --git a/runtime/src/main/java/dev/cel/runtime/planner/PlannedProgram.java b/runtime/src/main/java/dev/cel/runtime/planner/PlannedProgram.java index 2c0d402c2..095839bb7 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/PlannedProgram.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/PlannedProgram.java @@ -16,21 +16,22 @@ import com.google.auto.value.AutoValue; import com.google.errorprone.annotations.Immutable; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelRuntimeException; import dev.cel.common.values.ErrorValue; import dev.cel.runtime.Activation; import dev.cel.runtime.CelEvaluationException; import dev.cel.runtime.CelEvaluationExceptionBuilder; 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(); + abstract PlannedInterpretable interpretable(); + + abstract ErrorMetadata metadata(); @Override public Object eval() throws CelEvaluationException { @@ -48,34 +49,36 @@ public Object eval(Map mapValue, CelFunctionResolver lateBoundFunctio throw new UnsupportedOperationException("Late bound functions not supported yet"); } - private Object evalOrThrow(Interpretable interpretable, GlobalResolver resolver) + private Object evalOrThrow(PlannedInterpretable interpretable, GlobalResolver resolver) throws CelEvaluationException { try { Object evalResult = interpretable.eval(resolver); if (evalResult instanceof ErrorValue) { ErrorValue errorValue = (ErrorValue) evalResult; - throw newCelEvaluationException(errorValue.value()); + throw newCelEvaluationException(errorValue.exprId(), errorValue.value()); } return evalResult; } catch (RuntimeException e) { - throw newCelEvaluationException(e); + throw newCelEvaluationException(interpretable.exprId(), e); } } - private static CelEvaluationException newCelEvaluationException(Exception e) { + private CelEvaluationException newCelEvaluationException(long exprId, Exception e) { CelEvaluationExceptionBuilder builder; - if (e instanceof CelRuntimeException) { + if (e instanceof StrictErrorException) { // Preserve detailed error, including error codes if one exists. + builder = CelEvaluationExceptionBuilder.newBuilder((CelRuntimeException) e.getCause()); + } else if (e instanceof CelRuntimeException) { builder = CelEvaluationExceptionBuilder.newBuilder((CelRuntimeException) e); } else { builder = CelEvaluationExceptionBuilder.newBuilder(e.getMessage()).setCause(e); } - return builder.build(); + return builder.setMetadata(metadata(), exprId).build(); } - static Program create(Interpretable interpretable) { - return new AutoValue_PlannedProgram(interpretable); + static Program create(PlannedInterpretable interpretable, ErrorMetadata metadata) { + return new AutoValue_PlannedProgram(interpretable, metadata); } } diff --git a/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java b/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java index bf7729c0f..252695ed7 100644 --- a/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java +++ b/runtime/src/main/java/dev/cel/runtime/planner/ProgramPlanner.java @@ -66,17 +66,19 @@ public final class ProgramPlanner { * CelAbstractSyntaxTree}. */ public Program plan(CelAbstractSyntaxTree ast) throws CelEvaluationException { - Interpretable plannedInterpretable; + PlannedInterpretable plannedInterpretable; try { plannedInterpretable = plan(ast.getExpr(), PlannerContext.create(ast)); } catch (RuntimeException e) { throw CelEvaluationExceptionBuilder.newBuilder(e.getMessage()).setCause(e).build(); } - return PlannedProgram.create(plannedInterpretable); + ErrorMetadata errorMetadata = + ErrorMetadata.create(ast.getSource().getPositionsMap(), ast.getSource().getDescription()); + return PlannedProgram.create(plannedInterpretable, errorMetadata); } - private Interpretable plan(CelExpr celExpr, PlannerContext ctx) { + private PlannedInterpretable plan(CelExpr celExpr, PlannerContext ctx) { switch (celExpr.getKind()) { case CONSTANT: return planConstant(celExpr.constant()); @@ -97,7 +99,7 @@ private Interpretable plan(CelExpr celExpr, PlannerContext ctx) { } } - private Interpretable planConstant(CelConstant celConstant) { + private PlannedInterpretable planConstant(CelConstant celConstant) { switch (celConstant.getKind()) { case NULL_VALUE: return EvalConstant.create(celConstant.nullValue()); @@ -118,16 +120,17 @@ private Interpretable planConstant(CelConstant celConstant) { } } - private Interpretable planIdent(CelExpr celExpr, PlannerContext ctx) { + private PlannedInterpretable planIdent(CelExpr celExpr, PlannerContext ctx) { CelReference ref = ctx.referenceMap().get(celExpr.id()); if (ref != null) { return planCheckedIdent(celExpr.id(), ref, ctx.typeMap()); } - return EvalAttribute.create(attributeFactory.newMaybeAttribute(celExpr.ident().name())); + return EvalAttribute.create( + celExpr.id(), attributeFactory.newMaybeAttribute(celExpr.ident().name())); } - private Interpretable planCheckedIdent( + private PlannedInterpretable planCheckedIdent( long id, CelReference identRef, ImmutableMap typeMap) { if (identRef.value().isPresent()) { return planConstant(identRef.value().get()); @@ -146,10 +149,10 @@ private Interpretable planCheckedIdent( return EvalConstant.create(identType); } - return EvalAttribute.create(attributeFactory.newAbsoluteAttribute(identRef.name())); + return EvalAttribute.create(id, attributeFactory.newAbsoluteAttribute(identRef.name())); } - private Interpretable planCall(CelExpr expr, PlannerContext ctx) { + private PlannedInterpretable planCall(CelExpr expr, PlannerContext ctx) { ResolvedFunction resolvedFunction = resolveFunction(expr, ctx.referenceMap()); CelExpr target = resolvedFunction.target().orElse(null); int argCount = expr.call().args().size(); @@ -157,7 +160,7 @@ private Interpretable planCall(CelExpr expr, PlannerContext ctx) { argCount++; } - Interpretable[] evaluatedArgs = new Interpretable[argCount]; + PlannedInterpretable[] evaluatedArgs = new PlannedInterpretable[argCount]; int offset = 0; if (target != null) { @@ -175,11 +178,11 @@ private Interpretable planCall(CelExpr expr, PlannerContext ctx) { if (operator != null) { switch (operator) { case LOGICAL_OR: - return EvalOr.create(evaluatedArgs); + return EvalOr.create(expr.id(), evaluatedArgs); case LOGICAL_AND: - return EvalAnd.create(evaluatedArgs); + return EvalAnd.create(expr.id(), evaluatedArgs); case CONDITIONAL: - return EvalConditional.create(evaluatedArgs); + return EvalConditional.create(expr.id(), evaluatedArgs); default: // fall-through } @@ -200,15 +203,15 @@ private Interpretable planCall(CelExpr expr, PlannerContext ctx) { switch (argCount) { case 0: - return EvalZeroArity.create(resolvedOverload); + return EvalZeroArity.create(expr.id(), resolvedOverload); case 1: - return EvalUnary.create(resolvedOverload, evaluatedArgs[0]); + return EvalUnary.create(expr.id(), resolvedOverload, evaluatedArgs[0]); default: - return EvalVarArgsCall.create(resolvedOverload, evaluatedArgs); + return EvalVarArgsCall.create(expr.id(), resolvedOverload, evaluatedArgs); } } - private Interpretable planCreateStruct(CelExpr celExpr, PlannerContext ctx) { + private PlannedInterpretable planCreateStruct(CelExpr celExpr, PlannerContext ctx) { CelStruct struct = celExpr.struct(); StructType structType = resolveStructType(struct); @@ -222,28 +225,28 @@ private Interpretable planCreateStruct(CelExpr celExpr, PlannerContext ctx) { values[i] = plan(entry.value(), ctx); } - return EvalCreateStruct.create(valueProvider, structType, keys, values); + return EvalCreateStruct.create(celExpr.id(), valueProvider, structType, keys, values); } - private Interpretable planCreateList(CelExpr celExpr, PlannerContext ctx) { + private PlannedInterpretable planCreateList(CelExpr celExpr, PlannerContext ctx) { CelList list = celExpr.list(); ImmutableList elements = list.elements(); - Interpretable[] values = new Interpretable[elements.size()]; + PlannedInterpretable[] values = new PlannedInterpretable[elements.size()]; for (int i = 0; i < elements.size(); i++) { values[i] = plan(elements.get(i), ctx); } - return EvalCreateList.create(values); + return EvalCreateList.create(celExpr.id(), values); } - private Interpretable planCreateMap(CelExpr celExpr, PlannerContext ctx) { + private PlannedInterpretable planCreateMap(CelExpr celExpr, PlannerContext ctx) { CelMap map = celExpr.map(); ImmutableList entries = map.entries(); - Interpretable[] keys = new Interpretable[entries.size()]; - Interpretable[] values = new Interpretable[entries.size()]; + PlannedInterpretable[] keys = new PlannedInterpretable[entries.size()]; + PlannedInterpretable[] values = new PlannedInterpretable[entries.size()]; for (int i = 0; i < entries.size(); i++) { CelMap.Entry entry = entries.get(i); @@ -251,7 +254,7 @@ private Interpretable planCreateMap(CelExpr celExpr, PlannerContext ctx) { values[i] = plan(entry.value(), ctx); } - return EvalCreateMap.create(keys, values); + return EvalCreateMap.create(celExpr.id(), keys, values); } /** diff --git a/runtime/src/main/java/dev/cel/runtime/planner/StrictErrorException.java b/runtime/src/main/java/dev/cel/runtime/planner/StrictErrorException.java new file mode 100644 index 000000000..441a1f27c --- /dev/null +++ b/runtime/src/main/java/dev/cel/runtime/planner/StrictErrorException.java @@ -0,0 +1,42 @@ +// 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 dev.cel.common.CelErrorCode; +import dev.cel.common.exceptions.CelRuntimeException; + +/** + * An exception that's raised when a strict call failed to invoke, which includes the source of + * expression ID, along with canonical CelErrorCode. + * + *

Note that StrictErrorException should not be surfaced directly back to the user. + */ +final class StrictErrorException extends CelRuntimeException { + + private final long exprId; + + long exprId() { + return exprId; + } + + StrictErrorException(CelRuntimeException cause, long exprId) { + this(cause, cause.getErrorCode(), exprId); + } + + StrictErrorException(Throwable cause, CelErrorCode errorCode, long exprId) { + super(cause, errorCode); + this.exprId = exprId; + } +} diff --git a/runtime/src/main/java/dev/cel/runtime/standard/AddOperator.java b/runtime/src/main/java/dev/cel/runtime/standard/AddOperator.java index 783ddd81a..84f4e2050 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/AddOperator.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/AddOperator.java @@ -14,15 +14,13 @@ package dev.cel.runtime.standard; -import static dev.cel.runtime.standard.ArithmeticHelpers.getArithmeticErrorCode; - import com.google.common.collect.ImmutableSet; import com.google.common.primitives.UnsignedLong; import com.google.protobuf.ByteString; import com.google.protobuf.Duration; import com.google.protobuf.Timestamp; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelNumericOverflowException; import dev.cel.common.internal.DateTimeHelpers; import dev.cel.common.internal.ProtoTimeUtils; import dev.cel.common.values.CelByteString; @@ -61,7 +59,7 @@ public enum AddOverload implements CelStandardOverload { try { return RuntimeHelpers.int64Add(x, y, celOptions); } catch (ArithmeticException e) { - throw new CelRuntimeException(e, getArithmeticErrorCode(e)); + throw new CelNumericOverflowException(e); } })), ADD_UINT64( @@ -75,7 +73,7 @@ public enum AddOverload implements CelStandardOverload { try { return RuntimeHelpers.uint64Add(x, y); } catch (ArithmeticException e) { - throw new CelRuntimeException(e, getArithmeticErrorCode(e)); + throw new CelNumericOverflowException(e); } }); } else { @@ -87,7 +85,7 @@ public enum AddOverload implements CelStandardOverload { try { return RuntimeHelpers.uint64Add(x, y, celOptions); } catch (ArithmeticException e) { - throw new CelRuntimeException(e, getArithmeticErrorCode(e)); + throw new CelNumericOverflowException(e); } }); } diff --git a/runtime/src/main/java/dev/cel/runtime/standard/BUILD.bazel b/runtime/src/main/java/dev/cel/runtime/standard/BUILD.bazel index 041d07dea..9f1bc708b 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/BUILD.bazel +++ b/runtime/src/main/java/dev/cel/runtime/standard/BUILD.bazel @@ -45,11 +45,10 @@ java_library( tags = [ ], deps = [ - ":arithmetic_helpers", ":standard_function", ":standard_overload", "//common:options", - "//common:runtime_exception", + "//common/exceptions:numeric_overflow", "//common/internal:date_time_helpers", "//common/internal:proto_time_utils", "//common/values:cel_byte_string", @@ -67,11 +66,10 @@ cel_android_library( tags = [ ], deps = [ - ":arithmetic_helpers", ":standard_function_android", ":standard_overload_android", "//common:options", - "//common:runtime_exception", + "//common/exceptions:numeric_overflow", "//common/internal:date_time_helpers_android", "//common/internal:proto_time_utils_android", "//common/values:cel_byte_string", @@ -89,11 +87,10 @@ java_library( tags = [ ], deps = [ - ":arithmetic_helpers", ":standard_function", ":standard_overload", "//common:options", - "//common:runtime_exception", + "//common/exceptions:numeric_overflow", "//common/internal:date_time_helpers", "//common/internal:proto_time_utils", "//runtime:function_binding", @@ -110,11 +107,10 @@ cel_android_library( tags = [ ], deps = [ - ":arithmetic_helpers", ":standard_function_android", ":standard_overload_android", "//common:options", - "//common:runtime_exception", + "//common/exceptions:numeric_overflow", "//common/internal:date_time_helpers_android", "//common/internal:proto_time_utils_android", "//runtime:function_binding_android", @@ -132,9 +128,8 @@ java_library( ], deps = [ ":standard_overload", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", "//common/internal:safe_string_formatter", "//runtime:function_binding", "//runtime:runtime_equality", @@ -151,9 +146,8 @@ cel_android_library( deps = [ ":standard_function_android", ":standard_overload_android", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", "//common/internal:safe_string_formatter", "//runtime:function_binding_android", "//runtime:runtime_equality_android", @@ -232,9 +226,8 @@ java_library( ], deps = [ ":standard_overload", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", "//runtime:function_binding", "//runtime:runtime_equality", "//runtime/standard:standard_function", @@ -250,9 +243,8 @@ cel_android_library( deps = [ ":standard_function_android", ":standard_overload_android", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", "//runtime:function_binding_android", "//runtime:runtime_equality_android", "@maven_android//:com_google_guava_guava", @@ -266,9 +258,8 @@ java_library( ], deps = [ ":standard_overload", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", "//runtime:function_binding", "//runtime:runtime_equality", "//runtime:runtime_helpers", @@ -286,9 +277,8 @@ cel_android_library( deps = [ ":standard_function_android", ":standard_overload_android", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", "//runtime:function_binding_android", "//runtime:runtime_equality_android", "//runtime:runtime_helpers_android", @@ -884,9 +874,9 @@ java_library( ], deps = [ ":standard_overload", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", + "//common/exceptions:numeric_overflow", "//common/internal:proto_time_utils", "//runtime:function_binding", "//runtime:runtime_equality", @@ -905,9 +895,9 @@ cel_android_library( deps = [ ":standard_function_android", ":standard_overload_android", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", + "//common/exceptions:numeric_overflow", "//common/internal:proto_time_utils_android", "//runtime:function_binding_android", "//runtime:runtime_equality_android", @@ -1034,9 +1024,8 @@ java_library( ], deps = [ ":standard_overload", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:invalid_argument", "//runtime:function_binding", "//runtime:runtime_equality", "//runtime:runtime_helpers", @@ -1053,9 +1042,8 @@ cel_android_library( deps = [ ":standard_function_android", ":standard_overload_android", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:invalid_argument", "//runtime:function_binding_android", "//runtime:runtime_equality_android", "//runtime:runtime_helpers_android", @@ -1069,10 +1057,9 @@ java_library( tags = [ ], deps = [ - ":arithmetic_helpers", ":standard_overload", "//common:options", - "//common:runtime_exception", + "//common/exceptions:divide_by_zero", "//runtime:function_binding", "//runtime:runtime_equality", "//runtime:runtime_helpers", @@ -1087,11 +1074,10 @@ cel_android_library( tags = [ ], deps = [ - ":arithmetic_helpers", ":standard_function_android", ":standard_overload_android", "//common:options", - "//common:runtime_exception", + "//common/exceptions:divide_by_zero", "//runtime:function_binding_android", "//runtime:runtime_equality_android", "//runtime:runtime_helpers_android", @@ -1105,10 +1091,9 @@ java_library( tags = [ ], deps = [ - ":arithmetic_helpers", ":standard_overload", "//common:options", - "//common:runtime_exception", + "//common/exceptions:numeric_overflow", "//runtime:function_binding", "//runtime:runtime_equality", "//runtime:runtime_helpers", @@ -1123,11 +1108,10 @@ cel_android_library( tags = [ ], deps = [ - ":arithmetic_helpers", ":standard_function_android", ":standard_overload_android", "//common:options", - "//common:runtime_exception", + "//common/exceptions:numeric_overflow", "//runtime:function_binding_android", "//runtime:runtime_equality_android", "//runtime:runtime_helpers_android", @@ -1141,10 +1125,9 @@ java_library( tags = [ ], deps = [ - ":arithmetic_helpers", ":standard_overload", "//common:options", - "//common:runtime_exception", + "//common/exceptions:divide_by_zero", "//runtime:function_binding", "//runtime:runtime_equality", "//runtime:runtime_helpers", @@ -1159,11 +1142,10 @@ cel_android_library( tags = [ ], deps = [ - ":arithmetic_helpers", ":standard_function_android", ":standard_overload_android", "//common:options", - "//common:runtime_exception", + "//common/exceptions:divide_by_zero", "//runtime:function_binding_android", "//runtime:runtime_equality_android", "//runtime:runtime_helpers_android", @@ -1177,10 +1159,9 @@ java_library( tags = [ ], deps = [ - ":arithmetic_helpers", ":standard_overload", "//common:options", - "//common:runtime_exception", + "//common/exceptions:numeric_overflow", "//runtime:function_binding", "//runtime:runtime_equality", "//runtime:runtime_helpers", @@ -1195,11 +1176,10 @@ cel_android_library( tags = [ ], deps = [ - ":arithmetic_helpers", ":standard_function_android", ":standard_overload_android", "//common:options", - "//common:runtime_exception", + "//common/exceptions:numeric_overflow", "//runtime:function_binding_android", "//runtime:runtime_equality_android", "//runtime:runtime_helpers_android", @@ -1308,9 +1288,8 @@ java_library( ], deps = [ ":standard_overload", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", "//common/internal:date_time_helpers", "//common/internal:proto_time_utils", "//common/values:cel_byte_string", @@ -1330,9 +1309,8 @@ cel_android_library( deps = [ ":standard_function_android", ":standard_overload_android", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", "//common/internal:date_time_helpers_android", "//common/internal:proto_time_utils_android", "//common/values:cel_byte_string", @@ -1350,9 +1328,8 @@ java_library( ], deps = [ ":standard_overload", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", "//common/internal:date_time_helpers", "//common/internal:proto_time_utils", "//runtime:function_binding", @@ -1371,9 +1348,8 @@ cel_android_library( deps = [ ":standard_function_android", ":standard_overload_android", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", "//common/internal:date_time_helpers_android", "//common/internal:proto_time_utils_android", "//runtime:function_binding_android", @@ -1390,9 +1366,9 @@ java_library( ], deps = [ ":standard_overload", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", + "//common/exceptions:numeric_overflow", "//runtime:function_binding", "//runtime:runtime_equality", "//runtime:runtime_helpers", @@ -1409,9 +1385,9 @@ cel_android_library( deps = [ ":standard_function_android", ":standard_overload_android", - "//common:error_codes", "//common:options", - "//common:runtime_exception", + "//common/exceptions:bad_format", + "//common/exceptions:numeric_overflow", "//runtime:function_binding_android", "//runtime:runtime_equality_android", "//runtime:runtime_helpers_android", @@ -1476,13 +1452,3 @@ cel_android_library( "@maven//:com_google_errorprone_error_prone_annotations", ], ) - -java_library( - name = "arithmetic_helpers", - srcs = ["ArithmeticHelpers.java"], - # used_by_android - visibility = ["//visibility:private"], - deps = [ - "//common:error_codes", - ], -) diff --git a/runtime/src/main/java/dev/cel/runtime/standard/BoolFunction.java b/runtime/src/main/java/dev/cel/runtime/standard/BoolFunction.java index 5d9d3919c..330fbe73f 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/BoolFunction.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/BoolFunction.java @@ -15,9 +15,8 @@ package dev.cel.runtime.standard; import com.google.common.collect.ImmutableSet; -import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelBadFormatException; import dev.cel.common.internal.SafeStringFormatter; import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.RuntimeEquality; @@ -64,11 +63,9 @@ public enum BoolOverload implements CelStandardOverload { case "0": return false; default: - throw new CelRuntimeException( - new IllegalArgumentException( - SafeStringFormatter.format( - "Type conversion error from 'string' to 'bool': [%s]", str)), - CelErrorCode.BAD_FORMAT); + throw new CelBadFormatException( + SafeStringFormatter.format( + "Type conversion error from 'string' to 'bool': [%s]", str)); } })); diff --git a/runtime/src/main/java/dev/cel/runtime/standard/DivideOperator.java b/runtime/src/main/java/dev/cel/runtime/standard/DivideOperator.java index b9fdad33c..6a45752dc 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/DivideOperator.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/DivideOperator.java @@ -14,12 +14,10 @@ package dev.cel.runtime.standard; -import static dev.cel.runtime.standard.ArithmeticHelpers.getArithmeticErrorCode; - import com.google.common.collect.ImmutableSet; import com.google.common.primitives.UnsignedLong; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelDivideByZeroException; import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.RuntimeEquality; import dev.cel.runtime.RuntimeHelpers; @@ -57,7 +55,7 @@ public enum DivideOverload implements CelStandardOverload { try { return RuntimeHelpers.int64Divide(x, y, celOptions); } catch (ArithmeticException e) { - throw new CelRuntimeException(e, getArithmeticErrorCode(e)); + throw new CelDivideByZeroException(e); } })), DIVIDE_UINT64( diff --git a/runtime/src/main/java/dev/cel/runtime/standard/DoubleFunction.java b/runtime/src/main/java/dev/cel/runtime/standard/DoubleFunction.java index 508df1983..d2c4769b6 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/DoubleFunction.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/DoubleFunction.java @@ -16,9 +16,8 @@ import com.google.common.collect.ImmutableSet; import com.google.common.primitives.UnsignedLong; -import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelBadFormatException; import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.RuntimeEquality; import java.util.Arrays; @@ -56,7 +55,7 @@ public enum DoubleOverload implements CelStandardOverload { try { return Double.parseDouble(arg); } catch (NumberFormatException e) { - throw new CelRuntimeException(e, CelErrorCode.BAD_FORMAT); + throw new CelBadFormatException(e); } })), UINT64_TO_DOUBLE( diff --git a/runtime/src/main/java/dev/cel/runtime/standard/DurationFunction.java b/runtime/src/main/java/dev/cel/runtime/standard/DurationFunction.java index 72f96f785..b4fd184a6 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/DurationFunction.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/DurationFunction.java @@ -16,9 +16,8 @@ import com.google.common.collect.ImmutableSet; import com.google.protobuf.Duration; -import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelBadFormatException; import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.RuntimeEquality; import dev.cel.runtime.RuntimeHelpers; @@ -62,13 +61,13 @@ public enum DurationOverload implements CelStandardOverload { try { return RuntimeHelpers.createJavaDurationFromString(d); } catch (IllegalArgumentException e) { - throw new CelRuntimeException(e, CelErrorCode.BAD_FORMAT); + throw new CelBadFormatException(e); } } else { try { return RuntimeHelpers.createDurationFromString(d); } catch (IllegalArgumentException e) { - throw new CelRuntimeException(e, CelErrorCode.BAD_FORMAT); + throw new CelBadFormatException(e); } } })), diff --git a/runtime/src/main/java/dev/cel/runtime/standard/IntFunction.java b/runtime/src/main/java/dev/cel/runtime/standard/IntFunction.java index fda307a9d..0c0768026 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/IntFunction.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/IntFunction.java @@ -17,9 +17,9 @@ import com.google.common.collect.ImmutableSet; import com.google.common.primitives.UnsignedLong; import com.google.protobuf.Timestamp; -import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelBadFormatException; +import dev.cel.common.exceptions.CelNumericOverflowException; import dev.cel.common.internal.ProtoTimeUtils; import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.RuntimeEquality; @@ -56,9 +56,7 @@ public enum IntOverload implements CelStandardOverload { UnsignedLong.class, (UnsignedLong arg) -> { if (arg.compareTo(UnsignedLong.valueOf(Long.MAX_VALUE)) > 0) { - throw new CelRuntimeException( - new IllegalArgumentException("unsigned out of int range"), - CelErrorCode.NUMERIC_OVERFLOW); + throw new CelNumericOverflowException("unsigned out of int range"); } return arg.longValue(); }); @@ -68,9 +66,7 @@ public enum IntOverload implements CelStandardOverload { Long.class, (Long arg) -> { if (celOptions.errorOnIntWrap() && arg < 0) { - throw new CelRuntimeException( - new IllegalArgumentException("unsigned out of int range"), - CelErrorCode.NUMERIC_OVERFLOW); + throw new CelNumericOverflowException("unsigned out of int range"); } return arg; }); @@ -86,9 +82,7 @@ public enum IntOverload implements CelStandardOverload { return RuntimeHelpers.doubleToLongChecked(arg) .orElseThrow( () -> - new CelRuntimeException( - new IllegalArgumentException("double is out of range for int"), - CelErrorCode.NUMERIC_OVERFLOW)); + new CelNumericOverflowException("double is out of range for int")); } return arg.longValue(); })), @@ -101,7 +95,7 @@ public enum IntOverload implements CelStandardOverload { try { return Long.parseLong(arg); } catch (NumberFormatException e) { - throw new CelRuntimeException(e, CelErrorCode.BAD_FORMAT); + throw new CelBadFormatException(e); } })), TIMESTAMP_TO_INT64( diff --git a/runtime/src/main/java/dev/cel/runtime/standard/MatchesFunction.java b/runtime/src/main/java/dev/cel/runtime/standard/MatchesFunction.java index 7c24f65a2..2d0a504b8 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/MatchesFunction.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/MatchesFunction.java @@ -15,9 +15,8 @@ package dev.cel.runtime.standard; import com.google.common.collect.ImmutableSet; -import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelInvalidArgumentException; import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.RuntimeEquality; import dev.cel.runtime.RuntimeHelpers; @@ -51,7 +50,7 @@ public enum MatchesOverload implements CelStandardOverload { try { return RuntimeHelpers.matches(string, regexp, celOptions); } catch (RuntimeException e) { - throw new CelRuntimeException(e, CelErrorCode.INVALID_ARGUMENT); + throw new CelInvalidArgumentException(e); } })), // Duplicate receiver-style matches overload. @@ -65,7 +64,7 @@ public enum MatchesOverload implements CelStandardOverload { try { return RuntimeHelpers.matches(string, regexp, celOptions); } catch (RuntimeException e) { - throw new CelRuntimeException(e, CelErrorCode.INVALID_ARGUMENT); + throw new CelInvalidArgumentException(e); } })), ; diff --git a/runtime/src/main/java/dev/cel/runtime/standard/ModuloOperator.java b/runtime/src/main/java/dev/cel/runtime/standard/ModuloOperator.java index e7246851c..787d58de5 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/ModuloOperator.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/ModuloOperator.java @@ -14,12 +14,10 @@ package dev.cel.runtime.standard; -import static dev.cel.runtime.standard.ArithmeticHelpers.getArithmeticErrorCode; - import com.google.common.collect.ImmutableSet; import com.google.common.primitives.UnsignedLong; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelDivideByZeroException; import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.RuntimeEquality; import dev.cel.runtime.RuntimeHelpers; @@ -53,7 +51,7 @@ public enum ModuloOverload implements CelStandardOverload { try { return x % y; } catch (ArithmeticException e) { - throw new CelRuntimeException(e, getArithmeticErrorCode(e)); + throw new CelDivideByZeroException(e); } })), MODULO_UINT64( diff --git a/runtime/src/main/java/dev/cel/runtime/standard/MultiplyOperator.java b/runtime/src/main/java/dev/cel/runtime/standard/MultiplyOperator.java index 7e9e2f352..8a5ad1a56 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/MultiplyOperator.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/MultiplyOperator.java @@ -14,12 +14,10 @@ package dev.cel.runtime.standard; -import static dev.cel.runtime.standard.ArithmeticHelpers.getArithmeticErrorCode; - import com.google.common.collect.ImmutableSet; import com.google.common.primitives.UnsignedLong; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelNumericOverflowException; import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.RuntimeEquality; import dev.cel.runtime.RuntimeHelpers; @@ -53,7 +51,7 @@ public enum MultiplyOverload implements CelStandardOverload { try { return RuntimeHelpers.int64Multiply(x, y, celOptions); } catch (ArithmeticException e) { - throw new CelRuntimeException(e, getArithmeticErrorCode(e)); + throw new CelNumericOverflowException(e); } })), MULTIPLY_DOUBLE( @@ -71,7 +69,7 @@ public enum MultiplyOverload implements CelStandardOverload { try { return RuntimeHelpers.uint64Multiply(x, y); } catch (ArithmeticException e) { - throw new CelRuntimeException(e, getArithmeticErrorCode(e)); + throw new CelNumericOverflowException(e); } }); } else { @@ -83,7 +81,7 @@ public enum MultiplyOverload implements CelStandardOverload { try { return RuntimeHelpers.uint64Multiply(x, y, celOptions); } catch (ArithmeticException e) { - throw new CelRuntimeException(e, getArithmeticErrorCode(e)); + throw new CelNumericOverflowException(e); } }); } diff --git a/runtime/src/main/java/dev/cel/runtime/standard/NegateOperator.java b/runtime/src/main/java/dev/cel/runtime/standard/NegateOperator.java index 2e3f094f5..5ff2a614d 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/NegateOperator.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/NegateOperator.java @@ -14,11 +14,9 @@ package dev.cel.runtime.standard; -import static dev.cel.runtime.standard.ArithmeticHelpers.getArithmeticErrorCode; - import com.google.common.collect.ImmutableSet; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelNumericOverflowException; import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.RuntimeEquality; import dev.cel.runtime.RuntimeHelpers; @@ -51,7 +49,7 @@ public enum NegateOverload implements CelStandardOverload { try { return RuntimeHelpers.int64Negate(x, celOptions); } catch (ArithmeticException e) { - throw new CelRuntimeException(e, getArithmeticErrorCode(e)); + throw new CelNumericOverflowException(e); } })), NEGATE_DOUBLE( diff --git a/runtime/src/main/java/dev/cel/runtime/standard/StringFunction.java b/runtime/src/main/java/dev/cel/runtime/standard/StringFunction.java index c491bfe76..f19a4cd58 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/StringFunction.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/StringFunction.java @@ -20,9 +20,8 @@ import com.google.protobuf.ByteString; import com.google.protobuf.Duration; import com.google.protobuf.Timestamp; -import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelBadFormatException; import dev.cel.common.internal.DateTimeHelpers; import dev.cel.common.internal.ProtoTimeUtils; import dev.cel.common.values.CelByteString; @@ -69,10 +68,8 @@ public enum StringOverload implements CelStandardOverload { CelByteString.class, (byteStr) -> { if (!byteStr.isValidUtf8()) { - throw new CelRuntimeException( - new IllegalArgumentException( - "invalid UTF-8 in bytes, cannot convert to string"), - CelErrorCode.BAD_FORMAT); + throw new CelBadFormatException( + "invalid UTF-8 in bytes, cannot convert to string"); } return byteStr.toStringUtf8(); }); @@ -82,10 +79,8 @@ public enum StringOverload implements CelStandardOverload { ByteString.class, (byteStr) -> { if (!byteStr.isValidUtf8()) { - throw new CelRuntimeException( - new IllegalArgumentException( - "invalid UTF-8 in bytes, cannot convert to string"), - CelErrorCode.BAD_FORMAT); + throw new CelBadFormatException( + "invalid UTF-8 in bytes, cannot convert to string"); } return byteStr.toStringUtf8(); }); diff --git a/runtime/src/main/java/dev/cel/runtime/standard/SubtractOperator.java b/runtime/src/main/java/dev/cel/runtime/standard/SubtractOperator.java index 4b0ecd3ae..fe8b75230 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/SubtractOperator.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/SubtractOperator.java @@ -14,14 +14,12 @@ package dev.cel.runtime.standard; -import static dev.cel.runtime.standard.ArithmeticHelpers.getArithmeticErrorCode; - import com.google.common.collect.ImmutableSet; import com.google.common.primitives.UnsignedLong; import com.google.protobuf.Duration; import com.google.protobuf.Timestamp; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelNumericOverflowException; import dev.cel.common.internal.DateTimeHelpers; import dev.cel.common.internal.ProtoTimeUtils; import dev.cel.runtime.CelFunctionBinding; @@ -58,7 +56,7 @@ public enum SubtractOverload implements CelStandardOverload { try { return RuntimeHelpers.int64Subtract(x, y, celOptions); } catch (ArithmeticException e) { - throw new CelRuntimeException(e, getArithmeticErrorCode(e)); + throw new CelNumericOverflowException(e); } })), SUBTRACT_TIMESTAMP_TIMESTAMP( @@ -104,7 +102,7 @@ public enum SubtractOverload implements CelStandardOverload { try { return RuntimeHelpers.uint64Subtract(x, y); } catch (ArithmeticException e) { - throw new CelRuntimeException(e, getArithmeticErrorCode(e)); + throw new CelNumericOverflowException(e); } }); } else { @@ -116,7 +114,7 @@ public enum SubtractOverload implements CelStandardOverload { try { return RuntimeHelpers.uint64Subtract(x, y, celOptions); } catch (ArithmeticException e) { - throw new CelRuntimeException(e, getArithmeticErrorCode(e)); + throw new CelNumericOverflowException(e); } }); } diff --git a/runtime/src/main/java/dev/cel/runtime/standard/TimestampFunction.java b/runtime/src/main/java/dev/cel/runtime/standard/TimestampFunction.java index 34d3a1dc0..bffac3b06 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/TimestampFunction.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/TimestampFunction.java @@ -16,9 +16,8 @@ import com.google.common.collect.ImmutableSet; import com.google.protobuf.Timestamp; -import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelBadFormatException; import dev.cel.common.internal.DateTimeHelpers; import dev.cel.common.internal.ProtoTimeUtils; import dev.cel.runtime.CelFunctionBinding; @@ -56,14 +55,14 @@ public enum TimestampOverload implements CelStandardOverload { try { return DateTimeHelpers.parse(ts); } catch (DateTimeParseException e) { - throw new CelRuntimeException(e, CelErrorCode.BAD_FORMAT); + throw new CelBadFormatException(e); } } else { try { return ProtoTimeUtils.parse(ts); } catch (ParseException e) { - throw new CelRuntimeException(e, CelErrorCode.BAD_FORMAT); + throw new CelBadFormatException(e); } } })), diff --git a/runtime/src/main/java/dev/cel/runtime/standard/UintFunction.java b/runtime/src/main/java/dev/cel/runtime/standard/UintFunction.java index 89424e7fc..0548d8532 100644 --- a/runtime/src/main/java/dev/cel/runtime/standard/UintFunction.java +++ b/runtime/src/main/java/dev/cel/runtime/standard/UintFunction.java @@ -17,9 +17,9 @@ import com.google.common.collect.ImmutableSet; import com.google.common.primitives.UnsignedLong; import com.google.common.primitives.UnsignedLongs; -import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelBadFormatException; +import dev.cel.common.exceptions.CelNumericOverflowException; import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.RuntimeEquality; import dev.cel.runtime.RuntimeHelpers; @@ -61,9 +61,7 @@ public enum UintOverload implements CelStandardOverload { Long.class, (Long arg) -> { if (celOptions.errorOnIntWrap() && arg < 0) { - throw new CelRuntimeException( - new IllegalArgumentException("int out of uint range"), - CelErrorCode.NUMERIC_OVERFLOW); + throw new CelNumericOverflowException("int out of uint range"); } return UnsignedLong.valueOf(arg); }); @@ -73,9 +71,7 @@ public enum UintOverload implements CelStandardOverload { Long.class, (Long arg) -> { if (celOptions.errorOnIntWrap() && arg < 0) { - throw new CelRuntimeException( - new IllegalArgumentException("int out of uint range"), - CelErrorCode.NUMERIC_OVERFLOW); + throw new CelNumericOverflowException("int out of uint range"); } return arg; }); @@ -91,10 +87,7 @@ public enum UintOverload implements CelStandardOverload { if (celOptions.errorOnIntWrap()) { return RuntimeHelpers.doubleToUnsignedChecked(arg) .orElseThrow( - () -> - new CelRuntimeException( - new IllegalArgumentException("double out of uint range"), - CelErrorCode.NUMERIC_OVERFLOW)); + () -> new CelNumericOverflowException("double out of uint range")); } return UnsignedLong.valueOf(BigDecimal.valueOf(arg).toBigInteger()); }); @@ -108,9 +101,8 @@ public enum UintOverload implements CelStandardOverload { .map(UnsignedLong::longValue) .orElseThrow( () -> - new CelRuntimeException( - new IllegalArgumentException("double out of uint range"), - CelErrorCode.NUMERIC_OVERFLOW)); + new CelNumericOverflowException( + "double out of uint range")); } return arg.longValue(); }); @@ -126,7 +118,7 @@ public enum UintOverload implements CelStandardOverload { try { return UnsignedLong.valueOf(arg); } catch (NumberFormatException e) { - throw new CelRuntimeException(e, CelErrorCode.BAD_FORMAT); + throw new CelBadFormatException(e); } }); } else { @@ -137,7 +129,7 @@ public enum UintOverload implements CelStandardOverload { try { return UnsignedLongs.parseUnsignedLong(arg); } catch (NumberFormatException e) { - throw new CelRuntimeException(e, CelErrorCode.BAD_FORMAT); + throw new CelBadFormatException(e); } }); } diff --git a/runtime/src/test/java/dev/cel/runtime/BUILD.bazel b/runtime/src/test/java/dev/cel/runtime/BUILD.bazel index 1541e9c84..4c5a619f3 100644 --- a/runtime/src/test/java/dev/cel/runtime/BUILD.bazel +++ b/runtime/src/test/java/dev/cel/runtime/BUILD.bazel @@ -37,8 +37,11 @@ java_library( "//common:error_codes", "//common:options", "//common:proto_v1alpha1_ast", - "//common:runtime_exception", "//common/ast", + "//common/exceptions:bad_format", + "//common/exceptions:divide_by_zero", + "//common/exceptions:numeric_overflow", + "//common/exceptions:runtime_exception", "//common/internal:cel_descriptor_pools", "//common/internal:converter", "//common/internal:default_message_factory", diff --git a/runtime/src/test/java/dev/cel/runtime/CelEvaluationExceptionBuilderTest.java b/runtime/src/test/java/dev/cel/runtime/CelEvaluationExceptionBuilderTest.java index b3bc32e20..c073a8443 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelEvaluationExceptionBuilderTest.java +++ b/runtime/src/test/java/dev/cel/runtime/CelEvaluationExceptionBuilderTest.java @@ -18,7 +18,8 @@ import com.google.testing.junit.testparameterinjector.TestParameterInjector; import dev.cel.common.CelErrorCode; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelBadFormatException; +import dev.cel.common.exceptions.CelRuntimeException; import org.junit.Test; import org.junit.runner.RunWith; @@ -87,15 +88,14 @@ public boolean hasPosition(long exprId) { @Test public void builder_fromCelRuntimeException() { IllegalStateException cause = new IllegalStateException("cause error message"); - CelRuntimeException celRuntimeException = - new CelRuntimeException(cause, CelErrorCode.BAD_FORMAT); + CelRuntimeException celRuntimeException = new CelBadFormatException(cause); CelEvaluationExceptionBuilder builder = CelEvaluationExceptionBuilder.newBuilder(celRuntimeException); CelEvaluationException e = builder.build(); assertThat(e).hasMessageThat().isEqualTo("evaluation error: cause error message"); - assertThat(e).hasCauseThat().isEqualTo(cause); + assertThat(e).hasCauseThat().isEqualTo(celRuntimeException); assertThat(e.getErrorCode()).isEqualTo(CelErrorCode.BAD_FORMAT); } } diff --git a/runtime/src/test/java/dev/cel/runtime/CelRuntimeLegacyImplTest.java b/runtime/src/test/java/dev/cel/runtime/CelRuntimeLegacyImplTest.java index c4a041f6a..fa3b5f4ae 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelRuntimeLegacyImplTest.java +++ b/runtime/src/test/java/dev/cel/runtime/CelRuntimeLegacyImplTest.java @@ -18,6 +18,7 @@ import com.google.protobuf.Message; import dev.cel.common.CelException; +import dev.cel.common.exceptions.CelDivideByZeroException; import dev.cel.common.values.CelValueProvider; import dev.cel.compiler.CelCompiler; import dev.cel.compiler.CelCompilerFactory; @@ -39,7 +40,7 @@ public void evalException() throws CelException { CelRuntime runtime = CelRuntimeFactory.standardCelRuntimeBuilder().build(); CelRuntime.Program program = runtime.createProgram(compiler.compile("1/0").getAst()); CelEvaluationException e = Assert.assertThrows(CelEvaluationException.class, program::eval); - assertThat(e).hasCauseThat().isInstanceOf(ArithmeticException.class); + assertThat(e).hasCauseThat().isInstanceOf(CelDivideByZeroException.class); } @Test diff --git a/runtime/src/test/java/dev/cel/runtime/DescriptorMessageProviderTest.java b/runtime/src/test/java/dev/cel/runtime/DescriptorMessageProviderTest.java index 8866d1fa6..6ab1638b1 100644 --- a/runtime/src/test/java/dev/cel/runtime/DescriptorMessageProviderTest.java +++ b/runtime/src/test/java/dev/cel/runtime/DescriptorMessageProviderTest.java @@ -31,7 +31,7 @@ import dev.cel.common.CelDescriptors; import dev.cel.common.CelErrorCode; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelRuntimeException; import dev.cel.common.internal.CelDescriptorPool; import dev.cel.common.internal.DefaultDescriptorPool; import dev.cel.common.internal.DefaultMessageFactory; @@ -114,8 +114,8 @@ public void createMessage_missingDescriptorError() { () -> provider.createMessage( "google.api.tools.contract.test.MissingMessageTypes", ImmutableMap.of())); - assertThat(e).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(e.getErrorCode()).isEqualTo(CelErrorCode.ATTRIBUTE_NOT_FOUND); + assertThat(e).hasCauseThat().isNull(); } @Test @@ -159,8 +159,8 @@ public void selectField_mapKeyNotFound() { CelRuntimeException e = Assert.assertThrows( CelRuntimeException.class, () -> provider.selectField(ImmutableMap.of(), "hello")); - assertThat(e).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(e.getErrorCode()).isEqualTo(CelErrorCode.ATTRIBUTE_NOT_FOUND); + assertThat(e).hasCauseThat().isNull(); } @Test @@ -177,8 +177,8 @@ public void selectField_nonProtoObjectError() { CelRuntimeException e = Assert.assertThrows( CelRuntimeException.class, () -> provider.selectField("hello", "not_a_field")); - assertThat(e).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(e.getErrorCode()).isEqualTo(CelErrorCode.ATTRIBUTE_NOT_FOUND); + assertThat(e).hasCauseThat().isNull(); } @Test diff --git a/runtime/src/test/java/dev/cel/runtime/ProtoMessageRuntimeEqualityTest.java b/runtime/src/test/java/dev/cel/runtime/ProtoMessageRuntimeEqualityTest.java index 16806c856..dc4607e5f 100644 --- a/runtime/src/test/java/dev/cel/runtime/ProtoMessageRuntimeEqualityTest.java +++ b/runtime/src/test/java/dev/cel/runtime/ProtoMessageRuntimeEqualityTest.java @@ -42,7 +42,7 @@ import com.google.rpc.context.AttributeContext.Request; import dev.cel.common.CelDescriptorUtil; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelRuntimeException; import dev.cel.common.internal.AdaptingTypes; import dev.cel.common.internal.BidiConverter; import dev.cel.common.internal.DefaultDescriptorPool; diff --git a/runtime/src/test/java/dev/cel/runtime/ProtoMessageRuntimeHelpersTest.java b/runtime/src/test/java/dev/cel/runtime/ProtoMessageRuntimeHelpersTest.java index f6d701741..45b050fd1 100644 --- a/runtime/src/test/java/dev/cel/runtime/ProtoMessageRuntimeHelpersTest.java +++ b/runtime/src/test/java/dev/cel/runtime/ProtoMessageRuntimeHelpersTest.java @@ -36,7 +36,8 @@ import com.google.protobuf.UInt64Value; import com.google.protobuf.Value; import dev.cel.common.CelOptions; -import dev.cel.common.CelRuntimeException; +import dev.cel.common.exceptions.CelNumericOverflowException; +import dev.cel.common.exceptions.CelRuntimeException; import dev.cel.common.internal.DefaultMessageFactory; import dev.cel.common.internal.DynamicProto; import dev.cel.common.values.CelByteString; @@ -91,7 +92,7 @@ public void int64Divide() throws Exception { assertThat(ProtoMessageRuntimeHelpers.int64Divide(Long.MIN_VALUE, -1, CelOptions.LEGACY)) .isEqualTo(Long.MIN_VALUE); assertThrows( - ArithmeticException.class, + CelNumericOverflowException.class, () -> ProtoMessageRuntimeHelpers.int64Divide(Long.MIN_VALUE, -1, CelOptions.DEFAULT)); } @@ -171,7 +172,7 @@ public void uint64Add_signedLongs() throws Exception { assertThat(ProtoMessageRuntimeHelpers.uint64Add(4, 4, CelOptions.DEFAULT)).isEqualTo(8); assertThat(ProtoMessageRuntimeHelpers.uint64Add(-1, 1, CelOptions.LEGACY)).isEqualTo(0); assertThrows( - ArithmeticException.class, + CelNumericOverflowException.class, () -> ProtoMessageRuntimeHelpers.uint64Add(-1, 1, CelOptions.DEFAULT)); } @@ -185,7 +186,7 @@ public void uint64Add_unsignedLongs() throws Exception { UnsignedLong.MAX_VALUE.minus(UnsignedLong.ONE), UnsignedLong.ONE)) .isEqualTo(UnsignedLong.MAX_VALUE); assertThrows( - ArithmeticException.class, + CelNumericOverflowException.class, () -> ProtoMessageRuntimeHelpers.uint64Add(UnsignedLong.MAX_VALUE, UnsignedLong.ONE)); } @@ -202,7 +203,7 @@ public void uint64Multiply_signedLongs() throws Exception { .build())) .isEqualTo(0); assertThrows( - ArithmeticException.class, + CelNumericOverflowException.class, () -> ProtoMessageRuntimeHelpers.uint64Multiply(Long.MIN_VALUE, 2, CelOptions.DEFAULT)); } @@ -213,7 +214,7 @@ public void uint64Multiply_unsignedLongs() throws Exception { UnsignedLong.valueOf(32), UnsignedLong.valueOf(2))) .isEqualTo(UnsignedLong.valueOf(64)); assertThrows( - ArithmeticException.class, + CelNumericOverflowException.class, () -> ProtoMessageRuntimeHelpers.uint64Multiply( UnsignedLong.MAX_VALUE, UnsignedLong.valueOf(2))); @@ -295,13 +296,13 @@ public void uint64Subtract_signedLongs() throws Exception { assertThat(ProtoMessageRuntimeHelpers.uint64Subtract(-1, 2, CelOptions.DEFAULT)).isEqualTo(-3); assertThat(ProtoMessageRuntimeHelpers.uint64Subtract(0, 1, CelOptions.LEGACY)).isEqualTo(-1); assertThrows( - ArithmeticException.class, + CelNumericOverflowException.class, () -> ProtoMessageRuntimeHelpers.uint64Subtract(0, 1, CelOptions.DEFAULT)); assertThrows( - ArithmeticException.class, + CelNumericOverflowException.class, () -> ProtoMessageRuntimeHelpers.uint64Subtract(-3, -1, CelOptions.DEFAULT)); assertThrows( - ArithmeticException.class, + CelNumericOverflowException.class, () -> ProtoMessageRuntimeHelpers.uint64Subtract(55, -40, CelOptions.DEFAULT)); } @@ -314,7 +315,7 @@ public void uint64Subtract_unsignedLongs() throws Exception { UnsignedLong.valueOf(3), UnsignedLong.valueOf(2))) .isEqualTo(UnsignedLong.ONE); assertThrows( - ArithmeticException.class, + CelNumericOverflowException.class, () -> ProtoMessageRuntimeHelpers.uint64Subtract(UnsignedLong.ONE, UnsignedLong.valueOf(2))); } diff --git a/runtime/src/test/java/dev/cel/runtime/planner/BUILD.bazel b/runtime/src/test/java/dev/cel/runtime/planner/BUILD.bazel index 9e3a79ed9..7ddbf07dc 100644 --- a/runtime/src/test/java/dev/cel/runtime/planner/BUILD.bazel +++ b/runtime/src/test/java/dev/cel/runtime/planner/BUILD.bazel @@ -23,6 +23,7 @@ java_library( "//common:operator", "//common:options", "//common/ast", + "//common/exceptions:divide_by_zero", "//common/internal:cel_descriptor_pools", "//common/internal:default_message_factory", "//common/internal:dynamic_proto", diff --git a/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java b/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java index 205e2ef8b..d73809967 100644 --- a/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java +++ b/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java @@ -38,6 +38,7 @@ import dev.cel.common.CelSource; import dev.cel.common.Operator; import dev.cel.common.ast.CelExpr; +import dev.cel.common.exceptions.CelDivideByZeroException; import dev.cel.common.internal.CelDescriptorPool; import dev.cel.common.internal.DefaultDescriptorPool; import dev.cel.common.internal.DefaultMessageFactory; @@ -421,7 +422,7 @@ public void plan_call_throws() throws Exception { Program program = PLANNER.plan(ast); CelEvaluationException e = assertThrows(CelEvaluationException.class, program::eval); - assertThat(e).hasMessageThat().contains("evaluation error: Intentional error"); + assertThat(e).hasMessageThat().contains("evaluation error at :5: Intentional error"); assertThat(e).hasCauseThat().isInstanceOf(IllegalArgumentException.class); } @@ -514,9 +515,9 @@ public void plan_call_logicalOr_throws(String expression) throws Exception { Program program = PLANNER.plan(ast); CelEvaluationException e = assertThrows(CelEvaluationException.class, program::eval); - // TODO: Tag metadata (source loc) - assertThat(e).hasMessageThat().isEqualTo("evaluation error: / by zero"); - assertThat(e).hasCauseThat().isInstanceOf(ArithmeticException.class); + assertThat(e).hasMessageThat().startsWith("evaluation error at :"); + assertThat(e).hasMessageThat().endsWith("/ by zero"); + assertThat(e).hasCauseThat().isInstanceOf(CelDivideByZeroException.class); assertThat(e.getErrorCode()).isEqualTo(CelErrorCode.DIVIDE_BY_ZERO); } @@ -546,9 +547,9 @@ public void plan_call_logicalAnd_throws(String expression) throws Exception { Program program = PLANNER.plan(ast); CelEvaluationException e = assertThrows(CelEvaluationException.class, program::eval); - // TODO: Tag metadata (source loc) - assertThat(e).hasMessageThat().isEqualTo("evaluation error: / by zero"); - assertThat(e).hasCauseThat().isInstanceOf(ArithmeticException.class); + assertThat(e).hasMessageThat().startsWith("evaluation error at :"); + assertThat(e).hasMessageThat().endsWith("/ by zero"); + assertThat(e).hasCauseThat().isInstanceOf(CelDivideByZeroException.class); assertThat(e.getErrorCode()).isEqualTo(CelErrorCode.DIVIDE_BY_ZERO); } @@ -576,8 +577,9 @@ public void plan_call_conditional_throws(String expression) throws Exception { Program program = PLANNER.plan(ast); CelEvaluationException e = assertThrows(CelEvaluationException.class, program::eval); - assertThat(e).hasMessageThat().isEqualTo("evaluation error: / by zero"); - assertThat(e).hasCauseThat().isInstanceOf(ArithmeticException.class); + assertThat(e).hasMessageThat().startsWith("evaluation error at :"); + assertThat(e).hasMessageThat().endsWith("/ by zero"); + assertThat(e).hasCauseThat().isInstanceOf(CelDivideByZeroException.class); assertThat(e.getErrorCode()).isEqualTo(CelErrorCode.DIVIDE_BY_ZERO); }