]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-class.mm
4e23f5ec51a2a8d0b9cf5fd05e00d4f2a00cc71c
[apple/objc4.git] / runtime / objc-class.mm
1 /*
2 * Copyright (c) 1999-2007 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 * objc-class.m
25 * Copyright 1988-1997, Apple Computer, Inc.
26 * Author: s. naroff
27 **********************************************************************/
28
29
30 /***********************************************************************
31 * Lazy method list arrays and method list locking (2004-10-19)
32 *
33 * cls->methodLists may be in one of three forms:
34 * 1. NULL: The class has no methods.
35 * 2. non-NULL, with CLS_NO_METHOD_ARRAY set: cls->methodLists points
36 * to a single method list, which is the class's only method list.
37 * 3. non-NULL, with CLS_NO_METHOD_ARRAY clear: cls->methodLists points to
38 * an array of method list pointers. The end of the array's block
39 * is set to -1. If the actual number of method lists is smaller
40 * than that, the rest of the array is NULL.
41 *
42 * Attaching categories and adding and removing classes may change
43 * the form of the class list. In addition, individual method lists
44 * may be reallocated when fixed up.
45 *
46 * Classes are initially read as #1 or #2. If a category is attached
47 * or other methods added, the class is changed to #3. Once in form #3,
48 * the class is never downgraded to #1 or #2, even if methods are removed.
49 * Classes added with objc_addClass are initially either #1 or #3.
50 *
51 * Accessing and manipulating a class's method lists are synchronized,
52 * to prevent races when one thread restructures the list. However,
53 * if the class is not yet in use (i.e. not in class_hash), then the
54 * thread loading the class may access its method lists without locking.
55 *
56 * The following functions acquire methodListLock:
57 * class_getInstanceMethod
58 * class_getClassMethod
59 * class_nextMethodList
60 * class_addMethods
61 * class_removeMethods
62 * class_respondsToMethod
63 * _class_lookupMethodAndLoadCache
64 * lookupMethodInClassAndLoadCache
65 * _objc_add_category_flush_caches
66 *
67 * The following functions don't acquire methodListLock because they
68 * only access method lists during class load and unload:
69 * _objc_register_category
70 * _resolve_categories_for_class (calls _objc_add_category)
71 * add_class_to_loadable_list
72 * _objc_addClass
73 * _objc_remove_classes_in_image
74 *
75 * The following functions use method lists without holding methodListLock.
76 * The caller must either hold methodListLock, or be loading the class.
77 * _getMethod (called by class_getInstanceMethod, class_getClassMethod,
78 * and class_respondsToMethod)
79 * _findMethodInClass (called by _class_lookupMethodAndLoadCache,
80 * lookupMethodInClassAndLoadCache, _getMethod)
81 * _findMethodInList (called by _findMethodInClass)
82 * nextMethodList (called by _findMethodInClass and class_nextMethodList
83 * fixupSelectorsInMethodList (called by nextMethodList)
84 * _objc_add_category (called by _objc_add_category_flush_caches,
85 * resolve_categories_for_class and _objc_register_category)
86 * _objc_insertMethods (called by class_addMethods and _objc_add_category)
87 * _objc_removeMethods (called by class_removeMethods)
88 * _objcTweakMethodListPointerForClass (called by _objc_insertMethods)
89 * get_base_method_list (called by add_class_to_loadable_list)
90 * lookupNamedMethodInMethodList (called by add_class_to_loadable_list)
91 ***********************************************************************/
92
93 /***********************************************************************
94 * Thread-safety of class info bits (2004-10-19)
95 *
96 * Some class info bits are used to store mutable runtime state.
97 * Modifications of the info bits at particular times need to be
98 * synchronized to prevent races.
99 *
100 * Three thread-safe modification functions are provided:
101 * _class_setInfo() // atomically sets some bits
102 * _class_clearInfo() // atomically clears some bits
103 * _class_changeInfo() // atomically sets some bits and clears others
104 * These replace CLS_SETINFO() for the multithreaded cases.
105 *
106 * Three modification windows are defined:
107 * - compile time
108 * - class construction or image load (before +load) in one thread
109 * - multi-threaded messaging and method caches
110 *
111 * Info bit modification at compile time and class construction do not
112 * need to be locked, because only one thread is manipulating the class.
113 * Info bit modification during messaging needs to be locked, because
114 * there may be other threads simultaneously messaging or otherwise
115 * manipulating the class.
116 *
117 * Modification windows for each flag:
118 *
119 * CLS_CLASS: compile-time and class load
120 * CLS_META: compile-time and class load
121 * CLS_INITIALIZED: +initialize
122 * CLS_POSING: messaging
123 * CLS_MAPPED: compile-time
124 * CLS_FLUSH_CACHE: class load and messaging
125 * CLS_GROW_CACHE: messaging
126 * CLS_NEED_BIND: unused
127 * CLS_METHOD_ARRAY: unused
128 * CLS_JAVA_HYBRID: JavaBridge only
129 * CLS_JAVA_CLASS: JavaBridge only
130 * CLS_INITIALIZING: messaging
131 * CLS_FROM_BUNDLE: class load
132 * CLS_HAS_CXX_STRUCTORS: compile-time and class load
133 * CLS_NO_METHOD_ARRAY: class load and messaging
134 * CLS_HAS_LOAD_METHOD: class load
135 *
136 * CLS_INITIALIZED and CLS_INITIALIZING have additional thread-safety
137 * constraints to support thread-safe +initialize. See "Thread safety
138 * during class initialization" for details.
139 *
140 * CLS_JAVA_HYBRID and CLS_JAVA_CLASS are set immediately after JavaBridge
141 * calls objc_addClass(). The JavaBridge does not use an atomic update,
142 * but the modification counts as "class construction" unless some other
143 * thread quickly finds the class via the class list. This race is
144 * small and unlikely in well-behaved code.
145 *
146 * Most info bits that may be modified during messaging are also never
147 * read without a lock. There is no general read lock for the info bits.
148 * CLS_INITIALIZED: classInitLock
149 * CLS_FLUSH_CACHE: cacheUpdateLock
150 * CLS_GROW_CACHE: cacheUpdateLock
151 * CLS_NO_METHOD_ARRAY: methodListLock
152 * CLS_INITIALIZING: classInitLock
153 ***********************************************************************/
154
155 /***********************************************************************
156 * Imports.
157 **********************************************************************/
158
159 #include "objc-private.h"
160 #include "objc-abi.h"
161 #include "objc-auto.h"
162 #include <objc/message.h>
163
164
165 /* overriding the default object allocation and error handling routines */
166
167 OBJC_EXPORT id (*_alloc)(Class, size_t);
168 OBJC_EXPORT id (*_copy)(id, size_t);
169 OBJC_EXPORT id (*_realloc)(id, size_t);
170 OBJC_EXPORT id (*_dealloc)(id);
171 OBJC_EXPORT id (*_zoneAlloc)(Class, size_t, void *);
172 OBJC_EXPORT id (*_zoneRealloc)(id, size_t, void *);
173 OBJC_EXPORT id (*_zoneCopy)(id, size_t, void *);
174
175
176 /***********************************************************************
177 * Function prototypes internal to this module.
178 **********************************************************************/
179
180 static IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel);
181 static Method look_up_method(Class cls, SEL sel, BOOL withCache, BOOL withResolver);
182
183
184 /***********************************************************************
185 * Static data internal to this module.
186 **********************************************************************/
187
188 #if !TARGET_OS_WIN32 && !defined(__arm__)
189 # define MESSAGE_LOGGING
190 #endif
191
192 #if defined(MESSAGE_LOGGING)
193 // Method call logging
194 static int LogObjCMessageSend (BOOL isClassMethod, const char * objectsClass, const char * implementingClass, SEL selector);
195 typedef int (*ObjCLogProc)(BOOL, const char *, const char *, SEL);
196
197 static int objcMsgLogFD = (-1);
198 static ObjCLogProc objcMsgLogProc = &LogObjCMessageSend;
199 static int objcMsgLogEnabled = 0;
200 #endif
201
202
203 /***********************************************************************
204 * Information about multi-thread support:
205 *
206 * Since we do not lock many operations which walk the superclass, method
207 * and ivar chains, these chains must remain intact once a class is published
208 * by inserting it into the class hashtable. All modifications must be
209 * atomic so that someone walking these chains will always geta valid
210 * result.
211 ***********************************************************************/
212
213
214
215 /***********************************************************************
216 * object_getClass.
217 * Locking: None. If you add locking, tell gdb (rdar://7516456).
218 **********************************************************************/
219 Class object_getClass(id obj)
220 {
221 return _object_getClass(obj);
222 }
223
224
225 /***********************************************************************
226 * object_setClass.
227 **********************************************************************/
228 Class object_setClass(id obj, Class cls)
229 {
230 if (obj) {
231 Class old;
232 do {
233 old = obj->isa;
234 } while (! OSAtomicCompareAndSwapPtrBarrier(old, cls, (void * volatile *)&obj->isa));
235
236 if (old && _class_instancesHaveAssociatedObjects(old)) {
237 _class_setInstancesHaveAssociatedObjects(cls);
238 }
239
240 return old;
241 }
242 else return Nil;
243 }
244
245
246 /***********************************************************************
247 * object_getClassName.
248 **********************************************************************/
249 const char *object_getClassName(id obj)
250 {
251 Class isa = _object_getClass(obj);
252 if (isa) return _class_getName(isa);
253 else return "nil";
254 }
255
256 /***********************************************************************
257 * object_getIndexedIvars.
258 **********************************************************************/
259 void *object_getIndexedIvars(id obj)
260 {
261 // ivars are tacked onto the end of the object
262 if (obj) return ((char *) obj) + _class_getInstanceSize(_object_getClass(obj));
263 else return NULL;
264 }
265
266
267 Ivar object_setInstanceVariable(id obj, const char *name, void *value)
268 {
269 Ivar ivar = NULL;
270
271 if (obj && name) {
272 if ((ivar = class_getInstanceVariable(_object_getClass(obj), name))) {
273 object_setIvar(obj, ivar, (id)value);
274 }
275 }
276 return ivar;
277 }
278
279 Ivar object_getInstanceVariable(id obj, const char *name, void **value)
280 {
281 if (obj && name) {
282 Ivar ivar;
283 if ((ivar = class_getInstanceVariable(_object_getClass(obj), name))) {
284 if (value) *value = (void *)object_getIvar(obj, ivar);
285 return ivar;
286 }
287 }
288 if (value) *value = NULL;
289 return NULL;
290 }
291
292 static BOOL is_scanned_offset(ptrdiff_t ivar_offset, const uint8_t *layout) {
293 ptrdiff_t index = 0, ivar_index = ivar_offset / sizeof(void*);
294 uint8_t byte;
295 while ((byte = *layout++)) {
296 unsigned skips = (byte >> 4);
297 unsigned scans = (byte & 0x0F);
298 index += skips;
299 while (scans--) {
300 if (index == ivar_index) return YES;
301 if (index > ivar_index) return NO;
302 ++index;
303 }
304 }
305 return NO;
306 }
307
308 // FIXME: this could be optimized.
309
310 static Class _ivar_getClass(Class cls, Ivar ivar) {
311 Class ivar_class = NULL;
312 const char *ivar_name = ivar_getName(ivar);
313 Ivar named_ivar = _class_getVariable(cls, ivar_name, &ivar_class);
314 if (named_ivar) {
315 // the same ivar name can appear multiple times along the superclass chain.
316 while (named_ivar != ivar && ivar_class != NULL) {
317 ivar_class = class_getSuperclass(ivar_class);
318 named_ivar = _class_getVariable(cls, ivar_getName(ivar), &ivar_class);
319 }
320 }
321 return ivar_class;
322 }
323
324 void object_setIvar(id obj, Ivar ivar, id value)
325 {
326 if (obj && ivar) {
327 Class cls = _ivar_getClass(object_getClass(obj), ivar);
328 ptrdiff_t ivar_offset = ivar_getOffset(ivar);
329 id *location = (id *)((char *)obj + ivar_offset);
330 // if this ivar is a member of an ARR compiled class, then issue the correct barrier according to the layout.
331 if (_class_usesAutomaticRetainRelease(cls)) {
332 // for ARR, layout strings are relative to the instance start.
333 uint32_t instanceStart = _class_getInstanceStart(cls);
334 const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
335 if (weak_layout && is_scanned_offset(ivar_offset - instanceStart, weak_layout)) {
336 // use the weak system to write to this variable.
337 objc_storeWeak(location, value);
338 return;
339 }
340 const uint8_t *strong_layout = class_getIvarLayout(cls);
341 if (strong_layout && is_scanned_offset(ivar_offset - instanceStart, strong_layout)) {
342 objc_storeStrong(location, value);
343 return;
344 }
345 }
346 #if SUPPORT_GC
347 if (UseGC) {
348 // for GC, check for weak references.
349 const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
350 if (weak_layout && is_scanned_offset(ivar_offset, weak_layout)) {
351 objc_assign_weak(value, location);
352 }
353 }
354 objc_assign_ivar_internal(value, obj, ivar_offset);
355 #else
356 *location = value;
357 #endif
358 }
359 }
360
361
362 id object_getIvar(id obj, Ivar ivar)
363 {
364 if (obj && ivar) {
365 Class cls = _object_getClass(obj);
366 ptrdiff_t ivar_offset = ivar_getOffset(ivar);
367 if (_class_usesAutomaticRetainRelease(cls)) {
368 // for ARR, layout strings are relative to the instance start.
369 uint32_t instanceStart = _class_getInstanceStart(cls);
370 const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
371 if (weak_layout && is_scanned_offset(ivar_offset - instanceStart, weak_layout)) {
372 // use the weak system to read this variable.
373 id *location = (id *)((char *)obj + ivar_offset);
374 return objc_loadWeak(location);
375 }
376 }
377 id *idx = (id *)((char *)obj + ivar_offset);
378 #if SUPPORT_GC
379 if (UseGC) {
380 const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
381 if (weak_layout && is_scanned_offset(ivar_offset, weak_layout)) {
382 return objc_read_weak(idx);
383 }
384 }
385 #endif
386 return *idx;
387 }
388 return NULL;
389 }
390
391
392 /***********************************************************************
393 * object_cxxDestructFromClass.
394 * Call C++ destructors on obj, starting with cls's
395 * dtor method (if any) followed by superclasses' dtors (if any),
396 * stopping at cls's dtor (if any).
397 * Uses methodListLock and cacheUpdateLock. The caller must hold neither.
398 **********************************************************************/
399 static void object_cxxDestructFromClass(id obj, Class cls)
400 {
401 void (*dtor)(id);
402
403 // Call cls's dtor first, then superclasses's dtors.
404
405 for ( ; cls != NULL; cls = _class_getSuperclass(cls)) {
406 if (!_class_hasCxxStructors(cls)) return;
407 dtor = (void(*)(id))
408 lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
409 if (dtor != (void(*)(id))_objc_msgForward_internal) {
410 if (PrintCxxCtors) {
411 _objc_inform("CXX: calling C++ destructors for class %s",
412 _class_getName(cls));
413 }
414 (*dtor)(obj);
415 }
416 }
417 }
418
419
420 /***********************************************************************
421 * object_cxxDestruct.
422 * Call C++ destructors on obj, if any.
423 * Uses methodListLock and cacheUpdateLock. The caller must hold neither.
424 **********************************************************************/
425 void object_cxxDestruct(id obj)
426 {
427 if (!obj) return;
428 if (OBJC_IS_TAGGED_PTR(obj)) return;
429 object_cxxDestructFromClass(obj, obj->isa); // need not be object_getClass
430 }
431
432
433 /***********************************************************************
434 * object_cxxConstructFromClass.
435 * Recursively call C++ constructors on obj, starting with base class's
436 * ctor method (if any) followed by subclasses' ctors (if any), stopping
437 * at cls's ctor (if any).
438 * Returns YES if construction succeeded.
439 * Returns NO if some constructor threw an exception. The exception is
440 * caught and discarded. Any partial construction is destructed.
441 * Uses methodListLock and cacheUpdateLock. The caller must hold neither.
442 *
443 * .cxx_construct returns id. This really means:
444 * return self: construction succeeded
445 * return nil: construction failed because a C++ constructor threw an exception
446 **********************************************************************/
447 static BOOL object_cxxConstructFromClass(id obj, Class cls)
448 {
449 id (*ctor)(id);
450 Class supercls;
451
452 // Stop if neither this class nor any superclass has ctors.
453 if (!_class_hasCxxStructors(cls)) return YES; // no ctor - ok
454
455 supercls = _class_getSuperclass(cls);
456
457 // Call superclasses' ctors first, if any.
458 if (supercls) {
459 BOOL ok = object_cxxConstructFromClass(obj, supercls);
460 if (!ok) return NO; // some superclass's ctor failed - give up
461 }
462
463 // Find this class's ctor, if any.
464 ctor = (id(*)(id))lookupMethodInClassAndLoadCache(cls, SEL_cxx_construct);
465 if (ctor == (id(*)(id))_objc_msgForward_internal) return YES; // no ctor - ok
466
467 // Call this class's ctor.
468 if (PrintCxxCtors) {
469 _objc_inform("CXX: calling C++ constructors for class %s", _class_getName(cls));
470 }
471 if ((*ctor)(obj)) return YES; // ctor called and succeeded - ok
472
473 // This class's ctor was called and failed.
474 // Call superclasses's dtors to clean up.
475 if (supercls) object_cxxDestructFromClass(obj, supercls);
476 return NO;
477 }
478
479
480 /***********************************************************************
481 * object_cxxConstructFromClass.
482 * Call C++ constructors on obj, if any.
483 * Returns YES if construction succeeded.
484 * Returns NO if some constructor threw an exception. The exception is
485 * caught and discarded. Any partial construction is destructed.
486 * Uses methodListLock and cacheUpdateLock. The caller must hold neither.
487 **********************************************************************/
488 BOOL object_cxxConstruct(id obj)
489 {
490 if (!obj) return YES;
491 if (OBJC_IS_TAGGED_PTR(obj)) return YES;
492 return object_cxxConstructFromClass(obj, obj->isa); // need not be object_getClass
493 }
494
495
496 /***********************************************************************
497 * _class_resolveClassMethod
498 * Call +resolveClassMethod and return the method added or NULL.
499 * cls should be a metaclass.
500 * Assumes the method doesn't exist already.
501 **********************************************************************/
502 static Method _class_resolveClassMethod(Class cls, SEL sel)
503 {
504 BOOL resolved;
505 Method meth = NULL;
506 Class clsInstance;
507
508 if (!look_up_method(cls, SEL_resolveClassMethod,
509 YES /*cache*/, NO /*resolver*/))
510 {
511 return NULL;
512 }
513
514 // GrP fixme same hack as +initialize
515 if (strncmp(_class_getName(cls), "_%", 2) == 0) {
516 // Posee's meta's name is smashed and isn't in the class_hash,
517 // so objc_getClass doesn't work.
518 const char *baseName = strchr(_class_getName(cls), '%'); // get posee's real name
519 clsInstance = (Class)objc_getClass(baseName);
520 } else {
521 clsInstance = (Class)objc_getClass(_class_getName(cls));
522 }
523
524 resolved = ((BOOL(*)(id, SEL, SEL))objc_msgSend)((id)clsInstance, SEL_resolveClassMethod, sel);
525
526 if (resolved) {
527 // +resolveClassMethod adds to self->isa
528 meth = look_up_method(cls, sel, YES/*cache*/, NO/*resolver*/);
529
530 if (!meth) {
531 // Method resolver didn't add anything?
532 _objc_inform("+[%s resolveClassMethod:%s] returned YES, but "
533 "no new implementation of +[%s %s] was found",
534 class_getName(cls),
535 sel_getName(sel),
536 class_getName(cls),
537 sel_getName(sel));
538 return NULL;
539 }
540 }
541
542 return meth;
543 }
544
545
546 /***********************************************************************
547 * _class_resolveInstanceMethod
548 * Call +resolveInstanceMethod and return the method added or NULL.
549 * cls should be a non-meta class.
550 * Assumes the method doesn't exist already.
551 **********************************************************************/
552 static Method _class_resolveInstanceMethod(Class cls, SEL sel)
553 {
554 BOOL resolved;
555 Method meth = NULL;
556
557 if (!look_up_method(((id)cls)->isa, SEL_resolveInstanceMethod,
558 YES /*cache*/, NO /*resolver*/))
559 {
560 return NULL;
561 }
562
563 resolved = ((BOOL(*)(id, SEL, SEL))objc_msgSend)((id)cls, SEL_resolveInstanceMethod, sel);
564
565 if (resolved) {
566 // +resolveClassMethod adds to self
567 meth = look_up_method(cls, sel, YES/*cache*/, NO/*resolver*/);
568
569 if (!meth) {
570 // Method resolver didn't add anything?
571 _objc_inform("+[%s resolveInstanceMethod:%s] returned YES, but "
572 "no new implementation of %c[%s %s] was found",
573 class_getName(cls),
574 sel_getName(sel),
575 class_isMetaClass(cls) ? '+' : '-',
576 class_getName(cls),
577 sel_getName(sel));
578 return NULL;
579 }
580 }
581
582 return meth;
583 }
584
585
586 /***********************************************************************
587 * _class_resolveMethod
588 * Call +resolveClassMethod or +resolveInstanceMethod and return
589 * the method added or NULL.
590 * Assumes the method doesn't exist already.
591 **********************************************************************/
592 Method _class_resolveMethod(Class cls, SEL sel)
593 {
594 Method meth = NULL;
595
596 if (_class_isMetaClass(cls)) {
597 meth = _class_resolveClassMethod(cls, sel);
598 }
599 if (!meth) {
600 meth = _class_resolveInstanceMethod(cls, sel);
601 }
602
603 if (PrintResolving && meth) {
604 _objc_inform("RESOLVE: method %c[%s %s] dynamically resolved to %p",
605 class_isMetaClass(cls) ? '+' : '-',
606 class_getName(cls), sel_getName(sel),
607 method_getImplementation(meth));
608 }
609
610 return meth;
611 }
612
613
614 /***********************************************************************
615 * look_up_method
616 * Look up a method in the given class and its superclasses.
617 * If withCache==YES, look in the class's method cache too.
618 * If withResolver==YES, call +resolveClass/InstanceMethod too.
619 * Returns NULL if the method is not found.
620 * +forward:: entries are not returned.
621 **********************************************************************/
622 static Method look_up_method(Class cls, SEL sel,
623 BOOL withCache, BOOL withResolver)
624 {
625 Method meth = NULL;
626
627 if (withCache) {
628 meth = _cache_getMethod(cls, sel, _objc_msgForward_internal);
629 if (meth == (Method)1) {
630 // Cache contains forward:: . Stop searching.
631 return NULL;
632 }
633 }
634
635 if (!meth) meth = _class_getMethod(cls, sel);
636
637 if (!meth && withResolver) meth = _class_resolveMethod(cls, sel);
638
639 return meth;
640 }
641
642
643 /***********************************************************************
644 * class_getInstanceMethod. Return the instance method for the
645 * specified class and selector.
646 **********************************************************************/
647 Method class_getInstanceMethod(Class cls, SEL sel)
648 {
649 if (!cls || !sel) return NULL;
650
651 return look_up_method(cls, sel, YES/*cache*/, YES/*resolver*/);
652 }
653
654 /***********************************************************************
655 * class_getClassMethod. Return the class method for the specified
656 * class and selector.
657 **********************************************************************/
658 Method class_getClassMethod(Class cls, SEL sel)
659 {
660 if (!cls || !sel) return NULL;
661
662 return class_getInstanceMethod(_class_getMeta(cls), sel);
663 }
664
665
666 /***********************************************************************
667 * class_getInstanceVariable. Return the named instance variable.
668 **********************************************************************/
669 Ivar class_getInstanceVariable(Class cls, const char *name)
670 {
671 if (!cls || !name) return NULL;
672
673 return _class_getVariable(cls, name, NULL);
674 }
675
676
677 /***********************************************************************
678 * class_getClassVariable. Return the named class variable.
679 **********************************************************************/
680 Ivar class_getClassVariable(Class cls, const char *name)
681 {
682 if (!cls) return NULL;
683
684 return class_getInstanceVariable(((id)cls)->isa, name);
685 }
686
687
688 /***********************************************************************
689 * gdb_objc_class_changed
690 * Tell gdb that a class changed. Currently used for OBJC2 ivar layouts only
691 * Does nothing; gdb sets a breakpoint on it.
692 **********************************************************************/
693 BREAKPOINT_FUNCTION(
694 void gdb_objc_class_changed(Class cls, unsigned long changes, const char *classname)
695 );
696
697
698 /***********************************************************************
699 * _objc_flush_caches. Flush the caches of the specified class and any
700 * of its subclasses. If cls is a meta-class, only meta-class (i.e.
701 * class method) caches are flushed. If cls is an instance-class, both
702 * instance-class and meta-class caches are flushed.
703 **********************************************************************/
704 void _objc_flush_caches(Class cls)
705 {
706 flush_caches (cls, YES);
707
708 if (!cls) {
709 // collectALot if cls==nil
710 mutex_lock(&cacheUpdateLock);
711 _cache_collect(true);
712 mutex_unlock(&cacheUpdateLock);
713 }
714 }
715
716
717 /***********************************************************************
718 * class_respondsToSelector.
719 **********************************************************************/
720 BOOL class_respondsToMethod(Class cls, SEL sel)
721 {
722 OBJC_WARN_DEPRECATED;
723
724 return class_respondsToSelector(cls, sel);
725 }
726
727
728 BOOL class_respondsToSelector(Class cls, SEL sel)
729 {
730 IMP imp;
731
732 if (!sel || !cls) return NO;
733
734 // Avoids +initialize because it historically did so.
735 // We're not returning a callable IMP anyway.
736 imp = lookUpMethod(cls, sel, NO/*initialize*/, YES/*cache*/, nil);
737 return (imp != (IMP)_objc_msgForward_internal) ? YES : NO;
738 }
739
740
741 /***********************************************************************
742 * class_getMethodImplementation.
743 * Returns the IMP that would be invoked if [obj sel] were sent,
744 * where obj is an instance of class cls.
745 **********************************************************************/
746 IMP class_lookupMethod(Class cls, SEL sel)
747 {
748 OBJC_WARN_DEPRECATED;
749
750 // No one responds to zero!
751 if (!sel) {
752 __objc_error((id)cls, "invalid selector (null)");
753 }
754
755 return class_getMethodImplementation(cls, sel);
756 }
757
758 IMP class_getMethodImplementation(Class cls, SEL sel)
759 {
760 IMP imp;
761
762 if (!cls || !sel) return NULL;
763
764 imp = lookUpMethod(cls, sel, YES/*initialize*/, YES/*cache*/, nil);
765
766 // Translate forwarding function to C-callable external version
767 if (imp == _objc_msgForward_internal) {
768 return _objc_msgForward;
769 }
770
771 return imp;
772 }
773
774
775 IMP class_getMethodImplementation_stret(Class cls, SEL sel)
776 {
777 IMP imp = class_getMethodImplementation(cls, sel);
778
779 // Translate forwarding function to struct-returning version
780 if (imp == (IMP)&_objc_msgForward /* not _internal! */) {
781 return (IMP)&_objc_msgForward_stret;
782 }
783 return imp;
784 }
785
786
787 /***********************************************************************
788 * instrumentObjcMessageSends/logObjcMessageSends.
789 **********************************************************************/
790 #if !defined(MESSAGE_LOGGING) && defined(__arm__)
791 void instrumentObjcMessageSends (BOOL flag)
792 {
793 }
794 #elif defined(MESSAGE_LOGGING)
795 static int LogObjCMessageSend (BOOL isClassMethod,
796 const char * objectsClass,
797 const char * implementingClass,
798 SEL selector)
799 {
800 char buf[ 1024 ];
801
802 // Create/open the log file
803 if (objcMsgLogFD == (-1))
804 {
805 snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ());
806 objcMsgLogFD = secure_open (buf, O_WRONLY | O_CREAT, geteuid());
807 if (objcMsgLogFD < 0) {
808 // no log file - disable logging
809 objcMsgLogEnabled = 0;
810 objcMsgLogFD = -1;
811 return 1;
812 }
813 }
814
815 // Make the log entry
816 snprintf(buf, sizeof(buf), "%c %s %s %s\n",
817 isClassMethod ? '+' : '-',
818 objectsClass,
819 implementingClass,
820 sel_getName(selector));
821
822 static OSSpinLock lock = OS_SPINLOCK_INIT;
823 OSSpinLockLock(&lock);
824 write (objcMsgLogFD, buf, strlen(buf));
825 OSSpinLockUnlock(&lock);
826
827 // Tell caller to not cache the method
828 return 0;
829 }
830
831 void instrumentObjcMessageSends (BOOL flag)
832 {
833 int enabledValue = (flag) ? 1 : 0;
834
835 // Shortcut NOP
836 if (objcMsgLogEnabled == enabledValue)
837 return;
838
839 // If enabling, flush all method caches so we get some traces
840 if (flag)
841 flush_caches (Nil, YES);
842
843 // Sync our log file
844 if (objcMsgLogFD != (-1))
845 fsync (objcMsgLogFD);
846
847 objcMsgLogEnabled = enabledValue;
848 }
849
850 void logObjcMessageSends (ObjCLogProc logProc)
851 {
852 if (logProc)
853 {
854 objcMsgLogProc = logProc;
855 objcMsgLogEnabled = 1;
856 }
857 else
858 {
859 objcMsgLogProc = logProc;
860 objcMsgLogEnabled = 0;
861 }
862
863 if (objcMsgLogFD != (-1))
864 fsync (objcMsgLogFD);
865 }
866 #endif
867
868 /***********************************************************************
869 * log_and_fill_cache
870 * Log this method call. If the logger permits it, fill the method cache.
871 * cls is the method whose cache should be filled.
872 * implementer is the class that owns the implementation in question.
873 **********************************************************************/
874 void
875 log_and_fill_cache(Class cls, Class implementer, Method meth, SEL sel)
876 {
877 #if defined(MESSAGE_LOGGING)
878 BOOL cacheIt = YES;
879
880 if (objcMsgLogEnabled) {
881 cacheIt = objcMsgLogProc (_class_isMetaClass(implementer) ? YES : NO,
882 _class_getName(cls),
883 _class_getName(implementer),
884 sel);
885 }
886 if (cacheIt)
887 #endif
888 _cache_fill (cls, meth, sel);
889 }
890
891
892 /***********************************************************************
893 * _class_lookupMethodAndLoadCache.
894 * Method lookup for dispatchers ONLY. OTHER CODE SHOULD USE lookUpMethod().
895 * This lookup avoids optimistic cache scan because the dispatcher
896 * already tried that.
897 **********************************************************************/
898 IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
899 {
900 return lookUpMethod(cls, sel, YES/*initialize*/, NO/*cache*/, obj);
901 }
902
903
904 /***********************************************************************
905 * lookUpMethod.
906 * The standard method lookup.
907 * initialize==NO tries to avoid +initialize (but sometimes fails)
908 * cache==NO skips optimistic unlocked lookup (but uses cache elsewhere)
909 * Most callers should use initialize==YES and cache==YES.
910 * inst is an instance of cls or a subclass thereof, or nil if none is known.
911 * If cls is an un-initialized metaclass then a non-nil inst is faster.
912 * May return _objc_msgForward_internal. IMPs destined for external use
913 * must be converted to _objc_msgForward or _objc_msgForward_stret.
914 **********************************************************************/
915 IMP lookUpMethod(Class cls, SEL sel, BOOL initialize, BOOL cache, id inst)
916 {
917 Class curClass;
918 IMP methodPC = NULL;
919 Method meth;
920 BOOL triedResolver = NO;
921
922 // Optimistic cache lookup
923 if (cache) {
924 methodPC = _cache_getImp(cls, sel);
925 if (methodPC) return methodPC;
926 }
927
928 // realize, +initialize, and any special early exit
929 methodPC = prepareForMethodLookup(cls, sel, initialize, inst);
930 if (methodPC) return methodPC;
931
932
933 // The lock is held to make method-lookup + cache-fill atomic
934 // with respect to method addition. Otherwise, a category could
935 // be added but ignored indefinitely because the cache was re-filled
936 // with the old value after the cache flush on behalf of the category.
937 retry:
938 lockForMethodLookup();
939
940 // Ignore GC selectors
941 if (ignoreSelector(sel)) {
942 methodPC = _cache_addIgnoredEntry(cls, sel);
943 goto done;
944 }
945
946 // Try this class's cache.
947
948 methodPC = _cache_getImp(cls, sel);
949 if (methodPC) goto done;
950
951 // Try this class's method lists.
952
953 meth = _class_getMethodNoSuper_nolock(cls, sel);
954 if (meth) {
955 log_and_fill_cache(cls, cls, meth, sel);
956 methodPC = method_getImplementation(meth);
957 goto done;
958 }
959
960 // Try superclass caches and method lists.
961
962 curClass = cls;
963 while ((curClass = _class_getSuperclass(curClass))) {
964 // Superclass cache.
965 meth = _cache_getMethod(curClass, sel, _objc_msgForward_internal);
966 if (meth) {
967 if (meth != (Method)1) {
968 // Found the method in a superclass. Cache it in this class.
969 log_and_fill_cache(cls, curClass, meth, sel);
970 methodPC = method_getImplementation(meth);
971 goto done;
972 }
973 else {
974 // Found a forward:: entry in a superclass.
975 // Stop searching, but don't cache yet; call method
976 // resolver for this class first.
977 break;
978 }
979 }
980
981 // Superclass method list.
982 meth = _class_getMethodNoSuper_nolock(curClass, sel);
983 if (meth) {
984 log_and_fill_cache(cls, curClass, meth, sel);
985 methodPC = method_getImplementation(meth);
986 goto done;
987 }
988 }
989
990 // No implementation found. Try method resolver once.
991
992 if (!triedResolver) {
993 unlockForMethodLookup();
994 _class_resolveMethod(cls, sel);
995 // Don't cache the result; we don't hold the lock so it may have
996 // changed already. Re-do the search from scratch instead.
997 triedResolver = YES;
998 goto retry;
999 }
1000
1001 // No implementation found, and method resolver didn't help.
1002 // Use forwarding.
1003
1004 _cache_addForwardEntry(cls, sel);
1005 methodPC = _objc_msgForward_internal;
1006
1007 done:
1008 unlockForMethodLookup();
1009
1010 // paranoia: look for ignored selectors with non-ignored implementations
1011 assert(!(ignoreSelector(sel) && methodPC != (IMP)&_objc_ignored_method));
1012
1013 return methodPC;
1014 }
1015
1016
1017 /***********************************************************************
1018 * lookupMethodInClassAndLoadCache.
1019 * Like _class_lookupMethodAndLoadCache, but does not search superclasses.
1020 * Caches and returns objc_msgForward if the method is not found in the class.
1021 **********************************************************************/
1022 static IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel)
1023 {
1024 Method meth;
1025 IMP imp;
1026
1027 // fixme this still has the method list vs method cache race
1028 // because it doesn't hold a lock across lookup+cache_fill,
1029 // but it's only used for .cxx_construct/destruct and we assume
1030 // categories don't change them.
1031
1032 // Search cache first.
1033 imp = _cache_getImp(cls, sel);
1034 if (imp) return imp;
1035
1036 // Cache miss. Search method list.
1037
1038 meth = _class_getMethodNoSuper(cls, sel);
1039
1040 if (meth) {
1041 // Hit in method list. Cache it.
1042 _cache_fill(cls, meth, sel);
1043 return method_getImplementation(meth);
1044 } else {
1045 // Miss in method list. Cache objc_msgForward.
1046 _cache_addForwardEntry(cls, sel);
1047 return _objc_msgForward_internal;
1048 }
1049 }
1050
1051
1052 /***********************************************************************
1053 * _malloc_internal
1054 * _calloc_internal
1055 * _realloc_internal
1056 * _strdup_internal
1057 * _strdupcat_internal
1058 * _memdup_internal
1059 * _free_internal
1060 * Convenience functions for the internal malloc zone.
1061 **********************************************************************/
1062 void *_malloc_internal(size_t size)
1063 {
1064 return malloc_zone_malloc(_objc_internal_zone(), size);
1065 }
1066
1067 void *_calloc_internal(size_t count, size_t size)
1068 {
1069 return malloc_zone_calloc(_objc_internal_zone(), count, size);
1070 }
1071
1072 void *_realloc_internal(void *ptr, size_t size)
1073 {
1074 return malloc_zone_realloc(_objc_internal_zone(), ptr, size);
1075 }
1076
1077 char *_strdup_internal(const char *str)
1078 {
1079 size_t len;
1080 char *dup;
1081 if (!str) return NULL;
1082 len = strlen(str);
1083 dup = (char *)malloc_zone_malloc(_objc_internal_zone(), len + 1);
1084 memcpy(dup, str, len + 1);
1085 return dup;
1086 }
1087
1088 uint8_t *_ustrdup_internal(const uint8_t *str)
1089 {
1090 return (uint8_t *)_strdup_internal((char *)str);
1091 }
1092
1093 // allocate a new string that concatenates s1+s2.
1094 char *_strdupcat_internal(const char *s1, const char *s2)
1095 {
1096 size_t len1 = strlen(s1);
1097 size_t len2 = strlen(s2);
1098 char *dup = (char *)
1099 malloc_zone_malloc(_objc_internal_zone(), len1 + len2 + 1);
1100 memcpy(dup, s1, len1);
1101 memcpy(dup + len1, s2, len2 + 1);
1102 return dup;
1103 }
1104
1105 void *_memdup_internal(const void *mem, size_t len)
1106 {
1107 void *dup = malloc_zone_malloc(_objc_internal_zone(), len);
1108 memcpy(dup, mem, len);
1109 return dup;
1110 }
1111
1112 void _free_internal(void *ptr)
1113 {
1114 malloc_zone_free(_objc_internal_zone(), ptr);
1115 }
1116
1117 size_t _malloc_size_internal(void *ptr)
1118 {
1119 malloc_zone_t *zone = _objc_internal_zone();
1120 return zone->size(zone, ptr);
1121 }
1122
1123 Class _calloc_class(size_t size)
1124 {
1125 #if SUPPORT_GC
1126 if (UseGC) return (Class) malloc_zone_calloc(gc_zone, 1, size);
1127 #endif
1128 return (Class) _calloc_internal(1, size);
1129 }
1130
1131
1132 const char *class_getName(Class cls)
1133 {
1134 return _class_getName(cls);
1135 }
1136
1137 Class class_getSuperclass(Class cls)
1138 {
1139 return _class_getSuperclass(cls);
1140 }
1141
1142 BOOL class_isMetaClass(Class cls)
1143 {
1144 return _class_isMetaClass(cls);
1145 }
1146
1147
1148 size_t class_getInstanceSize(Class cls)
1149 {
1150 return _class_getInstanceSize(cls);
1151 }
1152
1153
1154 /***********************************************************************
1155 * method_getNumberOfArguments.
1156 **********************************************************************/
1157 unsigned int method_getNumberOfArguments(Method m)
1158 {
1159 if (!m) return 0;
1160 return encoding_getNumberOfArguments(method_getTypeEncoding(m));
1161 }
1162
1163
1164 void method_getReturnType(Method m, char *dst, size_t dst_len)
1165 {
1166 encoding_getReturnType(method_getTypeEncoding(m), dst, dst_len);
1167 }
1168
1169
1170 char * method_copyReturnType(Method m)
1171 {
1172 return encoding_copyReturnType(method_getTypeEncoding(m));
1173 }
1174
1175
1176 void method_getArgumentType(Method m, unsigned int index,
1177 char *dst, size_t dst_len)
1178 {
1179 encoding_getArgumentType(method_getTypeEncoding(m),
1180 index, dst, dst_len);
1181 }
1182
1183
1184 char * method_copyArgumentType(Method m, unsigned int index)
1185 {
1186 return encoding_copyArgumentType(method_getTypeEncoding(m), index);
1187 }
1188
1189
1190 /***********************************************************************
1191 * objc_constructInstance
1192 * Creates an instance of `cls` at the location pointed to by `bytes`.
1193 * `bytes` must point to at least class_getInstanceSize(cls) bytes of
1194 * well-aligned zero-filled memory.
1195 * The new object's isa is set. Any C++ constructors are called.
1196 * Returns `bytes` if successful. Returns nil if `cls` or `bytes` is
1197 * NULL, or if C++ constructors fail.
1198 * Note: class_createInstance() and class_createInstances() preflight this.
1199 **********************************************************************/
1200 static id
1201 _objc_constructInstance(Class cls, void *bytes)
1202 {
1203 id obj = (id)bytes;
1204
1205 // Set the isa pointer
1206 obj->isa = cls; // need not be object_setClass
1207
1208 // Call C++ constructors, if any.
1209 if (!object_cxxConstruct(obj)) {
1210 // Some C++ constructor threw an exception.
1211 return nil;
1212 }
1213
1214 return obj;
1215 }
1216
1217
1218 id
1219 objc_constructInstance(Class cls, void *bytes)
1220 {
1221 if (!cls || !bytes) return nil;
1222 return _objc_constructInstance(cls, bytes);
1223 }
1224
1225
1226 id
1227 _objc_constructOrFree(Class cls, void *bytes)
1228 {
1229 id obj = _objc_constructInstance(cls, bytes);
1230 if (!obj) {
1231 #if SUPPORT_GC
1232 if (UseGC) {
1233 auto_zone_retain(gc_zone, bytes); // gc free expects rc==1
1234 }
1235 #endif
1236 free(bytes);
1237 }
1238
1239 return obj;
1240 }
1241
1242
1243 /***********************************************************************
1244 * _class_createInstancesFromZone
1245 * Batch-allocating version of _class_createInstanceFromZone.
1246 * Attempts to allocate num_requested objects, each with extraBytes.
1247 * Returns the number of allocated objects (possibly zero), with
1248 * the allocated pointers in *results.
1249 **********************************************************************/
1250 unsigned
1251 _class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone,
1252 id *results, unsigned num_requested)
1253 {
1254 unsigned num_allocated;
1255 if (!cls) return 0;
1256
1257 size_t size = _class_getInstanceSize(cls) + extraBytes;
1258 // CF requires all objects be at least 16 bytes.
1259 if (size < 16) size = 16;
1260
1261 #if SUPPORT_GC
1262 if (UseGC) {
1263 num_allocated =
1264 auto_zone_batch_allocate(gc_zone, size, AUTO_OBJECT_SCANNED, 0, 1,
1265 (void**)results, num_requested);
1266 } else
1267 #endif
1268 {
1269 unsigned i;
1270 num_allocated =
1271 malloc_zone_batch_malloc((malloc_zone_t *)(zone ? zone : malloc_default_zone()),
1272 size, (void**)results, num_requested);
1273 for (i = 0; i < num_allocated; i++) {
1274 bzero(results[i], size);
1275 }
1276 }
1277
1278 // Construct each object, and delete any that fail construction.
1279
1280 unsigned shift = 0;
1281 unsigned i;
1282 BOOL ctor = _class_hasCxxStructors(cls);
1283 for (i = 0; i < num_allocated; i++) {
1284 id obj = results[i];
1285 if (ctor) obj = _objc_constructOrFree(cls, obj);
1286 else if (obj) obj->isa = cls; // need not be object_setClass
1287
1288 if (obj) {
1289 results[i-shift] = obj;
1290 } else {
1291 shift++;
1292 }
1293 }
1294
1295 return num_allocated - shift;
1296 }
1297
1298
1299 /***********************************************************************
1300 * inform_duplicate. Complain about duplicate class implementations.
1301 **********************************************************************/
1302 void
1303 inform_duplicate(const char *name, Class oldCls, Class cls)
1304 {
1305 #if TARGET_OS_WIN32
1306 _objc_inform ("Class %s is implemented in two different images.", name);
1307 #else
1308 const header_info *oldHeader = _headerForClass(oldCls);
1309 const header_info *newHeader = _headerForClass(cls);
1310 const char *oldName = oldHeader ? oldHeader->fname : "??";
1311 const char *newName = newHeader ? newHeader->fname : "??";
1312
1313 _objc_inform ("Class %s is implemented in both %s and %s. "
1314 "One of the two will be used. "
1315 "Which one is undefined.",
1316 name, oldName, newName);
1317 #endif
1318 }
1319
1320 #if SUPPORT_TAGGED_POINTERS
1321 /***********************************************************************
1322 * _objc_insert_tagged_isa
1323 * Insert an isa into a particular slot in the tagged isa table.
1324 * Will error & abort if slot already has an isa that is different.
1325 **********************************************************************/
1326 void _objc_insert_tagged_isa(unsigned char slotNumber, Class isa) {
1327 unsigned char actualSlotNumber = (slotNumber << 1) + 1;
1328 Class previousIsa = _objc_tagged_isa_table[actualSlotNumber];
1329
1330 if (actualSlotNumber & 0xF0) {
1331 _objc_fatal("%s -- Slot number %uc is too large. Aborting.", __FUNCTION__, slotNumber);
1332 }
1333
1334 if (actualSlotNumber == 0) {
1335 _objc_fatal("%s -- Slot number 0 doesn't make sense. Aborting.", __FUNCTION__);
1336 }
1337
1338 if (isa && previousIsa && (previousIsa != isa)) {
1339 _objc_fatal("%s -- Tagged pointer table already had an item in that slot (%s). "
1340 "Not putting (%s) in table. Aborting instead",
1341 __FUNCTION__, class_getName(previousIsa), class_getName(isa));
1342 }
1343 _objc_tagged_isa_table[actualSlotNumber] = isa;
1344 }
1345 #endif
1346
1347
1348 const char *
1349 copyPropertyAttributeString(const objc_property_attribute_t *attrs,
1350 unsigned int count)
1351 {
1352 char *result;
1353 unsigned int i;
1354 if (count == 0) return strdup("");
1355
1356 #ifndef NDEBUG
1357 // debug build: sanitize input
1358 for (i = 0; i < count; i++) {
1359 assert(attrs[i].name);
1360 assert(strlen(attrs[i].name) > 0);
1361 assert(! strchr(attrs[i].name, ','));
1362 assert(! strchr(attrs[i].name, '"'));
1363 if (attrs[i].value) assert(! strchr(attrs[i].value, ','));
1364 }
1365 #endif
1366
1367 size_t len = 0;
1368 for (i = 0; i < count; i++) {
1369 if (attrs[i].value) {
1370 size_t namelen = strlen(attrs[i].name);
1371 if (namelen > 1) namelen += 2; // long names get quoted
1372 len += namelen + strlen(attrs[i].value) + 1;
1373 }
1374 }
1375
1376 result = (char *)malloc(len + 1);
1377 char *s = result;
1378 for (i = 0; i < count; i++) {
1379 if (attrs[i].value) {
1380 size_t namelen = strlen(attrs[i].name);
1381 if (namelen > 1) {
1382 s += sprintf(s, "\"%s\"%s,", attrs[i].name, attrs[i].value);
1383 } else {
1384 s += sprintf(s, "%s%s,", attrs[i].name, attrs[i].value);
1385 }
1386 }
1387 }
1388
1389 // remove trailing ',' if any
1390 if (s > result) s[-1] = '\0';
1391
1392 return result;
1393 }
1394
1395 /*
1396 Property attribute string format:
1397
1398 - Comma-separated name-value pairs.
1399 - Name and value may not contain ,
1400 - Name may not contain "
1401 - Value may be empty
1402 - Name is single char, value follows
1403 - OR Name is double-quoted string of 2+ chars, value follows
1404
1405 Grammar:
1406 attribute-string: \0
1407 attribute-string: name-value-pair (',' name-value-pair)*
1408 name-value-pair: unquoted-name optional-value
1409 name-value-pair: quoted-name optional-value
1410 unquoted-name: [^",]
1411 quoted-name: '"' [^",]{2,} '"'
1412 optional-value: [^,]*
1413
1414 */
1415 static unsigned int
1416 iteratePropertyAttributes(const char *attrs,
1417 BOOL (*fn)(unsigned int index,
1418 void *ctx1, void *ctx2,
1419 const char *name, size_t nlen,
1420 const char *value, size_t vlen),
1421 void *ctx1, void *ctx2)
1422 {
1423 if (!attrs) return 0;
1424
1425 #ifndef NDEBUG
1426 const char *attrsend = attrs + strlen(attrs);
1427 #endif
1428 unsigned int attrcount = 0;
1429
1430 while (*attrs) {
1431 // Find the next comma-separated attribute
1432 const char *start = attrs;
1433 const char *end = start + strcspn(attrs, ",");
1434
1435 // Move attrs past this attribute and the comma (if any)
1436 attrs = *end ? end+1 : end;
1437
1438 assert(attrs <= attrsend);
1439 assert(start <= attrsend);
1440 assert(end <= attrsend);
1441
1442 // Skip empty attribute
1443 if (start == end) continue;
1444
1445 // Process one non-empty comma-free attribute [start,end)
1446 const char *nameStart;
1447 const char *nameEnd;
1448
1449 assert(start < end);
1450 assert(*start);
1451 if (*start != '\"') {
1452 // single-char short name
1453 nameStart = start;
1454 nameEnd = start+1;
1455 start++;
1456 }
1457 else {
1458 // double-quoted long name
1459 nameStart = start+1;
1460 nameEnd = nameStart + strcspn(nameStart, "\",");
1461 start++; // leading quote
1462 start += nameEnd - nameStart; // name
1463 if (*start == '\"') start++; // trailing quote, if any
1464 }
1465
1466 // Process one possibly-empty comma-free attribute value [start,end)
1467 const char *valueStart;
1468 const char *valueEnd;
1469
1470 assert(start <= end);
1471
1472 valueStart = start;
1473 valueEnd = end;
1474
1475 BOOL more = (*fn)(attrcount, ctx1, ctx2,
1476 nameStart, nameEnd-nameStart,
1477 valueStart, valueEnd-valueStart);
1478 attrcount++;
1479 if (!more) break;
1480 }
1481
1482 return attrcount;
1483 }
1484
1485
1486 static BOOL
1487 copyOneAttribute(unsigned int index, void *ctxa, void *ctxs,
1488 const char *name, size_t nlen, const char *value, size_t vlen)
1489 {
1490 objc_property_attribute_t **ap = (objc_property_attribute_t**)ctxa;
1491 char **sp = (char **)ctxs;
1492
1493 objc_property_attribute_t *a = *ap;
1494 char *s = *sp;
1495
1496 a->name = s;
1497 memcpy(s, name, nlen);
1498 s += nlen;
1499 *s++ = '\0';
1500
1501 a->value = s;
1502 memcpy(s, value, vlen);
1503 s += vlen;
1504 *s++ = '\0';
1505
1506 a++;
1507
1508 *ap = a;
1509 *sp = s;
1510
1511 return YES;
1512 }
1513
1514
1515 objc_property_attribute_t *
1516 copyPropertyAttributeList(const char *attrs, unsigned int *outCount)
1517 {
1518 if (!attrs) {
1519 if (outCount) *outCount = 0;
1520 return NULL;
1521 }
1522
1523 // Result size:
1524 // number of commas plus 1 for the attributes (upper bound)
1525 // plus another attribute for the attribute array terminator
1526 // plus strlen(attrs) for name/value string data (upper bound)
1527 // plus count*2 for the name/value string terminators (upper bound)
1528 unsigned int attrcount = 1;
1529 const char *s;
1530 for (s = attrs; s && *s; s++) {
1531 if (*s == ',') attrcount++;
1532 }
1533
1534 size_t size =
1535 attrcount * sizeof(objc_property_attribute_t) +
1536 sizeof(objc_property_attribute_t) +
1537 strlen(attrs) +
1538 attrcount * 2;
1539 objc_property_attribute_t *result = (objc_property_attribute_t *)
1540 calloc(size, 1);
1541
1542 objc_property_attribute_t *ra = result;
1543 char *rs = (char *)(ra+attrcount+1);
1544
1545 attrcount = iteratePropertyAttributes(attrs, copyOneAttribute, &ra, &rs);
1546
1547 assert((uint8_t *)(ra+1) <= (uint8_t *)result+size);
1548 assert((uint8_t *)rs <= (uint8_t *)result+size);
1549
1550 if (attrcount == 0) {
1551 free(result);
1552 result = NULL;
1553 }
1554
1555 if (outCount) *outCount = attrcount;
1556 return result;
1557 }
1558
1559
1560 static BOOL
1561 findOneAttribute(unsigned int index, void *ctxa, void *ctxs,
1562 const char *name, size_t nlen, const char *value, size_t vlen)
1563 {
1564 const char *query = (char *)ctxa;
1565 char **resultp = (char **)ctxs;
1566
1567 if (strlen(query) == nlen && 0 == strncmp(name, query, nlen)) {
1568 char *result = (char *)calloc(vlen+1, 1);
1569 memcpy(result, value, vlen);
1570 result[vlen] = '\0';
1571 *resultp = result;
1572 return NO;
1573 }
1574
1575 return YES;
1576 }
1577
1578 char *copyPropertyAttributeValue(const char *attrs, const char *name)
1579 {
1580 char *result = NULL;
1581
1582 iteratePropertyAttributes(attrs, findOneAttribute, (void*)name, &result);
1583
1584 return result;
1585 }