1// 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 "GPBRootObject_PackagePrivate.h" 32 33#import <objc/runtime.h> 34 35#import <CoreFoundation/CoreFoundation.h> 36 37#import "GPBDescriptor.h" 38#import "GPBExtensionRegistry.h" 39#import "GPBUtilities_PackagePrivate.h" 40 41@interface GPBExtensionDescriptor (GPBRootObject) 42// Get singletonName as a c string. 43- (const char *)singletonNameC; 44@end 45 46// We need some object to conform to the MessageSignatureProtocol to make sure 47// the selectors in it are recorded in our Objective C runtime information. 48// GPBMessage is arguably the more "obvious" choice, but given that all messages 49// inherit from GPBMessage, conflicts seem likely, so we are using GPBRootObject 50// instead. 51@interface GPBRootObject () <GPBMessageSignatureProtocol> 52@end 53 54@implementation GPBRootObject 55 56// Taken from http://www.burtleburtle.net/bob/hash/doobs.html 57// Public Domain 58static uint32_t jenkins_one_at_a_time_hash(const char *key) { 59 uint32_t hash = 0; 60 for (uint32_t i = 0; key[i] != '\0'; ++i) { 61 hash += key[i]; 62 hash += (hash << 10); 63 hash ^= (hash >> 6); 64 } 65 hash += (hash << 3); 66 hash ^= (hash >> 11); 67 hash += (hash << 15); 68 return hash; 69} 70 71// Key methods for our custom CFDictionary. 72// Note that the dictionary lasts for the lifetime of our app, so no need 73// to worry about deallocation. All of the items are added to it at 74// startup, and so the keys don't need to be retained/released. 75// Keys are NULL terminated char *. 76static const void *GPBRootExtensionKeyRetain(CFAllocatorRef allocator, 77 const void *value) { 78#pragma unused(allocator) 79 return value; 80} 81 82static void GPBRootExtensionKeyRelease(CFAllocatorRef allocator, 83 const void *value) { 84#pragma unused(allocator) 85#pragma unused(value) 86} 87 88static CFStringRef GPBRootExtensionCopyKeyDescription(const void *value) { 89 const char *key = (const char *)value; 90 return CFStringCreateWithCString(kCFAllocatorDefault, key, 91 kCFStringEncodingUTF8); 92} 93 94static Boolean GPBRootExtensionKeyEqual(const void *value1, 95 const void *value2) { 96 const char *key1 = (const char *)value1; 97 const char *key2 = (const char *)value2; 98 return strcmp(key1, key2) == 0; 99} 100 101static CFHashCode GPBRootExtensionKeyHash(const void *value) { 102 const char *key = (const char *)value; 103 return jenkins_one_at_a_time_hash(key); 104} 105 106// NOTE: OSSpinLock may seem like a good fit here but Apple engineers have 107// pointed out that they are vulnerable to live locking on iOS in cases of 108// priority inversion: 109// http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/ 110// https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html 111static dispatch_semaphore_t gExtensionSingletonDictionarySemaphore; 112static CFMutableDictionaryRef gExtensionSingletonDictionary = NULL; 113static GPBExtensionRegistry *gDefaultExtensionRegistry = NULL; 114 115+ (void)initialize { 116 // Ensure the global is started up. 117 if (!gExtensionSingletonDictionary) { 118 gExtensionSingletonDictionarySemaphore = dispatch_semaphore_create(1); 119 CFDictionaryKeyCallBacks keyCallBacks = { 120 // See description above for reason for using custom dictionary. 121 0, 122 GPBRootExtensionKeyRetain, 123 GPBRootExtensionKeyRelease, 124 GPBRootExtensionCopyKeyDescription, 125 GPBRootExtensionKeyEqual, 126 GPBRootExtensionKeyHash, 127 }; 128 gExtensionSingletonDictionary = 129 CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &keyCallBacks, 130 &kCFTypeDictionaryValueCallBacks); 131 gDefaultExtensionRegistry = [[GPBExtensionRegistry alloc] init]; 132 } 133 134 if ([self superclass] == [GPBRootObject class]) { 135 // This is here to start up all the per file "Root" subclasses. 136 // This must be done in initialize to enforce thread safety of start up of 137 // the protocol buffer library. 138 [self extensionRegistry]; 139 } 140} 141 142+ (GPBExtensionRegistry *)extensionRegistry { 143 // Is overridden in all the subclasses that provide extensions to provide the 144 // per class one. 145 return gDefaultExtensionRegistry; 146} 147 148+ (void)globallyRegisterExtension:(GPBExtensionDescriptor *)field { 149 const char *key = [field singletonNameC]; 150 dispatch_semaphore_wait(gExtensionSingletonDictionarySemaphore, 151 DISPATCH_TIME_FOREVER); 152 CFDictionarySetValue(gExtensionSingletonDictionary, key, field); 153 dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore); 154} 155 156static id ExtensionForName(id self, SEL _cmd) { 157 // Really fast way of doing "classname_selName". 158 // This came up as a hotspot (creation of NSString *) when accessing a 159 // lot of extensions. 160 const char *selName = sel_getName(_cmd); 161 if (selName[0] == '_') { 162 return nil; // Apple internal selector. 163 } 164 size_t selNameLen = 0; 165 while (1) { 166 char c = selName[selNameLen]; 167 if (c == '\0') { // String end. 168 break; 169 } 170 if (c == ':') { 171 return nil; // Selector took an arg, not one of the runtime methods. 172 } 173 ++selNameLen; 174 } 175 176 const char *className = class_getName(self); 177 size_t classNameLen = strlen(className); 178 char key[classNameLen + selNameLen + 2]; 179 memcpy(key, className, classNameLen); 180 key[classNameLen] = '_'; 181 memcpy(&key[classNameLen + 1], selName, selNameLen); 182 key[classNameLen + 1 + selNameLen] = '\0'; 183 184 // NOTE: Even though this method is called from another C function, 185 // gExtensionSingletonDictionarySemaphore and gExtensionSingletonDictionary 186 // will always be initialized. This is because this call flow is just to 187 // lookup the Extension, meaning the code is calling an Extension class 188 // message on a Message or Root class. This guarantees that the class was 189 // initialized and Message classes ensure their Root was also initialized. 190 NSAssert(gExtensionSingletonDictionary, @"Startup order broken!"); 191 192 dispatch_semaphore_wait(gExtensionSingletonDictionarySemaphore, 193 DISPATCH_TIME_FOREVER); 194 id extension = (id)CFDictionaryGetValue(gExtensionSingletonDictionary, key); 195 // We can't remove the key from the dictionary here (as an optimization), 196 // two threads could have gone into +resolveClassMethod: for the same method, 197 // and ended up here; there's no way to ensure both return YES without letting 198 // both try to wire in the method. 199 dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore); 200 return extension; 201} 202 203BOOL GPBResolveExtensionClassMethod(Class self, SEL sel) { 204 // Another option would be to register the extensions with the class at 205 // globallyRegisterExtension: 206 // Timing the two solutions, this solution turned out to be much faster 207 // and reduced startup time, and runtime memory. 208 // The advantage to globallyRegisterExtension is that it would reduce the 209 // size of the protos somewhat because the singletonNameC wouldn't need 210 // to include the class name. For a class with a lot of extensions it 211 // can add up. You could also significantly reduce the code complexity of this 212 // file. 213 id extension = ExtensionForName(self, sel); 214 if (extension != nil) { 215 const char *encoding = 216 GPBMessageEncodingForSelector(@selector(getClassValue), NO); 217 Class metaClass = objc_getMetaClass(class_getName(self)); 218 IMP imp = imp_implementationWithBlock(^(id obj) { 219#pragma unused(obj) 220 return extension; 221 }); 222 BOOL methodAdded = class_addMethod(metaClass, sel, imp, encoding); 223 // class_addMethod() is documented as also failing if the method was already 224 // added; so we check if the method is already there and return success so 225 // the method dispatch will still happen. Why would it already be added? 226 // Two threads could cause the same method to be bound at the same time, 227 // but only one will actually bind it; the other still needs to return true 228 // so things will dispatch. 229 if (!methodAdded) { 230 methodAdded = GPBClassHasSel(metaClass, sel); 231 } 232 return methodAdded; 233 } 234 return NO; 235} 236 237 238+ (BOOL)resolveClassMethod:(SEL)sel { 239 if (GPBResolveExtensionClassMethod(self, sel)) { 240 return YES; 241 } 242 return [super resolveClassMethod:sel]; 243} 244 245@end 246