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