From 1dcc329427fd103a0abd96ab787270f5d0a31861 Mon Sep 17 00:00:00 2001 From: Thomas Van Lenten Date: Thu, 21 May 2015 17:14:52 -0400 Subject: Objective C Second Alpha Drop - Style fixups in the code. - map<> serialization fixes and more tests. - Autocreation of map<> fields (to match repeated fields). - @@protoc_insertion_point(global_scope|imports). - Fixup proto2 syntax extension support. - Move all startup code to +initialize so it happen on class usage and not app startup. - Have generated headers use forward declarations and move imports into generated code, reduces what is need at compile time to speed up compiled and avoid pointless rippling of rebuilds. --- objectivec/GPBMessage.m | 867 ++++++++++++++++++++++++++++++------------------ 1 file changed, 536 insertions(+), 331 deletions(-) (limited to 'objectivec/GPBMessage.m') diff --git a/objectivec/GPBMessage.m b/objectivec/GPBMessage.m index 63ffc3bc..bd3235f1 100644 --- a/objectivec/GPBMessage.m +++ b/objectivec/GPBMessage.m @@ -39,15 +39,21 @@ #import "GPBDescriptor_PackagePrivate.h" #import "GPBDictionary_PackagePrivate.h" #import "GPBExtensionField_PackagePrivate.h" -#import "GPBExtensionRegistry_PackagePrivate.h" +#import "GPBExtensionRegistry.h" +#import "GPBRootObject_PackagePrivate.h" #import "GPBUnknownFieldSet_PackagePrivate.h" #import "GPBUtilities_PackagePrivate.h" +NSString *const GPBMessageErrorDomain = + GPBNSStringifySymbol(GPBMessageErrorDomain); + #ifdef DEBUG NSString *const GPBExceptionMessageKey = GPBNSStringifySymbol(GPBExceptionMessage); #endif // DEBUG +static NSString *const kGPBDataCoderKey = @"GPBData"; + // // PLEASE REMEMBER: // @@ -78,13 +84,32 @@ static id GetOrCreateArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field, GPBFileSyntax syntax); static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field); +static id CreateMapForField(GPBFieldDescriptor *field, + GPBMessage *autocreator) + __attribute__((ns_returns_retained)); static id GetOrCreateMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field, GPBFileSyntax syntax); +static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field); static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap, NSZone *zone) __attribute__((ns_returns_retained)); +static NSError *MessageError(NSInteger code, NSDictionary *userInfo) { + return [NSError errorWithDomain:GPBMessageErrorDomain + code:code + userInfo:userInfo]; +} + +static NSError *MessageErrorWithReason(NSInteger code, NSString *reason) { + NSDictionary *userInfo = nil; + if ([reason length]) { + userInfo = @{ @"Reason" : reason }; + } + return MessageError(code, userInfo); +} + + static void CheckExtension(GPBMessage *self, GPBExtensionField *extension) { if ([[self class] descriptor] != [extension containingType]) { [NSException @@ -201,6 +226,303 @@ static id CreateArrayForField(GPBFieldDescriptor *field, return result; } +static id CreateMapForField(GPBFieldDescriptor *field, + GPBMessage *autocreator) { + id result; + GPBType keyType = field.mapKeyType; + GPBType valueType = GPBGetFieldType(field); + switch (keyType) { + case GPBTypeBool: + switch (valueType) { + case GPBTypeBool: + result = [[GPBBoolBoolDictionary alloc] init]; + break; + case GPBTypeFixed32: + case GPBTypeUInt32: + result = [[GPBBoolUInt32Dictionary alloc] init]; + break; + case GPBTypeInt32: + case GPBTypeSFixed32: + case GPBTypeSInt32: + result = [[GPBBoolInt32Dictionary alloc] init]; + break; + case GPBTypeFixed64: + case GPBTypeUInt64: + result = [[GPBBoolUInt64Dictionary alloc] init]; + break; + case GPBTypeInt64: + case GPBTypeSFixed64: + case GPBTypeSInt64: + result = [[GPBBoolInt64Dictionary alloc] init]; + break; + case GPBTypeFloat: + result = [[GPBBoolFloatDictionary alloc] init]; + break; + case GPBTypeDouble: + result = [[GPBBoolDoubleDictionary alloc] init]; + break; + case GPBTypeEnum: + result = [[GPBBoolEnumDictionary alloc] + initWithValidationFunction:field.enumDescriptor.enumVerifier]; + break; + case GPBTypeData: + case GPBTypeMessage: + case GPBTypeString: + result = [[GPBBoolObjectDictionary alloc] init]; + break; + case GPBTypeGroup: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + break; + case GPBTypeFixed32: + case GPBTypeUInt32: + switch (valueType) { + case GPBTypeBool: + result = [[GPBUInt32BoolDictionary alloc] init]; + break; + case GPBTypeFixed32: + case GPBTypeUInt32: + result = [[GPBUInt32UInt32Dictionary alloc] init]; + break; + case GPBTypeInt32: + case GPBTypeSFixed32: + case GPBTypeSInt32: + result = [[GPBUInt32Int32Dictionary alloc] init]; + break; + case GPBTypeFixed64: + case GPBTypeUInt64: + result = [[GPBUInt32UInt64Dictionary alloc] init]; + break; + case GPBTypeInt64: + case GPBTypeSFixed64: + case GPBTypeSInt64: + result = [[GPBUInt32Int64Dictionary alloc] init]; + break; + case GPBTypeFloat: + result = [[GPBUInt32FloatDictionary alloc] init]; + break; + case GPBTypeDouble: + result = [[GPBUInt32DoubleDictionary alloc] init]; + break; + case GPBTypeEnum: + result = [[GPBUInt32EnumDictionary alloc] + initWithValidationFunction:field.enumDescriptor.enumVerifier]; + break; + case GPBTypeData: + case GPBTypeMessage: + case GPBTypeString: + result = [[GPBUInt32ObjectDictionary alloc] init]; + break; + case GPBTypeGroup: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + break; + case GPBTypeInt32: + case GPBTypeSFixed32: + case GPBTypeSInt32: + switch (valueType) { + case GPBTypeBool: + result = [[GPBInt32BoolDictionary alloc] init]; + break; + case GPBTypeFixed32: + case GPBTypeUInt32: + result = [[GPBInt32UInt32Dictionary alloc] init]; + break; + case GPBTypeInt32: + case GPBTypeSFixed32: + case GPBTypeSInt32: + result = [[GPBInt32Int32Dictionary alloc] init]; + break; + case GPBTypeFixed64: + case GPBTypeUInt64: + result = [[GPBInt32UInt64Dictionary alloc] init]; + break; + case GPBTypeInt64: + case GPBTypeSFixed64: + case GPBTypeSInt64: + result = [[GPBInt32Int64Dictionary alloc] init]; + break; + case GPBTypeFloat: + result = [[GPBInt32FloatDictionary alloc] init]; + break; + case GPBTypeDouble: + result = [[GPBInt32DoubleDictionary alloc] init]; + break; + case GPBTypeEnum: + result = [[GPBInt32EnumDictionary alloc] + initWithValidationFunction:field.enumDescriptor.enumVerifier]; + break; + case GPBTypeData: + case GPBTypeMessage: + case GPBTypeString: + result = [[GPBInt32ObjectDictionary alloc] init]; + break; + case GPBTypeGroup: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + break; + case GPBTypeFixed64: + case GPBTypeUInt64: + switch (valueType) { + case GPBTypeBool: + result = [[GPBUInt64BoolDictionary alloc] init]; + break; + case GPBTypeFixed32: + case GPBTypeUInt32: + result = [[GPBUInt64UInt32Dictionary alloc] init]; + break; + case GPBTypeInt32: + case GPBTypeSFixed32: + case GPBTypeSInt32: + result = [[GPBUInt64Int32Dictionary alloc] init]; + break; + case GPBTypeFixed64: + case GPBTypeUInt64: + result = [[GPBUInt64UInt64Dictionary alloc] init]; + break; + case GPBTypeInt64: + case GPBTypeSFixed64: + case GPBTypeSInt64: + result = [[GPBUInt64Int64Dictionary alloc] init]; + break; + case GPBTypeFloat: + result = [[GPBUInt64FloatDictionary alloc] init]; + break; + case GPBTypeDouble: + result = [[GPBUInt64DoubleDictionary alloc] init]; + break; + case GPBTypeEnum: + result = [[GPBUInt64EnumDictionary alloc] + initWithValidationFunction:field.enumDescriptor.enumVerifier]; + break; + case GPBTypeData: + case GPBTypeMessage: + case GPBTypeString: + result = [[GPBUInt64ObjectDictionary alloc] init]; + break; + case GPBTypeGroup: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + break; + case GPBTypeInt64: + case GPBTypeSFixed64: + case GPBTypeSInt64: + switch (valueType) { + case GPBTypeBool: + result = [[GPBInt64BoolDictionary alloc] init]; + break; + case GPBTypeFixed32: + case GPBTypeUInt32: + result = [[GPBInt64UInt32Dictionary alloc] init]; + break; + case GPBTypeInt32: + case GPBTypeSFixed32: + case GPBTypeSInt32: + result = [[GPBInt64Int32Dictionary alloc] init]; + break; + case GPBTypeFixed64: + case GPBTypeUInt64: + result = [[GPBInt64UInt64Dictionary alloc] init]; + break; + case GPBTypeInt64: + case GPBTypeSFixed64: + case GPBTypeSInt64: + result = [[GPBInt64Int64Dictionary alloc] init]; + break; + case GPBTypeFloat: + result = [[GPBInt64FloatDictionary alloc] init]; + break; + case GPBTypeDouble: + result = [[GPBInt64DoubleDictionary alloc] init]; + break; + case GPBTypeEnum: + result = [[GPBInt64EnumDictionary alloc] + initWithValidationFunction:field.enumDescriptor.enumVerifier]; + break; + case GPBTypeData: + case GPBTypeMessage: + case GPBTypeString: + result = [[GPBInt64ObjectDictionary alloc] init]; + break; + case GPBTypeGroup: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + break; + case GPBTypeString: + switch (valueType) { + case GPBTypeBool: + result = [[GPBStringBoolDictionary alloc] init]; + break; + case GPBTypeFixed32: + case GPBTypeUInt32: + result = [[GPBStringUInt32Dictionary alloc] init]; + break; + case GPBTypeInt32: + case GPBTypeSFixed32: + case GPBTypeSInt32: + result = [[GPBStringInt32Dictionary alloc] init]; + break; + case GPBTypeFixed64: + case GPBTypeUInt64: + result = [[GPBStringUInt64Dictionary alloc] init]; + break; + case GPBTypeInt64: + case GPBTypeSFixed64: + case GPBTypeSInt64: + result = [[GPBStringInt64Dictionary alloc] init]; + break; + case GPBTypeFloat: + result = [[GPBStringFloatDictionary alloc] init]; + break; + case GPBTypeDouble: + result = [[GPBStringDoubleDictionary alloc] init]; + break; + case GPBTypeEnum: + result = [[GPBStringEnumDictionary alloc] + initWithValidationFunction:field.enumDescriptor.enumVerifier]; + break; + case GPBTypeData: + case GPBTypeMessage: + case GPBTypeString: + if (autocreator) { + result = [[GPBAutocreatedDictionary alloc] init]; + } else { + result = [[NSMutableDictionary alloc] init]; + } + break; + case GPBTypeGroup: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + break; + + case GPBTypeFloat: + case GPBTypeDouble: + case GPBTypeEnum: + case GPBTypeData: + case GPBTypeGroup: + case GPBTypeMessage: + NSCAssert(NO, @"shouldn't happen"); + return nil; + } + + if (autocreator) { + if ((keyType == GPBTypeString) && GPBTypeIsObject(valueType)) { + GPBAutocreatedDictionary *autoDict = result; + autoDict->_autocreator = autocreator; + } else { + GPBInt32Int32Dictionary *gpbDict = result; + gpbDict->_autocreator = autocreator; + } + } + + return result; +} #if !defined(__clang_analyzer__) // These functions are blocked from the analyzer because the analyzer sees the @@ -249,285 +571,27 @@ static id GetOrCreateMapIvarWithField(GPBMessage *self, GPBFileSyntax syntax) { id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); if (!dict) { - GPBType keyType = field.mapKeyType; - GPBType valueType = GPBGetFieldType(field); - switch (keyType) { - case GPBTypeBool: - switch (valueType) { - case GPBTypeBool: - dict = [[GPBBoolBoolDictionary alloc] init]; - break; - case GPBTypeFixed32: - case GPBTypeUInt32: - dict = [[GPBBoolUInt32Dictionary alloc] init]; - break; - case GPBTypeInt32: - case GPBTypeSFixed32: - case GPBTypeSInt32: - dict = [[GPBBoolInt32Dictionary alloc] init]; - break; - case GPBTypeFixed64: - case GPBTypeUInt64: - dict = [[GPBBoolUInt64Dictionary alloc] init]; - break; - case GPBTypeInt64: - case GPBTypeSFixed64: - case GPBTypeSInt64: - dict = [[GPBBoolInt64Dictionary alloc] init]; - break; - case GPBTypeFloat: - dict = [[GPBBoolFloatDictionary alloc] init]; - break; - case GPBTypeDouble: - dict = [[GPBBoolDoubleDictionary alloc] init]; - break; - case GPBTypeEnum: - dict = [[GPBBoolEnumDictionary alloc] - initWithValidationFunction:field.enumDescriptor.enumVerifier]; - break; - case GPBTypeData: - case GPBTypeMessage: - case GPBTypeString: - dict = [[GPBBoolObjectDictionary alloc] init]; - break; - case GPBTypeGroup: - NSCAssert(NO, @"shouldn't happen"); - return nil; - } - break; - case GPBTypeFixed32: - case GPBTypeUInt32: - switch (valueType) { - case GPBTypeBool: - dict = [[GPBUInt32BoolDictionary alloc] init]; - break; - case GPBTypeFixed32: - case GPBTypeUInt32: - dict = [[GPBUInt32UInt32Dictionary alloc] init]; - break; - case GPBTypeInt32: - case GPBTypeSFixed32: - case GPBTypeSInt32: - dict = [[GPBUInt32Int32Dictionary alloc] init]; - break; - case GPBTypeFixed64: - case GPBTypeUInt64: - dict = [[GPBUInt32UInt64Dictionary alloc] init]; - break; - case GPBTypeInt64: - case GPBTypeSFixed64: - case GPBTypeSInt64: - dict = [[GPBUInt32Int64Dictionary alloc] init]; - break; - case GPBTypeFloat: - dict = [[GPBUInt32FloatDictionary alloc] init]; - break; - case GPBTypeDouble: - dict = [[GPBUInt32DoubleDictionary alloc] init]; - break; - case GPBTypeEnum: - dict = [[GPBUInt32EnumDictionary alloc] - initWithValidationFunction:field.enumDescriptor.enumVerifier]; - break; - case GPBTypeData: - case GPBTypeMessage: - case GPBTypeString: - dict = [[GPBUInt32ObjectDictionary alloc] init]; - break; - case GPBTypeGroup: - NSCAssert(NO, @"shouldn't happen"); - return nil; - } - break; - case GPBTypeInt32: - case GPBTypeSFixed32: - case GPBTypeSInt32: - switch (valueType) { - case GPBTypeBool: - dict = [[GPBInt32BoolDictionary alloc] init]; - break; - case GPBTypeFixed32: - case GPBTypeUInt32: - dict = [[GPBInt32UInt32Dictionary alloc] init]; - break; - case GPBTypeInt32: - case GPBTypeSFixed32: - case GPBTypeSInt32: - dict = [[GPBInt32Int32Dictionary alloc] init]; - break; - case GPBTypeFixed64: - case GPBTypeUInt64: - dict = [[GPBInt32UInt64Dictionary alloc] init]; - break; - case GPBTypeInt64: - case GPBTypeSFixed64: - case GPBTypeSInt64: - dict = [[GPBInt32Int64Dictionary alloc] init]; - break; - case GPBTypeFloat: - dict = [[GPBInt32FloatDictionary alloc] init]; - break; - case GPBTypeDouble: - dict = [[GPBInt32DoubleDictionary alloc] init]; - break; - case GPBTypeEnum: - dict = [[GPBInt32EnumDictionary alloc] - initWithValidationFunction:field.enumDescriptor.enumVerifier]; - break; - case GPBTypeData: - case GPBTypeMessage: - case GPBTypeString: - dict = [[GPBInt32ObjectDictionary alloc] init]; - break; - case GPBTypeGroup: - NSCAssert(NO, @"shouldn't happen"); - return nil; - } - break; - case GPBTypeFixed64: - case GPBTypeUInt64: - switch (valueType) { - case GPBTypeBool: - dict = [[GPBUInt64BoolDictionary alloc] init]; - break; - case GPBTypeFixed32: - case GPBTypeUInt32: - dict = [[GPBUInt64UInt32Dictionary alloc] init]; - break; - case GPBTypeInt32: - case GPBTypeSFixed32: - case GPBTypeSInt32: - dict = [[GPBUInt64Int32Dictionary alloc] init]; - break; - case GPBTypeFixed64: - case GPBTypeUInt64: - dict = [[GPBUInt64UInt64Dictionary alloc] init]; - break; - case GPBTypeInt64: - case GPBTypeSFixed64: - case GPBTypeSInt64: - dict = [[GPBUInt64Int64Dictionary alloc] init]; - break; - case GPBTypeFloat: - dict = [[GPBUInt64FloatDictionary alloc] init]; - break; - case GPBTypeDouble: - dict = [[GPBUInt64DoubleDictionary alloc] init]; - break; - case GPBTypeEnum: - dict = [[GPBUInt64EnumDictionary alloc] - initWithValidationFunction:field.enumDescriptor.enumVerifier]; - break; - case GPBTypeData: - case GPBTypeMessage: - case GPBTypeString: - dict = [[GPBUInt64ObjectDictionary alloc] init]; - break; - case GPBTypeGroup: - NSCAssert(NO, @"shouldn't happen"); - return nil; - } - break; - case GPBTypeInt64: - case GPBTypeSFixed64: - case GPBTypeSInt64: - switch (valueType) { - case GPBTypeBool: - dict = [[GPBInt64BoolDictionary alloc] init]; - break; - case GPBTypeFixed32: - case GPBTypeUInt32: - dict = [[GPBInt64UInt32Dictionary alloc] init]; - break; - case GPBTypeInt32: - case GPBTypeSFixed32: - case GPBTypeSInt32: - dict = [[GPBInt64Int32Dictionary alloc] init]; - break; - case GPBTypeFixed64: - case GPBTypeUInt64: - dict = [[GPBInt64UInt64Dictionary alloc] init]; - break; - case GPBTypeInt64: - case GPBTypeSFixed64: - case GPBTypeSInt64: - dict = [[GPBInt64Int64Dictionary alloc] init]; - break; - case GPBTypeFloat: - dict = [[GPBInt64FloatDictionary alloc] init]; - break; - case GPBTypeDouble: - dict = [[GPBInt64DoubleDictionary alloc] init]; - break; - case GPBTypeEnum: - dict = [[GPBInt64EnumDictionary alloc] - initWithValidationFunction:field.enumDescriptor.enumVerifier]; - break; - case GPBTypeData: - case GPBTypeMessage: - case GPBTypeString: - dict = [[GPBInt64ObjectDictionary alloc] init]; - break; - case GPBTypeGroup: - NSCAssert(NO, @"shouldn't happen"); - return nil; - } - break; - case GPBTypeString: - switch (valueType) { - case GPBTypeBool: - dict = [[GPBStringBoolDictionary alloc] init]; - break; - case GPBTypeFixed32: - case GPBTypeUInt32: - dict = [[GPBStringUInt32Dictionary alloc] init]; - break; - case GPBTypeInt32: - case GPBTypeSFixed32: - case GPBTypeSInt32: - dict = [[GPBStringInt32Dictionary alloc] init]; - break; - case GPBTypeFixed64: - case GPBTypeUInt64: - dict = [[GPBStringUInt64Dictionary alloc] init]; - break; - case GPBTypeInt64: - case GPBTypeSFixed64: - case GPBTypeSInt64: - dict = [[GPBStringInt64Dictionary alloc] init]; - break; - case GPBTypeFloat: - dict = [[GPBStringFloatDictionary alloc] init]; - break; - case GPBTypeDouble: - dict = [[GPBStringDoubleDictionary alloc] init]; - break; - case GPBTypeEnum: - dict = [[GPBStringEnumDictionary alloc] - initWithValidationFunction:field.enumDescriptor.enumVerifier]; - break; - case GPBTypeData: - case GPBTypeMessage: - case GPBTypeString: - dict = [[NSMutableDictionary alloc] init]; - break; - case GPBTypeGroup: - NSCAssert(NO, @"shouldn't happen"); - return nil; - } - break; + // No lock needed, this is called from places expecting to mutate + // so no threading protection is needed. + dict = CreateMapForField(field, nil); + GPBSetRetainedObjectIvarWithFieldInternal(self, field, dict, syntax); + } + return dict; +} - case GPBTypeFloat: - case GPBTypeDouble: - case GPBTypeEnum: - case GPBTypeData: - case GPBTypeGroup: - case GPBTypeMessage: - NSCAssert(NO, @"shouldn't happen"); - return nil; +// This is like GPBGetObjectIvarWithField(), but for maps, it should +// only be used to wire the method into the class. +static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) { + id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (!dict) { + // Check again after getting the lock. + OSSpinLockLock(&self->readOnlyMutex_); + dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (!dict) { + dict = CreateMapForField(field, self); + GPBSetAutocreatedRetainedObjectIvarWithField(self, field, dict); } - - GPBSetRetainedObjectIvarWithFieldInternal(self, field, dict, syntax); + OSSpinLockUnlock(&self->readOnlyMutex_); } return dict; } @@ -595,7 +659,30 @@ void GPBAutocreatedArrayModified(GPBMessage *self, id array) { } } } - NSCAssert(NO, @"Unknown array."); + NSCAssert(NO, @"Unknown autocreated %@ for %@.", [array class], self); +} + +void GPBAutocreatedDictionaryModified(GPBMessage *self, id dictionary) { + // When one of our autocreated dicts adds elements, make it visible. + GPBDescriptor *descriptor = [[self class] descriptor]; + for (GPBFieldDescriptor *field in descriptor->fields_) { + if (field.fieldType == GPBFieldTypeMap) { + id curDict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); + if (curDict == dictionary) { + if ((field.mapKeyType == GPBTypeString) && + GPBFieldTypeIsObject(field)) { + GPBAutocreatedDictionary *autoDict = dictionary; + autoDict->_autocreator = nil; + } else { + GPBInt32Int32Dictionary *gpbDict = dictionary; + gpbDict->_autocreator = nil; + } + GPBBecomeVisibleToAutocreator(self); + return; + } + } + } + NSCAssert(NO, @"Unknown autocreated %@ for %@.", [dictionary class], self); } void GPBClearMessageAutocreator(GPBMessage *self) { @@ -617,7 +704,8 @@ void GPBClearMessageAutocreator(GPBMessage *self) { : [self->autocreator_->autocreatedExtensionMap_ objectForKey:self->autocreatorExtension_]); NSCAssert(autocreatorHas || autocreatorFieldValue != self, - @"Cannot clear autocreator because it still refers to self."); + @"Cannot clear autocreator because it still refers to self, self: %@.", + self); #endif // DEBUG && !defined(NS_BLOCK_ASSERTIONS) @@ -636,26 +724,6 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { return self->unknownFields_; } -#ifdef DEBUG -static void DebugRaiseExceptionIfNotInitialized(GPBMessage *message) { - if (!message.initialized) { - NSString *reason = - [NSString stringWithFormat:@"Uninitialized Message %@", message]; - NSDictionary *userInfo = - message ? @{GPBExceptionMessageKey : message} : nil; - NSException *exception = - [NSException exceptionWithName:NSInternalInconsistencyException - reason:reason - userInfo:userInfo]; - [exception raise]; - } -} -#else -GPB_INLINE void DebugRaiseExceptionIfNotInitialized(GPBMessage *message) { -#pragma unused(message) -} -#endif // DEBUG - @implementation GPBMessage + (void)initialize { @@ -663,14 +731,20 @@ GPB_INLINE void DebugRaiseExceptionIfNotInitialized(GPBMessage *message) { if ([self class] == pbMessageClass) { // This is here to start up the "base" class descriptor. [self descriptor]; + // Message shares extension method resolving with GPBRootObject so insure + // it is started up at the same time. + (void)[GPBRootObject class]; } else if ([self superclass] == pbMessageClass) { // This is here to start up all the "message" subclasses. Just needs to be // done for the messages, not any of the subclasses. // This must be done in initialize to enforce thread safety of start up of - // the protocol buffer library. All of the extension registries must be - // created in either "+load" or "+initialize". + // the protocol buffer library. + // Note: The generated code for -descriptor calls + // +[GPBDescriptor allocDescriptorForClass:...], passing the GPBRootObject + // subclass for the file. That call chain is what ensures that *Root class + // is started up to support extension resolution off the message class + // (+resolveClassMethod: below) in a thread safe manner. [self descriptor]; - [self extensionRegistry]; } } @@ -728,25 +802,63 @@ GPB_INLINE void DebugRaiseExceptionIfNotInitialized(GPBMessage *message) { return self; } -- (instancetype)initWithData:(NSData *)data { - return [self initWithData:data extensionRegistry:nil]; +- (instancetype)initWithData:(NSData *)data error:(NSError **)errorPtr { + return [self initWithData:data extensionRegistry:nil error:errorPtr]; } - (instancetype)initWithData:(NSData *)data - extensionRegistry:(GPBExtensionRegistry *)extensionRegistry { + extensionRegistry:(GPBExtensionRegistry *)extensionRegistry + error:(NSError **)errorPtr { if ((self = [self init])) { - [self mergeFromData:data extensionRegistry:extensionRegistry]; - DebugRaiseExceptionIfNotInitialized(self); + @try { + [self mergeFromData:data extensionRegistry:extensionRegistry]; + } + @catch (NSException *exception) { + [self release]; + self = nil; + if (errorPtr) { + *errorPtr = MessageErrorWithReason(GPBMessageErrorCodeMalformedData, + exception.reason); + } + } +#ifdef DEBUG + if (self && !self.initialized) { + [self release]; + self = nil; + if (errorPtr) { + *errorPtr = MessageError(GPBMessageErrorCodeMissingRequiredField, nil); + } + } +#endif } return self; } - (instancetype)initWithCodedInputStream:(GPBCodedInputStream *)input extensionRegistry: - (GPBExtensionRegistry *)extensionRegistry { + (GPBExtensionRegistry *)extensionRegistry + error:(NSError **)errorPtr { if ((self = [self init])) { - [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry]; - DebugRaiseExceptionIfNotInitialized(self); + @try { + [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry]; + } + @catch (NSException *exception) { + [self release]; + self = nil; + if (errorPtr) { + *errorPtr = MessageErrorWithReason(GPBMessageErrorCodeMalformedData, + exception.reason); + } + } +#ifdef DEBUG + if (self && !self.initialized) { + [self release]; + self = nil; + if (errorPtr) { + *errorPtr = MessageError(GPBMessageErrorCodeMissingRequiredField, nil); + } + } +#endif } return self; } @@ -900,6 +1012,20 @@ GPB_INLINE void DebugRaiseExceptionIfNotInitialized(GPBMessage *message) { gpbArray->_autocreator = nil; } } + } else { + if ((field.mapKeyType == GPBTypeString) && + GPBFieldTypeIsObject(field)) { + GPBAutocreatedDictionary *autoDict = arrayOrMap; + if (autoDict->_autocreator == self) { + autoDict->_autocreator = nil; + } + } else { + // Type doesn't matter, it is a GPB*Dictionary. + GPBInt32Int32Dictionary *gpbDict = arrayOrMap; + if (gpbDict->_autocreator == self) { + gpbDict->_autocreator = nil; + } + } } [arrayOrMap release]; } @@ -960,7 +1086,8 @@ GPB_INLINE void DebugRaiseExceptionIfNotInitialized(GPBMessage *message) { } } else { NSAssert(field.isOptional, - @"If not required or optional, what was it?"); + @"%@: Single message field %@ not required or optional?", + [self class], field.name); if (GPBGetHasIvarField(self, field)) { GPBMessage *message = GPBGetMessageIvarWithField(self, field); if (!message.initialized) { @@ -1025,11 +1152,27 @@ GPB_INLINE void DebugRaiseExceptionIfNotInitialized(GPBMessage *message) { } - (NSData *)data { - DebugRaiseExceptionIfNotInitialized(self); +#ifdef DEBUG + if (!self.initialized) { + return nil; + } +#endif NSMutableData *data = [NSMutableData dataWithLength:[self serializedSize]]; GPBCodedOutputStream *stream = [[GPBCodedOutputStream alloc] initWithData:data]; - [self writeToCodedOutputStream:stream]; + @try { + [self writeToCodedOutputStream:stream]; + } + @catch (NSException *exception) { + // This really shouldn't happen. The only way writeToCodedOutputStream: + // could throw is if something in the library has a bug and the + // serializedSize was wrong. +#ifdef DEBUG + NSLog(@"%@: Internal exception while building message data: %@", + [self class], exception); +#endif + data = nil; + } [stream release]; return data; } @@ -1041,7 +1184,19 @@ GPB_INLINE void DebugRaiseExceptionIfNotInitialized(GPBMessage *message) { [NSMutableData dataWithLength:(serializedSize + varintSize)]; GPBCodedOutputStream *stream = [[GPBCodedOutputStream alloc] initWithData:data]; - [self writeDelimitedToCodedOutputStream:stream]; + @try { + [self writeDelimitedToCodedOutputStream:stream]; + } + @catch (NSException *exception) { + // This really shouldn't happen. The only way writeToCodedOutputStream: + // could throw is if something in the library has a bug and the + // serializedSize was wrong. +#ifdef DEBUG + NSLog(@"%@: Internal exception while building message delimitedData: %@", + [self class], exception); +#endif + data = nil; + } [stream release]; return data; } @@ -1717,32 +1872,55 @@ GPB_INLINE void DebugRaiseExceptionIfNotInitialized(GPBMessage *message) { #pragma mark - Parse From Data Support -+ (instancetype)parseFromData:(NSData *)data { - return [self parseFromData:data extensionRegistry:nil]; ++ (instancetype)parseFromData:(NSData *)data error:(NSError **)errorPtr { + return [self parseFromData:data extensionRegistry:nil error:errorPtr]; } + (instancetype)parseFromData:(NSData *)data - extensionRegistry:(GPBExtensionRegistry *)extensionRegistry { + extensionRegistry:(GPBExtensionRegistry *)extensionRegistry + error:(NSError **)errorPtr { return [[[self alloc] initWithData:data - extensionRegistry:extensionRegistry] autorelease]; + extensionRegistry:extensionRegistry + error:errorPtr] autorelease]; } + (instancetype)parseFromCodedInputStream:(GPBCodedInputStream *)input - extensionRegistry:(GPBExtensionRegistry *)extensionRegistry { + extensionRegistry:(GPBExtensionRegistry *)extensionRegistry + error:(NSError **)errorPtr { return [[[self alloc] initWithCodedInputStream:input - extensionRegistry:extensionRegistry] autorelease]; + extensionRegistry:extensionRegistry + error:errorPtr] autorelease]; } #pragma mark - Parse Delimited From Data Support + (instancetype)parseDelimitedFromCodedInputStream:(GPBCodedInputStream *)input extensionRegistry: - (GPBExtensionRegistry *)extensionRegistry { + (GPBExtensionRegistry *)extensionRegistry + error:(NSError **)errorPtr { GPBMessage *message = [[[self alloc] init] autorelease]; - [message mergeDelimitedFromCodedInputStream:input - extensionRegistry:extensionRegistry]; - DebugRaiseExceptionIfNotInitialized(message); + @try { + [message mergeDelimitedFromCodedInputStream:input + extensionRegistry:extensionRegistry]; + } + @catch (NSException *exception) { + [message release]; + message = nil; + if (errorPtr) { + *errorPtr = MessageErrorWithReason(GPBMessageErrorCodeMalformedData, + exception.reason); + } + } +#ifdef DEBUG + if (message && !message.initialized) { + [message release]; + message = nil; + if (errorPtr) { + *errorPtr = MessageError(GPBMessageErrorCodeMissingRequiredField, nil); + } + } +#endif return message; } @@ -4661,7 +4839,9 @@ static BOOL IvarSetEnum(GPBFieldDescriptor *field, void *voidContext) { context.impToAdd = imp_implementationWithBlock(^(id obj, BOOL value) { if (value) { [NSException raise:NSInvalidArgumentException - format:@"has fields can only be set to NO"]; + format:@"%@: %@ can only be set to NO (to clear field).", + [obj class], + NSStringFromSelector(field->setHasSel_)]; } GPBClearMessageField(obj, field); }); @@ -4684,9 +4864,15 @@ static BOOL IvarSetEnum(GPBFieldDescriptor *field, void *voidContext) { } } else { if (sel == field->getSel_) { - context.impToAdd = imp_implementationWithBlock(^(id obj) { - return GetArrayIvarWithField(obj, field); - }); + if (field.fieldType == GPBFieldTypeRepeated) { + context.impToAdd = imp_implementationWithBlock(^(id obj) { + return GetArrayIvarWithField(obj, field); + }); + } else { + context.impToAdd = imp_implementationWithBlock(^(id obj) { + return GetMapIvarWithField(obj, field); + }); + } context.encodingSelector = @selector(getArray); break; } else if (sel == field->setSel_) { @@ -4711,18 +4897,37 @@ static BOOL IvarSetEnum(GPBFieldDescriptor *field, void *voidContext) { return [super resolveInstanceMethod:sel]; } ++ (BOOL)resolveClassMethod:(SEL)sel { + // Extensions scoped to a Message and looked up via class methods. + if (GPBResolveExtensionClassMethod(self, sel)) { + return YES; + } + return [super resolveClassMethod:sel]; +} + #pragma mark - NSCoding Support ++ (BOOL)supportsSecureCoding { + return YES; +} + - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [self init]; if (self) { - [self mergeFromData:[aDecoder decodeDataObject] extensionRegistry:nil]; + NSData *data = + [aDecoder decodeObjectOfClass:[NSData class] forKey:kGPBDataCoderKey]; + if (data.length) { + [self mergeFromData:data extensionRegistry:nil]; + } } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { - [aCoder encodeDataObject:[self data]]; + NSData *data = [self data]; + if (data.length) { + [aCoder encodeObject:data forKey:kGPBDataCoderKey]; + } } #pragma mark - KVC Support -- cgit v1.2.3