diff --git a/objectivec/GPBDescriptor.m b/objectivec/GPBDescriptor.m index 130073b2f0..de16b49f3f 100644 --- a/objectivec/GPBDescriptor.m +++ b/objectivec/GPBDescriptor.m @@ -38,7 +38,8 @@ @interface GPBDescriptor () - (instancetype)initWithClass:(Class)messageClass - file:(GPBFileDescriptor *)file + messageName:(NSString *)messageName + fileDescription:(GPBFileDescription *)fileDescription fields:(NSArray *)fields storageSize:(uint32_t)storage wireFormat:(BOOL)wireFormat; @@ -48,8 +49,8 @@ // Single initializer // description has to be long lived, it is held as a raw pointer. - (instancetype)initWithFieldDescription:(void *)description - file:(GPBFileDescriptor *)file - descriptorFlags:(GPBDescriptorInitializationFlags)descriptorFlags; + descriptorFlags:(GPBDescriptorInitializationFlags)descriptorFlags + fileSyntax:(GPBFileSyntax)fileSyntax; @end @@ -72,6 +73,7 @@ static const char kTextFormatExtraValueKey = 0; static const char kParentClassValueKey = 0; static const char kClassNameSuffixKey = 0; +static const char kFileDescriptorCacheKey = 0; // Utility function to generate selectors on the fly. static SEL SelFromStrings(const char *prefix, const char *middle, const char *suffix, @@ -122,7 +124,8 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageField @implementation GPBDescriptor { Class messageClass_; - GPBFileDescriptor *file_; + NSString *messageName_; + const GPBFileDescription *fileDescription_; BOOL wireFormat_; } @@ -131,11 +134,11 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageField @synthesize oneofs = oneofs_; @synthesize extensionRanges = extensionRanges_; @synthesize extensionRangesCount = extensionRangesCount_; -@synthesize file = file_; @synthesize wireFormat = wireFormat_; + (instancetype)allocDescriptorForClass:(Class)messageClass - file:(GPBFileDescriptor *)file + messageName:(NSString *)messageName + fileDescription:(GPBFileDescription *)fileDescription fields:(void *)fieldDescriptions fieldCount:(uint32_t)fieldCount storageSize:(uint32_t)storageSize @@ -168,7 +171,9 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageField mergedFieldFlags |= (((GPBMessageFieldDescription *)fieldDescriptions)[i]).flags; } GPBFieldDescriptor *fieldDescriptor = - [[GPBFieldDescriptor alloc] initWithFieldDescription:desc file:file descriptorFlags:flags]; + [[GPBFieldDescriptor alloc] initWithFieldDescription:desc + descriptorFlags:flags + fileSyntax:fileDescription->syntax]; [fields addObject:fieldDescriptor]; [fieldDescriptor release]; } @@ -184,7 +189,8 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageField BOOL wireFormat = (flags & GPBDescriptorInitializationFlag_WireFormat) != 0; GPBDescriptor *descriptor = [[self alloc] initWithClass:messageClass - file:file + messageName:messageName + fileDescription:fileDescription fields:fields storageSize:storageSize wireFormat:wireFormat]; @@ -192,6 +198,35 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageField return descriptor; } ++ (instancetype)allocDescriptorForClass:(Class)messageClass + file:(GPBFileDescriptor *)file + fields:(void *)fieldDescriptions + fieldCount:(uint32_t)fieldCount + storageSize:(uint32_t)storageSize + flags:(GPBDescriptorInitializationFlags)flags { + GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30006, + time_to_remove_this_old_version_shim); + + // Use a local GPBFileDescription with just the syntax to allow legacy generation initialization, + // then clear the ivar and wire in the GPBFileDescriptor. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + GPBFileDescription localDesc = {NULL, NULL, file.syntax}; +#pragma clang diagnostic pop + + GPBDescriptor *result = [self allocDescriptorForClass:messageClass + messageName:nil + fileDescription:&localDesc + fields:fieldDescriptions + fieldCount:fieldCount + storageSize:storageSize + flags:flags]; + result->fileDescription_ = NULL; + objc_setAssociatedObject(result, &kFileDescriptorCacheKey, file, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return result; +} + + (instancetype)allocDescriptorForClass:(Class)messageClass rootClass:(__unused Class)rootClass file:(GPBFileDescriptor *)file @@ -213,13 +248,21 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageField } - (instancetype)initWithClass:(Class)messageClass - file:(GPBFileDescriptor *)file + messageName:(NSString *)messageName + fileDescription:(GPBFileDescription *)fileDescription fields:(NSArray *)fields storageSize:(uint32_t)storageSize wireFormat:(BOOL)wireFormat { if ((self = [super init])) { messageClass_ = messageClass; - file_ = file; +#if defined(DEBUG) && DEBUG + // If `messageName` is set, then `fileDescription` also must be set. `fileDescription` gets + // hotwired for legacy startup, so it can be non NULL without `messageName` having been set. + NSAssert((messageName == nil) || (fileDescription != NULL), + @"messageName and fileDescription should always be provided together"); +#endif + messageName_ = [messageName copy]; + fileDescription_ = fileDescription; fields_ = [fields retain]; storageSize_ = storageSize; wireFormat_ = wireFormat; @@ -228,6 +271,7 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageField } - (void)dealloc { + [messageName_ release]; [fields_ release]; [oneofs_ release]; [super dealloc]; @@ -292,6 +336,8 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageField } - (void)setupMessageClassNameSuffix:(NSString *)suffix { + GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30007, + time_to_remove_this_old_version_shim); if (suffix.length) { objc_setAssociatedObject(self, &kClassNameSuffixKey, suffix, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @@ -301,12 +347,54 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageField return NSStringFromClass(messageClass_); } +- (GPBFileDescriptor *)file { + @synchronized(self) { + GPBFileDescriptor *result = objc_getAssociatedObject(self, &kFileDescriptorCacheKey); + if (!result) { +#if defined(DEBUG) && DEBUG + NSAssert(fileDescription_ != NULL, @"Internal error in generation/startup"); +#endif + // `package` and `prefix` can both be NULL if there wasn't one for the file. + NSString *package = fileDescription_->package ? @(fileDescription_->package) : @""; + if (fileDescription_->prefix) { + result = [[GPBFileDescriptor alloc] initWithPackage:package + objcPrefix:@(fileDescription_->prefix) + syntax:fileDescription_->syntax]; + + } else { + result = [[GPBFileDescriptor alloc] initWithPackage:package + syntax:fileDescription_->syntax]; + } + objc_setAssociatedObject(result, &kFileDescriptorCacheKey, result, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return result; + } +} + - (GPBDescriptor *)containingType { Class parentClass = objc_getAssociatedObject(self, &kParentClassValueKey); return [parentClass descriptor]; } - (NSString *)fullName { + GPBDescriptor *parent = self.containingType; + if (messageName_) { + if (parent) { + return [NSString stringWithFormat:@"%@.%@", parent.fullName, messageName_]; + } + if (fileDescription_->package) { + return [NSString stringWithFormat:@"%s.%@", fileDescription_->package, messageName_]; + } + return messageName_; + } + + GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30007, + time_to_remove_this_old_approach); + // NOTE: When this code path is removed, this also means this api can't return nil any more but + // that would be a breaking code change (not longer a Swift optional), so changing that will be + // harder. + NSString *className = NSStringFromClass(self.messageClass); GPBFileDescriptor *file = self.file; NSString *objcPrefix = file.objcPrefix; @@ -314,7 +402,6 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageField NSAssert(0, @"Class didn't have correct prefix? (%@ - %@)", className, objcPrefix); return nil; } - GPBDescriptor *parent = self.containingType; NSString *name = nil; if (parent) { @@ -542,8 +629,8 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { @synthesize containingOneof = containingOneof_; - (instancetype)initWithFieldDescription:(void *)description - file:(GPBFileDescriptor *)file - descriptorFlags:(GPBDescriptorInitializationFlags)descriptorFlags { + descriptorFlags:(GPBDescriptorInitializationFlags)descriptorFlags + fileSyntax:(GPBFileSyntax)fileSyntax { if ((self = [super init])) { BOOL includesDefault = (descriptorFlags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0; @@ -571,11 +658,8 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { // - not repeated/map // - not in a oneof (negative has index) // - not a message (the flag doesn't make sense for messages) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - BOOL clearOnZero = ((file.syntax == GPBFileSyntaxProto3) && !isMapOrArray && + BOOL clearOnZero = ((fileSyntax == GPBFileSyntaxProto3) && !isMapOrArray && (coreDesc->hasIndex >= 0) && !isMessage); -#pragma clang diagnostic pop if (clearOnZero) { coreDesc->flags |= GPBFieldClearHasIvarOnZero; } @@ -592,10 +676,7 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { // to be what the runtime was doing (even though it was wrong). This is // only wrong in the rare cases an enum is declared in a proto3 syntax // file but used for a field in the proto2 syntax file. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - BOOL isClosedEnum = (dataType == GPBDataTypeEnum && file.syntax != GPBFileSyntaxProto3); -#pragma clang diagnostic pop + BOOL isClosedEnum = (dataType == GPBDataTypeEnum && fileSyntax != GPBFileSyntaxProto3); if (isClosedEnum) { coreDesc->flags |= GPBFieldClosedEnum; } diff --git a/objectivec/GPBDescriptor_PackagePrivate.h b/objectivec/GPBDescriptor_PackagePrivate.h index 0fadaf59ce..672cefddcc 100644 --- a/objectivec/GPBDescriptor_PackagePrivate.h +++ b/objectivec/GPBDescriptor_PackagePrivate.h @@ -91,6 +91,15 @@ typedef NS_OPTIONS(uint16_t, GPBFieldFlags) { // their size. This directly impacts the size of apps since these exist per // field/extension. +typedef struct GPBFileDescription { + // The proto package for the file. + const char *package; + // The objc_class_prefix option if present. + const char *prefix; + // The file's proto syntax. + GPBFileSyntax syntax; +} GPBFileDescription; + // Describes a single field in a protobuf as it is represented as an ivar. typedef struct GPBMessageFieldDescription { // Name of ivar. @@ -200,9 +209,10 @@ typedef NS_OPTIONS(uint32_t, GPBDescriptorInitializationFlags) { uint32_t storageSize_; } -// fieldDescriptions have to be long lived, they are held as raw pointers. +// fieldDescriptions and fileDescription have to be long lived, they are held as raw pointers. + (instancetype)allocDescriptorForClass:(Class)messageClass - file:(GPBFileDescriptor *)file + messageName:(NSString *)messageName + fileDescription:(GPBFileDescription *)fileDescription fields:(void *)fieldDescriptions fieldCount:(uint32_t)fieldCount storageSize:(uint32_t)storageSize @@ -217,9 +227,14 @@ typedef NS_OPTIONS(uint32_t, GPBDescriptorInitializationFlags) { - (void)setupExtraTextInfo:(const char *)extraTextFormatInfo; - (void)setupExtensionRanges:(const GPBExtensionRange *)ranges count:(int32_t)count; - (void)setupContainingMessageClass:(Class)msgClass; -- (void)setupMessageClassNameSuffix:(NSString *)suffix; // Deprecated, these remain to support older versions of source generation. ++ (instancetype)allocDescriptorForClass:(Class)messageClass + file:(GPBFileDescriptor *)file + fields:(void *)fieldDescriptions + fieldCount:(uint32_t)fieldCount + storageSize:(uint32_t)storageSize + flags:(GPBDescriptorInitializationFlags)flags; + (instancetype)allocDescriptorForClass:(Class)messageClass rootClass:(Class)rootClass file:(GPBFileDescriptor *)file @@ -228,6 +243,7 @@ typedef NS_OPTIONS(uint32_t, GPBDescriptorInitializationFlags) { storageSize:(uint32_t)storageSize flags:(GPBDescriptorInitializationFlags)flags; - (void)setupContainingMessageClassName:(const char *)msgClassName; +- (void)setupMessageClassNameSuffix:(NSString *)suffix; @end