• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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