aboutsummaryrefslogtreecommitdiffhomepage
path: root/objectivec/GPBMessage.m
diff options
context:
space:
mode:
authorGravatar Thomas Van Lenten <thomasvl@google.com>2015-05-21 17:14:52 -0400
committerGravatar Thomas Van Lenten <thomasvl@google.com>2015-05-22 14:27:31 -0400
commit1dcc329427fd103a0abd96ab787270f5d0a31861 (patch)
treecf1c52df0e1effa3d0985a3406a71c38c3a4e487 /objectivec/GPBMessage.m
parentc3480926f98eb7c45224daae5cf0373e120b3b8d (diff)
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.
Diffstat (limited to 'objectivec/GPBMessage.m')
-rw-r--r--objectivec/GPBMessage.m867
1 files changed, 536 insertions, 331 deletions
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