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