diff --git a/common/BUILD.bazel b/common/BUILD.bazel index 1c751bf53..16f8812ee 100644 --- a/common/BUILD.bazel +++ b/common/BUILD.bazel @@ -166,6 +166,11 @@ java_library( exports = ["//common/src/main/java/dev/cel/common:proto_json_adapter"], ) +cel_android_library( + name = "proto_json_adapter_android", + exports = ["//common/src/main/java/dev/cel/common:proto_json_adapter_android"], +) + java_library( name = "source", visibility = ["//:internal"], diff --git a/common/internal/BUILD.bazel b/common/internal/BUILD.bazel index 7c33e56b9..2fdcdbc25 100644 --- a/common/internal/BUILD.bazel +++ b/common/internal/BUILD.bazel @@ -42,6 +42,11 @@ java_library( exports = ["//common/src/main/java/dev/cel/common/internal:proto_lite_adapter"], ) +cel_android_library( + name = "proto_lite_adapter_android", + exports = ["//common/src/main/java/dev/cel/common/internal:proto_lite_adapter_android"], +) + java_library( name = "proto_equality", exports = ["//common/src/main/java/dev/cel/common/internal:proto_equality"], diff --git a/common/src/main/java/dev/cel/common/BUILD.bazel b/common/src/main/java/dev/cel/common/BUILD.bazel index d69cb0b1f..741af88aa 100644 --- a/common/src/main/java/dev/cel/common/BUILD.bazel +++ b/common/src/main/java/dev/cel/common/BUILD.bazel @@ -439,6 +439,22 @@ java_library( ], ) +cel_android_library( + name = "proto_json_adapter_android", + srcs = ["CelProtoJsonAdapter.java"], + tags = [ + ], + deps = [ + "//common/internal:date_time_helpers_android", + "//common/internal:proto_time_utils_android", + "//common/values:cel_byte_string", + "//common/values:values_android", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven_android//:com_google_guava_guava", + "@maven_android//:com_google_protobuf_protobuf_javalite", + ], +) + java_library( name = "source_location", srcs = ["CelSourceLocation.java"], 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 9de68482e..9a2d56fba 100644 --- a/common/src/main/java/dev/cel/common/internal/BUILD.bazel +++ b/common/src/main/java/dev/cel/common/internal/BUILD.bazel @@ -184,7 +184,6 @@ java_library( ":well_known_proto", "//common:options", "//common/annotations", - "//common/exceptions:numeric_overflow", "//common/values", "//common/values:cel_byte_string", "@maven//:com_google_code_findbugs_annotations", @@ -214,6 +213,26 @@ java_library( ], ) +cel_android_library( + name = "proto_lite_adapter_android", + srcs = ["ProtoLiteAdapter.java"], + tags = [ + ], + deps = [ + ":well_known_proto_android", + "//common:options", + "//common:proto_json_adapter_android", + "//common/annotations", + "//common/exceptions:numeric_overflow", + "//common/internal:proto_time_utils_android", + "//common/values:cel_byte_string", + "//common/values:values_android", + "@maven//:com_google_errorprone_error_prone_annotations", + "@maven_android//:com_google_guava_guava", + "@maven_android//:com_google_protobuf_protobuf_javalite", + ], +) + java_library( name = "proto_equality", srcs = ["ProtoEquality.java"], 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 b1b56afe1..b872df831 100644 --- a/common/src/main/java/dev/cel/common/internal/ProtoAdapter.java +++ b/common/src/main/java/dev/cel/common/internal/ProtoAdapter.java @@ -15,8 +15,9 @@ package dev.cel.common.internal; import static com.google.common.base.Preconditions.checkNotNull; +import static dev.cel.common.internal.ProtoLiteAdapter.intCheckedCast; +import static dev.cel.common.internal.ProtoLiteAdapter.unsignedIntCheckedCast; -import com.google.common.primitives.Ints; import com.google.common.primitives.UnsignedInts; import com.google.common.primitives.UnsignedLong; import com.google.errorprone.annotations.CheckReturnValue; @@ -33,7 +34,6 @@ import com.google.protobuf.MessageOrBuilder; import dev.cel.common.CelOptions; 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; @@ -347,14 +347,10 @@ private BidiConverter fieldToValueConverter(FieldDescriptor fieldDescriptor) { case ENUM: return BidiConverter.of( value -> (long) ((EnumValueDescriptor) value).getNumber(), - number -> { - if (number > Integer.MAX_VALUE || number < Integer.MIN_VALUE) { - throw new IllegalArgumentException("Enum value out of int32 range: " + number); - } - return fieldDescriptor - .getEnumType() - .findValueByNumberCreatingIfUnknown(number.intValue()); - }); + number -> + fieldDescriptor + .getEnumType() + .findValueByNumberCreatingIfUnknown(intCheckedCast(number))); case MESSAGE: return BidiConverter.of( this::adaptProtoToValue, @@ -401,9 +397,9 @@ public Message adaptValueToProto(Object value, String protoTypeName) { switch (wellKnownProto) { case ANY_VALUE: if (value instanceof Message) { - protoTypeName = ((Message) value).getDescriptorForType().getFullName(); + return Any.pack((Message) value); } - return protoLiteAdapter.adaptValueToAny(value, protoTypeName); + return protoLiteAdapter.adaptValueToAny(value); default: return (Message) protoLiteAdapter.adaptValueToWellKnownProto(value, wellKnownProto); } @@ -430,22 +426,6 @@ private static String typeName(Descriptor protoType) { return protoType.getFullName(); } - private static int intCheckedCast(long value) { - try { - return Ints.checkedCast(value); - } catch (IllegalArgumentException e) { - throw new CelNumericOverflowException(e); - } - } - - private static int unsignedIntCheckedCast(long value) { - try { - return UnsignedInts.checkedCast(value); - } catch (IllegalArgumentException e) { - throw new CelNumericOverflowException(e); - } - } - private Object maybeUnwrap(Object value) { if (value instanceof Message) { return adaptProtoToValue((MessageOrBuilder) value); 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 8131d0eec..4a4e2ea26 100644 --- a/common/src/main/java/dev/cel/common/internal/ProtoLiteAdapter.java +++ b/common/src/main/java/dev/cel/common/internal/ProtoLiteAdapter.java @@ -32,7 +32,6 @@ import com.google.protobuf.Int32Value; import com.google.protobuf.Int64Value; import com.google.protobuf.ListValue; -import com.google.protobuf.Message; import com.google.protobuf.MessageLite; import com.google.protobuf.MessageLiteOrBuilder; import com.google.protobuf.NullValue; @@ -110,11 +109,7 @@ public MessageLite adaptValueToWellKnownProto(Object value, WellKnownProto wellK } } - public Any adaptValueToAny(Object value, String typeName) { - if (value instanceof MessageLite) { - return packAnyMessage((MessageLite) value, typeName); - } - + public Any adaptValueToAny(Object value) { if (value instanceof dev.cel.common.values.NullValue) { return packAnyMessage( Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(), WellKnownProto.JSON_VALUE); @@ -234,7 +229,7 @@ private ImmutableMap adaptJsonStructToValue(Struct struct) { .collect(toImmutableMap(Entry::getKey, e -> adaptJsonToValue(e.getValue()))); } - private Message adaptValueToDouble(Object value) { + private MessageLite adaptValueToDouble(Object value) { if (value instanceof Double) { return DoubleValue.of((Double) value); } @@ -244,7 +239,7 @@ private Message adaptValueToDouble(Object value) { throw new IllegalArgumentException("Unexpected value type: " + value); } - private Message adaptValueToFloat(Object value) { + private MessageLite adaptValueToFloat(Object value) { if (value instanceof Double) { return FloatValue.of(((Double) value).floatValue()); } @@ -254,7 +249,7 @@ private Message adaptValueToFloat(Object value) { throw new IllegalArgumentException("Unexpected value type: " + value); } - private Message adaptValueToInt32(Object value) { + private MessageLite adaptValueToInt32(Object value) { if (value instanceof Integer) { return Int32Value.of((Integer) value); } @@ -264,7 +259,7 @@ private Message adaptValueToInt32(Object value) { throw new IllegalArgumentException("Unexpected value type: " + value); } - private Message adaptValueToInt64(Object value) { + private MessageLite adaptValueToInt64(Object value) { if (value instanceof Integer) { return Int64Value.of(((Integer) value).longValue()); } @@ -275,30 +270,22 @@ private Message adaptValueToInt64(Object value) { throw new IllegalArgumentException("Unexpected value type: " + value); } - private Message adaptValueToUint32(Object value) { + private MessageLite adaptValueToUint32(Object value) { if (value instanceof Integer) { return UInt32Value.of((Integer) value); } // Per the spec, an int isn't a uint, unless unsigned longs are disabled. if (!celOptions.enableUnsignedLongs() && value instanceof Long) { - try { - return UInt32Value.of(unsignedIntCheckedCast((Long) value)); - } catch (IllegalArgumentException e) { - throw new CelNumericOverflowException(e); - } + return UInt32Value.of(unsignedIntCheckedCast((Long) value)); } if (value instanceof UnsignedLong) { - try { - return UInt32Value.of(unsignedIntCheckedCast(((UnsignedLong) value).longValue())); - } catch (IllegalArgumentException e) { - throw new CelNumericOverflowException(e); - } + return UInt32Value.of(unsignedIntCheckedCast(((UnsignedLong) value).longValue())); } throw new IllegalArgumentException("Unexpected value type: " + value); } - private Message adaptValueToUint64(Object value) { + private MessageLite adaptValueToUint64(Object value) { if (value instanceof Integer) { return UInt64Value.of(UnsignedInts.toLong((Integer) value)); } @@ -312,7 +299,7 @@ private Message adaptValueToUint64(Object value) { throw new IllegalArgumentException("Unexpected value type: " + value); } - private static int intCheckedCast(long value) { + static int intCheckedCast(long value) { try { return Ints.checkedCast(value); } catch (IllegalArgumentException e) { @@ -320,7 +307,7 @@ private static int intCheckedCast(long value) { } } - private static int unsignedIntCheckedCast(long value) { + static int unsignedIntCheckedCast(long value) { try { return UnsignedInts.checkedCast(value); } catch (IllegalArgumentException e) { diff --git a/common/src/main/java/dev/cel/common/values/ProtoLiteCelValueConverter.java b/common/src/main/java/dev/cel/common/values/ProtoLiteCelValueConverter.java index 40d74924d..735caaa0d 100644 --- a/common/src/main/java/dev/cel/common/values/ProtoLiteCelValueConverter.java +++ b/common/src/main/java/dev/cel/common/values/ProtoLiteCelValueConverter.java @@ -145,7 +145,8 @@ private Object mergeOrReadMessageField( ByteString bytes, String fieldProtoTypeName, @Nullable Object existingValue, boolean isView) { MessageLiteDescriptor descriptor = descriptorPool.findDescriptor(fieldProtoTypeName).orElse(null); - if (descriptor == null) { + // An Any can name a map entry, which can't be built. + if (descriptor == null || descriptor.isMapEntry()) { if (existingValue instanceof RawProtoMessageLiteValue) { bytes = ((RawProtoMessageLiteValue) existingValue).toByteString().concat(bytes); } diff --git a/common/src/test/java/dev/cel/common/values/ProtoLiteCelValueConverterTest.java b/common/src/test/java/dev/cel/common/values/ProtoLiteCelValueConverterTest.java index f4c80a862..7cb9bef36 100644 --- a/common/src/test/java/dev/cel/common/values/ProtoLiteCelValueConverterTest.java +++ b/common/src/test/java/dev/cel/common/values/ProtoLiteCelValueConverterTest.java @@ -210,6 +210,18 @@ public void toRuntimeValue_anyWithUnregisteredMessage_returnsRawValueOfPackedTyp assertThat(rawValue.toByteString()).isEqualTo(msg.toByteString()); } + @Test + public void toRuntimeValue_anyWithMapEntry_returnsRawValueOfPackedType() { + String mapEntryTypeName = PROTO3_TEST_ALL_TYPES + ".MapStringStringEntry"; + Any any = Any.newBuilder().setTypeUrl("type.googleapis.com/" + mapEntryTypeName).build(); + + Object adaptedValue = PROTO_LITE_CEL_VALUE_CONVERTER.toRuntimeValue(any); + + assertThat(adaptedValue).isInstanceOf(RawProtoMessageLiteValue.class); + assertThat(((RawProtoMessageLiteValue) adaptedValue).protoTypeName()) + .isEqualTo(mapEntryTypeName); + } + @Test public void toRuntimeValue_anyNestedToMaxDepth_unpacks() { Object adaptedValue = PROTO_LITE_CEL_VALUE_CONVERTER.toRuntimeValue(nestedAny(100)); diff --git a/protobuf/src/main/java/dev/cel/protobuf/CelLiteDescriptor.java b/protobuf/src/main/java/dev/cel/protobuf/CelLiteDescriptor.java index e2b299445..a99845bfd 100644 --- a/protobuf/src/main/java/dev/cel/protobuf/CelLiteDescriptor.java +++ b/protobuf/src/main/java/dev/cel/protobuf/CelLiteDescriptor.java @@ -45,6 +45,9 @@ public abstract class CelLiteDescriptor { Collections.unmodifiableSet( new HashSet<>(Arrays.asList(false, 0, 0L, 0.0f, 0.0d, "", ByteString.EMPTY))); + // Map entries have no generated class to build. + private static final Supplier NO_BUILDER = () -> null; + @SuppressWarnings("Immutable") // Copied to unmodifiable map private final Map protoFqnToDescriptors; @@ -121,6 +124,11 @@ public MessageLite.Builder newMessageBuilder() { return messageBuilderSupplier.get(); } + /** Whether this describes a map field's entry, which has no message builder. */ + public boolean isMapEntry() { + return messageBuilderSupplier.equals(NO_BUILDER); + } + /** Whether {@link #getFieldValue} reads this message's fields with their generated getters. */ public boolean hasGetters() { return owner != null; @@ -415,7 +423,7 @@ protected static MessageLiteDescriptor mapEntry( valueType, false, valueTypeName)), - () -> null); + NO_BUILDER); } protected CelLiteDescriptor(String version, List messageInfoList) { 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 24160aef1..84515b88b 100644 --- a/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java +++ b/runtime/src/test/java/dev/cel/runtime/planner/ProgramPlannerTest.java @@ -709,6 +709,16 @@ public void plan_createStruct_oneofSetTwice_lastFieldWins( assertThat(result.getKindCase()).isEqualTo(expectedCase); } + @Test + public void plan_createStruct_enumOutOfRange_throwsNumericOverflow() throws Exception { + CelAbstractSyntaxTree ast = compile("TestAllTypes{standalone_enum: 5000000000}"); + Program program = PLANNER.plan(ast); + + CelEvaluationException e = assertThrows(CelEvaluationException.class, program::eval); + + assertThat(e.getErrorCode()).isEqualTo(CelErrorCode.NUMERIC_OVERFLOW); + } + @Test public void plan_call_zeroArgs() throws Exception { CelAbstractSyntaxTree ast = compile("zero()");