diff --git a/extensions/src/main/java/dev/cel/extensions/BUILD.bazel b/extensions/src/main/java/dev/cel/extensions/BUILD.bazel index e36c526fa..3cded5e17 100644 --- a/extensions/src/main/java/dev/cel/extensions/BUILD.bazel +++ b/extensions/src/main/java/dev/cel/extensions/BUILD.bazel @@ -362,6 +362,7 @@ java_library( "//runtime:function_binding", "//runtime:lite_runtime", "//runtime:runtime_equality", + "//runtime:runtime_helpers", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", "@maven//:com_google_protobuf_protobuf_java", @@ -382,6 +383,7 @@ cel_android_library( "//runtime:function_binding_android", "//runtime:lite_runtime_android", "//runtime:runtime_equality_android", + "//runtime:runtime_helpers_android", "@maven//:com_google_errorprone_error_prone_annotations", "@maven_android//:com_google_guava_guava", "@maven_android//:com_google_protobuf_protobuf_javalite", diff --git a/extensions/src/main/java/dev/cel/extensions/CelOptionalRuntimeLibrary.java b/extensions/src/main/java/dev/cel/extensions/CelOptionalRuntimeLibrary.java index 1959e99a8..68bd507b6 100644 --- a/extensions/src/main/java/dev/cel/extensions/CelOptionalRuntimeLibrary.java +++ b/extensions/src/main/java/dev/cel/extensions/CelOptionalRuntimeLibrary.java @@ -17,7 +17,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; -import com.google.common.primitives.Ints; import com.google.common.primitives.UnsignedLong; import com.google.errorprone.annotations.Immutable; import com.google.protobuf.MessageLite; @@ -30,6 +29,7 @@ import dev.cel.runtime.CelInternalLiteRuntimeLibrary; import dev.cel.runtime.CelLiteRuntimeBuilder; import dev.cel.runtime.RuntimeEquality; +import dev.cel.runtime.RuntimeHelpers; import java.time.Duration; import java.time.Instant; import java.util.Collection; @@ -222,10 +222,7 @@ public ImmutableSet newFunctionBindings(RuntimeEquality runt CelFunctionBinding.fromOverloads( Operator.OPTIONAL_INDEX.getFunction(), CelFunctionBinding.from( - "list_optindex_optional_int", - List.class, - Long.class, - CelOptionalRuntimeLibrary::indexList), + "list_optindex_optional_int", List.class, Long.class, RuntimeHelpers::optIndexList), CelFunctionBinding.from( "optional_list_optindex_optional_int", Optional.class, @@ -312,20 +309,12 @@ private static Object indexOptionalMap( return runtimeEquality.findInMap(map, key); } - private static Object indexList(List list, long index) { - int castIndex = Ints.checkedCast(index); - if (castIndex < 0 || castIndex >= list.size()) { - return Optional.empty(); - } - return Optional.of(list.get(castIndex)); - } - private static Object indexOptionalList(Optional optionalList, long index) { if (!optionalList.isPresent()) { return Optional.empty(); } List list = (List) optionalList.get(); - return indexList(list, index); + return RuntimeHelpers.optIndexList(list, index); } private static Object listOptionalFirst(Collection list) { diff --git a/extensions/src/test/java/dev/cel/extensions/CelOptionalLibraryTest.java b/extensions/src/test/java/dev/cel/extensions/CelOptionalLibraryTest.java index 7c228de41..68fb42b5e 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelOptionalLibraryTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelOptionalLibraryTest.java @@ -1123,6 +1123,38 @@ public void optionalIndex_onOptionalList_returnsOptionalValue() throws Exception assertThat(result).isEqualTo(Optional.of("hello")); } + @Test + public void optionalIndex_outOfBounds_returnsOptionalEmpty( + @TestParameter({ + "l[?-1]", + "l[?1]", + "l[?4294967296]", + "l[?-4294967296]", + "optl[?4294967296]", + "optl[4294967296]" + }) + String expression) + throws Exception { + TestCel cel = + newCelBuilder() + .addVar("l", ListType.create(SimpleType.STRING)) + .addVar("optl", OptionalType.create(ListType.create(SimpleType.STRING))) + .setResultType(OptionalType.create(SimpleType.STRING)) + .build(); + CelAbstractSyntaxTree ast = compile(cel, expression); + + Object result = + cel.createProgram(ast) + .eval( + ImmutableMap.of( + "l", + ImmutableList.of("hello"), + "optl", + Optional.of(ImmutableList.of("hello")))); + + assertThat(result).isEqualTo(Optional.empty()); + } + @Test public void optionalIndex_onListWithUnknownInput_returnsUnknownResult() throws Exception { TestCel cel = diff --git a/runtime/src/main/java/dev/cel/runtime/RuntimeHelpers.java b/runtime/src/main/java/dev/cel/runtime/RuntimeHelpers.java index 7444ffef1..fb2ef35d6 100644 --- a/runtime/src/main/java/dev/cel/runtime/RuntimeHelpers.java +++ b/runtime/src/main/java/dev/cel/runtime/RuntimeHelpers.java @@ -140,6 +140,14 @@ public static A indexList(List list, Number index) { return list.get((int) longIndex); } + /** Returns the element of {@code list} at {@code index}, or empty if it's out of bounds. */ + public static Optional optIndexList(List list, long index) { + if (index < 0 || index >= list.size()) { + return Optional.empty(); + } + return Optional.of(list.get((int) index)); + } + // Integer Arithmetic // ================== //