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