1/*
2 * Copyright (c) 2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*	CFPlugIn_Factory.c
25	Copyright (c) 1999-2013, Apple Inc.  All rights reserved.
26        Responsibility: Tony Parker
27*/
28
29#include "CFBundle_Internal.h"
30#include "CFInternal.h"
31
32static CFTypeID __kCFPFactoryTypeID = _kCFRuntimeNotATypeID;
33
34struct __CFPFactory {
35    CFRuntimeBase _base;
36
37    CFUUIDRef _uuid;
38    Boolean _enabled;
39    char _padding[3];
40
41    CFPlugInFactoryFunction _func;
42
43    CFPlugInRef _plugIn;
44    CFStringRef _funcName;
45
46    CFMutableArrayRef _types;
47    CFSpinLock_t _lock;
48};
49
50static void _CFPFactoryDeallocate(CFTypeRef factory);
51
52static const CFRuntimeClass __CFPFactoryClass = {
53    0,
54    "_CFPFactory",
55    NULL,	// init
56    NULL,	// copy
57    _CFPFactoryDeallocate,
58    NULL,	// equal
59    NULL,	// hash
60    NULL,       // formatting desc
61    NULL,       // debug desc
62};
63
64CF_PRIVATE void __CFPFactoryInitialize(void) {
65    __kCFPFactoryTypeID = _CFRuntimeRegisterClass(&__CFPFactoryClass);
66}
67
68static CFTypeID _CFPFactoryGetTypeID(void) {
69    return __kCFPFactoryTypeID;
70}
71
72static CFSpinLock_t CFPlugInGlobalDataLock = CFSpinLockInit;
73static CFMutableDictionaryRef _factoriesByFactoryID = NULL; /* Value is _CFPFactoryRef */
74static CFMutableDictionaryRef _factoriesByTypeID = NULL; /* Value is array of _CFPFactoryRef */
75
76static void _CFPFactoryAddToTable(_CFPFactoryRef factory) {
77    __CFSpinLock(&factory->_lock);
78    CFUUIDRef uuid = (CFUUIDRef)CFRetain(factory->_uuid);
79    CFRetain(factory);
80    __CFSpinUnlock(&factory->_lock);
81
82    __CFSpinLock(&CFPlugInGlobalDataLock);
83    if (!_factoriesByFactoryID) {
84        CFDictionaryValueCallBacks _factoryDictValueCallbacks = {0, NULL, NULL, NULL, NULL};
85        _factoriesByFactoryID = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &_factoryDictValueCallbacks);
86    }
87    CFDictionarySetValue(_factoriesByFactoryID, uuid, factory);
88    __CFSpinUnlock(&CFPlugInGlobalDataLock);
89
90    if (uuid) CFRelease(uuid);
91    CFRelease(factory);
92}
93
94static void _CFPFactoryRemoveFromTable(_CFPFactoryRef factory) {
95    __CFSpinLock(&factory->_lock);
96    CFUUIDRef uuid = factory->_uuid;
97    if (uuid) CFRetain(uuid);
98    __CFSpinUnlock(&factory->_lock);
99
100    __CFSpinLock(&CFPlugInGlobalDataLock);
101    if (uuid && _factoriesByTypeID) CFDictionaryRemoveValue(_factoriesByFactoryID, uuid);
102    __CFSpinUnlock(&CFPlugInGlobalDataLock);
103
104    if (uuid) CFRelease(uuid);
105}
106
107CF_PRIVATE _CFPFactoryRef _CFPFactoryFind(CFUUIDRef factoryID, Boolean enabled) {
108    _CFPFactoryRef result = NULL;
109
110    __CFSpinLock(&CFPlugInGlobalDataLock);
111    if (_factoriesByFactoryID) {
112        result = (_CFPFactoryRef )CFDictionaryGetValue(_factoriesByFactoryID, factoryID);
113        if (result && result->_enabled != enabled) result = NULL;
114    }
115    __CFSpinUnlock(&CFPlugInGlobalDataLock);
116    return result;
117}
118
119static void _CFPFactoryDeallocate(CFTypeRef ty) {
120    SInt32 c;
121    _CFPFactoryRef factory = (_CFPFactoryRef)ty;
122
123    _CFPFactoryRemoveFromTable(factory);
124
125    if (factory->_plugIn) {
126        _CFPlugInRemoveFactory(factory->_plugIn, factory);
127        CFRelease(factory->_plugIn);
128    }
129
130    /* Remove all types for this factory. */
131    c = CFArrayGetCount(factory->_types);
132    while (c-- > 0) _CFPFactoryRemoveType(factory, (CFUUIDRef)CFArrayGetValueAtIndex(factory->_types, c));
133    CFRelease(factory->_types);
134
135    if (factory->_funcName) CFRelease(factory->_funcName);
136    if (factory->_uuid) CFRelease(factory->_uuid);
137}
138
139static _CFPFactoryRef _CFPFactoryCommonCreate(CFAllocatorRef allocator, CFUUIDRef factoryID) {
140    _CFPFactoryRef factory;
141    uint32_t size;
142    size = sizeof(struct __CFPFactory) - sizeof(CFRuntimeBase);
143    factory = (_CFPFactoryRef)_CFRuntimeCreateInstance(allocator, _CFPFactoryGetTypeID(), size, NULL);
144    if (!factory) return NULL;
145
146    factory->_uuid = (CFUUIDRef)CFRetain(factoryID);
147    factory->_enabled = true;
148    factory->_types = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
149    factory->_lock = CFSpinLockInit; // WARNING: grab global lock before this lock
150
151    _CFPFactoryAddToTable(factory);
152
153    return factory;
154}
155
156CF_PRIVATE _CFPFactoryRef _CFPFactoryCreate(CFAllocatorRef allocator, CFUUIDRef factoryID, CFPlugInFactoryFunction func) {
157    _CFPFactoryRef factory = _CFPFactoryCommonCreate(allocator, factoryID);
158
159    __CFSpinLock(&factory->_lock);
160    factory->_func = func;
161    factory->_plugIn = NULL;
162    factory->_funcName = NULL;
163    __CFSpinUnlock(&factory->_lock);
164
165    return factory;
166}
167
168CF_PRIVATE _CFPFactoryRef _CFPFactoryCreateByName(CFAllocatorRef allocator, CFUUIDRef factoryID, CFPlugInRef plugIn, CFStringRef funcName) {
169    _CFPFactoryRef factory = _CFPFactoryCommonCreate(allocator, factoryID);
170
171    __CFSpinLock(&factory->_lock);
172    factory->_func = NULL;
173    factory->_plugIn = (CFPlugInRef)CFRetain(plugIn);
174    if (plugIn) _CFPlugInAddFactory(plugIn, factory);
175    factory->_funcName = (funcName ? (CFStringRef)CFStringCreateCopy(allocator, funcName) : NULL);
176    __CFSpinUnlock(&factory->_lock);
177
178    return factory;
179}
180
181CF_PRIVATE CFUUIDRef _CFPFactoryCopyFactoryID(_CFPFactoryRef factory) {
182    __CFSpinLock(&factory->_lock);
183    CFUUIDRef uuid = factory->_uuid;
184    if (uuid) CFRetain(uuid);
185    __CFSpinUnlock(&factory->_lock);
186    return uuid;
187}
188
189CF_PRIVATE CFPlugInRef _CFPFactoryCopyPlugIn(_CFPFactoryRef factory) {
190    __CFSpinLock(&factory->_lock);
191    CFPlugInRef result = factory->_plugIn;
192    if (result) CFRetain(result);
193    __CFSpinUnlock(&factory->_lock);
194    return result;
195}
196
197CF_PRIVATE void *_CFPFactoryCreateInstance(CFAllocatorRef allocator, _CFPFactoryRef factory, CFUUIDRef typeID) {
198    void *result = NULL;
199
200    __CFSpinLock(&factory->_lock);
201    if (factory->_enabled) {
202        if (!factory->_func) {
203            factory->_func = (CFPlugInFactoryFunction)CFBundleGetFunctionPointerForName(factory->_plugIn, factory->_funcName);
204            if (!factory->_func) CFLog(__kCFLogPlugIn, CFSTR("Cannot find function pointer %@ for factory %@ in %@"), factory->_funcName, factory->_uuid, factory->_plugIn);
205        }
206        if (factory->_func) {
207            // UPPGOOP
208            CFPlugInFactoryFunction f = factory->_func;
209            __CFSpinUnlock(&factory->_lock);
210            FAULT_CALLBACK((void **)&(f));
211            result = (void *)INVOKE_CALLBACK2(f, allocator, typeID);
212            __CFSpinLock(&factory->_lock);
213        }
214    } else {
215        CFLog(__kCFLogPlugIn, CFSTR("Factory %@ is disabled"), factory->_uuid);
216    }
217    __CFSpinUnlock(&factory->_lock);
218
219    return result;
220}
221
222CF_PRIVATE void _CFPFactoryDisable(_CFPFactoryRef factory) {
223    __CFSpinLock(&factory->_lock);
224    factory->_enabled = false;
225    __CFSpinUnlock(&factory->_lock);
226    CFRelease(factory);
227}
228
229CF_PRIVATE void _CFPFactoryFlushFunctionCache(_CFPFactoryRef factory) {
230    /* MF:!!! Assert that this factory belongs to a plugIn. */
231    /* This is called by the factory's plugIn when the plugIn unloads its code. */
232    __CFSpinLock(&factory->_lock);
233    factory->_func = NULL;
234    __CFSpinUnlock(&factory->_lock);
235}
236
237CF_PRIVATE void _CFPFactoryAddType(_CFPFactoryRef factory, CFUUIDRef typeID) {
238    /* Add the factory to the type's array of factories */
239    __CFSpinLock(&factory->_lock);
240    /* Add the type to the factory's type list */
241    CFArrayAppendValue(factory->_types, typeID);
242    __CFSpinUnlock(&factory->_lock);
243
244    __CFSpinLock(&CFPlugInGlobalDataLock);
245    if (!_factoriesByTypeID) _factoriesByTypeID = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
246    CFMutableArrayRef array = (CFMutableArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID);
247    if (!array) {
248        CFArrayCallBacks _factoryArrayCallbacks = {0, NULL, NULL, NULL, NULL};
249        // Create this from default allocator
250        array = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &_factoryArrayCallbacks);
251        CFDictionarySetValue(_factoriesByTypeID, typeID, array);
252        CFRelease(array);
253    }
254    CFArrayAppendValue(array, factory);
255    __CFSpinUnlock(&CFPlugInGlobalDataLock);
256}
257
258CF_PRIVATE void _CFPFactoryRemoveType(_CFPFactoryRef factory, CFUUIDRef typeID) {
259    /* Remove it from the factory's type list */
260    SInt32 idx;
261
262    __CFSpinLock(&factory->_lock);
263    idx = CFArrayGetFirstIndexOfValue(factory->_types, CFRangeMake(0, CFArrayGetCount(factory->_types)), typeID);
264    if (idx >= 0) CFArrayRemoveValueAtIndex(factory->_types, idx);
265    __CFSpinUnlock(&factory->_lock);
266
267    /* Remove the factory from the type's list of factories */
268    __CFSpinLock(&CFPlugInGlobalDataLock);
269    if (_factoriesByTypeID) {
270        CFMutableArrayRef array = (CFMutableArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID);
271        if (array) {
272            idx = CFArrayGetFirstIndexOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), factory);
273            if (idx >= 0) {
274                CFArrayRemoveValueAtIndex(array, idx);
275                if (CFArrayGetCount(array) == 0) CFDictionaryRemoveValue(_factoriesByTypeID, typeID);
276            }
277        }
278    }
279    __CFSpinUnlock(&CFPlugInGlobalDataLock);
280}
281
282CF_PRIVATE Boolean _CFPFactorySupportsType(_CFPFactoryRef factory, CFUUIDRef typeID) {
283    SInt32 idx;
284
285    __CFSpinLock(&factory->_lock);
286    idx = CFArrayGetFirstIndexOfValue(factory->_types, CFRangeMake(0, CFArrayGetCount(factory->_types)), typeID);
287    __CFSpinUnlock(&factory->_lock);
288
289    return (idx >= 0 ? true : false);
290}
291
292CF_PRIVATE CFArrayRef _CFPFactoryFindCopyForType(CFUUIDRef typeID) {
293    CFArrayRef result = NULL;
294    __CFSpinLock(&CFPlugInGlobalDataLock);
295    if (_factoriesByTypeID) {
296        result = (CFArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID);
297        if (result) CFRetain(result);
298    }
299    __CFSpinUnlock(&CFPlugInGlobalDataLock);
300
301    return result;
302}
303
304/* These methods are called by CFPlugInInstance when an instance is created or destroyed.  If a factory's instance count goes to 0 and the factory has been disabled, the factory is destroyed. */
305CF_PRIVATE void _CFPFactoryAddInstance(_CFPFactoryRef factory) {
306    /* MF:!!! Assert that factory is enabled. */
307    CFRetain(factory);
308    __CFSpinLock(&factory->_lock);
309    CFPlugInRef plugin = factory->_plugIn;
310    if (plugin) CFRetain(plugin);
311    __CFSpinUnlock(&factory->_lock);
312    if (plugin) {
313        _CFPlugInAddPlugInInstance(plugin);
314        CFRelease(plugin);
315    }
316}
317
318CF_PRIVATE void _CFPFactoryRemoveInstance(_CFPFactoryRef factory) {
319    __CFSpinLock(&factory->_lock);
320    CFPlugInRef plugin = factory->_plugIn;
321    if (plugin) CFRetain(plugin);
322    __CFSpinUnlock(&factory->_lock);
323    if (plugin) {
324        _CFPlugInRemovePlugInInstance(factory->_plugIn);
325        CFRelease(plugin);
326    }
327    CFRelease(factory);
328}
329