1// Protocol Buffers - Google's data interchange format 2// Copyright 2008 Google Inc. All rights reserved. 3// 4// Use of this source code is governed by a BSD-style 5// license that can be found in the LICENSE file or at 6// https://developers.google.com/open-source/licenses/bsd 7 8#import "GPBDescriptor.h" 9#import "GPBDescriptor_PackagePrivate.h" 10 11#import <objc/runtime.h> 12 13#import "GPBMessage.h" 14#import "GPBMessage_PackagePrivate.h" 15#import "GPBUtilities.h" 16#import "GPBUtilities_PackagePrivate.h" 17#import "GPBWireFormat.h" 18 19@interface GPBDescriptor () 20- (instancetype)initWithClass:(Class)messageClass 21 messageName:(NSString *)messageName 22 fileDescription:(GPBFileDescription *)fileDescription 23 fields:(NSArray *)fields 24 storageSize:(uint32_t)storage 25 wireFormat:(BOOL)wireFormat; 26@end 27 28@interface GPBFieldDescriptor () 29// Single initializer 30// description has to be long lived, it is held as a raw pointer. 31- (instancetype)initWithFieldDescription:(void *)description 32 descriptorFlags:(GPBDescriptorInitializationFlags)descriptorFlags; 33 34@end 35 36@interface GPBEnumDescriptor () 37- (instancetype)initWithName:(NSString *)name 38 valueNames:(const char *)valueNames 39 values:(const int32_t *)values 40 count:(uint32_t)valueCount 41 enumVerifier:(GPBEnumValidationFunc)enumVerifier 42 flags:(GPBEnumDescriptorInitializationFlags)flags; 43@end 44 45// Direct access is use for speed, to avoid even internally declaring things 46// read/write, etc. The warning is enabled in the project to ensure code calling 47// protos can turn on -Wdirect-ivar-access without issues. 48#pragma clang diagnostic push 49#pragma clang diagnostic ignored "-Wdirect-ivar-access" 50 51// The addresses of these variables are used as keys for objc_getAssociatedObject. 52static const char kTextFormatExtraValueKey = 0; 53static const char kParentClassValueKey = 0; 54static const char kClassNameSuffixKey = 0; 55static const char kFileDescriptorCacheKey = 0; 56 57static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageFields) 58 __attribute__((ns_returns_retained)); 59 60static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageFields) { 61 NSMutableArray *result = [[NSMutableArray alloc] init]; 62 for (GPBFieldDescriptor *fieldDesc in allMessageFields) { 63 if (fieldDesc->description_->hasIndex == hasIndex) { 64 [result addObject:fieldDesc]; 65 } 66 } 67 return result; 68} 69 70@implementation GPBDescriptor { 71 Class messageClass_; 72 NSString *messageName_; 73 const GPBFileDescription *fileDescription_; 74 BOOL wireFormat_; 75} 76 77@synthesize messageClass = messageClass_; 78@synthesize fields = fields_; 79@synthesize oneofs = oneofs_; 80@synthesize extensionRanges = extensionRanges_; 81@synthesize extensionRangesCount = extensionRangesCount_; 82@synthesize wireFormat = wireFormat_; 83 84+ (instancetype)allocDescriptorForClass:(Class)messageClass 85 messageName:(NSString *)messageName 86 fileDescription:(GPBFileDescription *)fileDescription 87 fields:(void *)fieldDescriptions 88 fieldCount:(uint32_t)fieldCount 89 storageSize:(uint32_t)storageSize 90 flags:(GPBDescriptorInitializationFlags)flags { 91 // Compute the unknown flags by this version of the runtime and then check the passed in flags 92 // (from the generated code) to detect when sources from a newer version are being used with an 93 // older runtime. 94 GPBDescriptorInitializationFlags unknownFlags = 95 ~(GPBDescriptorInitializationFlag_FieldsWithDefault | 96 GPBDescriptorInitializationFlag_WireFormat | GPBDescriptorInitializationFlag_UsesClassRefs | 97 GPBDescriptorInitializationFlag_Proto3OptionalKnown | 98 GPBDescriptorInitializationFlag_ClosedEnumSupportKnown); 99 if ((flags & unknownFlags) != 0) { 100 GPBRuntimeMatchFailure(); 101 } 102 103#if defined(DEBUG) && DEBUG 104 NSAssert((flags & GPBDescriptorInitializationFlag_UsesClassRefs) != 0, 105 @"Internal error: all fields should have class refs"); 106 NSAssert((flags & GPBDescriptorInitializationFlag_Proto3OptionalKnown) != 0, 107 @"Internal error: proto3 optional should be known"); 108 NSAssert((flags & GPBDescriptorInitializationFlag_ClosedEnumSupportKnown) != 0, 109 @"Internal error: close enum should be known"); 110 111 // `messageName` and `fileDescription` should both be set or both be unset depending on if this is 112 // being called from current code generation or legacy code generation. 113 NSAssert((messageName == nil) == (fileDescription == NULL), 114 @"name and fileDescription should always be provided together"); 115#endif 116 117 NSMutableArray *fields = 118 (fieldCount ? [[NSMutableArray alloc] initWithCapacity:fieldCount] : nil); 119 BOOL fieldsIncludeDefault = (flags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0; 120 121 void *desc; 122 GPBFieldFlags mergedFieldFlags = GPBFieldNone; 123 for (uint32_t i = 0; i < fieldCount; ++i) { 124 // Need correctly typed pointer for array indexing below to work. 125 if (fieldsIncludeDefault) { 126 desc = &(((GPBMessageFieldDescriptionWithDefault *)fieldDescriptions)[i]); 127 mergedFieldFlags |= 128 (((GPBMessageFieldDescriptionWithDefault *)fieldDescriptions)[i]).core.flags; 129 } else { 130 desc = &(((GPBMessageFieldDescription *)fieldDescriptions)[i]); 131 mergedFieldFlags |= (((GPBMessageFieldDescription *)fieldDescriptions)[i]).flags; 132 } 133 GPBFieldDescriptor *fieldDescriptor = 134 [[GPBFieldDescriptor alloc] initWithFieldDescription:desc descriptorFlags:flags]; 135 [fields addObject:fieldDescriptor]; 136 [fieldDescriptor release]; 137 } 138 // No real value in checking all the fields individually, just check the combined flags at the 139 // end. 140 GPBFieldFlags unknownFieldFlags = 141 ~(GPBFieldRequired | GPBFieldRepeated | GPBFieldPacked | GPBFieldOptional | 142 GPBFieldHasDefaultValue | GPBFieldClearHasIvarOnZero | GPBFieldTextFormatNameCustom | 143 GPBFieldHasEnumDescriptor | GPBFieldMapKeyMask | GPBFieldClosedEnum); 144 if ((mergedFieldFlags & unknownFieldFlags) != 0) { 145 GPBRuntimeMatchFailure(); 146 } 147 148 BOOL wireFormat = (flags & GPBDescriptorInitializationFlag_WireFormat) != 0; 149 GPBDescriptor *descriptor = [[self alloc] initWithClass:messageClass 150 messageName:messageName 151 fileDescription:fileDescription 152 fields:fields 153 storageSize:storageSize 154 wireFormat:wireFormat]; 155 [fields release]; 156 return descriptor; 157} 158 159+ (instancetype)allocDescriptorForClass:(Class)messageClass 160 file:(GPBFileDescriptor *)file 161 fields:(void *)fieldDescriptions 162 fieldCount:(uint32_t)fieldCount 163 storageSize:(uint32_t)storageSize 164 flags:(GPBDescriptorInitializationFlags)flags { 165 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30006, 166 time_to_remove_this_old_version_shim); 167 168 BOOL fixClassRefs = (flags & GPBDescriptorInitializationFlag_UsesClassRefs) == 0; 169 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30003, 170 time_to_remove_non_class_ref_support); 171 172 BOOL fixProto3Optional = (flags & GPBDescriptorInitializationFlag_Proto3OptionalKnown) == 0; 173 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30004, 174 time_to_remove_proto3_optional_fallback); 175 176 BOOL fixClosedEnums = (flags & GPBDescriptorInitializationFlag_ClosedEnumSupportKnown) == 0; 177 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30005, 178 time_to_remove_closed_enum_fallback); 179 180 if (fixClassRefs || fixProto3Optional || fixClosedEnums) { 181 BOOL fieldsIncludeDefault = (flags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0; 182#pragma clang diagnostic push 183#pragma clang diagnostic ignored "-Wdeprecated-declarations" 184 GPBFileSyntax fileSyntax = file.syntax; 185#pragma clang diagnostic pop 186 187 for (uint32_t i = 0; i < fieldCount; ++i) { 188 GPBMessageFieldDescription *coreDesc; 189 if (fieldsIncludeDefault) { 190 coreDesc = &((((GPBMessageFieldDescriptionWithDefault *)fieldDescriptions)[i]).core); 191 } else { 192 coreDesc = &(((GPBMessageFieldDescription *)fieldDescriptions)[i]); 193 } 194 195 if (fixClassRefs && GPBDataTypeIsMessage(coreDesc->dataType)) { 196 const char *className = coreDesc->dataTypeSpecific.className; 197 Class msgClass = objc_getClass(className); 198 NSAssert(msgClass, @"Class %s not defined", className); 199 coreDesc->dataTypeSpecific.clazz = msgClass; 200 } 201 202 if (fixProto3Optional) { 203 // If it was... 204 // - proto3 syntax 205 // - not repeated/map 206 // - not in a oneof (negative has index) 207 // - not a message (the flag doesn't make sense for messages) 208 BOOL clearOnZero = ((fileSyntax == GPBFileSyntaxProto3) && 209 ((coreDesc->flags & (GPBFieldRepeated | GPBFieldMapKeyMask)) == 0) && 210 (coreDesc->hasIndex >= 0) && !GPBDataTypeIsMessage(coreDesc->dataType)); 211 if (clearOnZero) { 212 coreDesc->flags |= GPBFieldClearHasIvarOnZero; 213 } 214 } 215 216 if (fixClosedEnums) { 217 // NOTE: This isn't correct, it is using the syntax of the file that 218 // declared the field, not the syntax of the file that declared the 219 // enum; but for older generated code, that's all we have and that happens 220 // to be what the runtime was doing (even though it was wrong). This is 221 // only wrong in the rare cases an enum is declared in a proto3 syntax 222 // file but used for a field in the proto2 syntax file. 223 BOOL isClosedEnum = 224 (coreDesc->dataType == GPBDataTypeEnum && fileSyntax == GPBFileSyntaxProto2); 225 if (isClosedEnum) { 226 coreDesc->flags |= GPBFieldClosedEnum; 227 } 228 } 229 } 230 flags |= (GPBDescriptorInitializationFlag_UsesClassRefs | 231 GPBDescriptorInitializationFlag_Proto3OptionalKnown | 232 GPBDescriptorInitializationFlag_ClosedEnumSupportKnown); 233 } 234 235 GPBDescriptor *result = [self allocDescriptorForClass:messageClass 236 messageName:nil 237 fileDescription:NULL 238 fields:fieldDescriptions 239 fieldCount:fieldCount 240 storageSize:storageSize 241 flags:flags]; 242 objc_setAssociatedObject(result, &kFileDescriptorCacheKey, file, 243 OBJC_ASSOCIATION_RETAIN_NONATOMIC); 244 return result; 245} 246 247+ (instancetype)allocDescriptorForClass:(Class)messageClass 248 rootClass:(__unused Class)rootClass 249 file:(GPBFileDescriptor *)file 250 fields:(void *)fieldDescriptions 251 fieldCount:(uint32_t)fieldCount 252 storageSize:(uint32_t)storageSize 253 flags:(GPBDescriptorInitializationFlags)flags { 254 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30006, 255 time_to_remove_this_old_version_shim); 256 // The rootClass is no longer used, but it is passed as [ROOT class] to 257 // ensure it was started up during initialization also when the message 258 // scopes extensions. 259 return [self allocDescriptorForClass:messageClass 260 file:file 261 fields:fieldDescriptions 262 fieldCount:fieldCount 263 storageSize:storageSize 264 flags:flags]; 265} 266 267- (instancetype)initWithClass:(Class)messageClass 268 messageName:(NSString *)messageName 269 fileDescription:(GPBFileDescription *)fileDescription 270 fields:(NSArray *)fields 271 storageSize:(uint32_t)storageSize 272 wireFormat:(BOOL)wireFormat { 273#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS) 274 // This is also checked by the generator. 275 NSAssert(!wireFormat || fields.count == 0, @"Internal error: MessageSets should not have fields"); 276#endif 277 if ((self = [super init])) { 278 messageClass_ = messageClass; 279 messageName_ = [messageName copy]; 280 fileDescription_ = fileDescription; 281 fields_ = [fields retain]; 282 storageSize_ = storageSize; 283 wireFormat_ = wireFormat; 284 } 285 return self; 286} 287 288- (void)dealloc { 289 [messageName_ release]; 290 [fields_ release]; 291 [oneofs_ release]; 292 [super dealloc]; 293} 294 295// No need to provide -hash/-isEqual: as the instances are singletons and the 296// default from NSObject is fine. 297- (instancetype)copyWithZone:(__unused NSZone *)zone { 298 // Immutable. 299 return [self retain]; 300} 301 302- (void)setupOneofs:(const char **)oneofNames 303 count:(uint32_t)count 304 firstHasIndex:(int32_t)firstHasIndex { 305 NSCAssert(firstHasIndex < 0, @"Should always be <0"); 306 NSMutableArray *oneofs = [[NSMutableArray alloc] initWithCapacity:count]; 307 for (uint32_t i = 0, hasIndex = firstHasIndex; i < count; ++i, --hasIndex) { 308 const char *name = oneofNames[i]; 309 NSArray *fieldsForOneof = NewFieldsArrayForHasIndex(hasIndex, fields_); 310 NSCAssert(fieldsForOneof.count > 0, @"No fields for this oneof? (%s:%d)", name, hasIndex); 311 GPBOneofDescriptor *oneofDescriptor = [[GPBOneofDescriptor alloc] initWithName:name 312 fields:fieldsForOneof]; 313 [oneofs addObject:oneofDescriptor]; 314 [oneofDescriptor release]; 315 [fieldsForOneof release]; 316 } 317 oneofs_ = oneofs; 318} 319 320- (void)setupExtraTextInfo:(const char *)extraTextFormatInfo { 321 // Extra info is a compile time option, so skip the work if not needed. 322 if (extraTextFormatInfo) { 323 NSValue *extraInfoValue = [NSValue valueWithPointer:extraTextFormatInfo]; 324 for (GPBFieldDescriptor *fieldDescriptor in fields_) { 325 if (fieldDescriptor->description_->flags & GPBFieldTextFormatNameCustom) { 326 objc_setAssociatedObject(fieldDescriptor, &kTextFormatExtraValueKey, extraInfoValue, 327 OBJC_ASSOCIATION_RETAIN_NONATOMIC); 328 } 329 } 330 } 331} 332 333- (void)setupExtensionRanges:(const GPBExtensionRange *)ranges count:(int32_t)count { 334 extensionRanges_ = ranges; 335 extensionRangesCount_ = count; 336} 337 338- (void)setupContainingMessageClass:(Class)messageClass { 339 objc_setAssociatedObject(self, &kParentClassValueKey, messageClass, OBJC_ASSOCIATION_ASSIGN); 340} 341 342- (void)setupContainingMessageClassName:(const char *)msgClassName { 343 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30003, 344 time_to_remove_this_old_version_shim); 345 // Note: Only fetch the class here, can't send messages to it because 346 // that could cause cycles back to this class within +initialize if 347 // two messages have each other in fields (i.e. - they build a graph). 348 Class clazz = objc_getClass(msgClassName); 349 NSAssert(clazz, @"Class %s not defined", msgClassName); 350 [self setupContainingMessageClass:clazz]; 351} 352 353- (void)setupMessageClassNameSuffix:(NSString *)suffix { 354 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30007, 355 time_to_remove_this_old_version_shim); 356 if (suffix.length) { 357 objc_setAssociatedObject(self, &kClassNameSuffixKey, suffix, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 358 } 359} 360 361- (NSString *)name { 362 return NSStringFromClass(messageClass_); 363} 364 365- (GPBFileDescriptor *)file { 366 @synchronized(self) { 367 GPBFileDescriptor *result = objc_getAssociatedObject(self, &kFileDescriptorCacheKey); 368 if (!result) { 369#if defined(DEBUG) && DEBUG 370 NSAssert(fileDescription_ != NULL, @"Internal error in generation/startup"); 371#endif 372 // `package` and `prefix` can both be NULL if there wasn't one for the file. 373 NSString *package = fileDescription_->package ? @(fileDescription_->package) : @""; 374 if (fileDescription_->prefix) { 375 result = [[GPBFileDescriptor alloc] initWithPackage:package 376 objcPrefix:@(fileDescription_->prefix) 377 syntax:fileDescription_->syntax]; 378 379 } else { 380 result = [[GPBFileDescriptor alloc] initWithPackage:package 381 syntax:fileDescription_->syntax]; 382 } 383 objc_setAssociatedObject(result, &kFileDescriptorCacheKey, result, 384 OBJC_ASSOCIATION_RETAIN_NONATOMIC); 385 } 386 return result; 387 } 388} 389 390- (GPBDescriptor *)containingType { 391 Class parentClass = objc_getAssociatedObject(self, &kParentClassValueKey); 392 return [parentClass descriptor]; 393} 394 395- (NSString *)fullName { 396 GPBDescriptor *parent = self.containingType; 397 if (messageName_) { 398 if (parent) { 399 return [NSString stringWithFormat:@"%@.%@", parent.fullName, messageName_]; 400 } 401 if (fileDescription_->package) { 402 return [NSString stringWithFormat:@"%s.%@", fileDescription_->package, messageName_]; 403 } 404 return messageName_; 405 } 406 407 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30007, 408 time_to_remove_this_old_approach); 409 // NOTE: When this code path is removed, this also means this api can't return nil any more but 410 // that would be a breaking code change (not longer a Swift optional), so changing that will be 411 // harder. 412 413 NSString *className = NSStringFromClass(self.messageClass); 414 GPBFileDescriptor *file = self.file; 415 NSString *objcPrefix = file.objcPrefix; 416 if (objcPrefix && ![className hasPrefix:objcPrefix]) { 417 NSAssert(0, @"Class didn't have correct prefix? (%@ - %@)", className, objcPrefix); 418 return nil; 419 } 420 421 NSString *name = nil; 422 if (parent) { 423 NSString *parentClassName = NSStringFromClass(parent.messageClass); 424 // The generator will add _Class to avoid reserved words, drop it. 425 NSString *suffix = objc_getAssociatedObject(parent, &kClassNameSuffixKey); 426 if (suffix) { 427 if (![parentClassName hasSuffix:suffix]) { 428 NSAssert(0, @"ParentMessage class didn't have correct suffix? (%@ - %@)", className, 429 suffix); 430 return nil; 431 } 432 parentClassName = [parentClassName substringToIndex:(parentClassName.length - suffix.length)]; 433 } 434 NSString *parentPrefix = [parentClassName stringByAppendingString:@"_"]; 435 if (![className hasPrefix:parentPrefix]) { 436 NSAssert(0, @"Class didn't have the correct parent name prefix? (%@ - %@)", parentPrefix, 437 className); 438 return nil; 439 } 440 name = [className substringFromIndex:parentPrefix.length]; 441 } else { 442 name = [className substringFromIndex:objcPrefix.length]; 443 } 444 445 // The generator will add _Class to avoid reserved words, drop it. 446 NSString *suffix = objc_getAssociatedObject(self, &kClassNameSuffixKey); 447 if (suffix) { 448 if (![name hasSuffix:suffix]) { 449 NSAssert(0, @"Message class didn't have correct suffix? (%@ - %@)", name, suffix); 450 return nil; 451 } 452 name = [name substringToIndex:(name.length - suffix.length)]; 453 } 454 455 NSString *prefix = (parent != nil ? parent.fullName : file.package); 456 NSString *result; 457 if (prefix.length > 0) { 458 result = [NSString stringWithFormat:@"%@.%@", prefix, name]; 459 } else { 460 result = name; 461 } 462 return result; 463} 464 465- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber { 466 for (GPBFieldDescriptor *descriptor in fields_) { 467 if (GPBFieldNumber(descriptor) == fieldNumber) { 468 return descriptor; 469 } 470 } 471 return nil; 472} 473 474- (GPBFieldDescriptor *)fieldWithName:(NSString *)name { 475 for (GPBFieldDescriptor *descriptor in fields_) { 476 if ([descriptor.name isEqual:name]) { 477 return descriptor; 478 } 479 } 480 return nil; 481} 482 483- (GPBOneofDescriptor *)oneofWithName:(NSString *)name { 484 for (GPBOneofDescriptor *descriptor in oneofs_) { 485 if ([descriptor.name isEqual:name]) { 486 return descriptor; 487 } 488 } 489 return nil; 490} 491 492@end 493 494@implementation GPBFileDescriptor { 495 NSString *package_; 496 NSString *objcPrefix_; 497 GPBFileSyntax syntax_; 498} 499 500@synthesize package = package_; 501@synthesize objcPrefix = objcPrefix_; 502@synthesize syntax = syntax_; 503 504- (instancetype)initWithPackage:(NSString *)package 505 objcPrefix:(NSString *)objcPrefix 506 syntax:(GPBFileSyntax)syntax { 507 self = [super init]; 508 if (self) { 509 package_ = [package copy]; 510 objcPrefix_ = [objcPrefix copy]; 511 syntax_ = syntax; 512 } 513 return self; 514} 515 516- (instancetype)initWithPackage:(NSString *)package syntax:(GPBFileSyntax)syntax { 517 self = [super init]; 518 if (self) { 519 package_ = [package copy]; 520 syntax_ = syntax; 521 } 522 return self; 523} 524 525- (void)dealloc { 526 [package_ release]; 527 [objcPrefix_ release]; 528 [super dealloc]; 529} 530 531- (BOOL)isEqual:(id)other { 532 if (other == self) { 533 return YES; 534 } 535 if (![other isKindOfClass:[GPBFileDescriptor class]]) { 536 return NO; 537 } 538 GPBFileDescriptor *otherFile = other; 539 // objcPrefix can be nil, otherwise, straight up compare. 540 return (syntax_ == otherFile->syntax_ && [package_ isEqual:otherFile->package_] && 541 (objcPrefix_ == otherFile->objcPrefix_ || 542 (otherFile->objcPrefix_ && [objcPrefix_ isEqual:otherFile->objcPrefix_]))); 543} 544 545- (NSUInteger)hash { 546 // The prefix is recommended to be the same for a given package, so just hash 547 // the package. 548 return [package_ hash]; 549} 550 551- (instancetype)copyWithZone:(__unused NSZone *)zone { 552 // Immutable. 553 return [self retain]; 554} 555 556@end 557 558@implementation GPBOneofDescriptor 559 560@synthesize fields = fields_; 561 562- (instancetype)initWithName:(const char *)name fields:(NSArray *)fields { 563 self = [super init]; 564 if (self) { 565 name_ = name; 566 fields_ = [fields retain]; 567 for (GPBFieldDescriptor *fieldDesc in fields) { 568 fieldDesc->containingOneof_ = self; 569 } 570 } 571 return self; 572} 573 574- (void)dealloc { 575 [fields_ release]; 576 [super dealloc]; 577} 578 579// No need to provide -hash/-isEqual: as the instances are singletons and the 580// default from NSObject is fine. 581- (instancetype)copyWithZone:(__unused NSZone *)zone { 582 // Immutable. 583 return [self retain]; 584} 585 586- (NSString *)name { 587 return (NSString *_Nonnull)@(name_); 588} 589 590- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber { 591 for (GPBFieldDescriptor *descriptor in fields_) { 592 if (GPBFieldNumber(descriptor) == fieldNumber) { 593 return descriptor; 594 } 595 } 596 return nil; 597} 598 599- (GPBFieldDescriptor *)fieldWithName:(NSString *)name { 600 for (GPBFieldDescriptor *descriptor in fields_) { 601 if ([descriptor.name isEqual:name]) { 602 return descriptor; 603 } 604 } 605 return nil; 606} 607 608@end 609 610uint32_t GPBFieldTag(GPBFieldDescriptor *self) { 611 GPBMessageFieldDescription *description = self->description_; 612 GPBWireFormat format; 613 if ((description->flags & GPBFieldMapKeyMask) != 0) { 614 // Maps are repeated messages on the wire. 615 format = GPBWireFormatForType(GPBDataTypeMessage, NO); 616 } else { 617 format = 618 GPBWireFormatForType(description->dataType, ((description->flags & GPBFieldPacked) != 0)); 619 } 620 return GPBWireFormatMakeTag(description->number, format); 621} 622 623uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { 624 GPBMessageFieldDescription *description = self->description_; 625 NSCAssert((description->flags & GPBFieldRepeated) != 0, @"Only valid on repeated fields"); 626 GPBWireFormat format = 627 GPBWireFormatForType(description->dataType, ((description->flags & GPBFieldPacked) == 0)); 628 return GPBWireFormatMakeTag(description->number, format); 629} 630 631@implementation GPBFieldDescriptor { 632 GPBGenericValue defaultValue_; 633 634 // Message ivars 635 Class msgClass_; 636 637 // Enum ivars. 638 GPBEnumDescriptor *enumDescriptor_; 639} 640 641@synthesize msgClass = msgClass_; 642@synthesize containingOneof = containingOneof_; 643 644- (instancetype)initWithFieldDescription:(void *)description 645 descriptorFlags:(GPBDescriptorInitializationFlags)descriptorFlags { 646 if ((self = [super init])) { 647 BOOL includesDefault = 648 (descriptorFlags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0; 649 GPBMessageFieldDescription *coreDesc; 650 if (includesDefault) { 651 coreDesc = &(((GPBMessageFieldDescriptionWithDefault *)description)->core); 652 } else { 653 coreDesc = description; 654 } 655 description_ = coreDesc; 656 657 GPBDataType dataType = coreDesc->dataType; 658 BOOL isMessage = GPBDataTypeIsMessage(dataType); 659 660 // Extra type specific data. 661 if (isMessage) { 662 // Note: Only fetch the class here, can't send messages to it because 663 // that could cause cycles back to this class within +initialize if 664 // two messages have each other in fields (i.e. - they build a graph). 665 msgClass_ = coreDesc->dataTypeSpecific.clazz; 666 } else if (dataType == GPBDataTypeEnum) { 667 enumDescriptor_ = coreDesc->dataTypeSpecific.enumDescFunc(); 668#if defined(DEBUG) && DEBUG 669 NSAssert((coreDesc->flags & GPBFieldHasEnumDescriptor) != 0, 670 @"Field must have GPBFieldHasEnumDescriptor set"); 671#endif // DEBUG 672 } 673 674 // Non map<>/repeated fields can have defaults in proto2 syntax. 675 BOOL isMapOrArray = GPBFieldIsMapOrArray(self); 676 if (!isMapOrArray && includesDefault) { 677 defaultValue_ = ((GPBMessageFieldDescriptionWithDefault *)description)->defaultValue; 678 if (dataType == GPBDataTypeBytes) { 679 // Data stored as a length prefixed (network byte order) c-string in 680 // descriptor structure. 681 const uint8_t *bytes = (const uint8_t *)defaultValue_.valueData; 682 if (bytes) { 683 uint32_t length; 684 memcpy(&length, bytes, sizeof(length)); 685 length = ntohl(length); 686 bytes += sizeof(length); 687 defaultValue_.valueData = [[NSData alloc] initWithBytes:bytes length:length]; 688 } 689 } 690 } 691 } 692 return self; 693} 694 695- (void)dealloc { 696 if (description_->dataType == GPBDataTypeBytes && !(description_->flags & GPBFieldRepeated)) { 697 [defaultValue_.valueData release]; 698 } 699 [super dealloc]; 700} 701 702// No need to provide -hash/-isEqual: as the instances are singletons and the 703// default from NSObject is fine. 704- (instancetype)copyWithZone:(__unused NSZone *)zone { 705 // Immutable. 706 return [self retain]; 707} 708 709- (GPBDataType)dataType { 710 return description_->dataType; 711} 712 713- (BOOL)hasDefaultValue { 714 return (description_->flags & GPBFieldHasDefaultValue) != 0; 715} 716 717- (uint32_t)number { 718 return description_->number; 719} 720 721- (NSString *)name { 722 return (NSString *_Nonnull)@(description_->name); 723} 724 725- (BOOL)isRequired { 726 return (description_->flags & GPBFieldRequired) != 0; 727} 728 729- (BOOL)isOptional { 730 return (description_->flags & GPBFieldOptional) != 0; 731} 732 733- (GPBFieldType)fieldType { 734 GPBFieldFlags flags = description_->flags; 735 if ((flags & GPBFieldRepeated) != 0) { 736 return GPBFieldTypeRepeated; 737 } else if ((flags & GPBFieldMapKeyMask) != 0) { 738 return GPBFieldTypeMap; 739 } else { 740 return GPBFieldTypeSingle; 741 } 742} 743 744- (GPBDataType)mapKeyDataType { 745 switch (description_->flags & GPBFieldMapKeyMask) { 746 case GPBFieldMapKeyInt32: 747 return GPBDataTypeInt32; 748 case GPBFieldMapKeyInt64: 749 return GPBDataTypeInt64; 750 case GPBFieldMapKeyUInt32: 751 return GPBDataTypeUInt32; 752 case GPBFieldMapKeyUInt64: 753 return GPBDataTypeUInt64; 754 case GPBFieldMapKeySInt32: 755 return GPBDataTypeSInt32; 756 case GPBFieldMapKeySInt64: 757 return GPBDataTypeSInt64; 758 case GPBFieldMapKeyFixed32: 759 return GPBDataTypeFixed32; 760 case GPBFieldMapKeyFixed64: 761 return GPBDataTypeFixed64; 762 case GPBFieldMapKeySFixed32: 763 return GPBDataTypeSFixed32; 764 case GPBFieldMapKeySFixed64: 765 return GPBDataTypeSFixed64; 766 case GPBFieldMapKeyBool: 767 return GPBDataTypeBool; 768 case GPBFieldMapKeyString: 769 return GPBDataTypeString; 770 771 default: 772 NSAssert(0, @"Not a map type"); 773 return GPBDataTypeInt32; // For lack of anything better. 774 } 775} 776 777- (BOOL)isPackable { 778 return (description_->flags & GPBFieldPacked) != 0; 779} 780 781- (BOOL)isValidEnumValue:(int32_t)value { 782 NSAssert(description_->dataType == GPBDataTypeEnum, @"Field Must be of type GPBDataTypeEnum"); 783 return enumDescriptor_.enumVerifier(value); 784} 785 786- (GPBEnumDescriptor *)enumDescriptor { 787 return enumDescriptor_; 788} 789 790- (GPBGenericValue)defaultValue { 791 // Depends on the fact that defaultValue_ is initialized either to "0/nil" or 792 // to an actual defaultValue in our initializer. 793 GPBGenericValue value = defaultValue_; 794 795 if (!(description_->flags & GPBFieldRepeated)) { 796 // We special handle data and strings. If they are nil, we replace them 797 // with empty string/empty data. 798 GPBDataType type = description_->dataType; 799 if (type == GPBDataTypeBytes && value.valueData == nil) { 800 value.valueData = GPBEmptyNSData(); 801 } else if (type == GPBDataTypeString && value.valueString == nil) { 802 value.valueString = @""; 803 } 804 } 805 return value; 806} 807 808- (NSString *)textFormatName { 809 if ((description_->flags & GPBFieldTextFormatNameCustom) != 0) { 810 NSValue *extraInfoValue = objc_getAssociatedObject(self, &kTextFormatExtraValueKey); 811 // Support can be left out at generation time. 812 if (!extraInfoValue) { 813 return nil; 814 } 815 const uint8_t *extraTextFormatInfo = [extraInfoValue pointerValue]; 816 return GPBDecodeTextFormatName(extraTextFormatInfo, GPBFieldNumber(self), self.name); 817 } 818 819 // The logic here has to match SetCommonFieldVariables() from 820 // objectivec/field.cc in the proto compiler. 821 NSString *name = self.name; 822 NSUInteger len = [name length]; 823 824 // Remove the "_p" added to reserved names. 825 if ([name hasSuffix:@"_p"]) { 826 name = [name substringToIndex:(len - 2)]; 827 len = [name length]; 828 } 829 830 // Remove "Array" from the end for repeated fields. 831 if (((description_->flags & GPBFieldRepeated) != 0) && [name hasSuffix:@"Array"]) { 832 name = [name substringToIndex:(len - 5)]; 833 len = [name length]; 834 } 835 836 // Groups vs. other fields. 837 if (description_->dataType == GPBDataTypeGroup) { 838 // Just capitalize the first letter. 839 unichar firstChar = [name characterAtIndex:0]; 840 if (firstChar >= 'a' && firstChar <= 'z') { 841 NSString *firstCharString = 842 [NSString stringWithFormat:@"%C", (unichar)(firstChar - 'a' + 'A')]; 843 NSString *result = [name stringByReplacingCharactersInRange:NSMakeRange(0, 1) 844 withString:firstCharString]; 845 return result; 846 } 847 return name; 848 849 } else { 850 // Undo the CamelCase. 851 NSMutableString *result = [NSMutableString stringWithCapacity:len]; 852 for (uint32_t i = 0; i < len; i++) { 853 unichar c = [name characterAtIndex:i]; 854 if (c >= 'A' && c <= 'Z') { 855 if (i > 0) { 856 [result appendFormat:@"_%C", (unichar)(c - 'A' + 'a')]; 857 } else { 858 [result appendFormat:@"%C", c]; 859 } 860 } else { 861 [result appendFormat:@"%C", c]; 862 } 863 } 864 return result; 865 } 866} 867 868@end 869 870@implementation GPBEnumDescriptor { 871 NSString *name_; 872 // valueNames_ is a single c string with all of the value names appended 873 // together, each null terminated. -calcValueNameOffsets fills in 874 // nameOffsets_ with the offsets to allow quicker access to the individual 875 // names. 876 const char *valueNames_; 877 const int32_t *values_; 878 GPBEnumValidationFunc enumVerifier_; 879 const uint8_t *extraTextFormatInfo_; 880 uint32_t *nameOffsets_; 881 uint32_t valueCount_; 882 uint32_t flags_; 883} 884 885@synthesize name = name_; 886@synthesize enumVerifier = enumVerifier_; 887 888+ (instancetype)allocDescriptorForName:(NSString *)name 889 valueNames:(const char *)valueNames 890 values:(const int32_t *)values 891 count:(uint32_t)valueCount 892 enumVerifier:(GPBEnumValidationFunc)enumVerifier 893 flags:(GPBEnumDescriptorInitializationFlags)flags { 894 // Compute the unknown flags by this version of the runtime and then check the passed in flags 895 // (from the generated code) to detect when sources from a newer version are being used with an 896 // older runtime. 897 GPBEnumDescriptorInitializationFlags unknownFlags = 898 ~(GPBEnumDescriptorInitializationFlag_IsClosed); 899 if ((flags & unknownFlags) != 0) { 900 GPBRuntimeMatchFailure(); 901 } 902 GPBEnumDescriptor *descriptor = [[self alloc] initWithName:name 903 valueNames:valueNames 904 values:values 905 count:valueCount 906 enumVerifier:enumVerifier 907 flags:flags]; 908 return descriptor; 909} 910 911+ (instancetype)allocDescriptorForName:(NSString *)name 912 valueNames:(const char *)valueNames 913 values:(const int32_t *)values 914 count:(uint32_t)valueCount 915 enumVerifier:(GPBEnumValidationFunc)enumVerifier 916 flags:(GPBEnumDescriptorInitializationFlags)flags 917 extraTextFormatInfo:(const char *)extraTextFormatInfo { 918 // Call the common case. 919 GPBEnumDescriptor *descriptor = [self allocDescriptorForName:name 920 valueNames:valueNames 921 values:values 922 count:valueCount 923 enumVerifier:enumVerifier 924 flags:flags]; 925 // Set the extra info. 926 descriptor->extraTextFormatInfo_ = (const uint8_t *)extraTextFormatInfo; 927 return descriptor; 928} 929 930+ (instancetype)allocDescriptorForName:(NSString *)name 931 valueNames:(const char *)valueNames 932 values:(const int32_t *)values 933 count:(uint32_t)valueCount 934 enumVerifier:(GPBEnumValidationFunc)enumVerifier { 935 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30005, 936 time_to_remove_this_old_version_shim); 937 return [self allocDescriptorForName:name 938 valueNames:valueNames 939 values:values 940 count:valueCount 941 enumVerifier:enumVerifier 942 flags:GPBEnumDescriptorInitializationFlag_None]; 943} 944 945+ (instancetype)allocDescriptorForName:(NSString *)name 946 valueNames:(const char *)valueNames 947 values:(const int32_t *)values 948 count:(uint32_t)valueCount 949 enumVerifier:(GPBEnumValidationFunc)enumVerifier 950 extraTextFormatInfo:(const char *)extraTextFormatInfo { 951 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30005, 952 time_to_remove_this_old_version_shim); 953 return [self allocDescriptorForName:name 954 valueNames:valueNames 955 values:values 956 count:valueCount 957 enumVerifier:enumVerifier 958 flags:GPBEnumDescriptorInitializationFlag_None 959 extraTextFormatInfo:extraTextFormatInfo]; 960} 961 962- (instancetype)initWithName:(NSString *)name 963 valueNames:(const char *)valueNames 964 values:(const int32_t *)values 965 count:(uint32_t)valueCount 966 enumVerifier:(GPBEnumValidationFunc)enumVerifier 967 flags:(GPBEnumDescriptorInitializationFlags)flags { 968 if ((self = [super init])) { 969 name_ = [name copy]; 970 valueNames_ = valueNames; 971 values_ = values; 972 valueCount_ = valueCount; 973 enumVerifier_ = enumVerifier; 974 flags_ = flags; 975 } 976 return self; 977} 978 979- (void)dealloc { 980 [name_ release]; 981 if (nameOffsets_) free(nameOffsets_); 982 [super dealloc]; 983} 984 985// No need to provide -hash/-isEqual: as the instances are singletons and the 986// default from NSObject is fine. 987- (instancetype)copyWithZone:(__unused NSZone *)zone { 988 // Immutable. 989 return [self retain]; 990} 991 992- (BOOL)isClosed { 993 return (flags_ & GPBEnumDescriptorInitializationFlag_IsClosed) != 0; 994} 995 996- (void)calcValueNameOffsets { 997 @synchronized(self) { 998 if (nameOffsets_ != NULL) { 999 return; 1000 } 1001 uint32_t *offsets = malloc(valueCount_ * sizeof(uint32_t)); 1002 if (!offsets) return; 1003 const char *scan = valueNames_; 1004 for (uint32_t i = 0; i < valueCount_; ++i) { 1005 offsets[i] = (uint32_t)(scan - valueNames_); 1006 while (*scan != '\0') ++scan; 1007 ++scan; // Step over the null. 1008 } 1009 nameOffsets_ = offsets; 1010 } 1011} 1012 1013- (NSString *)enumNameForValue:(int32_t)number { 1014 for (uint32_t i = 0; i < valueCount_; ++i) { 1015 if (values_[i] == number) { 1016 return [self getEnumNameForIndex:i]; 1017 } 1018 } 1019 return nil; 1020} 1021 1022- (BOOL)getValue:(int32_t *)outValue forEnumName:(NSString *)name { 1023 // Must have the prefix. 1024 NSUInteger prefixLen = name_.length + 1; 1025 if ((name.length <= prefixLen) || ![name hasPrefix:name_] || 1026 ([name characterAtIndex:prefixLen - 1] != '_')) { 1027 return NO; 1028 } 1029 1030 // Skip over the prefix. 1031 const char *nameAsCStr = [name UTF8String]; 1032 nameAsCStr += prefixLen; 1033 1034 [self calcValueNameOffsets]; 1035 if (nameOffsets_ == NULL) return NO; 1036 1037 // Find it. 1038 for (uint32_t i = 0; i < valueCount_; ++i) { 1039 const char *valueName = valueNames_ + nameOffsets_[i]; 1040 if (strcmp(nameAsCStr, valueName) == 0) { 1041 if (outValue) { 1042 *outValue = values_[i]; 1043 } 1044 return YES; 1045 } 1046 } 1047 return NO; 1048} 1049 1050- (BOOL)getValue:(int32_t *)outValue forEnumTextFormatName:(NSString *)textFormatName { 1051 [self calcValueNameOffsets]; 1052 if (nameOffsets_ == NULL) return NO; 1053 1054 for (uint32_t i = 0; i < valueCount_; ++i) { 1055 NSString *valueTextFormatName = [self getEnumTextFormatNameForIndex:i]; 1056 if ([valueTextFormatName isEqual:textFormatName]) { 1057 if (outValue) { 1058 *outValue = values_[i]; 1059 } 1060 return YES; 1061 } 1062 } 1063 return NO; 1064} 1065 1066- (NSString *)textFormatNameForValue:(int32_t)number { 1067 // Find the EnumValue descriptor and its index. 1068 BOOL foundIt = NO; 1069 uint32_t valueDescriptorIndex; 1070 for (valueDescriptorIndex = 0; valueDescriptorIndex < valueCount_; ++valueDescriptorIndex) { 1071 if (values_[valueDescriptorIndex] == number) { 1072 foundIt = YES; 1073 break; 1074 } 1075 } 1076 1077 if (!foundIt) { 1078 return nil; 1079 } 1080 return [self getEnumTextFormatNameForIndex:valueDescriptorIndex]; 1081} 1082 1083- (uint32_t)enumNameCount { 1084 return valueCount_; 1085} 1086 1087- (NSString *)getEnumNameForIndex:(uint32_t)index { 1088 [self calcValueNameOffsets]; 1089 if (nameOffsets_ == NULL) return nil; 1090 1091 if (index >= valueCount_) { 1092 return nil; 1093 } 1094 const char *valueName = valueNames_ + nameOffsets_[index]; 1095 NSString *fullName = [NSString stringWithFormat:@"%@_%s", name_, valueName]; 1096 return fullName; 1097} 1098 1099- (NSString *)getEnumTextFormatNameForIndex:(uint32_t)index { 1100 [self calcValueNameOffsets]; 1101 if (nameOffsets_ == NULL) return nil; 1102 1103 if (index >= valueCount_) { 1104 return nil; 1105 } 1106 NSString *result = nil; 1107 // Naming adds an underscore between enum name and value name, skip that also. 1108 const char *valueName = valueNames_ + nameOffsets_[index]; 1109 NSString *shortName = @(valueName); 1110 1111 // See if it is in the map of special format handling. 1112 if (extraTextFormatInfo_) { 1113 result = GPBDecodeTextFormatName(extraTextFormatInfo_, (int32_t)index, shortName); 1114 } 1115 // Logic here needs to match what objectivec/enum.cc does in the proto 1116 // compiler. 1117 if (result == nil) { 1118 NSUInteger len = [shortName length]; 1119 NSMutableString *worker = [NSMutableString stringWithCapacity:len]; 1120 for (NSUInteger i = 0; i < len; i++) { 1121 unichar c = [shortName characterAtIndex:i]; 1122 if (i > 0 && c >= 'A' && c <= 'Z') { 1123 [worker appendString:@"_"]; 1124 } 1125 [worker appendFormat:@"%c", toupper((char)c)]; 1126 } 1127 result = worker; 1128 } 1129 return result; 1130} 1131 1132@end 1133 1134@implementation GPBExtensionDescriptor { 1135 GPBGenericValue defaultValue_; 1136} 1137 1138- (instancetype)initWithExtensionDescription:(GPBExtensionDescription *)desc 1139 usesClassRefs:(BOOL)usesClassRefs { 1140 // Compute the unknown options by this version of the runtime and then check the passed in 1141 // descriptor's options (from the generated code) to detect when sources from a newer version are 1142 // being used with an older runtime. 1143 GPBExtensionOptions unknownOptions = 1144 ~(GPBExtensionRepeated | GPBExtensionPacked | GPBExtensionSetWireFormat); 1145 if ((desc->options & unknownOptions) != 0) { 1146 GPBRuntimeMatchFailure(); 1147 } 1148 1149#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS) 1150 NSAssert(usesClassRefs, @"Internal error: all extensions should have class refs"); 1151 1152 // These are also checked by the generator. 1153 if ((desc->options & GPBExtensionSetWireFormat) != 0) { 1154 NSAssert(desc->dataType == GPBDataTypeMessage, 1155 @"Internal error: If a MessageSet extension is set, the data type must be a message."); 1156 NSAssert((desc->options & GPBExtensionRepeated) == 0, 1157 @"Internal Error: MessageSet extension can't be repeated."); 1158 // NOTE: Could also check that the extended class is a MessageSet, but that would force the 1159 // ObjC runtime to start up that class and that isn't desirable here. 1160 } 1161#endif 1162 1163 if ((self = [super init])) { 1164 description_ = desc; 1165 1166 GPBDataType type = description_->dataType; 1167 if (type == GPBDataTypeBytes) { 1168 // Data stored as a length prefixed c-string in descriptor records. 1169 const uint8_t *bytes = (const uint8_t *)description_->defaultValue.valueData; 1170 if (bytes) { 1171 uint32_t length; 1172 memcpy(&length, bytes, sizeof(length)); 1173 // The length is stored in network byte order. 1174 length = ntohl(length); 1175 bytes += sizeof(length); 1176 defaultValue_.valueData = [[NSData alloc] initWithBytes:bytes length:length]; 1177 } 1178 } else if (type == GPBDataTypeMessage || type == GPBDataTypeGroup) { 1179 // The default is looked up in -defaultValue instead since extensions 1180 // aren't common, we avoid the hit startup hit and it avoids initialization 1181 // order issues. 1182 } else { 1183 defaultValue_ = description_->defaultValue; 1184 } 1185 } 1186 return self; 1187} 1188 1189- (instancetype)initWithExtensionDescription:(GPBExtensionDescription *)desc { 1190 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30003, 1191 time_to_remove_this_old_version_shim); 1192 1193 const char *className = desc->messageOrGroupClass.name; 1194 if (className) { 1195 Class clazz = objc_lookUpClass(className); 1196 NSAssert(clazz != Nil, @"Class %s not defined", className); 1197 desc->messageOrGroupClass.clazz = clazz; 1198 } 1199 1200 const char *extendedClassName = desc->extendedClass.name; 1201 if (extendedClassName) { 1202 Class clazz = objc_lookUpClass(extendedClassName); 1203 NSAssert(clazz, @"Class %s not defined", extendedClassName); 1204 desc->extendedClass.clazz = clazz; 1205 } 1206 1207 return [self initWithExtensionDescription:desc usesClassRefs:YES]; 1208} 1209 1210- (void)dealloc { 1211 if ((description_->dataType == GPBDataTypeBytes) && !GPBExtensionIsRepeated(description_)) { 1212 [defaultValue_.valueData release]; 1213 } 1214 [super dealloc]; 1215} 1216 1217// No need to provide -hash/-isEqual: as the instances are singletons and the 1218// default from NSObject is fine. 1219- (instancetype)copyWithZone:(__unused NSZone *)zone { 1220 // Immutable. 1221 return [self retain]; 1222} 1223 1224- (NSString *)singletonName { 1225 return (NSString *_Nonnull)@(description_->singletonName); 1226} 1227 1228- (const char *)singletonNameC { 1229 return description_->singletonName; 1230} 1231 1232- (uint32_t)fieldNumber { 1233 return description_->fieldNumber; 1234} 1235 1236- (GPBDataType)dataType { 1237 return description_->dataType; 1238} 1239 1240- (GPBWireFormat)wireType { 1241 return GPBWireFormatForType(description_->dataType, GPBExtensionIsPacked(description_)); 1242} 1243 1244- (GPBWireFormat)alternateWireType { 1245 NSAssert(GPBExtensionIsRepeated(description_), @"Only valid on repeated extensions"); 1246 return GPBWireFormatForType(description_->dataType, !GPBExtensionIsPacked(description_)); 1247} 1248 1249- (BOOL)isRepeated { 1250 return GPBExtensionIsRepeated(description_); 1251} 1252 1253- (BOOL)isPackable { 1254 return GPBExtensionIsPacked(description_); 1255} 1256 1257- (Class)msgClass { 1258 return description_->messageOrGroupClass.clazz; 1259} 1260 1261- (Class)containingMessageClass { 1262 return description_->extendedClass.clazz; 1263} 1264 1265- (GPBEnumDescriptor *)enumDescriptor { 1266 if (description_->dataType == GPBDataTypeEnum) { 1267 GPBEnumDescriptor *enumDescriptor = description_->enumDescriptorFunc(); 1268 return enumDescriptor; 1269 } 1270 return nil; 1271} 1272 1273- (id)defaultValue { 1274 if (GPBExtensionIsRepeated(description_)) { 1275 return nil; 1276 } 1277 1278 switch (description_->dataType) { 1279 case GPBDataTypeBool: 1280 return @(defaultValue_.valueBool); 1281 case GPBDataTypeFloat: 1282 return @(defaultValue_.valueFloat); 1283 case GPBDataTypeDouble: 1284 return @(defaultValue_.valueDouble); 1285 case GPBDataTypeInt32: 1286 case GPBDataTypeSInt32: 1287 case GPBDataTypeEnum: 1288 case GPBDataTypeSFixed32: 1289 return @(defaultValue_.valueInt32); 1290 case GPBDataTypeInt64: 1291 case GPBDataTypeSInt64: 1292 case GPBDataTypeSFixed64: 1293 return @(defaultValue_.valueInt64); 1294 case GPBDataTypeUInt32: 1295 case GPBDataTypeFixed32: 1296 return @(defaultValue_.valueUInt32); 1297 case GPBDataTypeUInt64: 1298 case GPBDataTypeFixed64: 1299 return @(defaultValue_.valueUInt64); 1300 case GPBDataTypeBytes: 1301 // Like message fields, the default is zero length data. 1302 return (defaultValue_.valueData ? defaultValue_.valueData : GPBEmptyNSData()); 1303 case GPBDataTypeString: 1304 // Like message fields, the default is zero length string. 1305 return (defaultValue_.valueString ? defaultValue_.valueString : @""); 1306 case GPBDataTypeGroup: 1307 case GPBDataTypeMessage: 1308 return nil; 1309 } 1310} 1311 1312- (NSComparisonResult)compareByFieldNumber:(GPBExtensionDescriptor *)other { 1313 int32_t selfNumber = description_->fieldNumber; 1314 int32_t otherNumber = other->description_->fieldNumber; 1315 if (selfNumber < otherNumber) { 1316 return NSOrderedAscending; 1317 } else if (selfNumber == otherNumber) { 1318 return NSOrderedSame; 1319 } else { 1320 return NSOrderedDescending; 1321 } 1322} 1323 1324@end 1325 1326#pragma clang diagnostic pop 1327