blob: 4b39c63b36a7c033844286fc925f4a35b65ec038 [file] [log] [blame]
Brian Silverman9c614bc2016-02-15 20:20:02 -05001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31#import "GPBDescriptor_PackagePrivate.h"
32
33#import <objc/runtime.h>
34
35#import "GPBUtilities_PackagePrivate.h"
36#import "GPBWireFormat.h"
37#import "GPBMessage_PackagePrivate.h"
Brian Silverman9c614bc2016-02-15 20:20:02 -050038
Austin Schuh40c16522018-10-28 20:27:54 -070039// Direct access is use for speed, to avoid even internally declaring things
40// read/write, etc. The warning is enabled in the project to ensure code calling
41// protos can turn on -Wdirect-ivar-access without issues.
42#pragma clang diagnostic push
43#pragma clang diagnostic ignored "-Wdirect-ivar-access"
44
45// The addresses of these variables are used as keys for objc_getAssociatedObject.
Brian Silverman9c614bc2016-02-15 20:20:02 -050046static const char kTextFormatExtraValueKey = 0;
Austin Schuh40c16522018-10-28 20:27:54 -070047static const char kParentClassNameValueKey = 0;
48static const char kClassNameSuffixKey = 0;
Brian Silverman9c614bc2016-02-15 20:20:02 -050049
50// Utility function to generate selectors on the fly.
51static SEL SelFromStrings(const char *prefix, const char *middle,
52 const char *suffix, BOOL takesArg) {
53 if (prefix == NULL && suffix == NULL && !takesArg) {
54 return sel_getUid(middle);
55 }
56 const size_t prefixLen = prefix != NULL ? strlen(prefix) : 0;
57 const size_t middleLen = strlen(middle);
58 const size_t suffixLen = suffix != NULL ? strlen(suffix) : 0;
59 size_t totalLen =
60 prefixLen + middleLen + suffixLen + 1; // include space for null on end.
61 if (takesArg) {
62 totalLen += 1;
63 }
64 char buffer[totalLen];
65 if (prefix != NULL) {
66 memcpy(buffer, prefix, prefixLen);
67 memcpy(buffer + prefixLen, middle, middleLen);
68 buffer[prefixLen] = (char)toupper(buffer[prefixLen]);
69 } else {
70 memcpy(buffer, middle, middleLen);
71 }
72 if (suffix != NULL) {
73 memcpy(buffer + prefixLen + middleLen, suffix, suffixLen);
74 }
75 if (takesArg) {
76 buffer[totalLen - 2] = ':';
77 }
78 // Always null terminate it.
79 buffer[totalLen - 1] = 0;
80
81 SEL result = sel_getUid(buffer);
82 return result;
83}
84
85static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
86 NSArray *allMessageFields)
87 __attribute__((ns_returns_retained));
88
89static NSArray *NewFieldsArrayForHasIndex(int hasIndex,
90 NSArray *allMessageFields) {
91 NSMutableArray *result = [[NSMutableArray alloc] init];
92 for (GPBFieldDescriptor *fieldDesc in allMessageFields) {
93 if (fieldDesc->description_->hasIndex == hasIndex) {
94 [result addObject:fieldDesc];
95 }
96 }
97 return result;
98}
99
100@implementation GPBDescriptor {
101 Class messageClass_;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500102 GPBFileDescriptor *file_;
103 BOOL wireFormat_;
104}
105
106@synthesize messageClass = messageClass_;
107@synthesize fields = fields_;
108@synthesize oneofs = oneofs_;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500109@synthesize extensionRanges = extensionRanges_;
110@synthesize extensionRangesCount = extensionRangesCount_;
111@synthesize file = file_;
112@synthesize wireFormat = wireFormat_;
113
114+ (instancetype)
115 allocDescriptorForClass:(Class)messageClass
116 rootClass:(Class)rootClass
117 file:(GPBFileDescriptor *)file
Austin Schuh40c16522018-10-28 20:27:54 -0700118 fields:(void *)fieldDescriptions
119 fieldCount:(uint32_t)fieldCount
120 storageSize:(uint32_t)storageSize
121 flags:(GPBDescriptorInitializationFlags)flags {
122 // The rootClass is no longer used, but it is passed in to ensure it
123 // was started up during initialization also.
124 (void)rootClass;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500125 NSMutableArray *fields = nil;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500126 GPBFileSyntax syntax = file.syntax;
Austin Schuh40c16522018-10-28 20:27:54 -0700127 BOOL fieldsIncludeDefault =
128 (flags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0;
129
130 void *desc;
131 for (uint32_t i = 0; i < fieldCount; ++i) {
Brian Silverman9c614bc2016-02-15 20:20:02 -0500132 if (fields == nil) {
133 fields = [[NSMutableArray alloc] initWithCapacity:fieldCount];
134 }
Austin Schuh40c16522018-10-28 20:27:54 -0700135 // Need correctly typed pointer for array indexing below to work.
136 if (fieldsIncludeDefault) {
137 GPBMessageFieldDescriptionWithDefault *fieldDescWithDefault = fieldDescriptions;
138 desc = &(fieldDescWithDefault[i]);
139 } else {
140 GPBMessageFieldDescription *fieldDesc = fieldDescriptions;
141 desc = &(fieldDesc[i]);
142 }
143 GPBFieldDescriptor *fieldDescriptor =
144 [[GPBFieldDescriptor alloc] initWithFieldDescription:desc
145 includesDefault:fieldsIncludeDefault
146 syntax:syntax];
Brian Silverman9c614bc2016-02-15 20:20:02 -0500147 [fields addObject:fieldDescriptor];
148 [fieldDescriptor release];
149 }
Brian Silverman9c614bc2016-02-15 20:20:02 -0500150
Austin Schuh40c16522018-10-28 20:27:54 -0700151 BOOL wireFormat = (flags & GPBDescriptorInitializationFlag_WireFormat) != 0;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500152 GPBDescriptor *descriptor = [[self alloc] initWithClass:messageClass
153 file:file
154 fields:fields
Brian Silverman9c614bc2016-02-15 20:20:02 -0500155 storageSize:storageSize
156 wireFormat:wireFormat];
157 [fields release];
Brian Silverman9c614bc2016-02-15 20:20:02 -0500158 return descriptor;
159}
160
161- (instancetype)initWithClass:(Class)messageClass
162 file:(GPBFileDescriptor *)file
163 fields:(NSArray *)fields
Austin Schuh40c16522018-10-28 20:27:54 -0700164 storageSize:(uint32_t)storageSize
Brian Silverman9c614bc2016-02-15 20:20:02 -0500165 wireFormat:(BOOL)wireFormat {
166 if ((self = [super init])) {
167 messageClass_ = messageClass;
168 file_ = file;
169 fields_ = [fields retain];
Brian Silverman9c614bc2016-02-15 20:20:02 -0500170 storageSize_ = storageSize;
171 wireFormat_ = wireFormat;
172 }
173 return self;
174}
175
176- (void)dealloc {
177 [fields_ release];
178 [oneofs_ release];
Brian Silverman9c614bc2016-02-15 20:20:02 -0500179 [super dealloc];
180}
181
Austin Schuh40c16522018-10-28 20:27:54 -0700182- (void)setupOneofs:(const char **)oneofNames
183 count:(uint32_t)count
184 firstHasIndex:(int32_t)firstHasIndex {
185 NSCAssert(firstHasIndex < 0, @"Should always be <0");
186 NSMutableArray *oneofs = [[NSMutableArray alloc] initWithCapacity:count];
187 for (uint32_t i = 0, hasIndex = firstHasIndex; i < count; ++i, --hasIndex) {
188 const char *name = oneofNames[i];
189 NSArray *fieldsForOneof = NewFieldsArrayForHasIndex(hasIndex, fields_);
190 NSCAssert(fieldsForOneof.count > 0,
191 @"No fields for this oneof? (%s:%d)", name, hasIndex);
192 GPBOneofDescriptor *oneofDescriptor =
193 [[GPBOneofDescriptor alloc] initWithName:name fields:fieldsForOneof];
194 [oneofs addObject:oneofDescriptor];
195 [oneofDescriptor release];
196 [fieldsForOneof release];
197 }
198 oneofs_ = oneofs;
199}
200
201- (void)setupExtraTextInfo:(const char *)extraTextFormatInfo {
202 // Extra info is a compile time option, so skip the work if not needed.
203 if (extraTextFormatInfo) {
204 NSValue *extraInfoValue = [NSValue valueWithPointer:extraTextFormatInfo];
205 for (GPBFieldDescriptor *fieldDescriptor in fields_) {
206 if (fieldDescriptor->description_->flags & GPBFieldTextFormatNameCustom) {
207 objc_setAssociatedObject(fieldDescriptor, &kTextFormatExtraValueKey,
208 extraInfoValue,
209 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
210 }
211 }
212 }
213}
214
215- (void)setupExtensionRanges:(const GPBExtensionRange *)ranges count:(int32_t)count {
216 extensionRanges_ = ranges;
217 extensionRangesCount_ = count;
218}
219
220- (void)setupContainingMessageClassName:(const char *)msgClassName {
221 // Note: Only fetch the class here, can't send messages to it because
222 // that could cause cycles back to this class within +initialize if
223 // two messages have each other in fields (i.e. - they build a graph).
224 NSAssert(objc_getClass(msgClassName), @"Class %s not defined", msgClassName);
225 NSValue *parentNameValue = [NSValue valueWithPointer:msgClassName];
226 objc_setAssociatedObject(self, &kParentClassNameValueKey,
227 parentNameValue,
228 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
229}
230
231- (void)setupMessageClassNameSuffix:(NSString *)suffix {
232 if (suffix.length) {
233 objc_setAssociatedObject(self, &kClassNameSuffixKey,
234 suffix,
235 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
236 }
237}
238
Brian Silverman9c614bc2016-02-15 20:20:02 -0500239- (NSString *)name {
240 return NSStringFromClass(messageClass_);
241}
242
Austin Schuh40c16522018-10-28 20:27:54 -0700243- (GPBDescriptor *)containingType {
244 NSValue *parentNameValue =
245 objc_getAssociatedObject(self, &kParentClassNameValueKey);
246 if (!parentNameValue) {
247 return nil;
248 }
249 const char *parentName = [parentNameValue pointerValue];
250 Class parentClass = objc_getClass(parentName);
251 NSAssert(parentClass, @"Class %s not defined", parentName);
252 return [parentClass descriptor];
253}
254
255- (NSString *)fullName {
256 NSString *className = NSStringFromClass(self.messageClass);
257 GPBFileDescriptor *file = self.file;
258 NSString *objcPrefix = file.objcPrefix;
259 if (objcPrefix && ![className hasPrefix:objcPrefix]) {
260 NSAssert(0,
261 @"Class didn't have correct prefix? (%@ - %@)",
262 className, objcPrefix);
263 return nil;
264 }
265 GPBDescriptor *parent = self.containingType;
266
267 NSString *name = nil;
268 if (parent) {
269 NSString *parentClassName = NSStringFromClass(parent.messageClass);
270 // The generator will add _Class to avoid reserved words, drop it.
271 NSString *suffix = objc_getAssociatedObject(parent, &kClassNameSuffixKey);
272 if (suffix) {
273 if (![parentClassName hasSuffix:suffix]) {
274 NSAssert(0,
275 @"ParentMessage class didn't have correct suffix? (%@ - %@)",
276 className, suffix);
277 return nil;
278 }
279 parentClassName =
280 [parentClassName substringToIndex:(parentClassName.length - suffix.length)];
281 }
282 NSString *parentPrefix = [parentClassName stringByAppendingString:@"_"];
283 if (![className hasPrefix:parentPrefix]) {
284 NSAssert(0,
285 @"Class didn't have the correct parent name prefix? (%@ - %@)",
286 parentPrefix, className);
287 return nil;
288 }
289 name = [className substringFromIndex:parentPrefix.length];
290 } else {
291 name = [className substringFromIndex:objcPrefix.length];
292 }
293
294 // The generator will add _Class to avoid reserved words, drop it.
295 NSString *suffix = objc_getAssociatedObject(self, &kClassNameSuffixKey);
296 if (suffix) {
297 if (![name hasSuffix:suffix]) {
298 NSAssert(0,
299 @"Message class didn't have correct suffix? (%@ - %@)",
300 name, suffix);
301 return nil;
302 }
303 name = [name substringToIndex:(name.length - suffix.length)];
304 }
305
306 NSString *prefix = (parent != nil ? parent.fullName : file.package);
307 NSString *result;
308 if (prefix.length > 0) {
309 result = [NSString stringWithFormat:@"%@.%@", prefix, name];
310 } else {
311 result = name;
312 }
313 return result;
314}
315
Brian Silverman9c614bc2016-02-15 20:20:02 -0500316- (id)copyWithZone:(NSZone *)zone {
317#pragma unused(zone)
318 return [self retain];
319}
320
321- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
322 for (GPBFieldDescriptor *descriptor in fields_) {
323 if (GPBFieldNumber(descriptor) == fieldNumber) {
324 return descriptor;
325 }
326 }
327 return nil;
328}
329
330- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
331 for (GPBFieldDescriptor *descriptor in fields_) {
332 if ([descriptor.name isEqual:name]) {
333 return descriptor;
334 }
335 }
336 return nil;
337}
338
339- (GPBOneofDescriptor *)oneofWithName:(NSString *)name {
340 for (GPBOneofDescriptor *descriptor in oneofs_) {
341 if ([descriptor.name isEqual:name]) {
342 return descriptor;
343 }
344 }
345 return nil;
346}
347
Brian Silverman9c614bc2016-02-15 20:20:02 -0500348@end
349
350@implementation GPBFileDescriptor {
351 NSString *package_;
Austin Schuh40c16522018-10-28 20:27:54 -0700352 NSString *objcPrefix_;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500353 GPBFileSyntax syntax_;
354}
355
356@synthesize package = package_;
Austin Schuh40c16522018-10-28 20:27:54 -0700357@synthesize objcPrefix = objcPrefix_;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500358@synthesize syntax = syntax_;
359
360- (instancetype)initWithPackage:(NSString *)package
Austin Schuh40c16522018-10-28 20:27:54 -0700361 objcPrefix:(NSString *)objcPrefix
362 syntax:(GPBFileSyntax)syntax {
363 self = [super init];
364 if (self) {
365 package_ = [package copy];
366 objcPrefix_ = [objcPrefix copy];
367 syntax_ = syntax;
368 }
369 return self;
370}
371
372- (instancetype)initWithPackage:(NSString *)package
Brian Silverman9c614bc2016-02-15 20:20:02 -0500373 syntax:(GPBFileSyntax)syntax {
374 self = [super init];
375 if (self) {
376 package_ = [package copy];
377 syntax_ = syntax;
378 }
379 return self;
380}
381
Austin Schuh40c16522018-10-28 20:27:54 -0700382- (void)dealloc {
383 [package_ release];
384 [objcPrefix_ release];
385 [super dealloc];
386}
387
Brian Silverman9c614bc2016-02-15 20:20:02 -0500388@end
389
390@implementation GPBOneofDescriptor
391
392@synthesize fields = fields_;
393
Austin Schuh40c16522018-10-28 20:27:54 -0700394- (instancetype)initWithName:(const char *)name fields:(NSArray *)fields {
Brian Silverman9c614bc2016-02-15 20:20:02 -0500395 self = [super init];
396 if (self) {
Austin Schuh40c16522018-10-28 20:27:54 -0700397 name_ = name;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500398 fields_ = [fields retain];
399 for (GPBFieldDescriptor *fieldDesc in fields) {
400 fieldDesc->containingOneof_ = self;
401 }
402
Austin Schuh40c16522018-10-28 20:27:54 -0700403 caseSel_ = SelFromStrings(NULL, name, "OneOfCase", NO);
Brian Silverman9c614bc2016-02-15 20:20:02 -0500404 }
405 return self;
406}
407
408- (void)dealloc {
409 [fields_ release];
410 [super dealloc];
411}
412
413- (NSString *)name {
Austin Schuh40c16522018-10-28 20:27:54 -0700414 return (NSString * _Nonnull)@(name_);
Brian Silverman9c614bc2016-02-15 20:20:02 -0500415}
416
417- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
418 for (GPBFieldDescriptor *descriptor in fields_) {
419 if (GPBFieldNumber(descriptor) == fieldNumber) {
420 return descriptor;
421 }
422 }
423 return nil;
424}
425
426- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
427 for (GPBFieldDescriptor *descriptor in fields_) {
428 if ([descriptor.name isEqual:name]) {
429 return descriptor;
430 }
431 }
432 return nil;
433}
434
435@end
436
437uint32_t GPBFieldTag(GPBFieldDescriptor *self) {
438 GPBMessageFieldDescription *description = self->description_;
439 GPBWireFormat format;
440 if ((description->flags & GPBFieldMapKeyMask) != 0) {
441 // Maps are repeated messages on the wire.
442 format = GPBWireFormatForType(GPBDataTypeMessage, NO);
443 } else {
444 format = GPBWireFormatForType(description->dataType,
445 ((description->flags & GPBFieldPacked) != 0));
446 }
447 return GPBWireFormatMakeTag(description->number, format);
448}
449
450uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) {
451 GPBMessageFieldDescription *description = self->description_;
452 NSCAssert((description->flags & GPBFieldRepeated) != 0,
453 @"Only valid on repeated fields");
454 GPBWireFormat format =
455 GPBWireFormatForType(description->dataType,
456 ((description->flags & GPBFieldPacked) == 0));
457 return GPBWireFormatMakeTag(description->number, format);
458}
459
460@implementation GPBFieldDescriptor {
461 GPBGenericValue defaultValue_;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500462
463 // Message ivars
464 Class msgClass_;
465
466 // Enum ivars.
467 // If protos are generated with GenerateEnumDescriptors on then it will
468 // be a enumDescriptor, otherwise it will be a enumVerifier.
469 union {
470 GPBEnumDescriptor *enumDescriptor_;
471 GPBEnumValidationFunc enumVerifier_;
472 } enumHandling_;
473}
474
Brian Silverman9c614bc2016-02-15 20:20:02 -0500475@synthesize msgClass = msgClass_;
476@synthesize containingOneof = containingOneof_;
477
478- (instancetype)init {
479 // Throw an exception if people attempt to not use the designated initializer.
480 self = [super init];
481 if (self != nil) {
482 [self doesNotRecognizeSelector:_cmd];
483 self = nil;
484 }
485 return self;
486}
487
Austin Schuh40c16522018-10-28 20:27:54 -0700488- (instancetype)initWithFieldDescription:(void *)description
489 includesDefault:(BOOL)includesDefault
Brian Silverman9c614bc2016-02-15 20:20:02 -0500490 syntax:(GPBFileSyntax)syntax {
491 if ((self = [super init])) {
Austin Schuh40c16522018-10-28 20:27:54 -0700492 GPBMessageFieldDescription *coreDesc;
493 if (includesDefault) {
494 coreDesc = &(((GPBMessageFieldDescriptionWithDefault *)description)->core);
495 } else {
496 coreDesc = description;
497 }
498 description_ = coreDesc;
499 getSel_ = sel_getUid(coreDesc->name);
500 setSel_ = SelFromStrings("set", coreDesc->name, NULL, YES);
Brian Silverman9c614bc2016-02-15 20:20:02 -0500501
Austin Schuh40c16522018-10-28 20:27:54 -0700502 GPBDataType dataType = coreDesc->dataType;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500503 BOOL isMessage = GPBDataTypeIsMessage(dataType);
504 BOOL isMapOrArray = GPBFieldIsMapOrArray(self);
505
506 if (isMapOrArray) {
507 // map<>/repeated fields get a *Count property (inplace of a has*) to
508 // support checking if there are any entries without triggering
509 // autocreation.
Austin Schuh40c16522018-10-28 20:27:54 -0700510 hasOrCountSel_ = SelFromStrings(NULL, coreDesc->name, "_Count", NO);
Brian Silverman9c614bc2016-02-15 20:20:02 -0500511 } else {
512 // If there is a positive hasIndex, then:
513 // - All fields types for proto2 messages get has* selectors.
514 // - Only message fields for proto3 messages get has* selectors.
515 // Note: the positive check is to handle oneOfs, we can't check
516 // containingOneof_ because it isn't set until after initialization.
Austin Schuh40c16522018-10-28 20:27:54 -0700517 if ((coreDesc->hasIndex >= 0) &&
518 (coreDesc->hasIndex != GPBNoHasBit) &&
Brian Silverman9c614bc2016-02-15 20:20:02 -0500519 ((syntax != GPBFileSyntaxProto3) || isMessage)) {
Austin Schuh40c16522018-10-28 20:27:54 -0700520 hasOrCountSel_ = SelFromStrings("has", coreDesc->name, NULL, NO);
521 setHasSel_ = SelFromStrings("setHas", coreDesc->name, NULL, YES);
Brian Silverman9c614bc2016-02-15 20:20:02 -0500522 }
523 }
524
525 // Extra type specific data.
526 if (isMessage) {
Austin Schuh40c16522018-10-28 20:27:54 -0700527 const char *className = coreDesc->dataTypeSpecific.className;
528 // Note: Only fetch the class here, can't send messages to it because
529 // that could cause cycles back to this class within +initialize if
530 // two messages have each other in fields (i.e. - they build a graph).
Brian Silverman9c614bc2016-02-15 20:20:02 -0500531 msgClass_ = objc_getClass(className);
532 NSAssert(msgClass_, @"Class %s not defined", className);
533 } else if (dataType == GPBDataTypeEnum) {
Austin Schuh40c16522018-10-28 20:27:54 -0700534 if ((coreDesc->flags & GPBFieldHasEnumDescriptor) != 0) {
Brian Silverman9c614bc2016-02-15 20:20:02 -0500535 enumHandling_.enumDescriptor_ =
Austin Schuh40c16522018-10-28 20:27:54 -0700536 coreDesc->dataTypeSpecific.enumDescFunc();
Brian Silverman9c614bc2016-02-15 20:20:02 -0500537 } else {
538 enumHandling_.enumVerifier_ =
Austin Schuh40c16522018-10-28 20:27:54 -0700539 coreDesc->dataTypeSpecific.enumVerifier;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500540 }
541 }
542
Austin Schuh40c16522018-10-28 20:27:54 -0700543 // Non map<>/repeated fields can have defaults in proto2 syntax.
544 if (!isMapOrArray && includesDefault) {
545 defaultValue_ = ((GPBMessageFieldDescriptionWithDefault *)description)->defaultValue;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500546 if (dataType == GPBDataTypeBytes) {
547 // Data stored as a length prefixed (network byte order) c-string in
548 // descriptor structure.
549 const uint8_t *bytes = (const uint8_t *)defaultValue_.valueData;
550 if (bytes) {
Austin Schuh40c16522018-10-28 20:27:54 -0700551 uint32_t length;
552 memcpy(&length, bytes, sizeof(length));
Brian Silverman9c614bc2016-02-15 20:20:02 -0500553 length = ntohl(length);
554 bytes += sizeof(length);
555 defaultValue_.valueData =
556 [[NSData alloc] initWithBytes:bytes length:length];
557 }
558 }
559 }
Brian Silverman9c614bc2016-02-15 20:20:02 -0500560 }
561 return self;
562}
563
564- (void)dealloc {
565 if (description_->dataType == GPBDataTypeBytes &&
566 !(description_->flags & GPBFieldRepeated)) {
567 [defaultValue_.valueData release];
568 }
569 [super dealloc];
570}
571
572- (GPBDataType)dataType {
573 return description_->dataType;
574}
575
576- (BOOL)hasDefaultValue {
577 return (description_->flags & GPBFieldHasDefaultValue) != 0;
578}
579
580- (uint32_t)number {
581 return description_->number;
582}
583
584- (NSString *)name {
Austin Schuh40c16522018-10-28 20:27:54 -0700585 return (NSString * _Nonnull)@(description_->name);
Brian Silverman9c614bc2016-02-15 20:20:02 -0500586}
587
588- (BOOL)isRequired {
589 return (description_->flags & GPBFieldRequired) != 0;
590}
591
592- (BOOL)isOptional {
593 return (description_->flags & GPBFieldOptional) != 0;
594}
595
596- (GPBFieldType)fieldType {
597 GPBFieldFlags flags = description_->flags;
598 if ((flags & GPBFieldRepeated) != 0) {
599 return GPBFieldTypeRepeated;
600 } else if ((flags & GPBFieldMapKeyMask) != 0) {
601 return GPBFieldTypeMap;
602 } else {
603 return GPBFieldTypeSingle;
604 }
605}
606
607- (GPBDataType)mapKeyDataType {
608 switch (description_->flags & GPBFieldMapKeyMask) {
609 case GPBFieldMapKeyInt32:
610 return GPBDataTypeInt32;
611 case GPBFieldMapKeyInt64:
612 return GPBDataTypeInt64;
613 case GPBFieldMapKeyUInt32:
614 return GPBDataTypeUInt32;
615 case GPBFieldMapKeyUInt64:
616 return GPBDataTypeUInt64;
617 case GPBFieldMapKeySInt32:
618 return GPBDataTypeSInt32;
619 case GPBFieldMapKeySInt64:
620 return GPBDataTypeSInt64;
621 case GPBFieldMapKeyFixed32:
622 return GPBDataTypeFixed32;
623 case GPBFieldMapKeyFixed64:
624 return GPBDataTypeFixed64;
625 case GPBFieldMapKeySFixed32:
626 return GPBDataTypeSFixed32;
627 case GPBFieldMapKeySFixed64:
628 return GPBDataTypeSFixed64;
629 case GPBFieldMapKeyBool:
630 return GPBDataTypeBool;
631 case GPBFieldMapKeyString:
632 return GPBDataTypeString;
633
634 default:
635 NSAssert(0, @"Not a map type");
636 return GPBDataTypeInt32; // For lack of anything better.
637 }
638}
639
640- (BOOL)isPackable {
641 return (description_->flags & GPBFieldPacked) != 0;
642}
643
644- (BOOL)isValidEnumValue:(int32_t)value {
645 NSAssert(description_->dataType == GPBDataTypeEnum,
646 @"Field Must be of type GPBDataTypeEnum");
647 if (description_->flags & GPBFieldHasEnumDescriptor) {
648 return enumHandling_.enumDescriptor_.enumVerifier(value);
649 } else {
650 return enumHandling_.enumVerifier_(value);
651 }
652}
653
654- (GPBEnumDescriptor *)enumDescriptor {
655 if (description_->flags & GPBFieldHasEnumDescriptor) {
656 return enumHandling_.enumDescriptor_;
657 } else {
658 return nil;
659 }
660}
661
662- (GPBGenericValue)defaultValue {
663 // Depends on the fact that defaultValue_ is initialized either to "0/nil" or
664 // to an actual defaultValue in our initializer.
665 GPBGenericValue value = defaultValue_;
666
667 if (!(description_->flags & GPBFieldRepeated)) {
668 // We special handle data and strings. If they are nil, we replace them
669 // with empty string/empty data.
670 GPBDataType type = description_->dataType;
671 if (type == GPBDataTypeBytes && value.valueData == nil) {
672 value.valueData = GPBEmptyNSData();
673 } else if (type == GPBDataTypeString && value.valueString == nil) {
674 value.valueString = @"";
675 }
676 }
677 return value;
678}
679
680- (NSString *)textFormatName {
681 if ((description_->flags & GPBFieldTextFormatNameCustom) != 0) {
682 NSValue *extraInfoValue =
683 objc_getAssociatedObject(self, &kTextFormatExtraValueKey);
684 // Support can be left out at generation time.
685 if (!extraInfoValue) {
686 return nil;
687 }
688 const uint8_t *extraTextFormatInfo = [extraInfoValue pointerValue];
689 return GPBDecodeTextFormatName(extraTextFormatInfo, GPBFieldNumber(self),
690 self.name);
691 }
692
693 // The logic here has to match SetCommonFieldVariables() from
694 // objectivec_field.cc in the proto compiler.
695 NSString *name = self.name;
696 NSUInteger len = [name length];
697
698 // Remove the "_p" added to reserved names.
699 if ([name hasSuffix:@"_p"]) {
700 name = [name substringToIndex:(len - 2)];
701 len = [name length];
702 }
703
704 // Remove "Array" from the end for repeated fields.
705 if (((description_->flags & GPBFieldRepeated) != 0) &&
706 [name hasSuffix:@"Array"]) {
707 name = [name substringToIndex:(len - 5)];
708 len = [name length];
709 }
710
711 // Groups vs. other fields.
712 if (description_->dataType == GPBDataTypeGroup) {
713 // Just capitalize the first letter.
714 unichar firstChar = [name characterAtIndex:0];
715 if (firstChar >= 'a' && firstChar <= 'z') {
716 NSString *firstCharString =
717 [NSString stringWithFormat:@"%C", (unichar)(firstChar - 'a' + 'A')];
718 NSString *result =
719 [name stringByReplacingCharactersInRange:NSMakeRange(0, 1)
720 withString:firstCharString];
721 return result;
722 }
723 return name;
724
725 } else {
726 // Undo the CamelCase.
727 NSMutableString *result = [NSMutableString stringWithCapacity:len];
Austin Schuh40c16522018-10-28 20:27:54 -0700728 for (uint32_t i = 0; i < len; i++) {
Brian Silverman9c614bc2016-02-15 20:20:02 -0500729 unichar c = [name characterAtIndex:i];
730 if (c >= 'A' && c <= 'Z') {
731 if (i > 0) {
732 [result appendFormat:@"_%C", (unichar)(c - 'A' + 'a')];
733 } else {
734 [result appendFormat:@"%C", c];
735 }
736 } else {
737 [result appendFormat:@"%C", c];
738 }
739 }
740 return result;
741 }
742}
743
744@end
745
746@implementation GPBEnumDescriptor {
747 NSString *name_;
Austin Schuh40c16522018-10-28 20:27:54 -0700748 // valueNames_ is a single c string with all of the value names appended
749 // together, each null terminated. -calcValueNameOffsets fills in
750 // nameOffsets_ with the offsets to allow quicker access to the individual
751 // names.
752 const char *valueNames_;
753 const int32_t *values_;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500754 GPBEnumValidationFunc enumVerifier_;
755 const uint8_t *extraTextFormatInfo_;
Austin Schuh40c16522018-10-28 20:27:54 -0700756 uint32_t *nameOffsets_;
757 uint32_t valueCount_;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500758}
759
760@synthesize name = name_;
761@synthesize enumVerifier = enumVerifier_;
762
763+ (instancetype)
764 allocDescriptorForName:(NSString *)name
Austin Schuh40c16522018-10-28 20:27:54 -0700765 valueNames:(const char *)valueNames
766 values:(const int32_t *)values
767 count:(uint32_t)valueCount
Brian Silverman9c614bc2016-02-15 20:20:02 -0500768 enumVerifier:(GPBEnumValidationFunc)enumVerifier {
769 GPBEnumDescriptor *descriptor = [[self alloc] initWithName:name
Austin Schuh40c16522018-10-28 20:27:54 -0700770 valueNames:valueNames
771 values:values
772 count:valueCount
Brian Silverman9c614bc2016-02-15 20:20:02 -0500773 enumVerifier:enumVerifier];
774 return descriptor;
775}
776
777+ (instancetype)
778 allocDescriptorForName:(NSString *)name
Austin Schuh40c16522018-10-28 20:27:54 -0700779 valueNames:(const char *)valueNames
780 values:(const int32_t *)values
781 count:(uint32_t)valueCount
Brian Silverman9c614bc2016-02-15 20:20:02 -0500782 enumVerifier:(GPBEnumValidationFunc)enumVerifier
783 extraTextFormatInfo:(const char *)extraTextFormatInfo {
784 // Call the common case.
785 GPBEnumDescriptor *descriptor = [self allocDescriptorForName:name
Austin Schuh40c16522018-10-28 20:27:54 -0700786 valueNames:valueNames
787 values:values
788 count:valueCount
Brian Silverman9c614bc2016-02-15 20:20:02 -0500789 enumVerifier:enumVerifier];
790 // Set the extra info.
791 descriptor->extraTextFormatInfo_ = (const uint8_t *)extraTextFormatInfo;
792 return descriptor;
793}
794
795- (instancetype)initWithName:(NSString *)name
Austin Schuh40c16522018-10-28 20:27:54 -0700796 valueNames:(const char *)valueNames
797 values:(const int32_t *)values
798 count:(uint32_t)valueCount
Brian Silverman9c614bc2016-02-15 20:20:02 -0500799 enumVerifier:(GPBEnumValidationFunc)enumVerifier {
800 if ((self = [super init])) {
801 name_ = [name copy];
Austin Schuh40c16522018-10-28 20:27:54 -0700802 valueNames_ = valueNames;
803 values_ = values;
804 valueCount_ = valueCount;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500805 enumVerifier_ = enumVerifier;
806 }
807 return self;
808}
809
Austin Schuh40c16522018-10-28 20:27:54 -0700810- (void)dealloc {
811 [name_ release];
812 if (nameOffsets_) free(nameOffsets_);
813 [super dealloc];
814}
815
816- (void)calcValueNameOffsets {
817 @synchronized(self) {
818 if (nameOffsets_ != NULL) {
819 return;
820 }
821 uint32_t *offsets = malloc(valueCount_ * sizeof(uint32_t));
822 const char *scan = valueNames_;
823 for (uint32_t i = 0; i < valueCount_; ++i) {
824 offsets[i] = (uint32_t)(scan - valueNames_);
825 while (*scan != '\0') ++scan;
826 ++scan; // Step over the null.
827 }
828 nameOffsets_ = offsets;
829 }
830}
831
Brian Silverman9c614bc2016-02-15 20:20:02 -0500832- (NSString *)enumNameForValue:(int32_t)number {
Austin Schuh40c16522018-10-28 20:27:54 -0700833 if (nameOffsets_ == NULL) [self calcValueNameOffsets];
834
835 for (uint32_t i = 0; i < valueCount_; ++i) {
836 if (values_[i] == number) {
837 const char *valueName = valueNames_ + nameOffsets_[i];
838 NSString *fullName = [NSString stringWithFormat:@"%@_%s", name_, valueName];
Brian Silverman9c614bc2016-02-15 20:20:02 -0500839 return fullName;
840 }
841 }
842 return nil;
843}
844
845- (BOOL)getValue:(int32_t *)outValue forEnumName:(NSString *)name {
846 // Must have the prefix.
847 NSUInteger prefixLen = name_.length + 1;
848 if ((name.length <= prefixLen) || ![name hasPrefix:name_] ||
849 ([name characterAtIndex:prefixLen - 1] != '_')) {
850 return NO;
851 }
852
853 // Skip over the prefix.
854 const char *nameAsCStr = [name UTF8String];
855 nameAsCStr += prefixLen;
856
Austin Schuh40c16522018-10-28 20:27:54 -0700857 if (nameOffsets_ == NULL) [self calcValueNameOffsets];
858
Brian Silverman9c614bc2016-02-15 20:20:02 -0500859 // Find it.
Austin Schuh40c16522018-10-28 20:27:54 -0700860 for (uint32_t i = 0; i < valueCount_; ++i) {
861 const char *valueName = valueNames_ + nameOffsets_[i];
862 if (strcmp(nameAsCStr, valueName) == 0) {
Brian Silverman9c614bc2016-02-15 20:20:02 -0500863 if (outValue) {
Austin Schuh40c16522018-10-28 20:27:54 -0700864 *outValue = values_[i];
Brian Silverman9c614bc2016-02-15 20:20:02 -0500865 }
866 return YES;
867 }
868 }
869 return NO;
870}
871
Austin Schuh40c16522018-10-28 20:27:54 -0700872- (BOOL)getValue:(int32_t *)outValue forEnumTextFormatName:(NSString *)textFormatName {
873 if (nameOffsets_ == NULL) [self calcValueNameOffsets];
874
875 for (uint32_t i = 0; i < valueCount_; ++i) {
876 int32_t value = values_[i];
877 NSString *valueTextFormatName = [self textFormatNameForValue:value];
878 if ([valueTextFormatName isEqual:textFormatName]) {
879 if (outValue) {
880 *outValue = value;
881 }
882 return YES;
883 }
884 }
885 return NO;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500886}
887
888- (NSString *)textFormatNameForValue:(int32_t)number {
Austin Schuh40c16522018-10-28 20:27:54 -0700889 if (nameOffsets_ == NULL) [self calcValueNameOffsets];
890
Brian Silverman9c614bc2016-02-15 20:20:02 -0500891 // Find the EnumValue descriptor and its index.
Austin Schuh40c16522018-10-28 20:27:54 -0700892 BOOL foundIt = NO;
893 uint32_t valueDescriptorIndex;
894 for (valueDescriptorIndex = 0; valueDescriptorIndex < valueCount_;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500895 ++valueDescriptorIndex) {
Austin Schuh40c16522018-10-28 20:27:54 -0700896 if (values_[valueDescriptorIndex] == number) {
897 foundIt = YES;
Brian Silverman9c614bc2016-02-15 20:20:02 -0500898 break;
899 }
900 }
901
Austin Schuh40c16522018-10-28 20:27:54 -0700902 if (!foundIt) {
Brian Silverman9c614bc2016-02-15 20:20:02 -0500903 return nil;
904 }
905
906 NSString *result = nil;
907 // Naming adds an underscore between enum name and value name, skip that also.
Austin Schuh40c16522018-10-28 20:27:54 -0700908 const char *valueName = valueNames_ + nameOffsets_[valueDescriptorIndex];
909 NSString *shortName = @(valueName);
Brian Silverman9c614bc2016-02-15 20:20:02 -0500910
911 // See if it is in the map of special format handling.
912 if (extraTextFormatInfo_) {
913 result = GPBDecodeTextFormatName(extraTextFormatInfo_,
914 (int32_t)valueDescriptorIndex, shortName);
915 }
916 // Logic here needs to match what objectivec_enum.cc does in the proto
917 // compiler.
918 if (result == nil) {
919 NSUInteger len = [shortName length];
920 NSMutableString *worker = [NSMutableString stringWithCapacity:len];
921 for (NSUInteger i = 0; i < len; i++) {
922 unichar c = [shortName characterAtIndex:i];
923 if (i > 0 && c >= 'A' && c <= 'Z') {
924 [worker appendString:@"_"];
925 }
926 [worker appendFormat:@"%c", toupper((char)c)];
927 }
928 result = worker;
929 }
930 return result;
931}
932
933@end
934
935@implementation GPBExtensionDescriptor {
936 GPBGenericValue defaultValue_;
937}
938
939@synthesize containingMessageClass = containingMessageClass_;
940
941- (instancetype)initWithExtensionDescription:
942 (GPBExtensionDescription *)description {
943 if ((self = [super init])) {
944 description_ = description;
945
Austin Schuh40c16522018-10-28 20:27:54 -0700946#if defined(DEBUG) && DEBUG
Brian Silverman9c614bc2016-02-15 20:20:02 -0500947 const char *className = description->messageOrGroupClassName;
948 if (className) {
949 NSAssert(objc_lookUpClass(className) != Nil,
950 @"Class %s not defined", className);
951 }
952#endif
953
954 if (description->extendedClass) {
955 Class containingClass = objc_lookUpClass(description->extendedClass);
956 NSAssert(containingClass, @"Class %s not defined",
957 description->extendedClass);
958 containingMessageClass_ = containingClass;
959 }
960
961 GPBDataType type = description_->dataType;
962 if (type == GPBDataTypeBytes) {
963 // Data stored as a length prefixed c-string in descriptor records.
964 const uint8_t *bytes =
965 (const uint8_t *)description->defaultValue.valueData;
966 if (bytes) {
Austin Schuh40c16522018-10-28 20:27:54 -0700967 uint32_t length;
968 memcpy(&length, bytes, sizeof(length));
Brian Silverman9c614bc2016-02-15 20:20:02 -0500969 // The length is stored in network byte order.
970 length = ntohl(length);
971 bytes += sizeof(length);
972 defaultValue_.valueData =
973 [[NSData alloc] initWithBytes:bytes length:length];
974 }
975 } else if (type == GPBDataTypeMessage || type == GPBDataTypeGroup) {
976 // The default is looked up in -defaultValue instead since extensions
977 // aren't common, we avoid the hit startup hit and it avoid initialization
978 // order issues.
979 } else {
980 defaultValue_ = description->defaultValue;
981 }
982 }
983 return self;
984}
985
986- (void)dealloc {
987 if ((description_->dataType == GPBDataTypeBytes) &&
988 !GPBExtensionIsRepeated(description_)) {
989 [defaultValue_.valueData release];
990 }
991 [super dealloc];
992}
993
994- (instancetype)copyWithZone:(NSZone *)zone {
995#pragma unused(zone)
996 // Immutable.
997 return [self retain];
998}
999
1000- (NSString *)singletonName {
Austin Schuh40c16522018-10-28 20:27:54 -07001001 return (NSString * _Nonnull)@(description_->singletonName);
Brian Silverman9c614bc2016-02-15 20:20:02 -05001002}
1003
1004- (const char *)singletonNameC {
1005 return description_->singletonName;
1006}
1007
1008- (uint32_t)fieldNumber {
1009 return description_->fieldNumber;
1010}
1011
1012- (GPBDataType)dataType {
1013 return description_->dataType;
1014}
1015
1016- (GPBWireFormat)wireType {
1017 return GPBWireFormatForType(description_->dataType,
1018 GPBExtensionIsPacked(description_));
1019}
1020
1021- (GPBWireFormat)alternateWireType {
1022 NSAssert(GPBExtensionIsRepeated(description_),
1023 @"Only valid on repeated extensions");
1024 return GPBWireFormatForType(description_->dataType,
1025 !GPBExtensionIsPacked(description_));
1026}
1027
1028- (BOOL)isRepeated {
1029 return GPBExtensionIsRepeated(description_);
1030}
1031
Brian Silverman9c614bc2016-02-15 20:20:02 -05001032- (BOOL)isPackable {
1033 return GPBExtensionIsPacked(description_);
1034}
1035
1036- (Class)msgClass {
1037 return objc_getClass(description_->messageOrGroupClassName);
1038}
1039
1040- (GPBEnumDescriptor *)enumDescriptor {
1041 if (description_->dataType == GPBDataTypeEnum) {
1042 GPBEnumDescriptor *enumDescriptor = description_->enumDescriptorFunc();
1043 return enumDescriptor;
1044 }
1045 return nil;
1046}
1047
1048- (id)defaultValue {
1049 if (GPBExtensionIsRepeated(description_)) {
1050 return nil;
1051 }
1052
1053 switch (description_->dataType) {
1054 case GPBDataTypeBool:
1055 return @(defaultValue_.valueBool);
1056 case GPBDataTypeFloat:
1057 return @(defaultValue_.valueFloat);
1058 case GPBDataTypeDouble:
1059 return @(defaultValue_.valueDouble);
1060 case GPBDataTypeInt32:
1061 case GPBDataTypeSInt32:
1062 case GPBDataTypeEnum:
1063 case GPBDataTypeSFixed32:
1064 return @(defaultValue_.valueInt32);
1065 case GPBDataTypeInt64:
1066 case GPBDataTypeSInt64:
1067 case GPBDataTypeSFixed64:
1068 return @(defaultValue_.valueInt64);
1069 case GPBDataTypeUInt32:
1070 case GPBDataTypeFixed32:
1071 return @(defaultValue_.valueUInt32);
1072 case GPBDataTypeUInt64:
1073 case GPBDataTypeFixed64:
1074 return @(defaultValue_.valueUInt64);
1075 case GPBDataTypeBytes:
1076 // Like message fields, the default is zero length data.
1077 return (defaultValue_.valueData ? defaultValue_.valueData
1078 : GPBEmptyNSData());
1079 case GPBDataTypeString:
1080 // Like message fields, the default is zero length string.
1081 return (defaultValue_.valueString ? defaultValue_.valueString : @"");
1082 case GPBDataTypeGroup:
1083 case GPBDataTypeMessage:
1084 return nil;
1085 }
1086}
1087
1088- (NSComparisonResult)compareByFieldNumber:(GPBExtensionDescriptor *)other {
1089 int32_t selfNumber = description_->fieldNumber;
1090 int32_t otherNumber = other->description_->fieldNumber;
1091 if (selfNumber < otherNumber) {
1092 return NSOrderedAscending;
1093 } else if (selfNumber == otherNumber) {
1094 return NSOrderedSame;
1095 } else {
1096 return NSOrderedDescending;
1097 }
1098}
1099
1100@end
Austin Schuh40c16522018-10-28 20:27:54 -07001101
1102#pragma clang diagnostic pop