From 2414cf486447f1ce3397fc417df2237d35eb6ee6 Mon Sep 17 00:00:00 2001 From: Dave MacLachlan Date: Mon, 20 Apr 2026 13:49:04 -0700 Subject: [PATCH] Refactor: Improve map entry parsing in GeneratedMessage.mm This change adds validation for map key types and ensures that default values are used for missing key or value fields when parsing map entries from a stream. Error handling is also improved by checking the return value of ReadMapEntryField. By adding a couple of judicious autoreleases, it also protects us from memory leaks in case of bad maps. PiperOrigin-RevId: 902805678 --- .../protobuf/compiler/j2objc/j2objc_enum.cc | 2 +- .../protobuf/compiler/j2objc/j2objc_field.cc | 30 +- .../protobuf/compiler/j2objc/j2objc_field.h | 3 - .../compiler/j2objc/j2objc_message.cc | 13 +- .../src/com/google/protobuf/Descriptors.h | 8 +- .../src/com/google/protobuf/Descriptors.m | 133 +++-- .../protobuf/Descriptors_PackagePrivate.h | 143 ++++- .../src/com/google/protobuf/ExtensionLite.h | 4 +- .../src/com/google/protobuf/ExtensionLite.m | 4 +- .../com/google/protobuf/ExtensionRegistry.mm | 2 +- .../src/com/google/protobuf/FieldTypes.h | 255 ++++----- .../com/google/protobuf/GeneratedMessage.mm | 508 +++++++++--------- .../src/com/google/protobuf/MapField.h | 4 +- .../src/com/google/protobuf/MapField.m | 22 +- .../src/com/google/protobuf/RepeatedField.h | 8 - .../src/com/google/protobuf/RepeatedField.m | 60 +-- protobuf/tests/MapsTest.java | 134 ++++- protobuf/tests/Proto3EnumTest.java | 37 ++ protobuf/tests/protos/map_fields.proto | 70 ++- protobuf/tests/protos/proto3_enum.proto | 2 + 20 files changed, 853 insertions(+), 589 deletions(-) diff --git a/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_enum.cc b/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_enum.cc index d47e061dcb..7ebd77492f 100644 --- a/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_enum.cc +++ b/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_enum.cc @@ -255,7 +255,7 @@ void EnumGenerator::GenerateSource(io::Printer* printer) { "\n" "$classname$ *$classname$_values_[$count$];\n" "\n" - "ComGoogleProtobufDescriptors_EnumDescriptor" + "static ComGoogleProtobufDescriptors_EnumDescriptor" " *$classname$_descriptor_ = nil;\n" "\n" "@implementation $classname$\n" diff --git a/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_field.cc b/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_field.cc index a1a2ff967c..89715cc8b2 100644 --- a/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_field.cc +++ b/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_field.cc @@ -178,10 +178,6 @@ void CollectForwardDeclarationsForFieldType(std::set* declarations, declarations->insert("@class ComGoogleProtobufDescriptors_EnumDescriptor"); declarations->insert("J2OBJC_CLASS_DECLARATION(" + ClassName(descriptor->enum_type()) + ")"); - declarations->insert( - "FOUNDATION_EXPORT " - "ComGoogleProtobufDescriptors_EnumDescriptor * _Nonnull " + - ClassName(descriptor->enum_type()) + "_descriptor_"); } else if (type == JAVATYPE_MESSAGE) { std::string classname = ClassName(descriptor->message_type()); declarations->insert("@class " + classname); @@ -279,7 +275,6 @@ void FieldGenerator::GenerateFieldData(io::Printer *printer) const { ); GenerateFieldDataOffset(printer); GenerateClassNameOrMapData(printer); - GenerateStaticRefs(printer); printer->Print(variables_, " .containingType = NULL,\n" // Used by extensions. " .optionsData = $options_data$,\n" @@ -295,21 +290,6 @@ void FieldGenerator::GenerateClassNameOrMapData(io::Printer *printer) const { GenerateObjcClassRef(printer, descriptor_); } -void FieldGenerator::GenerateStaticRefs(io::Printer *printer) const { - JavaType type = GetJavaType(descriptor_); - std::string staticref; - if (type == JAVATYPE_MESSAGE) { - staticref = "&" + - GetParameterType(descriptor_) + "_descriptor_"; - } else if (type == JAVATYPE_ENUM) { - staticref = "&" + - GetParameterType(descriptor_) + "_descriptor_"; - } else { - staticref = "NULL"; - } - printer->Print(" .descriptorRef = $staticref$,\n", "staticref", staticref); -} - SingleFieldGenerator::SingleFieldGenerator( const FieldDescriptor *descriptor, uint32_t *numHasBits) : FieldGenerator(descriptor) { @@ -333,7 +313,7 @@ void SingleFieldGenerator::GenerateFieldBuilderHeader(io::Printer* printer) "- (nonnull $classname$_Builder *)clear$capitalized_name$;\n"); if (IsGenerateProperties(descriptor_->file())) { - printer->Print(GetStorageType(descriptor_) == GetNonNullType(descriptor_) + printer->Print(GetStorageType(descriptor_) == GetNonNullType(descriptor_) ? "@property (" : "@property (nonnull, retain, "); printer->Print( variables_, @@ -600,10 +580,6 @@ void MapEntryFieldGenerator::GenerateFieldDataOffset(io::Printer *printer) printer->Print(variables_, " .offset = 0,\n"); } -void MapFieldGenerator::GenerateStaticRefs(io::Printer *printer) const { - printer->Print(variables_, " .descriptorRef = NULL,\n"); -} - void MapEntryFieldGenerator::GenerateFieldBuilderHeader(io::Printer* printer) const { } @@ -615,10 +591,6 @@ void MapEntryFieldGenerator::GenerateMessageOrBuilderProtocol( void MapEntryFieldGenerator::GenerateDeclaration(io::Printer* printer) const { } -void MapEntryFieldGenerator::GenerateStaticRefs(io::Printer *printer) const { - printer->Print(variables_, " .descriptorRef = NULL,\n"); -} - FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor) : descriptor_(descriptor), field_generators_( diff --git a/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_field.h b/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_field.h index 1d7102b6b6..3691bc92bb 100644 --- a/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_field.h +++ b/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_field.h @@ -83,7 +83,6 @@ class FieldGenerator { virtual void GenerateFieldDataOffset(io::Printer *printer) const; virtual void GenerateClassNameOrMapData(io::Printer *printer) const; - virtual void GenerateStaticRefs(io::Printer *printer) const; private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGenerator); @@ -158,7 +157,6 @@ class MapFieldGenerator : public FieldGenerator { protected: virtual void GenerateClassNameOrMapData(io::Printer *printer) const; - virtual void GenerateStaticRefs(io::Printer *printer) const; private: const FieldDescriptor* key_field_; @@ -181,7 +179,6 @@ class MapEntryFieldGenerator : public FieldGenerator { protected: virtual void GenerateFieldDataOffset(io::Printer *printer) const; - virtual void GenerateStaticRefs(io::Printer *printer) const; private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapEntryFieldGenerator); diff --git a/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_message.cc b/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_message.cc index b3ee9a4d65..974644a569 100644 --- a/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_message.cc +++ b/protobuf/compiler/src/google/protobuf/compiler/j2objc/j2objc_message.cc @@ -304,10 +304,7 @@ void MessageGenerator::GenerateHeader(io::Printer* printer) { "\n" "J2OBJC_STATIC_INIT($classname$)\n" "\n" - "J2OBJC_TYPE_LITERAL_HEADER($classname$)\n" - "\n" - "FOUNDATION_EXPORT ComGoogleProtobufDescriptors_Descriptor " - "* _Nonnull $classname$_descriptor_;\n", + "J2OBJC_TYPE_LITERAL_HEADER($classname$)\n", "classname", ClassName(descriptor_)); for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) { @@ -358,7 +355,7 @@ void MessageGenerator::GenerateSource(io::Printer* printer) { "\n" "J2OBJC_INITIALIZED_DEFN($classname$);\n" "\n" - "ComGoogleProtobufDescriptors_Descriptor * _Nonnull " + "static ComGoogleProtobufDescriptors_Descriptor * _Nonnull " "$classname$_descriptor_;\n", "classname", ClassName(descriptor_)); @@ -459,7 +456,7 @@ void MessageGenerator::GenerateSource(io::Printer* printer) { "classname", ClassName(descriptor_), "flags", GetMessageFlags(descriptor_)); if (field_generators_.numMapFields() > 0) { - printer->Print("static CGPFieldData mapEntryFields[] = {\n"); + printer->Print("static const CGPFieldData mapEntryFields[] = {\n"); printer->Indent(); for (int i = 0; i < descriptor_->field_count(); i++) { field_generators_.get(descriptor_->field(i)) @@ -468,7 +465,7 @@ void MessageGenerator::GenerateSource(io::Printer* printer) { printer->Outdent(); printer->Print("};\n"); } - printer->Print("static CGPFieldData fields[] = {\n"); + printer->Print("static const CGPFieldData fields[] = {\n"); printer->Indent(); for (int i = 0; i < descriptor_->field_count(); i++) { field_generators_.get(descriptor_->field(i)).GenerateFieldData(printer); @@ -493,7 +490,7 @@ void MessageGenerator::GenerateSource(io::Printer* printer) { descriptor_->real_oneof_decl_count() > 0 ? "oneofs" : "NULL"); if (descriptor_->extension_count() > 0) { - printer->Print("static CGPFieldData extensionFields[] = {\n"); + printer->Print("static const CGPFieldData extensionFields[] = {\n"); printer->Indent(); for (int i = 0; i < descriptor_->extension_count(); i++) { ExtensionGenerator(descriptor_->extension(i)).GenerateFieldData(printer); diff --git a/protobuf/runtime/src/com/google/protobuf/Descriptors.h b/protobuf/runtime/src/com/google/protobuf/Descriptors.h index 1a7c6d2514..5f4bfc9fe5 100644 --- a/protobuf/runtime/src/com/google/protobuf/Descriptors.h +++ b/protobuf/runtime/src/com/google/protobuf/Descriptors.h @@ -116,7 +116,7 @@ J2OBJC_EMPTY_STATIC_INIT(ComGoogleProtobufDescriptors_EnumDescriptor) J2OBJC_TYPE_LITERAL_HEADER(ComGoogleProtobufDescriptors_EnumDescriptor) -@interface ComGoogleProtobufDescriptors_EnumValueDescriptor : NSObject +@interface ComGoogleProtobufDescriptors_UnknownEnumValueDescriptor : NSObject - (jint)getNumber; @@ -124,6 +124,10 @@ J2OBJC_TYPE_LITERAL_HEADER(ComGoogleProtobufDescriptors_EnumDescriptor) @end +@interface ComGoogleProtobufDescriptors_EnumValueDescriptor + : ComGoogleProtobufDescriptors_UnknownEnumValueDescriptor +@end + J2OBJC_EMPTY_STATIC_INIT(ComGoogleProtobufDescriptors_EnumValueDescriptor) J2OBJC_TYPE_LITERAL_HEADER(ComGoogleProtobufDescriptors_EnumValueDescriptor) @@ -142,4 +146,4 @@ J2OBJC_EMPTY_STATIC_INIT(ComGoogleProtobufDescriptors_OneofDescriptor) J2OBJC_TYPE_LITERAL_HEADER(ComGoogleProtobufDescriptors_OneofDescriptor) -#endif // __ComGoogleProtobufDescriptors_H__ +#endif // __ComGoogleProtobufDescriptors_H__ diff --git a/protobuf/runtime/src/com/google/protobuf/Descriptors.m b/protobuf/runtime/src/com/google/protobuf/Descriptors.m index 5e34185e44..c4464cdaf1 100644 --- a/protobuf/runtime/src/com/google/protobuf/Descriptors.m +++ b/protobuf/runtime/src/com/google/protobuf/Descriptors.m @@ -51,15 +51,6 @@ #import "java/util/Arrays.h" #import "java/util/Collections.h" -// Defines the field in the CGPValue union type to use for each field type. -#define VALUE_FIELD_Int valueInt -#define VALUE_FIELD_Long valueLong -#define VALUE_FIELD_Float valueFloat -#define VALUE_FIELD_Double valueDouble -#define VALUE_FIELD_Bool valueBool -#define VALUE_FIELD_Enum valueId -#define VALUE_FIELD_Retainable valueId - BOOL CGPIsRetainedType(CGPFieldJavaType type) { switch (type) { case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_INT: @@ -67,8 +58,8 @@ BOOL CGPIsRetainedType(CGPFieldJavaType type) { case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_FLOAT: case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_DOUBLE: case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BOOLEAN: - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM: return NO; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM: case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_STRING: case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BYTE_STRING: case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_MESSAGE: @@ -79,13 +70,13 @@ BOOL CGPIsRetainedType(CGPFieldJavaType type) { size_t CGPGetTypeSize(CGPFieldJavaType type) { #define GET_TYPE_SIZE_CASE(NAME) return sizeof(TYPE_##NAME); - SWITCH_TYPES_NO_ENUM(type, GET_TYPE_SIZE_CASE) + SWITCH_TYPES(type, GET_TYPE_SIZE_CASE) #undef GET_TYPE_SIZE_CASE } -IOSObjectArray *CreateFields(jint fieldCount, CGPFieldData *fieldData, - CGPDescriptor *containingType) { +static IOSObjectArray *CreateFields(jint fieldCount, const CGPFieldData *fieldData, + CGPDescriptor *containingType) { IOSObjectArray *fields = [IOSObjectArray newArrayWithLength:fieldCount type:ComGoogleProtobufDescriptors_FieldDescriptor_class_()]; @@ -104,7 +95,7 @@ size_t CGPGetTypeSize(CGPFieldJavaType type) { storageSize:storageSize]; } -void CGPInitFields(CGPDescriptor *descriptor, jint fieldCount, CGPFieldData *fieldData, +void CGPInitFields(CGPDescriptor *descriptor, jint fieldCount, const CGPFieldData *fieldData, jint oneofCount, const CGPOneofData *oneofData) { descriptor->fields_ = CreateFields(fieldCount, fieldData, descriptor); @@ -127,7 +118,7 @@ void CGPInitFields(CGPDescriptor *descriptor, jint fieldCount, CGPFieldData *fie } } -CGPDescriptor *NewMapEntryDescriptor(CGPFieldData *fieldData) { +static CGPDescriptor *NewMapEntryDescriptor(const CGPFieldData *fieldData) { CGPDescriptor *descriptor = [[CGPDescriptor alloc] init]; descriptor->fields_ = CreateFields(2, fieldData, descriptor); return descriptor; @@ -290,7 +281,7 @@ int SerializationOrderComp(const void *a, const void *b) { @implementation ComGoogleProtobufDescriptors_FieldDescriptor -static uint32_t TagFromData(CGPFieldData *data) { +static uint32_t TagFromData(const CGPFieldData *data) { BOOL isPacked = data->flags & CGPFieldFlagPacked; return CGPWireFormatMakeTag(data->number, CGPWireFormatForType(data->type, isPacked)); } @@ -313,8 +304,8 @@ static uint32_t TagFromData(CGPFieldData *data) { } // Default values for enums and message types can't be assigned in static data. -static void CGPFieldFixDefaultValue(CGPFieldDescriptor *descriptor) { - CGPFieldData *data = descriptor->data_; +static void CGPFieldUpdateValueType(CGPFieldDescriptor *descriptor) { + const CGPFieldData *data = descriptor->data_; switch (descriptor->javaType_) { case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_INT: case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_LONG: @@ -327,15 +318,12 @@ static void CGPFieldFixDefaultValue(CGPFieldDescriptor *descriptor) { Class enumClass = data->objcType; NSCAssert(enumClass != nil, @"Field data is missing objc enum type."); CGPEnumDescriptor *enumDescriptor = [enumClass performSelector:@selector(getDescriptor)]; - CGPEnumValueDescriptor *valueDescriptor = - IOSObjectArray_Get(enumDescriptor->values_, data->defaultValue.valueInt); - data->defaultValue.valueId = valueDescriptor->enum_; descriptor->valueType_ = enumDescriptor; break; } case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BYTE_STRING: if (data->defaultValue.valueId == nil) { - data->defaultValue.valueId = ComGoogleProtobufByteString_get_EMPTY(); + descriptor->valueType_ = ComGoogleProtobufByteString_get_EMPTY(); } else { // Default byte string data is written to static data as a length // prefixed c-string. @@ -346,7 +334,7 @@ static void CGPFieldFixDefaultValue(CGPFieldDescriptor *descriptor) { rawBytes += sizeof(length); CGPByteString *byteString = CGPNewByteString(length); memcpy(byteString->buffer_, rawBytes, length); - data->defaultValue.valueId = byteString; + descriptor->valueType_ = byteString; } break; case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_MESSAGE: { @@ -354,27 +342,21 @@ static void CGPFieldFixDefaultValue(CGPFieldDescriptor *descriptor) { descriptor->valueType_ = NewMapEntryDescriptor(data->mapEntryFields); break; } - CGPDescriptor *msgDescriptor = - data->descriptorRef ? (__bridge id) * (data->descriptorRef) : nil; - if (msgDescriptor == nil) { - // The descriptorRef wasn't specified, so use its accessor. - Class msgClass = data->objcType; - msgDescriptor = [msgClass performSelector:@selector(getDescriptor)]; - } + Class msgClass = data->objcType; + CGPDescriptor *msgDescriptor = [msgClass performSelector:@selector(getDescriptor)]; NSCAssert(msgDescriptor != nil, @"Field data is missing descriptor reference."); - data->defaultValue.valueId = msgDescriptor->defaultInstance_; descriptor->valueType_ = msgDescriptor; break; } } } -- (instancetype)initWithData:(CGPFieldData *)data { +- (instancetype)initWithData:(const CGPFieldData *)data { if (self = [self init]) { data_ = data; tag_ = TagFromData(data); javaType_ = [GetTypeObj(data->type)->javaType_ ordinal]; - CGPFieldFixDefaultValue(self); + CGPFieldUpdateValueType(self); } return self; } @@ -433,7 +415,7 @@ - (CGPEnumDescriptor *)getEnumType { } - (id)getDefaultValue { - return CGPFieldGetDefaultValue(self); + return CGPFieldGetDefaultValueObject(self); } - (ComGoogleProtobufDescriptorProtos_FieldOptions *)getOptions { @@ -453,17 +435,59 @@ - (ComGoogleProtobufDescriptorProtos_FieldOptions *)getOptions { J2OBJC_CLASS_TYPE_LITERAL_SOURCE(ComGoogleProtobufDescriptors_FieldDescriptor) -id CGPFieldGetDefaultValue(CGPFieldDescriptor *field) { +CGPValue CGPFieldGetDefaultValue(const CGPFieldDescriptor *field) { + CGPValue value; + switch (CGPFieldGetJavaType(field)) { + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_INT: + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_LONG: + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_FLOAT: + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_DOUBLE: + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BOOLEAN: + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_STRING: + value = field->data_->defaultValue; + break; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BYTE_STRING: + value.valueId = field->valueType_; + break; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM: { + jint enumOrdinal = field->data_->defaultValue.valueInt; + CGPEnumDescriptor *enumDescriptor = (CGPEnumDescriptor *)field->valueType_; + CGPEnumValueDescriptor **valuesBuf = enumDescriptor->values_->buffer_; + value.valueEnum = valuesBuf[enumOrdinal]; + break; + } + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_MESSAGE: + value.valueId = ((CGPDescriptor *)field->valueType_)->defaultInstance_; + break; + } + return value; +} + +id CGPFieldGetDefaultValueObject(CGPFieldDescriptor *field) { if (CGPFieldIsRepeated(field)) { return [JavaUtilCollections emptyList]; } - -#define GET_DEFAULT_VALUE_CASE(NAME) \ - return CGPToReflectionType##NAME(field->data_->defaultValue.VALUE_FIELD_##NAME, field); - - SWITCH_TYPES_WITH_ENUM(CGPFieldGetJavaType(field), GET_DEFAULT_VALUE_CASE) - -#undef GET_DEFAULT_VALUE_CASE + switch (CGPFieldGetJavaType(field)) { + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_INT: + return [JavaLangInteger valueOfWithInt:field->data_->defaultValue.valueInt]; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_LONG: + return [JavaLangLong valueOfWithLong:field->data_->defaultValue.valueLong]; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_FLOAT: + return [JavaLangFloat valueOfWithFloat:field->data_->defaultValue.valueFloat]; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_DOUBLE: + return [JavaLangDouble valueOfWithDouble:field->data_->defaultValue.valueDouble]; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BOOLEAN: + return [JavaLangBoolean valueOfWithBoolean:field->data_->defaultValue.valueBool]; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_STRING: + return field->data_->defaultValue.valueId; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BYTE_STRING: + return field->valueType_; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM: + return ((CGPEnumDescriptor *)field->valueType_) + ->values_->buffer_[field->data_->defaultValue.valueInt]; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_MESSAGE: + return ((CGPDescriptor *)field->valueType_)->defaultInstance_; + } } CGPEnumValueDescriptor *CGPEnumValueDescriptorFromInt(CGPEnumDescriptor *enumType, jint value) { @@ -476,8 +500,23 @@ id CGPFieldGetDefaultValue(CGPFieldDescriptor *field) { return valueDescriptor; } } - // If proto3 (not closed), the UNRECOGNIZED value is the last values element. - return enumType->is_closed_ ? nil : valuesBuf[count - 1]; + + if (enumType->is_closed_) { + return nil; + } + + CGPEnumValueDescriptor *unrecognizedValue = valuesBuf[count - 1]; + if (value == -1) { + // -1 is the value generated for the UNRECOGNIZED constant in open enums. + // If the value is -1, we return the UNRECOGNIZED descriptor. + return unrecognizedValue; + } else { + CGPEnumValueDescriptor *unknownValue = (CGPEnumValueDescriptor *)AUTORELEASE( + [[ComGoogleProtobufDescriptors_UnknownEnumValueDescriptor alloc] init]); + unknownValue->number_ = value; + unknownValue->enum_ = unrecognizedValue->enum_; + return unknownValue; + } } @implementation ComGoogleProtobufDescriptors_EnumDescriptor @@ -503,7 +542,7 @@ - (CGPEnumValueDescriptor *)findValueByNumberWithInt:(jint)number { J2OBJC_CLASS_TYPE_LITERAL_SOURCE(ComGoogleProtobufDescriptors_EnumDescriptor) -@implementation ComGoogleProtobufDescriptors_EnumValueDescriptor +@implementation ComGoogleProtobufDescriptors_UnknownEnumValueDescriptor - (jint)getNumber { return number_; @@ -513,6 +552,10 @@ - (NSString *)getName { return [enum_ name]; } +@end + +@implementation ComGoogleProtobufDescriptors_EnumValueDescriptor + J2OBJC_ETERNAL_SINGLETON @end diff --git a/protobuf/runtime/src/com/google/protobuf/Descriptors_PackagePrivate.h b/protobuf/runtime/src/com/google/protobuf/Descriptors_PackagePrivate.h index 5829afda29..d7d8628d70 100644 --- a/protobuf/runtime/src/com/google/protobuf/Descriptors_PackagePrivate.h +++ b/protobuf/runtime/src/com/google/protobuf/Descriptors_PackagePrivate.h @@ -45,6 +45,7 @@ typedef union { jdouble valueDouble; bool valueBool; __unsafe_unretained id valueId; + __unsafe_unretained ComGoogleProtobufDescriptors_EnumValueDescriptor *valueEnum; const void *valuePtr; } CGPValue; @@ -53,8 +54,7 @@ typedef union { #define CGPValueField_Float valueFloat #define CGPValueField_Double valueDouble #define CGPValueField_Bool valueBool -#define CGPValueField_Enum valueId -#define CGPValueField_Retainable valueId +#define CGPValueField_Enum valueEnum #define CGPValueField_Id valueId typedef NS_OPTIONS(uint32_t, CGPMessageFlags) { @@ -80,9 +80,8 @@ typedef struct CGPFieldData { uint32_t offset; union { Class objcType; - struct CGPFieldData *mapEntryFields; + const struct CGPFieldData *mapEntryFields; }; - const __unsafe_unretained id *descriptorRef; const char *containingType; const char *optionsData; } CGPFieldData; @@ -116,16 +115,17 @@ typedef struct CGPOneofData { @interface ComGoogleProtobufDescriptors_FieldDescriptor () { @package - CGPFieldData *data_; + const CGPFieldData *data_; uint32_t tag_; CGPFieldJavaType javaType_; - // Either nil, a Descriptor or a EnumDescriptor depending on the field type. + // Either nil, a ComGoogleProtobufByteString, a Descriptor or a EnumDescriptor depending on the + // field type. id valueType_; ComGoogleProtobufDescriptorProtos_FieldOptions *fieldOptions_; CGPOneofDescriptor *containingOneof_; } -- (instancetype)initWithData:(CGPFieldData *)data; +- (instancetype)initWithData:(const CGPFieldData *)data; @end @@ -142,7 +142,7 @@ typedef struct CGPOneofData { @end -@interface ComGoogleProtobufDescriptors_EnumValueDescriptor () { +@interface ComGoogleProtobufDescriptors_UnknownEnumValueDescriptor () { @package JavaLangEnum *enum_; jint number_; @@ -160,26 +160,13 @@ typedef struct CGPOneofData { @end -// Functions that convert a value from its field storage type to the type -// expected by a reflection accessor. (accessing with a descriptor) -// For enums, the reflection type is a EnumValueDescriptor. -#define CGPToReflectionTypeInt(value, field) [JavaLangInteger valueOfWithInt:value] -#define CGPToReflectionTypeLong(value, field) [JavaLangLong valueOfWithLong:value] -#define CGPToReflectionTypeFloat(value, field) [JavaLangFloat valueOfWithFloat:value] -#define CGPToReflectionTypeDouble(value, field) [JavaLangDouble valueOfWithDouble:value] -#define CGPToReflectionTypeBool(value, field) [JavaLangBoolean valueOfWithBoolean:value] -#define CGPToReflectionTypeEnum(value, field) \ - ((CGPEnumDescriptor *)field->valueType_)->values_->buffer_[[(JavaLangEnum *)value ordinal]] -#define CGPToReflectionTypeRetainable(value, field) RETAIN_AND_AUTORELEASE(value) - CF_EXTERN_C_BEGIN NS_RETURNS_RETAINED CGPDescriptor *CGPInitDescriptor(Class messageClass, Class builderClass, CGPMessageFlags flags, size_t storageSize); -void CGPInitFields( - CGPDescriptor *descriptor, jint fieldCount, CGPFieldData *fieldData, - jint oneofCount, const CGPOneofData *oneofData); +void CGPInitFields(CGPDescriptor *descriptor, jint fieldCount, const CGPFieldData *fieldData, + jint oneofCount, const CGPOneofData *oneofData); CGP_ALWAYS_INLINE BOOL CGPIsExtendable(const CGPDescriptor *descriptor) { return descriptor->flags_ & CGPMessageFlagExtendable; @@ -264,11 +251,12 @@ CGP_ALWAYS_INLINE BOOL CGPJavaTypeIsEnum(CGPFieldJavaType type) { return type == ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM; } -CGP_ALWAYS_INLINE jint CGPEnumGetIntValue(CGPEnumDescriptor *descriptor, id enumObj) { - return *(jint *)((char *)(ARCBRIDGE void *)enumObj + descriptor->valueOffset_); +CGP_ALWAYS_INLINE jint CGPEnumGetIntValue(CGPEnumDescriptor *descriptor, TYPE_Enum enumObj) { + return enumObj->number_; } -id CGPFieldGetDefaultValue(CGPFieldDescriptor *field); +id CGPFieldGetDefaultValueObject(CGPFieldDescriptor *field); +CGPValue CGPFieldGetDefaultValue(const CGPFieldDescriptor *field); Class CGPOneofGetCaseClass(CGPOneofDescriptor *oneof); @@ -306,4 +294,107 @@ J2OBJC_FIELD_SETTER(ComGoogleProtobufDescriptors_FieldDescriptor_Type, javaType_ J2OBJC_FIELD_SETTER(ComGoogleProtobufDescriptors_FieldDescriptor_JavaType, defaultDefault_, id) +// Functions that box a value from its field storage type into an object type. +// For enums, the boxed type is a Java enum object. +CGP_ALWAYS_INLINE JavaLangInteger *CGPBoxedValueInt(jint value) { + return [JavaLangInteger valueOfWithInt:value]; +} +CGP_ALWAYS_INLINE JavaLangLong *CGPBoxedValueLong(jlong value) { + return [JavaLangLong valueOfWithLong:value]; +} +CGP_ALWAYS_INLINE JavaLangFloat *CGPBoxedValueFloat(jfloat value) { + return [JavaLangFloat valueOfWithFloat:value]; +} +CGP_ALWAYS_INLINE JavaLangDouble *CGPBoxedValueDouble(jdouble value) { + return [JavaLangDouble valueOfWithDouble:value]; +} +CGP_ALWAYS_INLINE JavaLangBoolean *CGPBoxedValueBool(bool value) { + return [JavaLangBoolean valueOfWithBoolean:value]; +} +CGP_ALWAYS_INLINE JavaLangEnum *CGPBoxedValueEnum( + CGPEnumValueDescriptor *value) { + return value->enum_; +} +CGP_ALWAYS_INLINE id CGPBoxedValueId(id value) { return value; } + +// Functions that unbox a value into its primitive type. +CGP_ALWAYS_INLINE jint CGPUnboxValueInt(JavaLangInteger *value) { return [value intValue]; } +CGP_ALWAYS_INLINE jlong CGPUnboxValueLong(JavaLangLong *value) { return [value longLongValue]; } +CGP_ALWAYS_INLINE jfloat CGPUnboxValueFloat(JavaLangFloat *value) { return [value floatValue]; } +CGP_ALWAYS_INLINE jdouble CGPUnboxValueDouble(JavaLangDouble *value) { return [value doubleValue]; } +CGP_ALWAYS_INLINE bool CGPUnboxValueBool(JavaLangBoolean *value) { return [value booleanValue]; } +CGP_ALWAYS_INLINE CGPEnumValueDescriptor *CGPUnboxValueEnum( + JavaLangEnum *value) { + return [value getValueDescriptor]; +} +CGP_ALWAYS_INLINE id CGPUnboxValueId(id value) { return value; } + +// Functions that convert a value from its reflection to its storage type. +CGP_ALWAYS_INLINE jint CGPFromReflectionTypeInt(JavaLangInteger *value) { return [value intValue]; } +CGP_ALWAYS_INLINE jlong CGPFromReflectionTypeLong(JavaLangLong *value) { + return [value longLongValue]; +} +CGP_ALWAYS_INLINE jfloat CGPFromReflectionTypeFloat(JavaLangFloat *value) { + return [value floatValue]; +} +CGP_ALWAYS_INLINE jdouble CGPFromReflectionTypeDouble(JavaLangDouble *value) { + return [value doubleValue]; +} +CGP_ALWAYS_INLINE bool CGPFromReflectionTypeBool(JavaLangBoolean *value) { + return [value booleanValue]; +} +CGP_ALWAYS_INLINE CGPEnumValueDescriptor *CGPFromReflectionTypeEnum(CGPEnumValueDescriptor *value) { + return value; +} +CGP_ALWAYS_INLINE id CGPFromReflectionTypeId(id value) { return value; } + +// Functions that convert a value from its field storage type to the type +// expected by a reflection accessor. (accessing with a descriptor) +// For enums, the reflection type is a EnumValueDescriptor. +CGP_ALWAYS_INLINE JavaLangInteger *CGPToReflectionTypeInt(jint value) { + return [JavaLangInteger valueOfWithInt:value]; +} +CGP_ALWAYS_INLINE JavaLangLong *CGPToReflectionTypeLong(jlong value) { + return [JavaLangLong valueOfWithLong:value]; +} +CGP_ALWAYS_INLINE JavaLangFloat *CGPToReflectionTypeFloat(jfloat value) { + return [JavaLangFloat valueOfWithFloat:value]; +} +CGP_ALWAYS_INLINE JavaLangDouble *CGPToReflectionTypeDouble(jdouble value) { + return [JavaLangDouble valueOfWithDouble:value]; +} +CGP_ALWAYS_INLINE JavaLangBoolean *CGPToReflectionTypeBool(bool value) { + return [JavaLangBoolean valueOfWithBoolean:value]; +} +CGP_ALWAYS_INLINE CGPEnumValueDescriptor *CGPToReflectionTypeEnum(CGPEnumValueDescriptor *value) { + return RETAIN_AND_AUTORELEASE(value); +} +CGP_ALWAYS_INLINE id CGPToReflectionTypeId(id value) { return RETAIN_AND_AUTORELEASE(value); } + +// Functions that convert a value from its field storage type to its external +// type. +CGP_ALWAYS_INLINE EXTERNAL_TYPE_Int ToExternalTypeInt(TYPE_Int value) { return value; } +CGP_ALWAYS_INLINE EXTERNAL_TYPE_Long ToExternalTypeLong(TYPE_Long value) { return value; } +CGP_ALWAYS_INLINE EXTERNAL_TYPE_Float ToExternalTypeFloat(TYPE_Float value) { return value; } +CGP_ALWAYS_INLINE EXTERNAL_TYPE_Double ToExternalTypeDouble(TYPE_Double value) { return value; } +CGP_ALWAYS_INLINE EXTERNAL_TYPE_Bool ToExternalTypeBool(TYPE_Bool value) { return value; } +CGP_ALWAYS_INLINE EXTERNAL_TYPE_Enum ToExternalTypeEnum(TYPE_Enum value) { + return RETAIN_AND_AUTORELEASE(value->enum_); +} +CGP_ALWAYS_INLINE EXTERNAL_TYPE_Id ToExternalTypeId(TYPE_Id value) { + return RETAIN_AND_AUTORELEASE(value); +} + +// Functions that convert a value from its external type to its field storage +// type. +CGP_ALWAYS_INLINE TYPE_Int ToTypeInt(EXTERNAL_TYPE_Int value) { return value; } +CGP_ALWAYS_INLINE TYPE_Long ToTypeLong(EXTERNAL_TYPE_Long value) { return value; } +CGP_ALWAYS_INLINE TYPE_Float ToTypeFloat(EXTERNAL_TYPE_Float value) { return value; } +CGP_ALWAYS_INLINE TYPE_Double ToTypeDouble(EXTERNAL_TYPE_Double value) { return value; } +CGP_ALWAYS_INLINE TYPE_Bool ToTypeBool(EXTERNAL_TYPE_Bool value) { return value; } +CGP_ALWAYS_INLINE TYPE_Enum ToTypeEnum(EXTERNAL_TYPE_Enum value) { + return [value getValueDescriptor]; +} +CGP_ALWAYS_INLINE TYPE_Id ToTypeId(EXTERNAL_TYPE_Id value) { return value; } + #endif // __ComGoogleProtobufDescriptors_PackagePrivate_H__ diff --git a/protobuf/runtime/src/com/google/protobuf/ExtensionLite.h b/protobuf/runtime/src/com/google/protobuf/ExtensionLite.h index 000eca72db..7fddbc60ee 100644 --- a/protobuf/runtime/src/com/google/protobuf/ExtensionLite.h +++ b/protobuf/runtime/src/com/google/protobuf/ExtensionLite.h @@ -46,7 +46,7 @@ struct CGPFieldData; CGPFieldDescriptor *fieldDescriptor_; } -- (instancetype)initWithFieldData:(struct CGPFieldData *)data; +- (instancetype)initWithFieldData:(const struct CGPFieldData *)data; - (ComGoogleProtobufDescriptors_FieldDescriptor *)getDescriptor; @@ -60,7 +60,7 @@ typedef ComGoogleProtobufExtensionLite CGPExtensionLite; CF_EXTERN_C_BEGIN void ComGoogleProtobufExtensionLite_initWithFieldData_(CGPExtensionLite *self, - struct CGPFieldData *data); + const struct CGPFieldData *data); CF_EXTERN_C_END J2OBJC_EMPTY_STATIC_INIT(ComGoogleProtobufExtensionLite) diff --git a/protobuf/runtime/src/com/google/protobuf/ExtensionLite.m b/protobuf/runtime/src/com/google/protobuf/ExtensionLite.m index a2df6097f3..9bcfad1fef 100644 --- a/protobuf/runtime/src/com/google/protobuf/ExtensionLite.m +++ b/protobuf/runtime/src/com/google/protobuf/ExtensionLite.m @@ -36,7 +36,7 @@ @implementation ComGoogleProtobufExtensionLite -- (instancetype)initWithFieldData:(struct CGPFieldData *)data { +- (instancetype)initWithFieldData:(const struct CGPFieldData *)data { ComGoogleProtobufExtensionLite_initWithFieldData_(self, data); return self; } @@ -61,7 +61,7 @@ - (jint)getNumber { @end void ComGoogleProtobufExtensionLite_initWithFieldData_(CGPExtensionLite *self, - struct CGPFieldData *data) { + const struct CGPFieldData *data) { NSObject_init(self); self->fieldDescriptor_ = [[CGPFieldDescriptor alloc] initWithData:data]; } diff --git a/protobuf/runtime/src/com/google/protobuf/ExtensionRegistry.mm b/protobuf/runtime/src/com/google/protobuf/ExtensionRegistry.mm index c0f9a18f99..33e3a20338 100644 --- a/protobuf/runtime/src/com/google/protobuf/ExtensionRegistry.mm +++ b/protobuf/runtime/src/com/google/protobuf/ExtensionRegistry.mm @@ -122,7 +122,7 @@ - (instancetype)initWithField:(CGPFieldDescriptor *)field { descriptor_ = field; if (CGPFieldTypeIsMessage(field)) { // No need to retain. Default message values are eternal. - defaultInstance_ = CGPFieldGetDefaultValue(field); + defaultInstance_ = CGPFieldGetDefaultValueObject(field); } } return self; diff --git a/protobuf/runtime/src/com/google/protobuf/FieldTypes.h b/protobuf/runtime/src/com/google/protobuf/FieldTypes.h index 5c61026278..d0b4c81390 100644 --- a/protobuf/runtime/src/com/google/protobuf/FieldTypes.h +++ b/protobuf/runtime/src/com/google/protobuf/FieldTypes.h @@ -36,6 +36,7 @@ #define __ComGoogleProtobufFieldTypes_H__ #import "com/google/protobuf/ProtocolMessageEnum.h" +#import "com/google/protobuf/common.h" #import "java/lang/Boolean.h" #import "java/lang/Double.h" #import "java/lang/Enum.h" @@ -43,154 +44,100 @@ #import "java/lang/Integer.h" #import "java/lang/Long.h" +@class ComGoogleProtobufDescriptors_EnumValueDescriptor; @class ComGoogleProtobufDescriptors_FieldDescriptor_JavaType; @class ComGoogleProtobufDescriptorProtos_FieldDescriptorProto_Type; -#define TYPE_Int jint -#define TYPE_Long jlong -#define TYPE_Float jfloat -#define TYPE_Double jdouble -#define TYPE_Bool bool -#define TYPE_Enum id -#define TYPE_Id id -#define TYPE_Retainable id - -#define TYPE_RETAIN_Int(value) value -#define TYPE_RETAIN_Long(value) value -#define TYPE_RETAIN_Float(value) value -#define TYPE_RETAIN_Double(value) value -#define TYPE_RETAIN_Bool(value) value -#define TYPE_RETAIN_Enum(value) value -#if __has_feature(objc_arc) -#define TYPE_RETAIN_Retainable(value) value -#else -#define TYPE_RETAIN_Retainable(value) [value retain] -#endif - -#define TYPE_ASSIGN_Int(assignee, value) assignee = value -#define TYPE_ASSIGN_Long(assignee, value) assignee = value -#define TYPE_ASSIGN_Float(assignee, value) assignee = value -#define TYPE_ASSIGN_Double(assignee, value) assignee = value -#define TYPE_ASSIGN_Bool(assignee, value) assignee = value -#define TYPE_ASSIGN_Enum(assignee, value) assignee = value -#if __has_feature(objc_arc) -#define TYPE_ASSIGN_Retainable(assignee, value) assignee = value -#else -#define TYPE_ASSIGN_Retainable(assignee, value) \ - ([assignee autorelease], assignee = [value retain]) -#endif - -#define HASH_Int(value) value -#define HASH_Long(value) (int)((uint64_t)value ^ ((uint64_t)value >> 32)) -#define HASH_Float(value) *(int *)&value -#define HASH_Double(value) (int)(*(uint64_t *)&value ^ (*(uint64_t *)&value >> 32)) -#define HASH_Bool(value) (value ? 1231 : 1237) -#define HASH_Id(value) (int)[value hash] - -// Functions that box a value from its field storage type into an object type. -// For enums, the boxed type is the same as its storage type. (a Java enum -// object) -#define CGPBoxedValueInt(value) [JavaLangInteger valueOfWithInt:value] -#define CGPBoxedValueLong(value) [JavaLangLong valueOfWithLong:value] -#define CGPBoxedValueFloat(value) [JavaLangFloat valueOfWithFloat:value] -#define CGPBoxedValueDouble(value) [JavaLangDouble valueOfWithDouble:value] -#define CGPBoxedValueBool(value) [JavaLangBoolean valueOfWithBoolean:value] -#define CGPBoxedValueId(value) value - -// Functions that unbox a value into its primitive type. -#define CGPUnboxValueInt(value) [value intValue] -#define CGPUnboxValueLong(value) [value longLongValue] -#define CGPUnboxValueFloat(value) [value floatValue] -#define CGPUnboxValueDouble(value) [value doubleValue] -#define CGPUnboxValueBool(value) [value booleanValue] -#define CGPUnboxValueEnum(value) value -#define CGPUnboxValueRetainable(value) value - -// Functions that convert a value from its reflection to its storage type. -#define CGPFromReflectionTypeInt(value) [value intValue] -#define CGPFromReflectionTypeLong(value) [value longLongValue] -#define CGPFromReflectionTypeFloat(value) [value floatValue] -#define CGPFromReflectionTypeDouble(value) [value doubleValue] -#define CGPFromReflectionTypeBool(value) [value booleanValue] -#define CGPFromReflectionTypeEnum(value) ((CGPEnumValueDescriptor *)value)->enum_ -#define CGPFromReflectionTypeRetainable(value) value +typedef jint TYPE_Int; +typedef jlong TYPE_Long; +typedef jfloat TYPE_Float; +typedef jdouble TYPE_Double; +typedef bool TYPE_Bool; +typedef ComGoogleProtobufDescriptors_EnumValueDescriptor *TYPE_Enum; +typedef id TYPE_Id; + +typedef TYPE_Int EXTERNAL_TYPE_Int; +typedef TYPE_Long EXTERNAL_TYPE_Long; +typedef TYPE_Float EXTERNAL_TYPE_Float; +typedef TYPE_Double EXTERNAL_TYPE_Double; +typedef TYPE_Bool EXTERNAL_TYPE_Bool; +typedef JavaLangEnum *EXTERNAL_TYPE_Enum; +typedef TYPE_Id EXTERNAL_TYPE_Id; + +CGP_ALWAYS_INLINE void TYPE_ASSIGN_Int(jint *assignee, jint value) { *assignee = value; } +CGP_ALWAYS_INLINE void TYPE_ASSIGN_Long(jlong *assignee, jlong value) { *assignee = value; } +CGP_ALWAYS_INLINE void TYPE_ASSIGN_Float(jfloat *assignee, jfloat value) { *assignee = value; } +CGP_ALWAYS_INLINE void TYPE_ASSIGN_Double(jdouble *assignee, jdouble value) { *assignee = value; } +CGP_ALWAYS_INLINE void TYPE_ASSIGN_Bool(bool *assignee, bool value) { *assignee = value; } +CGP_ALWAYS_INLINE void TYPE_ASSIGN_Enum(id *assignee, id value) { + __unused id unused = AUTORELEASE(*assignee); + *assignee = RETAIN_(value); +} +CGP_ALWAYS_INLINE void TYPE_ASSIGN_Id(id *assignee, id value) { + __unused id unused = AUTORELEASE(*assignee); + *assignee = RETAIN_(value); +} + +CGP_ALWAYS_INLINE int HASH_Int(jint value) { return value; } +CGP_ALWAYS_INLINE int HASH_Long(jlong value) { + return (int)((uint64_t)value ^ ((uint64_t)value >> 32)); +} +CGP_ALWAYS_INLINE int HASH_Float(jfloat value) { + uint32_t bits; + memcpy(&bits, &value, sizeof(bits)); + return bits; +} +CGP_ALWAYS_INLINE int HASH_Double(jdouble value) { + uint64_t bits; + memcpy(&bits, &value, sizeof(bits)); + return (int)(bits ^ (bits >> 32)); +} +CGP_ALWAYS_INLINE int HASH_Bool(bool value) { return (value ? 1231 : 1237); } +CGP_ALWAYS_INLINE int HASH_Enum(id value) { return (int)[value hash]; } +CGP_ALWAYS_INLINE int HASH_Id(id value) { return (int)[value hash]; } // Creates a switch statement over the java types grouping enums together with // the other object types. -#define SWITCH_TYPES_NO_ENUM(type, CASE_MACRO) \ - switch (type) { \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_INT: \ - CASE_MACRO(Int) \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_LONG: \ - CASE_MACRO(Long) \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_FLOAT: \ - CASE_MACRO(Float) \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_DOUBLE: \ - CASE_MACRO(Double) \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BOOLEAN: \ - CASE_MACRO(Bool) \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_STRING: \ +#define SWITCH_TYPES(type, TYPE_MACRO) \ + switch (type) { \ + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_INT: \ + TYPE_MACRO(Int) \ + break; \ + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_LONG: \ + TYPE_MACRO(Long) \ + break; \ + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_FLOAT: \ + TYPE_MACRO(Float) \ + break; \ + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_DOUBLE: \ + TYPE_MACRO(Double) \ + break; \ + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BOOLEAN: \ + TYPE_MACRO(Bool) \ + break; \ + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM: \ + TYPE_MACRO(Enum) \ + break; \ + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_STRING: \ case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BYTE_STRING: \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM: \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_MESSAGE: \ - CASE_MACRO(Id) \ + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_MESSAGE: \ + TYPE_MACRO(Id) \ + break; \ } -// Creates a switch statement over the java types separating enums from the -// other object types. -#define SWITCH_TYPES_WITH_ENUM(type, CASE_MACRO) \ - switch (type) { \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_INT: \ - CASE_MACRO(Int) \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_LONG: \ - CASE_MACRO(Long) \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_FLOAT: \ - CASE_MACRO(Float) \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_DOUBLE: \ - CASE_MACRO(Double) \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BOOLEAN: \ - CASE_MACRO(Bool) \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_STRING: \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BYTE_STRING: \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_MESSAGE: \ - CASE_MACRO(Retainable) \ - case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM: \ - CASE_MACRO(Enum) \ - } - - -// Declares the given macro once for each java field type, grouping enums and -// the other object types together as "Id". Non-retainable types use the -// PRIMITIVE_TYPE_MACRO, retainable types use the RETAINABLE_TYPE_MACRO -#define FOR_EACH_TYPE_NO_ENUM(PRIMITIVE_TYPE_MACRO, RETAINABLE_TYPE_MACRO) \ - PRIMITIVE_TYPE_MACRO(Int) \ - PRIMITIVE_TYPE_MACRO(Long) \ - PRIMITIVE_TYPE_MACRO(Float) \ - PRIMITIVE_TYPE_MACRO(Double) \ - PRIMITIVE_TYPE_MACRO(Bool) \ - RETAINABLE_TYPE_MACRO(Id) - - -// Declares the given macro once for each java field type, declaring it -// separately for enums (as "Enum) and the other object types (as "Retainable"). -#define FOR_EACH_TYPE_WITH_ENUM(MACRO) \ - MACRO(Int) \ - MACRO(Long) \ - MACRO(Float) \ - MACRO(Double) \ - MACRO(Bool) \ - MACRO(Enum) \ - MACRO(Retainable) - // Declares the given macro once for each java field type except for retainable -//types. -#define FOR_EACH_TYPE_NO_RETAINABLE(MACRO) \ - MACRO(Int) \ - MACRO(Long) \ - MACRO(Float) \ - MACRO(Double) \ - MACRO(Bool) \ - MACRO(Enum) +// types. +#define FOR_EACH_PRIMITIVE_TYPE(PRIMITIVE_TYPE_MACRO) \ + PRIMITIVE_TYPE_MACRO(Int) \ + PRIMITIVE_TYPE_MACRO(Long) \ + PRIMITIVE_TYPE_MACRO(Float) \ + PRIMITIVE_TYPE_MACRO(Double) \ + PRIMITIVE_TYPE_MACRO(Bool) + +#define FOR_EACH_TYPE(TYPE_MACRO) \ + FOR_EACH_PRIMITIVE_TYPE(TYPE_MACRO) \ + TYPE_MACRO(Enum) \ + TYPE_MACRO(Id) // The remainder of this file is copied from the translation of the types // FieldDescriptor.Type and FieldDescriptor.JavaType in Descriptor.java. @@ -218,7 +165,7 @@ typedef NS_ENUM(NSUInteger, ComGoogleProtobufDescriptors_FieldDescriptor_Type_En typedef ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum CGPFieldType; -@interface ComGoogleProtobufDescriptors_FieldDescriptor_Type : JavaLangEnum < NSCopying > +@interface ComGoogleProtobufDescriptors_FieldDescriptor_Type : JavaLangEnum #pragma mark Public @@ -226,7 +173,9 @@ typedef ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum CGPFieldType; - (ComGoogleProtobufDescriptorProtos_FieldDescriptorProto_Type *)toProto; -+ (ComGoogleProtobufDescriptors_FieldDescriptor_Type *)valueOfWithComGoogleProtobufDescriptorProtos_FieldDescriptorProto_Type:(ComGoogleProtobufDescriptorProtos_FieldDescriptorProto_Type *)type; ++ (ComGoogleProtobufDescriptors_FieldDescriptor_Type *) + valueOfWithComGoogleProtobufDescriptorProtos_FieldDescriptorProto_Type: + (ComGoogleProtobufDescriptorProtos_FieldDescriptorProto_Type *)type; #pragma mark Package-Private @@ -241,7 +190,8 @@ typedef ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum CGPFieldType; J2OBJC_STATIC_INIT(ComGoogleProtobufDescriptors_FieldDescriptor_Type) /*! INTERNAL ONLY - Use enum accessors declared below. */ -FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_Type *ComGoogleProtobufDescriptors_FieldDescriptor_Type_values_[]; +FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_Type + *ComGoogleProtobufDescriptors_FieldDescriptor_Type_values_[]; inline ComGoogleProtobufDescriptors_FieldDescriptor_Type * ComGoogleProtobufDescriptors_FieldDescriptor_Type_get_DOUBLE(void); @@ -315,13 +265,17 @@ inline ComGoogleProtobufDescriptors_FieldDescriptor_Type * ComGoogleProtobufDescriptors_FieldDescriptor_Type_get_SINT64(void); J2OBJC_ENUM_CONSTANT(ComGoogleProtobufDescriptors_FieldDescriptor_Type, SINT64) -FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_Type *ComGoogleProtobufDescriptors_FieldDescriptor_Type_valueOfWithComGoogleProtobufDescriptorProtos_FieldDescriptorProto_Type_(ComGoogleProtobufDescriptorProtos_FieldDescriptorProto_Type *type); +FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_Type * +ComGoogleProtobufDescriptors_FieldDescriptor_Type_valueOfWithComGoogleProtobufDescriptorProtos_FieldDescriptorProto_Type_( + ComGoogleProtobufDescriptorProtos_FieldDescriptorProto_Type *type); FOUNDATION_EXPORT IOSObjectArray *ComGoogleProtobufDescriptors_FieldDescriptor_Type_values(void); -FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_Type *ComGoogleProtobufDescriptors_FieldDescriptor_Type_valueOfWithNSString_(NSString *name); +FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_Type * +ComGoogleProtobufDescriptors_FieldDescriptor_Type_valueOfWithNSString_(NSString *name); -FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_Type *ComGoogleProtobufDescriptors_FieldDescriptor_Type_fromOrdinal(NSUInteger ordinal); +FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_Type * +ComGoogleProtobufDescriptors_FieldDescriptor_Type_fromOrdinal(NSUInteger ordinal); J2OBJC_TYPE_LITERAL_HEADER(ComGoogleProtobufDescriptors_FieldDescriptor_Type) @@ -337,7 +291,7 @@ typedef NS_ENUM(NSUInteger, ComGoogleProtobufDescriptors_FieldDescriptor_JavaTyp ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_MESSAGE = 8, }; -@interface ComGoogleProtobufDescriptors_FieldDescriptor_JavaType : JavaLangEnum < NSCopying > +@interface ComGoogleProtobufDescriptors_FieldDescriptor_JavaType : JavaLangEnum #pragma mark Package-Private @@ -352,7 +306,8 @@ typedef NS_ENUM(NSUInteger, ComGoogleProtobufDescriptors_FieldDescriptor_JavaTyp J2OBJC_STATIC_INIT(ComGoogleProtobufDescriptors_FieldDescriptor_JavaType) /*! INTERNAL ONLY - Use enum accessors declared below. */ -FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_JavaType *ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_values_[]; +FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_JavaType + *ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_values_[]; inline ComGoogleProtobufDescriptors_FieldDescriptor_JavaType * ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_get_INT(void); @@ -393,10 +348,12 @@ J2OBJC_ENUM_CONSTANT(ComGoogleProtobufDescriptors_FieldDescriptor_JavaType, MESS FOUNDATION_EXPORT IOSObjectArray *ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_values( void); -FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_JavaType *ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_valueOfWithNSString_(NSString *name); +FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_JavaType * +ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_valueOfWithNSString_(NSString *name); -FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_JavaType *ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_fromOrdinal(NSUInteger ordinal); +FOUNDATION_EXPORT ComGoogleProtobufDescriptors_FieldDescriptor_JavaType * +ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_fromOrdinal(NSUInteger ordinal); J2OBJC_TYPE_LITERAL_HEADER(ComGoogleProtobufDescriptors_FieldDescriptor_JavaType) -#endif // __ComGoogleProtobufFieldTypes_H__ +#endif // __ComGoogleProtobufFieldTypes_H__ diff --git a/protobuf/runtime/src/com/google/protobuf/GeneratedMessage.mm b/protobuf/runtime/src/com/google/protobuf/GeneratedMessage.mm index 0027d399ff..9af14301c8 100644 --- a/protobuf/runtime/src/com/google/protobuf/GeneratedMessage.mm +++ b/protobuf/runtime/src/com/google/protobuf/GeneratedMessage.mm @@ -71,8 +71,8 @@ #define NIL_CHECK_Float(value) #define NIL_CHECK_Double(value) #define NIL_CHECK_Bool(value) -#define NIL_CHECK_Enum(value) (void)nil_chk(value); -#define NIL_CHECK_Retainable(value) (void)nil_chk(value); +#define NIL_CHECK_Enum(value) (void)nil_chk(value) +#define NIL_CHECK_Id(value) (void)nil_chk(value) // Forward declarations. class CGPExtensionValue; @@ -90,33 +90,17 @@ static inline int SerializedSizeForMessage(ComGoogleProtobufGeneratedMessage *ms static void MessageToString(id msg, CGPDescriptor *descriptor, NSMutableString *builder, int indent); -#define REPEATED_PRIMITIVE_FIELD_GETTER_IMP(NAME) \ - CGP_ALWAYS_INLINE TYPE_##NAME CGPRepeatedFieldGet##NAME(CGPRepeatedField *field, jint idx) { \ - CGPRepeatedFieldCheckBounds(field, idx); \ - return ((TYPE_##NAME *)field->data->buffer)[idx]; \ - } - -#define REPEATED_RETAINABLE_FIELD_GETTER_IMP(NAME) \ - CGP_ALWAYS_INLINE TYPE_##NAME CGPRepeatedFieldGet##NAME(CGPRepeatedField *field, jint idx) { \ - CGPRepeatedFieldCheckBounds(field, idx); \ - return RETAIN_AND_AUTORELEASE(((TYPE_##NAME *)field->data->buffer)[idx]); \ - } - -FOR_EACH_TYPE_NO_ENUM(REPEATED_PRIMITIVE_FIELD_GETTER_IMP, REPEATED_RETAINABLE_FIELD_GETTER_IMP) - -#undef REPEATED_PRIMITIVE_FIELD_GETTER_IMP -#undef REPEATED_RETAINABLE_FIELD_GETTER_IMP - #define REPEATED_FIELD_ADDER_IMP(NAME) \ CGP_ALWAYS_INLINE void CGPRepeatedFieldAdd##NAME(CGPRepeatedField *field, TYPE_##NAME value) { \ uint32_t total_size = CGPRepeatedFieldTotalSize(field); \ if (CGPRepeatedFieldSize(field) == total_size) { \ CGPRepeatedFieldReserve(field, total_size + 1, sizeof(TYPE_##NAME)); \ } \ - ((TYPE_##NAME *)field->data->buffer)[field->data->size++] = TYPE_RETAIN_##NAME(value); \ + TYPE_##NAME *ptr = &((TYPE_##NAME *)field->data->buffer)[field->data->size++]; \ + TYPE_ASSIGN_##NAME(ptr, value); \ } -FOR_EACH_TYPE_WITH_ENUM(REPEATED_FIELD_ADDER_IMP) +FOR_EACH_TYPE(REPEATED_FIELD_ADDER_IMP) #undef REPEATED_FIELD_ADDER_IMP @@ -125,10 +109,10 @@ static void MessageToString(id msg, CGPDescriptor *descriptor, NSMutableString * TYPE_##NAME value) { \ CGPRepeatedFieldCheckBounds(field, idx); \ TYPE_##NAME *ptr = &((TYPE_##NAME *)field->data->buffer)[idx]; \ - TYPE_ASSIGN_##NAME(*ptr, value); \ + TYPE_ASSIGN_##NAME(ptr, value); \ } -FOR_EACH_TYPE_WITH_ENUM(REPEATED_FIELD_SETTER_IMP) +FOR_EACH_TYPE(REPEATED_FIELD_SETTER_IMP) #undef REPEATED_FIELD_SETTER_IMP @@ -302,23 +286,37 @@ static inline BOOL ClearPreviousOneof(id msg, CGPHasLocator loc, uintptr_t ptr) return wasCleared; } -#define REPEATED_FIELD_PTR(msg, offset) ((CGPRepeatedField *)((uint8_t *)msg + offset)) -#define MAP_FIELD_PTR(msg, offset) ((CGPMapField *)((uint8_t *)msg + offset)) +CGP_ALWAYS_INLINE CGPRepeatedField *REPEATED_FIELD_PTR(id msg, ptrdiff_t offset) { + return (CGPRepeatedField *)((uint8_t *)msg + offset); +} +CGP_ALWAYS_INLINE CGPMapField *MAP_FIELD_PTR(id msg, ptrdiff_t offset) { + return (CGPMapField *)((uint8_t *)msg + offset); +} #define FIELD_PTR(TYPE, msg, offset) ((TYPE *)((uint8_t *)msg + offset)) #define SINGULAR_SETTER_IMP(NAME) \ static void SingularSet##NAME(id msg, TYPE_##NAME value, size_t offset, CGPHasLocator hasLoc) { \ TYPE_##NAME *ptr = FIELD_PTR(TYPE_##NAME, msg, offset); \ ClearPreviousOneof(msg, hasLoc, (uintptr_t)ptr); \ - TYPE_ASSIGN_##NAME(*ptr, value); \ + TYPE_ASSIGN_##NAME(ptr, value); \ SetHas(msg, hasLoc); \ } -FOR_EACH_TYPE_NO_RETAINABLE(SINGULAR_SETTER_IMP) +FOR_EACH_PRIMITIVE_TYPE(SINGULAR_SETTER_IMP) + +static void SingularSetId(id msg, TYPE_Id value, size_t offset, CGPHasLocator hasLoc) { + TYPE_Id *ptr = FIELD_PTR(TYPE_Id, msg, offset); + if (!ClearPreviousOneof(msg, hasLoc, (uintptr_t)ptr)) { + // If it is not a one of field, we need to release the previous value. + // Otherwise ClearPreviousOneof has already done it for us if necessary. + AUTORELEASE(*ptr); + } + *ptr = RETAIN_(value); + SetHas(msg, hasLoc); +} -static void SingularSetRetainable(id msg, TYPE_Retainable value, size_t offset, - CGPHasLocator hasLoc) { - TYPE_Retainable *ptr = FIELD_PTR(TYPE_Retainable, msg, offset); +static void SingularSetEnum(id msg, TYPE_Enum value, size_t offset, CGPHasLocator hasLoc) { + TYPE_Enum *ptr = FIELD_PTR(TYPE_Enum, msg, offset); if (!ClearPreviousOneof(msg, hasLoc, (uintptr_t)ptr)) { // If it is not a one of field, we need to release the previous value. // Otherwise ClearPreviousOneof has already done it for us if necessary. @@ -334,43 +332,30 @@ static void SingularSetRetainable(id msg, TYPE_Retainable value, size_t offset, // ********** Dynamic field accessors ****************************************** // ***************************************************************************** -#define SINGULAR_PRIMITIVE_GETTER_IMP(NAME) \ - static IMP GetSingularGetterImp##NAME(size_t offset, CGPHasLocator hasLoc, \ - TYPE_##NAME defaultValue) { \ - return imp_implementationWithBlock(^TYPE_##NAME(id msg) { \ - if (GetHas(msg, hasLoc)) { \ - return *FIELD_PTR(TYPE_##NAME, msg, offset); \ - } \ - return defaultValue; \ - }); \ - } - -#define SINGULAR_RETAINABLE_GETTER_IMP(NAME) \ - static IMP GetSingularGetterImp##NAME(size_t offset, CGPHasLocator hasLoc, \ - TYPE_##NAME defaultValue) { \ - return imp_implementationWithBlock(^TYPE_##NAME(id msg) { \ - if (GetHas(msg, hasLoc)) { \ - return RETAIN_AND_AUTORELEASE(*FIELD_PTR(TYPE_##NAME, msg, offset)); \ - } \ - return defaultValue; \ - }); \ +#define SINGULAR_GETTER_IMP(NAME) \ + static IMP GetSingularGetterImp##NAME(size_t offset, CGPHasLocator hasLoc, \ + TYPE_##NAME defaultValue) { \ + return imp_implementationWithBlock(^EXTERNAL_TYPE_##NAME(id msg) { \ + TYPE_##NAME value = \ + GetHas(msg, hasLoc) ? *FIELD_PTR(TYPE_##NAME, msg, offset) : defaultValue; \ + return ToExternalType##NAME(value); \ + }); \ } -FOR_EACH_TYPE_NO_ENUM(SINGULAR_PRIMITIVE_GETTER_IMP, SINGULAR_RETAINABLE_GETTER_IMP) +FOR_EACH_TYPE(SINGULAR_GETTER_IMP) -#undef SINGULAR_PRIMITIVE_GETTER_IMP -#undef SINGULAR_RETAINABLE_GETTER_IMP +#undef SINGULAR_GETTER_IMP -#define REPEATED_GETTER_IMP(NAME) \ - static IMP GetRepeatedGetterImp##NAME(size_t offset) { \ - return imp_implementationWithBlock(^TYPE_##NAME(id msg, jint idx) { \ - return CGPRepeatedFieldGet##NAME(REPEATED_FIELD_PTR(msg, offset), idx); \ - }); \ +#define REPEATED_GETTER_IMP(NAME) \ + static IMP GetRepeatedGetterImp##NAME(size_t offset) { \ + return imp_implementationWithBlock(^EXTERNAL_TYPE_##NAME(id msg, jint idx) { \ + CGPRepeatedField *field = REPEATED_FIELD_PTR(msg, offset); \ + CGPRepeatedFieldCheckBounds(field, idx); \ + return ToExternalType##NAME(((TYPE_##NAME *)field->data->buffer)[idx]); \ + }); \ } -// Same macro for all types, uses getter functions defined earlier that -// already handle primitive vs retainable. -FOR_EACH_TYPE_NO_ENUM(REPEATED_GETTER_IMP, REPEATED_GETTER_IMP) +FOR_EACH_TYPE(REPEATED_GETTER_IMP) #undef REPEATED_GETTER_IMP @@ -381,14 +366,13 @@ static BOOL AddGetterMethod(Class cls, SEL sel, CGPFieldDescriptor *field) { size_t offset = CGPFieldGetOffset(field, cls); CGPHasLocator hasLoc = GetHasLocator(cls, field); -#define ADD_GETTER_METHOD_CASE(NAME) \ - imp = repeated \ - ? GetRepeatedGetterImp##NAME(offset) \ - : GetSingularGetterImp##NAME(offset, hasLoc, field->data_->defaultValue.value##NAME); \ - strcpy(encoding, @encode(TYPE_##NAME)); \ - break; +#define ADD_GETTER_METHOD_CASE(NAME) \ + imp = repeated ? GetRepeatedGetterImp##NAME(offset) \ + : GetSingularGetterImp##NAME(offset, hasLoc, \ + CGPFieldGetDefaultValue(field).value##NAME); \ + strcpy(encoding, @encode(TYPE_##NAME)); - SWITCH_TYPES_NO_ENUM(CGPFieldGetJavaType(field), ADD_GETTER_METHOD_CASE) + SWITCH_TYPES(CGPFieldGetJavaType(field), ADD_GETTER_METHOD_CASE) #undef ADD_GETTER_METHOD_CASE @@ -505,28 +489,31 @@ static BOOL AddClearMethod(Class cls, SEL sel, CGPFieldDescriptor *field) { return class_addMethod(cls, sel, imp, "@@:"); } -#define GET_SINGULAR_SETTER_IMP(NAME) \ - static IMP GetSingularSetterImp##NAME(size_t offset, CGPHasLocator hasLoc) { \ - return imp_implementationWithBlock(^id(id msg, TYPE_##NAME value) { \ - NIL_CHECK_##NAME(value) SingularSet##NAME(msg, value, offset, hasLoc); \ - return msg; \ - }); \ +#define GET_SINGULAR_SETTER_IMP(NAME) \ + static IMP GetSingularSetterImp##NAME(size_t offset, CGPHasLocator hasLoc) { \ + return imp_implementationWithBlock(^id(id msg, EXTERNAL_TYPE_##NAME value) { \ + NIL_CHECK_##NAME(value); \ + TYPE_##NAME internalValue = ToType##NAME(value); \ + SingularSet##NAME(msg, internalValue, offset, hasLoc); \ + return msg; \ + }); \ } -FOR_EACH_TYPE_WITH_ENUM(GET_SINGULAR_SETTER_IMP) +FOR_EACH_TYPE(GET_SINGULAR_SETTER_IMP) #undef GET_SINGULAR_SETTER_IMP -#define GET_REPEATED_SETTER_IMP(NAME) \ - static IMP GetRepeatedSetterImp##NAME(size_t offset) { \ - return imp_implementationWithBlock(^id(id msg, jint idx, TYPE_##NAME value) { \ - NIL_CHECK_##NAME(value) \ - CGPRepeatedFieldSet##NAME(REPEATED_FIELD_PTR(msg, offset), idx, value); \ - return msg; \ - }); \ +#define GET_REPEATED_SETTER_IMP(NAME) \ + static IMP GetRepeatedSetterImp##NAME(size_t offset) { \ + return imp_implementationWithBlock(^id(id msg, jint idx, EXTERNAL_TYPE_##NAME value) { \ + NIL_CHECK_##NAME(value); \ + TYPE_##NAME internalValue = ToType##NAME(value); \ + CGPRepeatedFieldSet##NAME(REPEATED_FIELD_PTR(msg, offset), idx, internalValue); \ + return msg; \ + }); \ } -FOR_EACH_TYPE_WITH_ENUM(GET_REPEATED_SETTER_IMP) +FOR_EACH_TYPE(GET_REPEATED_SETTER_IMP) #undef GET_REPEATED_SETTER_IMP @@ -543,10 +530,9 @@ static BOOL AddSetterMethod(Class cls, SEL sel, CGPFieldDescriptor *field) { #define ADD_SETTER_METHOD_CASE(NAME) \ imp = repeated ? GetRepeatedSetterImp##NAME(offset) \ : GetSingularSetterImp##NAME(offset, GetHasLocator(cls, field)); \ - strcat(encoding, @encode(TYPE_##NAME)); \ - break; + strcat(encoding, @encode(TYPE_##NAME)); - SWITCH_TYPES_WITH_ENUM(CGPFieldGetJavaType(field), ADD_SETTER_METHOD_CASE) + SWITCH_TYPES(CGPFieldGetJavaType(field), ADD_SETTER_METHOD_CASE) #undef ADD_SETTER_METHOD_CASE @@ -572,15 +558,17 @@ static BOOL AddBuilderSetterMethod(Class cls, SEL sel, CGPFieldDescriptor *field return class_addMethod(cls, sel, imp, "@@:@"); } -#define GET_ADDER_IMP(NAME) \ - static IMP GetAdderImp##NAME(size_t offset) { \ - return imp_implementationWithBlock(^id(id msg, TYPE_##NAME value) { \ - NIL_CHECK_##NAME(value) CGPRepeatedFieldAdd##NAME(REPEATED_FIELD_PTR(msg, offset), value); \ - return msg; \ - }); \ +#define GET_ADDER_IMP(NAME) \ + static IMP GetAdderImp##NAME(size_t offset) { \ + return imp_implementationWithBlock(^id(id msg, EXTERNAL_TYPE_##NAME value) { \ + NIL_CHECK_##NAME(value); \ + TYPE_##NAME internalValue = ToType##NAME(value); \ + CGPRepeatedFieldAdd##NAME(REPEATED_FIELD_PTR(msg, offset), internalValue); \ + return msg; \ + }); \ } -FOR_EACH_TYPE_WITH_ENUM(GET_ADDER_IMP) +FOR_EACH_TYPE(GET_ADDER_IMP) #undef GET_ADDER_IMP @@ -590,12 +578,11 @@ static BOOL AddAdderMethod(Class cls, SEL sel, CGPFieldDescriptor *field) { strcpy(encoding, "@@:"); size_t offset = CGPFieldGetOffset(field, cls); -#define ADD_ADDER_METHOD_CASE(NAME) \ - imp = GetAdderImp##NAME(offset); \ - strcat(encoding, @encode(TYPE_##NAME)); \ - break; +#define ADD_ADDER_METHOD_CASE(NAME) \ + imp = GetAdderImp##NAME(offset); \ + strcat(encoding, @encode(TYPE_##NAME)); - SWITCH_TYPES_WITH_ENUM(CGPFieldGetJavaType(field), ADD_ADDER_METHOD_CASE) + SWITCH_TYPES(CGPFieldGetJavaType(field), ADD_ADDER_METHOD_CASE) #undef ADD_ADDER_METHOD_CASE @@ -607,7 +594,7 @@ static BOOL AddBuilderAdderMethod(Class cls, SEL sel, CGPFieldDescriptor *field) IMP imp = imp_implementationWithBlock(^id(id msg, ComGoogleProtobufGeneratedMessage_Builder *value) { (void)nil_chk(value); - CGPRepeatedFieldAddRetainable(REPEATED_FIELD_PTR(msg, offset), [value build]); + CGPRepeatedFieldAddId(REPEATED_FIELD_PTR(msg, offset), [value build]); return msg; }); return class_addMethod(cls, sel, imp, "@@:@"); @@ -624,7 +611,7 @@ static BOOL AddBuilderAdderMethod(Class cls, SEL sel, CGPFieldDescriptor *field) }); \ } -FOR_EACH_TYPE_WITH_ENUM(GET_ADD_ALL_IMP) +FOR_EACH_TYPE(GET_ADD_ALL_IMP) #undef GET_ADD_ALL_IMP @@ -632,11 +619,9 @@ static BOOL AddAddAllMethod(Class cls, SEL sel, CGPFieldDescriptor *field) { IMP imp = NULL; size_t offset = CGPFieldGetOffset(field, cls); -#define ADD_ALL_METHOD_CASE(NAME) \ - imp = GetAddAllImp##NAME(offset); \ - break; +#define ADD_ALL_METHOD_CASE(NAME) imp = GetAddAllImp##NAME(offset); - SWITCH_TYPES_WITH_ENUM(CGPFieldGetJavaType(field), ADD_ALL_METHOD_CASE) + SWITCH_TYPES(CGPFieldGetJavaType(field), ADD_ALL_METHOD_CASE) #undef ADD_ALL_METHOD_CASE @@ -646,9 +631,9 @@ static BOOL AddAddAllMethod(Class cls, SEL sel, CGPFieldDescriptor *field) { #define GET_CONTAINS_IMP(NAME) \ static IMP GetContainsImp##NAME(size_t offset, CGPFieldJavaType keyType, \ CGPFieldJavaType valueType) { \ - return imp_implementationWithBlock(^bool(id msg, TYPE_##NAME pKey) { \ + return imp_implementationWithBlock(^bool(id msg, EXTERNAL_TYPE_##NAME pKey) { \ CGPValue key; \ - key.CGPValueField_##NAME = pKey; \ + key.CGPValueField_##NAME = ToType##NAME(pKey); \ return CGPMapFieldGetWithKey(MAP_FIELD_PTR(msg, offset), key, keyType, valueType) != nil; \ }); \ } @@ -696,83 +681,46 @@ static BOOL AddContainsMethod(Class cls, SEL sel, CGPFieldDescriptor *field) { return class_addMethod(cls, sel, imp, encoding); } -#define GET_MAP_PRIMITIVE_GETTER_IMP(KEY_NAME, VALUE_NAME) \ +#define GET_MAP_GETTER_IMP(KEY_NAME, VALUE_NAME) \ static IMP GetMapGetOrThrowImp##KEY_NAME##VALUE_NAME(size_t offset, CGPFieldJavaType keyType, \ CGPFieldJavaType valueType) { \ - return imp_implementationWithBlock(^TYPE_##VALUE_NAME(id msg, TYPE_##KEY_NAME pKey) { \ - CGPValue key; \ - key.CGPValueField_##KEY_NAME = pKey; \ - CGPMapFieldEntry *entry = \ - CGPMapFieldGetWithKey(MAP_FIELD_PTR(msg, offset), key, keyType, valueType); \ - if (entry) { \ - return entry->value.CGPValueField_##VALUE_NAME; \ - } \ - @throw create_JavaLangIllegalArgumentException_init(); \ - }); \ - } \ - static IMP GetMapGetOrDefaultImp##KEY_NAME##VALUE_NAME(size_t offset, CGPFieldJavaType keyType, \ - CGPFieldJavaType valueType) { \ return imp_implementationWithBlock( \ - ^TYPE_##VALUE_NAME(id msg, TYPE_##KEY_NAME pKey, TYPE_##VALUE_NAME defaultValue) { \ + ^EXTERNAL_TYPE_##VALUE_NAME(id msg, EXTERNAL_TYPE_##KEY_NAME pKey) { \ CGPValue key; \ - key.CGPValueField_##KEY_NAME = pKey; \ + key.CGPValueField_##KEY_NAME = ToType##KEY_NAME(pKey); \ CGPMapFieldEntry *entry = \ CGPMapFieldGetWithKey(MAP_FIELD_PTR(msg, offset), key, keyType, valueType); \ if (entry) { \ - return entry->value.CGPValueField_##VALUE_NAME; \ + return ToExternalType##VALUE_NAME(entry->value.CGPValueField_##VALUE_NAME); \ } \ - return defaultValue; \ + @throw create_JavaLangIllegalArgumentException_init(); \ }); \ - } - -#define GET_MAP_RETAINABLE_GETTER_IMP(KEY_NAME, VALUE_NAME) \ - static IMP GetMapGetOrThrowImp##KEY_NAME##VALUE_NAME(size_t offset, CGPFieldJavaType keyType, \ - CGPFieldJavaType valueType) { \ - return imp_implementationWithBlock(^TYPE_##VALUE_NAME(id msg, TYPE_##KEY_NAME pKey) { \ + } \ + static IMP GetMapGetOrDefaultImp##KEY_NAME##VALUE_NAME(size_t offset, CGPFieldJavaType keyType, \ + CGPFieldJavaType valueType) { \ + return imp_implementationWithBlock(^EXTERNAL_TYPE_##VALUE_NAME( \ + id msg, EXTERNAL_TYPE_##KEY_NAME pKey, TYPE_##VALUE_NAME defaultValue) { \ CGPValue key; \ - key.CGPValueField_##KEY_NAME = pKey; \ + key.CGPValueField_##KEY_NAME = ToType##KEY_NAME(pKey); \ CGPMapFieldEntry *entry = \ CGPMapFieldGetWithKey(MAP_FIELD_PTR(msg, offset), key, keyType, valueType); \ if (entry) { \ - return RETAIN_AND_AUTORELEASE(entry->value.CGPValueField_##VALUE_NAME); \ + return ToExternalType##VALUE_NAME(entry->value.CGPValueField_##VALUE_NAME); \ } \ - @throw create_JavaLangIllegalArgumentException_init(); \ + return ToExternalType##VALUE_NAME(defaultValue); \ }); \ - } \ - static IMP GetMapGetOrDefaultImp##KEY_NAME##VALUE_NAME(size_t offset, CGPFieldJavaType keyType, \ - CGPFieldJavaType valueType) { \ - return imp_implementationWithBlock( \ - ^TYPE_##VALUE_NAME(id msg, TYPE_##KEY_NAME pKey, TYPE_##VALUE_NAME defaultValue) { \ - CGPValue key; \ - key.CGPValueField_##KEY_NAME = pKey; \ - CGPMapFieldEntry *entry = \ - CGPMapFieldGetWithKey(MAP_FIELD_PTR(msg, offset), key, keyType, valueType); \ - if (entry) { \ - return RETAIN_AND_AUTORELEASE(entry->value.CGPValueField_##VALUE_NAME); \ - } \ - return defaultValue; \ - }); \ } -#define GET_MAP_PRIMITIVE_GETTER_IMP_FOR_VALUE(VALUE_NAME) \ - GET_MAP_PRIMITIVE_GETTER_IMP(Int, VALUE_NAME) \ - GET_MAP_PRIMITIVE_GETTER_IMP(Long, VALUE_NAME) \ - GET_MAP_PRIMITIVE_GETTER_IMP(Bool, VALUE_NAME) \ - GET_MAP_PRIMITIVE_GETTER_IMP(Id, VALUE_NAME) - -#define GET_MAP_RETAINABLE_GETTER_IMP_FOR_VALUE(VALUE_NAME) \ - GET_MAP_RETAINABLE_GETTER_IMP(Int, VALUE_NAME) \ - GET_MAP_RETAINABLE_GETTER_IMP(Long, VALUE_NAME) \ - GET_MAP_RETAINABLE_GETTER_IMP(Bool, VALUE_NAME) \ - GET_MAP_RETAINABLE_GETTER_IMP(Id, VALUE_NAME) +#define GET_MAP_GETTER_IMP_FOR_VALUE(VALUE_NAME) \ + GET_MAP_GETTER_IMP(Int, VALUE_NAME) \ + GET_MAP_GETTER_IMP(Long, VALUE_NAME) \ + GET_MAP_GETTER_IMP(Bool, VALUE_NAME) \ + GET_MAP_GETTER_IMP(Id, VALUE_NAME) -FOR_EACH_TYPE_NO_ENUM(GET_MAP_PRIMITIVE_GETTER_IMP_FOR_VALUE, - GET_MAP_RETAINABLE_GETTER_IMP_FOR_VALUE) +FOR_EACH_TYPE(GET_MAP_GETTER_IMP_FOR_VALUE) -#undef GET_MAP_PRIMITIVE_GETTER_IMP -#undef GET_MAP_RETAINABLE_GETTER_IMP -#undef GET_MAP_PRIMITIVE_GETTER_IMP_FOR_VALUE -#undef GET_MAP_RETAINABLE_GETTER_IMP_FOR_VALUE +#undef GET_MAP_GETTER_IMP +#undef GET_MAP_GETTER_IMP_FOR_VALUE static BOOL AddMapGetWithKeyMethod(Class cls, SEL sel, CGPFieldDescriptor *field, bool orDefault) { IMP imp = NULL; @@ -808,10 +756,9 @@ static BOOL AddMapGetWithKeyMethod(Class cls, SEL sel, CGPFieldDescriptor *field } \ if (orDefault) { \ strcat(encoding, @encode(TYPE_##VALUE_NAME)); \ - } \ - break; + } - SWITCH_TYPES_NO_ENUM(valueType, MAP_GETTER_INNER_SWITCH) + SWITCH_TYPES(valueType, MAP_GETTER_INNER_SWITCH) #undef MAP_GETTER_CASE #undef MAP_GETTER_INNER_SWITCH @@ -819,19 +766,18 @@ static BOOL AddMapGetWithKeyMethod(Class cls, SEL sel, CGPFieldDescriptor *field return class_addMethod(cls, sel, imp, encoding); } -#define GET_PUT_IMP(KEY_NAME, VALUE_NAME) \ - static IMP GetPutImp##KEY_NAME##VALUE_NAME(size_t offset, CGPFieldJavaType keyType, \ - CGPFieldJavaType valueType) { \ - return imp_implementationWithBlock( \ - ^id(id msg, TYPE_##KEY_NAME pKey, TYPE_##VALUE_NAME pValue) { \ - CGPValue key; \ - key.CGPValueField_##KEY_NAME = pKey; \ - CGPValue value; \ - value.CGPValueField_##VALUE_NAME = pValue; \ - CGPMapFieldPut(MAP_FIELD_PTR(msg, offset), key, keyType, value, valueType, \ - /* retainedKeyAndValue */ false); \ - return msg; \ - }); \ +#define GET_PUT_IMP(KEY_NAME, VALUE_NAME) \ + static IMP GetPutImp##KEY_NAME##VALUE_NAME(size_t offset, CGPFieldJavaType keyType, \ + CGPFieldJavaType valueType) { \ + return imp_implementationWithBlock( \ + ^id(id msg, EXTERNAL_TYPE_##KEY_NAME pKey, EXTERNAL_TYPE_##VALUE_NAME pValue) { \ + CGPValue key; \ + key.CGPValueField_##KEY_NAME = ToType##KEY_NAME(pKey); \ + CGPValue value; \ + value.CGPValueField_##VALUE_NAME = ToType##VALUE_NAME(pValue); \ + CGPMapFieldPut(MAP_FIELD_PTR(msg, offset), key, keyType, value, valueType); \ + return msg; \ + }); \ } #define GET_PUT_IMP_FOR_VALUE(VALUE_NAME) \ @@ -841,7 +787,7 @@ static BOOL AddMapGetWithKeyMethod(Class cls, SEL sel, CGPFieldDescriptor *field GET_PUT_IMP(Id, VALUE_NAME) // No difference in put implementation by type, CGPMapFieldPut() handles both types. -FOR_EACH_TYPE_NO_ENUM(GET_PUT_IMP_FOR_VALUE, GET_PUT_IMP_FOR_VALUE) +FOR_EACH_TYPE(GET_PUT_IMP_FOR_VALUE) #undef GET_PUT_IMP #undef GET_PUT_IMP_FOR_VALUE @@ -876,10 +822,9 @@ static BOOL AddPutMethod(Class cls, SEL sel, CGPFieldDescriptor *field) { case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_MESSAGE: \ return NO; \ } \ - strcat(encoding, @encode(TYPE_##VALUE_NAME)); \ - break; + strcat(encoding, @encode(TYPE_##VALUE_NAME)); - SWITCH_TYPES_NO_ENUM(valueType, PUT_METHOD_INNER_SWITCH) + SWITCH_TYPES(valueType, PUT_METHOD_INNER_SWITCH) #undef PUT_METHOD_CASE #undef PUT_METHOD_INNER_SWITCH @@ -887,15 +832,15 @@ static BOOL AddPutMethod(Class cls, SEL sel, CGPFieldDescriptor *field) { return class_addMethod(cls, sel, imp, encoding); } -#define GET_MAP_REMOVE_IMP(NAME) \ - static IMP GetMapRemoveImp##NAME(size_t offset, CGPFieldJavaType keyType, \ - CGPFieldJavaType valueType) { \ - return imp_implementationWithBlock(^id(id msg, TYPE_##NAME pKey) { \ - CGPValue key; \ - key.CGPValueField_##NAME = pKey; \ - CGPMapFieldRemove(MAP_FIELD_PTR(msg, offset), key, keyType, valueType); \ - return msg; \ - }); \ +#define GET_MAP_REMOVE_IMP(NAME) \ + static IMP GetMapRemoveImp##NAME(size_t offset, CGPFieldJavaType keyType, \ + CGPFieldJavaType valueType) { \ + return imp_implementationWithBlock(^id(id msg, EXTERNAL_TYPE_##NAME pKey) { \ + CGPValue key; \ + key.CGPValueField_##NAME = ToType##NAME(pKey); \ + CGPMapFieldRemove(MAP_FIELD_PTR(msg, offset), key, keyType, valueType); \ + return msg; \ + }); \ } GET_MAP_REMOVE_IMP(Int) @@ -1296,14 +1241,14 @@ static id GetSingularField(id msg, CGPFieldDescriptor *field) { bool isSet = GetHas(msg, GetHasLocator(msgCls, field)); size_t offset = CGPFieldGetOffset(field, msgCls); -#define GET_FIELD_CASE(NAME) \ - { \ - TYPE_##NAME value = isSet ? *FIELD_PTR(TYPE_##NAME, msg, offset) \ - : field->data_->defaultValue.CGPValueField_##NAME; \ - return CGPToReflectionType##NAME(value, field); \ +#define GET_FIELD_CASE(NAME) \ + { \ + TYPE_##NAME value = isSet ? *FIELD_PTR(TYPE_##NAME, msg, offset) \ + : CGPFieldGetDefaultValue(field).CGPValueField_##NAME; \ + return CGPToReflectionType##NAME(value); \ } - SWITCH_TYPES_WITH_ENUM(CGPFieldGetJavaType(field), GET_FIELD_CASE) + SWITCH_TYPES(CGPFieldGetJavaType(field), GET_FIELD_CASE) #undef GET_FIELD_CASE @@ -1501,13 +1446,10 @@ static void MergeFieldsFromMessage(id msg, id other, CGPDescriptor *descriptor) } ClearPreviousOneof(msg, hasLoc, fieldPtr); -#define MERGE_FIELD_CASE(NAME) \ - { \ - TYPE_ASSIGN_##NAME(*(TYPE_##NAME *)fieldPtr, *(TYPE_##NAME *)otherFieldPtr); \ - break; \ - } +#define MERGE_FIELD_CASE(NAME) \ + TYPE_ASSIGN_##NAME((TYPE_##NAME *)fieldPtr, *(TYPE_##NAME *)otherFieldPtr); - SWITCH_TYPES_WITH_ENUM(type, MERGE_FIELD_CASE) + SWITCH_TYPES(type, MERGE_FIELD_CASE) #undef MERGE_FIELD_CASE @@ -1572,21 +1514,13 @@ static id DynamicMergeFromMessage(id builder, SEL _cmd, id other) { // ***************************************************************************** static inline BOOL ReadEnumValueDescriptor(CGPCodedInputStream *input, CGPEnumDescriptor *enumType, - id *valueDescriptor) { + CGPEnumValueDescriptor **valueDescriptor) { jint value; if (!CGPReadEnum(input, &value)) return NO; *valueDescriptor = CGPEnumValueDescriptorFromInt(enumType, value); return YES; } -static BOOL ReadEnumJavaValue(CGPCodedInputStream *input, CGPEnumDescriptor *enumType, - id *javaValue) { - CGPEnumValueDescriptor *valueDescriptor; - if (!ReadEnumValueDescriptor(input, enumType, &valueDescriptor)) return NO; - *javaValue = valueDescriptor == nil ? nil : valueDescriptor->enum_; - return YES; -} - static inline BOOL MergeGroupFieldFromStream(id msg, CGPFieldDescriptor *field, CGPCodedInputStream *input, CGPExtensionRegistryLite *registry) { @@ -1642,8 +1576,16 @@ static BOOL ReadMapEntryField(CGPCodedInputStream *stream, CGPFieldDescriptor *f return CGPReadFloat(stream, &value->valueFloat); case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_DOUBLE: return CGPReadDouble(stream, &value->valueDouble); - case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_ENUM: - return ReadEnumJavaValue(stream, field->valueType_, &value->valueId) && value->valueId != nil; + case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_ENUM: { + CGPEnumValueDescriptor *enumValueDescriptor; + BOOL result = ReadEnumValueDescriptor(stream, field->valueType_, &enumValueDescriptor); + if (result && enumValueDescriptor != nil) { + value->valueEnum = RETAIN_(enumValueDescriptor); + return YES; + } else { + return NO; + } + } case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_BYTES: return stream->ReadRetainedByteString(&value->valueId); case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_STRING: @@ -1671,28 +1613,34 @@ static BOOL MergeMapEntryFromStream(CGPMapField *field, CGPCodedInputStream *str if (!CGPReadInt32(stream, &length)) return NO; CGPCodedInputStream::Limit limit = stream->PushLimit(length); CGPFieldDescriptor *keyField = entry->fields_->buffer_[0]; + CGPFieldJavaType keyType = CGPFieldGetJavaType(keyField); CGPFieldDescriptor *valueField = entry->fields_->buffer_[1]; - BOOL hasKey = NO; - BOOL hasValue = NO; + CGPFieldJavaType valueType = CGPFieldGetJavaType(valueField); CGPValue key; + key.valueId = nil; CGPValue value; + value.valueId = nil; while (YES) { uint32_t tag = stream->ReadTag(); if (tag == 0) break; switch (CGPWireFormatGetTagFieldNumber(tag)) { case 1: - if (hasKey && CGPIsRetainedType(CGPFieldGetJavaType(keyField))) { - RELEASE_(key.valueId); + if (ReadMapEntryField(stream, keyField, tag, registry, &key)) { + if (CGPIsRetainedType(keyType)) { + AUTORELEASE(key.valueId); + } + } else { + return NO; } - ReadMapEntryField(stream, keyField, tag, registry, &key); - hasKey = YES; break; case 2: - if (hasValue && CGPIsRetainedType(CGPFieldGetJavaType(valueField))) { - RELEASE_(value.valueId); + if (ReadMapEntryField(stream, valueField, tag, registry, &value)) { + if (CGPIsRetainedType(valueType)) { + AUTORELEASE(value.valueId); + } + } else { + return NO; } - ReadMapEntryField(stream, valueField, tag, registry, &value); - hasValue = YES; break; default: if (!CGPWireFormatSkipField(stream, tag)) return NO; @@ -1701,8 +1649,40 @@ static BOOL MergeMapEntryFromStream(CGPMapField *field, CGPCodedInputStream *str } if (!stream->ConsumedEntireMessage()) return NO; stream->PopLimit(limit); - CGPMapFieldPut(field, key, CGPFieldGetJavaType(keyField), value, CGPFieldGetJavaType(valueField), - /* retainedKeyAndValue */ true); + // Strings are the only retainer type that can be a key. + // https://protobuf.dev/programming-guides/proto3/#maps + if ((keyType == ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_STRING) && + (key.valueId == nil)) { + key.valueId = @""; + } + if ((CGPIsRetainedType(valueType)) && (value.valueId == nil)) { + switch (valueType) { + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_STRING: + value.valueId = @""; + break; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BYTE_STRING: + value.valueId = [CGPByteString empty]; + break; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_MESSAGE: + if (valueField->valueType_ == nil) { + // Should not happen, but protect against crash. + return NO; + } + // Force the class to be initialized. + [valueField->valueType_ class]; + value.valueId = AUTORELEASE(CGPNewMessage(valueField->valueType_)); + break; + case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM: + value.valueEnum = [valueField getDefaultValue]; + break; + default: + // Should not happen, but we don't trust potential undefined behavior + // in CGPIsRetainedType. + return NO; + break; + } + } + CGPMapFieldPut(field, key, keyType, value, valueType); return YES; } @@ -1726,7 +1706,7 @@ static BOOL MergeExtensionValueFromStream(CGPCodedInputStream *stream, CGPFieldD case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_##ENUM_NAME: { \ TYPE_##JAVA_NAME value; \ if (!CGPRead##WIRE_NAME(stream, &value)) return NO; \ - *result = CGPToReflectionType##JAVA_NAME(value, field); \ + *result = CGPToReflectionType##JAVA_NAME(value); \ *isRetained = NO; \ return YES; \ } @@ -1914,8 +1894,7 @@ static BOOL MergeFieldFromStream(id msg, CGPFieldDescriptor *field, CGPCodedInpu BOOL alreadyCleared = NO; if (!repeated) { hasLoc = GetHasLocator(msgCls, field); - ClearPreviousOneof(msg, hasLoc, fieldPtr); - alreadyCleared = YES; + alreadyCleared = ClearPreviousOneof(msg, hasLoc, fieldPtr); } switch (CGPFieldGetType(field)) { #define MERGE_FIELD_CASE(NAME, ENUM_NAME, JAVA_NAME) \ @@ -1967,22 +1946,26 @@ static BOOL MergeFieldFromStream(id msg, CGPFieldDescriptor *field, CGPCodedInpu CGPRepeatedFieldReserveAdditionalCapacity(repeatedField, length, sizeof(id)); CGPCodedInputStream::Limit limit = stream->PushLimit(length); while (stream->BytesUntilLimit() > 0) { - if (!ReadEnumJavaValue(stream, enumType, &value)) return NO; + if (!ReadEnumValueDescriptor(stream, enumType, &value)) return NO; if (value != nil) { CGPRepeatedFieldAddEnum(repeatedField, value); } } stream->PopLimit(limit); } else { - if (!ReadEnumJavaValue(stream, enumType, &value)) return NO; + if (!ReadEnumValueDescriptor(stream, enumType, &value)) return NO; if (value != nil) { CGPRepeatedFieldAddEnum(repeatedField, value); } } } else { - if (!ReadEnumJavaValue(stream, enumType, &value)) return NO; + if (!ReadEnumValueDescriptor(stream, enumType, &value)) return NO; if (value == nil) return YES; // Skip setting has-bit. - *(id *)fieldPtr = value; + id *ptr = (id *)fieldPtr; + if (!alreadyCleared) { + AUTORELEASE(*ptr); + } + *ptr = RETAIN_(value); SetHas(msg, hasLoc); } } @@ -1991,7 +1974,8 @@ static BOOL MergeFieldFromStream(id msg, CGPFieldDescriptor *field, CGPCodedInpu CGPByteString *value; if (!stream->ReadRetainedByteString(&value)) return NO; if (repeated) { - CGPRepeatedFieldAddRetainedId((CGPRepeatedField *)fieldPtr, value); + CGPRepeatedFieldAddId((CGPRepeatedField *)fieldPtr, value); + RELEASE_(value); } else { id *ptr = (id *)fieldPtr; if (!alreadyCleared) { @@ -2006,7 +1990,8 @@ static BOOL MergeFieldFromStream(id msg, CGPFieldDescriptor *field, CGPCodedInpu NSString *value; if (!stream->ReadRetainedNSString(&value)) return NO; if (repeated) { - CGPRepeatedFieldAddRetainedId((CGPRepeatedField *)fieldPtr, value); + CGPRepeatedFieldAddId((CGPRepeatedField *)fieldPtr, value); + RELEASE_(value); } else { id *ptr = (id *)fieldPtr; if (!alreadyCleared) { @@ -2027,7 +2012,8 @@ static BOOL MergeFieldFromStream(id msg, CGPFieldDescriptor *field, CGPCodedInpu } ComGoogleProtobufGeneratedMessage *msgField = CGPNewMessage(fieldType); if (repeated) { - CGPRepeatedFieldAddRetainedId((CGPRepeatedField *)fieldPtr, msgField); + CGPRepeatedFieldAddId((CGPRepeatedField *)fieldPtr, msgField); + RELEASE_(msgField); } else { id *ptr = (id *)fieldPtr; if (GetHas(msg, hasLoc)) { @@ -2266,7 +2252,7 @@ static int SerializedSizeForMapEntryField(CGPFieldDescriptor *field, CGPValue va case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_BOOL: return tagSize + 1; case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_ENUM: - return tagSize + CGPGetEnumSize(CGPEnumGetIntValue(field->valueType_, value.valueId)); + return tagSize + CGPGetEnumSize(CGPEnumGetIntValue(field->valueType_, value.valueEnum)); case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_BYTES: return tagSize + CGPGetBytesSize(value.valueId); case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_STRING: @@ -2348,7 +2334,7 @@ static int SerializedSizeForRepeatedField(id msg, CGPFieldDescriptor *field) { break; case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_ENUM: { CGPEnumDescriptor *enumType = field->valueType_; - id *buffer = (id *)data->buffer; + TYPE_Enum *buffer = (TYPE_Enum *)data->buffer; for (uint32_t i = 0; i < arrayLen; i++) { arraySize += CGPGetEnumSize(CGPEnumGetIntValue(enumType, buffer[i])); } @@ -2496,8 +2482,8 @@ static void WriteSingularExtensionValue(CGPFieldDescriptor *field, id value, WRITE_SINGULAR_EXTENSION_CASE(Bool, BOOL, Bool) WRITE_SINGULAR_EXTENSION_CASE(Float, FLOAT, Float) WRITE_SINGULAR_EXTENSION_CASE(Double, DOUBLE, Double) - WRITE_SINGULAR_EXTENSION_CASE(Bytes, BYTES, Retainable) - WRITE_SINGULAR_EXTENSION_CASE(String, STRING, Retainable) + WRITE_SINGULAR_EXTENSION_CASE(Bytes, BYTES, Id) + WRITE_SINGULAR_EXTENSION_CASE(String, STRING, Id) case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_ENUM: CGPWriteEnum(((CGPEnumValueDescriptor *)value)->number_, output); return; @@ -2640,7 +2626,7 @@ static void WriteMapEntryField(CGPFieldDescriptor *field, CGPValue value, CGPWriteDouble(value.valueDouble, output); return; case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_ENUM: - CGPWriteEnum(CGPEnumGetIntValue(field->valueType_, value.valueId), output); + CGPWriteEnum(CGPEnumGetIntValue(field->valueType_, value.valueEnum), output); return; case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_BYTES: CGPWriteBytes(value.valueId, output); @@ -2743,7 +2729,7 @@ static void WriteRepeatedField(id msg, CGPFieldDescriptor *field, CGPCodedOutput WRITE_REPEATED_FIELD_FIXED_LENGTH_CASE(Float, FLOAT, Float, sizeof(uint32_t)) WRITE_REPEATED_FIELD_FIXED_LENGTH_CASE(Double, DOUBLE, Double, sizeof(uint64_t)) case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_ENUM: { - id *buffer = (id *)data->buffer; + TYPE_Enum *buffer = (TYPE_Enum *)data->buffer; CGPEnumDescriptor *enumType = field->valueType_; if (CGPFieldIsPacked(field)) { std::vector intValues(arrayLen); @@ -3041,7 +3027,7 @@ void ValueToString(CGPValue value, CGPFieldDescriptor *field, NSMutableString *b [builder appendFormat:@"%s%s: %g\n", padding, fieldName, value.valueDouble]; return; case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_ENUM: - [builder appendFormat:@"%s%s: %@\n", padding, fieldName, value.valueId]; + [builder appendFormat:@"%s%s: %@\n", padding, fieldName, value.valueEnum]; return; case ComGoogleProtobufDescriptors_FieldDescriptor_Type_Enum_BYTES: [builder appendFormat:@"%s%s: %@\n", padding, fieldName, BytesToString(value.valueId)]; @@ -3193,20 +3179,20 @@ static void MessageToString(id msg, CGPDescriptor *descriptor, NSMutableString * // ********** isEqual and hash ************************************************* // ***************************************************************************** -#define FieldIsEqualInt(a, b) a == b -#define FieldIsEqualLong(a, b) a == b -#define FieldIsEqualFloat(a, b) a == b -#define FieldIsEqualDouble(a, b) a == b -#define FieldIsEqualBool(a, b) a == b -#define FieldIsEqualEnum(a, b) a == b -#define FieldIsEqualRetainable(a, b) a == b || [a isEqual:b] +CGP_ALWAYS_INLINE BOOL FieldIsEqualInt(jint a, jint b) { return a == b; } +CGP_ALWAYS_INLINE BOOL FieldIsEqualLong(jlong a, jlong b) { return a == b; } +CGP_ALWAYS_INLINE BOOL FieldIsEqualFloat(jfloat a, jfloat b) { return a == b; } +CGP_ALWAYS_INLINE BOOL FieldIsEqualDouble(jdouble a, jdouble b) { return a == b; } +CGP_ALWAYS_INLINE BOOL FieldIsEqualBool(bool a, bool b) { return a == b; } +CGP_ALWAYS_INLINE BOOL FieldIsEqualEnum(id a, id b) { return a == b || [a isEqual:b]; } +CGP_ALWAYS_INLINE BOOL FieldIsEqualId(id a, id b) { return a == b || [a isEqual:b]; } static BOOL FieldIsEqual(id self, id other, size_t offset, CGPFieldJavaType type) { #define IS_FIELD_EQUAL_CASE(NAME) \ return FieldIsEqual##NAME(*FIELD_PTR(TYPE_##NAME, self, offset), \ *FIELD_PTR(TYPE_##NAME, other, offset)); - SWITCH_TYPES_WITH_ENUM(type, IS_FIELD_EQUAL_CASE) + SWITCH_TYPES(type, IS_FIELD_EQUAL_CASE) #undef IS_FIELD_EQUAL_CASE @@ -3290,10 +3276,9 @@ static int RepeatedFieldHash(id msg, CGPFieldDescriptor *field, int hash) { TYPE_##NAME value = buffer[i]; \ hash = 31 * hash + HASH_##NAME(value); \ } \ - } \ - break; + } - SWITCH_TYPES_NO_ENUM(CGPFieldGetJavaType(field), REPEATED_FIELD_HASH_CASE) + SWITCH_TYPES(CGPFieldGetJavaType(field), REPEATED_FIELD_HASH_CASE) #undef REPEATED_FIELD_HASH_CASE @@ -3314,7 +3299,7 @@ static int SingularFieldHash(id msg, CGPFieldDescriptor *field, int hash) { return 53 * hash + HASH_##NAME(value); \ } - SWITCH_TYPES_NO_ENUM(CGPFieldGetJavaType(field), SINGULAR_FIELD_HASH_CASE) + SWITCH_TYPES(CGPFieldGetJavaType(field), SINGULAR_FIELD_HASH_CASE) #undef SINGULAR_FIELD_HASH_CASE @@ -3697,11 +3682,10 @@ - (id)getRepeatedFieldWithComGoogleProtobufDescriptors_FieldDescriptor: } else { CGPHasLocator hasLoc = GetHasLocator(cls, descriptor); -#define SET_SINGULAR_FIELD_CASE(NAME) \ - SingularSet##NAME(self, CGPFromReflectionType##NAME(object), offset, hasLoc); \ - break; +#define SET_SINGULAR_FIELD_CASE(NAME) \ + SingularSet##NAME(self, CGPFromReflectionType##NAME(object), offset, hasLoc); - SWITCH_TYPES_WITH_ENUM(javaType, SET_SINGULAR_FIELD_CASE) + SWITCH_TYPES(javaType, SET_SINGULAR_FIELD_CASE) #undef SET_SINGULAR_FIELD_CASE } @@ -3950,7 +3934,7 @@ static id GetSingularExtension(CGPExtensionMap *extensionMap, if (it != extensionMap->end()) { value = it->second.get(); } else { - value = CGPFieldGetDefaultValue(field); + value = CGPFieldGetDefaultValueObject(field); } return FromReflectionType(field, value); } diff --git a/protobuf/runtime/src/com/google/protobuf/MapField.h b/protobuf/runtime/src/com/google/protobuf/MapField.h index 4236555915..fdc54cd2ec 100644 --- a/protobuf/runtime/src/com/google/protobuf/MapField.h +++ b/protobuf/runtime/src/com/google/protobuf/MapField.h @@ -106,10 +106,10 @@ CGP_ALWAYS_INLINE uint32_t CGPMapFieldMapSize( CGPMapFieldEntry *CGPMapFieldGetWithKey( CGPMapField *field, CGPValue key, CGPFieldJavaType keyType, CGPFieldJavaType valueType); -// The caller will indicate whether the key and value are already retained. +// The key and value are assumed to not be retained. void CGPMapFieldPut( CGPMapField *field, CGPValue key, CGPFieldJavaType keyType, CGPValue value, - CGPFieldJavaType valueType, bool retainedKeyAndValue); + CGPFieldJavaType valueType); void CGPMapFieldRemove( CGPMapField *field, CGPValue key, CGPFieldJavaType keyType, CGPFieldJavaType valueType); diff --git a/protobuf/runtime/src/com/google/protobuf/MapField.m b/protobuf/runtime/src/com/google/protobuf/MapField.m index 5af34ab0e8..ab4eb8783a 100644 --- a/protobuf/runtime/src/com/google/protobuf/MapField.m +++ b/protobuf/runtime/src/com/google/protobuf/MapField.m @@ -50,7 +50,7 @@ static uint32_t Hash0(CGPValue value, CGPFieldJavaType type) { #define HASH_CASE(NAME) return HASH_##NAME(value.CGPValueField_##NAME); -SWITCH_TYPES_NO_ENUM(type, HASH_CASE) + SWITCH_TYPES(type, HASH_CASE) #undef HASH_CASE } @@ -76,7 +76,7 @@ static bool Equals(CGPValue a, CGPValue b, CGPFieldJavaType type) { case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BOOLEAN: return a.valueBool == b.valueBool; case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM: - return a.valueId == b.valueId; + return [a.valueEnum isEqual:b.valueEnum]; case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_FLOAT: return a.valueFloat == b.valueFloat; case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_DOUBLE: @@ -322,11 +322,11 @@ static void EnsureAdditionalHashMapCapacity( void CGPMapFieldPut( CGPMapField *field, CGPValue key, CGPFieldJavaType keyType, CGPValue value, - CGPFieldJavaType valueType, bool retainedKeyAndValue) { + CGPFieldJavaType valueType) { BOOL keyTypeIsRetainable = CGPIsRetainedType(keyType); BOOL valueTypeIsRetainable = CGPIsRetainedType(valueType); // The value is always added to the map so make sure it's retained. - if (valueTypeIsRetainable && !retainedKeyAndValue) { + if (valueTypeIsRetainable) { RETAIN_(value.valueId); } uint32_t hash = Hash(key, keyType); @@ -337,10 +337,6 @@ void CGPMapFieldPut( entry = GetFromHashArray(data, key, keyType, hash); } if (entry) { - // Existing entry so the key is not added to the map and must not be retained. - if (keyTypeIsRetainable && retainedKeyAndValue) { - [key.valueId autorelease]; - } // Release the previous value. if (valueTypeIsRetainable) { [entry->value.valueId autorelease]; @@ -348,7 +344,7 @@ void CGPMapFieldPut( entry->value = value; } else { // Creating a new entry using the passed in key which must be retained. - if (keyTypeIsRetainable && !retainedKeyAndValue) { + if (keyTypeIsRetainable) { RETAIN_(key.valueId); } EnsureAdditionalHashMapCapacity(field, 1, keyType, valueType); @@ -507,6 +503,7 @@ static id BoxedValue(CGPValue value, CGPFieldJavaType type) { case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BOOLEAN: return JavaLangBoolean_valueOfWithBoolean_(value.valueBool); case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM: + return value.valueEnum->enum_; case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_STRING: case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BYTE_STRING: case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_MESSAGE: @@ -533,6 +530,8 @@ static CGPValue UnboxValue(id value, CGPFieldJavaType type) { result.valueBool = [value booleanValue]; break; case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM: + result.valueEnum = [value getValueDescriptor]; + break; case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_STRING: case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BYTE_STRING: case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_MESSAGE: @@ -546,8 +545,7 @@ static CGPValue UnboxValue(id value, CGPFieldJavaType type) { // java.lang.Integer using the enum number (not ordinal). static id BoxedReflectionValue(CGPValue value, CGPFieldJavaType type) { if (type == ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM) { - return JavaLangInteger_valueOfWithInt_( - [(id)value.valueId getNumber]); + return JavaLangInteger_valueOfWithInt_([value.valueEnum getNumber]); } return BoxedValue(value, type); } @@ -558,7 +556,7 @@ static CGPValue UnboxReflectionValue(id value, CGPFieldDescriptor *field) { CGPEnumValueDescriptor *valueDesc = CGPEnumValueDescriptorFromInt(field->valueType_, [value intValue]); CGPValue result; - result.valueId = valueDesc ? valueDesc->enum_ : nil; + result.valueEnum = valueDesc; return result; } return UnboxValue(value, type); diff --git a/protobuf/runtime/src/com/google/protobuf/RepeatedField.h b/protobuf/runtime/src/com/google/protobuf/RepeatedField.h index ab11be09d9..b9fcf5c171 100644 --- a/protobuf/runtime/src/com/google/protobuf/RepeatedField.h +++ b/protobuf/runtime/src/com/google/protobuf/RepeatedField.h @@ -90,14 +90,6 @@ CGP_ALWAYS_INLINE void CGPRepeatedFieldCheckBounds(CGPRepeatedField *field, jint } } -CGP_ALWAYS_INLINE void CGPRepeatedFieldAddRetainedId(CGPRepeatedField *field, id value) { - uint32_t total_size = CGPRepeatedFieldTotalSize(field); - if (CGPRepeatedFieldSize(field) == total_size) { - CGPRepeatedFieldReserve(field, total_size + 1, sizeof(id)); - } - ((void **)field->data->buffer)[field->data->size++] = (ARCBRIDGE void *)(value); -} - id CGPNewRepeatedFieldList(CGPRepeatedField *field, CGPFieldJavaType type); NSArray *CGPNewRepeatedFieldArray(CGPRepeatedField *field, CGPFieldJavaType type); diff --git a/protobuf/runtime/src/com/google/protobuf/RepeatedField.m b/protobuf/runtime/src/com/google/protobuf/RepeatedField.m index 90a090af6d..cce9e3fffd 100644 --- a/protobuf/runtime/src/com/google/protobuf/RepeatedField.m +++ b/protobuf/runtime/src/com/google/protobuf/RepeatedField.m @@ -58,8 +58,10 @@ void CGPRepeatedFieldReserve(CGPRepeatedField *field, uint32_t new_size, size_t } uint32_t newTotalSize = MAX(MIN_REPEATED_FIELD_SIZE, MAX(data->total_size * 2, new_size)); + uint32_t oldTotalSize = data->total_size; data->total_size = newTotalSize; data->buffer = realloc(data->buffer, newTotalSize * elemSize); + bzero(data->buffer + elemSize * oldTotalSize, elemSize * (newTotalSize - oldTotalSize)); } void CGPRepeatedFieldCopyData(CGPRepeatedField *field, CGPFieldJavaType type) { @@ -82,8 +84,8 @@ void CGPRepeatedFieldCopyData(CGPRepeatedField *field, CGPFieldJavaType type) { } } -void CGPRepeatedFieldAppendOther( - CGPRepeatedField *field, CGPRepeatedField *other, CGPFieldJavaType type) { +void CGPRepeatedFieldAppendOther(CGPRepeatedField *field, CGPRepeatedField *other, + CGPFieldJavaType type) { uint32_t otherSize = CGPRepeatedFieldSize(other); if (otherSize == 0) { return; @@ -130,9 +132,9 @@ id CGPRepeatedFieldGet(CGPRepeatedField *field, jint index, CGPFieldDescriptor * CGPRepeatedFieldCheckBounds(field, index); #define REPEATED_GET_CASE(NAME) \ - return CGPToReflectionType##NAME(((TYPE_##NAME *)field->data->buffer)[index], descriptor); \ + return CGPToReflectionType##NAME(((TYPE_##NAME *)field->data->buffer)[index]); - SWITCH_TYPES_WITH_ENUM(CGPFieldGetJavaType(descriptor), REPEATED_GET_CASE) + SWITCH_TYPES(CGPFieldGetJavaType(descriptor), REPEATED_GET_CASE) #undef REPEATED_GET_CASE } @@ -143,9 +145,7 @@ void CGPRepeatedMessageFieldRemove(CGPRepeatedField *field, jint index) { id *msgBuffer = (id *)field->data->buffer; AUTORELEASE(msgBuffer[index]); if (count > (uint32_t)index + 1) { - memmove(&(msgBuffer[index]), - &(msgBuffer[index + 1]), - sizeof(id) * (count - (index + 1))); + memmove(&(msgBuffer[index]), &(msgBuffer[index + 1]), sizeof(id) * (count - (index + 1))); } field->data->size -= 1; } @@ -153,26 +153,26 @@ void CGPRepeatedMessageFieldRemove(CGPRepeatedField *field, jint index) { void CGPRepeatedFieldSet(CGPRepeatedField *field, jint index, id value, CGPFieldJavaType type) { CGPRepeatedFieldCheckBounds(field, index); -#define REPEATED_SET_CASE(NAME) \ - { \ +#define REPEATED_SET_CASE(NAME) \ + { \ TYPE_##NAME *ptr = &((TYPE_##NAME *)field->data->buffer)[index]; \ - TYPE_ASSIGN_##NAME(*ptr, CGPFromReflectionType##NAME(value)); \ - break; \ + TYPE_ASSIGN_##NAME(ptr, CGPFromReflectionType##NAME(value)); \ } - SWITCH_TYPES_WITH_ENUM(type, REPEATED_SET_CASE) + SWITCH_TYPES(type, REPEATED_SET_CASE) #undef REPEATED_SET_CASE } // Make sure to reserve enough space in the buffer BEFORE calling this. static void CGPRepeatedFieldAddUnsafe(CGPRepeatedFieldData *data, id value, CGPFieldJavaType type) { -#define REPEATED_ADD_CASE(NAME) \ - ((TYPE_##NAME *)data->buffer)[data->size++] = \ - TYPE_RETAIN_##NAME(CGPFromReflectionType##NAME(value)); \ - break; +#define REPEATED_ADD_CASE(NAME) \ + { \ + TYPE_##NAME *ptr = &((TYPE_##NAME *)data->buffer)[data->size++]; \ + TYPE_ASSIGN_##NAME(ptr, CGPFromReflectionType##NAME(value)); \ + } - SWITCH_TYPES_WITH_ENUM(type, REPEATED_ADD_CASE) + SWITCH_TYPES(type, REPEATED_ADD_CASE) #undef REPEATED_ADD_CASE } @@ -185,8 +185,8 @@ void CGPRepeatedFieldAdd(CGPRepeatedField *field, id value, CGPFieldJavaType typ CGPRepeatedFieldAddUnsafe(field->data, value, type); } -void CGPRepeatedFieldAssignFromList( - CGPRepeatedField *field, id list, CGPFieldJavaType type) { +void CGPRepeatedFieldAssignFromList(CGPRepeatedField *field, id list, + CGPFieldJavaType type) { CGPRepeatedFieldClear(field, type); CGPRepeatedFieldReserve(field, [list size], CGPGetTypeSize(type)); @@ -203,13 +203,12 @@ void CGPRepeatedFieldAssignFromList( return newList; } -#define REPEATED_COPY_ELEM_CASE(NAME) \ - for (uint32_t i = 0; i < data->size; i++) { \ - [newList addWithId:CGPToReflectionType##NAME(((TYPE_##NAME *)data->buffer)[i], descriptor)]; \ - } \ - break; +#define REPEATED_COPY_ELEM_CASE(NAME) \ + for (uint32_t i = 0; i < data->size; i++) { \ + [newList addWithId:CGPToReflectionType##NAME(((TYPE_##NAME *)data->buffer)[i])]; \ + } - SWITCH_TYPES_WITH_ENUM(CGPFieldGetJavaType(descriptor), REPEATED_COPY_ELEM_CASE) + SWITCH_TYPES(CGPFieldGetJavaType(descriptor), REPEATED_COPY_ELEM_CASE) #undef REPEATED_COPY_ELEM_CASE @@ -254,7 +253,7 @@ @interface CGPRepeatedFieldList : JavaUtilAbstractList { @end // We need a subclass for String fields which must implement the ProtocolStringList interface. -@interface CGPRepeatedStringFieldList : CGPRepeatedFieldList < ComGoogleProtobufProtocolStringList > +@interface CGPRepeatedStringFieldList : CGPRepeatedFieldList @end @interface CGPStringAsByteStringList : JavaUtilAbstractList { @@ -265,8 +264,9 @@ @interface CGPStringAsByteStringList : JavaUtilAbstractList { id CGPNewRepeatedFieldList(CGPRepeatedField *field, CGPFieldJavaType type) { CGPRepeatedFieldList *list = - type == ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_STRING ? - [[CGPRepeatedStringFieldList alloc] init] : [[CGPRepeatedFieldList alloc] init]; + type == ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_STRING + ? [[CGPRepeatedStringFieldList alloc] init] + : [[CGPRepeatedFieldList alloc] init]; CGPRepeatedFieldData *data = field->data; if (data != NULL) { list->field_.data = data; @@ -293,9 +293,9 @@ - (id)getWithInt:(jint)index { CGPRepeatedFieldCheckBounds(&field_, index); #define REPEATED_LIST_GET_CASE(NAME) \ - return CGPBoxedValue##NAME(((TYPE_##NAME *)field_.data->buffer)[index]); \ + return CGPBoxedValue##NAME(((TYPE_##NAME *)field_.data->buffer)[index]); - SWITCH_TYPES_NO_ENUM(type_, REPEATED_LIST_GET_CASE) + SWITCH_TYPES(type_, REPEATED_LIST_GET_CASE) #undef REPEATED_LIST_GET_CASE } diff --git a/protobuf/tests/MapsTest.java b/protobuf/tests/MapsTest.java index 875fb0214d..c7d0e78c3b 100644 --- a/protobuf/tests/MapsTest.java +++ b/protobuf/tests/MapsTest.java @@ -13,6 +13,7 @@ */ import com.google.j2objc.annotations.AutoreleasePool; +import com.google.protobuf.ByteString; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor.Type; @@ -23,13 +24,25 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import protos.FakeScalarBytesMap; +import protos.FakeScalarBytesMapFieldEntry; +import protos.FakeScalarEnumMap; +import protos.FakeScalarEnumMapFieldEntry; +import protos.FakeScalarMsgMap; +import protos.FakeScalarMsgMapFieldEntry; +import protos.FakeStringStringMap; +import protos.FakeStringStringMapFieldEntry; import protos.MapMsg; import protos.MapMsgOrBuilder; import protos.MapValue; - -/** - * Tests for correct serialization and deserialization of map fields. - */ +import protos.RandomMessage; +import protos.RealScalarBytesMap; +import protos.RealScalarEnumMap; +import protos.RealScalarMsgMap; +import protos.RealStringBytesMap; +import protos.RealStringStringMap; + +/** Tests for correct serialization and deserialization of map fields. */ public class MapsTest extends ProtobufTest { @AutoreleasePool @@ -272,6 +285,119 @@ public void testEquals() throws Exception { assertEquals(msg1.hashCode(), msg2.hashCode()); } + public void testStringStringRepeatedFieldsToMapConversions() throws Exception { + // According to https://protobuf.dev/programming-guides/proto3/#backwards repeated fields are + // converted to maps by using the first field as the key and the second field as the value. + // This also verifies that we can parse maps with missing fields by using default values. + FakeStringStringMap fakeMap = + FakeStringStringMap.newBuilder() + .addMapField(FakeStringStringMapFieldEntry.newBuilder().setKey("duck").build()) + .addMapField(FakeStringStringMapFieldEntry.newBuilder().setValue("quack").build()) + .addMapField( + FakeStringStringMapFieldEntry.newBuilder().setKey("cat").setValue("meow").build()) + .build(); + byte[] bytes = fakeMap.toByteArray(); + RealStringStringMap realMap = RealStringStringMap.parseFrom(bytes); + assertEquals("", realMap.getMapFieldOrThrow("duck")); + assertEquals("quack", realMap.getMapFieldOrThrow("")); + assertEquals("meow", realMap.getMapFieldOrThrow("cat")); + } + + public void testScalarBytesRepeatedFieldsToMapConversions() throws Exception { + // According to https://protobuf.dev/programming-guides/proto3/#backwards repeated fields are + // converted to maps by using the first field as the key and the second field as the value. + // This also verifies that we can parse maps with missing fields by using default values. + FakeScalarBytesMap fakeScalarBytesMap = + FakeScalarBytesMap.newBuilder() + .addMapField(FakeScalarBytesMapFieldEntry.newBuilder().setKey(42).build()) + .addMapField( + FakeScalarBytesMapFieldEntry.newBuilder().setValue(ByteString.EMPTY).build()) + .build(); + byte[] bytes = fakeScalarBytesMap.toByteArray(); + RealScalarBytesMap realScalarBytesMap = RealScalarBytesMap.parseFrom(bytes); + assertEquals(ByteString.EMPTY, realScalarBytesMap.getMapFieldOrThrow(42)); + assertEquals(ByteString.EMPTY, realScalarBytesMap.getMapFieldOrThrow(0)); + } + + public void testScalarMsgRepeatedFieldsToMapConversions() throws Exception { + // According to https://protobuf.dev/programming-guides/proto3/#backwards repeated fields are + // converted to maps by using the first field as the key and the second field as the value. + // This also verifies that we can parse maps with missing fields by using default values. + FakeScalarMsgMap fakeScalarMsgMap = + FakeScalarMsgMap.newBuilder() + .addMapField(FakeScalarMsgMapFieldEntry.newBuilder().setKey(777).build()) + .addMapField( + FakeScalarMsgMapFieldEntry.newBuilder() + .setValue(RandomMessage.getDefaultInstance()) + .build()) + .build(); + byte[] bytes = fakeScalarMsgMap.toByteArray(); + RealScalarMsgMap realScalarMsgMap = RealScalarMsgMap.parseFrom(bytes); + assertEquals(RandomMessage.getDefaultInstance(), realScalarMsgMap.getMapFieldOrThrow(777)); + assertEquals(RandomMessage.getDefaultInstance(), realScalarMsgMap.getMapFieldOrThrow(0)); + } + + public void testScalarEnumRepeatedFieldsToMapConversions() throws Exception { + // According to https://protobuf.dev/programming-guides/proto3/#backwards repeated fields are + // converted to maps by using the first field as the key and the second field as the value. + // This also verifies that we can parse maps with missing fields by using default values. + // Note that the default value for an enum is the first enum value. + FakeScalarEnumMap fakeScalarEnumMap = + FakeScalarEnumMap.newBuilder() + .addMapField(FakeScalarEnumMapFieldEntry.newBuilder().setKey(123).build()) + .addMapField( + FakeScalarEnumMapFieldEntry.newBuilder() + .setKey(456) + .setValue(MapMsg.Color.YELLOW) + .build()) + .build(); + byte[] bytes = fakeScalarEnumMap.toByteArray(); + RealScalarEnumMap realScalarEnumMap = RealScalarEnumMap.parseFrom(bytes); + assertEquals(MapMsg.Color.GREEN, realScalarEnumMap.getMapFieldOrThrow(123)); + assertEquals(MapMsg.Color.YELLOW, realScalarEnumMap.getMapFieldOrThrow(456)); + } + + public void testBadValueType() throws Exception { + // Verifies that maps fail to parse if the value type is not the expected type. + FakeScalarBytesMap fakeMap = + FakeScalarBytesMap.newBuilder() + .addMapField( + FakeScalarBytesMapFieldEntry.newBuilder() + .setKey(7) + .setValue(ByteString.copyFromUtf8("hello")) + .build()) + .build(); + byte[] bytes = fakeMap.toByteArray(); + try { + RealScalarMsgMap realMap = RealScalarMsgMap.parseFrom(bytes); + fail("Expected exception instead of map: " + realMap); + } catch (Exception e) { + // Expected. + } + } + + // This test is disabled because the java runtime doesn't throw an exception when the key type is + // not the expected type. Instead, it has undefined behavior with regards to what map you actually + // get. The j2objc runtime throws an exception. + public void disabledTestBadKeyType() throws Exception { + // Verifies that maps fail to parse if the key type is not the expected type. + FakeScalarBytesMap fakeMap = + FakeScalarBytesMap.newBuilder() + .addMapField( + FakeScalarBytesMapFieldEntry.newBuilder() + .setKey(7) + .setValue(ByteString.copyFromUtf8("hello")) + .build()) + .build(); + byte[] bytes = fakeMap.toByteArray(); + try { + RealStringBytesMap realMap = RealStringBytesMap.parseFrom(bytes); + fail("Expected exception instead of map: " + realMap); + } catch (Exception e) { + // Expected. + } + } + public void testToString() throws Exception { String result = getFilledMessage().toString(); assertTrue(result.contains("int_int")); diff --git a/protobuf/tests/Proto3EnumTest.java b/protobuf/tests/Proto3EnumTest.java index acae257070..26341a6177 100644 --- a/protobuf/tests/Proto3EnumTest.java +++ b/protobuf/tests/Proto3EnumTest.java @@ -101,4 +101,41 @@ public void testNegativeEnumNumber() throws Exception { Text text = Text.parseFrom(new byte[] {0x08, 0x7f}, ExtensionRegistry.getEmptyRegistry()); assertThat(text.getGreeting()).isSameInstanceAs(Greetings.UNRECOGNIZED); } + + public void testSingularParseUnknownEnumSerialization() throws Exception { + // field 1 (fruit), value 5 (unrecognized) + // Tag: (1 << 3) | 0 = 8 + // Value: 5 + byte[] bytes = new byte[] {0x08, 0x05}; + FruitBox box = FruitBox.parseFrom(bytes, ExtensionRegistry.getEmptyRegistry()); + + byte[] outputBytes = box.toByteArray(); + assertThat(outputBytes).isEqualTo(bytes); + } + + public void testRepeatedParseUnknownEnumSerialization() throws Exception { + // field 2 (fruits), repeated, packed + // Tag: (2 << 3) | 2 = 18 + // Length: 3 + // Values: 1 (APPLE), 2 (BANANA), 5 (unrecognized) + byte[] bytes = new byte[] {0x12, 0x03, 0x01, 0x02, 0x05}; + FruitBox box = FruitBox.parseFrom(bytes, ExtensionRegistry.getEmptyRegistry()); + + byte[] outputBytes = box.toByteArray(); + assertThat(outputBytes).isEqualTo(bytes); + } + + public void testMapParseUnknownEnumSerialization() throws Exception { + // field 3 (fruit_map), map + // Tag: (3 << 3) | 2 = 26 + // Length: 4 + // Map Entry: + // key: field 1, value 1 -> 0x08 0x01 + // value: field 2, value 5 -> 0x10 0x05 + byte[] bytes = new byte[] {0x1a, 0x04, 0x08, 0x01, 0x10, 0x05}; + FruitBox box = FruitBox.parseFrom(bytes, ExtensionRegistry.getEmptyRegistry()); + + byte[] outputBytes = box.toByteArray(); + assertThat(outputBytes).isEqualTo(bytes); + } } diff --git a/protobuf/tests/protos/map_fields.proto b/protobuf/tests/protos/map_fields.proto index 8b8ade3f64..a8386ba6d3 100644 --- a/protobuf/tests/protos/map_fields.proto +++ b/protobuf/tests/protos/map_fields.proto @@ -38,7 +38,71 @@ message MapMsg { } message MapValue { - string foo = 1 [ - features.field_presence = LEGACY_REQUIRED - ]; + string foo = 1 [features.field_presence = LEGACY_REQUIRED]; +} + +// Verifying conversion from RepeatedFields to Maps for string, string. +message FakeStringStringMapFieldEntry { + string key = 1; + string value = 2; +} + +message FakeStringStringMap { + repeated FakeStringStringMapFieldEntry map_field = 1; +} + +message RealStringStringMap { + map map_field = 1; +} + +// Verifying conversion from RepeatedFields to Maps for scalar, bytes. +message FakeScalarBytesMapFieldEntry { + int32 key = 1; + bytes value = 2; +} + +message FakeScalarBytesMap { + repeated FakeScalarBytesMapFieldEntry map_field = 1; +} + +message RealScalarBytesMap { + map map_field = 1; +} + +// Verifying conversion from RepeatedFields to Maps for scalar, msg. +message RandomMessage { + string foo = 1; +} + +message FakeScalarMsgMapFieldEntry { + int32 key = 1; + RandomMessage value = 2; +} + +message FakeScalarMsgMap { + repeated FakeScalarMsgMapFieldEntry map_field = 1; +} + +message RealScalarMsgMap { + map map_field = 1; +} + +// Verifying conversion from RepeatedFields to Maps for scalar, enum. +// Specifically making sure the default value is used when the value is +// missing. +message FakeScalarEnumMapFieldEntry { + int32 key = 1; + MapMsg.Color value = 2; +} + +message FakeScalarEnumMap { + repeated FakeScalarEnumMapFieldEntry map_field = 1; +} + +message RealScalarEnumMap { + map map_field = 1; +} + +message RealStringBytesMap { + map map_field = 1; } diff --git a/protobuf/tests/protos/proto3_enum.proto b/protobuf/tests/protos/proto3_enum.proto index 663c761f72..12a1f1d542 100644 --- a/protobuf/tests/protos/proto3_enum.proto +++ b/protobuf/tests/protos/proto3_enum.proto @@ -27,6 +27,8 @@ enum Fruit { message FruitBox { Fruit fruit = 1; + repeated Fruit fruits = 2; + map fruit_map = 3; } enum Greetings {