xref: /aosp_15_r20/external/protobuf/objectivec/GPBRootObject.m (revision 1b3f573f81763fcece89efc2b6a5209149e44ab8)
1*1b3f573fSAndroid Build Coastguard Worker// Protocol Buffers - Google's data interchange format
2*1b3f573fSAndroid Build Coastguard Worker// Copyright 2008 Google Inc.  All rights reserved.
3*1b3f573fSAndroid Build Coastguard Worker// https://developers.google.com/protocol-buffers/
4*1b3f573fSAndroid Build Coastguard Worker//
5*1b3f573fSAndroid Build Coastguard Worker// Redistribution and use in source and binary forms, with or without
6*1b3f573fSAndroid Build Coastguard Worker// modification, are permitted provided that the following conditions are
7*1b3f573fSAndroid Build Coastguard Worker// met:
8*1b3f573fSAndroid Build Coastguard Worker//
9*1b3f573fSAndroid Build Coastguard Worker//     * Redistributions of source code must retain the above copyright
10*1b3f573fSAndroid Build Coastguard Worker// notice, this list of conditions and the following disclaimer.
11*1b3f573fSAndroid Build Coastguard Worker//     * Redistributions in binary form must reproduce the above
12*1b3f573fSAndroid Build Coastguard Worker// copyright notice, this list of conditions and the following disclaimer
13*1b3f573fSAndroid Build Coastguard Worker// in the documentation and/or other materials provided with the
14*1b3f573fSAndroid Build Coastguard Worker// distribution.
15*1b3f573fSAndroid Build Coastguard Worker//     * Neither the name of Google Inc. nor the names of its
16*1b3f573fSAndroid Build Coastguard Worker// contributors may be used to endorse or promote products derived from
17*1b3f573fSAndroid Build Coastguard Worker// this software without specific prior written permission.
18*1b3f573fSAndroid Build Coastguard Worker//
19*1b3f573fSAndroid Build Coastguard Worker// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20*1b3f573fSAndroid Build Coastguard Worker// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21*1b3f573fSAndroid Build Coastguard Worker// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22*1b3f573fSAndroid Build Coastguard Worker// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23*1b3f573fSAndroid Build Coastguard Worker// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24*1b3f573fSAndroid Build Coastguard Worker// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25*1b3f573fSAndroid Build Coastguard Worker// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26*1b3f573fSAndroid Build Coastguard Worker// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27*1b3f573fSAndroid Build Coastguard Worker// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28*1b3f573fSAndroid Build Coastguard Worker// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29*1b3f573fSAndroid Build Coastguard Worker// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30*1b3f573fSAndroid Build Coastguard Worker
31*1b3f573fSAndroid Build Coastguard Worker#import "GPBRootObject_PackagePrivate.h"
32*1b3f573fSAndroid Build Coastguard Worker
33*1b3f573fSAndroid Build Coastguard Worker#import <objc/runtime.h>
34*1b3f573fSAndroid Build Coastguard Worker
35*1b3f573fSAndroid Build Coastguard Worker#import <CoreFoundation/CoreFoundation.h>
36*1b3f573fSAndroid Build Coastguard Worker
37*1b3f573fSAndroid Build Coastguard Worker#import "GPBDescriptor.h"
38*1b3f573fSAndroid Build Coastguard Worker#import "GPBExtensionRegistry.h"
39*1b3f573fSAndroid Build Coastguard Worker#import "GPBUtilities_PackagePrivate.h"
40*1b3f573fSAndroid Build Coastguard Worker
41*1b3f573fSAndroid Build Coastguard Worker@interface GPBExtensionDescriptor (GPBRootObject)
42*1b3f573fSAndroid Build Coastguard Worker// Get singletonName as a c string.
43*1b3f573fSAndroid Build Coastguard Worker- (const char *)singletonNameC;
44*1b3f573fSAndroid Build Coastguard Worker@end
45*1b3f573fSAndroid Build Coastguard Worker
46*1b3f573fSAndroid Build Coastguard Worker// We need some object to conform to the MessageSignatureProtocol to make sure
47*1b3f573fSAndroid Build Coastguard Worker// the selectors in it are recorded in our Objective C runtime information.
48*1b3f573fSAndroid Build Coastguard Worker// GPBMessage is arguably the more "obvious" choice, but given that all messages
49*1b3f573fSAndroid Build Coastguard Worker// inherit from GPBMessage, conflicts seem likely, so we are using GPBRootObject
50*1b3f573fSAndroid Build Coastguard Worker// instead.
51*1b3f573fSAndroid Build Coastguard Worker@interface GPBRootObject () <GPBMessageSignatureProtocol>
52*1b3f573fSAndroid Build Coastguard Worker@end
53*1b3f573fSAndroid Build Coastguard Worker
54*1b3f573fSAndroid Build Coastguard Worker@implementation GPBRootObject
55*1b3f573fSAndroid Build Coastguard Worker
56*1b3f573fSAndroid Build Coastguard Worker// Taken from http://www.burtleburtle.net/bob/hash/doobs.html
57*1b3f573fSAndroid Build Coastguard Worker// Public Domain
58*1b3f573fSAndroid Build Coastguard Workerstatic uint32_t jenkins_one_at_a_time_hash(const char *key) {
59*1b3f573fSAndroid Build Coastguard Worker  uint32_t hash = 0;
60*1b3f573fSAndroid Build Coastguard Worker  for (uint32_t i = 0; key[i] != '\0'; ++i) {
61*1b3f573fSAndroid Build Coastguard Worker    hash += key[i];
62*1b3f573fSAndroid Build Coastguard Worker    hash += (hash << 10);
63*1b3f573fSAndroid Build Coastguard Worker    hash ^= (hash >> 6);
64*1b3f573fSAndroid Build Coastguard Worker  }
65*1b3f573fSAndroid Build Coastguard Worker  hash += (hash << 3);
66*1b3f573fSAndroid Build Coastguard Worker  hash ^= (hash >> 11);
67*1b3f573fSAndroid Build Coastguard Worker  hash += (hash << 15);
68*1b3f573fSAndroid Build Coastguard Worker  return hash;
69*1b3f573fSAndroid Build Coastguard Worker}
70*1b3f573fSAndroid Build Coastguard Worker
71*1b3f573fSAndroid Build Coastguard Worker// Key methods for our custom CFDictionary.
72*1b3f573fSAndroid Build Coastguard Worker// Note that the dictionary lasts for the lifetime of our app, so no need
73*1b3f573fSAndroid Build Coastguard Worker// to worry about deallocation. All of the items are added to it at
74*1b3f573fSAndroid Build Coastguard Worker// startup, and so the keys don't need to be retained/released.
75*1b3f573fSAndroid Build Coastguard Worker// Keys are NULL terminated char *.
76*1b3f573fSAndroid Build Coastguard Workerstatic const void *GPBRootExtensionKeyRetain(CFAllocatorRef allocator,
77*1b3f573fSAndroid Build Coastguard Worker                                             const void *value) {
78*1b3f573fSAndroid Build Coastguard Worker#pragma unused(allocator)
79*1b3f573fSAndroid Build Coastguard Worker  return value;
80*1b3f573fSAndroid Build Coastguard Worker}
81*1b3f573fSAndroid Build Coastguard Worker
82*1b3f573fSAndroid Build Coastguard Workerstatic void GPBRootExtensionKeyRelease(CFAllocatorRef allocator,
83*1b3f573fSAndroid Build Coastguard Worker                                       const void *value) {
84*1b3f573fSAndroid Build Coastguard Worker#pragma unused(allocator)
85*1b3f573fSAndroid Build Coastguard Worker#pragma unused(value)
86*1b3f573fSAndroid Build Coastguard Worker}
87*1b3f573fSAndroid Build Coastguard Worker
88*1b3f573fSAndroid Build Coastguard Workerstatic CFStringRef GPBRootExtensionCopyKeyDescription(const void *value) {
89*1b3f573fSAndroid Build Coastguard Worker  const char *key = (const char *)value;
90*1b3f573fSAndroid Build Coastguard Worker  return CFStringCreateWithCString(kCFAllocatorDefault, key,
91*1b3f573fSAndroid Build Coastguard Worker                                   kCFStringEncodingUTF8);
92*1b3f573fSAndroid Build Coastguard Worker}
93*1b3f573fSAndroid Build Coastguard Worker
94*1b3f573fSAndroid Build Coastguard Workerstatic Boolean GPBRootExtensionKeyEqual(const void *value1,
95*1b3f573fSAndroid Build Coastguard Worker                                        const void *value2) {
96*1b3f573fSAndroid Build Coastguard Worker  const char *key1 = (const char *)value1;
97*1b3f573fSAndroid Build Coastguard Worker  const char *key2 = (const char *)value2;
98*1b3f573fSAndroid Build Coastguard Worker  return strcmp(key1, key2) == 0;
99*1b3f573fSAndroid Build Coastguard Worker}
100*1b3f573fSAndroid Build Coastguard Worker
101*1b3f573fSAndroid Build Coastguard Workerstatic CFHashCode GPBRootExtensionKeyHash(const void *value) {
102*1b3f573fSAndroid Build Coastguard Worker  const char *key = (const char *)value;
103*1b3f573fSAndroid Build Coastguard Worker  return jenkins_one_at_a_time_hash(key);
104*1b3f573fSAndroid Build Coastguard Worker}
105*1b3f573fSAndroid Build Coastguard Worker
106*1b3f573fSAndroid Build Coastguard Worker// NOTE: OSSpinLock may seem like a good fit here but Apple engineers have
107*1b3f573fSAndroid Build Coastguard Worker// pointed out that they are vulnerable to live locking on iOS in cases of
108*1b3f573fSAndroid Build Coastguard Worker// priority inversion:
109*1b3f573fSAndroid Build Coastguard Worker//   http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/
110*1b3f573fSAndroid Build Coastguard Worker//   https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html
111*1b3f573fSAndroid Build Coastguard Workerstatic dispatch_semaphore_t gExtensionSingletonDictionarySemaphore;
112*1b3f573fSAndroid Build Coastguard Workerstatic CFMutableDictionaryRef gExtensionSingletonDictionary = NULL;
113*1b3f573fSAndroid Build Coastguard Workerstatic GPBExtensionRegistry *gDefaultExtensionRegistry = NULL;
114*1b3f573fSAndroid Build Coastguard Worker
115*1b3f573fSAndroid Build Coastguard Worker+ (void)initialize {
116*1b3f573fSAndroid Build Coastguard Worker  // Ensure the global is started up.
117*1b3f573fSAndroid Build Coastguard Worker  if (!gExtensionSingletonDictionary) {
118*1b3f573fSAndroid Build Coastguard Worker    gExtensionSingletonDictionarySemaphore = dispatch_semaphore_create(1);
119*1b3f573fSAndroid Build Coastguard Worker    CFDictionaryKeyCallBacks keyCallBacks = {
120*1b3f573fSAndroid Build Coastguard Worker      // See description above for reason for using custom dictionary.
121*1b3f573fSAndroid Build Coastguard Worker      0,
122*1b3f573fSAndroid Build Coastguard Worker      GPBRootExtensionKeyRetain,
123*1b3f573fSAndroid Build Coastguard Worker      GPBRootExtensionKeyRelease,
124*1b3f573fSAndroid Build Coastguard Worker      GPBRootExtensionCopyKeyDescription,
125*1b3f573fSAndroid Build Coastguard Worker      GPBRootExtensionKeyEqual,
126*1b3f573fSAndroid Build Coastguard Worker      GPBRootExtensionKeyHash,
127*1b3f573fSAndroid Build Coastguard Worker    };
128*1b3f573fSAndroid Build Coastguard Worker    gExtensionSingletonDictionary =
129*1b3f573fSAndroid Build Coastguard Worker        CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &keyCallBacks,
130*1b3f573fSAndroid Build Coastguard Worker                                  &kCFTypeDictionaryValueCallBacks);
131*1b3f573fSAndroid Build Coastguard Worker    gDefaultExtensionRegistry = [[GPBExtensionRegistry alloc] init];
132*1b3f573fSAndroid Build Coastguard Worker  }
133*1b3f573fSAndroid Build Coastguard Worker
134*1b3f573fSAndroid Build Coastguard Worker  if ([self superclass] == [GPBRootObject class]) {
135*1b3f573fSAndroid Build Coastguard Worker    // This is here to start up all the per file "Root" subclasses.
136*1b3f573fSAndroid Build Coastguard Worker    // This must be done in initialize to enforce thread safety of start up of
137*1b3f573fSAndroid Build Coastguard Worker    // the protocol buffer library.
138*1b3f573fSAndroid Build Coastguard Worker    [self extensionRegistry];
139*1b3f573fSAndroid Build Coastguard Worker  }
140*1b3f573fSAndroid Build Coastguard Worker}
141*1b3f573fSAndroid Build Coastguard Worker
142*1b3f573fSAndroid Build Coastguard Worker+ (GPBExtensionRegistry *)extensionRegistry {
143*1b3f573fSAndroid Build Coastguard Worker  // Is overridden in all the subclasses that provide extensions to provide the
144*1b3f573fSAndroid Build Coastguard Worker  // per class one.
145*1b3f573fSAndroid Build Coastguard Worker  return gDefaultExtensionRegistry;
146*1b3f573fSAndroid Build Coastguard Worker}
147*1b3f573fSAndroid Build Coastguard Worker
148*1b3f573fSAndroid Build Coastguard Worker+ (void)globallyRegisterExtension:(GPBExtensionDescriptor *)field {
149*1b3f573fSAndroid Build Coastguard Worker  const char *key = [field singletonNameC];
150*1b3f573fSAndroid Build Coastguard Worker  dispatch_semaphore_wait(gExtensionSingletonDictionarySemaphore,
151*1b3f573fSAndroid Build Coastguard Worker                          DISPATCH_TIME_FOREVER);
152*1b3f573fSAndroid Build Coastguard Worker  CFDictionarySetValue(gExtensionSingletonDictionary, key, field);
153*1b3f573fSAndroid Build Coastguard Worker  dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore);
154*1b3f573fSAndroid Build Coastguard Worker}
155*1b3f573fSAndroid Build Coastguard Worker
156*1b3f573fSAndroid Build Coastguard Workerstatic id ExtensionForName(id self, SEL _cmd) {
157*1b3f573fSAndroid Build Coastguard Worker  // Really fast way of doing "classname_selName".
158*1b3f573fSAndroid Build Coastguard Worker  // This came up as a hotspot (creation of NSString *) when accessing a
159*1b3f573fSAndroid Build Coastguard Worker  // lot of extensions.
160*1b3f573fSAndroid Build Coastguard Worker  const char *selName = sel_getName(_cmd);
161*1b3f573fSAndroid Build Coastguard Worker  if (selName[0] == '_') {
162*1b3f573fSAndroid Build Coastguard Worker    return nil;  // Apple internal selector.
163*1b3f573fSAndroid Build Coastguard Worker  }
164*1b3f573fSAndroid Build Coastguard Worker  size_t selNameLen = 0;
165*1b3f573fSAndroid Build Coastguard Worker  while (1) {
166*1b3f573fSAndroid Build Coastguard Worker    char c = selName[selNameLen];
167*1b3f573fSAndroid Build Coastguard Worker    if (c == '\0') {  // String end.
168*1b3f573fSAndroid Build Coastguard Worker      break;
169*1b3f573fSAndroid Build Coastguard Worker    }
170*1b3f573fSAndroid Build Coastguard Worker    if (c == ':') {
171*1b3f573fSAndroid Build Coastguard Worker      return nil;  // Selector took an arg, not one of the runtime methods.
172*1b3f573fSAndroid Build Coastguard Worker    }
173*1b3f573fSAndroid Build Coastguard Worker    ++selNameLen;
174*1b3f573fSAndroid Build Coastguard Worker  }
175*1b3f573fSAndroid Build Coastguard Worker
176*1b3f573fSAndroid Build Coastguard Worker  const char *className = class_getName(self);
177*1b3f573fSAndroid Build Coastguard Worker  size_t classNameLen = strlen(className);
178*1b3f573fSAndroid Build Coastguard Worker  char key[classNameLen + selNameLen + 2];
179*1b3f573fSAndroid Build Coastguard Worker  memcpy(key, className, classNameLen);
180*1b3f573fSAndroid Build Coastguard Worker  key[classNameLen] = '_';
181*1b3f573fSAndroid Build Coastguard Worker  memcpy(&key[classNameLen + 1], selName, selNameLen);
182*1b3f573fSAndroid Build Coastguard Worker  key[classNameLen + 1 + selNameLen] = '\0';
183*1b3f573fSAndroid Build Coastguard Worker
184*1b3f573fSAndroid Build Coastguard Worker  // NOTE: Even though this method is called from another C function,
185*1b3f573fSAndroid Build Coastguard Worker  // gExtensionSingletonDictionarySemaphore and gExtensionSingletonDictionary
186*1b3f573fSAndroid Build Coastguard Worker  // will always be initialized. This is because this call flow is just to
187*1b3f573fSAndroid Build Coastguard Worker  // lookup the Extension, meaning the code is calling an Extension class
188*1b3f573fSAndroid Build Coastguard Worker  // message on a Message or Root class. This guarantees that the class was
189*1b3f573fSAndroid Build Coastguard Worker  // initialized and Message classes ensure their Root was also initialized.
190*1b3f573fSAndroid Build Coastguard Worker  NSAssert(gExtensionSingletonDictionary, @"Startup order broken!");
191*1b3f573fSAndroid Build Coastguard Worker
192*1b3f573fSAndroid Build Coastguard Worker  dispatch_semaphore_wait(gExtensionSingletonDictionarySemaphore,
193*1b3f573fSAndroid Build Coastguard Worker                          DISPATCH_TIME_FOREVER);
194*1b3f573fSAndroid Build Coastguard Worker  id extension = (id)CFDictionaryGetValue(gExtensionSingletonDictionary, key);
195*1b3f573fSAndroid Build Coastguard Worker  // We can't remove the key from the dictionary here (as an optimization),
196*1b3f573fSAndroid Build Coastguard Worker  // two threads could have gone into +resolveClassMethod: for the same method,
197*1b3f573fSAndroid Build Coastguard Worker  // and ended up here; there's no way to ensure both return YES without letting
198*1b3f573fSAndroid Build Coastguard Worker  // both try to wire in the method.
199*1b3f573fSAndroid Build Coastguard Worker  dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore);
200*1b3f573fSAndroid Build Coastguard Worker  return extension;
201*1b3f573fSAndroid Build Coastguard Worker}
202*1b3f573fSAndroid Build Coastguard Worker
203*1b3f573fSAndroid Build Coastguard WorkerBOOL GPBResolveExtensionClassMethod(Class self, SEL sel) {
204*1b3f573fSAndroid Build Coastguard Worker  // Another option would be to register the extensions with the class at
205*1b3f573fSAndroid Build Coastguard Worker  // globallyRegisterExtension:
206*1b3f573fSAndroid Build Coastguard Worker  // Timing the two solutions, this solution turned out to be much faster
207*1b3f573fSAndroid Build Coastguard Worker  // and reduced startup time, and runtime memory.
208*1b3f573fSAndroid Build Coastguard Worker  // The advantage to globallyRegisterExtension is that it would reduce the
209*1b3f573fSAndroid Build Coastguard Worker  // size of the protos somewhat because the singletonNameC wouldn't need
210*1b3f573fSAndroid Build Coastguard Worker  // to include the class name. For a class with a lot of extensions it
211*1b3f573fSAndroid Build Coastguard Worker  // can add up. You could also significantly reduce the code complexity of this
212*1b3f573fSAndroid Build Coastguard Worker  // file.
213*1b3f573fSAndroid Build Coastguard Worker  id extension = ExtensionForName(self, sel);
214*1b3f573fSAndroid Build Coastguard Worker  if (extension != nil) {
215*1b3f573fSAndroid Build Coastguard Worker    const char *encoding =
216*1b3f573fSAndroid Build Coastguard Worker        GPBMessageEncodingForSelector(@selector(getClassValue), NO);
217*1b3f573fSAndroid Build Coastguard Worker    Class metaClass = objc_getMetaClass(class_getName(self));
218*1b3f573fSAndroid Build Coastguard Worker    IMP imp = imp_implementationWithBlock(^(id obj) {
219*1b3f573fSAndroid Build Coastguard Worker#pragma unused(obj)
220*1b3f573fSAndroid Build Coastguard Worker      return extension;
221*1b3f573fSAndroid Build Coastguard Worker    });
222*1b3f573fSAndroid Build Coastguard Worker    BOOL methodAdded = class_addMethod(metaClass, sel, imp, encoding);
223*1b3f573fSAndroid Build Coastguard Worker    // class_addMethod() is documented as also failing if the method was already
224*1b3f573fSAndroid Build Coastguard Worker    // added; so we check if the method is already there and return success so
225*1b3f573fSAndroid Build Coastguard Worker    // the method dispatch will still happen.  Why would it already be added?
226*1b3f573fSAndroid Build Coastguard Worker    // Two threads could cause the same method to be bound at the same time,
227*1b3f573fSAndroid Build Coastguard Worker    // but only one will actually bind it; the other still needs to return true
228*1b3f573fSAndroid Build Coastguard Worker    // so things will dispatch.
229*1b3f573fSAndroid Build Coastguard Worker    if (!methodAdded) {
230*1b3f573fSAndroid Build Coastguard Worker      methodAdded = GPBClassHasSel(metaClass, sel);
231*1b3f573fSAndroid Build Coastguard Worker    }
232*1b3f573fSAndroid Build Coastguard Worker    return methodAdded;
233*1b3f573fSAndroid Build Coastguard Worker  }
234*1b3f573fSAndroid Build Coastguard Worker  return NO;
235*1b3f573fSAndroid Build Coastguard Worker}
236*1b3f573fSAndroid Build Coastguard Worker
237*1b3f573fSAndroid Build Coastguard Worker
238*1b3f573fSAndroid Build Coastguard Worker+ (BOOL)resolveClassMethod:(SEL)sel {
239*1b3f573fSAndroid Build Coastguard Worker  if (GPBResolveExtensionClassMethod(self, sel)) {
240*1b3f573fSAndroid Build Coastguard Worker    return YES;
241*1b3f573fSAndroid Build Coastguard Worker  }
242*1b3f573fSAndroid Build Coastguard Worker  return [super resolveClassMethod:sel];
243*1b3f573fSAndroid Build Coastguard Worker}
244*1b3f573fSAndroid Build Coastguard Worker
245*1b3f573fSAndroid Build Coastguard Worker@end
246