From c84d5015de303ebbac7926293a32130afc20c718 Mon Sep 17 00:00:00 2001 From: Sokwhan Huh Date: Wed, 5 Nov 2025 15:18:38 -0800 Subject: [PATCH] Remove CelEvaluationException checked exception from function overloads PiperOrigin-RevId: 828650061 --- .../test/java/dev/cel/bundle/CelImplTest.java | 9 +- .../main/java/dev/cel/extensions/BUILD.bazel | 2 + .../cel/extensions/CelStringExtensions.java | 136 ++++++++++-------- .../dev/cel/runtime/CelFunctionOverload.java | 6 +- .../runtime/CelLateFunctionBindingsTest.java | 9 +- .../java/dev/cel/runtime/CelRuntimeTest.java | 8 ++ 6 files changed, 97 insertions(+), 73 deletions(-) diff --git a/bundle/src/test/java/dev/cel/bundle/CelImplTest.java b/bundle/src/test/java/dev/cel/bundle/CelImplTest.java index 00b47494d..24304dc27 100644 --- a/bundle/src/test/java/dev/cel/bundle/CelImplTest.java +++ b/bundle/src/test/java/dev/cel/bundle/CelImplTest.java @@ -103,7 +103,6 @@ import dev.cel.runtime.CelAttribute.Qualifier; import dev.cel.runtime.CelAttributePattern; import dev.cel.runtime.CelEvaluationException; -import dev.cel.runtime.CelEvaluationExceptionBuilder; import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.CelRuntime; import dev.cel.runtime.CelRuntime.Program; @@ -742,13 +741,13 @@ public void program_withThrowingFunction() throws Exception { "throws", ImmutableList.of(), (args) -> { - throw new CelEvaluationException("this method always throws"); + throw new RuntimeException("this method always throws"); })) .setResultType(SimpleType.BOOL) .build(); CelRuntime.Program program = cel.createProgram(cel.compile("throws()").getAst()); CelEvaluationException e = Assert.assertThrows(CelEvaluationException.class, program::eval); - assertThat(e).hasMessageThat().contains("this method always throws"); + assertThat(e.getCause()).hasMessageThat().contains("this method always throws"); } @Test @@ -770,9 +769,7 @@ public void program_withThrowingFunctionShortcircuited() throws Exception { "throws", ImmutableList.of(), (args) -> { - throw CelEvaluationExceptionBuilder.newBuilder("this method always throws") - .setCause(new RuntimeException("reason")) - .build(); + throw new RuntimeException("this method always throws"); })) .setResultType(SimpleType.BOOL) .build(); diff --git a/extensions/src/main/java/dev/cel/extensions/BUILD.bazel b/extensions/src/main/java/dev/cel/extensions/BUILD.bazel index 9b897cf84..e7ea39a2c 100644 --- a/extensions/src/main/java/dev/cel/extensions/BUILD.bazel +++ b/extensions/src/main/java/dev/cel/extensions/BUILD.bazel @@ -84,6 +84,8 @@ java_library( deps = [ "//checker:checker_builder", "//common:compiler_common", + "//common:error_codes", + "//common:runtime_exception", "//common/internal", "//common/types", "//compiler:compiler_builder", diff --git a/extensions/src/main/java/dev/cel/extensions/CelStringExtensions.java b/extensions/src/main/java/dev/cel/extensions/CelStringExtensions.java index 37b2a368b..3761f5627 100644 --- a/extensions/src/main/java/dev/cel/extensions/CelStringExtensions.java +++ b/extensions/src/main/java/dev/cel/extensions/CelStringExtensions.java @@ -26,14 +26,14 @@ import com.google.common.collect.Lists; import com.google.errorprone.annotations.Immutable; import dev.cel.checker.CelCheckerBuilder; +import dev.cel.common.CelErrorCode; import dev.cel.common.CelFunctionDecl; import dev.cel.common.CelOverloadDecl; +import dev.cel.common.CelRuntimeException; import dev.cel.common.internal.CelCodePointArray; import dev.cel.common.types.ListType; import dev.cel.common.types.SimpleType; import dev.cel.compiler.CelCompilerLibrary; -import dev.cel.runtime.CelEvaluationException; -import dev.cel.runtime.CelEvaluationExceptionBuilder; import dev.cel.runtime.CelFunctionBinding; import dev.cel.runtime.CelRuntimeBuilder; import dev.cel.runtime.CelRuntimeLibrary; @@ -253,7 +253,7 @@ String getFunction() { } private static final CelExtensionLibrary LIBRARY = - new CelExtensionLibrary() { + new CelExtensionLibrary<>() { private final CelStringExtensions version0 = new CelStringExtensions(); @Override @@ -291,15 +291,15 @@ public void setRuntimeOptions(CelRuntimeBuilder runtimeBuilder) { functions.forEach(function -> runtimeBuilder.addFunctionBindings(function.functionBindings)); } - private static String charAt(String s, long i) throws CelEvaluationException { + private static String charAt(String s, long i) { int index; try { index = Math.toIntExact(i); } catch (ArithmeticException e) { - throw CelEvaluationExceptionBuilder.newBuilder( - "charAt failure: Index must not exceed the int32 range: %d", i) - .setCause(e) - .build(); + throw new CelRuntimeException( + new IllegalArgumentException( + String.format("charAt failure: Index must not exceed the int32 range: %d", i), e), + CelErrorCode.INDEX_OUT_OF_BOUNDS); } CelCodePointArray codePointArray = CelCodePointArray.fromString(s); @@ -307,15 +307,16 @@ private static String charAt(String s, long i) throws CelEvaluationException { return ""; } if (index < 0 || index > codePointArray.length()) { - throw CelEvaluationExceptionBuilder.newBuilder( - "charAt failure: Index out of range: %d", index) - .build(); + throw new CelRuntimeException( + new IllegalArgumentException( + String.format("charAt failure: Index out of range: %d", index)), + CelErrorCode.INDEX_OUT_OF_BOUNDS); } return codePointArray.slice(index, index + 1).toString(); } - private static Long indexOf(String str, String substr) throws CelEvaluationException { + private static Long indexOf(String str, String substr) { Object[] params = {str, substr, 0L}; return indexOf(params); } @@ -323,7 +324,7 @@ private static Long indexOf(String str, String substr) throws CelEvaluationExcep /** * @param args Object array with indices of: [0: string], [1: substring], [2: offset] */ - private static Long indexOf(Object[] args) throws CelEvaluationException { + private static Long indexOf(Object[] args) { String str = (String) args[0]; String substr = (String) args[1]; long offsetInLong = (Long) args[2]; @@ -331,16 +332,18 @@ private static Long indexOf(Object[] args) throws CelEvaluationException { try { offset = Math.toIntExact(offsetInLong); } catch (ArithmeticException e) { - throw CelEvaluationExceptionBuilder.newBuilder( - "indexOf failure: Offset must not exceed the int32 range: %d", offsetInLong) - .setCause(e) - .build(); + throw new CelRuntimeException( + new IllegalArgumentException( + String.format( + "indexOf failure: Offset must not exceed the int32 range: %d", offsetInLong), + e), + CelErrorCode.INDEX_OUT_OF_BOUNDS); } return indexOf(str, substr, offset); } - private static Long indexOf(String str, String substr, int offset) throws CelEvaluationException { + private static Long indexOf(String str, String substr, int offset) { if (substr.isEmpty()) { return (long) offset; } @@ -349,9 +352,10 @@ private static Long indexOf(String str, String substr, int offset) throws CelEva CelCodePointArray substrCpa = CelCodePointArray.fromString(substr); if (offset < 0 || offset >= strCpa.length()) { - throw CelEvaluationExceptionBuilder.newBuilder( - "indexOf failure: Offset out of range: %d", offset) - .build(); + throw new CelRuntimeException( + new IllegalArgumentException( + String.format("indexOf failure: Offset out of range: %d", offset)), + CelErrorCode.INDEX_OUT_OF_BOUNDS); } return safeIndexOf(strCpa, substrCpa, offset); @@ -384,7 +388,7 @@ private static String join(List stringList, String separator) { return Joiner.on(separator).join(stringList); } - private static Long lastIndexOf(String str, String substr) throws CelEvaluationException { + private static Long lastIndexOf(String str, String substr) { CelCodePointArray strCpa = CelCodePointArray.fromString(str); CelCodePointArray substrCpa = CelCodePointArray.fromString(substr); if (substrCpa.isEmpty()) { @@ -398,7 +402,7 @@ private static Long lastIndexOf(String str, String substr) throws CelEvaluationE return lastIndexOf(strCpa, substrCpa, (long) strCpa.length() - 1); } - private static Long lastIndexOf(Object[] args) throws CelEvaluationException { + private static Long lastIndexOf(Object[] args) { CelCodePointArray strCpa = CelCodePointArray.fromString((String) args[0]); CelCodePointArray substrCpa = CelCodePointArray.fromString((String) args[1]); long offset = (long) args[2]; @@ -406,8 +410,7 @@ private static Long lastIndexOf(Object[] args) throws CelEvaluationException { return lastIndexOf(strCpa, substrCpa, offset); } - private static Long lastIndexOf(CelCodePointArray str, CelCodePointArray substr, long offset) - throws CelEvaluationException { + private static Long lastIndexOf(CelCodePointArray str, CelCodePointArray substr, long offset) { if (substr.isEmpty()) { return offset; } @@ -416,16 +419,19 @@ private static Long lastIndexOf(CelCodePointArray str, CelCodePointArray substr, try { off = Math.toIntExact(offset); } catch (ArithmeticException e) { - throw CelEvaluationExceptionBuilder.newBuilder( - "lastIndexOf failure: Offset must not exceed the int32 range: %d", offset) - .setCause(e) - .build(); + throw new CelRuntimeException( + new IllegalArgumentException( + String.format( + "lastIndexOf failure: Offset must not exceed the int32 range: %d", offset), + e), + CelErrorCode.INDEX_OUT_OF_BOUNDS); } if (off < 0 || off >= str.length()) { - throw CelEvaluationExceptionBuilder.newBuilder( - "lastIndexOf failure: Offset out of range: %d", offset) - .build(); + throw new CelRuntimeException( + new IllegalArgumentException( + String.format("lastIndexOf failure: Offset out of range: %d", offset)), + CelErrorCode.INDEX_OUT_OF_BOUNDS); } if (off > str.length() - substr.length()) { @@ -452,16 +458,18 @@ private static String replaceAll(Object[] objects) { return replace((String) objects[0], (String) objects[1], (String) objects[2], -1); } - private static String replace(Object[] objects) throws CelEvaluationException { + private static String replace(Object[] objects) { Long indexInLong = (Long) objects[3]; int index; try { index = Math.toIntExact(indexInLong); } catch (ArithmeticException e) { - throw CelEvaluationExceptionBuilder.newBuilder( - "replace failure: Index must not exceed the int32 range: %d", indexInLong) - .setCause(e) - .build(); + throw new CelRuntimeException( + new IllegalArgumentException( + String.format( + "replace failure: Index must not exceed the int32 range: %d", indexInLong), + e), + CelErrorCode.INDEX_OUT_OF_BOUNDS); } return replace((String) objects[0], (String) objects[1], (String) objects[2], index); @@ -510,16 +518,18 @@ private static List split(String str, String separator) { /** * @param args Object array with indices of: [0: string], [1: separator], [2: limit] */ - private static List split(Object[] args) throws CelEvaluationException { + private static List split(Object[] args) { long limitInLong = (Long) args[2]; int limit; try { limit = Math.toIntExact(limitInLong); } catch (ArithmeticException e) { - throw CelEvaluationExceptionBuilder.newBuilder( - "split failure: Limit must not exceed the int32 range: %d", limitInLong) - .setCause(e) - .build(); + throw new CelRuntimeException( + new IllegalArgumentException( + String.format( + "split failure: Limit must not exceed the int32 range: %d", limitInLong), + e), + CelErrorCode.INDEX_OUT_OF_BOUNDS); } return split((String) args[0], (String) args[1], limit); @@ -575,25 +585,27 @@ private static List explode(String str, int limit) { return exploded; } - private static Object substring(String s, long i) throws CelEvaluationException { + private static Object substring(String s, long i) { int beginIndex; try { beginIndex = Math.toIntExact(i); } catch (ArithmeticException e) { - throw CelEvaluationExceptionBuilder.newBuilder( - "substring failure: Index must not exceed the int32 range: %d", i) - .setCause(e) - .build(); + throw new CelRuntimeException( + new IllegalArgumentException( + String.format("substring failure: Index must not exceed the int32 range: %d", i), e), + CelErrorCode.INDEX_OUT_OF_BOUNDS); } CelCodePointArray codePointArray = CelCodePointArray.fromString(s); boolean indexIsInRange = beginIndex <= codePointArray.length() && beginIndex >= 0; if (!indexIsInRange) { - throw CelEvaluationExceptionBuilder.newBuilder( - "substring failure: Range [%d, %d) out of bounds", - beginIndex, codePointArray.length()) - .build(); + throw new CelRuntimeException( + new IllegalArgumentException( + String.format( + "substring failure: Range [%d, %d) out of bounds", + beginIndex, codePointArray.length())), + CelErrorCode.INDEX_OUT_OF_BOUNDS); } if (beginIndex == codePointArray.length()) { @@ -606,7 +618,7 @@ private static Object substring(String s, long i) throws CelEvaluationException /** * @param args Object array with indices of [0: string], [1: beginIndex], [2: endIndex] */ - private static String substring(Object[] args) throws CelEvaluationException { + private static String substring(Object[] args) { Long beginIndexInLong = (Long) args[1]; Long endIndexInLong = (Long) args[2]; int beginIndex; @@ -615,11 +627,13 @@ private static String substring(Object[] args) throws CelEvaluationException { beginIndex = Math.toIntExact(beginIndexInLong); endIndex = Math.toIntExact(endIndexInLong); } catch (ArithmeticException e) { - throw CelEvaluationExceptionBuilder.newBuilder( - "substring failure: Indices must not exceed the int32 range: [%d, %d)", - beginIndexInLong, endIndexInLong) - .setCause(e) - .build(); + throw new CelRuntimeException( + new IllegalArgumentException( + String.format( + "substring failure: Indices must not exceed the int32 range: [%d, %d)", + beginIndexInLong, endIndexInLong), + e), + CelErrorCode.INDEX_OUT_OF_BOUNDS); } String s = (String) args[0]; @@ -631,9 +645,11 @@ private static String substring(Object[] args) throws CelEvaluationException { && beginIndex <= codePointArray.length() && endIndex <= codePointArray.length(); if (!indicesIsInRange) { - throw CelEvaluationExceptionBuilder.newBuilder( - "substring failure: Range [%d, %d) out of bounds", beginIndex, endIndex) - .build(); + throw new CelRuntimeException( + new IllegalArgumentException( + String.format( + "substring failure: Range [%d, %d) out of bounds", beginIndex, endIndex)), + CelErrorCode.INDEX_OUT_OF_BOUNDS); } if (beginIndex == endIndex) { diff --git a/runtime/src/main/java/dev/cel/runtime/CelFunctionOverload.java b/runtime/src/main/java/dev/cel/runtime/CelFunctionOverload.java index a1341cb21..9d4d4c6a9 100644 --- a/runtime/src/main/java/dev/cel/runtime/CelFunctionOverload.java +++ b/runtime/src/main/java/dev/cel/runtime/CelFunctionOverload.java @@ -22,7 +22,7 @@ public interface CelFunctionOverload { /** Evaluate a set of arguments throwing a {@code CelException} on error. */ - Object apply(Object[] args) throws CelEvaluationException; + Object apply(Object[] args); /** * Helper interface for describing unary functions where the type-parameter is used to improve @@ -31,7 +31,7 @@ public interface CelFunctionOverload { @Immutable @FunctionalInterface interface Unary { - Object apply(T arg) throws CelEvaluationException; + Object apply(T arg); } /** @@ -41,6 +41,6 @@ interface Unary { @Immutable @FunctionalInterface interface Binary { - Object apply(T1 arg1, T2 arg2) throws CelEvaluationException; + Object apply(T1 arg1, T2 arg2); } } diff --git a/runtime/src/test/java/dev/cel/runtime/CelLateFunctionBindingsTest.java b/runtime/src/test/java/dev/cel/runtime/CelLateFunctionBindingsTest.java index 3f9e1e105..ccb92a7d1 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelLateFunctionBindingsTest.java +++ b/runtime/src/test/java/dev/cel/runtime/CelLateFunctionBindingsTest.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.primitives.UnsignedLong; import dev.cel.common.CelErrorCode; +import dev.cel.common.CelRuntimeException; import dev.cel.expr.conformance.proto3.TestAllTypes; import java.util.Optional; import org.junit.Assert; @@ -83,8 +84,8 @@ public void findOverload_badInput_throwsException() throws Exception { UnsignedLong.class, (arg) -> { if (arg.equals(UnsignedLong.MAX_VALUE)) { - throw new CelEvaluationException( - "numeric overflow", null, CelErrorCode.NUMERIC_OVERFLOW); + throw new CelRuntimeException( + new ArithmeticException("numeric overflow"), CelErrorCode.NUMERIC_OVERFLOW); } return arg.plus(UnsignedLong.ONE); })); @@ -94,9 +95,9 @@ public void findOverload_badInput_throwsException() throws Exception { assertThat(overload).isPresent(); assertThat(overload.get().getOverloadId()).isEqualTo("increment_uint"); assertThat(overload.get().getParameterTypes()).containsExactly(UnsignedLong.class); - CelEvaluationException e = + CelRuntimeException e = Assert.assertThrows( - CelEvaluationException.class, + CelRuntimeException.class, () -> overload.get().getDefinition().apply(new Object[] {UnsignedLong.MAX_VALUE})); assertThat(e.getErrorCode()).isEqualTo(CelErrorCode.NUMERIC_OVERFLOW); } diff --git a/runtime/src/test/java/dev/cel/runtime/CelRuntimeTest.java b/runtime/src/test/java/dev/cel/runtime/CelRuntimeTest.java index 39723083e..ce3d87fe7 100644 --- a/runtime/src/test/java/dev/cel/runtime/CelRuntimeTest.java +++ b/runtime/src/test/java/dev/cel/runtime/CelRuntimeTest.java @@ -733,4 +733,12 @@ public void standardEnvironmentDisabledForRuntime_throws() throws Exception { .hasMessageThat() .contains("No matching overload for function 'size'. Overload candidates: size_string"); } + + @Test + public void smokeTest() throws Exception { + Cel cel = CelFactory.standardCelBuilder().addVar("foo", SimpleType.INT).build(); + CelAbstractSyntaxTree ast = cel.compile("foo").getAst(); + + cel.createProgram(ast).eval(); + } }