aboutsummaryrefslogtreecommitdiffhomepage
path: root/objectivec/GPBMessage.m
diff options
context:
space:
mode:
Diffstat (limited to 'objectivec/GPBMessage.m')
-rw-r--r--objectivec/GPBMessage.m316
1 files changed, 220 insertions, 96 deletions
diff --git a/objectivec/GPBMessage.m b/objectivec/GPBMessage.m
index fdb695ec..db5d3b60 100644
--- a/objectivec/GPBMessage.m
+++ b/objectivec/GPBMessage.m
@@ -32,10 +32,11 @@
#import <objc/runtime.h>
#import <objc/message.h>
+#import <stdatomic.h>
#import "GPBArray_PackagePrivate.h"
#import "GPBCodedInputStream_PackagePrivate.h"
-#import "GPBCodedOutputStream.h"
+#import "GPBCodedOutputStream_PackagePrivate.h"
#import "GPBDescriptor_PackagePrivate.h"
#import "GPBDictionary_PackagePrivate.h"
#import "GPBExtensionInternals.h"
@@ -44,28 +45,19 @@
#import "GPBUnknownFieldSet_PackagePrivate.h"
#import "GPBUtilities_PackagePrivate.h"
+// Direct access is use for speed, to avoid even internally declaring things
+// read/write, etc. The warning is enabled in the project to ensure code calling
+// protos can turn on -Wdirect-ivar-access without issues.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
NSString *const GPBMessageErrorDomain =
GPBNSStringifySymbol(GPBMessageErrorDomain);
-#ifdef DEBUG
-NSString *const GPBExceptionMessageKey =
- GPBNSStringifySymbol(GPBExceptionMessage);
-#endif // DEBUG
+NSString *const GPBErrorReasonKey = @"Reason";
static NSString *const kGPBDataCoderKey = @"GPBData";
-#ifndef _GPBCompileAssert
- #if __has_feature(c_static_assert) || __has_extension(c_static_assert)
- #define _GPBCompileAssert(test, msg) _Static_assert((test), #msg)
- #else
- // Pre-Xcode 7 support.
- #define _GPBCompileAssertSymbolInner(line, msg) _GPBCompileAssert ## line ## __ ## msg
- #define _GPBCompileAssertSymbol(line, msg) _GPBCompileAssertSymbolInner(line, msg)
- #define _GPBCompileAssert(test, msg) \
- typedef char _GPBCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
- #endif // __has_feature(c_static_assert) || __has_extension(c_static_assert)
-#endif // _GPBCompileAssert
-
//
// PLEASE REMEMBER:
//
@@ -86,6 +78,20 @@ static NSString *const kGPBDataCoderKey = @"GPBData";
GPBMessage *autocreator_;
GPBFieldDescriptor *autocreatorField_;
GPBExtensionDescriptor *autocreatorExtension_;
+
+ // A lock to provide mutual exclusion from internal data that can be modified
+ // by *read* operations such as getters (autocreation of message fields and
+ // message extensions, not setting of values). Used to guarantee thread safety
+ // for concurrent reads on the message.
+ // NOTE: OSSpinLock may seem like a good fit here but Apple engineers have
+ // pointed out that they are vulnerable to live locking on iOS in cases of
+ // priority inversion:
+ // http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/
+ // https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html
+ // Use of readOnlySemaphore_ must be prefaced by a call to
+ // GPBPrepareReadOnlySemaphore to ensure it has been created. This allows
+ // readOnlySemaphore_ to be only created when actually needed.
+ _Atomic(dispatch_semaphore_t) readOnlySemaphore_;
}
@end
@@ -107,24 +113,39 @@ static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap,
NSZone *zone)
__attribute__((ns_returns_retained));
+#ifdef DEBUG
static NSError *MessageError(NSInteger code, NSDictionary *userInfo) {
return [NSError errorWithDomain:GPBMessageErrorDomain
code:code
userInfo:userInfo];
}
+#endif
-static NSError *MessageErrorWithReason(NSInteger code, NSString *reason) {
- NSDictionary *userInfo = nil;
- if ([reason length]) {
- userInfo = @{ @"Reason" : reason };
+static NSError *ErrorFromException(NSException *exception) {
+ NSError *error = nil;
+
+ if ([exception.name isEqual:GPBCodedInputStreamException]) {
+ NSDictionary *exceptionInfo = exception.userInfo;
+ error = exceptionInfo[GPBCodedInputStreamUnderlyingErrorKey];
}
- return MessageError(code, userInfo);
-}
+ if (!error) {
+ NSString *reason = exception.reason;
+ NSDictionary *userInfo = nil;
+ if ([reason length]) {
+ userInfo = @{ GPBErrorReasonKey : reason };
+ }
+
+ error = [NSError errorWithDomain:GPBMessageErrorDomain
+ code:GPBMessageErrorCodeOther
+ userInfo:userInfo];
+ }
+ return error;
+}
static void CheckExtension(GPBMessage *self,
GPBExtensionDescriptor *extension) {
- if ([self class] != extension.containingMessageClass) {
+ if (![self isKindOfClass:extension.containingMessageClass]) {
[NSException
raise:NSInvalidArgumentException
format:@"Extension %@ used on wrong class (%@ instead of %@)",
@@ -568,6 +589,7 @@ static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
id array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!array) {
// Check again after getting the lock.
+ GPBPrepareReadOnlySemaphore(self);
dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!array) {
@@ -598,6 +620,7 @@ static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!dict) {
// Check again after getting the lock.
+ GPBPrepareReadOnlySemaphore(self);
dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!dict) {
@@ -704,7 +727,7 @@ void GPBClearMessageAutocreator(GPBMessage *self) {
return;
}
-#if DEBUG && !defined(NS_BLOCK_ASSERTIONS)
+#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS)
// Either the autocreator must have its "has" flag set to YES, or it must be
// NO and not equal to ourselves.
BOOL autocreatorHas =
@@ -730,6 +753,31 @@ void GPBClearMessageAutocreator(GPBMessage *self) {
self->autocreatorExtension_ = nil;
}
+// Call this before using the readOnlySemaphore_. This ensures it is created only once.
+void GPBPrepareReadOnlySemaphore(GPBMessage *self) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdirect-ivar-access"
+
+ // Create the semaphore on demand (rather than init) as developers might not cause them
+ // to be needed, and the heap usage can add up. The atomic swap is used to avoid needing
+ // another lock around creating it.
+ if (self->readOnlySemaphore_ == nil) {
+ dispatch_semaphore_t worker = dispatch_semaphore_create(1);
+ dispatch_semaphore_t expected = nil;
+ if (!atomic_compare_exchange_strong(&self->readOnlySemaphore_, &expected, worker)) {
+ dispatch_release(worker);
+ }
+#if defined(__clang_analyzer__)
+ // The Xcode 9.2 (and 9.3 beta) static analyzer thinks worker is leaked
+ // (doesn't seem to know about atomic_compare_exchange_strong); so just
+ // for the analyzer, let it think worker is also released in this case.
+ else { dispatch_release(worker); }
+#endif
+ }
+
+#pragma clang diagnostic pop
+}
+
static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
if (!self->unknownFields_) {
self->unknownFields_ = [[GPBUnknownFieldSet alloc] init];
@@ -789,14 +837,8 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
file:fileDescriptor
fields:NULL
fieldCount:0
- oneofs:NULL
- oneofCount:0
- enums:NULL
- enumCount:0
- ranges:NULL
- rangeCount:0
storageSize:0
- wireFormat:NO];
+ flags:0];
}
return descriptor;
}
@@ -809,8 +851,6 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
if ((self = [super init])) {
messageStorage_ = (GPBMessage_StoragePtr)(
((uint8_t *)self) + class_getInstanceSize([self class]));
-
- readOnlySemaphore_ = dispatch_semaphore_create(1);
}
return self;
@@ -834,8 +874,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
[self release];
self = nil;
if (errorPtr) {
- *errorPtr = MessageErrorWithReason(GPBMessageErrorCodeMalformedData,
- exception.reason);
+ *errorPtr = ErrorFromException(exception);
}
}
#ifdef DEBUG
@@ -866,8 +905,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
[self release];
self = nil;
if (errorPtr) {
- *errorPtr = MessageErrorWithReason(GPBMessageErrorCodeMalformedData,
- exception.reason);
+ *errorPtr = ErrorFromException(exception);
}
}
#ifdef DEBUG
@@ -886,7 +924,9 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
- (void)dealloc {
[self internalClear:NO];
NSCAssert(!autocreator_, @"Autocreator was not cleared before dealloc.");
- dispatch_release(readOnlySemaphore_);
+ if (readOnlySemaphore_) {
+ dispatch_release(readOnlySemaphore_);
+ }
[super dealloc];
}
@@ -950,7 +990,8 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
newValue = [value copyWithZone:zone];
}
} else {
- if (field.mapKeyDataType == GPBDataTypeString) {
+ if ((field.mapKeyDataType == GPBDataTypeString) &&
+ GPBFieldDataTypeIsObject(field)) {
// NSDictionary
newValue = [value mutableCopyWithZone:zone];
} else {
@@ -1023,9 +1064,11 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
if (arrayOrMap) {
if (field.fieldType == GPBFieldTypeRepeated) {
if (GPBFieldDataTypeIsObject(field)) {
- GPBAutocreatedArray *autoArray = arrayOrMap;
- if (autoArray->_autocreator == self) {
- autoArray->_autocreator = nil;
+ if ([arrayOrMap isKindOfClass:[GPBAutocreatedArray class]]) {
+ GPBAutocreatedArray *autoArray = arrayOrMap;
+ if (autoArray->_autocreator == self) {
+ autoArray->_autocreator = nil;
+ }
}
} else {
// Type doesn't matter, it is a GPB*Array.
@@ -1037,9 +1080,11 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
} else {
if ((field.mapKeyDataType == GPBDataTypeString) &&
GPBFieldDataTypeIsObject(field)) {
- GPBAutocreatedDictionary *autoDict = arrayOrMap;
- if (autoDict->_autocreator == self) {
- autoDict->_autocreator = nil;
+ if ([arrayOrMap isKindOfClass:[GPBAutocreatedDictionary class]]) {
+ GPBAutocreatedDictionary *autoDict = arrayOrMap;
+ if (autoDict->_autocreator == self) {
+ autoDict->_autocreator = nil;
+ }
}
} else {
// Type doesn't matter, it is a GPB*Dictionary.
@@ -1724,6 +1769,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
}
// Check for an autocreated value.
+ GPBPrepareReadOnlySemaphore(self);
dispatch_semaphore_wait(readOnlySemaphore_, DISPATCH_TIME_FOREVER);
value = [autocreatedExtensionMap_ objectForKey:extension];
if (!value) {
@@ -1751,7 +1797,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
}
- (BOOL)hasExtension:(GPBExtensionDescriptor *)extension {
-#if DEBUG
+#if defined(DEBUG) && DEBUG
CheckExtension(self, extension);
#endif // DEBUG
return nil != [extensionMap_ objectForKey:extension];
@@ -1776,11 +1822,6 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
}
}
-- (NSArray *)sortedExtensionsInUse {
- return [[extensionMap_ allKeys]
- sortedArrayUsingSelector:@selector(compareByFieldNumber:)];
-}
-
- (void)setExtension:(GPBExtensionDescriptor *)extension value:(id)value {
if (!value) {
[self clearExtension:extension];
@@ -1935,16 +1976,13 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
}
}
@catch (NSException *exception) {
- [message release];
message = nil;
if (errorPtr) {
- *errorPtr = MessageErrorWithReason(GPBMessageErrorCodeMalformedData,
- exception.reason);
+ *errorPtr = ErrorFromException(exception);
}
}
#ifdef DEBUG
if (message && !message.initialized) {
- [message release];
message = nil;
if (errorPtr) {
*errorPtr = MessageError(GPBMessageErrorCodeMissingRequiredField, nil);
@@ -2010,7 +2048,12 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
[newInput release];
} else {
GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
- [unknownFields mergeMessageSetMessage:typeId data:rawBytes];
+ // rawBytes was created via a NoCopy, so it can be reusing a
+ // subrange of another NSData that might go out of scope as things
+ // unwind, so a copy is needed to ensure what is saved in the
+ // unknown fields stays valid.
+ NSData *cloned = [NSData dataWithData:rawBytes];
+ [unknownFields mergeMessageSetMessage:typeId data:cloned];
}
}
}
@@ -2281,6 +2324,9 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream(
while (YES) {
BOOL merged = NO;
tag = GPBCodedInputStreamReadTag(state);
+ if (tag == 0) {
+ break; // Reached end.
+ }
for (NSUInteger i = 0; i < numFields; ++i) {
if (startingIndex >= numFields) startingIndex = 0;
GPBFieldDescriptor *fieldDescriptor = fields[startingIndex];
@@ -2319,7 +2365,7 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream(
}
} // for(i < numFields)
- if (!merged) {
+ if (!merged && (tag != 0)) {
// Primitive, repeated types can be packed on unpacked on the wire, and
// are parsed either way. The above loop covered tag in the preferred
// for, so this need to check the alternate form.
@@ -2353,17 +2399,11 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream(
// zero signals EOF / limit reached
return;
} else {
- if (GPBPreserveUnknownFields(syntax)) {
- if (![self parseUnknownField:input
- extensionRegistry:extensionRegistry
- tag:tag]) {
- // it's an endgroup tag
- return;
- }
- } else {
- if (![input skipField:tag]) {
- return;
- }
+ if (![self parseUnknownField:input
+ extensionRegistry:extensionRegistry
+ tag:tag]) {
+ // it's an endgroup tag
+ return;
}
}
} // if(!merged)
@@ -2574,18 +2614,20 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream(
#pragma mark - isEqual: & hash Support
-- (BOOL)isEqual:(GPBMessage *)other {
+- (BOOL)isEqual:(id)other {
if (other == self) {
return YES;
}
- if (![other isKindOfClass:[self class]] &&
- ![self isKindOfClass:[other class]]) {
+ if (![other isKindOfClass:[GPBMessage class]]) {
return NO;
}
-
+ GPBMessage *otherMsg = other;
GPBDescriptor *descriptor = [[self class] descriptor];
+ if ([[otherMsg class] descriptor] != descriptor) {
+ return NO;
+ }
uint8_t *selfStorage = (uint8_t *)messageStorage_;
- uint8_t *otherStorage = (uint8_t *)other->messageStorage_;
+ uint8_t *otherStorage = (uint8_t *)otherMsg->messageStorage_;
for (GPBFieldDescriptor *field in descriptor->fields_) {
if (GPBFieldIsMapOrArray(field)) {
@@ -2620,9 +2662,13 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream(
size_t fieldOffset = field->description_->offset;
switch (fieldDataType) {
case GPBDataTypeBool: {
- BOOL *selfValPtr = (BOOL *)&selfStorage[fieldOffset];
- BOOL *otherValPtr = (BOOL *)&otherStorage[fieldOffset];
- if (*selfValPtr != *otherValPtr) {
+ // Bools are stored in has_bits to avoid needing explicit space in
+ // the storage structure.
+ // (the field number passed to the HasIvar helper doesn't really
+ // matter since the offset is never negative)
+ BOOL selfValue = GPBGetHasIvar(self, (int32_t)(fieldOffset), 0);
+ BOOL otherValue = GPBGetHasIvar(other, (int32_t)(fieldOffset), 0);
+ if (selfValue != otherValue) {
return NO;
}
break;
@@ -2634,7 +2680,7 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream(
case GPBDataTypeFixed32:
case GPBDataTypeUInt32:
case GPBDataTypeFloat: {
- _GPBCompileAssert(sizeof(float) == sizeof(uint32_t), float_not_32_bits);
+ GPBInternalCompileAssert(sizeof(float) == sizeof(uint32_t), float_not_32_bits);
// These are all 32bit, signed/unsigned doesn't matter for equality.
uint32_t *selfValPtr = (uint32_t *)&selfStorage[fieldOffset];
uint32_t *otherValPtr = (uint32_t *)&otherStorage[fieldOffset];
@@ -2649,7 +2695,7 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream(
case GPBDataTypeFixed64:
case GPBDataTypeUInt64:
case GPBDataTypeDouble: {
- _GPBCompileAssert(sizeof(double) == sizeof(uint64_t), double_not_64_bits);
+ GPBInternalCompileAssert(sizeof(double) == sizeof(uint64_t), double_not_64_bits);
// These are all 64bit, signed/unsigned doesn't matter for equality.
uint64_t *selfValPtr = (uint64_t *)&selfStorage[fieldOffset];
uint64_t *otherValPtr = (uint64_t *)&otherStorage[fieldOffset];
@@ -2675,14 +2721,14 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream(
} // for(fields)
// nil and empty are equal
- if (extensionMap_.count != 0 || other->extensionMap_.count != 0) {
- if (![extensionMap_ isEqual:other->extensionMap_]) {
+ if (extensionMap_.count != 0 || otherMsg->extensionMap_.count != 0) {
+ if (![extensionMap_ isEqual:otherMsg->extensionMap_]) {
return NO;
}
}
// nil and empty are equal
- GPBUnknownFieldSet *otherUnknowns = other->unknownFields_;
+ GPBUnknownFieldSet *otherUnknowns = otherMsg->unknownFields_;
if ([unknownFields_ countOfFields] != 0 ||
[otherUnknowns countOfFields] != 0) {
if (![unknownFields_ isEqual:otherUnknowns]) {
@@ -2731,8 +2777,12 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream(
size_t fieldOffset = field->description_->offset;
switch (fieldDataType) {
case GPBDataTypeBool: {
- BOOL *valPtr = (BOOL *)&storage[fieldOffset];
- result = prime * result + *valPtr;
+ // Bools are stored in has_bits to avoid needing explicit space in
+ // the storage structure.
+ // (the field number passed to the HasIvar helper doesn't really
+ // matter since the offset is never negative)
+ BOOL value = GPBGetHasIvar(self, (int32_t)(fieldOffset), 0);
+ result = prime * result + value;
break;
}
case GPBDataTypeSFixed32:
@@ -2742,7 +2792,7 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream(
case GPBDataTypeFixed32:
case GPBDataTypeUInt32:
case GPBDataTypeFloat: {
- _GPBCompileAssert(sizeof(float) == sizeof(uint32_t), float_not_32_bits);
+ GPBInternalCompileAssert(sizeof(float) == sizeof(uint32_t), float_not_32_bits);
// These are all 32bit, just mix it in.
uint32_t *valPtr = (uint32_t *)&storage[fieldOffset];
result = prime * result + *valPtr;
@@ -2754,7 +2804,7 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream(
case GPBDataTypeFixed64:
case GPBDataTypeUInt64:
case GPBDataTypeDouble: {
- _GPBCompileAssert(sizeof(double) == sizeof(uint64_t), double_not_64_bits);
+ GPBInternalCompileAssert(sizeof(double) == sizeof(uint64_t), double_not_64_bits);
// These are all 64bit, just mix what fits into an NSUInteger in.
uint64_t *valPtr = (uint64_t *)&storage[fieldOffset];
result = prime * result + (NSUInteger)(*valPtr);
@@ -2801,7 +2851,7 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream(
return description;
}
-#if DEBUG
+#if defined(DEBUG) && DEBUG
// Xcode 5.1 added support for custom quick look info.
// https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/CustomClassDisplay_in_QuickLook/CH01-quick_look_for_custom_objects/CH01-quick_look_for_custom_objects.html#//apple_ref/doc/uid/TP40014001-CH2-SW1
@@ -2975,7 +3025,10 @@ typedef struct ResolveIvarAccessorMethodResult {
SEL encodingSelector;
} ResolveIvarAccessorMethodResult;
-static void ResolveIvarGet(GPBFieldDescriptor *field,
+// |field| can be __unsafe_unretained because they are created at startup
+// and are essentially global. No need to pay for retain/release when
+// they are captured in blocks.
+static void ResolveIvarGet(__unsafe_unretained GPBFieldDescriptor *field,
ResolveIvarAccessorMethodResult *result) {
GPBDataType fieldDataType = GPBGetFieldDataType(field);
switch (fieldDataType) {
@@ -3017,7 +3070,8 @@ static void ResolveIvarGet(GPBFieldDescriptor *field,
}
}
-static void ResolveIvarSet(GPBFieldDescriptor *field,
+// See comment about __unsafe_unretained on ResolveIvarGet.
+static void ResolveIvarSet(__unsafe_unretained GPBFieldDescriptor *field,
GPBFileSyntax syntax,
ResolveIvarAccessorMethodResult *result) {
GPBDataType fieldDataType = GPBGetFieldDataType(field);
@@ -3055,15 +3109,16 @@ static void ResolveIvarSet(GPBFieldDescriptor *field,
+ (BOOL)resolveInstanceMethod:(SEL)sel {
const GPBDescriptor *descriptor = [self descriptor];
if (!descriptor) {
- return NO;
+ return [super resolveInstanceMethod:sel];
}
// NOTE: hasOrCountSel_/setHasSel_ will be NULL if the field for the given
// message should not have has support (done in GPBDescriptor.m), so there is
// no need for checks here to see if has*/setHas* are allowed.
-
ResolveIvarAccessorMethodResult result = {NULL, NULL};
- for (GPBFieldDescriptor *field in descriptor->fields_) {
+
+ // See comment about __unsafe_unretained on ResolveIvarGet.
+ for (__unsafe_unretained GPBFieldDescriptor *field in descriptor->fields_) {
BOOL isMapOrArray = GPBFieldIsMapOrArray(field);
if (!isMapOrArray) {
// Single fields.
@@ -3096,7 +3151,7 @@ static void ResolveIvarSet(GPBFieldDescriptor *field,
} else {
GPBOneofDescriptor *oneof = field->containingOneof_;
if (oneof && (sel == oneof->caseSel_)) {
- int32_t index = oneof->oneofDescription_->index;
+ int32_t index = GPBFieldHasIndex(field);
result.impToAdd = imp_implementationWithBlock(^(id obj) {
return GPBGetHasOneof(obj, index);
});
@@ -3143,8 +3198,17 @@ static void ResolveIvarSet(GPBFieldDescriptor *field,
if (result.impToAdd) {
const char *encoding =
GPBMessageEncodingForSelector(result.encodingSelector, YES);
- BOOL methodAdded = class_addMethod(descriptor.messageClass, sel,
- result.impToAdd, encoding);
+ Class msgClass = descriptor.messageClass;
+ BOOL methodAdded = class_addMethod(msgClass, sel, result.impToAdd, encoding);
+ // class_addMethod() is documented as also failing if the method was already
+ // added; so we check if the method is already there and return success so
+ // the method dispatch will still happen. Why would it already be added?
+ // Two threads could cause the same method to be bound at the same time,
+ // but only one will actually bind it; the other still needs to return true
+ // so things will dispatch.
+ if (!methodAdded) {
+ methodAdded = GPBClassHasSel(msgClass, sel);
+ }
return methodAdded;
}
return [super resolveInstanceMethod:sel];
@@ -3152,7 +3216,7 @@ static void ResolveIvarSet(GPBFieldDescriptor *field,
+ (BOOL)resolveClassMethod:(SEL)sel {
// Extensions scoped to a Message and looked up via class methods.
- if (GPBResolveExtensionClassMethod(self, sel)) {
+ if (GPBResolveExtensionClassMethod([self descriptor].messageClass, sel)) {
return YES;
}
return [super resolveClassMethod:sel];
@@ -3191,3 +3255,63 @@ static void ResolveIvarSet(GPBFieldDescriptor *field,
}
@end
+
+#pragma mark - Messages from GPBUtilities.h but defined here for access to helpers.
+
+// Only exists for public api, no core code should use this.
+id GPBGetMessageRepeatedField(GPBMessage *self, GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+ if (field.fieldType != GPBFieldTypeRepeated) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"%@.%@ is not a repeated field.",
+ [self class], field.name];
+ }
+#endif
+ GPBDescriptor *descriptor = [[self class] descriptor];
+ GPBFileSyntax syntax = descriptor.file.syntax;
+ return GetOrCreateArrayIvarWithField(self, field, syntax);
+}
+
+// Only exists for public api, no core code should use this.
+id GPBGetMessageMapField(GPBMessage *self, GPBFieldDescriptor *field) {
+#if defined(DEBUG) && DEBUG
+ if (field.fieldType != GPBFieldTypeMap) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"%@.%@ is not a map<> field.",
+ [self class], field.name];
+ }
+#endif
+ GPBDescriptor *descriptor = [[self class] descriptor];
+ GPBFileSyntax syntax = descriptor.file.syntax;
+ return GetOrCreateMapIvarWithField(self, field, syntax);
+}
+
+id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
+ NSCAssert(!GPBFieldIsMapOrArray(field), @"Shouldn't get here");
+ if (GPBGetHasIvarField(self, field)) {
+ uint8_t *storage = (uint8_t *)self->messageStorage_;
+ id *typePtr = (id *)&storage[field->description_->offset];
+ return *typePtr;
+ }
+ // Not set...
+
+ // Non messages (string/data), get their default.
+ if (!GPBFieldDataTypeIsMessage(field)) {
+ return field.defaultValue.valueMessage;
+ }
+
+ GPBPrepareReadOnlySemaphore(self);
+ dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
+ GPBMessage *result = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+ if (!result) {
+ // For non repeated messages, create the object, set it and return it.
+ // This object will not initially be visible via GPBGetHasIvar, so
+ // we save its creator so it can become visible if it's mutated later.
+ result = GPBCreateMessageWithAutocreator(field.msgClass, self, field);
+ GPBSetAutocreatedRetainedObjectIvarWithField(self, field, result);
+ }
+ dispatch_semaphore_signal(self->readOnlySemaphore_);
+ return result;
+}
+
+#pragma clang diagnostic pop