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