blob: 0c9012c1123874b5734ed54430afb64d55ffe499 [file] [log] [blame]
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001// 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
Thomas Van Lenten189f6322022-09-19 17:21:13 -040035#import "GPBMessage_PackagePrivate.h"
Thomas Van Lenten30650d82015-05-01 08:57:16 -040036#import "GPBUtilities_PackagePrivate.h"
37#import "GPBWireFormat.h"
Thomas Van Lenten30650d82015-05-01 08:57:16 -040038
Protobuf Team Bot3a742662022-11-15 08:56:51 -080039@interface GPBDescriptor ()
40- (instancetype)initWithClass:(Class)messageClass
Protobuf Team Botf4c79722023-02-08 06:29:16 -080041 messageName:(NSString *)messageName
42 fileDescription:(GPBFileDescription *)fileDescription
Protobuf Team Bot3a742662022-11-15 08:56:51 -080043 fields:(NSArray *)fields
44 storageSize:(uint32_t)storage
45 wireFormat:(BOOL)wireFormat;
46@end
47
48@interface GPBFieldDescriptor ()
49// Single initializer
50// description has to be long lived, it is held as a raw pointer.
51- (instancetype)initWithFieldDescription:(void *)description
Protobuf Team Botf4c79722023-02-08 06:29:16 -080052 descriptorFlags:(GPBDescriptorInitializationFlags)descriptorFlags
53 fileSyntax:(GPBFileSyntax)fileSyntax;
Protobuf Team Bot3a742662022-11-15 08:56:51 -080054
55@end
56
57@interface GPBEnumDescriptor ()
58- (instancetype)initWithName:(NSString *)name
59 valueNames:(const char *)valueNames
60 values:(const int32_t *)values
61 count:(uint32_t)valueCount
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -080062 enumVerifier:(GPBEnumValidationFunc)enumVerifier
63 flags:(GPBEnumDescriptorInitializationFlags)flags;
Protobuf Team Bot3a742662022-11-15 08:56:51 -080064@end
65
Thomas Van Lentenc8a440d2016-05-25 13:46:00 -040066// Direct access is use for speed, to avoid even internally declaring things
67// read/write, etc. The warning is enabled in the project to ensure code calling
68// protos can turn on -Wdirect-ivar-access without issues.
69#pragma clang diagnostic push
70#pragma clang diagnostic ignored "-Wdirect-ivar-access"
71
Thomas Van Lenten337ec302016-08-16 11:26:49 -040072// The addresses of these variables are used as keys for objc_getAssociatedObject.
Thomas Van Lenten30650d82015-05-01 08:57:16 -040073static const char kTextFormatExtraValueKey = 0;
Dave MacLachlan74956e12019-12-17 17:32:09 -080074static const char kParentClassValueKey = 0;
Thomas Van Lenten337ec302016-08-16 11:26:49 -040075static const char kClassNameSuffixKey = 0;
Protobuf Team Botf4c79722023-02-08 06:29:16 -080076static const char kFileDescriptorCacheKey = 0;
Thomas Van Lenten30650d82015-05-01 08:57:16 -040077
78// Utility function to generate selectors on the fly.
Thomas Van Lenten189f6322022-09-19 17:21:13 -040079static SEL SelFromStrings(const char *prefix, const char *middle, const char *suffix,
80 BOOL takesArg) {
Thomas Van Lenten30650d82015-05-01 08:57:16 -040081 if (prefix == NULL && suffix == NULL && !takesArg) {
82 return sel_getUid(middle);
83 }
84 const size_t prefixLen = prefix != NULL ? strlen(prefix) : 0;
85 const size_t middleLen = strlen(middle);
86 const size_t suffixLen = suffix != NULL ? strlen(suffix) : 0;
Thomas Van Lenten189f6322022-09-19 17:21:13 -040087 size_t totalLen = prefixLen + middleLen + suffixLen + 1; // include space for null on end.
Thomas Van Lenten30650d82015-05-01 08:57:16 -040088 if (takesArg) {
89 totalLen += 1;
90 }
91 char buffer[totalLen];
92 if (prefix != NULL) {
93 memcpy(buffer, prefix, prefixLen);
94 memcpy(buffer + prefixLen, middle, middleLen);
95 buffer[prefixLen] = (char)toupper(buffer[prefixLen]);
96 } else {
97 memcpy(buffer, middle, middleLen);
98 }
99 if (suffix != NULL) {
100 memcpy(buffer + prefixLen + middleLen, suffix, suffixLen);
101 }
102 if (takesArg) {
103 buffer[totalLen - 2] = ':';
104 }
105 // Always null terminate it.
106 buffer[totalLen - 1] = 0;
107
108 SEL result = sel_getUid(buffer);
109 return result;
110}
111
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400112static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageFields)
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400113 __attribute__((ns_returns_retained));
114
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400115static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageFields) {
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400116 NSMutableArray *result = [[NSMutableArray alloc] init];
117 for (GPBFieldDescriptor *fieldDesc in allMessageFields) {
118 if (fieldDesc->description_->hasIndex == hasIndex) {
119 [result addObject:fieldDesc];
120 }
121 }
122 return result;
123}
124
125@implementation GPBDescriptor {
126 Class messageClass_;
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800127 NSString *messageName_;
128 const GPBFileDescription *fileDescription_;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400129 BOOL wireFormat_;
130}
131
132@synthesize messageClass = messageClass_;
133@synthesize fields = fields_;
134@synthesize oneofs = oneofs_;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400135@synthesize extensionRanges = extensionRanges_;
136@synthesize extensionRangesCount = extensionRangesCount_;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400137@synthesize wireFormat = wireFormat_;
138
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400139+ (instancetype)allocDescriptorForClass:(Class)messageClass
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800140 messageName:(NSString *)messageName
141 fileDescription:(GPBFileDescription *)fileDescription
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400142 fields:(void *)fieldDescriptions
143 fieldCount:(uint32_t)fieldCount
144 storageSize:(uint32_t)storageSize
145 flags:(GPBDescriptorInitializationFlags)flags {
Protobuf Team Bot6e5a01b2023-01-31 13:11:17 -0800146 // Compute the unknown flags by this version of the runtime and then check the passed in flags
147 // (from the generated code) to detect when sources from a newer version are being used with an
148 // older runtime.
149 GPBDescriptorInitializationFlags unknownFlags =
150 ~(GPBDescriptorInitializationFlag_FieldsWithDefault |
151 GPBDescriptorInitializationFlag_WireFormat | GPBDescriptorInitializationFlag_UsesClassRefs |
152 GPBDescriptorInitializationFlag_Proto3OptionalKnown |
153 GPBDescriptorInitializationFlag_ClosedEnumSupportKnown);
154 if ((flags & unknownFlags) != 0) {
155 GPBRuntimeMatchFailure();
156 }
Protobuf Team Bot5e6fbbb2023-02-08 07:30:33 -0800157
158#if defined(DEBUG) && DEBUG
159 NSAssert((flags & GPBDescriptorInitializationFlag_UsesClassRefs) != 0,
160 @"Internal error: all fields should have class refs");
Protobuf Team Bot699561f2023-02-08 08:15:28 -0800161 NSAssert((flags & GPBDescriptorInitializationFlag_Proto3OptionalKnown) != 0,
162 @"Internal error: proto3 optional should be known");
Protobuf Team Bot5e6fbbb2023-02-08 07:30:33 -0800163#endif
164
Protobuf Team Bot3a742662022-11-15 08:56:51 -0800165 NSMutableArray *fields =
166 (fieldCount ? [[NSMutableArray alloc] initWithCapacity:fieldCount] : nil);
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400167 BOOL fieldsIncludeDefault = (flags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0;
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400168
169 void *desc;
Protobuf Team Bot6e5a01b2023-01-31 13:11:17 -0800170 GPBFieldFlags mergedFieldFlags = GPBFieldNone;
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400171 for (uint32_t i = 0; i < fieldCount; ++i) {
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400172 // Need correctly typed pointer for array indexing below to work.
173 if (fieldsIncludeDefault) {
Protobuf Team Bot3a742662022-11-15 08:56:51 -0800174 desc = &(((GPBMessageFieldDescriptionWithDefault *)fieldDescriptions)[i]);
Protobuf Team Bot6e5a01b2023-01-31 13:11:17 -0800175 mergedFieldFlags |=
176 (((GPBMessageFieldDescriptionWithDefault *)fieldDescriptions)[i]).core.flags;
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400177 } else {
Protobuf Team Bot3a742662022-11-15 08:56:51 -0800178 desc = &(((GPBMessageFieldDescription *)fieldDescriptions)[i]);
Protobuf Team Bot6e5a01b2023-01-31 13:11:17 -0800179 mergedFieldFlags |= (((GPBMessageFieldDescription *)fieldDescriptions)[i]).flags;
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400180 }
181 GPBFieldDescriptor *fieldDescriptor =
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800182 [[GPBFieldDescriptor alloc] initWithFieldDescription:desc
183 descriptorFlags:flags
184 fileSyntax:fileDescription->syntax];
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400185 [fields addObject:fieldDescriptor];
186 [fieldDescriptor release];
187 }
Protobuf Team Bot6e5a01b2023-01-31 13:11:17 -0800188 // No real value in checking all the fields individually, just check the combined flags at the
189 // end.
190 GPBFieldFlags unknownFieldFlags =
191 ~(GPBFieldRequired | GPBFieldRepeated | GPBFieldPacked | GPBFieldOptional |
192 GPBFieldHasDefaultValue | GPBFieldClearHasIvarOnZero | GPBFieldTextFormatNameCustom |
193 GPBFieldHasEnumDescriptor | GPBFieldMapKeyMask | GPBFieldClosedEnum);
194 if ((mergedFieldFlags & unknownFieldFlags) != 0) {
195 GPBRuntimeMatchFailure();
196 }
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400197
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400198 BOOL wireFormat = (flags & GPBDescriptorInitializationFlag_WireFormat) != 0;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400199 GPBDescriptor *descriptor = [[self alloc] initWithClass:messageClass
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800200 messageName:messageName
201 fileDescription:fileDescription
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400202 fields:fields
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400203 storageSize:storageSize
204 wireFormat:wireFormat];
205 [fields release];
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400206 return descriptor;
207}
208
Protobuf Team Bot48ff4f62023-01-26 11:37:19 -0800209+ (instancetype)allocDescriptorForClass:(Class)messageClass
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800210 file:(GPBFileDescriptor *)file
211 fields:(void *)fieldDescriptions
212 fieldCount:(uint32_t)fieldCount
213 storageSize:(uint32_t)storageSize
214 flags:(GPBDescriptorInitializationFlags)flags {
215 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30006,
216 time_to_remove_this_old_version_shim);
217
Protobuf Team Bot5e6fbbb2023-02-08 07:30:33 -0800218 BOOL fixClassRefs = (flags & GPBDescriptorInitializationFlag_UsesClassRefs) == 0;
219 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30003,
220 time_to_remove_non_class_ref_support);
221
Protobuf Team Bot699561f2023-02-08 08:15:28 -0800222 BOOL fixProto3Optional = (flags & GPBDescriptorInitializationFlag_Proto3OptionalKnown) == 0;
223 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30004,
224 time_to_remove_proto3_optional_fallback);
225
226 if (fixClassRefs || fixProto3Optional) {
Protobuf Team Bot5e6fbbb2023-02-08 07:30:33 -0800227 BOOL fieldsIncludeDefault = (flags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0;
Protobuf Team Bot699561f2023-02-08 08:15:28 -0800228#pragma clang diagnostic push
229#pragma clang diagnostic ignored "-Wdeprecated-declarations"
230 GPBFileSyntax fileSyntax = file.syntax;
231#pragma clang diagnostic pop
Protobuf Team Bot5e6fbbb2023-02-08 07:30:33 -0800232
233 for (uint32_t i = 0; i < fieldCount; ++i) {
234 GPBMessageFieldDescription *coreDesc;
235 if (fieldsIncludeDefault) {
236 coreDesc = &((((GPBMessageFieldDescriptionWithDefault *)fieldDescriptions)[i]).core);
237 } else {
238 coreDesc = &(((GPBMessageFieldDescription *)fieldDescriptions)[i]);
239 }
240
Protobuf Team Bot699561f2023-02-08 08:15:28 -0800241 if (fixClassRefs && GPBDataTypeIsMessage(coreDesc->dataType)) {
Protobuf Team Bot5e6fbbb2023-02-08 07:30:33 -0800242 const char *className = coreDesc->dataTypeSpecific.className;
243 Class msgClass = objc_getClass(className);
244 NSAssert(msgClass, @"Class %s not defined", className);
245 coreDesc->dataTypeSpecific.clazz = msgClass;
246 }
Protobuf Team Bot699561f2023-02-08 08:15:28 -0800247
248 if (fixProto3Optional) {
249 // If it was...
250 // - proto3 syntax
251 // - not repeated/map
252 // - not in a oneof (negative has index)
253 // - not a message (the flag doesn't make sense for messages)
254 BOOL clearOnZero = ((fileSyntax == GPBFileSyntaxProto3) &&
255 ((coreDesc->flags & (GPBFieldRepeated | GPBFieldMapKeyMask)) == 0) &&
256 (coreDesc->hasIndex >= 0) && !GPBDataTypeIsMessage(coreDesc->dataType));
257 if (clearOnZero) {
258 coreDesc->flags |= GPBFieldClearHasIvarOnZero;
259 }
260 }
Protobuf Team Bot5e6fbbb2023-02-08 07:30:33 -0800261 }
Protobuf Team Bot699561f2023-02-08 08:15:28 -0800262 flags |= (GPBDescriptorInitializationFlag_UsesClassRefs |
263 GPBDescriptorInitializationFlag_Proto3OptionalKnown);
Protobuf Team Bot5e6fbbb2023-02-08 07:30:33 -0800264 }
265
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800266 // Use a local GPBFileDescription with just the syntax to allow legacy generation initialization,
267 // then clear the ivar and wire in the GPBFileDescriptor.
268#pragma clang diagnostic push
269#pragma clang diagnostic ignored "-Wdeprecated-declarations"
270 GPBFileDescription localDesc = {NULL, NULL, file.syntax};
271#pragma clang diagnostic pop
272
273 GPBDescriptor *result = [self allocDescriptorForClass:messageClass
274 messageName:nil
275 fileDescription:&localDesc
276 fields:fieldDescriptions
277 fieldCount:fieldCount
278 storageSize:storageSize
279 flags:flags];
280 result->fileDescription_ = NULL;
281 objc_setAssociatedObject(result, &kFileDescriptorCacheKey, file,
282 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
283 return result;
284}
285
286+ (instancetype)allocDescriptorForClass:(Class)messageClass
Protobuf Team Bot48ff4f62023-01-26 11:37:19 -0800287 rootClass:(__unused Class)rootClass
288 file:(GPBFileDescriptor *)file
289 fields:(void *)fieldDescriptions
290 fieldCount:(uint32_t)fieldCount
291 storageSize:(uint32_t)storageSize
292 flags:(GPBDescriptorInitializationFlags)flags {
Protobuf Team Bot663fca12023-01-31 09:07:43 -0800293 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30006,
294 time_to_remove_this_old_version_shim);
Protobuf Team Bot48ff4f62023-01-26 11:37:19 -0800295 // The rootClass is no longer used, but it is passed as [ROOT class] to
296 // ensure it was started up during initialization also when the message
297 // scopes extensions.
298 return [self allocDescriptorForClass:messageClass
299 file:file
300 fields:fieldDescriptions
301 fieldCount:fieldCount
302 storageSize:storageSize
303 flags:flags];
304}
305
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400306- (instancetype)initWithClass:(Class)messageClass
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800307 messageName:(NSString *)messageName
308 fileDescription:(GPBFileDescription *)fileDescription
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400309 fields:(NSArray *)fields
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400310 storageSize:(uint32_t)storageSize
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400311 wireFormat:(BOOL)wireFormat {
312 if ((self = [super init])) {
313 messageClass_ = messageClass;
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800314#if defined(DEBUG) && DEBUG
315 // If `messageName` is set, then `fileDescription` also must be set. `fileDescription` gets
316 // hotwired for legacy startup, so it can be non NULL without `messageName` having been set.
317 NSAssert((messageName == nil) || (fileDescription != NULL),
318 @"messageName and fileDescription should always be provided together");
319#endif
320 messageName_ = [messageName copy];
321 fileDescription_ = fileDescription;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400322 fields_ = [fields retain];
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400323 storageSize_ = storageSize;
324 wireFormat_ = wireFormat;
325 }
326 return self;
327}
328
329- (void)dealloc {
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800330 [messageName_ release];
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400331 [fields_ release];
332 [oneofs_ release];
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400333 [super dealloc];
334}
335
Protobuf Team Bot8f799092023-01-31 07:43:13 -0800336// No need to provide -hash/-isEqual: as the instances are singletons and the
337// default from NSObject is fine.
338- (instancetype)copyWithZone:(__unused NSZone *)zone {
339 // Immutable.
340 return [self retain];
341}
342
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400343- (void)setupOneofs:(const char **)oneofNames
344 count:(uint32_t)count
345 firstHasIndex:(int32_t)firstHasIndex {
346 NSCAssert(firstHasIndex < 0, @"Should always be <0");
347 NSMutableArray *oneofs = [[NSMutableArray alloc] initWithCapacity:count];
348 for (uint32_t i = 0, hasIndex = firstHasIndex; i < count; ++i, --hasIndex) {
349 const char *name = oneofNames[i];
350 NSArray *fieldsForOneof = NewFieldsArrayForHasIndex(hasIndex, fields_);
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400351 NSCAssert(fieldsForOneof.count > 0, @"No fields for this oneof? (%s:%d)", name, hasIndex);
352 GPBOneofDescriptor *oneofDescriptor = [[GPBOneofDescriptor alloc] initWithName:name
353 fields:fieldsForOneof];
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400354 [oneofs addObject:oneofDescriptor];
355 [oneofDescriptor release];
356 [fieldsForOneof release];
357 }
358 oneofs_ = oneofs;
359}
360
361- (void)setupExtraTextInfo:(const char *)extraTextFormatInfo {
362 // Extra info is a compile time option, so skip the work if not needed.
363 if (extraTextFormatInfo) {
364 NSValue *extraInfoValue = [NSValue valueWithPointer:extraTextFormatInfo];
365 for (GPBFieldDescriptor *fieldDescriptor in fields_) {
366 if (fieldDescriptor->description_->flags & GPBFieldTextFormatNameCustom) {
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400367 objc_setAssociatedObject(fieldDescriptor, &kTextFormatExtraValueKey, extraInfoValue,
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400368 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
369 }
370 }
371 }
372}
373
374- (void)setupExtensionRanges:(const GPBExtensionRange *)ranges count:(int32_t)count {
375 extensionRanges_ = ranges;
376 extensionRangesCount_ = count;
377}
378
Dave MacLachlan74956e12019-12-17 17:32:09 -0800379- (void)setupContainingMessageClass:(Class)messageClass {
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400380 objc_setAssociatedObject(self, &kParentClassValueKey, messageClass, OBJC_ASSOCIATION_ASSIGN);
Dave MacLachlan74956e12019-12-17 17:32:09 -0800381}
382
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400383- (void)setupContainingMessageClassName:(const char *)msgClassName {
Protobuf Team Bot663fca12023-01-31 09:07:43 -0800384 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30003,
385 time_to_remove_this_old_version_shim);
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400386 // Note: Only fetch the class here, can't send messages to it because
387 // that could cause cycles back to this class within +initialize if
388 // two messages have each other in fields (i.e. - they build a graph).
Dave MacLachlan74956e12019-12-17 17:32:09 -0800389 Class clazz = objc_getClass(msgClassName);
390 NSAssert(clazz, @"Class %s not defined", msgClassName);
391 [self setupContainingMessageClass:clazz];
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400392}
393
394- (void)setupMessageClassNameSuffix:(NSString *)suffix {
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800395 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30007,
396 time_to_remove_this_old_version_shim);
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400397 if (suffix.length) {
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400398 objc_setAssociatedObject(self, &kClassNameSuffixKey, suffix, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400399 }
400}
401
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400402- (NSString *)name {
403 return NSStringFromClass(messageClass_);
404}
405
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800406- (GPBFileDescriptor *)file {
407 @synchronized(self) {
408 GPBFileDescriptor *result = objc_getAssociatedObject(self, &kFileDescriptorCacheKey);
409 if (!result) {
410#if defined(DEBUG) && DEBUG
411 NSAssert(fileDescription_ != NULL, @"Internal error in generation/startup");
412#endif
413 // `package` and `prefix` can both be NULL if there wasn't one for the file.
414 NSString *package = fileDescription_->package ? @(fileDescription_->package) : @"";
415 if (fileDescription_->prefix) {
416 result = [[GPBFileDescriptor alloc] initWithPackage:package
417 objcPrefix:@(fileDescription_->prefix)
418 syntax:fileDescription_->syntax];
419
420 } else {
421 result = [[GPBFileDescriptor alloc] initWithPackage:package
422 syntax:fileDescription_->syntax];
423 }
424 objc_setAssociatedObject(result, &kFileDescriptorCacheKey, result,
425 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
426 }
427 return result;
428 }
429}
430
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400431- (GPBDescriptor *)containingType {
Dave MacLachlan74956e12019-12-17 17:32:09 -0800432 Class parentClass = objc_getAssociatedObject(self, &kParentClassValueKey);
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400433 return [parentClass descriptor];
434}
435
436- (NSString *)fullName {
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800437 GPBDescriptor *parent = self.containingType;
438 if (messageName_) {
439 if (parent) {
440 return [NSString stringWithFormat:@"%@.%@", parent.fullName, messageName_];
441 }
442 if (fileDescription_->package) {
443 return [NSString stringWithFormat:@"%s.%@", fileDescription_->package, messageName_];
444 }
445 return messageName_;
446 }
447
448 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30007,
449 time_to_remove_this_old_approach);
450 // NOTE: When this code path is removed, this also means this api can't return nil any more but
451 // that would be a breaking code change (not longer a Swift optional), so changing that will be
452 // harder.
453
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400454 NSString *className = NSStringFromClass(self.messageClass);
455 GPBFileDescriptor *file = self.file;
456 NSString *objcPrefix = file.objcPrefix;
457 if (objcPrefix && ![className hasPrefix:objcPrefix]) {
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400458 NSAssert(0, @"Class didn't have correct prefix? (%@ - %@)", className, objcPrefix);
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400459 return nil;
460 }
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400461
462 NSString *name = nil;
463 if (parent) {
464 NSString *parentClassName = NSStringFromClass(parent.messageClass);
465 // The generator will add _Class to avoid reserved words, drop it.
466 NSString *suffix = objc_getAssociatedObject(parent, &kClassNameSuffixKey);
467 if (suffix) {
468 if (![parentClassName hasSuffix:suffix]) {
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400469 NSAssert(0, @"ParentMessage class didn't have correct suffix? (%@ - %@)", className,
470 suffix);
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400471 return nil;
472 }
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400473 parentClassName = [parentClassName substringToIndex:(parentClassName.length - suffix.length)];
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400474 }
475 NSString *parentPrefix = [parentClassName stringByAppendingString:@"_"];
476 if (![className hasPrefix:parentPrefix]) {
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400477 NSAssert(0, @"Class didn't have the correct parent name prefix? (%@ - %@)", parentPrefix,
478 className);
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400479 return nil;
480 }
481 name = [className substringFromIndex:parentPrefix.length];
482 } else {
483 name = [className substringFromIndex:objcPrefix.length];
484 }
485
486 // The generator will add _Class to avoid reserved words, drop it.
487 NSString *suffix = objc_getAssociatedObject(self, &kClassNameSuffixKey);
488 if (suffix) {
489 if (![name hasSuffix:suffix]) {
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400490 NSAssert(0, @"Message class didn't have correct suffix? (%@ - %@)", name, suffix);
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400491 return nil;
492 }
493 name = [name substringToIndex:(name.length - suffix.length)];
494 }
495
496 NSString *prefix = (parent != nil ? parent.fullName : file.package);
497 NSString *result;
498 if (prefix.length > 0) {
499 result = [NSString stringWithFormat:@"%@.%@", prefix, name];
500 } else {
501 result = name;
502 }
503 return result;
504}
505
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400506- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
507 for (GPBFieldDescriptor *descriptor in fields_) {
508 if (GPBFieldNumber(descriptor) == fieldNumber) {
509 return descriptor;
510 }
511 }
512 return nil;
513}
514
515- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
516 for (GPBFieldDescriptor *descriptor in fields_) {
517 if ([descriptor.name isEqual:name]) {
518 return descriptor;
519 }
520 }
521 return nil;
522}
523
524- (GPBOneofDescriptor *)oneofWithName:(NSString *)name {
525 for (GPBOneofDescriptor *descriptor in oneofs_) {
526 if ([descriptor.name isEqual:name]) {
527 return descriptor;
528 }
529 }
530 return nil;
531}
532
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400533@end
534
535@implementation GPBFileDescriptor {
536 NSString *package_;
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400537 NSString *objcPrefix_;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400538 GPBFileSyntax syntax_;
539}
540
541@synthesize package = package_;
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400542@synthesize objcPrefix = objcPrefix_;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400543@synthesize syntax = syntax_;
544
545- (instancetype)initWithPackage:(NSString *)package
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400546 objcPrefix:(NSString *)objcPrefix
547 syntax:(GPBFileSyntax)syntax {
548 self = [super init];
549 if (self) {
550 package_ = [package copy];
551 objcPrefix_ = [objcPrefix copy];
552 syntax_ = syntax;
553 }
554 return self;
555}
556
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400557- (instancetype)initWithPackage:(NSString *)package syntax:(GPBFileSyntax)syntax {
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400558 self = [super init];
559 if (self) {
560 package_ = [package copy];
561 syntax_ = syntax;
562 }
563 return self;
564}
565
Sergio Campamá71f4a9c2016-06-14 06:28:22 -0700566- (void)dealloc {
567 [package_ release];
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400568 [objcPrefix_ release];
Sergio Campamá71f4a9c2016-06-14 06:28:22 -0700569 [super dealloc];
570}
571
Protobuf Team Botf17a6292023-01-27 11:27:21 -0800572- (BOOL)isEqual:(id)other {
573 if (other == self) {
574 return YES;
575 }
576 if (![other isKindOfClass:[GPBFileDescriptor class]]) {
577 return NO;
578 }
579 GPBFileDescriptor *otherFile = other;
580 // objcPrefix can be nil, otherwise, straight up compare.
581 return (syntax_ == otherFile->syntax_ && [package_ isEqual:otherFile->package_] &&
582 (objcPrefix_ == otherFile->objcPrefix_ ||
583 (otherFile->objcPrefix_ && [objcPrefix_ isEqual:otherFile->objcPrefix_])));
584}
585
586- (NSUInteger)hash {
587 // The prefix is recommended to be the same for a given package, so just hash
588 // the package.
589 return [package_ hash];
590}
591
Protobuf Team Bot8f799092023-01-31 07:43:13 -0800592- (instancetype)copyWithZone:(__unused NSZone *)zone {
593 // Immutable.
594 return [self retain];
595}
596
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400597@end
598
599@implementation GPBOneofDescriptor
600
601@synthesize fields = fields_;
602
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400603- (instancetype)initWithName:(const char *)name fields:(NSArray *)fields {
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400604 self = [super init];
605 if (self) {
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400606 name_ = name;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400607 fields_ = [fields retain];
608 for (GPBFieldDescriptor *fieldDesc in fields) {
609 fieldDesc->containingOneof_ = self;
610 }
611
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400612 caseSel_ = SelFromStrings(NULL, name, "OneOfCase", NO);
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400613 }
614 return self;
615}
616
617- (void)dealloc {
618 [fields_ release];
619 [super dealloc];
620}
621
Protobuf Team Bot8f799092023-01-31 07:43:13 -0800622// No need to provide -hash/-isEqual: as the instances are singletons and the
623// default from NSObject is fine.
624- (instancetype)copyWithZone:(__unused NSZone *)zone {
625 // Immutable.
626 return [self retain];
627}
628
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400629- (NSString *)name {
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400630 return (NSString *_Nonnull)@(name_);
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400631}
632
633- (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber {
634 for (GPBFieldDescriptor *descriptor in fields_) {
635 if (GPBFieldNumber(descriptor) == fieldNumber) {
636 return descriptor;
637 }
638 }
639 return nil;
640}
641
642- (GPBFieldDescriptor *)fieldWithName:(NSString *)name {
643 for (GPBFieldDescriptor *descriptor in fields_) {
644 if ([descriptor.name isEqual:name]) {
645 return descriptor;
646 }
647 }
648 return nil;
649}
650
651@end
652
653uint32_t GPBFieldTag(GPBFieldDescriptor *self) {
654 GPBMessageFieldDescription *description = self->description_;
655 GPBWireFormat format;
656 if ((description->flags & GPBFieldMapKeyMask) != 0) {
657 // Maps are repeated messages on the wire.
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400658 format = GPBWireFormatForType(GPBDataTypeMessage, NO);
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400659 } else {
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400660 format =
661 GPBWireFormatForType(description->dataType, ((description->flags & GPBFieldPacked) != 0));
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400662 }
663 return GPBWireFormatMakeTag(description->number, format);
664}
665
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400666uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) {
667 GPBMessageFieldDescription *description = self->description_;
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400668 NSCAssert((description->flags & GPBFieldRepeated) != 0, @"Only valid on repeated fields");
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400669 GPBWireFormat format =
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400670 GPBWireFormatForType(description->dataType, ((description->flags & GPBFieldPacked) == 0));
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400671 return GPBWireFormatMakeTag(description->number, format);
672}
673
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400674@implementation GPBFieldDescriptor {
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400675 GPBGenericValue defaultValue_;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400676
677 // Message ivars
678 Class msgClass_;
679
680 // Enum ivars.
Protobuf Team Bot7140f6f2022-11-14 09:04:23 -0800681 GPBEnumDescriptor *enumDescriptor_;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400682}
683
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400684@synthesize msgClass = msgClass_;
685@synthesize containingOneof = containingOneof_;
686
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400687- (instancetype)initWithFieldDescription:(void *)description
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800688 descriptorFlags:(GPBDescriptorInitializationFlags)descriptorFlags
689 fileSyntax:(GPBFileSyntax)fileSyntax {
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400690 if ((self = [super init])) {
Protobuf Team Bot83677a92022-11-23 10:52:09 -0800691 BOOL includesDefault =
Protobuf Team Bot48ff4f62023-01-26 11:37:19 -0800692 (descriptorFlags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0;
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400693 GPBMessageFieldDescription *coreDesc;
694 if (includesDefault) {
695 coreDesc = &(((GPBMessageFieldDescriptionWithDefault *)description)->core);
696 } else {
697 coreDesc = description;
698 }
699 description_ = coreDesc;
700 getSel_ = sel_getUid(coreDesc->name);
701 setSel_ = SelFromStrings("set", coreDesc->name, NULL, YES);
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400702
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400703 GPBDataType dataType = coreDesc->dataType;
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400704 BOOL isMessage = GPBDataTypeIsMessage(dataType);
705 BOOL isMapOrArray = GPBFieldIsMapOrArray(self);
706
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -0800707 // If the ClosedEnum flag wasn't known (i.e. generated code from an older
708 // version), compute the flag for the rest of the runtime.
Protobuf Team Bot48ff4f62023-01-26 11:37:19 -0800709 if ((descriptorFlags & GPBDescriptorInitializationFlag_ClosedEnumSupportKnown) == 0) {
Protobuf Team Bot663fca12023-01-31 09:07:43 -0800710 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30005,
711 time_to_remove_closed_enum_fallback);
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -0800712 // NOTE: This isn't correct, it is using the syntax of the file that
713 // declared the field, not the syntax of the file that declared the
714 // enum; but for older generated code, that's all we have and that happens
715 // to be what the runtime was doing (even though it was wrong). This is
716 // only wrong in the rare cases an enum is declared in a proto3 syntax
717 // file but used for a field in the proto2 syntax file.
Protobuf Team Botf4c79722023-02-08 06:29:16 -0800718 BOOL isClosedEnum = (dataType == GPBDataTypeEnum && fileSyntax != GPBFileSyntaxProto3);
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -0800719 if (isClosedEnum) {
720 coreDesc->flags |= GPBFieldClosedEnum;
721 }
722 }
723
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400724 if (isMapOrArray) {
725 // map<>/repeated fields get a *Count property (inplace of a has*) to
726 // support checking if there are any entries without triggering
727 // autocreation.
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400728 hasOrCountSel_ = SelFromStrings(NULL, coreDesc->name, "_Count", NO);
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400729 } else {
Thomas Van Lentendddeed22020-04-24 13:40:59 -0400730 // It is a single field; it gets has/setHas selectors if...
Thomas Van Lenten8d224b42020-04-08 11:34:37 -0400731 // - not in a oneof (negative has index)
732 // - not clearing on zero
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400733 if ((coreDesc->hasIndex >= 0) && ((coreDesc->flags & GPBFieldClearHasIvarOnZero) == 0)) {
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400734 hasOrCountSel_ = SelFromStrings("has", coreDesc->name, NULL, NO);
735 setHasSel_ = SelFromStrings("setHas", coreDesc->name, NULL, YES);
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400736 }
737 }
738
739 // Extra type specific data.
740 if (isMessage) {
Thomas Van Lenten337ec302016-08-16 11:26:49 -0400741 // Note: Only fetch the class here, can't send messages to it because
742 // that could cause cycles back to this class within +initialize if
743 // two messages have each other in fields (i.e. - they build a graph).
Protobuf Team Bot5e6fbbb2023-02-08 07:30:33 -0800744 msgClass_ = coreDesc->dataTypeSpecific.clazz;
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400745 } else if (dataType == GPBDataTypeEnum) {
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -0800746 enumDescriptor_ = coreDesc->dataTypeSpecific.enumDescFunc();
747#if defined(DEBUG) && DEBUG
Protobuf Team Bot7140f6f2022-11-14 09:04:23 -0800748 NSAssert((coreDesc->flags & GPBFieldHasEnumDescriptor) != 0,
749 @"Field must have GPBFieldHasEnumDescriptor set");
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -0800750#endif // DEBUG
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400751 }
752
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400753 // Non map<>/repeated fields can have defaults in proto2 syntax.
754 if (!isMapOrArray && includesDefault) {
755 defaultValue_ = ((GPBMessageFieldDescriptionWithDefault *)description)->defaultValue;
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400756 if (dataType == GPBDataTypeBytes) {
757 // Data stored as a length prefixed (network byte order) c-string in
758 // descriptor structure.
759 const uint8_t *bytes = (const uint8_t *)defaultValue_.valueData;
760 if (bytes) {
Thomas Van Lentend570d482018-01-31 15:25:13 -0500761 uint32_t length;
762 memcpy(&length, bytes, sizeof(length));
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400763 length = ntohl(length);
764 bytes += sizeof(length);
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400765 defaultValue_.valueData = [[NSData alloc] initWithBytes:bytes length:length];
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400766 }
767 }
768 }
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400769 }
770 return self;
771}
772
773- (void)dealloc {
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400774 if (description_->dataType == GPBDataTypeBytes && !(description_->flags & GPBFieldRepeated)) {
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400775 [defaultValue_.valueData release];
776 }
777 [super dealloc];
778}
779
Protobuf Team Bot8f799092023-01-31 07:43:13 -0800780// No need to provide -hash/-isEqual: as the instances are singletons and the
781// default from NSObject is fine.
782- (instancetype)copyWithZone:(__unused NSZone *)zone {
783 // Immutable.
784 return [self retain];
785}
786
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400787- (GPBDataType)dataType {
788 return description_->dataType;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400789}
790
791- (BOOL)hasDefaultValue {
792 return (description_->flags & GPBFieldHasDefaultValue) != 0;
793}
794
795- (uint32_t)number {
796 return description_->number;
797}
798
799- (NSString *)name {
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400800 return (NSString *_Nonnull)@(description_->name);
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400801}
802
803- (BOOL)isRequired {
804 return (description_->flags & GPBFieldRequired) != 0;
805}
806
807- (BOOL)isOptional {
808 return (description_->flags & GPBFieldOptional) != 0;
809}
810
811- (GPBFieldType)fieldType {
812 GPBFieldFlags flags = description_->flags;
813 if ((flags & GPBFieldRepeated) != 0) {
814 return GPBFieldTypeRepeated;
815 } else if ((flags & GPBFieldMapKeyMask) != 0) {
816 return GPBFieldTypeMap;
817 } else {
818 return GPBFieldTypeSingle;
819 }
820}
821
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400822- (GPBDataType)mapKeyDataType {
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400823 switch (description_->flags & GPBFieldMapKeyMask) {
824 case GPBFieldMapKeyInt32:
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400825 return GPBDataTypeInt32;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400826 case GPBFieldMapKeyInt64:
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400827 return GPBDataTypeInt64;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400828 case GPBFieldMapKeyUInt32:
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400829 return GPBDataTypeUInt32;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400830 case GPBFieldMapKeyUInt64:
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400831 return GPBDataTypeUInt64;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400832 case GPBFieldMapKeySInt32:
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400833 return GPBDataTypeSInt32;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400834 case GPBFieldMapKeySInt64:
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400835 return GPBDataTypeSInt64;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400836 case GPBFieldMapKeyFixed32:
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400837 return GPBDataTypeFixed32;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400838 case GPBFieldMapKeyFixed64:
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400839 return GPBDataTypeFixed64;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400840 case GPBFieldMapKeySFixed32:
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400841 return GPBDataTypeSFixed32;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400842 case GPBFieldMapKeySFixed64:
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400843 return GPBDataTypeSFixed64;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400844 case GPBFieldMapKeyBool:
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400845 return GPBDataTypeBool;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400846 case GPBFieldMapKeyString:
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400847 return GPBDataTypeString;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400848
849 default:
850 NSAssert(0, @"Not a map type");
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400851 return GPBDataTypeInt32; // For lack of anything better.
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400852 }
853}
854
855- (BOOL)isPackable {
856 return (description_->flags & GPBFieldPacked) != 0;
857}
858
859- (BOOL)isValidEnumValue:(int32_t)value {
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400860 NSAssert(description_->dataType == GPBDataTypeEnum, @"Field Must be of type GPBDataTypeEnum");
Protobuf Team Bot7140f6f2022-11-14 09:04:23 -0800861 return enumDescriptor_.enumVerifier(value);
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400862}
863
Protobuf Team Bot47862bb2022-11-14 12:19:34 -0800864- (GPBEnumDescriptor *)enumDescriptor {
865 return enumDescriptor_;
866}
867
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400868- (GPBGenericValue)defaultValue {
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400869 // Depends on the fact that defaultValue_ is initialized either to "0/nil" or
870 // to an actual defaultValue in our initializer.
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400871 GPBGenericValue value = defaultValue_;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400872
873 if (!(description_->flags & GPBFieldRepeated)) {
874 // We special handle data and strings. If they are nil, we replace them
875 // with empty string/empty data.
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400876 GPBDataType type = description_->dataType;
877 if (type == GPBDataTypeBytes && value.valueData == nil) {
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400878 value.valueData = GPBEmptyNSData();
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400879 } else if (type == GPBDataTypeString && value.valueString == nil) {
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400880 value.valueString = @"";
881 }
882 }
883 return value;
884}
885
886- (NSString *)textFormatName {
887 if ((description_->flags & GPBFieldTextFormatNameCustom) != 0) {
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400888 NSValue *extraInfoValue = objc_getAssociatedObject(self, &kTextFormatExtraValueKey);
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400889 // Support can be left out at generation time.
890 if (!extraInfoValue) {
891 return nil;
892 }
893 const uint8_t *extraTextFormatInfo = [extraInfoValue pointerValue];
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400894 return GPBDecodeTextFormatName(extraTextFormatInfo, GPBFieldNumber(self), self.name);
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400895 }
896
897 // The logic here has to match SetCommonFieldVariables() from
Thomas Van Lenten53d8b032022-10-04 10:59:48 -0400898 // objectivec/field.cc in the proto compiler.
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400899 NSString *name = self.name;
900 NSUInteger len = [name length];
901
902 // Remove the "_p" added to reserved names.
903 if ([name hasSuffix:@"_p"]) {
904 name = [name substringToIndex:(len - 2)];
905 len = [name length];
906 }
907
908 // Remove "Array" from the end for repeated fields.
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400909 if (((description_->flags & GPBFieldRepeated) != 0) && [name hasSuffix:@"Array"]) {
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400910 name = [name substringToIndex:(len - 5)];
911 len = [name length];
912 }
913
914 // Groups vs. other fields.
Thomas Van Lentend846b0b2015-06-08 16:24:57 -0400915 if (description_->dataType == GPBDataTypeGroup) {
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400916 // Just capitalize the first letter.
917 unichar firstChar = [name characterAtIndex:0];
918 if (firstChar >= 'a' && firstChar <= 'z') {
919 NSString *firstCharString =
920 [NSString stringWithFormat:@"%C", (unichar)(firstChar - 'a' + 'A')];
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400921 NSString *result = [name stringByReplacingCharactersInRange:NSMakeRange(0, 1)
922 withString:firstCharString];
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400923 return result;
924 }
925 return name;
926
927 } else {
928 // Undo the CamelCase.
929 NSMutableString *result = [NSMutableString stringWithCapacity:len];
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400930 for (uint32_t i = 0; i < len; i++) {
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400931 unichar c = [name characterAtIndex:i];
932 if (c >= 'A' && c <= 'Z') {
933 if (i > 0) {
934 [result appendFormat:@"_%C", (unichar)(c - 'A' + 'a')];
935 } else {
936 [result appendFormat:@"%C", c];
937 }
938 } else {
939 [result appendFormat:@"%C", c];
940 }
941 }
942 return result;
943 }
944}
945
946@end
947
948@implementation GPBEnumDescriptor {
949 NSString *name_;
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400950 // valueNames_ is a single c string with all of the value names appended
951 // together, each null terminated. -calcValueNameOffsets fills in
952 // nameOffsets_ with the offsets to allow quicker access to the individual
953 // names.
954 const char *valueNames_;
955 const int32_t *values_;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400956 GPBEnumValidationFunc enumVerifier_;
957 const uint8_t *extraTextFormatInfo_;
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400958 uint32_t *nameOffsets_;
959 uint32_t valueCount_;
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -0800960 uint32_t flags_;
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400961}
962
963@synthesize name = name_;
964@synthesize enumVerifier = enumVerifier_;
965
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400966+ (instancetype)allocDescriptorForName:(NSString *)name
967 valueNames:(const char *)valueNames
968 values:(const int32_t *)values
969 count:(uint32_t)valueCount
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -0800970 enumVerifier:(GPBEnumValidationFunc)enumVerifier
971 flags:(GPBEnumDescriptorInitializationFlags)flags {
Protobuf Team Bot6e5a01b2023-01-31 13:11:17 -0800972 // Compute the unknown flags by this version of the runtime and then check the passed in flags
973 // (from the generated code) to detect when sources from a newer version are being used with an
974 // older runtime.
975 GPBEnumDescriptorInitializationFlags unknownFlags =
976 ~(GPBEnumDescriptorInitializationFlag_IsClosed);
977 if ((flags & unknownFlags) != 0) {
978 GPBRuntimeMatchFailure();
979 }
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400980 GPBEnumDescriptor *descriptor = [[self alloc] initWithName:name
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400981 valueNames:valueNames
982 values:values
983 count:valueCount
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -0800984 enumVerifier:enumVerifier
985 flags:flags];
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400986 return descriptor;
987}
988
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400989+ (instancetype)allocDescriptorForName:(NSString *)name
990 valueNames:(const char *)valueNames
991 values:(const int32_t *)values
992 count:(uint32_t)valueCount
993 enumVerifier:(GPBEnumValidationFunc)enumVerifier
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -0800994 flags:(GPBEnumDescriptorInitializationFlags)flags
Thomas Van Lenten189f6322022-09-19 17:21:13 -0400995 extraTextFormatInfo:(const char *)extraTextFormatInfo {
Thomas Van Lenten30650d82015-05-01 08:57:16 -0400996 // Call the common case.
997 GPBEnumDescriptor *descriptor = [self allocDescriptorForName:name
Thomas Van Lenten79a23c42016-03-17 10:04:21 -0400998 valueNames:valueNames
999 values:values
1000 count:valueCount
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -08001001 enumVerifier:enumVerifier
1002 flags:flags];
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001003 // Set the extra info.
1004 descriptor->extraTextFormatInfo_ = (const uint8_t *)extraTextFormatInfo;
1005 return descriptor;
1006}
1007
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -08001008+ (instancetype)allocDescriptorForName:(NSString *)name
1009 valueNames:(const char *)valueNames
1010 values:(const int32_t *)values
1011 count:(uint32_t)valueCount
1012 enumVerifier:(GPBEnumValidationFunc)enumVerifier {
Protobuf Team Bot663fca12023-01-31 09:07:43 -08001013 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30005,
1014 time_to_remove_this_old_version_shim);
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -08001015 return [self allocDescriptorForName:name
1016 valueNames:valueNames
1017 values:values
1018 count:valueCount
1019 enumVerifier:enumVerifier
1020 flags:GPBEnumDescriptorInitializationFlag_None];
1021}
1022
1023+ (instancetype)allocDescriptorForName:(NSString *)name
1024 valueNames:(const char *)valueNames
1025 values:(const int32_t *)values
1026 count:(uint32_t)valueCount
1027 enumVerifier:(GPBEnumValidationFunc)enumVerifier
1028 extraTextFormatInfo:(const char *)extraTextFormatInfo {
Protobuf Team Bot663fca12023-01-31 09:07:43 -08001029 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30005,
1030 time_to_remove_this_old_version_shim);
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -08001031 return [self allocDescriptorForName:name
1032 valueNames:valueNames
1033 values:values
1034 count:valueCount
1035 enumVerifier:enumVerifier
1036 flags:GPBEnumDescriptorInitializationFlag_None
1037 extraTextFormatInfo:extraTextFormatInfo];
1038}
1039
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001040- (instancetype)initWithName:(NSString *)name
Thomas Van Lenten79a23c42016-03-17 10:04:21 -04001041 valueNames:(const char *)valueNames
1042 values:(const int32_t *)values
1043 count:(uint32_t)valueCount
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -08001044 enumVerifier:(GPBEnumValidationFunc)enumVerifier
1045 flags:(GPBEnumDescriptorInitializationFlags)flags {
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001046 if ((self = [super init])) {
1047 name_ = [name copy];
Thomas Van Lenten79a23c42016-03-17 10:04:21 -04001048 valueNames_ = valueNames;
1049 values_ = values;
1050 valueCount_ = valueCount;
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001051 enumVerifier_ = enumVerifier;
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -08001052 flags_ = flags;
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001053 }
1054 return self;
1055}
1056
Thomas Van Lenten79a23c42016-03-17 10:04:21 -04001057- (void)dealloc {
1058 [name_ release];
1059 if (nameOffsets_) free(nameOffsets_);
1060 [super dealloc];
1061}
1062
Protobuf Team Bot8f799092023-01-31 07:43:13 -08001063// No need to provide -hash/-isEqual: as the instances are singletons and the
1064// default from NSObject is fine.
1065- (instancetype)copyWithZone:(__unused NSZone *)zone {
1066 // Immutable.
1067 return [self retain];
1068}
1069
Protobuf Team Bot7bb699b2022-11-15 08:59:25 -08001070- (BOOL)isClosed {
1071 return (flags_ & GPBEnumDescriptorInitializationFlag_IsClosed) != 0;
1072}
1073
Thomas Van Lenten79a23c42016-03-17 10:04:21 -04001074- (void)calcValueNameOffsets {
1075 @synchronized(self) {
1076 if (nameOffsets_ != NULL) {
1077 return;
1078 }
1079 uint32_t *offsets = malloc(valueCount_ * sizeof(uint32_t));
Parveen Bhatia29f27bf2018-10-19 15:37:00 -04001080 if (!offsets) return;
Thomas Van Lenten79a23c42016-03-17 10:04:21 -04001081 const char *scan = valueNames_;
1082 for (uint32_t i = 0; i < valueCount_; ++i) {
1083 offsets[i] = (uint32_t)(scan - valueNames_);
1084 while (*scan != '\0') ++scan;
1085 ++scan; // Step over the null.
1086 }
1087 nameOffsets_ = offsets;
1088 }
1089}
1090
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001091- (NSString *)enumNameForValue:(int32_t)number {
Thomas Van Lenten79a23c42016-03-17 10:04:21 -04001092 for (uint32_t i = 0; i < valueCount_; ++i) {
1093 if (values_[i] == number) {
leovitch28049022018-05-29 21:08:00 +09001094 return [self getEnumNameForIndex:i];
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001095 }
1096 }
1097 return nil;
1098}
1099
1100- (BOOL)getValue:(int32_t *)outValue forEnumName:(NSString *)name {
1101 // Must have the prefix.
1102 NSUInteger prefixLen = name_.length + 1;
1103 if ((name.length <= prefixLen) || ![name hasPrefix:name_] ||
1104 ([name characterAtIndex:prefixLen - 1] != '_')) {
1105 return NO;
1106 }
1107
1108 // Skip over the prefix.
1109 const char *nameAsCStr = [name UTF8String];
1110 nameAsCStr += prefixLen;
1111
Protobuf Team Bot8be2ce32022-12-19 11:47:50 -08001112 [self calcValueNameOffsets];
Parveen Bhatia29f27bf2018-10-19 15:37:00 -04001113 if (nameOffsets_ == NULL) return NO;
Thomas Van Lenten79a23c42016-03-17 10:04:21 -04001114
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001115 // Find it.
Thomas Van Lenten79a23c42016-03-17 10:04:21 -04001116 for (uint32_t i = 0; i < valueCount_; ++i) {
1117 const char *valueName = valueNames_ + nameOffsets_[i];
1118 if (strcmp(nameAsCStr, valueName) == 0) {
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001119 if (outValue) {
Thomas Van Lenten79a23c42016-03-17 10:04:21 -04001120 *outValue = values_[i];
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001121 }
1122 return YES;
1123 }
1124 }
1125 return NO;
1126}
1127
Thomas Van Lentenbe0d7f62016-06-27 14:24:59 -04001128- (BOOL)getValue:(int32_t *)outValue forEnumTextFormatName:(NSString *)textFormatName {
Protobuf Team Bot8be2ce32022-12-19 11:47:50 -08001129 [self calcValueNameOffsets];
Thomas Van Lenten189f6322022-09-19 17:21:13 -04001130 if (nameOffsets_ == NULL) return NO;
Dimitris Koutsogiorgas37ca94f2016-06-24 17:40:29 -07001131
Thomas Van Lenten189f6322022-09-19 17:21:13 -04001132 for (uint32_t i = 0; i < valueCount_; ++i) {
1133 NSString *valueTextFormatName = [self getEnumTextFormatNameForIndex:i];
1134 if ([valueTextFormatName isEqual:textFormatName]) {
1135 if (outValue) {
1136 *outValue = values_[i];
1137 }
1138 return YES;
Dimitris Koutsogiorgas37ca94f2016-06-24 17:40:29 -07001139 }
Thomas Van Lenten189f6322022-09-19 17:21:13 -04001140 }
1141 return NO;
Dimitris Koutsogiorgas37ca94f2016-06-24 17:40:29 -07001142}
1143
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001144- (NSString *)textFormatNameForValue:(int32_t)number {
1145 // Find the EnumValue descriptor and its index.
Thomas Van Lenten79a23c42016-03-17 10:04:21 -04001146 BOOL foundIt = NO;
1147 uint32_t valueDescriptorIndex;
Thomas Van Lenten189f6322022-09-19 17:21:13 -04001148 for (valueDescriptorIndex = 0; valueDescriptorIndex < valueCount_; ++valueDescriptorIndex) {
Thomas Van Lenten79a23c42016-03-17 10:04:21 -04001149 if (values_[valueDescriptorIndex] == number) {
1150 foundIt = YES;
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001151 break;
1152 }
1153 }
1154
Thomas Van Lenten79a23c42016-03-17 10:04:21 -04001155 if (!foundIt) {
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001156 return nil;
1157 }
leovitch28049022018-05-29 21:08:00 +09001158 return [self getEnumTextFormatNameForIndex:valueDescriptorIndex];
1159}
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001160
leovitch28049022018-05-29 21:08:00 +09001161- (uint32_t)enumNameCount {
1162 return valueCount_;
1163}
1164
1165- (NSString *)getEnumNameForIndex:(uint32_t)index {
Protobuf Team Bot8be2ce32022-12-19 11:47:50 -08001166 [self calcValueNameOffsets];
Parveen Bhatia29f27bf2018-10-19 15:37:00 -04001167 if (nameOffsets_ == NULL) return nil;
leovitch28049022018-05-29 21:08:00 +09001168
1169 if (index >= valueCount_) {
1170 return nil;
1171 }
1172 const char *valueName = valueNames_ + nameOffsets_[index];
1173 NSString *fullName = [NSString stringWithFormat:@"%@_%s", name_, valueName];
1174 return fullName;
1175}
1176
1177- (NSString *)getEnumTextFormatNameForIndex:(uint32_t)index {
Protobuf Team Bot8be2ce32022-12-19 11:47:50 -08001178 [self calcValueNameOffsets];
Parveen Bhatia29f27bf2018-10-19 15:37:00 -04001179 if (nameOffsets_ == NULL) return nil;
leovitch28049022018-05-29 21:08:00 +09001180
1181 if (index >= valueCount_) {
1182 return nil;
1183 }
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001184 NSString *result = nil;
1185 // Naming adds an underscore between enum name and value name, skip that also.
leovitch28049022018-05-29 21:08:00 +09001186 const char *valueName = valueNames_ + nameOffsets_[index];
Thomas Van Lenten79a23c42016-03-17 10:04:21 -04001187 NSString *shortName = @(valueName);
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001188
1189 // See if it is in the map of special format handling.
1190 if (extraTextFormatInfo_) {
Thomas Van Lenten189f6322022-09-19 17:21:13 -04001191 result = GPBDecodeTextFormatName(extraTextFormatInfo_, (int32_t)index, shortName);
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001192 }
Thomas Van Lenten53d8b032022-10-04 10:59:48 -04001193 // Logic here needs to match what objectivec/enum.cc does in the proto
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001194 // compiler.
1195 if (result == nil) {
1196 NSUInteger len = [shortName length];
1197 NSMutableString *worker = [NSMutableString stringWithCapacity:len];
1198 for (NSUInteger i = 0; i < len; i++) {
1199 unichar c = [shortName characterAtIndex:i];
1200 if (i > 0 && c >= 'A' && c <= 'Z') {
1201 [worker appendString:@"_"];
1202 }
1203 [worker appendFormat:@"%c", toupper((char)c)];
1204 }
1205 result = worker;
1206 }
1207 return result;
1208}
1209
1210@end
1211
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001212@implementation GPBExtensionDescriptor {
1213 GPBGenericValue defaultValue_;
1214}
1215
Dave MacLachlan74956e12019-12-17 17:32:09 -08001216- (instancetype)initWithExtensionDescription:(GPBExtensionDescription *)desc
1217 usesClassRefs:(BOOL)usesClassRefs {
Protobuf Team Bot6e5a01b2023-01-31 13:11:17 -08001218 // Compute the unknown options by this version of the runtime and then check the passed in
1219 // descriptor's options (from the generated code) to detect when sources from a newer version are
1220 // being used with an older runtime.
1221 GPBExtensionOptions unknownOptions =
1222 ~(GPBExtensionRepeated | GPBExtensionPacked | GPBExtensionSetWireFormat);
1223 if ((desc->options & unknownOptions) != 0) {
1224 GPBRuntimeMatchFailure();
1225 }
1226
Protobuf Team Bot5e6fbbb2023-02-08 07:30:33 -08001227#if defined(DEBUG) && DEBUG
1228 NSAssert(usesClassRefs, @"Internal error: all extensions should have class refs");
1229#endif
1230
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001231 if ((self = [super init])) {
Dave MacLachlan74956e12019-12-17 17:32:09 -08001232 description_ = desc;
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001233
1234 GPBDataType type = description_->dataType;
1235 if (type == GPBDataTypeBytes) {
1236 // Data stored as a length prefixed c-string in descriptor records.
Thomas Van Lenten189f6322022-09-19 17:21:13 -04001237 const uint8_t *bytes = (const uint8_t *)description_->defaultValue.valueData;
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001238 if (bytes) {
Thomas Van Lentend570d482018-01-31 15:25:13 -05001239 uint32_t length;
1240 memcpy(&length, bytes, sizeof(length));
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001241 // The length is stored in network byte order.
1242 length = ntohl(length);
1243 bytes += sizeof(length);
Thomas Van Lenten189f6322022-09-19 17:21:13 -04001244 defaultValue_.valueData = [[NSData alloc] initWithBytes:bytes length:length];
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001245 }
1246 } else if (type == GPBDataTypeMessage || type == GPBDataTypeGroup) {
1247 // The default is looked up in -defaultValue instead since extensions
Protobuf Team Bot5e6fbbb2023-02-08 07:30:33 -08001248 // aren't common, we avoid the hit startup hit and it avoids initialization
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001249 // order issues.
1250 } else {
Dave MacLachlan74956e12019-12-17 17:32:09 -08001251 defaultValue_ = description_->defaultValue;
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001252 }
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001253 }
1254 return self;
1255}
1256
Dave MacLachlan74956e12019-12-17 17:32:09 -08001257- (instancetype)initWithExtensionDescription:(GPBExtensionDescription *)desc {
Protobuf Team Bot663fca12023-01-31 09:07:43 -08001258 GPBInternalCompileAssert(GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION <= 30003,
1259 time_to_remove_this_old_version_shim);
Protobuf Team Bot5e6fbbb2023-02-08 07:30:33 -08001260
1261 const char *className = desc->messageOrGroupClass.name;
1262 if (className) {
1263 Class clazz = objc_lookUpClass(className);
1264 NSAssert(clazz != Nil, @"Class %s not defined", className);
1265 desc->messageOrGroupClass.clazz = clazz;
1266 }
1267
1268 const char *extendedClassName = desc->extendedClass.name;
1269 if (extendedClassName) {
1270 Class clazz = objc_lookUpClass(extendedClassName);
1271 NSAssert(clazz, @"Class %s not defined", extendedClassName);
1272 desc->extendedClass.clazz = clazz;
1273 }
1274
1275 return [self initWithExtensionDescription:desc usesClassRefs:YES];
Dave MacLachlan74956e12019-12-17 17:32:09 -08001276}
1277
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001278- (void)dealloc {
Thomas Van Lenten189f6322022-09-19 17:21:13 -04001279 if ((description_->dataType == GPBDataTypeBytes) && !GPBExtensionIsRepeated(description_)) {
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001280 [defaultValue_.valueData release];
1281 }
1282 [super dealloc];
1283}
1284
Protobuf Team Bot8f799092023-01-31 07:43:13 -08001285// No need to provide -hash/-isEqual: as the instances are singletons and the
1286// default from NSObject is fine.
Thomas Van Lenten2fb33b82022-09-20 09:14:32 -04001287- (instancetype)copyWithZone:(__unused NSZone *)zone {
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001288 // Immutable.
1289 return [self retain];
1290}
1291
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001292- (NSString *)singletonName {
Thomas Van Lenten189f6322022-09-19 17:21:13 -04001293 return (NSString *_Nonnull)@(description_->singletonName);
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001294}
1295
1296- (const char *)singletonNameC {
1297 return description_->singletonName;
1298}
1299
1300- (uint32_t)fieldNumber {
1301 return description_->fieldNumber;
1302}
1303
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001304- (GPBDataType)dataType {
1305 return description_->dataType;
1306}
1307
1308- (GPBWireFormat)wireType {
Thomas Van Lenten189f6322022-09-19 17:21:13 -04001309 return GPBWireFormatForType(description_->dataType, GPBExtensionIsPacked(description_));
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001310}
1311
1312- (GPBWireFormat)alternateWireType {
Thomas Van Lenten189f6322022-09-19 17:21:13 -04001313 NSAssert(GPBExtensionIsRepeated(description_), @"Only valid on repeated extensions");
1314 return GPBWireFormatForType(description_->dataType, !GPBExtensionIsPacked(description_));
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001315}
1316
1317- (BOOL)isRepeated {
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001318 return GPBExtensionIsRepeated(description_);
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001319}
1320
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001321- (BOOL)isPackable {
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001322 return GPBExtensionIsPacked(description_);
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001323}
1324
1325- (Class)msgClass {
Dave MacLachlan74956e12019-12-17 17:32:09 -08001326 return description_->messageOrGroupClass.clazz;
1327}
1328
1329- (Class)containingMessageClass {
1330 return description_->extendedClass.clazz;
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001331}
1332
1333- (GPBEnumDescriptor *)enumDescriptor {
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001334 if (description_->dataType == GPBDataTypeEnum) {
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001335 GPBEnumDescriptor *enumDescriptor = description_->enumDescriptorFunc();
1336 return enumDescriptor;
1337 }
1338 return nil;
1339}
1340
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001341- (id)defaultValue {
1342 if (GPBExtensionIsRepeated(description_)) {
1343 return nil;
1344 }
1345
1346 switch (description_->dataType) {
1347 case GPBDataTypeBool:
1348 return @(defaultValue_.valueBool);
1349 case GPBDataTypeFloat:
1350 return @(defaultValue_.valueFloat);
1351 case GPBDataTypeDouble:
1352 return @(defaultValue_.valueDouble);
1353 case GPBDataTypeInt32:
1354 case GPBDataTypeSInt32:
1355 case GPBDataTypeEnum:
1356 case GPBDataTypeSFixed32:
1357 return @(defaultValue_.valueInt32);
1358 case GPBDataTypeInt64:
1359 case GPBDataTypeSInt64:
1360 case GPBDataTypeSFixed64:
1361 return @(defaultValue_.valueInt64);
1362 case GPBDataTypeUInt32:
1363 case GPBDataTypeFixed32:
1364 return @(defaultValue_.valueUInt32);
1365 case GPBDataTypeUInt64:
1366 case GPBDataTypeFixed64:
1367 return @(defaultValue_.valueUInt64);
1368 case GPBDataTypeBytes:
1369 // Like message fields, the default is zero length data.
Thomas Van Lenten189f6322022-09-19 17:21:13 -04001370 return (defaultValue_.valueData ? defaultValue_.valueData : GPBEmptyNSData());
Thomas Van Lentend846b0b2015-06-08 16:24:57 -04001371 case GPBDataTypeString:
1372 // Like message fields, the default is zero length string.
1373 return (defaultValue_.valueString ? defaultValue_.valueString : @"");
1374 case GPBDataTypeGroup:
1375 case GPBDataTypeMessage:
1376 return nil;
1377 }
1378}
1379
1380- (NSComparisonResult)compareByFieldNumber:(GPBExtensionDescriptor *)other {
1381 int32_t selfNumber = description_->fieldNumber;
1382 int32_t otherNumber = other->description_->fieldNumber;
1383 if (selfNumber < otherNumber) {
1384 return NSOrderedAscending;
1385 } else if (selfNumber == otherNumber) {
1386 return NSOrderedSame;
1387 } else {
1388 return NSOrderedDescending;
1389 }
1390}
1391
Thomas Van Lenten30650d82015-05-01 08:57:16 -04001392@end
Thomas Van Lentenc8a440d2016-05-25 13:46:00 -04001393
1394#pragma clang diagnostic pop