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 50 static 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 *. 68 static const void *GPBRootExtensionKeyRetain(CFAllocatorRef allocator, 69 const void *value) { 70 #pragma unused(allocator) 71 return value; 72 } 73 74 static void GPBRootExtensionKeyRelease(CFAllocatorRef allocator, 75 const void *value) { 76 #pragma unused(allocator) 77 #pragma unused(value) 78 } 79 80 static CFStringRef GPBRootExtensionCopyKeyDescription(const void *value) { 81 const char *key = (const char *)value; 82 return CFStringCreateWithCString(kCFAllocatorDefault, key, 83 kCFStringEncodingUTF8); 84 } 85 86 static 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 93 static 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 103 static dispatch_semaphore_t gExtensionSingletonDictionarySemaphore; 104 static CFMutableDictionaryRef gExtensionSingletonDictionary = NULL; 105 static 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 148 static 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 196 BOOL 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