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