__objc_init
-_map_images
-_sel_registerName
-___sel_registerName
-__objc_search_builtins
-_phash
-_lookup
-_exception_init
-__getImageSlide
-__getObjcImageInfo
-_getsegbynamefromheader
-__getObjcModules
+_environ_init
+_tls_init
+_lock_init
+_recursive_mutex_init
__malloc_internal
__objc_internal_zone
+_exception_init
+_map_images
+_map_images_nolock
+__getObjcImageInfo
+__calloc_internal
+__hasObjcContents
+__objc_appendHeader
_verify_gc_readiness
_gc_init
+__objc_inform_on_crash
+__objc_crashlog
_rtp_init
+_gc_fixup_barrier_stubs
+__objc_update_stubs_in_mach_header
+_sel_init
+_sel_lock
+___sel_registerName
+__objc_search_builtins
+__ZNK8objc_opt13objc_selopt_t3getEPKc
+__ZNK8objc_opt13objc_selopt_t4hashEPKc
+_sel_unlock
+_sel_registerName
+_arr_init
+__ZN4objc8DenseMapIP11objc_objectmLb1ENS_12DenseMapInfoIS2_EENS3_ImEEE4initEj
__read_images
-__objc_init_class_hash
+__Z11initVtablesv
+__Z17appendTrampolinesP22objc_trampoline_header
+_gdb_objc_trampolines_changed
+_crashlog_header_name
+__getObjc2ClassList
+_NXCreateMapTableFromZone
+_NXCreateHashTable
_NXCreateHashTableFromZone
_NXHashGet
-_hashPrototype
-_isEqualPrototype
_NXHashInsert
__NXHashRehashToCapacity
+_NXNextHashState
_freeBuckets
_NXNoEffectFree
-_log2u
-__NXHashCapacity
-__class_hasLoadMethod
-__class_getLoadMethod_nocheck
-_classHash
-__class_getName
-_resolve_categories_for_class
-_lookupNamedMethodInMethodList
-_classIsEqual
-_objc_lookUpClass
-_look_up_class
-__objc_insertMethods
-__calloc_internal
-__class_clearInfo
-__class_changeInfo
-__class_setInfo
-__class_addProperties
-__memdup_internal
-_allocateExt
-_connect_class
-_class_is_connected
-_NXHashMember
-_really_connect_class
-_set_superclass
-_NXHashRemove
-_NXCountHashTable
-_NXFreeHashTable
-__getObjcClassRefs
-__getObjcSelectorRefs
-_sel_lock
+_hashPrototype
+_NXPtrHash
+_isEqualPrototype
+__Z13futureClassesv
+_NXCountMapTable
+__Z13addNamedClassP7class_tPKc
+_NXMapGet
+__mapStrHash
+_NXMapInsert
+__mapPtrHash
+__Z10remapClassP7class_t
+__Z15remappedClassesa
+_NXMapMember
+__NXMapMember
+__mapStrIsEqual
+__mapPtrIsEqual
+__getObjc2ClassRefs
+__getObjc2SuperRefs
+_sel_preoptimizationValid
+__getObjc2SelectorRefs
_sel_registerNameNoLock
___objc_sel_set_create
___objc_sel_set_add
___objc_sel_set_findBuckets
___objc_sel_set_get
-_sel_unlock
-_objc_getClass
-_NXCreateMapTableFromZone
-_NXCreateHashTable
-_NXPtrHash
-__getObjcProtocols
-__getObjcClassNames
-_map_method_descs
-_NXMapGet
-__mapStrHash
+__Z9protocolsv
+__getObjc2ProtocolList
_NXMapKeyCopyingInsert
__strdup_internal
-_NXMapInsert
-__mapStrIsEqual
-__mapPtrHash
-__mapPtrIsEqual
__NXMapRehash
+__getObjc2ProtocolRefs
+__Z13remapProtocolm
+__getObjc2NonlazyClassList
+__Z12realizeClassP7class_t
+__Z11addSubclassP7class_tS0_
+__Z17attachMethodListsP7class_tPP13method_list_tiaPa
+__Z15fixupMethodListP13method_list_ta
+__memdup_internal
+__ZNSt3__113__stable_sortIRN8method_t16SortBySELAddressEN13method_list_t15method_iteratorEEEvT0_S6_T_NS_15iterator_traitsIS6_E1
+__Z9addMethodP7class_tP13objc_selectorPFP11objc_objectS3_S1_zEPKca
+__Z23getMethodNoSuper_nolockP7class_tP13objc_selector
+__ZN7class_t14setHasCustomRREv
+__Z20unattachedCategoriesv
+_NXMapRemove
+__Z21attachCategoryMethodsP7class_tP13category_listPa
+_objc_addRegisteredClass
+_layout_bitmap_create
+_set_bits
+_layout_bitmap_free
__free_internal
+__ZNSt3__116__insertion_sortIRN8method_t16SortBySELAddressEN13method_list_t15method_iteratorEEEvT0_S6_T_
+__Z17buildProtocolListP13category_listPK15protocol_list_tPS3_
+__Z17buildPropertyListPK15property_list_tP13category_lista
+__ZNSt3__120get_temporary_bufferI8method_tEENS_4pairIPT_lEEl
+__ZNSt3__118__stable_sort_moveIRN8method_t16SortBySELAddressEN13method_list_t15method_iteratorEEEvT0_S6_T_NS_15iterator_traitsI
+__ZNSt3__122__merge_move_constructIRN8method_t16SortBySELAddressEN13method_list_t15method_iteratorES5_EEvT0_S6_T1_S7_PNS_15iter
+__ZNSt3__119__merge_move_assignIRN8method_t16SortBySELAddressEPS1_S4_N13method_list_t15method_iteratorEEEvT0_S7_T1_S8_T2_T_
+_NXPtrIsEqual
+__getObjc2CategoryList
+__Z29addUnattachedCategoryForClassP10category_tP7class_tP12_header_info
+__Z16remethodizeClassP7class_t
+__Z11flushCachesP7class_t
+_flush_cache
+__class_getCache
+__realloc_internal
_load_images
+_load_images_nolock
_prepare_load_methods
-_schedule_class_load
+__Z19schedule_class_loadP7class_t
_add_class_to_loadable_list
__class_getLoadMethod
+__getObjc2NonlazyCategoryList
_call_load_methods
-__realloc_internal
-_add_category_to_loadable_list
-__category_getLoadMethod
++[Protocol load]
+_objc_lookUpClass
+_look_up_class
_object_getClass
_protocol_copyMethodDescriptionList
_class_getClassMethod
__class_getMeta
_look_up_method
+__cache_getMethod
__class_getMethod
-_fixupSelectorsInMethodList
_method_getTypeEncoding
_method_getImplementation
_method_getName
_class_addMethod
-__class_addMethod
-_flush_caches
-__cache_flush
-__class_getCache
_class_getInstanceMethod
+__Z12flushVtablesP7class_t
+__Z12updateVtableP7class_ta
_class_replaceMethod
-_method_setImplementation
+__Z25_method_setImplementationP7class_tP8method_tPFP11objc_objectS4_P13objc_selectorzE
_class_addProtocol
-_objc_exception_get_functions
-_objc_exception_set_functions
+_class_conformsToProtocol
+_objc_setExceptionPreprocessor
+_objc_setExceptionMatcher
+_objc_setUncaughtExceptionHandler
_objc_setForwardHandler
_objc_setEnumerationMutationHandler
-_objc_collecting_enabled
+_objc_collectingEnabled
_objc_getFutureClass
-_objc_setFutureClass
-_setOriginalClassForFutureClass
-_change_class_references
-_NXInitHashState
-_NXNextHashState
+_objc_assign_strongCast_non_gc
+_objc_getClass
+__objc_insert_tagged_isa
+_objc_msgSend_fixup
+__objc_fixupMessageRef
_objc_msgSend
-__class_lookupMethodAndLoadCache
-__class_getFreedObjectClass
-__class_isInitialized
+__class_lookupMethodAndLoadCache3
+_lookUpMethod
+_prepareForMethodLookup
__class_initialize
-__class_isMetaClass
+__class_getNonMetaClass
+__Z15getNonMetaClassP7class_t
__class_getSuperclass
+__class_isInitialized
__class_isInitializing
__class_setInitializing
__fetchInitializingClassList
__objc_fetch_pthread_data
-__cache_getMethod
-__class_getMethodNoSuper
+_lockForMethodLookup
+__cache_getImp
+__class_getMethodNoSuper_nolock
_log_and_fill_cache
__cache_fill
-_objc_assign_global
+_unlockForMethodLookup
+_objc_assign_global_non_gc
+_class_setSuperclass
_class_setVersion
-__class_setInitialized
-__cache_getImp
-__cache_malloc
-__class_setCache
-__class_setGrowCache
+_objc_msgSend_vtable1
+__objc_rootAlloc
_class_getInstanceSize
__class_getInstanceSize
-_class_createInstanceFromZone
-__objc_warn_deprecated
-__internal_class_createInstanceFromZone
-_object_cxxConstructFromClass
-__class_hasCxxStructorsNoSuper
+_class_createInstance
_object_getClassName
+__class_getName
_object_getIndexedIvars
-_objc_assign_strongCast
+_objc_msgSend_vtable0
+__objc_rootAllocWithZone
+__objc_rootInit
+_objc_msgSend_vtable3
+_objc_assign_ivar_non_gc
+__objc_rootRetain
+__ZN4objc8DenseMapIP11objc_objectmLb1ENS_12DenseMapInfoIS2_EENS3_ImEEE16FindAndConstructERKS2_
+__ZNK4objc8DenseMapIP11objc_objectmLb1ENS_12DenseMapInfoIS2_EENS3_ImEEE15LookupBucketForERKS2_RPNSt3__14pairIS2_mEE
+__ZN4objc8DenseMapIP11objc_objectmLb1ENS_12DenseMapInfoIS2_EENS3_ImEEE16InsertIntoBucketERKS2_RKmPNSt3__14pairIS2_mEE
+__objc_rootRelease
+__objc_rootReleaseWasZero
+__ZN4objc8DenseMapIP11objc_objectmLb1ENS_12DenseMapInfoIS2_EENS3_ImEEE4findERKS2_
+__finishInitializing
+__class_setInitialized
+_NXFreeMapTable
+_NXResetMapTable
+__cache_malloc
+__class_setCache
+__class_setGrowCache
+_objc_initializeClassPair
+__Z33objc_initializeClassPair_internalP10objc_classPKcS0_S0_
+_objc_registerClassPair
+_add_category_to_loadable_list
+__category_getLoadMethod
+__category_getClass
+__class_isLoadable
+_objc_msgSendSuper2
+__objc_autoreleasePoolPush
+_objc_autoreleasePoolPush
+__ZN12_GLOBAL__N_119AutoreleasePoolPageC1EPS0_
+__ZN12_GLOBAL__N_119AutoreleasePoolPage9fastcheckEb
+_objc_destructInstance
+_objc_clear_deallocating
+__ZN4objc8DenseMapIP11objc_objectmLb1ENS_12DenseMapInfoIS2_EENS3_ImEEE5eraseERKS2_
+_objc_msgSend_vtable9
__class_shouldGrowCache
-_class_createInstance
-__internal_class_createInstance
-_objc_msgSendSuper
-_objc_assign_ivar
__cache_collect_free
+__cache_collect
_class_getSuperclass
-__category_getClass
-__class_isLoadable
-_object_dispose
-__internal_object_dispose
-_object_cxxDestructFromClass
-__objc_getFreedObjectClass
-_objc_exception_try_enter
-_objc_exception_try_exit
-_class_getVersion
+_objc_msgSend_vtable2
+_objc_msgSend_vtable13
+_objc_msgSend_vtable14
+_objc_memmove_collectable
_class_respondsToSelector
__class_resolveMethod
+__class_isMetaClass
__cache_addForwardEntry
-_objc_msgSend_fpret
+__objc_rootDealloc
+_object_dispose
+_objc_msgSend_fixedup
+_class_getName
+_objc_atomicCompareAndSwapPtrBarrier
+_objc_msgSend_vtable7
+__objc_rootAutorelease
+__Z22_objc_rootAutorelease2P11objc_object
+_objc_msgSend_vtable12
+_objc_msgSend_vtable11
+_objc_msgSend_vtable8
+_objc_msgSend_vtable15
+__objc_autoreleasePoolPop
+__ZN12_GLOBAL__N_119AutoreleasePoolPage3popEPv
+_objc_msgSend_vtable4
+_objc_msgSend_vtable10
+_objc_retain
+_objc_atomicCompareAndSwapInstanceVariableBarrier
+_objc_msgSendSuper2_fixup
+_objc_msgSendSuper2_fixedup
+__collecting_in_critical
+__cache_free_block
+_class_getVersion
+_objc_finalizeOnMainThread
+_class_getImageName
+__objc_rootZone
+__Z35_protocol_conformsToProtocol_nolockP10protocol_tS0_
+_objc_msgSend_vtable5
_objc_sync_enter
_id2data
_fetch_cache
-_class_getImageName
-__objc_getOrigClass
-_class_getName
_objc_sync_exit
-_objc_finalizeOnMainThread
-__cache_free_block
-_class_conformsToProtocol
-_protocol_conformsToProtocol
-+[Object initialize]
-_object_setInstanceVariable
-__class_getVariable
-_ivar_getOffset
_gc_enforcer
-_flush_marked_caches
+_cache_region_calloc
_class_getMethodImplementation
-_objc_setProperty
-_objc_assign_weak
-_objc_read_weak
+_objc_msgSend_stret
+__ZN4objc8DenseMapIP11objc_objectmLb1ENS_12DenseMapInfoIS2_EENS3_ImEEE4growEj
+__objc_rootHash
+_objc_assign_weak_non_gc
+_objc_read_weak_non_gc
+_sel_getName
+_method_getArgumentType
+_encoding_getArgumentType
+_encoding_getArgumentInfo
+_SkipFirstType
+_class_isMetaClass
_objc_allocateClassPair
-__objc_defaultClassHandler
-_objc_registerClassPair
-_NXHashInsertIfAbsent
+__calloc_class
+_class_getInstanceVariable
+__class_getVariable
+__Z7getIvarP7class_tPKc
+_object_setClass
+__class_instancesHaveAssociatedObjects
_method_getNumberOfArguments
_encoding_getNumberOfArguments
-_SkipFirstType
-_method_copyArgumentType
-_encoding_copyArgumentType
-_encoding_getArgumentInfo
_method_copyReturnType
_encoding_copyReturnType
-_objc_getProperty
-_cache_region_calloc
+_method_copyArgumentType
+_encoding_copyArgumentType
+__objc_rootRetainCount
+_objc_getAssociatedObject_non_gc
+__object_get_associative_reference
+__ZN19AssociationsManagerC2Ev
+__ZN19AssociationsManager12associationsEv
+__ZNK23objc_references_support15ObjcPointerHashclEPv
+_objc_release
+_objc_removeAssociatedObjects
+_objc_setProperty_non_gc
+_objc_getProperty_non_gc
+_objc_autoreleaseReturnValue
+_objc_setAssociatedObject_non_gc
+__object_set_associative_reference
+__ZN9__gnu_cxx8hash_mapIPvPN23objc_references_support20ObjectAssociationMapENS2_15ObjcPointerHashENS2_16ObjcPointerEqualENS2_13
+__ZNSt3__13mapIPvN23objc_references_support15ObjcAssociationENS2_17ObjectPointerLessENS2_13ObjcAllocatorINS_4pairIKS1_S3_EEEEEi
+__ZNSt3__13mapIPvN23objc_references_support15ObjcAssociationENS2_17ObjectPointerLessENS2_13ObjcAllocatorINS_4pairIKS1_S3_EEEEE1
+__ZNSt3__127__tree_balance_after_insertIPNS_16__tree_node_baseIPvEEEEvT_S5_
+__class_setInstancesHaveAssociatedObjects
+_ivar_getTypeEncoding
+_object_getIvar
+_ivar_getOffset
+__class_usesAutomaticRetainRelease
+__objc_msgForward_internal
+__objc_msgForward
_class_copyProtocolList
-_class_isMetaClass
_protocol_getMethodDescription
-_lookup_protocol_method
+__protocol_getMethod
+__Z26_protocol_getMethod_nolockP10protocol_tP13objc_selectoraa
_method_getDescription
-_sel_getName
+_ivar_getName
+_objc_addExceptionHandler
+_read_address
+_read_sleb
+_fetch_handler_list
+_objc_removeExceptionHandler
+_SubtypeUntil
+_objc_collecting_enabled
+_objc_msgSend_vtable6
+_objc_is_finalized
+_class_copyPropertyList
+_property_getName
+_property_getAttributes
+_objc_msgSendSuper2_stret
+_object_setInstanceVariable
+_object_setIvar
+_objc_assign_ivar
+__ZN12_GLOBAL__N_119AutoreleasePoolPage15autoreleaseSlowEP11objc_object
+_objc_atomicCompareAndSwapPtr
+_objc_atomicCompareAndSwapGlobalBarrier
+_sel_getUid
+__ZN12_GLOBAL__N_119AutoreleasePoolPage11tls_deallocEPv
+__ZN12_GLOBAL__N_119AutoreleasePoolPage4killEv
+__objc_constructOrFree
+_object_cxxConstruct
+_object_cxxConstructFromClass
+__class_hasCxxStructors
+_lookupMethodInClassAndLoadCache
+__class_getMethodNoSuper
+_object_cxxDestruct
+_object_cxxDestructFromClass
+_class_copyIvarList
+__object_addExternalReference_rr
+__objc_rootRetain_slow
+__objc_rootReleaseWasZero_slow
+_object_copy
+__Z20_object_copyFromZoneP11objc_objectmPv
__objc_pthread_destroyspecific
__destroyInitializingClassList
-__destroyLockList
__destroySyncCache
__destroyAltHandlerList
-_objc_msgSend_stret
-_objc_is_finalized
-_class_getInstanceVariable
-_objc_msgSendSuper_stret
-__objc_msgForward
-_objc_memmove_collectable
-_objc_atomicCompareAndSwapInstanceVariableBarrier
-_object_setClass
-_flush_cache
-_objc_atomicCompareAndSwapGlobalBarrier
-_ivar_getTypeEncoding
-_class_getProperty
-_property_list_nth
-_property_getAttributes
-_object_copyFromZone
-__internal_object_copyFromZone
-_class_copyIvarList
-_ivar_getName
-__objcInit
-_objc_copyStruct
-_objc_sync_nil
-_objc_getClassList
-_objc_copyImageNames
-_objc_copyClassNamesForImage
-__objc_copyClassNamesForImage
-_objc_setMultithreaded
-_sel_getUid
-_class_poseAs
-__objc_addOrigClass
-_objc_exception_throw
-_objc_exception_extract
-_objc_exception_match
-_object_getIvar
-_object_setIvar
-_method_invoke
-_unmap_image
-_class_copyPropertyList
-_property_getName
+__object_remove_assocations
+__ZNSt3__114__split_bufferIN23objc_references_support15ObjcAssociationERNS1_13ObjcAllocatorIS2_EEE9push_backERKS2_
+__ZNSt3__16vectorIN23objc_references_support15ObjcAssociationENS1_13ObjcAllocatorIS2_EEE26__swap_out_circular_bufferERNS_14__sp
+__ZNSt3__112__hash_tableINS_4pairIPvPN23objc_references_support20ObjectAssociationMapEEEN9__gnu_cxx17__hash_map_hasherIS6_NS3_1
+__ZNSt3__114__split_bufferIN23objc_references_support15ObjcAssociationERNS1_13ObjcAllocatorIS2_EEE10push_frontERKS2_
+__ZNSt3__16__treeINS_4pairIPvN23objc_references_support15ObjcAssociationEEENS_19__map_value_compareIS2_S4_NS3_17ObjectPointerLe
+__ZNSt3__113__tree_removeIPNS_16__tree_node_baseIPvEEEEvT_S5_
/*
- * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2007-2009 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#import <mach-o/arch.h>
#import <mach-o/loader.h>
+// from "objc-private.h"
+// masks for objc_image_info.flags
+#define OBJC_IMAGE_IS_REPLACEMENT (1<<0)
+#define OBJC_IMAGE_SUPPORTS_GC (1<<1)
+#define OBJC_IMAGE_REQUIRES_GC (1<<2)
+#define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3)
+#define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4)
+
typedef char bool;
#define true 1
#define false 0
bool quiet;
bool rrOnly;
bool patch = true;
+bool unpatch = false;
struct gcinfo {
bool hasObjC;
int i;
//dumpinfo("/System/Library/Frameworks/AppKit.framework/AppKit");
if (argc == 1) {
- printf("Usage: gcinfo [-v] [-r] [--] library_or_executable_image [image2 ...]\n");
- printf(" prints Garbage Collection readiness of named images, ignoring those without ObjC segments\n");
- printf(" 'GC' - compiled with write-barriers, presumably otherwise aware\n");
- printf(" 'RR' - retain/release (presumed) aware for non-GC\n");
- printf(" 'GC-only' - compiled with write-barriers and marked at compile time as not being retain/release savvy\n");
- printf(" -v - provide archtectural details\n");
- printf(" -r - only show libraries that are non-GC, e.g. RR only\n");
- printf(" -- - read files & directories from stdin, e.g. find /Plug-ins | gcinfo --\n");
+ printf("Usage: markgc [-v] [-r] [--] library_or_executable_image [image2 ...]\n");
+ printf(" changes Garbage Collection readiness of named images, ignoring those without ObjC segments\n");
+ printf(" -p - patch RR binary to (apparently) support GC (default)\n");
+ printf(" -u - unpatch GC binary to RR only\n");
printf("\nAuthor: blaine@apple.com\n");
exit(0);
}
quiet = true;
continue;
}
- if (!strcmp(argv[i], "-r")) {
- quiet = true;
- rrOnly = true;
- continue;
- }
if (!strcmp(argv[i], "-p")) {
patch = true;
continue;
}
- if (!strcmp(argv[i], "--")) {
- char buf[1024];
- while (fgets(buf, 1024, stdin)) {
- int len = strlen(buf);
- buf[len-1] = 0;
- dumpinfo(buf);
- }
+ if (!strcmp(argv[i], "-u")) {
+ unpatch = true;
+ patch = false;
continue;
}
dumpinfo(argv[i]);
int fd = open(FileName, 1);
off_t lresult = lseek(fd, offset, SEEK_SET);
if (lresult == -1) {
- printf("couldn't seek to %x position on fd %d\n", offset, fd);
+ printf("couldn't seek to 0x%lx position on fd %d\n", offset, fd);
++Errors;
return;
}
- int wresult = write(fd, &value, 4);
+ size_t wresult = write(fd, &value, 4);
if (wresult != 4) {
++Errors;
printf("didn't write new value\n");
}
else {
- printf("patched %s at offset %p\n", FileName, offset);
+ printf("patched %s at offset 0x%lx\n", FileName, offset);
}
close(fd);
}
-uint32_t iiflags(struct imageInfo *ii, uint32_t size, bool needsFlip) {
+uint32_t iiflags(struct imageInfo *ii, size_t size, bool needsFlip) {
if (needsFlip) {
ii->flags = OSSwapInt32(ii->flags);
}
- if (debug) printf("flags->%x, nitems %d\n", ii->flags, size/sizeof(struct imageInfo));
+ if (debug) printf("flags->%x, nitems %lu\n", ii->flags, size/sizeof(struct imageInfo));
+ uint32_t support_mask = (OBJC_IMAGE_SUPPORTS_GC | OBJC_IMAGE_SUPPORTS_COMPACTION);
uint32_t flags = ii->flags;
- if (patch && (flags&0x2)==0) {
+ if (patch && (flags & support_mask) != support_mask) {
//printf("will patch %s at offset %p\n", FileName, (char*)(&ii->flags) - FileBase);
- uint32_t newvalue = flags | 0x2;
+ uint32_t newvalue = flags | support_mask;
+ if (needsFlip) newvalue = OSSwapInt32(newvalue);
+ patchFile(newvalue, (char*)(&ii->flags) - FileBase);
+ }
+ if (unpatch && (flags & support_mask) == support_mask) {
+ uint32_t newvalue = flags & ~support_mask;
if (needsFlip) newvalue = OSSwapInt32(newvalue);
patchFile(newvalue, (char*)(&ii->flags) - FileBase);
}
sect->size = OSSwapInt64(sect->size);
}
// these guys aren't inline - they point elsewhere
- gcip->flags = iiflags(start + sect->offset, sect->size, needsFlip);
+ gcip->flags = iiflags(start + sect->offset, (size_t)sect->size, needsFlip);
}
void doseg32(void *start, struct segment_command *seg, bool needsFlip, struct gcinfo *gcip) {
if (seg->segname[0]) {
if (strcmp("__OBJC", seg->segname)) return;
}
- int nsects;
struct section *sect = (struct section *)(seg + 1);
- for (int nsects = 0; nsects < seg->nsects; ++nsects) {
+ for (uint32_t nsects = 0; nsects < seg->nsects; ++nsects) {
// sections directly follow
dosect32(start, sect + nsects, needsFlip, gcip);
seg->fileoff = OSSwapInt64(seg->fileoff);
seg->nsects = OSSwapInt32(seg->nsects);
}
- int nsects;
struct section_64 *sect = (struct section_64 *)(seg + 1);
- for (int nsects = 0; nsects < seg->nsects; ++nsects) {
+ for (uint32_t nsects = 0; nsects < seg->nsects; ++nsects) {
// sections directly follow
dosect64(start, sect + nsects, needsFlip, gcip);
if (!verbose) return;
if (needsFlip) {
}
- int count = dylibCmd->cmdsize - sizeof(struct dylib_command);
+ size_t count = dylibCmd->cmdsize - sizeof(struct dylib_command);
//printf("offset is %d, count is %d\n", dylibCmd->dylib.name.offset, count);
if (dylibCmd->dylib.name.offset > count) return;
//printf("-->%.*s<---", count, ((void *)dylibCmd)+dylibCmd->dylib.name.offset);
- if (verbose) printf("load %s\n", ((void *)dylibCmd)+dylibCmd->dylib.name.offset);
+ if (verbose) printf("load %s\n", ((char *)dylibCmd)+dylibCmd->dylib.name.offset);
}
struct load_command *doloadcommand(void *start, struct load_command *lc, bool needsFlip, bool is32, struct gcinfo *gcip) {
return (struct load_command *)((void *)lc + lc->cmdsize);
}
-void doofile(void *start, uint32_t size, struct gcinfo *gcip) {
+void doofile(void *start, size_t size, struct gcinfo *gcip) {
struct mach_header *mh = (struct mach_header *)start;
bool isFlipped = false;
if (mh->magic == MH_CIGAM || mh->magic == MH_CIGAM_64) {
close(fd);
return false;
}
- FileSize = statb.st_size;
+ FileSize = (size_t)statb.st_size;
FileBase = malloc(FileSize);
if (!FileBase) {
- printf("couldn't malloc %d bytes\n", FileSize);
+ printf("couldn't malloc %lu bytes\n", FileSize);
close(fd);
return false;
}
off_t readsize = read(fd, FileBase, FileSize);
if (readsize != FileSize) {
- printf("read %d bytes, wanted %d\n", readsize, FileSize);
+ printf("read %ld bytes, wanted %ld\n", (size_t)readsize, FileSize);
close(fd);
return false;
}
>\r
<Tool\r
Name="VCPreBuildEventTool"\r
- Description="Install"\r
- CommandLine="xcopy /Y "$(ProjectDir)runtime\objc.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\objc-api.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\objc-auto.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\objc-exception.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\message.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\runtime.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\hashtable.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\hashtable2.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\maptable.h" "%DSTROOT%\AppleInternal\include\objc\"
"\r
+ Description="prebuild.bat..."\r
+ CommandLine="prebuild"\r
/>\r
<Tool\r
Name="VCCustomBuildTool"\r
OutputFile="$(OutDir)\$(ProjectName)_debug.dll"\r
LinkIncremental="2"\r
GenerateDebugInformation="true"\r
+ StripPrivateSymbols="$(TargetDir)$(TargetName).pdbstripped"\r
SubSystem="2"\r
OptimizeReferences="1"\r
EnableCOMDATFolding="0"\r
<Tool\r
Name="VCPostBuildEventTool"\r
Description="Install"\r
- CommandLine="xcopy /Y "$(TargetDir)$(TargetName).dll" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).pdb" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).exp" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).lib" "%DSTROOT%\AppleInternal\lib\"
"\r
+ CommandLine="xcopy /Y "$(TargetDir)$(TargetName).dll" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).exp" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).lib" "%DSTROOT%\AppleInternal\lib\"
xcopy /Y "$(TargetDir)$(TargetName).pdb" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).pdbstripped" "$(TargetDir)$(TargetName).pdb"
xcopy /Y "$(TargetDir)$(TargetName).pdb" "%DSTROOT%\AppleInternal\public\sym\"
"\r
/>\r
</Configuration>\r
<Configuration\r
>\r
<Tool\r
Name="VCPreBuildEventTool"\r
- Description="Install Headers"\r
- CommandLine="xcopy /Y "$(ProjectDir)runtime\objc.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\objc-api.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\objc-auto.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\objc-exception.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\message.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\runtime.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\hashtable.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\hashtable2.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\maptable.h" "%DSTROOT%\AppleInternal\include\objc\"
"\r
+ Description="prebuild.bat..."\r
+ CommandLine="prebuild"\r
/>\r
<Tool\r
Name="VCCustomBuildTool"\r
AdditionalOptions="/dynamicbase"\r
LinkIncremental="1"\r
GenerateDebugInformation="true"\r
+ StripPrivateSymbols="$(TargetDir)$(TargetName).pdbstripped"\r
SubSystem="2"\r
OptimizeReferences="1"\r
EnableCOMDATFolding="2"\r
<Tool\r
Name="VCPostBuildEventTool"\r
Description="Install"\r
- CommandLine="xcopy /Y "$(TargetDir)$(TargetName).dll" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).pdb" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).exp" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).lib" "%DSTROOT%\AppleInternal\lib\"
"\r
+ CommandLine="xcopy /Y "$(TargetDir)$(TargetName).dll" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).exp" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).lib" "%DSTROOT%\AppleInternal\lib\"
xcopy /Y "$(TargetDir)$(TargetName).pdb" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).pdbstripped" "$(TargetDir)$(TargetName).pdb"
xcopy /Y "$(TargetDir)$(TargetName).pdb" "%DSTROOT%\AppleInternal\public\sym\"
"\r
/>\r
</Configuration>\r
<Configuration\r
>\r
<Tool\r
Name="VCPreBuildEventTool"\r
- Description="Install"\r
- CommandLine="xcopy /Y "$(ProjectDir)runtime\objc.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\objc-api.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\objc-auto.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\objc-exception.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\message.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\runtime.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\hashtable.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\hashtable2.h" "%DSTROOT%\AppleInternal\include\objc\"
xcopy /Y "$(ProjectDir)runtime\maptable.h" "%DSTROOT%\AppleInternal\include\objc\"
"\r
+ Description="prebuild.bat..."\r
+ CommandLine="prebuild"\r
/>\r
<Tool\r
Name="VCCustomBuildTool"\r
OutputFile="$(OutDir)\$(ProjectName)_debug.dll"\r
LinkIncremental="2"\r
GenerateDebugInformation="true"\r
+ StripPrivateSymbols="$(TargetDir)$(TargetName).pdbstripped"\r
SubSystem="2"\r
OptimizeReferences="1"\r
EnableCOMDATFolding="0"\r
<Tool\r
Name="VCPostBuildEventTool"\r
Description="Install"\r
- CommandLine="xcopy /Y "$(TargetDir)$(TargetName).dll" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).pdb" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).exp" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).lib" "%DSTROOT%\AppleInternal\lib\"
"\r
+ CommandLine="xcopy /Y "$(TargetDir)$(TargetName).dll" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).exp" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).lib" "%DSTROOT%\AppleInternal\lib\"
xcopy /Y "$(TargetDir)$(TargetName).pdb" "%DSTROOT%\AppleInternal\bin\"
xcopy /Y "$(TargetDir)$(TargetName).pdbstripped" "$(TargetDir)$(TargetName).pdb"
xcopy /Y "$(TargetDir)$(TargetName).pdb" "%DSTROOT%\AppleInternal\public\sym\"
"\r
/>\r
</Configuration>\r
</Configurations>\r
</FileConfiguration>\r
</File>\r
<File\r
- RelativePath=".\runtime\objc-file.m"\r
+ RelativePath=".\runtime\objc-file-old.m"\r
+ >\r
+ <FileConfiguration\r
+ Name="Debug|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Release|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="DebugDLL|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ />\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath=".\runtime\objc-file.mm"\r
>\r
<FileConfiguration\r
Name="Debug|Win32"\r
>\r
<Tool\r
Name="VCCLCompilerTool"\r
- CompileAs="1"\r
/>\r
</FileConfiguration>\r
<FileConfiguration\r
>\r
<Tool\r
Name="VCCLCompilerTool"\r
- CompileAs="1"\r
/>\r
</FileConfiguration>\r
<FileConfiguration\r
>\r
<Tool\r
Name="VCCLCompilerTool"\r
- CompileAs="1"\r
/>\r
</FileConfiguration>\r
</File>\r
</FileConfiguration>\r
</File>\r
<File\r
- RelativePath=".\runtime\objc-runtime-new.m"\r
+ RelativePath=".\runtime\objc-runtime-new.mm"\r
>\r
<FileConfiguration\r
Name="Debug|Win32"\r
>\r
<Tool\r
Name="VCCLCompilerTool"\r
- CompileAs="1"\r
/>\r
</FileConfiguration>\r
<FileConfiguration\r
>\r
<Tool\r
Name="VCCLCompilerTool"\r
- CompileAs="1"\r
/>\r
</FileConfiguration>\r
<FileConfiguration\r
>\r
<Tool\r
Name="VCCLCompilerTool"\r
- CompileAs="1"\r
/>\r
</FileConfiguration>\r
</File>\r
RelativePath=".\runtime\objc-exception.h"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\runtime\objc-file-old.h"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\runtime\objc-file.h"\r
+ >\r
+ </File>\r
<File\r
RelativePath=".\runtime\objc-initialize.h"\r
>\r
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
>\r
+ <File\r
+ RelativePath=".\version.rc"\r
+ >\r
+ <FileConfiguration\r
+ Name="Debug|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ AdditionalIncludeDirectories="$(OBJROOT)"\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Release|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ AdditionalIncludeDirectories="$(OBJROOT)"\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="DebugDLL|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ AdditionalIncludeDirectories="$(OBJROOT)"\r
+ />\r
+ </FileConfiguration>\r
+ </File>\r
</Filter>\r
</Files>\r
<Globals>\r
objectVersion = 45;
objects = {
+/* Begin PBXAggregateTarget section */
+ 83E2799A117F76DF00452546 /* objc-selopt */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 83E2799E117F770D00452546 /* Build configuration list for PBXAggregateTarget "objc-selopt" */;
+ buildPhases = (
+ 83E27999117F76DF00452546 /* CopyFiles */,
+ );
+ dependencies = (
+ );
+ name = "objc-selopt";
+ productName = "objc-selopt";
+ };
+/* End PBXAggregateTarget section */
+
/* Begin PBXBuildFile section */
- 393CEAC00DC69E3E000B69DE /* objc-references.mm in Sources */ = {isa = PBXBuildFile; fileRef = 393CEABF0DC69E3E000B69DE /* objc-references.mm */; settings = {COMPILER_FLAGS = "-fvisibility=hidden"; }; };
+ 39023F7712F09D0400E1426D /* hashtable.h in Headers */ = {isa = PBXBuildFile; fileRef = 830F2A970D738DC200392440 /* hashtable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F7812F09D0400E1426D /* hashtable2.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485B70D6D687300CEA253 /* hashtable2.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F7912F09D0400E1426D /* List.h in Headers */ = {isa = PBXBuildFile; fileRef = 838486240D6D68F000CEA253 /* List.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F7A12F09D0400E1426D /* maptable.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485BB0D6D687300CEA253 /* maptable.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 39023F7B12F09D0400E1426D /* message.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485BD0D6D687300CEA253 /* message.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F7C12F09D0400E1426D /* objc-abi.h in Headers */ = {isa = PBXBuildFile; fileRef = 834EC0A311614167009B2563 /* objc-abi.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 39023F7D12F09D0400E1426D /* objc-accessors.h in Headers */ = {isa = PBXBuildFile; fileRef = 830F2A920D73876100392440 /* objc-accessors.h */; };
+ 39023F7E12F09D0400E1426D /* objc-api.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485C80D6D68A200CEA253 /* objc-api.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F7F12F09D0400E1426D /* objc-auto-dump.h in Headers */ = {isa = PBXBuildFile; fileRef = BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 39023F8012F09D0400E1426D /* objc-auto.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485C90D6D68A200CEA253 /* objc-auto.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F8112F09D0400E1426D /* objc-class.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485CD0D6D68A200CEA253 /* objc-class.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F8212F09D0400E1426D /* objc-config.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485CF0D6D68A200CEA253 /* objc-config.h */; };
+ 39023F8312F09D0400E1426D /* objc-exception.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D10D6D68A200CEA253 /* objc-exception.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F8412F09D0400E1426D /* objc-file-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E50FCCB24D00661494 /* objc-file-old.h */; };
+ 39023F8512F09D0400E1426D /* objc-file.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E60FCCB24D00661494 /* objc-file.h */; };
+ 39023F8612F09D0400E1426D /* objc-gdb.h in Headers */ = {isa = PBXBuildFile; fileRef = 834266D70E665A8B002E4DA2 /* objc-gdb.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 39023F8712F09D0400E1426D /* objc-initialize.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D40D6D68A200CEA253 /* objc-initialize.h */; };
+ 39023F8812F09D0400E1426D /* objc-internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 83112ED30F00599600A5FBAF /* objc-internal.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 39023F8912F09D0400E1426D /* objc-load.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D70D6D68A200CEA253 /* objc-load.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F8A12F09D0400E1426D /* objc-loadmethod.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D90D6D68A200CEA253 /* objc-loadmethod.h */; };
+ 39023F8B12F09D0400E1426D /* objc-os.h in Headers */ = {isa = PBXBuildFile; fileRef = 831C85D30E10CF850066E64C /* objc-os.h */; };
+ 39023F8C12F09D0400E1426D /* objc-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485DC0D6D68A200CEA253 /* objc-private.h */; };
+ 39023F8D12F09D0400E1426D /* objc-references.h in Headers */ = {isa = PBXBuildFile; fileRef = 393CEAC50DC69E67000B69DE /* objc-references.h */; };
+ 39023F8E12F09D0400E1426D /* objc-runtime-new.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E00D6D68A200CEA253 /* objc-runtime-new.h */; };
+ 39023F8F12F09D0400E1426D /* objc-runtime-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E70FCCB24D00661494 /* objc-runtime-old.h */; };
+ 39023F9012F09D0400E1426D /* objc-runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E30D6D68A200CEA253 /* objc-runtime.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F9112F09D0400E1426D /* objc-sel-set.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E50D6D68A200CEA253 /* objc-sel-set.h */; };
+ 39023F9212F09D0400E1426D /* objc-sync.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E90D6D68A200CEA253 /* objc-sync.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F9312F09D0400E1426D /* objc.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485EC0D6D68A200CEA253 /* objc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F9412F09D0400E1426D /* Object.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485ED0D6D68A200CEA253 /* Object.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F9512F09D0400E1426D /* Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 838486180D6D68A800CEA253 /* Protocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F9612F09D0400E1426D /* runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384861A0D6D68A800CEA253 /* runtime.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 39023F9912F09D0400E1426D /* hashtable2.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485B80D6D687300CEA253 /* hashtable2.m */; };
+ 39023F9A12F09D0400E1426D /* maptable.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485BC0D6D687300CEA253 /* maptable.m */; };
+ 39023F9B12F09D0400E1426D /* objc-auto.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CA0D6D68A200CEA253 /* objc-auto.m */; };
+ 39023F9C12F09D0400E1426D /* objc-cache.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CB0D6D68A200CEA253 /* objc-cache.m */; };
+ 39023F9D12F09D0400E1426D /* objc-class-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CC0D6D68A200CEA253 /* objc-class-old.m */; };
+ 39023F9E12F09D0400E1426D /* objc-class.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CE0D6D68A200CEA253 /* objc-class.m */; };
+ 39023F9F12F09D0400E1426D /* objc-errors.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D00D6D68A200CEA253 /* objc-errors.m */; };
+ 39023FA012F09D0400E1426D /* objc-exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D20D6D68A200CEA253 /* objc-exception.m */; };
+ 39023FA112F09D0400E1426D /* objc-file.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D30D6D68A200CEA253 /* objc-file.mm */; };
+ 39023FA212F09D0400E1426D /* objc-initialize.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D50D6D68A200CEA253 /* objc-initialize.m */; };
+ 39023FA312F09D0400E1426D /* objc-layout.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D60D6D68A200CEA253 /* objc-layout.m */; };
+ 39023FA412F09D0400E1426D /* objc-load.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D80D6D68A200CEA253 /* objc-load.m */; };
+ 39023FA512F09D0400E1426D /* objc-loadmethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DA0D6D68A200CEA253 /* objc-loadmethod.m */; };
+ 39023FA612F09D0400E1426D /* objc-lockdebug.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DB0D6D68A200CEA253 /* objc-lockdebug.m */; };
+ 39023FA712F09D0400E1426D /* objc-rtp.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DF0D6D68A200CEA253 /* objc-rtp.m */; };
+ 39023FA812F09D0400E1426D /* objc-runtime-new.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E10D6D68A200CEA253 /* objc-runtime-new.mm */; };
+ 39023FA912F09D0400E1426D /* objc-runtime-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E20D6D68A200CEA253 /* objc-runtime-old.m */; };
+ 39023FAA12F09D0400E1426D /* objc-runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E40D6D68A200CEA253 /* objc-runtime.m */; };
+ 39023FAB12F09D0400E1426D /* objc-sel-set.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E60D6D68A200CEA253 /* objc-sel-set.m */; };
+ 39023FAC12F09D0400E1426D /* objc-sel.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E80D6D68A200CEA253 /* objc-sel.mm */; };
+ 39023FAD12F09D0400E1426D /* objc-sync.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EA0D6D68A200CEA253 /* objc-sync.m */; };
+ 39023FAE12F09D0400E1426D /* objc-typeencoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EB0D6D68A200CEA253 /* objc-typeencoding.m */; };
+ 39023FAF12F09D0400E1426D /* Object.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EE0D6D68A200CEA253 /* Object.m */; };
+ 39023FB012F09D0400E1426D /* Protocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 838486190D6D68A800CEA253 /* Protocol.m */; };
+ 39023FB112F09D0400E1426D /* List.m in Sources */ = {isa = PBXBuildFile; fileRef = 838486230D6D68F000CEA253 /* List.m */; };
+ 39023FB212F09D0400E1426D /* objc-msg-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A690D737FB800392440 /* objc-msg-arm.s */; };
+ 39023FB312F09D0400E1426D /* objc-msg-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A6A0D737FB800392440 /* objc-msg-i386.s */; };
+ 39023FB412F09D0400E1426D /* objc-msg-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A720D737FB800392440 /* objc-msg-x86_64.s */; };
+ 39023FB512F09D0400E1426D /* objc-auto-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A850D73819A00392440 /* objc-auto-i386.s */; };
+ 39023FB612F09D0400E1426D /* objc-accessors.m in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A930D73876100392440 /* objc-accessors.m */; };
+ 39023FB712F09D0400E1426D /* objc-references.mm in Sources */ = {isa = PBXBuildFile; fileRef = 393CEABF0DC69E3E000B69DE /* objc-references.mm */; };
+ 39023FB812F09D0400E1426D /* objc-os.m in Sources */ = {isa = PBXBuildFile; fileRef = 831C85D40E10CF850066E64C /* objc-os.m */; };
+ 39023FB912F09D0400E1426D /* objc-probes.d in Sources */ = {isa = PBXBuildFile; fileRef = 87BB4E900EC39633005D08E1 /* objc-probes.d */; };
+ 39023FBA12F09D0400E1426D /* objc-auto-dump.m in Sources */ = {isa = PBXBuildFile; fileRef = BC07A0100EF72D9C0014EC61 /* objc-auto-dump.m */; };
+ 39023FBB12F09D0400E1426D /* objc-file-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BE02E30FCCB23400661494 /* objc-file-old.m */; };
+ 39023FBC12F09D0400E1426D /* a1a2-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9C116AB2820071B552 /* a1a2-blocktramps-i386.s */; };
+ 39023FBD12F09D0400E1426D /* a1a2-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */; };
+ 39023FBE12F09D0400E1426D /* a2a3-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */; };
+ 39023FBF12F09D0400E1426D /* a2a3-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9F116AB2820071B552 /* a2a3-blocktramps-x86_64.s */; };
+ 39023FC012F09D0400E1426D /* objc-block-trampolines.m in Sources */ = {isa = PBXBuildFile; fileRef = E8923DA0116AB2820071B552 /* objc-block-trampolines.m */; };
+ 39023FC112F09D0400E1426D /* objc-msg-simulator-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 83B1A8BC0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s */; };
+ 39023FC212F09D0400E1426D /* objc-sel-table.s in Sources */ = {isa = PBXBuildFile; fileRef = 83EB007A121C9EC200B92C16 /* objc-sel-table.s */; };
+ 39023FC312F09D0400E1426D /* a1a2-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A1122600E9009290B8 /* a1a2-blocktramps-arm.s */; };
+ 39023FC412F09D0400E1426D /* a2a3-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A2122600E9009290B8 /* a2a3-blocktramps-arm.s */; };
+ 39023FC512F09D0400E1426D /* objc-externalref.m in Sources */ = {isa = PBXBuildFile; fileRef = 399BC72D1224831B007FBDF0 /* objc-externalref.m */; };
+ 393CEAC00DC69E3E000B69DE /* objc-references.mm in Sources */ = {isa = PBXBuildFile; fileRef = 393CEABF0DC69E3E000B69DE /* objc-references.mm */; };
393CEAC60DC69E67000B69DE /* objc-references.h in Headers */ = {isa = PBXBuildFile; fileRef = 393CEAC50DC69E67000B69DE /* objc-references.h */; };
+ 399BC72E1224831B007FBDF0 /* objc-externalref.m in Sources */ = {isa = PBXBuildFile; fileRef = 399BC72D1224831B007FBDF0 /* objc-externalref.m */; };
+ 39ABD72112F0B61800D1054C /* objc-weak.h in Headers */ = {isa = PBXBuildFile; fileRef = 39ABD71F12F0B61800D1054C /* objc-weak.h */; };
+ 39ABD72212F0B61800D1054C /* objc-weak.mm in Sources */ = {isa = PBXBuildFile; fileRef = 39ABD72012F0B61800D1054C /* objc-weak.mm */; };
+ 39ABD72312F0B61800D1054C /* objc-weak.h in Headers */ = {isa = PBXBuildFile; fileRef = 39ABD71F12F0B61800D1054C /* objc-weak.h */; };
+ 39ABD72412F0B61800D1054C /* objc-weak.mm in Sources */ = {isa = PBXBuildFile; fileRef = 39ABD72012F0B61800D1054C /* objc-weak.mm */; };
+ 39ABD72512F0B61800D1054C /* objc-weak.h in Headers */ = {isa = PBXBuildFile; fileRef = 39ABD71F12F0B61800D1054C /* objc-weak.h */; };
+ 39ABD72612F0B61800D1054C /* objc-weak.mm in Sources */ = {isa = PBXBuildFile; fileRef = 39ABD72012F0B61800D1054C /* objc-weak.mm */; };
+ 552B2FB712F9D31E00650C0D /* objc-arr.mm in Sources */ = {isa = PBXBuildFile; fileRef = 552B2FB612F9D31E00650C0D /* objc-arr.mm */; };
+ 552B2FB812F9D31E00650C0D /* objc-arr.mm in Sources */ = {isa = PBXBuildFile; fileRef = 552B2FB612F9D31E00650C0D /* objc-arr.mm */; };
+ 552B2FB912F9D31E00650C0D /* objc-arr.mm in Sources */ = {isa = PBXBuildFile; fileRef = 552B2FB612F9D31E00650C0D /* objc-arr.mm */; };
830F2A740D737FB800392440 /* objc-msg-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A690D737FB800392440 /* objc-msg-arm.s */; };
830F2A750D737FB900392440 /* objc-msg-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A6A0D737FB800392440 /* objc-msg-i386.s */; };
- 830F2A760D737FB900392440 /* objc-msg-ppc.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A6B0D737FB800392440 /* objc-msg-ppc.s */; };
830F2A7D0D737FBB00392440 /* objc-msg-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A720D737FB800392440 /* objc-msg-x86_64.s */; };
830F2A890D73819A00392440 /* objc-auto-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A850D73819A00392440 /* objc-auto-i386.s */; };
- 830F2A8A0D73819A00392440 /* objc-auto-ppc.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A860D73819A00392440 /* objc-auto-ppc.s */; };
- 830F2A8B0D73819A00392440 /* objc-auto-ppc64.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A870D73819A00392440 /* objc-auto-ppc64.s */; };
830F2A940D73876100392440 /* objc-accessors.h in Headers */ = {isa = PBXBuildFile; fileRef = 830F2A920D73876100392440 /* objc-accessors.h */; };
830F2A950D73876100392440 /* objc-accessors.m in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A930D73876100392440 /* objc-accessors.m */; };
830F2A980D738DC200392440 /* hashtable.h in Headers */ = {isa = PBXBuildFile; fileRef = 830F2A970D738DC200392440 /* hashtable.h */; settings = {ATTRIBUTES = (Public, ); }; };
831C85D50E10CF850066E64C /* objc-os.h in Headers */ = {isa = PBXBuildFile; fileRef = 831C85D30E10CF850066E64C /* objc-os.h */; };
831C85D60E10CF850066E64C /* objc-os.m in Sources */ = {isa = PBXBuildFile; fileRef = 831C85D40E10CF850066E64C /* objc-os.m */; };
834266D80E665A8B002E4DA2 /* objc-gdb.h in Headers */ = {isa = PBXBuildFile; fileRef = 834266D70E665A8B002E4DA2 /* objc-gdb.h */; settings = {ATTRIBUTES = (Private, ); }; };
- 838485BE0D6D687300CEA253 /* error.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485B60D6D687300CEA253 /* error.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 834EC0A411614167009B2563 /* objc-abi.h in Headers */ = {isa = PBXBuildFile; fileRef = 834EC0A311614167009B2563 /* objc-abi.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 8383A3A3122600E9009290B8 /* a1a2-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A1122600E9009290B8 /* a1a2-blocktramps-arm.s */; };
+ 8383A3A4122600E9009290B8 /* a2a3-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A2122600E9009290B8 /* a2a3-blocktramps-arm.s */; };
+ 8383A3AC122600FB009290B8 /* a1a2-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A1122600E9009290B8 /* a1a2-blocktramps-arm.s */; };
+ 8383A3AD122600FB009290B8 /* a2a3-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A2122600E9009290B8 /* a2a3-blocktramps-arm.s */; };
+ 8383A3AE122600FB009290B8 /* hashtable2.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485B80D6D687300CEA253 /* hashtable2.m */; };
+ 8383A3AF122600FB009290B8 /* maptable.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485BC0D6D687300CEA253 /* maptable.m */; };
+ 8383A3B0122600FB009290B8 /* objc-accessors.m in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A930D73876100392440 /* objc-accessors.m */; };
+ 8383A3B1122600FB009290B8 /* objc-auto.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CA0D6D68A200CEA253 /* objc-auto.m */; };
+ 8383A3B2122600FB009290B8 /* objc-auto-dump.m in Sources */ = {isa = PBXBuildFile; fileRef = BC07A0100EF72D9C0014EC61 /* objc-auto-dump.m */; };
+ 8383A3B3122600FB009290B8 /* objc-block-trampolines.m in Sources */ = {isa = PBXBuildFile; fileRef = E8923DA0116AB2820071B552 /* objc-block-trampolines.m */; };
+ 8383A3B4122600FB009290B8 /* objc-cache.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CB0D6D68A200CEA253 /* objc-cache.m */; };
+ 8383A3B5122600FB009290B8 /* objc-class-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CC0D6D68A200CEA253 /* objc-class-old.m */; };
+ 8383A3B6122600FB009290B8 /* objc-class.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CE0D6D68A200CEA253 /* objc-class.m */; };
+ 8383A3B7122600FB009290B8 /* objc-errors.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D00D6D68A200CEA253 /* objc-errors.m */; };
+ 8383A3B8122600FB009290B8 /* objc-exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D20D6D68A200CEA253 /* objc-exception.m */; };
+ 8383A3B9122600FB009290B8 /* objc-file.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D30D6D68A200CEA253 /* objc-file.mm */; };
+ 8383A3BA122600FB009290B8 /* objc-file-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BE02E30FCCB23400661494 /* objc-file-old.m */; };
+ 8383A3BB122600FB009290B8 /* objc-initialize.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D50D6D68A200CEA253 /* objc-initialize.m */; };
+ 8383A3BC122600FB009290B8 /* objc-layout.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D60D6D68A200CEA253 /* objc-layout.m */; };
+ 8383A3BD122600FB009290B8 /* objc-load.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D80D6D68A200CEA253 /* objc-load.m */; };
+ 8383A3BE122600FB009290B8 /* objc-loadmethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DA0D6D68A200CEA253 /* objc-loadmethod.m */; };
+ 8383A3BF122600FB009290B8 /* objc-lockdebug.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DB0D6D68A200CEA253 /* objc-lockdebug.m */; };
+ 8383A3C0122600FB009290B8 /* objc-os.m in Sources */ = {isa = PBXBuildFile; fileRef = 831C85D40E10CF850066E64C /* objc-os.m */; };
+ 8383A3C1122600FB009290B8 /* objc-references.mm in Sources */ = {isa = PBXBuildFile; fileRef = 393CEABF0DC69E3E000B69DE /* objc-references.mm */; };
+ 8383A3C2122600FB009290B8 /* objc-rtp.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DF0D6D68A200CEA253 /* objc-rtp.m */; };
+ 8383A3C3122600FB009290B8 /* objc-runtime-new.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E10D6D68A200CEA253 /* objc-runtime-new.mm */; };
+ 8383A3C4122600FB009290B8 /* objc-runtime-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E20D6D68A200CEA253 /* objc-runtime-old.m */; };
+ 8383A3C5122600FB009290B8 /* objc-runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E40D6D68A200CEA253 /* objc-runtime.m */; };
+ 8383A3C6122600FB009290B8 /* objc-sel-set.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E60D6D68A200CEA253 /* objc-sel-set.m */; };
+ 8383A3C7122600FB009290B8 /* objc-sel-table.s in Sources */ = {isa = PBXBuildFile; fileRef = 83EB007A121C9EC200B92C16 /* objc-sel-table.s */; };
+ 8383A3C8122600FB009290B8 /* objc-sel.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E80D6D68A200CEA253 /* objc-sel.mm */; };
+ 8383A3C9122600FB009290B8 /* objc-sync.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EA0D6D68A200CEA253 /* objc-sync.m */; };
+ 8383A3CA122600FB009290B8 /* objc-typeencoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EB0D6D68A200CEA253 /* objc-typeencoding.m */; };
+ 8383A3CB122600FB009290B8 /* a1a2-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9C116AB2820071B552 /* a1a2-blocktramps-i386.s */; };
+ 8383A3CC122600FB009290B8 /* a1a2-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */; };
+ 8383A3CD122600FB009290B8 /* a2a3-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */; };
+ 8383A3CE122600FB009290B8 /* a2a3-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9F116AB2820071B552 /* a2a3-blocktramps-x86_64.s */; };
+ 8383A3CF122600FB009290B8 /* objc-auto-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A850D73819A00392440 /* objc-auto-i386.s */; };
+ 8383A3D0122600FB009290B8 /* objc-msg-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A690D737FB800392440 /* objc-msg-arm.s */; };
+ 8383A3D1122600FB009290B8 /* objc-msg-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A6A0D737FB800392440 /* objc-msg-i386.s */; };
+ 8383A3D2122600FB009290B8 /* objc-msg-simulator-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 83B1A8BC0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s */; };
+ 8383A3D3122600FB009290B8 /* objc-msg-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A720D737FB800392440 /* objc-msg-x86_64.s */; };
+ 8383A3D4122600FB009290B8 /* objc-probes.d in Sources */ = {isa = PBXBuildFile; fileRef = 87BB4E900EC39633005D08E1 /* objc-probes.d */; };
+ 8383A3DC1226291C009290B8 /* objc-externalref.m in Sources */ = {isa = PBXBuildFile; fileRef = 399BC72D1224831B007FBDF0 /* objc-externalref.m */; };
838485BF0D6D687300CEA253 /* hashtable2.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485B70D6D687300CEA253 /* hashtable2.h */; settings = {ATTRIBUTES = (Public, ); }; };
838485C00D6D687300CEA253 /* hashtable2.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485B80D6D687300CEA253 /* hashtable2.m */; };
838485C30D6D687300CEA253 /* maptable.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485BB0D6D687300CEA253 /* maptable.h */; settings = {ATTRIBUTES = (Private, ); }; };
838485F70D6D68A200CEA253 /* objc-errors.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D00D6D68A200CEA253 /* objc-errors.m */; };
838485F80D6D68A200CEA253 /* objc-exception.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D10D6D68A200CEA253 /* objc-exception.h */; settings = {ATTRIBUTES = (Public, ); }; };
838485F90D6D68A200CEA253 /* objc-exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D20D6D68A200CEA253 /* objc-exception.m */; };
- 838485FA0D6D68A200CEA253 /* objc-file.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D30D6D68A200CEA253 /* objc-file.m */; };
+ 838485FA0D6D68A200CEA253 /* objc-file.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D30D6D68A200CEA253 /* objc-file.mm */; };
838485FB0D6D68A200CEA253 /* objc-initialize.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D40D6D68A200CEA253 /* objc-initialize.h */; };
838485FC0D6D68A200CEA253 /* objc-initialize.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D50D6D68A200CEA253 /* objc-initialize.m */; };
838485FD0D6D68A200CEA253 /* objc-layout.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D60D6D68A200CEA253 /* objc-layout.m */; };
838486010D6D68A200CEA253 /* objc-loadmethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DA0D6D68A200CEA253 /* objc-loadmethod.m */; };
838486020D6D68A200CEA253 /* objc-lockdebug.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DB0D6D68A200CEA253 /* objc-lockdebug.m */; };
838486030D6D68A200CEA253 /* objc-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485DC0D6D68A200CEA253 /* objc-private.h */; };
- 838486050D6D68A200CEA253 /* objc-rtp.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485DE0D6D68A200CEA253 /* objc-rtp.h */; };
838486060D6D68A200CEA253 /* objc-rtp.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DF0D6D68A200CEA253 /* objc-rtp.m */; };
838486070D6D68A200CEA253 /* objc-runtime-new.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E00D6D68A200CEA253 /* objc-runtime-new.h */; };
- 838486080D6D68A200CEA253 /* objc-runtime-new.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E10D6D68A200CEA253 /* objc-runtime-new.m */; };
+ 838486080D6D68A200CEA253 /* objc-runtime-new.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E10D6D68A200CEA253 /* objc-runtime-new.mm */; };
838486090D6D68A200CEA253 /* objc-runtime-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E20D6D68A200CEA253 /* objc-runtime-old.m */; };
8384860A0D6D68A200CEA253 /* objc-runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E30D6D68A200CEA253 /* objc-runtime.h */; settings = {ATTRIBUTES = (Public, ); }; };
8384860B0D6D68A200CEA253 /* objc-runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E40D6D68A200CEA253 /* objc-runtime.m */; };
838486250D6D68F000CEA253 /* List.m in Sources */ = {isa = PBXBuildFile; fileRef = 838486230D6D68F000CEA253 /* List.m */; };
838486260D6D68F000CEA253 /* List.h in Headers */ = {isa = PBXBuildFile; fileRef = 838486240D6D68F000CEA253 /* List.h */; settings = {ATTRIBUTES = (Public, ); }; };
838486280D6D6A2400CEA253 /* message.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485BD0D6D687300CEA253 /* message.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 838B85700DB8915A0029D21A /* objc-sel-table.s in Sources */ = {isa = PBXBuildFile; fileRef = 838B856F0DB8915A0029D21A /* objc-sel-table.s */; };
- 83CDE7530E2E0040003703C1 /* objc-selopt.h in Headers */ = {isa = PBXBuildFile; fileRef = 83CDE7520E2E0040003703C1 /* objc-selopt.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 83B1A8BE0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 83B1A8BC0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s */; };
+ 83BE02E40FCCB23400661494 /* objc-file-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BE02E30FCCB23400661494 /* objc-file-old.m */; };
+ 83BE02E80FCCB24D00661494 /* objc-file-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E50FCCB24D00661494 /* objc-file-old.h */; };
+ 83BE02E90FCCB24D00661494 /* objc-file.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E60FCCB24D00661494 /* objc-file.h */; };
+ 83BE02EA0FCCB24D00661494 /* objc-runtime-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E70FCCB24D00661494 /* objc-runtime-old.h */; };
+ 83E2799D117F76EF00452546 /* objc-selopt.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83CDE7520E2E0040003703C1 /* objc-selopt.h */; };
+ 83E50CDB0FF19E8200D74C19 /* hashtable2.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485B70D6D687300CEA253 /* hashtable2.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CDC0FF19E8200D74C19 /* maptable.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485BB0D6D687300CEA253 /* maptable.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 83E50CDD0FF19E8200D74C19 /* objc-api.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485C80D6D68A200CEA253 /* objc-api.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CDE0FF19E8200D74C19 /* objc-auto.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485C90D6D68A200CEA253 /* objc-auto.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CDF0FF19E8200D74C19 /* objc-auto-dump.h in Headers */ = {isa = PBXBuildFile; fileRef = BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 83E50CE00FF19E8200D74C19 /* objc-class.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485CD0D6D68A200CEA253 /* objc-class.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CE10FF19E8200D74C19 /* objc-config.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485CF0D6D68A200CEA253 /* objc-config.h */; };
+ 83E50CE20FF19E8200D74C19 /* objc-exception.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D10D6D68A200CEA253 /* objc-exception.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CE30FF19E8200D74C19 /* objc-initialize.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D40D6D68A200CEA253 /* objc-initialize.h */; };
+ 83E50CE40FF19E8200D74C19 /* objc-load.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D70D6D68A200CEA253 /* objc-load.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CE50FF19E8200D74C19 /* objc-loadmethod.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D90D6D68A200CEA253 /* objc-loadmethod.h */; };
+ 83E50CE60FF19E8200D74C19 /* objc-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485DC0D6D68A200CEA253 /* objc-private.h */; };
+ 83E50CE80FF19E8200D74C19 /* objc-runtime-new.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E00D6D68A200CEA253 /* objc-runtime-new.h */; };
+ 83E50CE90FF19E8200D74C19 /* objc-runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E30D6D68A200CEA253 /* objc-runtime.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CEB0FF19E8200D74C19 /* objc-sel-set.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E50D6D68A200CEA253 /* objc-sel-set.h */; };
+ 83E50CEC0FF19E8200D74C19 /* objc-sync.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E90D6D68A200CEA253 /* objc-sync.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CED0FF19E8200D74C19 /* objc.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485EC0D6D68A200CEA253 /* objc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CEE0FF19E8200D74C19 /* Object.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485ED0D6D68A200CEA253 /* Object.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CEF0FF19E8200D74C19 /* Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 838486180D6D68A800CEA253 /* Protocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CF00FF19E8200D74C19 /* runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384861A0D6D68A800CEA253 /* runtime.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CF10FF19E8200D74C19 /* List.h in Headers */ = {isa = PBXBuildFile; fileRef = 838486240D6D68F000CEA253 /* List.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CF20FF19E8200D74C19 /* message.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485BD0D6D687300CEA253 /* message.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CF30FF19E8200D74C19 /* objc-accessors.h in Headers */ = {isa = PBXBuildFile; fileRef = 830F2A920D73876100392440 /* objc-accessors.h */; };
+ 83E50CF40FF19E8200D74C19 /* hashtable.h in Headers */ = {isa = PBXBuildFile; fileRef = 830F2A970D738DC200392440 /* hashtable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 83E50CF50FF19E8200D74C19 /* objc-references.h in Headers */ = {isa = PBXBuildFile; fileRef = 393CEAC50DC69E67000B69DE /* objc-references.h */; };
+ 83E50CF60FF19E8200D74C19 /* objc-os.h in Headers */ = {isa = PBXBuildFile; fileRef = 831C85D30E10CF850066E64C /* objc-os.h */; };
+ 83E50CF70FF19E8200D74C19 /* objc-gdb.h in Headers */ = {isa = PBXBuildFile; fileRef = 834266D70E665A8B002E4DA2 /* objc-gdb.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 83E50CF80FF19E8200D74C19 /* objc-internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 83112ED30F00599600A5FBAF /* objc-internal.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 83E50D130FF19E8200D74C19 /* Object.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EE0D6D68A200CEA253 /* Object.m */; };
+ 83E50D140FF19E8200D74C19 /* Protocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 838486190D6D68A800CEA253 /* Protocol.m */; };
+ 83E50D150FF19E8200D74C19 /* List.m in Sources */ = {isa = PBXBuildFile; fileRef = 838486230D6D68F000CEA253 /* List.m */; };
+ 83E57595121E892100295464 /* objc-abi.h in Headers */ = {isa = PBXBuildFile; fileRef = 834EC0A311614167009B2563 /* objc-abi.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 83E57596121E896200295464 /* objc-file-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E50FCCB24D00661494 /* objc-file-old.h */; };
+ 83E57597121E8A0A00295464 /* objc-runtime-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E70FCCB24D00661494 /* objc-runtime-old.h */; };
+ 83E57598121E8A1600295464 /* objc-file.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E60FCCB24D00661494 /* objc-file.h */; };
+ 83EB007B121C9EC200B92C16 /* objc-sel-table.s in Sources */ = {isa = PBXBuildFile; fileRef = 83EB007A121C9EC200B92C16 /* objc-sel-table.s */; };
87BB4EA70EC39854005D08E1 /* objc-probes.d in Sources */ = {isa = PBXBuildFile; fileRef = 87BB4E900EC39633005D08E1 /* objc-probes.d */; };
BC07A00C0EF72D360014EC61 /* objc-auto-dump.h in Headers */ = {isa = PBXBuildFile; fileRef = BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */; settings = {ATTRIBUTES = (Private, ); }; };
BC07A0110EF72D9C0014EC61 /* objc-auto-dump.m in Sources */ = {isa = PBXBuildFile; fileRef = BC07A0100EF72D9C0014EC61 /* objc-auto-dump.m */; };
+ E8923DA1116AB2820071B552 /* a1a2-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9C116AB2820071B552 /* a1a2-blocktramps-i386.s */; };
+ E8923DA2116AB2820071B552 /* a1a2-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */; };
+ E8923DA3116AB2820071B552 /* a2a3-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */; };
+ E8923DA4116AB2820071B552 /* a2a3-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9F116AB2820071B552 /* a2a3-blocktramps-x86_64.s */; };
+ E8923DA5116AB2820071B552 /* objc-block-trampolines.m in Sources */ = {isa = PBXBuildFile; fileRef = E8923DA0116AB2820071B552 /* objc-block-trampolines.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
- 830F2AAF0D7394E900392440 /* PBXContainerItemProxy */ = {
+ 39023F7512F09D0400E1426D /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 830F2AA80D7394D000392440;
+ remoteInfo = markgc;
+ };
+ 835720F50F8BF8EE00BD4FAD /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
proxyType = 1;
};
/* End PBXContainerItemProxy section */
+/* Begin PBXCopyFilesBuildPhase section */
+ 83E27999117F76DF00452546 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = /usr/local/include/objc;
+ dstSubfolderSpec = 0;
+ files = (
+ 83E2799D117F76EF00452546 /* objc-selopt.h in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
/* Begin PBXFileReference section */
+ 39023FCE12F09D0400E1426D /* libobjc.A.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libobjc.A.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
393CEABF0DC69E3E000B69DE /* objc-references.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-references.mm"; path = "runtime/objc-references.mm"; sourceTree = "<group>"; };
393CEAC50DC69E67000B69DE /* objc-references.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-references.h"; path = "runtime/objc-references.h"; sourceTree = "<group>"; };
+ 399BC72D1224831B007FBDF0 /* objc-externalref.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-externalref.m"; path = "runtime/objc-externalref.m"; sourceTree = "<group>"; };
+ 39ABD71F12F0B61800D1054C /* objc-weak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-weak.h"; path = "runtime/objc-weak.h"; sourceTree = "<group>"; };
+ 39ABD72012F0B61800D1054C /* objc-weak.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-weak.mm"; path = "runtime/objc-weak.mm"; sourceTree = "<group>"; };
+ 552B2FB612F9D31E00650C0D /* objc-arr.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-arr.mm"; path = "runtime/objc-arr.mm"; sourceTree = "<group>"; };
830F2A690D737FB800392440 /* objc-msg-arm.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-arm.s"; path = "runtime/Messengers.subproj/objc-msg-arm.s"; sourceTree = "<group>"; };
830F2A6A0D737FB800392440 /* objc-msg-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-i386.s"; path = "runtime/Messengers.subproj/objc-msg-i386.s"; sourceTree = "<group>"; };
- 830F2A6B0D737FB800392440 /* objc-msg-ppc.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-ppc.s"; path = "runtime/Messengers.subproj/objc-msg-ppc.s"; sourceTree = "<group>"; };
- 830F2A720D737FB800392440 /* objc-msg-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-x86_64.s"; path = "runtime/Messengers.subproj/objc-msg-x86_64.s"; sourceTree = "<group>"; };
+ 830F2A720D737FB800392440 /* objc-msg-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-x86_64.s"; path = "runtime/Messengers.subproj/objc-msg-x86_64.s"; sourceTree = "<group>"; tabWidth = 8; usesTabs = 1; };
830F2A850D73819A00392440 /* objc-auto-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-auto-i386.s"; path = "runtime/Auto.subproj/objc-auto-i386.s"; sourceTree = "<group>"; };
- 830F2A860D73819A00392440 /* objc-auto-ppc.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-auto-ppc.s"; path = "runtime/Auto.subproj/objc-auto-ppc.s"; sourceTree = "<group>"; };
- 830F2A870D73819A00392440 /* objc-auto-ppc64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-auto-ppc64.s"; path = "runtime/Auto.subproj/objc-auto-ppc64.s"; sourceTree = "<group>"; };
830F2A920D73876100392440 /* objc-accessors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-accessors.h"; path = "runtime/Accessors.subproj/objc-accessors.h"; sourceTree = "<group>"; };
830F2A930D73876100392440 /* objc-accessors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-accessors.m"; path = "runtime/Accessors.subproj/objc-accessors.m"; sourceTree = "<group>"; };
830F2A970D738DC200392440 /* hashtable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hashtable.h; path = runtime/hashtable.h; sourceTree = "<group>"; };
831C85D30E10CF850066E64C /* objc-os.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-os.h"; path = "runtime/objc-os.h"; sourceTree = "<group>"; };
831C85D40E10CF850066E64C /* objc-os.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-os.m"; path = "runtime/objc-os.m"; sourceTree = "<group>"; };
834266D70E665A8B002E4DA2 /* objc-gdb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-gdb.h"; path = "runtime/objc-gdb.h"; sourceTree = "<group>"; };
+ 834EC0A311614167009B2563 /* objc-abi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-abi.h"; path = "runtime/objc-abi.h"; sourceTree = "<group>"; };
+ 8383A3A1122600E9009290B8 /* a1a2-blocktramps-arm.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a1a2-blocktramps-arm.s"; path = "runtime/a1a2-blocktramps-arm.s"; sourceTree = "<group>"; };
+ 8383A3A2122600E9009290B8 /* a2a3-blocktramps-arm.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a2a3-blocktramps-arm.s"; path = "runtime/a2a3-blocktramps-arm.s"; sourceTree = "<group>"; };
838485B30D6D682B00CEA253 /* libobjc.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = libobjc.order; sourceTree = "<group>"; };
838485B40D6D683300CEA253 /* APPLE_LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = APPLE_LICENSE; sourceTree = "<group>"; };
838485B50D6D683300CEA253 /* ReleaseNotes.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = ReleaseNotes.rtf; sourceTree = "<group>"; };
- 838485B60D6D687300CEA253 /* error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = error.h; path = runtime/error.h; sourceTree = "<group>"; };
838485B70D6D687300CEA253 /* hashtable2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hashtable2.h; path = runtime/hashtable2.h; sourceTree = "<group>"; };
838485B80D6D687300CEA253 /* hashtable2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = hashtable2.m; path = runtime/hashtable2.m; sourceTree = "<group>"; };
838485BB0D6D687300CEA253 /* maptable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = maptable.h; path = runtime/maptable.h; sourceTree = "<group>"; };
838485D00D6D68A200CEA253 /* objc-errors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-errors.m"; path = "runtime/objc-errors.m"; sourceTree = "<group>"; };
838485D10D6D68A200CEA253 /* objc-exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-exception.h"; path = "runtime/objc-exception.h"; sourceTree = "<group>"; };
838485D20D6D68A200CEA253 /* objc-exception.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-exception.m"; path = "runtime/objc-exception.m"; sourceTree = "<group>"; };
- 838485D30D6D68A200CEA253 /* objc-file.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-file.m"; path = "runtime/objc-file.m"; sourceTree = "<group>"; };
+ 838485D30D6D68A200CEA253 /* objc-file.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-file.mm"; path = "runtime/objc-file.mm"; sourceTree = "<group>"; };
838485D40D6D68A200CEA253 /* objc-initialize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-initialize.h"; path = "runtime/objc-initialize.h"; sourceTree = "<group>"; };
838485D50D6D68A200CEA253 /* objc-initialize.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-initialize.m"; path = "runtime/objc-initialize.m"; sourceTree = "<group>"; };
838485D60D6D68A200CEA253 /* objc-layout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-layout.m"; path = "runtime/objc-layout.m"; sourceTree = "<group>"; };
838485DA0D6D68A200CEA253 /* objc-loadmethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-loadmethod.m"; path = "runtime/objc-loadmethod.m"; sourceTree = "<group>"; };
838485DB0D6D68A200CEA253 /* objc-lockdebug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-lockdebug.m"; path = "runtime/objc-lockdebug.m"; sourceTree = "<group>"; };
838485DC0D6D68A200CEA253 /* objc-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-private.h"; path = "runtime/objc-private.h"; sourceTree = "<group>"; };
- 838485DE0D6D68A200CEA253 /* objc-rtp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-rtp.h"; path = "runtime/objc-rtp.h"; sourceTree = "<group>"; };
838485DF0D6D68A200CEA253 /* objc-rtp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-rtp.m"; path = "runtime/objc-rtp.m"; sourceTree = "<group>"; };
838485E00D6D68A200CEA253 /* objc-runtime-new.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-runtime-new.h"; path = "runtime/objc-runtime-new.h"; sourceTree = "<group>"; };
- 838485E10D6D68A200CEA253 /* objc-runtime-new.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-runtime-new.m"; path = "runtime/objc-runtime-new.m"; sourceTree = "<group>"; };
+ 838485E10D6D68A200CEA253 /* objc-runtime-new.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-runtime-new.mm"; path = "runtime/objc-runtime-new.mm"; sourceTree = "<group>"; };
838485E20D6D68A200CEA253 /* objc-runtime-old.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-runtime-old.m"; path = "runtime/objc-runtime-old.m"; sourceTree = "<group>"; };
838485E30D6D68A200CEA253 /* objc-runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-runtime.h"; path = "runtime/objc-runtime.h"; sourceTree = "<group>"; };
838485E40D6D68A200CEA253 /* objc-runtime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-runtime.m"; path = "runtime/objc-runtime.m"; sourceTree = "<group>"; };
8384861A0D6D68A800CEA253 /* runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = runtime.h; path = runtime/runtime.h; sourceTree = "<group>"; };
838486230D6D68F000CEA253 /* List.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = List.m; path = runtime/OldClasses.subproj/List.m; sourceTree = "<group>"; };
838486240D6D68F000CEA253 /* List.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = List.h; path = runtime/OldClasses.subproj/List.h; sourceTree = "<group>"; };
- 838B856F0DB8915A0029D21A /* objc-sel-table.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-sel-table.s"; path = "runtime/objc-sel-table.s"; sourceTree = "<group>"; };
+ 83B1A8BC0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-simulator-i386.s"; path = "runtime/Messengers.subproj/objc-msg-simulator-i386.s"; sourceTree = "<group>"; };
+ 83BE02E30FCCB23400661494 /* objc-file-old.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-file-old.m"; path = "runtime/objc-file-old.m"; sourceTree = "<group>"; };
+ 83BE02E50FCCB24D00661494 /* objc-file-old.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-file-old.h"; path = "runtime/objc-file-old.h"; sourceTree = "<group>"; };
+ 83BE02E60FCCB24D00661494 /* objc-file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-file.h"; path = "runtime/objc-file.h"; sourceTree = "<group>"; };
+ 83BE02E70FCCB24D00661494 /* objc-runtime-old.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-runtime-old.h"; path = "runtime/objc-runtime-old.h"; sourceTree = "<group>"; };
83CDE7520E2E0040003703C1 /* objc-selopt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-selopt.h"; path = "runtime/objc-selopt.h"; sourceTree = "<group>"; };
+ 83E50D2A0FF19E8200D74C19 /* libobjc.A.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libobjc.A.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+ 83E50D2B0FF19E9E00D74C19 /* IndigoSDK.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = IndigoSDK.xcconfig; path = AppleInternal/XcodeConfig/IndigoSDK.xcconfig; sourceTree = DEVELOPER_DIR; };
+ 83EB007A121C9EC200B92C16 /* objc-sel-table.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-sel-table.s"; path = "runtime/objc-sel-table.s"; sourceTree = "<group>"; };
87BB4E900EC39633005D08E1 /* objc-probes.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; name = "objc-probes.d"; path = "runtime/objc-probes.d"; sourceTree = "<group>"; };
BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-auto-dump.h"; path = "runtime/objc-auto-dump.h"; sourceTree = "<group>"; };
BC07A0100EF72D9C0014EC61 /* objc-auto-dump.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-auto-dump.m"; path = "runtime/objc-auto-dump.m"; sourceTree = "<group>"; };
+ BC8B5D1212D3D48100C78A5B /* libauto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libauto.dylib; path = /usr/lib/libauto.dylib; sourceTree = "<absolute>"; };
D2AAC0630554660B00DB518D /* libobjc.A.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libobjc.A.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+ E8923D9C116AB2820071B552 /* a1a2-blocktramps-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a1a2-blocktramps-i386.s"; path = "runtime/a1a2-blocktramps-i386.s"; sourceTree = "<group>"; };
+ E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a1a2-blocktramps-x86_64.s"; path = "runtime/a1a2-blocktramps-x86_64.s"; sourceTree = "<group>"; };
+ E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a2a3-blocktramps-i386.s"; path = "runtime/a2a3-blocktramps-i386.s"; sourceTree = "<group>"; };
+ E8923D9F116AB2820071B552 /* a2a3-blocktramps-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a2a3-blocktramps-x86_64.s"; path = "runtime/a2a3-blocktramps-x86_64.s"; sourceTree = "<group>"; };
+ E8923DA0116AB2820071B552 /* objc-block-trampolines.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-block-trampolines.m"; path = "runtime/objc-block-trampolines.m"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 39023FC712F09D0400E1426D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
830F2AA70D7394D000392440 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 83E50D240FF19E8200D74C19 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
D289988505E68E00004EDB86 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
08FB7794FE84155DC02AAC07 /* objc */ = {
isa = PBXGroup;
children = (
+ BC8B5D1212D3D48100C78A5B /* libauto.dylib */,
838485C60D6D687700CEA253 /* Public Headers */,
838485C70D6D688200CEA253 /* Private Headers */,
8384862A0D6D6ABC00CEA253 /* Project Headers */,
08FB7795FE84155DC02AAC07 /* Source */ = {
isa = PBXGroup;
children = (
- 838B856F0DB8915A0029D21A /* objc-sel-table.s */,
+ 8383A3A1122600E9009290B8 /* a1a2-blocktramps-arm.s */,
+ 8383A3A2122600E9009290B8 /* a2a3-blocktramps-arm.s */,
838485B80D6D687300CEA253 /* hashtable2.m */,
838485BC0D6D687300CEA253 /* maptable.m */,
830F2A930D73876100392440 /* objc-accessors.m */,
838485CA0D6D68A200CEA253 /* objc-auto.m */,
BC07A0100EF72D9C0014EC61 /* objc-auto-dump.m */,
+ 552B2FB612F9D31E00650C0D /* objc-arr.mm */,
+ 39ABD72012F0B61800D1054C /* objc-weak.mm */,
+ E8923DA0116AB2820071B552 /* objc-block-trampolines.m */,
838485CB0D6D68A200CEA253 /* objc-cache.m */,
838485CC0D6D68A200CEA253 /* objc-class-old.m */,
838485CE0D6D68A200CEA253 /* objc-class.m */,
838485D00D6D68A200CEA253 /* objc-errors.m */,
838485D20D6D68A200CEA253 /* objc-exception.m */,
- 838485D30D6D68A200CEA253 /* objc-file.m */,
+ 399BC72D1224831B007FBDF0 /* objc-externalref.m */,
+ 838485D30D6D68A200CEA253 /* objc-file.mm */,
+ 83BE02E30FCCB23400661494 /* objc-file-old.m */,
838485D50D6D68A200CEA253 /* objc-initialize.m */,
838485D60D6D68A200CEA253 /* objc-layout.m */,
838485D80D6D68A200CEA253 /* objc-load.m */,
831C85D40E10CF850066E64C /* objc-os.m */,
393CEABF0DC69E3E000B69DE /* objc-references.mm */,
838485DF0D6D68A200CEA253 /* objc-rtp.m */,
- 838485E10D6D68A200CEA253 /* objc-runtime-new.m */,
+ 838485E10D6D68A200CEA253 /* objc-runtime-new.mm */,
838485E20D6D68A200CEA253 /* objc-runtime-old.m */,
838485E40D6D68A200CEA253 /* objc-runtime.m */,
838485E60D6D68A200CEA253 /* objc-sel-set.m */,
+ 83EB007A121C9EC200B92C16 /* objc-sel-table.s */,
838485E80D6D68A200CEA253 /* objc-sel.mm */,
838485EA0D6D68A200CEA253 /* objc-sync.m */,
838485EB0D6D68A200CEA253 /* objc-typeencoding.m */,
+ E8923D9C116AB2820071B552 /* a1a2-blocktramps-i386.s */,
+ E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */,
+ E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */,
+ E8923D9F116AB2820071B552 /* a2a3-blocktramps-x86_64.s */,
830F2A850D73819A00392440 /* objc-auto-i386.s */,
- 830F2A860D73819A00392440 /* objc-auto-ppc.s */,
- 830F2A870D73819A00392440 /* objc-auto-ppc64.s */,
830F2A690D737FB800392440 /* objc-msg-arm.s */,
830F2A6A0D737FB800392440 /* objc-msg-i386.s */,
- 830F2A6B0D737FB800392440 /* objc-msg-ppc.s */,
+ 83B1A8BC0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s */,
830F2A720D737FB800392440 /* objc-msg-x86_64.s */,
87BB4E900EC39633005D08E1 /* objc-probes.d */,
);
children = (
D2AAC0630554660B00DB518D /* libobjc.A.dylib */,
830F2AA90D7394D000392440 /* markgc */,
+ 83E50D2A0FF19E8200D74C19 /* libobjc.A.dylib */,
+ 39023FCE12F09D0400E1426D /* libobjc.A.dylib */,
);
name = Products;
sourceTree = "<group>";
838485B40D6D683300CEA253 /* APPLE_LICENSE */,
838485B50D6D683300CEA253 /* ReleaseNotes.rtf */,
838485B30D6D682B00CEA253 /* libobjc.order */,
+ 83E50D2B0FF19E9E00D74C19 /* IndigoSDK.xcconfig */,
);
name = Other;
sourceTree = "<group>";
isa = PBXGroup;
children = (
83112ED30F00599600A5FBAF /* objc-internal.h */,
+ 834EC0A311614167009B2563 /* objc-abi.h */,
838485BB0D6D687300CEA253 /* maptable.h */,
BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */,
834266D70E665A8B002E4DA2 /* objc-gdb.h */,
838486220D6D68E300CEA253 /* Obsolete Headers */ = {
isa = PBXGroup;
children = (
- 838485B60D6D687300CEA253 /* error.h */,
830F2A970D738DC200392440 /* hashtable.h */,
838485B70D6D687300CEA253 /* hashtable2.h */,
838485CD0D6D68A200CEA253 /* objc-class.h */,
8384862A0D6D6ABC00CEA253 /* Project Headers */ = {
isa = PBXGroup;
children = (
- 831C85D30E10CF850066E64C /* objc-os.h */,
830F2A920D73876100392440 /* objc-accessors.h */,
838485CF0D6D68A200CEA253 /* objc-config.h */,
+ 83BE02E60FCCB24D00661494 /* objc-file.h */,
+ 83BE02E50FCCB24D00661494 /* objc-file-old.h */,
838485D40D6D68A200CEA253 /* objc-initialize.h */,
838485D90D6D68A200CEA253 /* objc-loadmethod.h */,
+ 831C85D30E10CF850066E64C /* objc-os.h */,
838485DC0D6D68A200CEA253 /* objc-private.h */,
393CEAC50DC69E67000B69DE /* objc-references.h */,
- 838485DE0D6D68A200CEA253 /* objc-rtp.h */,
838485E00D6D68A200CEA253 /* objc-runtime-new.h */,
+ 83BE02E70FCCB24D00661494 /* objc-runtime-old.h */,
838485E50D6D68A200CEA253 /* objc-sel-set.h */,
+ 39ABD71F12F0B61800D1054C /* objc-weak.h */,
);
name = "Project Headers";
sourceTree = "<group>";
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
+ 39023F7612F09D0400E1426D /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 39023F7712F09D0400E1426D /* hashtable.h in Headers */,
+ 39023F7812F09D0400E1426D /* hashtable2.h in Headers */,
+ 39023F7912F09D0400E1426D /* List.h in Headers */,
+ 39023F7A12F09D0400E1426D /* maptable.h in Headers */,
+ 39023F7B12F09D0400E1426D /* message.h in Headers */,
+ 39023F7C12F09D0400E1426D /* objc-abi.h in Headers */,
+ 39023F7D12F09D0400E1426D /* objc-accessors.h in Headers */,
+ 39023F7E12F09D0400E1426D /* objc-api.h in Headers */,
+ 39023F7F12F09D0400E1426D /* objc-auto-dump.h in Headers */,
+ 39023F8012F09D0400E1426D /* objc-auto.h in Headers */,
+ 39023F8112F09D0400E1426D /* objc-class.h in Headers */,
+ 39023F8212F09D0400E1426D /* objc-config.h in Headers */,
+ 39023F8312F09D0400E1426D /* objc-exception.h in Headers */,
+ 39023F8412F09D0400E1426D /* objc-file-old.h in Headers */,
+ 39023F8512F09D0400E1426D /* objc-file.h in Headers */,
+ 39023F8612F09D0400E1426D /* objc-gdb.h in Headers */,
+ 39023F8712F09D0400E1426D /* objc-initialize.h in Headers */,
+ 39023F8812F09D0400E1426D /* objc-internal.h in Headers */,
+ 39023F8912F09D0400E1426D /* objc-load.h in Headers */,
+ 39023F8A12F09D0400E1426D /* objc-loadmethod.h in Headers */,
+ 39023F8B12F09D0400E1426D /* objc-os.h in Headers */,
+ 39023F8C12F09D0400E1426D /* objc-private.h in Headers */,
+ 39023F8D12F09D0400E1426D /* objc-references.h in Headers */,
+ 39023F8E12F09D0400E1426D /* objc-runtime-new.h in Headers */,
+ 39023F8F12F09D0400E1426D /* objc-runtime-old.h in Headers */,
+ 39023F9012F09D0400E1426D /* objc-runtime.h in Headers */,
+ 39023F9112F09D0400E1426D /* objc-sel-set.h in Headers */,
+ 39023F9212F09D0400E1426D /* objc-sync.h in Headers */,
+ 39023F9312F09D0400E1426D /* objc.h in Headers */,
+ 39023F9412F09D0400E1426D /* Object.h in Headers */,
+ 39023F9512F09D0400E1426D /* Protocol.h in Headers */,
+ 39023F9612F09D0400E1426D /* runtime.h in Headers */,
+ 39ABD72112F0B61800D1054C /* objc-weak.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 83E50CDA0FF19E8200D74C19 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 83E50CF40FF19E8200D74C19 /* hashtable.h in Headers */,
+ 83E50CDB0FF19E8200D74C19 /* hashtable2.h in Headers */,
+ 83E50CF10FF19E8200D74C19 /* List.h in Headers */,
+ 83E50CDC0FF19E8200D74C19 /* maptable.h in Headers */,
+ 83E50CF20FF19E8200D74C19 /* message.h in Headers */,
+ 83E57595121E892100295464 /* objc-abi.h in Headers */,
+ 83E50CF30FF19E8200D74C19 /* objc-accessors.h in Headers */,
+ 83E50CDD0FF19E8200D74C19 /* objc-api.h in Headers */,
+ 83E50CDE0FF19E8200D74C19 /* objc-auto.h in Headers */,
+ 83E50CDF0FF19E8200D74C19 /* objc-auto-dump.h in Headers */,
+ 83E50CE00FF19E8200D74C19 /* objc-class.h in Headers */,
+ 83E50CE10FF19E8200D74C19 /* objc-config.h in Headers */,
+ 83E50CE20FF19E8200D74C19 /* objc-exception.h in Headers */,
+ 83E57596121E896200295464 /* objc-file-old.h in Headers */,
+ 83E57598121E8A1600295464 /* objc-file.h in Headers */,
+ 83E50CF70FF19E8200D74C19 /* objc-gdb.h in Headers */,
+ 83E50CE30FF19E8200D74C19 /* objc-initialize.h in Headers */,
+ 83E50CF80FF19E8200D74C19 /* objc-internal.h in Headers */,
+ 83E50CE40FF19E8200D74C19 /* objc-load.h in Headers */,
+ 83E50CE50FF19E8200D74C19 /* objc-loadmethod.h in Headers */,
+ 83E50CF60FF19E8200D74C19 /* objc-os.h in Headers */,
+ 83E50CE60FF19E8200D74C19 /* objc-private.h in Headers */,
+ 83E50CF50FF19E8200D74C19 /* objc-references.h in Headers */,
+ 83E50CE80FF19E8200D74C19 /* objc-runtime-new.h in Headers */,
+ 83E57597121E8A0A00295464 /* objc-runtime-old.h in Headers */,
+ 83E50CE90FF19E8200D74C19 /* objc-runtime.h in Headers */,
+ 83E50CEB0FF19E8200D74C19 /* objc-sel-set.h in Headers */,
+ 83E50CEC0FF19E8200D74C19 /* objc-sync.h in Headers */,
+ 83E50CED0FF19E8200D74C19 /* objc.h in Headers */,
+ 83E50CEE0FF19E8200D74C19 /* Object.h in Headers */,
+ 83E50CEF0FF19E8200D74C19 /* Protocol.h in Headers */,
+ 83E50CF00FF19E8200D74C19 /* runtime.h in Headers */,
+ 39ABD72512F0B61800D1054C /* objc-weak.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
D2AAC0600554660B00DB518D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- 838485BE0D6D687300CEA253 /* error.h in Headers */,
+ 830F2A980D738DC200392440 /* hashtable.h in Headers */,
838485BF0D6D687300CEA253 /* hashtable2.h in Headers */,
+ 838486260D6D68F000CEA253 /* List.h in Headers */,
838485C30D6D687300CEA253 /* maptable.h in Headers */,
+ 838486280D6D6A2400CEA253 /* message.h in Headers */,
+ 834EC0A411614167009B2563 /* objc-abi.h in Headers */,
+ 830F2A940D73876100392440 /* objc-accessors.h in Headers */,
838485EF0D6D68A200CEA253 /* objc-api.h in Headers */,
- 838485F00D6D68A200CEA253 /* objc-auto.h in Headers */,
BC07A00C0EF72D360014EC61 /* objc-auto-dump.h in Headers */,
+ 838485F00D6D68A200CEA253 /* objc-auto.h in Headers */,
838485F40D6D68A200CEA253 /* objc-class.h in Headers */,
838485F60D6D68A200CEA253 /* objc-config.h in Headers */,
838485F80D6D68A200CEA253 /* objc-exception.h in Headers */,
+ 83BE02E80FCCB24D00661494 /* objc-file-old.h in Headers */,
+ 83BE02E90FCCB24D00661494 /* objc-file.h in Headers */,
+ 834266D80E665A8B002E4DA2 /* objc-gdb.h in Headers */,
838485FB0D6D68A200CEA253 /* objc-initialize.h in Headers */,
+ 83112ED40F00599600A5FBAF /* objc-internal.h in Headers */,
838485FE0D6D68A200CEA253 /* objc-load.h in Headers */,
838486000D6D68A200CEA253 /* objc-loadmethod.h in Headers */,
+ 831C85D50E10CF850066E64C /* objc-os.h in Headers */,
838486030D6D68A200CEA253 /* objc-private.h in Headers */,
- 838486050D6D68A200CEA253 /* objc-rtp.h in Headers */,
+ 393CEAC60DC69E67000B69DE /* objc-references.h in Headers */,
838486070D6D68A200CEA253 /* objc-runtime-new.h in Headers */,
+ 83BE02EA0FCCB24D00661494 /* objc-runtime-old.h in Headers */,
8384860A0D6D68A200CEA253 /* objc-runtime.h in Headers */,
- 83CDE7530E2E0040003703C1 /* objc-selopt.h in Headers */,
8384860C0D6D68A200CEA253 /* objc-sel-set.h in Headers */,
838486100D6D68A200CEA253 /* objc-sync.h in Headers */,
838486130D6D68A200CEA253 /* objc.h in Headers */,
838486140D6D68A200CEA253 /* Object.h in Headers */,
8384861E0D6D68A800CEA253 /* Protocol.h in Headers */,
838486200D6D68A800CEA253 /* runtime.h in Headers */,
- 838486260D6D68F000CEA253 /* List.h in Headers */,
- 838486280D6D6A2400CEA253 /* message.h in Headers */,
- 830F2A940D73876100392440 /* objc-accessors.h in Headers */,
- 830F2A980D738DC200392440 /* hashtable.h in Headers */,
- 393CEAC60DC69E67000B69DE /* objc-references.h in Headers */,
- 831C85D50E10CF850066E64C /* objc-os.h in Headers */,
- 834266D80E665A8B002E4DA2 /* objc-gdb.h in Headers */,
- 83112ED40F00599600A5FBAF /* objc-internal.h in Headers */,
+ 39ABD72312F0B61800D1054C /* objc-weak.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
+ 39023F7312F09D0400E1426D /* objc-device */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 39023FCB12F09D0400E1426D /* Build configuration list for PBXNativeTarget "objc-device" */;
+ buildPhases = (
+ 39023F7612F09D0400E1426D /* Headers */,
+ 39023F9812F09D0400E1426D /* Sources */,
+ 39023FC712F09D0400E1426D /* Frameworks */,
+ 39023FC912F09D0400E1426D /* Run Script (markgc) */,
+ 39023FCA12F09D0400E1426D /* Run Script (symlink) */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 39023F7412F09D0400E1426D /* PBXTargetDependency */,
+ );
+ name = "objc-device";
+ productName = objc;
+ productReference = 39023FCE12F09D0400E1426D /* libobjc.A.dylib */;
+ productType = "com.apple.product-type.library.dynamic";
+ };
830F2AA80D7394D000392440 /* markgc */ = {
isa = PBXNativeTarget;
buildConfigurationList = 830F2AAE0D7394D600392440 /* Build configuration list for PBXNativeTarget "markgc" */;
productReference = 830F2AA90D7394D000392440 /* markgc */;
productType = "com.apple.product-type.tool";
};
+ 83E50CD70FF19E8200D74C19 /* objc-simulator */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 83E50D270FF19E8200D74C19 /* Build configuration list for PBXNativeTarget "objc-simulator" */;
+ buildPhases = (
+ 83E50CDA0FF19E8200D74C19 /* Headers */,
+ 83E50CFC0FF19E8200D74C19 /* Sources */,
+ 83E50D240FF19E8200D74C19 /* Frameworks */,
+ 83E50D260FF19E8200D74C19 /* Run Script (symlink) */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "objc-simulator";
+ productName = objc;
+ productReference = 83E50D2A0FF19E8200D74C19 /* libobjc.A.dylib */;
+ productType = "com.apple.product-type.library.dynamic";
+ };
D2AAC0620554660B00DB518D /* objc */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1DEB914A08733D8E0010E9CD /* Build configuration list for PBXNativeTarget "objc" */;
buildRules = (
);
dependencies = (
- 830F2AB00D7394E900392440 /* PBXTargetDependency */,
+ 835720F60F8BF8EE00BD4FAD /* PBXTargetDependency */,
);
name = objc;
productName = objc;
};
buildConfigurationList = 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "objc" */;
compatibilityVersion = "Xcode 3.1";
+ developmentRegion = English;
hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ );
mainGroup = 08FB7794FE84155DC02AAC07 /* objc */;
projectDirPath = "";
projectRoot = "";
targets = (
D2AAC0620554660B00DB518D /* objc */,
830F2AA80D7394D000392440 /* markgc */,
+ 83E50CD70FF19E8200D74C19 /* objc-simulator */,
+ 39023F7312F09D0400E1426D /* objc-device */,
+ 83E2799A117F76DF00452546 /* objc-selopt */,
);
};
/* End PBXProject section */
/* Begin PBXShellScriptBuildPhase section */
+ 39023FC912F09D0400E1426D /* Run Script (markgc) */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ comments = "Set the GC-supported bit in libobjc itself.\n\nlibobjc cannot be built with -fobjc-gc, because it needs more precise control over write-barrier use.\n\nThis is done on all architectures and platforms, even though some don't actually support GC. In those cases, a program that actually tries to use GC will fail with link errors.";
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script (markgc)";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "if [ ${NATIVE_ARCH} = ${NATIVE_ARCH_32_BIT} -o ${NATIVE_ARCH} = ${NATIVE_ARCH_64_BIT} -o ${NATIVE_ARCH} = ${NATIVE_ARCH_ACTUAL} ]; then\n \"${BUILT_PRODUCTS_DIR}/markgc\" -p \"${BUILT_PRODUCTS_DIR}/libobjc.A.dylib\"\nelse\n echo \"Skipping markgc for cross compile.\"\nfi";
+ };
+ 39023FCA12F09D0400E1426D /* Run Script (symlink) */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script (symlink)";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "cd \"${INSTALL_DIR}\"\n/bin/ln -s libobjc.A.dylib libobjc.dylib\n";
+ };
830F2AB60D739AB600392440 /* Run Script (markgc) */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${BUILT_PRODUCTS_DIR}/markgc\" -p \"${BUILT_PRODUCTS_DIR}/libobjc.A.dylib\"\n";
+ shellScript = "if [ ${NATIVE_ARCH} = ${NATIVE_ARCH_32_BIT} -o ${NATIVE_ARCH} = ${NATIVE_ARCH_64_BIT} -o ${NATIVE_ARCH} = ${NATIVE_ARCH_ACTUAL} ]; then\n \"${BUILT_PRODUCTS_DIR}/markgc\" -p \"${BUILT_PRODUCTS_DIR}/libobjc.A.dylib\"\nelse\n echo \"Skipping markgc for cross compile.\"\nfi";
};
830F2AFA0D73BC5800392440 /* Run Script (symlink) */ = {
isa = PBXShellScriptBuildPhase;
shellPath = /bin/sh;
shellScript = "cd \"${INSTALL_DIR}\"\n/bin/ln -s libobjc.A.dylib libobjc.dylib\n";
};
+ 83E50D260FF19E8200D74C19 /* Run Script (symlink) */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script (symlink)";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "cd \"${INSTALL_DIR}\"\n/bin/ln -s libobjc.A.dylib libobjc.dylib\n";
+ };
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 39023F9812F09D0400E1426D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 39023F9912F09D0400E1426D /* hashtable2.m in Sources */,
+ 39023F9A12F09D0400E1426D /* maptable.m in Sources */,
+ 39023F9B12F09D0400E1426D /* objc-auto.m in Sources */,
+ 39023F9C12F09D0400E1426D /* objc-cache.m in Sources */,
+ 39023F9D12F09D0400E1426D /* objc-class-old.m in Sources */,
+ 39023F9E12F09D0400E1426D /* objc-class.m in Sources */,
+ 39023F9F12F09D0400E1426D /* objc-errors.m in Sources */,
+ 39023FA012F09D0400E1426D /* objc-exception.m in Sources */,
+ 39023FA112F09D0400E1426D /* objc-file.mm in Sources */,
+ 39023FA212F09D0400E1426D /* objc-initialize.m in Sources */,
+ 39023FA312F09D0400E1426D /* objc-layout.m in Sources */,
+ 39023FA412F09D0400E1426D /* objc-load.m in Sources */,
+ 39023FA512F09D0400E1426D /* objc-loadmethod.m in Sources */,
+ 39023FA612F09D0400E1426D /* objc-lockdebug.m in Sources */,
+ 39023FA712F09D0400E1426D /* objc-rtp.m in Sources */,
+ 39023FA812F09D0400E1426D /* objc-runtime-new.mm in Sources */,
+ 39023FA912F09D0400E1426D /* objc-runtime-old.m in Sources */,
+ 39023FAA12F09D0400E1426D /* objc-runtime.m in Sources */,
+ 39023FAB12F09D0400E1426D /* objc-sel-set.m in Sources */,
+ 39023FAC12F09D0400E1426D /* objc-sel.mm in Sources */,
+ 39023FAD12F09D0400E1426D /* objc-sync.m in Sources */,
+ 39023FAE12F09D0400E1426D /* objc-typeencoding.m in Sources */,
+ 39023FAF12F09D0400E1426D /* Object.m in Sources */,
+ 39023FB012F09D0400E1426D /* Protocol.m in Sources */,
+ 39023FB112F09D0400E1426D /* List.m in Sources */,
+ 39023FB212F09D0400E1426D /* objc-msg-arm.s in Sources */,
+ 39023FB312F09D0400E1426D /* objc-msg-i386.s in Sources */,
+ 39023FB412F09D0400E1426D /* objc-msg-x86_64.s in Sources */,
+ 39023FB512F09D0400E1426D /* objc-auto-i386.s in Sources */,
+ 39023FB612F09D0400E1426D /* objc-accessors.m in Sources */,
+ 39023FB712F09D0400E1426D /* objc-references.mm in Sources */,
+ 39023FB812F09D0400E1426D /* objc-os.m in Sources */,
+ 39023FB912F09D0400E1426D /* objc-probes.d in Sources */,
+ 39023FBA12F09D0400E1426D /* objc-auto-dump.m in Sources */,
+ 39023FBB12F09D0400E1426D /* objc-file-old.m in Sources */,
+ 39023FBC12F09D0400E1426D /* a1a2-blocktramps-i386.s in Sources */,
+ 39023FBD12F09D0400E1426D /* a1a2-blocktramps-x86_64.s in Sources */,
+ 39023FBE12F09D0400E1426D /* a2a3-blocktramps-i386.s in Sources */,
+ 39023FBF12F09D0400E1426D /* a2a3-blocktramps-x86_64.s in Sources */,
+ 39023FC012F09D0400E1426D /* objc-block-trampolines.m in Sources */,
+ 39023FC112F09D0400E1426D /* objc-msg-simulator-i386.s in Sources */,
+ 39023FC212F09D0400E1426D /* objc-sel-table.s in Sources */,
+ 39023FC312F09D0400E1426D /* a1a2-blocktramps-arm.s in Sources */,
+ 39023FC412F09D0400E1426D /* a2a3-blocktramps-arm.s in Sources */,
+ 39023FC512F09D0400E1426D /* objc-externalref.m in Sources */,
+ 39ABD72212F0B61800D1054C /* objc-weak.mm in Sources */,
+ 552B2FB912F9D31E00650C0D /* objc-arr.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
830F2AA60D7394D000392440 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 83E50CFC0FF19E8200D74C19 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 83E50D130FF19E8200D74C19 /* Object.m in Sources */,
+ 83E50D140FF19E8200D74C19 /* Protocol.m in Sources */,
+ 83E50D150FF19E8200D74C19 /* List.m in Sources */,
+ 8383A3AC122600FB009290B8 /* a1a2-blocktramps-arm.s in Sources */,
+ 8383A3AD122600FB009290B8 /* a2a3-blocktramps-arm.s in Sources */,
+ 8383A3AE122600FB009290B8 /* hashtable2.m in Sources */,
+ 8383A3AF122600FB009290B8 /* maptable.m in Sources */,
+ 8383A3B0122600FB009290B8 /* objc-accessors.m in Sources */,
+ 8383A3B1122600FB009290B8 /* objc-auto.m in Sources */,
+ 8383A3B2122600FB009290B8 /* objc-auto-dump.m in Sources */,
+ 8383A3B3122600FB009290B8 /* objc-block-trampolines.m in Sources */,
+ 8383A3B4122600FB009290B8 /* objc-cache.m in Sources */,
+ 8383A3B5122600FB009290B8 /* objc-class-old.m in Sources */,
+ 8383A3B6122600FB009290B8 /* objc-class.m in Sources */,
+ 8383A3B7122600FB009290B8 /* objc-errors.m in Sources */,
+ 8383A3B8122600FB009290B8 /* objc-exception.m in Sources */,
+ 8383A3B9122600FB009290B8 /* objc-file.mm in Sources */,
+ 8383A3BA122600FB009290B8 /* objc-file-old.m in Sources */,
+ 8383A3BB122600FB009290B8 /* objc-initialize.m in Sources */,
+ 8383A3BC122600FB009290B8 /* objc-layout.m in Sources */,
+ 8383A3BD122600FB009290B8 /* objc-load.m in Sources */,
+ 8383A3BE122600FB009290B8 /* objc-loadmethod.m in Sources */,
+ 8383A3BF122600FB009290B8 /* objc-lockdebug.m in Sources */,
+ 8383A3C0122600FB009290B8 /* objc-os.m in Sources */,
+ 8383A3C1122600FB009290B8 /* objc-references.mm in Sources */,
+ 8383A3C2122600FB009290B8 /* objc-rtp.m in Sources */,
+ 8383A3C3122600FB009290B8 /* objc-runtime-new.mm in Sources */,
+ 8383A3C4122600FB009290B8 /* objc-runtime-old.m in Sources */,
+ 8383A3C5122600FB009290B8 /* objc-runtime.m in Sources */,
+ 8383A3C6122600FB009290B8 /* objc-sel-set.m in Sources */,
+ 8383A3C7122600FB009290B8 /* objc-sel-table.s in Sources */,
+ 8383A3C8122600FB009290B8 /* objc-sel.mm in Sources */,
+ 8383A3C9122600FB009290B8 /* objc-sync.m in Sources */,
+ 8383A3CA122600FB009290B8 /* objc-typeencoding.m in Sources */,
+ 8383A3CB122600FB009290B8 /* a1a2-blocktramps-i386.s in Sources */,
+ 8383A3CC122600FB009290B8 /* a1a2-blocktramps-x86_64.s in Sources */,
+ 8383A3CD122600FB009290B8 /* a2a3-blocktramps-i386.s in Sources */,
+ 8383A3CE122600FB009290B8 /* a2a3-blocktramps-x86_64.s in Sources */,
+ 8383A3CF122600FB009290B8 /* objc-auto-i386.s in Sources */,
+ 8383A3D0122600FB009290B8 /* objc-msg-arm.s in Sources */,
+ 8383A3D1122600FB009290B8 /* objc-msg-i386.s in Sources */,
+ 8383A3D2122600FB009290B8 /* objc-msg-simulator-i386.s in Sources */,
+ 8383A3D3122600FB009290B8 /* objc-msg-x86_64.s in Sources */,
+ 8383A3D4122600FB009290B8 /* objc-probes.d in Sources */,
+ 8383A3DC1226291C009290B8 /* objc-externalref.m in Sources */,
+ 39ABD72612F0B61800D1054C /* objc-weak.mm in Sources */,
+ 552B2FB812F9D31E00650C0D /* objc-arr.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
D2AAC0610554660B00DB518D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
838485F50D6D68A200CEA253 /* objc-class.m in Sources */,
838485F70D6D68A200CEA253 /* objc-errors.m in Sources */,
838485F90D6D68A200CEA253 /* objc-exception.m in Sources */,
- 838485FA0D6D68A200CEA253 /* objc-file.m in Sources */,
+ 838485FA0D6D68A200CEA253 /* objc-file.mm in Sources */,
838485FC0D6D68A200CEA253 /* objc-initialize.m in Sources */,
838485FD0D6D68A200CEA253 /* objc-layout.m in Sources */,
838485FF0D6D68A200CEA253 /* objc-load.m in Sources */,
838486010D6D68A200CEA253 /* objc-loadmethod.m in Sources */,
838486020D6D68A200CEA253 /* objc-lockdebug.m in Sources */,
838486060D6D68A200CEA253 /* objc-rtp.m in Sources */,
- 838486080D6D68A200CEA253 /* objc-runtime-new.m in Sources */,
+ 838486080D6D68A200CEA253 /* objc-runtime-new.mm in Sources */,
838486090D6D68A200CEA253 /* objc-runtime-old.m in Sources */,
8384860B0D6D68A200CEA253 /* objc-runtime.m in Sources */,
8384860D0D6D68A200CEA253 /* objc-sel-set.m in Sources */,
838486250D6D68F000CEA253 /* List.m in Sources */,
830F2A740D737FB800392440 /* objc-msg-arm.s in Sources */,
830F2A750D737FB900392440 /* objc-msg-i386.s in Sources */,
- 830F2A760D737FB900392440 /* objc-msg-ppc.s in Sources */,
830F2A7D0D737FBB00392440 /* objc-msg-x86_64.s in Sources */,
830F2A890D73819A00392440 /* objc-auto-i386.s in Sources */,
- 830F2A8A0D73819A00392440 /* objc-auto-ppc.s in Sources */,
- 830F2A8B0D73819A00392440 /* objc-auto-ppc64.s in Sources */,
830F2A950D73876100392440 /* objc-accessors.m in Sources */,
- 838B85700DB8915A0029D21A /* objc-sel-table.s in Sources */,
393CEAC00DC69E3E000B69DE /* objc-references.mm in Sources */,
831C85D60E10CF850066E64C /* objc-os.m in Sources */,
87BB4EA70EC39854005D08E1 /* objc-probes.d in Sources */,
BC07A0110EF72D9C0014EC61 /* objc-auto-dump.m in Sources */,
+ 83BE02E40FCCB23400661494 /* objc-file-old.m in Sources */,
+ E8923DA1116AB2820071B552 /* a1a2-blocktramps-i386.s in Sources */,
+ E8923DA2116AB2820071B552 /* a1a2-blocktramps-x86_64.s in Sources */,
+ E8923DA3116AB2820071B552 /* a2a3-blocktramps-i386.s in Sources */,
+ E8923DA4116AB2820071B552 /* a2a3-blocktramps-x86_64.s in Sources */,
+ E8923DA5116AB2820071B552 /* objc-block-trampolines.m in Sources */,
+ 83B1A8BE0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s in Sources */,
+ 83EB007B121C9EC200B92C16 /* objc-sel-table.s in Sources */,
+ 8383A3A3122600E9009290B8 /* a1a2-blocktramps-arm.s in Sources */,
+ 8383A3A4122600E9009290B8 /* a2a3-blocktramps-arm.s in Sources */,
+ 399BC72E1224831B007FBDF0 /* objc-externalref.m in Sources */,
+ 39ABD72412F0B61800D1054C /* objc-weak.mm in Sources */,
+ 552B2FB712F9D31E00650C0D /* objc-arr.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
- 830F2AB00D7394E900392440 /* PBXTargetDependency */ = {
+ 39023F7412F09D0400E1426D /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 830F2AA80D7394D000392440 /* markgc */;
+ targetProxy = 39023F7512F09D0400E1426D /* PBXContainerItemProxy */;
+ };
+ 835720F60F8BF8EE00BD4FAD /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 830F2AA80D7394D000392440 /* markgc */;
- targetProxy = 830F2AAF0D7394E900392440 /* PBXContainerItemProxy */;
+ targetProxy = 835720F50F8BF8EE00BD4FAD /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
1DEB914B08733D8E0010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
COPY_PHASE_STRIP = NO;
- DYLIB_CURRENT_VERSION = 227;
+ DYLIB_CURRENT_VERSION = 228;
EXECUTABLE_PREFIX = lib;
GCC_CW_ASM_SYNTAX = NO;
GCC_DYNAMIC_NO_PIC = NO;
- GCC_FAST_OBJC_DISPATCH = NO;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_THREADSAFE_STATICS = NO;
INSTALL_PATH = /usr/lib;
ORDER_FILE = libobjc.order;
OTHER_CFLAGS = "-fdollars-in-identifiers";
- "OTHER_LDFLAGS[arch=i386]" = "-lauto";
- "OTHER_LDFLAGS[arch=ppc64]" = (
- "-lauto",
- "-lstdc++",
- );
- "OTHER_LDFLAGS[arch=ppc]" = "-lauto";
- "OTHER_LDFLAGS[arch=x86_64]" = (
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = "-lstdc++";
+ "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "-l_BUILD_objc-simulator_TARGET_INSTEAD";
+ "OTHER_LDFLAGS[sdk=macosx10.7]" = (
+ "-lCrashReporterClient",
"-lauto",
- "-lstdc++",
+ "-lc++",
+ "-lc++abi",
);
PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc;
PRODUCT_NAME = objc.A;
1DEB914C08733D8E0010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- DYLIB_CURRENT_VERSION = 227;
+ DYLIB_CURRENT_VERSION = 228;
EXECUTABLE_PREFIX = lib;
GCC_CW_ASM_SYNTAX = NO;
- GCC_FAST_OBJC_DISPATCH = NO;
- GCC_MODEL_TUNING = G5;
GCC_THREADSAFE_STATICS = NO;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
HEADER_SEARCH_PATHS = (
INSTALL_PATH = /usr/lib;
ORDER_FILE = libobjc.order;
OTHER_CFLAGS = "-fdollars-in-identifiers";
- "OTHER_LDFLAGS[arch=i386]" = "-lauto";
- "OTHER_LDFLAGS[arch=ppc64]" = (
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = "-lstdc++";
+ "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "-l_BUILD_objc-simulator_TARGET_INSTEAD";
+ "OTHER_LDFLAGS[sdk=macosx10.7]" = (
+ "-lCrashReporterClient",
"-lauto",
- "-lstdc++",
- );
- "OTHER_LDFLAGS[arch=ppc]" = "-lauto";
- "OTHER_LDFLAGS[arch=x86_64]" = (
- "-lauto",
- "-lstdc++",
+ "-lc++",
+ "-lc++abi",
);
PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc;
PRODUCT_NAME = objc.A;
1DEB914F08733D8E0010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = (
- i386,
- x86_64,
- ppc,
- );
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ "GCC_VERSION[sdk=macosx10.7][arch=*]" = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- ONLY_ACTIVE_ARCH = NO;
+ "OTHER_CPLUSPLUSFLAGS[sdk=macosx10.7][arch=*]" = (
+ "-stdlib=libc++",
+ "$(OTHER_CFLAGS)",
+ );
PREBINDING = NO;
- SDKROOT = "";
STANDARD_C_PLUS_PLUS_LIBRARY_TYPE = dynamic;
- UNEXPORTED_SYMBOLS_FILE = unexported_symbols;
+ VALID_ARCHS = "armv6 armv7 i386 x86_64";
WARNING_CFLAGS = (
"-Wall",
"-Wno-four-char-constants",
1DEB915008733D8E0010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = (
- i386,
- x86_64,
- ppc,
- );
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ "GCC_VERSION[sdk=macosx10.7][arch=*]" = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- ONLY_ACTIVE_ARCH = NO;
+ "OTHER_CPLUSPLUSFLAGS[sdk=macosx10.7][arch=*]" = (
+ "-stdlib=libc++",
+ "$(OTHER_CFLAGS)",
+ );
PREBINDING = NO;
- SDKROOT = "";
STANDARD_C_PLUS_PLUS_LIBRARY_TYPE = dynamic;
+ VALID_ARCHS = "armv6 armv7 i386 x86_64";
WARNING_CFLAGS = (
"-Wall",
"-Wno-four-char-constants",
};
name = Release;
};
+ 39023FCC12F09D0400E1426D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ COPY_PHASE_STRIP = NO;
+ DYLIB_CURRENT_VERSION = 227;
+ EXECUTABLE_PREFIX = lib;
+ GCC_CW_ASM_SYNTAX = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_THREADSAFE_STATICS = NO;
+ GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
+ HEADER_SEARCH_PATHS = (
+ "$(DSTROOT)/usr/include/**",
+ "$(DSTROOT)/usr/local/include/**",
+ "$(CONFIGURATION_BUILD_DIR)/usr/include/**",
+ "$(CONFIGURATION_BUILD_DIR)/usr/local/include/**",
+ );
+ INSTALL_PATH = /usr/lib;
+ ORDER_FILE = libobjc.order;
+ OTHER_CFLAGS = "-fdollars-in-identifiers";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = "-lstdc++";
+ "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "-l_BUILD_objc-simulator_TARGET_INSTEAD";
+ "OTHER_LDFLAGS[sdk=macosx10.7]" = (
+ "-lCrashReporterClient",
+ "-lauto",
+ "-lc++",
+ "-lc++abi",
+ );
+ PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc;
+ PRODUCT_NAME = objc.A;
+ PUBLIC_HEADERS_FOLDER_PATH = /usr/include/objc;
+ SDKROOT = iphoneos5.0.internal;
+ UNEXPORTED_SYMBOLS_FILE = unexported_symbols;
+ };
+ name = Debug;
+ };
+ 39023FCD12F09D0400E1426D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ DYLIB_CURRENT_VERSION = 227;
+ EXECUTABLE_PREFIX = lib;
+ GCC_CW_ASM_SYNTAX = NO;
+ GCC_THREADSAFE_STATICS = NO;
+ GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
+ HEADER_SEARCH_PATHS = (
+ "$(DSTROOT)/usr/include/**",
+ "$(DSTROOT)/usr/local/include/**",
+ "$(CONFIGURATION_BUILD_DIR)/usr/include/**",
+ "$(CONFIGURATION_BUILD_DIR)/usr/local/include/**",
+ );
+ INSTALL_PATH = /usr/lib;
+ ORDER_FILE = libobjc.order;
+ OTHER_CFLAGS = "-fdollars-in-identifiers";
+ "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = "-lstdc++";
+ "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "-l_BUILD_objc-simulator_TARGET_INSTEAD";
+ "OTHER_LDFLAGS[sdk=macosx10.7]" = (
+ "-lCrashReporterClient",
+ "-lauto",
+ "-lc++",
+ "-lc++abi",
+ );
+ PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc;
+ PRODUCT_NAME = objc.A;
+ PUBLIC_HEADERS_FOLDER_PATH = /usr/include/objc;
+ UNEXPORTED_SYMBOLS_FILE = unexported_symbols;
+ };
+ name = Release;
+ };
830F2AAB0D7394D100392440 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- ARCHS = "$(NATIVE_ARCH)";
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- ARCHS = "$(NATIVE_ARCH)";
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
INSTALL_PATH = /usr/local/bin;
};
name = Release;
};
+ 83E2799B117F76E000452546 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ PRODUCT_NAME = "objc-selopt";
+ };
+ name = Debug;
+ };
+ 83E2799C117F76E000452546 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ PRODUCT_NAME = "objc-selopt";
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ 83E50D280FF19E8200D74C19 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 83E50D2B0FF19E9E00D74C19 /* IndigoSDK.xcconfig */;
+ buildSettings = {
+ ARCHS = i386;
+ COPY_PHASE_STRIP = NO;
+ DYLIB_CURRENT_VERSION = 227;
+ EXECUTABLE_PREFIX = lib;
+ GCC_CW_ASM_SYNTAX = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_THREADSAFE_STATICS = NO;
+ GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
+ HEADER_SEARCH_PATHS = (
+ "$(DSTROOT)/$(INDIGO_INSTALL_PATH_PREFIX)/usr/include/**",
+ "$(DSTROOT)/$(INDIGO_INSTALL_PATH_PREFIX)/usr/local/include/**",
+ "$(CONFIGURATION_BUILD_DIR)/$(INDIGO_INSTALL_PATH_PREFIX)/usr/include/**",
+ "$(CONFIGURATION_BUILD_DIR)/$(INDIGO_INSTALL_PATH_PREFIX)/usr/local/include/**",
+ );
+ INSTALL_PATH = "$(INDIGO_INSTALL_PATH_PREFIX)/usr/lib";
+ LD_DYLIB_INSTALL_NAME_mh_dylib = "/usr/lib/$(EXECUTABLE_PATH)";
+ OTHER_CFLAGS = (
+ "-fobjc-legacy-dispatch",
+ "-fobjc-abi-version=2",
+ "-fdollars-in-identifiers",
+ );
+ OTHER_LDFLAGS = "-lstdc++";
+ "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = (
+ "-lc++",
+ "-lc++abi",
+ );
+ PRIVATE_HEADERS_FOLDER_PATH = "$(INDIGO_INSTALL_PATH_PREFIX)/usr/local/include/objc";
+ PRODUCT_NAME = objc.A;
+ PUBLIC_HEADERS_FOLDER_PATH = "$(INDIGO_INSTALL_PATH_PREFIX)/usr/include/objc";
+ UNEXPORTED_SYMBOLS_FILE = unexported_symbols;
+ VALID_ARCHS = i386;
+ };
+ name = Debug;
+ };
+ 83E50D290FF19E8200D74C19 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 83E50D2B0FF19E9E00D74C19 /* IndigoSDK.xcconfig */;
+ buildSettings = {
+ ARCHS = i386;
+ DYLIB_CURRENT_VERSION = 227;
+ EXECUTABLE_PREFIX = lib;
+ GCC_CW_ASM_SYNTAX = NO;
+ GCC_THREADSAFE_STATICS = NO;
+ GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
+ HEADER_SEARCH_PATHS = (
+ "$(DSTROOT)/$(INDIGO_INSTALL_PATH_PREFIX)/usr/include/**",
+ "$(DSTROOT)/$(INDIGO_INSTALL_PATH_PREFIX)/usr/local/include/**",
+ "$(CONFIGURATION_BUILD_DIR)/$(INDIGO_INSTALL_PATH_PREFIX)/usr/include/**",
+ "$(CONFIGURATION_BUILD_DIR)/$(INDIGO_INSTALL_PATH_PREFIX)/usr/local/include/**",
+ );
+ INSTALL_PATH = "$(INDIGO_INSTALL_PATH_PREFIX)/usr/lib";
+ LD_DYLIB_INSTALL_NAME_mh_dylib = "/usr/lib/$(EXECUTABLE_PATH)";
+ OTHER_CFLAGS = (
+ "-fobjc-legacy-dispatch",
+ "-fobjc-abi-version=2",
+ "-fdollars-in-identifiers",
+ );
+ OTHER_LDFLAGS = "-lstdc++";
+ "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = (
+ "-lc++",
+ "-lc++abi",
+ );
+ PRIVATE_HEADERS_FOLDER_PATH = "$(INDIGO_INSTALL_PATH_PREFIX)/usr/local/include/objc";
+ PRODUCT_NAME = objc.A;
+ PUBLIC_HEADERS_FOLDER_PATH = "$(INDIGO_INSTALL_PATH_PREFIX)/usr/include/objc";
+ UNEXPORTED_SYMBOLS_FILE = unexported_symbols;
+ VALID_ARCHS = i386;
+ };
+ name = Release;
+ };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 39023FCB12F09D0400E1426D /* Build configuration list for PBXNativeTarget "objc-device" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 39023FCC12F09D0400E1426D /* Debug */,
+ 39023FCD12F09D0400E1426D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
830F2AAE0D7394D600392440 /* Build configuration list for PBXNativeTarget "markgc" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 83E2799E117F770D00452546 /* Build configuration list for PBXAggregateTarget "objc-selopt" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 83E2799B117F76E000452546 /* Debug */,
+ 83E2799C117F76E000452546 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 83E50D270FF19E8200D74C19 /* Build configuration list for PBXNativeTarget "objc-simulator" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 83E50D280FF19E8200D74C19 /* Debug */,
+ 83E50D290FF19E8200D74C19 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
/* End XCConfigurationList section */
};
rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
--- /dev/null
+@echo off
+
+echo prebuild: installing headers
+xcopy /Y "%ProjectDir%runtime\objc.h" "%DSTROOT%\AppleInternal\include\objc\"
+xcopy /Y "%ProjectDir%runtime\objc-api.h" "%DSTROOT%\AppleInternal\include\objc\"
+xcopy /Y "%ProjectDir%runtime\objc-auto.h" "%DSTROOT%\AppleInternal\include\objc\"
+xcopy /Y "%ProjectDir%runtime\objc-exception.h" "%DSTROOT%\AppleInternal\include\objc\"
+xcopy /Y "%ProjectDir%runtime\message.h" "%DSTROOT%\AppleInternal\include\objc\"
+xcopy /Y "%ProjectDir%runtime\runtime.h" "%DSTROOT%\AppleInternal\include\objc\"
+xcopy /Y "%ProjectDir%runtime\hashtable.h" "%DSTROOT%\AppleInternal\include\objc\"
+xcopy /Y "%ProjectDir%runtime\hashtable2.h" "%DSTROOT%\AppleInternal\include\objc\"
+xcopy /Y "%ProjectDir%runtime\maptable.h" "%DSTROOT%\AppleInternal\include\objc\"
+
+echo prebuild: setting version
+version
TestsDir="test/"
cd "$ObjcDir"
# <rdar://problem/6456031> ER: option to not require extra privileges (-nosudo or somesuch)
-Buildit="/Network/Servers/xs1/release/bin/buildit -rootsDirectory ${RootsDirectory} -arch i386 -arch ppc -arch x86_64 -project objc4 ."
+Buildit="/Network/Servers/xs1/release/bin/buildit -rootsDirectory ${RootsDirectory} -arch i386 -arch x86_64 -project objc4 ."
echo Sudoing for buildit:
sudo $Buildit
XIT=$?
if [[ $XIT == 0 ]]; then
cd "$TestsDir"
- ObjcRootPath="$RootsDirectory/objc4.roots/objc4~dst/usr/lib/libobjc.A.dylib"
- make HALT=YES OBJC_LIB="$ObjcRootPath"
- XIT=$?
- make clean
+ #ObjcRootPath="$RootsDirectory/objc4.roots/objc4~dst/usr/lib/libobjc.A.dylib"
+ #ObjcRootHeaders="$RootsDirectory/objc4.roots/objc4~dst/usr/include/"
+ #make HALT=YES OBJC_LIB="$ObjcRootPath" OTHER_CFLAGS="-isystem $ObjcRootHeaders"
+ perl test.pl ARCHS=x86_64 OBJC_ROOT="$RootsDirectory/objc4.roots/"
+ XIT=`expr $XIT \| $?`
+ perl test.pl ARCHS=i386 OBJC_ROOT="$RootsDirectory/objc4.roots/"
+ XIT=`expr $XIT \| $?`
+ perl test.pl ARCHS=x86_64 GUARDMALLOC=YES OBJC_ROOT="$RootsDirectory/objc4.roots/"
+ XIT=`expr $XIT \| $?`
+ perl test.pl ARCHS=i386 GUARDMALLOC=YES OBJC_ROOT="$RootsDirectory/objc4.roots/"
+ XIT=`expr $XIT \| $?`
+ perl test.pl clean
fi
cd "$StartingDir"
exit $XIT
\ No newline at end of file
#import <objc/objc.h>
#import <stddef.h>
+__BEGIN_DECLS
+
// Called under non-GC for retain or copy attributed properties
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy);
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic);
+// GC-specific accessors.
+void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy);
+id objc_getProperty_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic);
+
+// Non-GC accessors.
+void objc_setProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy);
+id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic);
+
// Called under GC by compiler for copying structures containing objects or other strong pointers when
// the destination memory is not known to be stack local memory.
// Called to read instance variable structures (or other non-word sized entities) atomically
id object_getProperty_byref(id object, SEL _cmd, ptrdiff_t offset);
void object_setProperty_byref(id object, SEL _cmd, id value, ptrdiff_t offset);
+__END_DECLS
+
#endif
- (id)mutableCopyWithZone:(void *)zone;
@end
-@interface __NSRetained
-- (id)retain;
-- (oneway void)release;
-- (id)autorelease;
-@end
-
typedef uintptr_t spin_lock_t;
extern void _spin_lock(spin_lock_t *lockp);
#define GOODHASH(x) (((long)x >> 5) & GOODMASK)
static spin_lock_t PropertyLocks[1 << GOODPOWER] = { 0 };
-id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
- if (UseGC) {
- return *(id*) ((char*)self + offset);
- }
-
+PRIVATE_EXTERN id objc_getProperty_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
+ return *(id*) ((char*)self + offset);
+}
+
+PRIVATE_EXTERN id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
// Atomic retain release world
spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
_spin_lock(slotlock);
- id value = [*slot retain];
+ id value = objc_retain(*slot);
_spin_unlock(slotlock);
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
- return [value autorelease];
+ return objc_autoreleaseReturnValue(value);
+}
+
+id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
+ return
+#if SUPPORT_GC
+ (UseGC ? objc_getProperty_gc : objc_getProperty_non_gc)
+#else
+ objc_getProperty_non_gc
+#endif
+ (self, _cmd, offset, atomic);
}
enum { OBJC_PROPERTY_RETAIN = 0, OBJC_PROPERTY_COPY = 1, OBJC_PROPERTY_MUTABLECOPY = 2 };
-void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) {
- if (UseGC) {
- if (shouldCopy) {
- newValue = (shouldCopy == OBJC_PROPERTY_MUTABLECOPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]);
- }
- objc_assign_ivar_internal(newValue, self, offset);
- return;
+#if SUPPORT_GC
+PRIVATE_EXTERN void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) {
+ if (shouldCopy) {
+ newValue = (shouldCopy == OBJC_PROPERTY_MUTABLECOPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]);
}
+ objc_assign_ivar_gc(newValue, self, offset);
+}
+#endif
+PRIVATE_EXTERN void objc_setProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) {
// Retain release world
id oldValue, *slot = (id*) ((char*)self + offset);
if (shouldCopy) {
newValue = (shouldCopy == OBJC_PROPERTY_MUTABLECOPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]);
} else {
- newValue = [newValue retain];
+ newValue = objc_retain(newValue);
}
if (!atomic) {
_spin_unlock(slotlock);
}
- [oldValue release];
+ objc_release(oldValue);
+}
+
+void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) {
+#if SUPPORT_GC
+ (UseGC ? objc_setProperty_gc : objc_setProperty_non_gc)
+#else
+ objc_setProperty_non_gc
+#endif
+ (self, _cmd, offset, newValue, atomic, shouldCopy);
}
_spin_lock(lockfirst);
if (locksecond) _spin_lock(locksecond);
}
+#if SUPPORT_GC
if (UseGC && hasStrong) {
auto_zone_write_barrier_memmove(gc_zone, dest, src, size);
- }
- else {
+ } else
+#endif
+ {
memmove(dest, src, size);
}
if (atomic) {
* @APPLE_LICENSE_HEADER_END@
*/
-#ifdef __i386__
+#include <TargetConditionals.h>
+
+#if __i386__ && !TARGET_OS_IPHONE && !TARGET_OS_WIN32
/*
This file defines the non-GC variants of objc_assign_* on a dedicated
.align 12 // align to page boundary
-LNonGCAssigns$Begin:
-
// id objc_assign_ivar(id value, id dest, ptrdiff_t offset);
.globl _objc_assign_ivar
_objc_assign_ivar:
leave
ret
+// id objc_assign_threadlocal(id value, id *dest);
+.globl _objc_assign_threadlocal
+_objc_assign_threadlocal:
+ pushl %ebp
+ movl %esp,%ebp
+ movl 0x08(%ebp),%eax // value
+ movl 0x0c(%ebp),%edx // dest
+ movl %eax,(%edx) // return (*dest = value);
+ leave
+ ret
+
// As of OS X 10.5, objc_assign_strongCast_non_gc is identical to
// objc_assign_global_non_gc.
leave
ret
-LNonGCAssigns$End:
-
// Claim the remainder of the page.
-.set L$set$assignsSize,LNonGCAssigns$End-LNonGCAssigns$Begin
-.space 4096-L$set$assignsSize
+.align 12, 0
#endif
+++ /dev/null
-/*
- * Copyright (c) 2004, 2006 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifdef __ppc__
-
- ;
- ; This section includes declarations of routines that will be used to populate
- ; the runtime pages during auto initialization. Each wb_routine definition
- ; creates an absolute branch into the rtp plus a non-gc version of code for
- ; non collecting apps. Note - the blr is necessary at the end of the non-gc
- ; routine for code copying to behave correctly.
- ;
-
-#undef OBJC_ASM
-#define OBJC_ASM
-#include "objc-rtp.h"
-
- .macro wb_routine
- .globl _$0 ; primary entry name
-; .abs _abs_$0,kRTAddress_$0
-_$0: ; primary entry point
- ba $1 ; branch to runtime page
-
- .private_extern _$0_non_gc ; non_gc entry point name
-_$0_non_gc: ; non_gc entry point
- .endmacro
-
- .text
-
-// note - unfortunately ba does not accept constant expressions
-
- ; non-gc routines
-
- ; id objc_assign_strongCast(id value, id *dest)
- wb_routine objc_assign_strongCast,0xfffefea0
- stw r3,0(r4) ; store value at dest
- blr ; return
-
- ; id objc_assign_global(id value, id *dest)
- wb_routine objc_assign_global,0xfffefeb0
- stw r3,0(r4) ; store value at dest
- blr ; return
-
- ; id objc_assign_ivar(id value, id dest, unsigned int offset)
- wb_routine objc_assign_ivar,0xfffefec0
- stwx r3,r4,r5 ; store value at (dest+offset)
- blr ; return
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2004 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifdef __ppc64__
-
-#warning ppc64 version needs to be implemented.
-
-#endif
* @APPLE_LICENSE_HEADER_END@
*/
+#error not currently used
+
#ifdef __x86_64__
/*
.data
.align 12 // align to page boundary
-LNonGCAssigns$Begin:
-
// id objc_assign_ivar(id value, id dest, ptrdiff_t offset);
.globl _objc_assign_ivar
_objc_assign_ivar:
leave
ret
+// id objc_assign_threadlocal(id value, id *dest);
+.globl _objc_assign_threadlocal
+_objc_assign_threadlocal:
+ pushq %rbp
+ movq %rsp,%rbp
+ movq %rdi,(%rsi) // *(dest = value);
+ movq %rdi,%rax // return value;
+ leave
+ ret
+
// As of OS X 10.5, objc_assign_strongCast_non_gc is identical to
// objc_assign_global_non_gc.
leave
ret
-LNonGCAssigns$End:
-
// Claim the remainder of the page.
-.set L$set$assignsSize,LNonGCAssigns$End-LNonGCAssigns$Begin
-.space 4096-L$set$assignsSize
+.align 12, 0
#endif
*
* @APPLE_LICENSE_HEADER_END@
*/
-
-#ifdef __arm__
-
/********************************************************************
*
* objc-msg-arm.s - ARM code to support objc messaging
*
********************************************************************/
+#ifdef __arm__
+
+#include <arm/arch.h>
#ifdef ARM11
#define MOVE cpy
#define MOVENE movne
#endif
-#ifdef VFP_ARGS
-#define SAVE_VFP fstmfdd sp!, {d0-d7}
-#define RESTORE_VFP fldmfdd sp!, {d0-d7}
-#else
-#define SAVE_VFP /* empty */
-#define RESTORE_VFP /* empty */
+#ifdef _ARM_ARCH_7
+#define THUMB 1
#endif
+.syntax unified
#if defined(__DYNAMIC__)
#define MI_EXTERN(var) \
.non_lazy_symbol_pointer ;\
-L ## var ## __non_lazy_ptr: ;\
+L ## var ## __non_lazy_ptr: ;\
.indirect_symbol var ;\
.long 0
#else
.globl var
#endif
-#if defined(__DYNAMIC__)
+
+#if defined(__DYNAMIC__) && defined(THUMB)
+#define MI_GET_ADDRESS(reg,var) \
+ ldr reg, 4f ;\
+3: add reg, pc, reg ;\
+ ldr reg, [reg] ;\
+ b 5f ;\
+4: .long L ## var ## __non_lazy_ptr - (3b + 4) ;\
+5:
+#elif defined(__DYNAMIC__)
#define MI_GET_ADDRESS(reg,var) \
ldr reg, 4f ;\
3: ldr reg, [pc, reg] ;\
b 5f ;\
4: .long L ## var ## __non_lazy_ptr - (3b + 8) ;\
5:
-#else
+#else
#define MI_GET_ADDRESS(reg,var) \
ldr reg, 3f ;\
b 4f ;\
4:
#endif
+
#if defined(__DYNAMIC__)
-#define MI_BRANCH_EXTERNAL(var) \
+#define MI_BRANCH_EXTERNAL(var) \
MI_GET_ADDRESS(ip, var) ;\
bx ip
#else
-#define MI_BRANCH_EXTERNAL(var) ;\
+#define MI_BRANCH_EXTERNAL(var) \
b var
#endif
-#if defined(__DYNAMIC__)
+#if defined(__DYNAMIC__) && defined(THUMB)
+#define MI_CALL_EXTERNAL(var) \
+ MI_GET_ADDRESS(ip,var) ;\
+ blx ip
+#elif defined(__DYNAMIC__)
#define MI_CALL_EXTERNAL(var) \
MI_GET_ADDRESS(ip,var) ;\
MOVE lr, pc ;\
bx ip
#else
-#define MI_CALL_EXTERNAL(var) \
+#define MI_CALL_EXTERNAL(var) \
bl var
#endif
# in the cache for dispatching. The labels surround the asm code
# that do cache lookups. The tables are zero-terminated.
.data
-.globl _objc_entryPoints
+.private_extern _objc_entryPoints
_objc_entryPoints:
.long __cache_getImp
.long __cache_getMethod
.long _objc_msgSend
+ .long _objc_msgSend_noarg
.long _objc_msgSend_stret
.long _objc_msgSendSuper
.long _objc_msgSendSuper_stret
+ .long _objc_msgSendSuper2
+ .long _objc_msgSendSuper2_stret
.long 0
.data
-.globl _objc_exitPoints
+.private_extern _objc_exitPoints
_objc_exitPoints:
.long LGetImpExit
.long LGetMethodExit
.long LMsgSendExit
+ .long LMsgSendNoArgExit
.long LMsgSendStretExit
.long LMsgSendSuperExit
.long LMsgSendSuperStretExit
+ .long LMsgSendSuper2Exit
+ .long LMsgSendSuper2StretExit
.long 0
/* Selected field offsets in class structure */
.set ISA, 0
-#if __OBJC2__
+.set SUPERCLASS, 4
.set CACHE, 8
-#else
-.set CACHE, 32
-#endif
/* Method descriptor */
.set METHOD_NAME, 0
/* Cache header */
.set MASK, 0
+.set NEGMASK, -8
.set OCCUPIED, 4
.set BUCKETS, 8 /* variable length array */
.macro ENTRY /* name */
.text
- .align 2
+#ifdef THUMB
+ .thumb
+#endif
+ .align 2
.globl _$0
+#ifdef THUMB
+ .thumb_func
+#endif
_$0:
.endmacro
+.macro STATIC_ENTRY /*name*/
+ .text
+#ifdef THUMB
+ .thumb
+#endif
+ .align 2
+ .private_extern _$0
+#ifdef THUMB
+ .thumb_func
+#endif
+_$0:
+.endmacro
+
+
#####################################################################
#
# END_ENTRY functionName
#####################################################################
#
-# CacheLookup selectorRegister, cacheMissLabel
+# CacheLookup selectorRegister, classReg, cacheMissLabel
#
# Locate the implementation for a selector in a class method cache.
#
# Takes:
-# v1 = class whose cache is to be searched
# $0 = register containing selector (a2 or a3 ONLY)
+# $1 = class whose cache is to be searched
# cacheMissLabel = label to branch to iff method is not cached
#
# Kills:
-# a4, v1, v2, v3, ip
+# a4, $1, r9, ip
#
-# On exit: (found) method triplet in v1, imp in ip
+# On exit: (found) method triplet in $1, imp in ip
# (not found) jumps to cacheMissLabel
#
#####################################################################
-.macro CacheLookup /* selReg, missLabel */
+.macro CacheLookup /* selReg, classReg, missLabel */
- ldr v2, [v1, #CACHE] /* cache = class->cache */
- ldr v3, [v2, #MASK] /* mask = cache->mask */
- add a4, v2, #BUCKETS /* buckets = &cache->buckets */
- and v2, v3, $0, LSR #2 /* index = mask & (sel >> 2) */
+ MOVE r9, $0, LSR #2 /* index = (sel >> 2) */
+ ldr a4, [$1, #CACHE] /* cache = class->cache */
+ add a4, a4, #BUCKETS /* buckets = &cache->buckets */
/* search the cache */
-/* a1=receiver, a2 or a3=sel, v2=index, v3=mask, a4=buckets, v1=method */
+/* a1=receiver, a2 or a3=sel, r9=index, a4=buckets, $1=method */
1:
- ldr v1, [a4, v2, LSL #2] /* method = buckets[index] */
- teq v1, #0 /* if (method == NULL) */
- add v2, v2, #1 /* index++ */
- beq $1 /* goto cacheMissLabel */
-
- ldr ip, [v1, #METHOD_NAME] /* load method->method_name */
+ ldr ip, [a4, #NEGMASK] /* mask = cache->mask */
+ and r9, r9, ip /* index &= mask */
+ ldr $1, [a4, r9, LSL #2] /* method = buckets[index] */
+ teq $1, #0 /* if (method == NULL) */
+ add r9, r9, #1 /* index++ */
+ beq $2 /* goto cacheMissLabel */
+
+ ldr ip, [$1, #METHOD_NAME] /* load method->method_name */
teq $0, ip /* if (method->method_name != sel) */
- and v2, v2, v3 /* index &= mask */
bne 1b /* retry */
-/* cache hit, v1 == method triplet address */
-/* Return triplet in v1 and imp in ip */
- ldr ip, [v1, #METHOD_IMP] /* imp = method->method_imp */
+/* cache hit, $1 == method triplet address */
+/* Return triplet in $1 and imp in ip */
+ ldr ip, [$1, #METHOD_IMP] /* imp = method->method_imp */
.endmacro
* efficient to do the (PIC) lookup once in the caller than repeatedly here.
********************************************************************/
- ENTRY _cache_getMethod
-
-# save registers and load class for CacheLookup
- stmfd sp!, {a4,v1-v3,r7,lr}
- add r7, sp, #16
- MOVE v1, a1
+ STATIC_ENTRY _cache_getMethod
# search the cache
- CacheLookup a2, LGetMethodMiss
+ CacheLookup a2, a1, LGetMethodMiss
-# cache hit, method triplet in v1 and imp in ip
+# cache hit, method triplet in a1 and imp in ip
teq ip, a3 /* check for _objc_msgForward_internal */
+ it eq
MOVEEQ a1, #1 /* return (Method)1 if forward */
- MOVENE a1, v1 /* return triplet if not forward */
- ldmfd sp!, {a4,v1-v3,r7,pc}
+ /* else return triplet (already in a1) */
+ bx lr
LGetMethodMiss:
MOVE a1, #0 /* return nil if cache miss */
- ldmfd sp!, {a4,v1-v3,r7,pc}
+ bx lr
LGetMethodExit:
END_ENTRY _cache_getMethod
* If not found, returns NULL.
********************************************************************/
- ENTRY _cache_getImp
+ STATIC_ENTRY _cache_getImp
# save registers and load class for CacheLookup
- stmfd sp!, {a4,v1-v3,r7,lr}
- add r7, sp, #16
- MOVE v1, a1
# search the cache
- CacheLookup a2, LGetImpMiss
+ CacheLookup a2, a1, LGetImpMiss
-# cache hit, method triplet in v1 and imp in ip
+# cache hit, method triplet in a1 and imp in ip
MOVE a1, ip @ return imp
- ldmfd sp!, {a4,v1-v3,r7,pc}
+ bx lr
LGetImpMiss:
MOVE a1, #0 @ return nil if cache miss
- ldmfd sp!, {a4,v1-v3,r7,pc}
+ bx lr
LGetImpExit:
END_ENTRY _cache_getImp
ENTRY objc_msgSend
# check whether receiver is nil
teq a1, #0
+ itt eq
moveq a2, #0
bxeq lr
# save registers and load receiver's class for CacheLookup
- stmfd sp!, {a4,v1-v3}
+ stmfd sp!, {a4,v1}
ldr v1, [a1, #ISA]
# receiver is non-nil: search the cache
- CacheLookup a2, LMsgSendCacheMiss
+ CacheLookup a2, v1, LMsgSendCacheMiss
-# cache hit (imp in ip) - prep for forwarding, restore registers and call
- teq v1, v1 /* set nonstret (eq) */
- ldmfd sp!, {a4,v1-v3}
+# cache hit (imp in ip) and CacheLookup returns with nonstret (eq) set, restore registers and call
+ ldmfd sp!, {a4,v1}
bx ip
# cache miss: go search the method lists
LMsgSendCacheMiss:
- ldmfd sp!, {a4,v1-v3}
+ ldmfd sp!, {a4,v1}
b _objc_msgSend_uncached
LMsgSendExit:
END_ENTRY objc_msgSend
- .text
- .align 2
-_objc_msgSend_uncached:
+ STATIC_ENTRY objc_msgSend_uncached
# Push stack frame
stmfd sp!, {a1-a4,r7,lr}
add r7, sp, #16
- SAVE_VFP
# Load class and selector
ldr a1, [a1, #ISA] /* class = receiver->isa */
# Prep for forwarding, Pop stack frame and call imp
teq v1, v1 /* set nonstret (eq) */
- RESTORE_VFP
ldmfd sp!, {a1-a4,r7,lr}
bx ip
+/********************************************************************
+ * id objc_msgSend_noarg(id self, SEL op)
+ *
+ * On entry: a1 is the message receiver,
+ * a2 is the selector
+ ********************************************************************/
+
+ ENTRY objc_msgSend_noarg
+# check whether receiver is nil
+ teq a1, #0
+ itt eq
+ moveq a2, #0
+ bxeq lr
+
+# load receiver's class for CacheLookup
+ ldr a3, [a1, #ISA]
+
+# receiver is non-nil: search the cache
+ CacheLookup a2, a3, LMsgSendNoArgCacheMiss
+
+# cache hit (imp in ip) and CacheLookup returns with nonstret (eq) set
+ bx ip
+
+# cache miss: go search the method lists
+LMsgSendNoArgCacheMiss:
+ b _objc_msgSend_uncached
+
+LMsgSendNoArgExit:
+ END_ENTRY objc_msgSend_noarg
+
/********************************************************************
* struct_type objc_msgSend_stret(id self,
ENTRY objc_msgSend_stret
# check whether receiver is nil
teq a2, #0
+ it eq
bxeq lr
# save registers and load receiver's class for CacheLookup
- stmfd sp!, {a4,v1-v3}
+ stmfd sp!, {a4,v1}
ldr v1, [a2, #ISA]
# receiver is non-nil: search the cache
- CacheLookup a3, LMsgSendStretCacheMiss
+ CacheLookup a3, v1, LMsgSendStretCacheMiss
# cache hit (imp in ip) - prep for forwarding, restore registers and call
tst v1, v1 /* set stret (ne); v1 is nonzero (triplet) */
- ldmfd sp!, {a4,v1-v3}
+ ldmfd sp!, {a4,v1}
bx ip
# cache miss: go search the method lists
LMsgSendStretCacheMiss:
- ldmfd sp!, {a4,v1-v3}
+ ldmfd sp!, {a4,v1}
b _objc_msgSend_stret_uncached
LMsgSendStretExit:
END_ENTRY objc_msgSend_stret
- .text
- .align 2
-_objc_msgSend_stret_uncached:
+ STATIC_ENTRY objc_msgSend_stret_uncached
# Push stack frame
stmfd sp!, {a1-a4,r7,lr}
add r7, sp, #16
- SAVE_VFP
# Load class and selector
ldr a1, [a2, #ISA] /* class = receiver->isa */
# Prep for forwarding, pop stack frame and call imp
tst a1, a1 /* set stret (ne); a1 is nonzero (imp) */
- RESTORE_VFP
ldmfd sp!, {a1-a4,r7,lr}
bx ip
* }
********************************************************************/
- ENTRY objc_msgSendSuper2
- @ objc_super->class is superclass of the class to search
- ldr r12, [a1, #CLASS]
- ldr r12, [r12, #4] @ r12 = cls->super_class
- str r12, [a1, #CLASS]
- b _objc_msgSendSuper
- END_ENTRY
-
ENTRY objc_msgSendSuper
# save registers and load super class for CacheLookup
- stmfd sp!, {a4,v1-v3}
+ stmfd sp!, {a4,v1}
ldr v1, [a1, #CLASS]
# search the cache
- CacheLookup a2, LMsgSendSuperCacheMiss
+ CacheLookup a2, v1, LMsgSendSuperCacheMiss
-# cache hit (imp in ip) - prep for forwarding, restore registers and call
- teq v1, v1 /* set nonstret (eq) */
- ldmfd sp!, {a4,v1-v3}
+# cache hit (imp in ip) and CacheLookup returns with nonstret (eq) set, restore registers and call
+ ldmfd sp!, {a4,v1}
ldr a1, [a1, #RECEIVER] @ fetch real receiver
bx ip
# cache miss: go search the method lists
LMsgSendSuperCacheMiss:
- ldmfd sp!, {a4,v1-v3}
+ ldmfd sp!, {a4,v1}
b _objc_msgSendSuper_uncached
LMsgSendSuperExit:
END_ENTRY objc_msgSendSuper
- .text
- .align 2
-_objc_msgSendSuper_uncached:
+ STATIC_ENTRY objc_msgSendSuper_uncached
# Push stack frame
stmfd sp!, {a1-a4,r7,lr}
add r7, sp, #16
- SAVE_VFP
# Load class and selector
ldr a1, [a1, #CLASS] /* class = super->class */
# Prep for forwarding, pop stack frame and call imp
teq v1, v1 /* set nonstret (eq) */
- RESTORE_VFP
+ ldmfd sp!, {a1-a4,r7,lr}
+ ldr a1, [a1, #RECEIVER] @ fetch real receiver
+ bx ip
+
+
+/********************************************************************
+ * objc_msgSendSuper2
+ ********************************************************************/
+
+ ENTRY objc_msgSendSuper2
+
+# save registers and load super class for CacheLookup
+ stmfd sp!, {a4,v1}
+ ldr v1, [a1, #CLASS]
+ ldr v1, [v1, #SUPERCLASS]
+
+# search the cache
+ CacheLookup a2, v1, LMsgSendSuper2CacheMiss
+
+# cache hit (imp in ip) and CacheLookup returns with nonstret (eq) set, restore registers and call
+ ldmfd sp!, {a4,v1}
+ ldr a1, [a1, #RECEIVER] @ fetch real receiver
+ bx ip
+
+# cache miss: go search the method lists
+LMsgSendSuper2CacheMiss:
+ ldmfd sp!, {a4,v1}
+ b _objc_msgSendSuper2_uncached
+
+LMsgSendSuper2Exit:
+ END_ENTRY objc_msgSendSuper2
+
+
+ STATIC_ENTRY objc_msgSendSuper2_uncached
+
+# Push stack frame
+ stmfd sp!, {a1-a4,r7,lr}
+ add r7, sp, #16
+
+# Load class and selector
+ ldr a1, [a1, #CLASS] /* class = super->class */
+ ldr a1, [a1, #SUPERCLASS] /* class = class->superclass */
+ # MOVE a2, a2 /* selector already in a2 */
+
+# Do the lookup
+ MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache)
+ MOVE ip, a1
+
+# Prep for forwarding, pop stack frame and call imp
+ teq v1, v1 /* set nonstret (eq) */
ldmfd sp!, {a1-a4,r7,lr}
ldr a1, [a1, #RECEIVER] @ fetch real receiver
bx ip
* a3 is the selector
********************************************************************/
- ENTRY objc_msgSendSuper2_stret
- @ objc_super->class is superclass of the class to search
- ldr r12, [a2, #CLASS]
- ldr r12, [r12, #4] @ xx = cls->super_class
- str r12, [a2, #CLASS]
- b _objc_msgSendSuper_stret
- END_ENTRY
-
ENTRY objc_msgSendSuper_stret
# save registers and load super class for CacheLookup
- stmfd sp!, {a4,v1-v3}
+ stmfd sp!, {a4,v1}
ldr v1, [a2, #CLASS]
# search the cache
- CacheLookup a3, LMsgSendSuperStretCacheMiss
+ CacheLookup a3, v1, LMsgSendSuperStretCacheMiss
# cache hit (imp in ip) - prep for forwarding, restore registers and call
tst v1, v1 /* set stret (ne); v1 is nonzero (triplet) */
- ldmfd sp!, {a4,v1-v3}
+ ldmfd sp!, {a4,v1}
ldr a2, [a2, #RECEIVER] @ fetch real receiver
bx ip
# cache miss: go search the method lists
LMsgSendSuperStretCacheMiss:
- ldmfd sp!, {a4,v1-v3}
+ ldmfd sp!, {a4,v1}
b _objc_msgSendSuper_stret_uncached
LMsgSendSuperStretExit:
END_ENTRY objc_msgSendSuper_stret
- .text
- .align 2
-_objc_msgSendSuper_stret_uncached:
+ STATIC_ENTRY objc_msgSendSuper_stret_uncached
# Push stack frame
stmfd sp!, {a1-a4,r7,lr}
add r7, sp, #16
- SAVE_VFP
# Load class and selector
ldr a1, [a2, #CLASS] /* class = super->class */
# Prep for forwarding, pop stack frame and call imp
tst v1, v1 /* set stret (ne); v1 is nonzero (triplet) */
+
+ ldmfd sp!, {a1-a4,r7,lr}
+ ldr a2, [a2, #RECEIVER] @ fetch real receiver
+ bx ip
+
+
+/********************************************************************
+ * id objc_msgSendSuper2_stret
+ ********************************************************************/
+
+ ENTRY objc_msgSendSuper2_stret
+
+# save registers and load super class for CacheLookup
+ stmfd sp!, {a4,v1}
+ ldr v1, [a2, #CLASS]
+ ldr v1, [v1, #SUPERCLASS]
+
+# search the cache
+ CacheLookup a3, v1, LMsgSendSuper2StretCacheMiss
+
+# cache hit (imp in ip) - prep for forwarding, restore registers and call
+ tst v1, v1 /* set stret (ne); v1 is nonzero (triplet) */
+ ldmfd sp!, {a4,v1}
+ ldr a2, [a2, #RECEIVER] @ fetch real receiver
+ bx ip
+
+# cache miss: go search the method lists
+LMsgSendSuper2StretCacheMiss:
+ ldmfd sp!, {a4,v1}
+ b _objc_msgSendSuper2_stret_uncached
- RESTORE_VFP
+LMsgSendSuper2StretExit:
+ END_ENTRY objc_msgSendSuper2_stret
+
+
+ STATIC_ENTRY objc_msgSendSuper2_stret_uncached
+
+# Push stack frame
+ stmfd sp!, {a1-a4,r7,lr}
+ add r7, sp, #16
+
+# Load class and selector
+ ldr a1, [a2, #CLASS] /* class = super->class */
+ ldr a1, [a1, #SUPERCLASS] /* class = class->superclass */
+ MOVE a2, a3 /* selector */
+
+# Do the lookup
+ MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache)
+ MOVE ip, a1
+
+# Prep for forwarding, pop stack frame and call imp
+ tst v1, v1 /* set stret (ne); v1 is nonzero (triplet) */
+
ldmfd sp!, {a1-a4,r7,lr}
ldr a2, [a2, #RECEIVER] @ fetch real receiver
bx ip
* stack args...
*
* typedef struct objc_sendv_margs {
- * #ifdef VFP_ARGS
- * double vfp[8];
- * #endif
* int a[4];
* int stackArgs[...];
* };
.long 0
- ENTRY _objc_msgForward_internal
- .private_extern __objc_msgForward_internal
+ STATIC_ENTRY _objc_msgForward_internal
// Method cache version
// THIS IS NOT A CALLABLE C FUNCTION
MI_GET_ADDRESS(ip, __objc_forward_handler)
ldr ip, [ip]
teq ip, #0
+ it ne
bxne ip
# build marg_list
stmfd sp!, {a1-a4} @ push args to marg_list
-#ifdef VFP_ARGS
- fstmfdd sp!, {d0-d7} @ push fp args to marg_list
-#endif
# build forward::'s parameter list (self, forward::, original sel, marg_list)
# a1 already is self
# pop stack frame and return
ldr lr, [sp]
-#ifdef VFP_ARGS
- add sp, sp, #(4 + 4 + 4*4 + 8*8) @ skip lr, pad, a1..a4, d0..d7
-#else
add sp, sp, #(4 + 4 + 4*4) @ skip lr, pad, a1..a4
-#endif
bx lr
END_ENTRY _objc_msgForward
ENTRY _objc_msgForward_stret
// Struct-return version
-
+
# check for user-installed forwarding handler
MI_GET_ADDRESS(ip, __objc_forward_stret_handler)
ldr ip, [ip]
teq ip, #0
+ it ne
bxne ip
# build marg_list
stmfd sp!, {a1-a4} @ push args to marg_list
-#ifdef VFP_ARGS
- fstmfdd sp!, {d0-d7} @ push fp args to marg_list
-#endif
# build forward::'s parameter list (self, forward::, original sel, marg_list)
MOVE a1, a2 @ self
# pop stack frame and return
ldr lr, [sp]
-#ifdef VFP_ARGS
- add sp, sp, #(4 + 4 + 4*4 + 8*8) @ skip lr, pad, a1..a4, d0..d7
-#else
add sp, sp, #(4 + 4 + 4*4) @ skip lr, pad, a1..a4
-#endif
bx lr
END_ENTRY _objc_msgForward_stret
.ascii "Does not recognize selector %s\0"
-/********************************************************************
- * id objc_msgSendv(id self,
- * SEL op,
- * unsigned arg_size,
- * marg_list arg_frame);
- *
- * typedef struct objc_sendv_margs {
- * #ifdef VFP_ARGS
- * double vfp[8];
- * #endif
- * int a[4];
- * int stackArgs[...];
- * };
- *
- * arg_frame is the number of bytes used in a[] plus stackArgs.
- * It does not include vfp[].
- *
- ********************************************************************/
-
- ENTRY objc_msgSendv
-
-# Push stack frame
- SAVE_VFP
- stmfd sp!, {a4,v1-v3,r7,lr} @ a4 saved for stack alignment only
- add r7, sp, #16
-
-# save sendv's parameters
- # self stays in a1
- # sel stays in a2
- MOVE v1, a3 @ v1 is arg count in bytes
- MOVE v2, a4 @ v2 is marg_list
-
-# load FP from marg_list
-#ifdef VFP_ARGS
- fldmfdd v2!, {d0-d7}
-#endif
-
-# load arg registers from marg_list
-# v1 is remaining count, v2 is pointer into marg_list
- # self already in a1
- # sel already in a2
- cmp v1, #12
- ldrhs a3, [v2, #8] @ pop a3 if arg bytes is at least 12
- ldrhi a4, [v2, #12] @ pop a4 if arg bytes is more than 12
- subs v1, v1, #16 @ skip past register args
- ble LMsgSendvCall @ call now if no args remain
- add v2, v2, #16 @ skip past register args
-
-# copy stack args from marg_list
-# v1 is remaining bytes, v2 is pointer into marg_list, sp is pointer into stack
- tst v1, #4
- subne sp, sp, #4 @ push 4-byte pad if word count is odd
-
- sub sp, sp, v1 @ move sp to end and copy backwards
- @ (this preserves ABI's stack constraints)
-LMsgSendvArgLoop:
- subs v1, v1, #4
- ldr v3, [v2, v1]
- str v3, [sp, v1]
- bne LMsgSendvArgLoop
-
-LMsgSendvCall:
- bl _objc_msgSend
-
-# Pop stack frame and return
- MOVE sp, r7
- ldmfd sp!, {a4,v1-v3,r7,pc}
-#ifdef VFP_ARGS
-#error broken for vfp
-#endif
+ ENTRY objc_msgSend_debug
+ b _objc_msgSend
+ END_ENTRY objc_msgSend_debug
- END_ENTRY objc_msgSendv
+ ENTRY objc_msgSendSuper2_debug
+ b _objc_msgSendSuper2
+ END_ENTRY objc_msgSendSuper2_debug
+ ENTRY objc_msgSend_stret_debug
+ b _objc_msgSend_stret
+ END_ENTRY objc_msgSend_stret_debug
-
-/********************************************************************
- * struct_type objc_msgSendv_stret(id self,
- * SEL op,
- * unsigned arg_size,
- * marg_list arg_frame);
- *
- * typedef struct objc_sendv_margs {
- * #ifdef VFP_ARGS
- * double vfp[8];
- * #endif
- * int a[4];
- * int stackArgs[...];
- * };
- *
- * arg_frame is the number of bytes used in a[] plus stackArgs.
- * It does not include vfp[].
- ********************************************************************/
-
- ENTRY objc_msgSendv_stret
-
-# Push stack frame
- stmfd sp!, {a4,v1-v3,r7,lr} @ a4 saved for stack alignment only
- add r7, sp, #16
- SAVE_VFP
-
-# save sendv's parameters
- # stret address stays in a1
- # self stays in a2
- # sel stays in a3
- MOVE v1, a4 @ v1 is arg count in bytes
- ldr v2, [r7, #24] @ v2 is marg_list
-
-# load FP from marg_list
-#ifdef VFP_ARGS
- fldmfdd v2!, {d0-d7}
-#endif
-
-# load arg registers from marg_list
-# v1 is remaining count, v2 is pointer into marg_list
- # stret already in a1
- # self already in a2
- # sel already in a3
- subs v1, v1, #16 @ skip past register args
- ldrhs a4, [v2, #12] @ pop a4 if arg bytes is at least 16
- beq LMsgSendvStretCall @ call now if no args remain
- add v2, v2, #16 @ skip past register args
-
-# copy stack args from marg_list
-# v1 is remaining count, v2 is pointer into marg_list, sp is pointer into stack
- tst v1, #4
- subne sp, sp, #4 @ push 4-byte pad if word count is odd
-
- sub sp, sp, v1 @ move pointers to end and copy backwards
- @ (this preserves ABI's stack constraints)
-LMsgSendvStretArgLoop:
- subs v1, v1, #4
- ldr v3, [v2, v1]
- str v3, [sp, v1]
- bne LMsgSendvStretArgLoop
-
-LMsgSendvStretCall:
- bl _objc_msgSend_stret
-
-# Pop stack frame and return
- MOVE sp, r7
- ldmfd sp!, {a4,v1-v3,r7,pc}
-#ifdef VFP_ARGS
-#error broken for vfp
-#endif
-
- END_ENTRY objc_msgSendv_stret
+ ENTRY objc_msgSendSuper2_stret_debug
+ b _objc_msgSendSuper2_stret
+ END_ENTRY objc_msgSendSuper2_stret_debug
ENTRY method_invoke
bx ip
END_ENTRY method_invoke_stret
+
+ STATIC_ENTRY _objc_ignored_method
+
+ # self is already in a0
+ bx lr
+
+ END_ENTRY _objc_ignored_method
+
#endif
* @APPLE_LICENSE_HEADER_END@
*/
-#ifdef __i386__
+#include <TargetConditionals.h>
+#if defined(__i386__) && !TARGET_IPHONE_SIMULATOR
/********************************************************************
********************************************************************
********************************************************************
********************************************************************/
-#undef OBJC_ASM
-#define OBJC_ASM
-#include "objc-rtp.h"
+// for kIgnore
+#include "objc-config.h"
/********************************************************************
.long 0
-/*
- * Handcrafted dyld stubs for each external call.
- * They should be converted into a local branch after linking. aB.
- */
-
-/* asm_help.h version is not what we want */
-#undef CALL_EXTERN
-
-#if defined(__DYNAMIC__)
-
-#define CALL_EXTERN(name) call L ## name ## $stub
-
-#define LAZY_PIC_FUNCTION_STUB(name) \
-.data ;\
-.picsymbol_stub ;\
-L ## name ## $stub: ;\
- .indirect_symbol name ;\
- call L0$ ## name ;\
-L0$ ## name: ;\
- popl %edx ;\
- movl L ## name ## $lz-L0$ ## name(%edx),%ecx ;\
- jmp *%ecx ;\
-L ## name ## $stub_binder: ;\
- lea L ## name ## $lz-L0$ ## name(%edx),%eax ;\
- pushl %eax ;\
- jmp dyld_stub_binding_helper ;\
-.data ;\
-.lazy_symbol_pointer ;\
-L ## name ## $lz: ;\
- .indirect_symbol name ;\
- .long L ## name ## $stub_binder
-
-#else /* __DYNAMIC__ */
-
-#define CALL_EXTERN(name) call name
-
-#define LAZY_PIC_FUNCTION_STUB(name)
-
-#endif /* __DYNAMIC__ */
-
-// _class_lookupMethodAndLoadCache
-LAZY_PIC_FUNCTION_STUB(__class_lookupMethodAndLoadCache)
-
-// __objc_error
-LAZY_PIC_FUNCTION_STUB(___objc_error) /* No stub needed */
-
-#if defined(PROFILE)
-// mcount
-LAZY_PIC_FUNCTION_STUB(mcount)
-#endif /* PROFILE */
-
-
/********************************************************************
*
* Common offsets.
$0:
.endmacro
+.macro STATIC_ENTRY
+ .text
+ .private_extern $0
+ .align 4, 0x90
+$0:
+.endmacro
+
//////////////////////////////////////////////////////////////////////
//
// END_ENTRY functionName
movl %esp,%ebp
subl $$8,%esp
// Current stack contents: ret, ebp, pad, pad
- CALL_EXTERN(mcount)
+ call mcount
movl %ebp,%esp
popl %ebp
#endif
// push args (class, selector)
pushl %ecx
pushl %eax
- CALL_EXTERN(__class_lookupMethodAndLoadCache)
+ call __class_lookupMethodAndLoadCache
addl $$12, %esp // pop parameters and alignment
.endmacro
* efficient to do the (PIC) lookup once in the caller than repeatedly here.
********************************************************************/
- .private_extern __cache_getMethod
- ENTRY __cache_getMethod
+ STATIC_ENTRY __cache_getMethod
// load the class and selector
movl selector(%esp), %ecx
* If not found, returns NULL.
********************************************************************/
- .private_extern __cache_getImp
- ENTRY __cache_getImp
+ STATIC_ENTRY __cache_getImp
// load the class and selector
movl selector(%esp), %ecx
*
********************************************************************/
- ENTRY _objc_msgSend_fixup_rtp
-// selector(%esp) is address of message ref instead of SEL
- movl selector(%esp), %edx
- movl 4(%edx), %ecx
- movl %ecx, selector(%esp) // convert selector
- jmp _objc_msgSend
- END_ENTRY _objc_msgSend_fixup_rtp
-
ENTRY _objc_msgSend
CALL_MCOUNTER
* };
********************************************************************/
- ENTRY _objc_msgSendSuper2_fixup_rtp
- // super(%esp) is objc_super struct with subclass instead of superclass
- mov super(%esp), %edx // edx = objc_super
- mov class(%edx), %eax // eax = objc_super->class
- mov 4(%eax), %eax // eax = objc_super->class->super_class
- mov %eax, class(%edx) // objc_super->class = eax
- // selector(%esp) is address of message ref instead of SEL
- mov selector(%esp), %eax
- mov 4(%eax), %eax
- mov %eax, selector(%esp)
- jmp _objc_msgSendSuper
- END_ENTRY _objc_msgSendSuper2_fixedup_rtp
-
ENTRY _objc_msgSendSuper
CALL_MCOUNTER
*
********************************************************************/
- ENTRY _objc_msgSend_fpret_fixup_rtp
-// selector(%esp) is address of message ref instead of SEL
- movl selector(%esp), %edx
- movl 4(%edx), %ecx
- movl %ecx, selector(%esp) // convert selector
- jmp _objc_msgSend_fpret
- END_ENTRY _objc_msgSend_fpret_fixup_rtp
-
ENTRY _objc_msgSend_fpret
CALL_MCOUNTER
* (sp+12) is the selector
********************************************************************/
- ENTRY _objc_msgSend_stret_fixup_rtp
-// selector_stret(%esp) is address of message ref instead of SEL
- movl selector_stret(%esp), %edx
- movl 4(%edx), %ecx
- movl %ecx, selector_stret(%esp) // convert selector
- jmp _objc_msgSend_stret
- END_ENTRY _objc_msgSend_stret_fixup_rtp
-
ENTRY _objc_msgSend_stret
CALL_MCOUNTER
*
********************************************************************/
- ENTRY _objc_msgSendSuper2_stret_fixup_rtp
- // super_stret(%esp) is objc_super with subclass instead of superclass
- mov super_stret(%esp), %edx // edx = objc_super
- mov class(%edx), %eax // eax = objc_super->class
- mov 4(%eax), %eax // eax = objc_super->class->super_class
- mov %eax, class(%edx) // objc_super->class = eax
- // selector_stret(%esp) is address of message ref instead of SEL
- mov selector_stret(%esp), %eax
- mov 4(%eax), %eax
- mov %eax, selector_stret(%esp)
- jmp _objc_msgSendSuper_stret
- END_ENTRY _objc_msgSendSuper2_stret_fixedup_rtp
-
ENTRY _objc_msgSendSuper_stret
CALL_MCOUNTER
.private_extern __objc_forward_stret_handler
__objc_forward_stret_handler: .long 0
- ENTRY __objc_msgForward_internal
- .private_extern __objc_msgForward_internal
+ STATIC_ENTRY __objc_msgForward_internal
// Method cache version
// THIS IS NOT A CALLABLE C FUNCTION
leal LUnkSelStr-L__objc_msgForward$pic_base(%edx),%eax
pushl %eax
pushl (self+4)(%ebp)
- CALL_EXTERN(___objc_error) // never returns
+ call ___objc_error // never returns
END_ENTRY __objc_msgForward
leal LUnkSelStr-L__objc_msgForwardStret$pic_base(%edx),%eax
pushl %eax
pushl (self_stret+4)(%ebp)
- CALL_EXTERN(___objc_error) // never returns
+ call ___objc_error // never returns
END_ENTRY __objc_msgForward_stret
END_ENTRY _method_invoke_stret
+
+ STATIC_ENTRY __objc_ignored_method
+
+ movl self(%esp), %eax
+ ret
+
+ END_ENTRY __objc_ignored_method
+
#endif
+++ /dev/null
-/*
- * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifdef __ppc__
-
-/********************************************************************
- *
- * objc-msg-ppc.s - PowerPC code to support objc messaging.
- *
- * Copyright 1988-1996 NeXT Software, Inc.
- *
- * December 2002 Andy Belk (abelk at apple.com)
- * Use r2 in the messenger - no longer need r10.
- * Removed "few args" variants (no longer worth it, especially since gcc3 still
- * doesn't generate code for them).
- * Add NonNil entry points to objc_msgSend and objc_msgSend_stret.
- * Align objc_msgSend et al on cache lines.
- * Replace CALL_EXTERN references (which caused excess mflr/mtlr usage) with
- * dyld-stub-compatible versions: shorter and become local branches within a dylib.
- *
- * 8-Nov-2000 Laurent Ramontianu (ramontia@apple.com)
- * Added "few args" params. to CacheLookup and MethodTableLookup
- * Added the alternate entry points:
- * objc_msgSendFew, objc_msgSendFew_stret,
- * objc_msgSendSuperFew, objc_msgSendSuperFew_stret
- *
- * 18-Jun-97 David Harrison (harrison@apple.com)
- * Restructured.
- *
- * 1-May-97 Umesh Vaishampayan (umeshv@NeXT.com)
- * Incorporated locking code fixes from
- * David Harrison (harrison@NeXT.com)
- *
- * 2-Apr-97 Umesh Vaishampayan (umeshv@NeXT.com)
- * Incorporated changes for messenger with struct return
- * Cleaned up the labels to use local labels
- * Fixed bug in the msgSendSuper that did not do the locking.
- *
- * 31-Dec-96 Umesh Vaishampayan (umeshv@NeXT.com)
- * Created from m98k.
- ********************************************************************/
-
-#undef OBJC_ASM
-#define OBJC_ASM
-#include "objc-rtp.h"
-
-/********************************************************************
- * Data used by the ObjC runtime.
- *
- ********************************************************************/
-
- .data
-; Substitute receiver for messages sent to nil (usually also nil)
-; id _objc_nilReceiver
- .align 4
-.private_extern __objc_nilReceiver
-__objc_nilReceiver:
- .long 0
-
-; _objc_entryPoints and _objc_exitPoints are used by method dispatch
-; caching code to figure out whether any threads are actively
-; in the cache for dispatching. The labels surround the asm code
-; that do cache lookups. The tables are zero-terminated.
-.private_extern _objc_entryPoints
-_objc_entryPoints:
- .long __cache_getImp
- .long __cache_getMethod
- .long _objc_msgSend
- .long _objc_msgSend_stret
- .long _objc_msgSendSuper
- .long _objc_msgSendSuper_stret
- .long _objc_msgSend_rtp
- .long 0
-
-.private_extern _objc_exitPoints
-_objc_exitPoints:
- .long LGetImpExit
- .long LGetMethodExit
- .long LMsgSendExit
- .long LMsgSendStretExit
- .long LMsgSendSuperExit
- .long LMsgSendSuperStretExit
- .long _objc_msgSend_rtp_exit
- .long 0
-
-/*
- * Handcrafted dyld stubs for each external call.
- * They should be converted into a local branch after linking. aB.
- */
-
-/* asm_help.h version is not what we want */
-#undef CALL_EXTERN
-
-#if defined(__DYNAMIC__)
-
-#define CALL_EXTERN(name) bl L ## name ## $stub
-
-#define LAZY_PIC_FUNCTION_STUB(name) \
-.data @\
-.picsymbol_stub @\
-L ## name ## $stub: @\
- .indirect_symbol name @\
- mflr r0 @\
- bcl 20,31,L0$ ## name @\
-L0$ ## name: @\
- mflr r11 @\
- addis r11,r11,ha16(L ## name ## $lazy_ptr-L0$ ## name) @\
- mtlr r0 @\
- lwz r12,lo16(L ## name ## $lazy_ptr-L0$ ## name)(r11) @\
- mtctr r12 @\
- addi r11,r11,lo16(L ## name ## $lazy_ptr-L0$ ## name) @\
- bctr @\
-.data @\
-.lazy_symbol_pointer @\
-L ## name ## $lazy_ptr: @\
- .indirect_symbol name @\
- .long dyld_stub_binding_helper
-
-#else /* __DYNAMIC__ */
-
-#define CALL_EXTERN(name) bl name
-
-#define LAZY_PIC_FUNCTION_STUB(name)
-
-#endif /* __DYNAMIC__ */
-
-; _class_lookupMethodAndLoadCache
-LAZY_PIC_FUNCTION_STUB(__class_lookupMethodAndLoadCache)
-
-; __objc_error
-LAZY_PIC_FUNCTION_STUB(___objc_error) /* No stub needed */
-
-#if defined(PROFILE)
-; mcount
-LAZY_PIC_FUNCTION_STUB(mcount)
-#endif /* PROFILE */
-
-
-/********************************************************************
- *
- * Structure definitions.
- *
- ********************************************************************/
-
-; objc_super parameter to sendSuper
-#define RECEIVER 0
-#define CLASS 4
-
-; Selected field offsets in class structure
-#define ISA 0
-#define CACHE 32
-
-; Method descriptor
-#define METHOD_NAME 0
-#define METHOD_IMP 8
-
-; Cache header
-#define MASK 0
-#define OCCUPIED 4
-#define BUCKETS 8 // variable length array
-
-#if defined(OBJC_INSTRUMENTED)
-; Cache instrumentation data, follows buckets
-#define hitCount 0
-#define hitProbes hitCount + 4
-#define maxHitProbes hitProbes + 4
-#define missCount maxHitProbes + 4
-#define missProbes missCount + 4
-#define maxMissProbes missProbes + 4
-#define flushCount maxMissProbes + 4
-#define flushedEntries flushCount + 4
-#endif
-
-/********************************************************************
- *
- * Constants.
- *
- ********************************************************************/
-
-// In case the implementation is _objc_msgForward_internal, indicate to it
-// whether the method was invoked as a word-return or struct-return.
-// The li instruction costs nothing because it fits into spare
-// processor cycles. We choose to make the MsgSend indicator non-zero
-// as r11 is already guaranteed non-zero for a cache hit (no li needed).
-
-#define kFwdMsgSend 1
-#define kFwdMsgSendStret 0
-
-
-/********************************************************************
- *
- * Useful macros. Macros are used instead of subroutines, for speed.
- *
- ********************************************************************/
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;
-; LOAD_STATIC_WORD targetReg, symbolName, LOCAL_SYMBOL | EXTERNAL_SYMBOL
-;
-; Load the value of the named static data word.
-;
-; Takes: targetReg - the register, other than r0, to load
-; symbolName - the name of the symbol
-; LOCAL_SYMBOL - symbol name used as-is
-; EXTERNAL_SYMBOL - symbol name gets nonlazy treatment
-;
-; Eats: r0 and targetReg
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-; Values to specify whether the symbols is plain or nonlazy
-#define LOCAL_SYMBOL 0
-#define EXTERNAL_SYMBOL 1
-
-.macro LOAD_STATIC_WORD
-
-#if defined(__DYNAMIC__)
- mflr r0
- bcl 20,31,1f ; 31 is cr7[so]
-1: mflr $0
- mtlr r0
-.if $2 == EXTERNAL_SYMBOL
- addis $0,$0,ha16(L$1-1b)
- lwz $0,lo16(L$1-1b)($0)
- lwz $0,0($0)
-.elseif $2 == LOCAL_SYMBOL
- addis $0,$0,ha16($1-1b)
- lwz $0,lo16($1-1b)($0)
-.else
- !!! Unknown symbol type !!!
-.endif
-#else
- lis $0,ha16($1)
- lwz $0,lo16($1)($0)
-#endif
-
-.endmacro
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;
-; LEA_STATIC_DATA targetReg, symbolName, LOCAL_SYMBOL | EXTERNAL_SYMBOL
-;
-; Load the address of the named static data.
-;
-; Takes: targetReg - the register, other than r0, to load
-; symbolName - the name of the symbol
-; LOCAL_SYMBOL - symbol is local to this module
-; EXTERNAL_SYMBOL - symbol is imported from another module
-;
-; Eats: r0 and targetReg
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-.macro LEA_STATIC_DATA
-#if defined(__DYNAMIC__)
- mflr r0
- bcl 20,31,1f ; 31 is cr7[so]
-1: mflr $0
- mtlr r0
-.if $2 == EXTERNAL_SYMBOL
- addis $0,$0,ha16(L$1-1b)
- lwz $0,lo16(L$1-1b)($0)
-.elseif $2 == LOCAL_SYMBOL
- addis $0,$0,ha16($1-1b)
- addi $0,$0,lo16($1-1b)
-.else
- !!! Unknown symbol type !!!
-.endif
-#else
- lis $0,hi16($1)
- ori $0,$0,lo16($1)
-#endif
-
-.endmacro
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;
-; ENTRY functionName
-;
-; Assembly directives to begin an exported function.
-; We align on cache boundaries for these few functions.
-;
-; Takes: functionName - name of the exported function
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-.macro ENTRY
- .text
- .align 5
- .globl $0
-$0:
-.endmacro
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;
-; END_ENTRY functionName
-;
-; Assembly directives to end an exported function. Just a placeholder,
-; a close-parenthesis for ENTRY, until it is needed for something.
-;
-; Takes: functionName - name of the exported function
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-.macro END_ENTRY
-.endmacro
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;
-; PLOCK scratchReg, lockName
-;
-; Acquire named spinlock.
-;
-; Takes: scratchReg - a register, other than r0, that can be mangled
-; lockName - the name of a static, aligned, 32-bit lock word
-;
-; Eats: r0 and scratchReg
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-.macro PLOCK
- LEA_STATIC_DATA $0, $1, EXTERNAL_SYMBOL
- b .+16 ; jump into loop at the reserving check
- lwz r0,0($0) ; check with fast, less intrusive lwz versus lwarx
- cmplwi r0,0 ; lock held?
- bne .-8 ; if so, spin until it appears unlocked
- lwarx r0,0,$0 ; get lock value, acquire memory reservation
- cmplwi r0,0 ; lock held?
- bne .-20 ; if locked, go spin waiting for unlock
- li r0,1 ; get value that means locked
- stwcx. r0,0,$0 ; store it iff reservation still holds
- bne- .-20 ; if reservation was lost, go re-reserve
- isync ; discard effects of prefetched instructions
-.endmacro
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;
-; PUNLOCK scratchReg, lockName
-;
-; Release named spinlock.
-;
-; Takes: scratchReg - a register, other than r0, that can be mangled
-; lockName - the name of a static, aligned, 32-bit lock word
-;
-; Eats: r0 and scratchReg
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-.macro PUNLOCK
- sync ; force out changes before unlocking
- LEA_STATIC_DATA $0, $1, EXTERNAL_SYMBOL
- li r0,0 ; get value meaning "unlocked"
- stw r0,0($0) ; unlock the lock
-.endmacro
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;
-; CacheLookup selectorRegister, cacheMissLabel
-;
-; Locate the implementation for a selector in a class method cache.
-;
-; Takes:
-; $0 = register containing selector (r4 or r5 ONLY);
-; cacheMissLabel = label to branch to iff method is not cached
-; r12 = class whose cache is to be searched
-;
-; On exit: (found) method triplet in r2, imp in r12, r11 is non-zero
-; (not found) jumps to cacheMissLabel
-;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-.macro CacheLookup
-
-#if defined(OBJC_INSTRUMENTED)
- ; when instrumented, we use r6 and r7
- stw r6,36(r1) ; save r6 for use as cache pointer
- stw r7,40(r1) ; save r7 for use as probe count
- li r7,0 ; no probes so far!
-#endif
-
- lwz r2,CACHE(r12) ; cache = class->cache
- stw r9,48(r1) ; save r9
-
-#if defined(OBJC_INSTRUMENTED)
- mr r6,r2 ; save cache pointer
-#endif
-
- lwz r11,MASK(r2) ; mask = cache->mask
- addi r0,r2,BUCKETS ; buckets = cache->buckets
- slwi r11,r11,2 ; r11 = mask << 2
- and r9,$0,r11 ; bytes = sel & (mask<<2)
-
-#if defined(OBJC_INSTRUMENTED)
- b LLoop_$0_$1
-
-LMiss_$0_$1:
- ; r6 = cache, r7 = probeCount
- lwz r9,MASK(r6) ; entryCount = mask + 1
- addi r9,r9,1 ;
- slwi r9,r9,2 ; tableSize = entryCount * sizeof(entry)
- addi r9,r9,BUCKETS ; offset = buckets + tableSize
- add r11,r6,r9 ; cacheData = &cache->buckets[mask+1]
- lwz r9,missCount(r11) ; cacheData->missCount += 1
- addi r9,r9,1 ;
- stw r9,missCount(r11) ;
- lwz r9,missProbes(r11) ; cacheData->missProbes += probeCount
- add r9,r9,r7 ;
- stw r9,missProbes(r11) ;
- lwz r9,maxMissProbes(r11) ; if (probeCount > cacheData->maxMissProbes)
- cmplw r7,r9 ; maxMissProbes = probeCount
- ble .+8 ;
- stw r7,maxMissProbes(r11) ;
-
- lwz r6,36(r1) ; restore r6
- lwz r7,40(r1) ; restore r7
-
- b $1 ; goto cacheMissLabel
-#endif
-
-; search the cache
-LLoop_$0_$1:
-#if defined(OBJC_INSTRUMENTED)
- addi r7,r7,1 ; probeCount += 1
-#endif
-
- lwzx r2,r9,r0 ; method = buckets[bytes/4]
- addi r9,r9,4 ; bytes += 4
- cmplwi r2,0 ; if (method == NULL)
-#if defined(OBJC_INSTRUMENTED)
- beq- LMiss_$0_$1
-#else
- beq- $1 ; goto cacheMissLabel
-#endif
-
- lwz r12,METHOD_NAME(r2) ; name = method->method_name
- and r9,r9,r11 ; bytes &= (mask<<2)
- cmplw r12,$0 ; if (name != selector)
- bne- LLoop_$0_$1 ; goto loop
-
-; cache hit, r2 == method triplet address
-; Return triplet in r2 and imp in r12
- lwz r12,METHOD_IMP(r2) ; imp = method->method_imp
-
-#if defined(OBJC_INSTRUMENTED)
- ; r6 = cache, r7 = probeCount
- lwz r9,MASK(r6) ; entryCount = mask + 1
- addi r9,r9,1 ;
- slwi r9,r9,2 ; tableSize = entryCount * sizeof(entry)
- addi r9,r9,BUCKETS ; offset = buckets + tableSize
- add r11,r6,r9 ; cacheData = &cache->buckets[mask+1]
- lwz r9,hitCount(r11) ; cache->hitCount += 1
- addi r9,r9,1 ;
- stw r9,hitCount(r11) ;
- lwz r9,hitProbes(r11) ; cache->hitProbes += probeCount
- add r9,r9,r7 ;
- stw r9,hitProbes(r11) ;
- lwz r9,maxHitProbes(r11) ; if (probeCount > cache->maxMissProbes)
- cmplw r7,r9 ;maxMissProbes = probeCount
- ble .+8 ;
- stw r7,maxHitProbes(r11) ;
-
- lwz r6,36(r1) ; restore r6
- lwz r7,40(r1) ; restore r7
-#endif
-
- lwz r9,48(r1) ; restore r9
-
-.endmacro
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;
-; CacheLookup cache locking - 2001-11-12
-; The collecting cache mechanism precludes the need for a cache lock
-; in objc_msgSend. The cost of the collecting cache is small: a few
-; K of memory for uncollected caches, and less than 1 ms per collection.
-; A large app will only run collection a few times.
-; Using the code below to lock the cache almost doubles messaging time,
-; costing several seconds of CPU across several minutes of operation.
-; The code below probably could be improved, but almost all of the
-; locking slowdown is in the sync and isync.
-;
-; 40 million message test times (G4 1x667):
-; no lock 4.390u 0.030s 0:04.59 96.2% 0+0k 0+1io 0pf+0w
-; with lock 9.120u 0.010s 0:09.83 92.8% 0+0k 0+0io 0pf+0w
-;
-;; LockCache mask_dest, cache
-;.macro LockCache
-; ; LOCKED mask is NEGATIVE
-; lwarx $0, mask, $1 ; mask = reserve(cache->mask)
-; cmpwi $0, 0 ;
-; blt .-8 ; try again if mask < 0
-; neg r0, $0 ;
-; stwcx. r0, mask, $1 ; cache->mask = -mask ($0 keeps +mask)
-; bne .-20 ; try again if lost reserve
-; isync ; flush prefetched instructions after locking
-;.endmacro
-;
-;; UnlockCache (mask<<2), cache
-;.macro UnlockCache
-; sync ; finish previous instructions before unlocking
-; srwi r0, $0, 2 ; r0 = (mask<<2) >> 2
-; stw r0, mask($1) ; cache->mask = +mask
-;.endmacro
-;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;
-;
-; MethodTableLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER
-;
-; Takes: WORD_RETURN (r3 is first parameter)
-; STRUCT_RETURN (r3 is structure return address, r4 is first parameter)
-; MSG_SEND (first parameter is receiver)
-; MSG_SENDSUPER (first parameter is address of objc_super structure)
-;
-; Eats: r0, r2, r11, r12
-; On exit: restores r9 saved by CacheLookup
-; imp in ctr
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-; Values to specify to method lookup macros whether the return type of
-; the method is an integer or structure.
-#define WORD_RETURN 0
-#define STRUCT_RETURN 1
-
-; Values to specify to method lookup macros whether the return type of
-; the method is an integer or structure.
-#define MSG_SEND 0
-#define MSG_SENDSUPER 1
-
-.macro MethodTableLookup
- mflr r0 ; save lr
- stw r0, 8(r1) ;
-
- stw r3, 24(r1) ; save arguments
- stw r4, 28(r1) ;
- stw r5, 32(r1) ;
- stw r6, 36(r1) ;
- stw r7, 40(r1) ;
- stw r8, 44(r1) ;
- ; r9 was saved by CacheLookup
- stw r10, 52(r1) ;
-
-#if !defined(KERNEL)
-; Save the FP parameter registers.
-; We do not spill vector argument registers. This is
-; harmless because vector parameters are unsupported.
- stfd f1, -104(r1) ;
- stfd f2, -96(r1) ;
- stfd f3, -88(r1) ;
- stfd f4, -80(r1) ;
- stfd f5, -72(r1) ;
- stfd f6, -64(r1) ;
- stfd f7, -56(r1) ;
- stfd f8, -48(r1) ;
- stfd f9, -40(r1) ;
- stfd f10, -32(r1) ;
- stfd f11, -24(r1) ;
- stfd f12, -16(r1) ;
- stfd f13, -8(r1) ;
-
- stwu r1,-56-(13*8)(r1) ; grow the stack. Must be 16-byte-aligned.
-#else
- stwu r1,-64(r1) ; grow the stack. Must be 16-byte-aligned.
-#endif
-
-; Pass parameters to __class_lookupMethodAndLoadCache. First parameter is
-; the class pointer. Second parameter is the selector. Where they come
-; from depends on who called us. In the int return case, the selector is
-; already in r4.
-.if $0 == WORD_RETURN ; WORD_RETURN
-.if $1 == MSG_SEND ; MSG_SEND
- lwz r3,ISA(r3) ; class = receiver->isa
-.else ; MSG_SENDSUPER
- lwz r3,CLASS(r3) ; class = super->class
-.endif
-
-.else ; STRUCT_RETURN
-.if $1 == MSG_SEND ; MSG_SEND
- lwz r3,ISA(r4) ; class = receiver->isa
-.else ; MSG_SENDSUPER
- lwz r3,CLASS(r4) ; class = super->class
-.endif
- mr r4,r5 ; selector = selector
-.endif
-
- ; We code the call inline rather than using the CALL_EXTERN macro because
- ; that leads to a lot of extra unnecessary and inefficient instructions.
- CALL_EXTERN(__class_lookupMethodAndLoadCache)
-
- mr r12,r3 ; copy implementation to r12
- mtctr r3 ; copy imp to ctr
- lwz r1,0(r1) ; restore the stack pointer
- lwz r0,8(r1) ;
- mtlr r0 ; restore return pc
-
-#if !defined(KERNEL)
-
-; Restore FP parameter registers
- lfd f1, -104(r1) ;
- lfd f2, -96(r1) ;
- lfd f3, -88(r1) ;
- lfd f4, -80(r1) ;
- lfd f5, -72(r1) ;
- lfd f6, -64(r1) ;
- lfd f7, -56(r1) ;
- lfd f8, -48(r1) ;
- lfd f9, -40(r1) ;
- lfd f10, -32(r1) ;
- lfd f11, -24(r1) ;
- lfd f12, -16(r1) ;
- lfd f13, -8(r1) ;
-
- lwz r3, 24(r1) ; restore parameter registers
- lwz r4, 28(r1) ;
- lwz r5, 32(r1) ;
- lwz r6, 36(r1) ;
- lwz r7, 40(r1) ;
- lwz r8, 44(r1) ;
- lwz r9, 48(r1) ; r9 was saved by CacheLookup
- lwz r10, 52(r1) ;
-
-#endif /* !KERNEL */
-
-.endmacro
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;
-; CALL_MCOUNT
-;
-; Macro to call mcount function in profiled builds.
-;
-; NOTE: Makes sure to save/restore r11 and r12, even though they
-; are not defined to be volatile, because they are used during
-; forwarding.
-;
-; Takes: lr Callers return PC
-;
-; Eats: r0
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- .macro CALL_MCOUNT
-#if defined(PROFILE)
- mflr r0 ; save return pc
- stw r0,8(r1) ;
-
- stwu r1,-208(r1) ; push aligned areas, set stack link
-
- stw r3, 56(r1) ; save all volatile registers
- stw r4, 60(r1) ;
- stw r5, 64(r1) ;
- stw r6, 68(r1) ;
- stw r7, 72(r1) ;
- stw r8, 76(r1) ;
- stw r9, 80(r1) ;
- stw r10,84(r1) ;
- stw r11,88(r1) ; save r11 and r12, too
- stw r12,92(r1) ;
-
- stfd f1, 96(r1) ;
- stfd f2, 104(r1) ;
- stfd f3, 112(r1) ;
- stfd f4, 120(r1) ;
- stfd f5, 128(r1) ;
- stfd f6, 136(r1) ;
- stfd f7, 144(r1) ;
- stfd f8, 152(r1) ;
- stfd f9, 160(r1) ;
- stfd f10,168(r1) ;
- stfd f11,176(r1) ;
- stfd f12,184(r1) ;
- stfd f13,192(r1) ;
-
- mr r3, r0 ; pass our callers address
-
- CALL_EXTERN(mcount)
-
- lwz r3, 56(r1) ; restore all volatile registers
- lwz r4, 60(r1) ;
- lwz r5, 64(r1) ;
- lwz r6, 68(r1) ;
- lwz r7, 72(r1) ;
- lwz r8, 76(r1) ;
- lwz r9, 80(r1) ;
- lwz r10,84(r1) ;
- lwz r11,88(r1) ; restore r11 and r12, too
- lwz r12,92(r1) ;
-
- lfd f1, 96(r1) ;
- lfd f2, 104(r1) ;
- lfd f3, 112(r1) ;
- lfd f4, 120(r1) ;
- lfd f5, 128(r1) ;
- lfd f6, 136(r1) ;
- lfd f7, 144(r1) ;
- lfd f8, 152(r1) ;
- lfd f9, 160(r1) ;
- lfd f10,168(r1) ;
- lfd f11,176(r1) ;
- lfd f12,184(r1) ;
- lfd f13,192(r1) ;
-
- lwz r1,0(r1) ; restore the stack pointer
- lwz r0,8(r1) ;
- mtlr r0 ; restore return pc
-#endif
- .endmacro
-
-
-/********************************************************************
- * Method _cache_getMethod(Class cls, SEL sel, IMP objc_msgForward_imp)
- *
- * On entry: r3 = class whose cache is to be searched
- * r4 = selector to search for
- * r5 = _objc_msgForward IMP
- *
- * If found, returns method triplet pointer.
- * If not found, returns NULL.
- *
- * NOTE: _cache_getMethod never returns any cache entry whose implementation
- * is _objc_msgForward. It returns (Method)1 instead. This prevents thread-
- * safety and memory management bugs in _class_lookupMethodAndLoadCache.
- * See _class_lookupMethodAndLoadCache for details.
- *
- * _objc_msgForward is passed as a parameter because it's more efficient
- * to do the (PIC) lookup once in the caller than repeatedly here.
- ********************************************************************/
-
- .private_extern __cache_getMethod
- ENTRY __cache_getMethod
-; do profiling if enabled
- CALL_MCOUNT
-
-; do lookup
- mr r12,r3 ; move class to r12 for CacheLookup
- CacheLookup r4, LGetMethodMiss
-
-; cache hit, method triplet in r2 and imp in r12
- cmplw r12, r5 ; check for _objc_msgForward
- mr r3, r2 ; optimistically get the return value
- bnelr ; Not _objc_msgForward, return the triplet address
- li r3, 1 ; Is _objc_msgForward, return (Method)1
- blr
-
-LGetMethodMiss:
- li r3, 0 ; cache miss or _objc_msgForward, return nil
- blr
-
-LGetMethodExit:
- END_ENTRY __cache_getMethod
-
-
-/********************************************************************
- * IMP _cache_getImp(Class cls, SEL sel)
- *
- * On entry: r3 = class whose cache is to be searched
- * r4 = selector to search for
- *
- * If found, returns method implementation.
- * If not found, returns NULL.
- ********************************************************************/
-
- .private_extern __cache_getImp
- ENTRY __cache_getImp
-; do profiling if enabled
- CALL_MCOUNT
-
-; do lookup
- mr r12,r3 ; move class to r12 for CacheLookup
- CacheLookup r4, LGetImpMiss
-
-; cache hit, method triplet in r2 and imp in r12
- mr r3, r12 ; return method imp address
- blr
-
-LGetImpMiss:
-; cache miss, return nil
- li r3, 0 ; return nil
- blr
-
-LGetImpExit:
- END_ENTRY __cache_getImp
-
-
-/********************************************************************
- * id objc_msgSend(id self,
- * SEL op,
- * ...);
- *
- * On entry: r3 is the message receiver,
- * r4 is the selector
- ********************************************************************/
-
-; WARNING - This code may be copied as is to the Objective-C runtime pages.
-; The code is copied by rtp_set_up_objc_msgSend() from the
-; beginning to the blr marker just prior to the cache miss code.
-; Do not add callouts, global variable accesses, or rearrange
-; the code without updating rtp_set_up_objc_msgSend.
-
-; Absolute symbols bounding the runtime page version of objc_msgSend.
-_objc_msgSend_rtp = 0xfffeff00
-_objc_msgSend_rtp_exit = 0xfffeff00+0x100
-
- ENTRY _objc_msgSend_fixup_rtp
- lwz r4, 4(r4) ; load _cmd from message_ref
- b _objc_msgSend
- END_ENTRY _objc_msgSend_fixup_rtp
-
- ENTRY _objc_msgSend
-; check whether receiver is nil or selector is to be ignored
- cmplwi r3,0 ; receiver nil?
- xoris r11,r4,((kIgnore>>16) & 0xffff) ; clear hi if equal to ignored
- cmplwi cr1,r11,(kIgnore & 0xffff) ; selector is to be ignored?
- beq- LMsgSendNilSelf ; if nil receiver, call handler or return nil
- lwz r12,ISA(r3) ; class = receiver->isa
- beqlr- cr1 ; if ignored selector, return self immediately
-
-; guaranteed non-nil entry point (disabled for now)
-; .globl _objc_msgSendNonNil
-; _objc_msgSendNonNil:
-
-; do profiling when enabled
- CALL_MCOUNT
-
-; receiver is non-nil: search the cache
-LMsgSendReceiverOk:
- ; class is already in r12
- CacheLookup r4, LMsgSendCacheMiss
- ; CacheLookup placed imp in r12
- mtctr r12
- ; r11 guaranteed non-zero on exit from CacheLookup with a hit
- // li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward
- bctr ; goto *imp;
-
-; WARNING - The first six instructions of LMsgSendNilSelf are
-; rewritten when objc_msgSend is copied to the runtime pages.
-; These instructions must be maintained AS IS unless the code in
-; rtp_set_up_objc_msgSend is also updated.
-; * `mflr r0` must not be changed (not even to use a different register)
-; * the load of _objc_nilReceiver value must remain six insns long
-; * the value of _objc_nilReceiver must continue to be loaded into r11
-
-; message sent to nil: redirect to nil receiver, if any
-LMsgSendNilSelf:
- ; DO NOT CHANGE THE NEXT SIX INSTRUCTIONS - see note above
- mflr r0 ; save return address
- bcl 20,31,1f ; 31 is cr7[so]
-1: mflr r11
- addis r11,r11,ha16(__objc_nilReceiver-1b)
- lwz r11,lo16(__objc_nilReceiver-1b)(r11)
- mtlr r0 ; restore return address
- ; DO NOT CHANGE THE PREVIOUS SIX INSTRUCTIONS - see note above
-
- cmplwi r11,0 ; return nil if no new receiver
- beq LMsgSendReturnZero
-
- mr r3,r11 ; send to new receiver
- lwz r12,ISA(r11) ; class = receiver->isa
- b LMsgSendReceiverOk
-
-LMsgSendReturnZero:
- li r3, 0
- li r4, 0
- lis r12, ha16(kRTAddress_zero)
- lfd f1, lo16(kRTAddress_zero)(r12)
- lfd f2, lo16(kRTAddress_zero)(r12)
-
-; WARNING - This blr marks the end of the copy to the ObjC runtime pages and
-; also marks the beginning of the cache miss code. Do not move
-; around without checking the ObjC runtime pages initialization code.
- blr
-
-; cache miss: go search the method lists
-LMsgSendCacheMiss:
- MethodTableLookup WORD_RETURN, MSG_SEND
- li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward
- bctr ; goto *imp;
-
-LMsgSendExit:
- END_ENTRY _objc_msgSend
-
-/********************************************************************
- *
- * double objc_msgSend_fpret(id self, SEL op, ...);
- *
- ********************************************************************/
-
- ENTRY _objc_msgSend_fpret
- b _objc_msgSend
- END_ENTRY _objc_msgSend_fpret
-
-/********************************************************************
- * struct_type objc_msgSend_stret(id self,
- * SEL op,
- * ...);
- *
- * objc_msgSend_stret is the struct-return form of msgSend.
- * The ABI calls for r3 to be used as the address of the structure
- * being returned, with the parameters in the succeeding registers.
- *
- * On entry: r3 is the address where the structure is returned,
- * r4 is the message receiver,
- * r5 is the selector
- ********************************************************************/
-
- ENTRY _objc_msgSend_stret_fixup_rtp
- lwz r5, 4(r5) ; load _cmd from message_ref
- b _objc_msgSend_stret
- END_ENTRY _objc_msgSend_stret_fixup_rtp
-
- ENTRY _objc_msgSend_stret
-; check whether receiver is nil
- cmplwi r4,0 ; receiver nil?
- beq LMsgSendStretNilSelf ; if so, call handler or just return
-
-; guaranteed non-nil entry point (disabled for now)
-; .globl _objc_msgSendNonNil_stret
-; _objc_msgSendNonNil_stret:
-
-; do profiling when enabled
- CALL_MCOUNT
-
-; receiver is non-nil: search the cache
-LMsgSendStretReceiverOk:
- lwz r12, ISA(r4) ; class = receiver->isa
- CacheLookup r5, LMsgSendStretCacheMiss
- ; CacheLookup placed imp in r12
- mtctr r12
- li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward
- bctr ; goto *imp;
-
-; cache miss: go search the method lists
-LMsgSendStretCacheMiss:
- MethodTableLookup STRUCT_RETURN, MSG_SEND
- li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward
- bctr ; goto *imp;
-
-; message sent to nil: redirect to nil receiver, if any
-LMsgSendStretNilSelf:
- mflr r0 ; load new receiver
- bcl 20,31,1f ; 31 is cr7[so]
-1: mflr r11
- addis r11,r11,ha16(__objc_nilReceiver-1b)
- lwz r11,lo16(__objc_nilReceiver-1b)(r11)
- mtlr r0
-
- cmplwi r11,0 ; return if no new receiver
- beqlr
-
- mr r4,r11 ; send to new receiver
- b LMsgSendStretReceiverOk
-
-LMsgSendStretExit:
- END_ENTRY _objc_msgSend_stret
-
-
-/********************************************************************
- * id objc_msgSendSuper(struct objc_super *super,
- * SEL op,
- * ...);
- *
- * struct objc_super {
- * id receiver;
- * Class class;
- * };
- ********************************************************************/
-
- ENTRY _objc_msgSendSuper2_fixup_rtp
- ; objc_super->class is superclass of the class to search
- lwz r11, CLASS(r3)
- lwz r4, 4(r4) ; load _cmd from message_ref
- lwz r11, 4(r11) ; r11 = cls->super_class
- stw r11, CLASS(r3)
- b _objc_msgSendSuper
- END_ENTRY _objc_msgSendSuper2_fixup_rtp
-
- ENTRY _objc_msgSendSuper
-; do profiling when enabled
- CALL_MCOUNT
-
-; check whether selector is to be ignored
- xoris r11,r4,((kIgnore>>16) & 0xffff) ; clear hi if to be ignored
- cmplwi r11,(kIgnore & 0xffff) ; selector is to be ignored?
- lwz r12,CLASS(r3) ; class = super->class
- beq- LMsgSendSuperIgnored ; if ignored, return self
-
-; search the cache
- ; class is already in r12
- CacheLookup r4, LMsgSendSuperCacheMiss
- ; CacheLookup placed imp in r12
- mtctr r12
- lwz r3,RECEIVER(r3) ; receiver is the first arg
- ; r11 guaranteed non-zero after cache hit
- ; li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward
- bctr ; goto *imp;
-
-; cache miss: go search the method lists
-LMsgSendSuperCacheMiss:
- MethodTableLookup WORD_RETURN, MSG_SENDSUPER
- lwz r3,RECEIVER(r3) ; receiver is the first arg
- li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward
- bctr ; goto *imp;
-
-; ignored selector: return self
-LMsgSendSuperIgnored:
- lwz r3,RECEIVER(r3)
- blr
-
-LMsgSendSuperExit:
- END_ENTRY _objc_msgSendSuper
-
-
-/********************************************************************
- * struct_type objc_msgSendSuper_stret(objc_super *super,
- * SEL op,
- * ...);
- *
- * struct objc_super {
- * id receiver;
- * Class class;
- * };
- *
- *
- * objc_msgSendSuper_stret is the struct-return form of msgSendSuper.
- * The ABI calls for r3 to be used as the address of the structure
- * being returned, with the parameters in the succeeding registers.
- *
- * On entry: r3 is the address to which to copy the returned structure,
- * r4 is the address of the objc_super structure,
- * r5 is the selector
- ********************************************************************/
-
- ENTRY _objc_msgSendSuper2_stret_fixup_rtp
- ; objc_super->class is superclass of the class to search
- lwz r11, CLASS(r4)
- lwz r5, 4(r5) ; load _cmd from message_ref
- lwz r11, 4(r11) ; r11 = cls->super_class
- stw r11, CLASS(r4)
- b _objc_msgSendSuper_stret
- END_ENTRY _objc_msgSendSuper2_stret_fixup_rtp
-
- ENTRY _objc_msgSendSuper_stret
-; do profiling when enabled
- CALL_MCOUNT
-
-; search the cache
- lwz r12,CLASS(r4) ; class = super->class
- CacheLookup r5, LMsgSendSuperStretCacheMiss
- ; CacheLookup placed imp in r12
- mtctr r12
- lwz r4,RECEIVER(r4) ; receiver is the first arg
- li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward
- bctr ; goto *imp;
-
-; cache miss: go search the method lists
-LMsgSendSuperStretCacheMiss:
- MethodTableLookup STRUCT_RETURN, MSG_SENDSUPER
- lwz r4,RECEIVER(r4) ; receiver is the first arg
- li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward
- bctr ; goto *imp;
-
-LMsgSendSuperStretExit:
- END_ENTRY _objc_msgSendSuper_stret
-
-
-/********************************************************************
- *
- * Out-of-band parameter r11 indicates whether it was objc_msgSend or
- * objc_msgSend_stret that triggered the message forwarding. The
- *
- * Iff r11 == kFwdMsgSend, it is the word-return (objc_msgSend) case,
- * and the interface is:
- *
- * id _objc_msgForward(id self,
- * SEL sel,
- * ...);
- *
- * Iff r11 == kFwdMsgSendStret, it is the structure-return
- * (objc_msgSend_stret) case, and the interface is:
- *
- * struct_type _objc_msgForward(id self,
- * SEL sel,
- * ...);
- *
- * There are numerous reasons why it is better to have one
- * _objc_msgForward, rather than adding _objc_msgForward_stret.
- * The best one is that _objc_msgForward is the method that
- * gets cached when respondsToMethod returns false, and it
- * wouldnt know which one to use.
- *
- * Sends the message to a method having the signature
- *
- * - forward:(SEL)sel :(marg_list)args;
- *
- * But the marg_list is prepended with the 13 double precision
- * floating point registers that could be used as parameters into
- * the method (fortunately, the same registers are used for either
- * single or double precision floats). These registers are layed
- * down by _objc_msgForward, and picked up by _objc_msgSendv. So
- * the "marg_list" is actually:
- *
- * typedef struct objc_sendv_margs {
- * double floatingPointArgs[13];
- * intptr_t linkageArea[6];
- * intptr_t registerArgs[8];
- * intptr_t stackArgs[variable];
- * };
- *
- ********************************************************************/
-
-; _FwdSel is @selector(forward::), set up in map_images().
-; ALWAYS dereference _FwdSel to get to "forward::" !!
- .data
- .align 2
- .private_extern _FwdSel
-_FwdSel: .long 0
-
- .cstring
- .align 2
-LUnkSelStr: .ascii "Does not recognize selector %s\0"
-
- .data
- .align 2
- .private_extern __objc_forward_handler
-__objc_forward_handler: .long 0
-
- .data
- .align 2
- .private_extern __objc_forward_stret_handler
-__objc_forward_stret_handler: .long 0
-
-
- ENTRY __objc_msgForward
- // Non-stret version
- li r11,kFwdMsgSend
- b __objc_msgForward_internal
- END_ENTRY _objc_msgForward
-
- ENTRY __objc_msgForward_stret
- // Struct-return version
- li r11,kFwdMsgSendStret
- b __objc_msgForward_internal
- END_ENTRY _objc_msgForward_stret
-
-
- ENTRY __objc_msgForward_internal
- // Method cache version
-
- // THIS IS NOT A CALLABLE C FUNCTION
- // Out-of-band register %r11 is zero for stret, nonzero otherwise
-
-; do profiling when enabled
- CALL_MCOUNT
-
- ; Check return type (stret or not)
- cmplwi r11,kFwdMsgSendStret
- beq LMsgForwardStretSel
-
- ; Non-stret return
- ; Call user handler, if any
- LOAD_STATIC_WORD r12, __objc_forward_handler, LOCAL_SYMBOL
- cmplwi r12, 0
- mtctr r12
- bnectr ; call _objc_forward_handler if not NULL
- ; No user handler
- mr r11, r3 ; r11 = receiver
- mr r12, r4 ; r12 = SEL
- b LMsgForwardSelCmp
-
-LMsgForwardStretSel:
- ; Stret return
- ; Call user handler, if any
- LOAD_STATIC_WORD r12, __objc_forward_stret_handler, LOCAL_SYMBOL
- cmplwi r12, 0
- mtctr r12
- bnectr ; call _objc_forward_stret_handler if not NULL
- ; No user handler
- mr r11, r4 ; r11 = receiver
- mr r12, r5 ; r12 = SEL
-
-LMsgForwardSelCmp:
- ; r11 is the receiver
- ; r12 is the selector
-
- ; Die if forwarding "forward::"
- LOAD_STATIC_WORD r2, _FwdSel, LOCAL_SYMBOL
- cmplw r2, r12
- beq LMsgForwardError
-
- ; Save registers to margs
- ; Link register
- mflr r0
- stw r0, 8(r1)
-
- ; GPR parameters
- stw r3, 24(r1)
- stw r4, 28(r1)
- stw r5, 32(r1)
- stw r6, 36(r1)
- stw r7, 40(r1)
- stw r8, 44(r1)
- stw r9, 48(r1)
- stw r10,52(r1)
-
- ; FP parameters
- stfd f1, -104(r1)
- stfd f2, -96(r1)
- stfd f3, -88(r1)
- stfd f4, -80(r1)
- stfd f5, -72(r1)
- stfd f6, -64(r1)
- stfd f7, -56(r1)
- stfd f8, -48(r1)
- stfd f9, -40(r1)
- stfd f10, -32(r1)
- stfd f11, -24(r1)
- stfd f12, -16(r1)
- stfd f13, -8(r1)
-
- ; Call [receiver forward:sel :margs]
- mr r3, r11 ; receiver
- mr r4, r2 ; forward::
- mr r5, r12 ; sel
- subi r6,r1,13*8 ; &margs (on stack)
-
- stwu r1,-56-(13*8)(r1) ; push stack frame
- bl _objc_msgSend ; [self forward:sel :objc_sendv_margs]
- addi r1,r1,56+13*8 ; pop stack frame
-
- lwz r0,8(r1) ; restore lr
- mtlr r0 ;
- blr ;
-
-LMsgForwardError:
- ; Call __objc_error(receiver, "unknown selector %s", "forward::")
- mr r3, r11
- LEA_STATIC_DATA r4, LUnkSelStr, LOCAL_SYMBOL
- mr r5, r2
- CALL_EXTERN(___objc_error) ; never returns
- trap
-
- END_ENTRY __objc_msgForward_internal
-
-
-/********************************************************************
- * id objc_msgSendv(id self,
- * SEL op,
- * unsigned arg_size,
- * marg_list arg_frame);
- *
- * But the marg_list is prepended with the 13 double precision
- * floating point registers that could be used as parameters into
- * the method (fortunately, the same registers are used for either
- * single or double precision floats). These registers are layed
- * down by _objc_msgForward, and picked up by _objc_msgSendv. So
- * the "marg_list" is actually:
- *
- * typedef struct objc_sendv_margs {
- * double floatingPointArgs[13];
- * int linkageArea[6];
- * int registerArgs[8];
- * int stackArgs[variable];
- * };
- *
- * arg_size is the number of bytes of parameters in registerArgs and
- * stackArgs combined (i.e. it is method_getSizeOfArguments(method)).
- * Specifically, it is NOT the overall arg_frame size, because that
- * would include the floatingPointArgs and linkageArea, which are
- * PowerPC-specific. This is consistent with the other architectures.
- ********************************************************************/
-
- ENTRY _objc_msgSendv
-
-#if !defined(KERNEL)
-; do profiling when enabled
- CALL_MCOUNT
-
- mflr r0
- stw r0,8(r1) ; save lr
-
- cmplwi r5,32 ; check parameter size against minimum
- ble+ LMsgSendvMinFrame ; is less than minimum, go use minimum
- mr r12,r1 ; remember current stack pointer
- sub r11,r1,r5 ; push parameter area
- rlwinm r1,r11,0,0,27 ; align stack pointer to 16 byte boundary
- stwu r12,-32(r1) ; push aligned linkage area, set stack link
- b LMsgSendvHaveFrame
-
-LMsgSendvMinFrame:
- stwu r1,-64(r1) ; push aligned linkage and parameter areas, set stack link
-
-LMsgSendvHaveFrame:
- ; restore floating point register parameters from marg_list
- lfd f1, 0(r6) ;
- lfd f2, 8(r6) ;
- lfd f3, 16(r6) ;
- lfd f4, 24(r6) ;
- lfd f5, 32(r6) ;
- lfd f6, 40(r6) ;
- lfd f7, 48(r6) ;
- lfd f8, 56(r6) ;
- lfd f9, 64(r6) ;
- lfd f10,72(r6) ;
- lfd f11,80(r6) ;
- lfd f12,88(r6) ;
- lfd f13,96(r6) ;
-
-; load the register based arguments from the marg_list
-; the first two parameters are already in r3 and r4, respectively
- subi r0,r5,(2*4)-3 ; make word count from byte count rounded up to multiple of 4...
- srwi. r0,r0,2 ; ... and subtracting for params already in r3 and r4
- beq LMsgSendvSendIt ; branch if there are no parameters to load
- mtctr r0 ; counter = number of remaining words
- lwz r5,32+(13*8)(r6) ; load 3rd parameter
- bdz LMsgSendvSendIt ; decrement counter, branch if result is zero
- addi r11,r6,36+(13*8) ; switch to r11, because we are setting r6
- lwz r6,0(r11) ; load 4th parameter
- bdz LMsgSendvSendIt ; decrement counter, branch if result is zero
- lwz r7,4(r11) ; load 5th parameter
- bdz LMsgSendvSendIt ; decrement counter, branch if result is zero
- lwz r8,8(r11) ; load 6th parameter
- bdz LMsgSendvSendIt ; decrement counter, branch if result is zero
- lwz r9,12(r11) ; load 7th parameter
- bdz LMsgSendvSendIt ; decrement counter, branch if result is zero
- lwzu r10,16(r11) ; load 8th parameter, and update r11
- bdz LMsgSendvSendIt ; decrement counter, branch if result is zero
-
-; copy the stack based arguments from the marg_list
- addi r12,r1,24+32-4 ; target = address of stack based parameters
-LMsgSendvArgLoop:
- lwzu r0,4(r11) ; loop to copy remaining marg_list words to stack
- stwu r0,4(r12) ;
- bdnz LMsgSendvArgLoop ; decrement counter, branch if still non-zero
-
-LMsgSendvSendIt:
- bl _objc_msgSend ; objc_msgSend (self, selector, ...)
-
- lwz r1,0(r1) ; restore stack pointer
- lwz r0,8(r1) ; restore lr
- mtlr r0 ;
- blr ;
-#else
- trap ; _objc_msgSendv is not for the kernel
-#endif
-
- END_ENTRY _objc_msgSendv
-
-/********************************************************************
- * double objc_msgSendv_fpret(id self, SEL op, unsigned arg_size,
- * marg_list arg_frame);
- ********************************************************************/
-
- ENTRY _objc_msgSendv_fpret
- b _objc_msgSendv
- END_ENTRY _objc_msgSendv_fpret
-
-/********************************************************************
- * void objc_msgSendv_stret(void *structStorage,
- * id self,
- * SEL op,
- * unsigned arg_size,
- * marg_list arg_frame);
- *
- * objc_msgSendv_stret is the struct-return form of msgSendv.
- * This function does not use the struct-return ABI; instead, the
- * structure return address is passed as a normal parameter.
- * The two are functionally identical on ppc, but not on other architectures.
- *
- * On entry: r3 is the address in which the returned struct is put,
- * r4 is the message receiver,
- * r5 is the selector,
- * r6 is the size of the marg_list, in bytes,
- * r7 is the address of the marg_list
- ********************************************************************/
-
- ENTRY _objc_msgSendv_stret
-
-#if !defined(KERNEL)
-; do profiling when enabled
- CALL_MCOUNT
-
- mflr r0
- stw r0,8(r1) ; (save return pc)
-
- cmplwi r6,32 ; check parameter size against minimum
- ble+ LMsgSendvStretMinFrame ; is less than minimum, go use minimum
- mr r12,r1 ; remember current stack pointer
- sub r11,r1,r6 ; push parameter area
- rlwinm r1,r11,0,0,27 ; align stack pointer to 16 byte boundary
- stwu r12,-32(r1) ; push aligned linkage area, set stack link
- b LMsgSendvStretHaveFrame
-
-LMsgSendvStretMinFrame:
- stwu r1,-64(r1) ; push aligned linkage and parameter areas, set stack link
-
-LMsgSendvStretHaveFrame:
-; restore floating point register parameters from marg_list
- lfd f1,0(r7) ;
- lfd f2,8(r7) ;
- lfd f3,16(r7) ;
- lfd f4,24(r7) ;
- lfd f5,32(r7) ;
- lfd f6,40(r7) ;
- lfd f7,48(r7) ;
- lfd f8,56(r7) ;
- lfd f9,64(r7) ;
- lfd f10,72(r7) ;
- lfd f11,80(r7) ;
- lfd f12,88(r7) ;
- lfd f13,96(r7) ;
-
-; load the register based arguments from the marg_list
-; the structure return address and the first two parameters
-; are already in r3, r4, and r5, respectively.
-; NOTE: The callers r3 probably, but not necessarily, matches
-; the r3 in the marg_list. That is, the struct-return
-; storage used by the caller could be an intermediate buffer
-; that will end up being copied into the original
-; struct-return buffer (pointed to by the marg_listed r3).
- subi r0,r6,(3*4)-3 ; make word count from byte count rounded up to multiple of 4...
- srwi. r0,r0,2 ; ... and subtracting for params already in r3 and r4 and r5
- beq LMsgSendvStretSendIt ; branch if there are no parameters to load
- mtctr r0 ; counter = number of remaining words
- lwz r6,36+(13*8)(r7) ; load 4th parameter
- bdz LMsgSendvStretSendIt ; decrement counter, branch if result is zero
- addi r11,r7,40+(13*8) ; switch to r11, because we are setting r7
- lwz r7,0(r11) ; load 5th parameter
- bdz LMsgSendvStretSendIt ; decrement counter, branch if result is zero
- lwz r8,4(r11) ; load 6th parameter
- bdz LMsgSendvStretSendIt ; decrement counter, branch if result is zero
- lwz r9,8(r11) ; load 7th parameter
- bdz LMsgSendvStretSendIt ; decrement counter, branch if result is zero
- lwzu r10,12(r11) ; load 8th parameter, and update r11
- bdz LMsgSendvStretSendIt ; decrement counter, branch if result is zero
-
-; copy the stack based arguments from the marg_list
- addi r12,r1,24+32-4 ; target = address of stack based parameters
-LMsgSendvStretArgLoop:
- lwzu r0,4(r11) ; loop to copy remaining marg_list words to stack
- stwu r0,4(r12) ;
- bdnz LMsgSendvStretArgLoop ; decrement counter, branch if still non-zero
-
-LMsgSendvStretSendIt:
- bl _objc_msgSend_stret ; struct_type objc_msgSend_stret (self, selector, ...)
-
- lwz r1,0(r1) ; restore stack pointer
- lwz r0,8(r1) ; restore return pc
- mtlr r0
- blr ; return
-#else /* KERNEL */
- trap ; _objc_msgSendv_stret is not for the kernel
-#endif /* !KERNEL */
-
- END_ENTRY _objc_msgSendv_stret
-
-
- ENTRY _method_invoke
-
- lwz r12, METHOD_IMP(r4)
- lwz r4, METHOD_NAME(r4)
- mtctr r12
- bctr
-
- END_ENTRY _method_invoke
-
-
- ENTRY _method_invoke_stret
-
- lwz r12, METHOD_IMP(r5)
- lwz r5, METHOD_NAME(r5)
- mtctr r12
- bctr
-
- END_ENTRY _method_invoke_stret
-
-#endif
--- /dev/null
+/*
+ * Copyright (c) 1999-2009 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <TargetConditionals.h>
+#if defined(__i386__) && TARGET_IPHONE_SIMULATOR
+
+#define __OBJC2__ 1
+
+#include "objc-config.h"
+
+.data
+
+// Substitute receiver for messages sent to nil (usually also nil)
+// id _objc_nilReceiver
+.align 4
+.private_extern __objc_nilReceiver
+__objc_nilReceiver:
+ .long 0
+
+// _objc_entryPoints and _objc_exitPoints are used by objc
+// to get the critical regions for which method caches
+// cannot be garbage collected.
+
+.private_extern _objc_entryPoints
+_objc_entryPoints:
+ .long __cache_getImp
+ .long __cache_getMethod
+ .long _objc_msgSend
+ .long _objc_msgSend_fpret
+ .long _objc_msgSend_stret
+ .long _objc_msgSendSuper
+ .long _objc_msgSendSuper2
+ .long _objc_msgSendSuper_stret
+ .long _objc_msgSendSuper2_stret
+ .long 0
+
+.private_extern _objc_exitPoints
+_objc_exitPoints:
+ .long LGetImpExit
+ .long LGetMethodExit
+ .long LMsgSendExit
+ .long LMsgSendFpretExit
+ .long LMsgSendStretExit
+ .long LMsgSendSuperExit
+ .long LMsgSendSuper2Exit
+ .long LMsgSendSuperStretExit
+ .long LMsgSendSuper2StretExit
+ .long 0
+
+
+/********************************************************************
+ *
+ * Structure definitions.
+ *
+ ********************************************************************/
+
+// Offsets from %esp
+ self = 4
+ super = 4
+ selector = 8
+ marg_size = 12
+ marg_list = 16
+ first_arg = 12
+
+ struct_addr = 4
+
+ self_stret = 8
+ super_stret = 8
+ selector_stret = 12
+ marg_size_stret = 16
+ marg_list_stret = 20
+
+// objc_super parameter to sendSuper
+ receiver = 0
+ class = 4
+
+// Selected field offsets in class structure
+ isa = 0
+ superclass = 4
+#if __OBJC2__
+ cache = 8
+#else
+ cache = 32
+#endif
+
+// Method descriptor
+ method_name = 0
+ method_imp = 8
+
+// Cache header
+ mask = 0
+ occupied = 4
+ buckets = 8 // variable length array
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// ENTRY functionName
+//
+// Assembly directives to begin an exported function.
+//
+// Takes: functionName - name of the exported function
+//////////////////////////////////////////////////////////////////////
+
+.macro ENTRY
+ .text
+ .globl $0
+ .align 2, 0x90
+$0:
+.endmacro
+
+.macro STATIC_ENTRY
+ .text
+ .private_extern $0
+ .align 4, 0x90
+$0:
+.endmacro
+
+//////////////////////////////////////////////////////////////////////
+//
+// END_ENTRY functionName
+//
+// Assembly directives to end an exported function. Just a placeholder,
+// a close-parenthesis for ENTRY, until it is needed for something.
+//
+// Takes: functionName - name of the exported function
+//////////////////////////////////////////////////////////////////////
+
+.macro END_ENTRY
+.endmacro
+
+
+/////////////////////////////////////////////////////////////////////
+//
+//
+// CacheLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER | MSG_SENDSUPER2 | CACHE_GET, cacheMissLabel
+//
+// Locate the implementation for a selector in a class method cache.
+//
+// Takes: WORD_RETURN (first parameter is at sp+4)
+// STRUCT_RETURN (struct address is at sp+4, first parameter at sp+8)
+// MSG_SEND (first parameter is receiver)
+// MSG_SENDSUPER[2] (first parameter is address of objc_super structure)
+// CACHE_GET (first parameter is class; return method triplet)
+// selector in %ecx
+// class to search in %edx
+//
+// cacheMissLabel = label to branch to iff method is not cached
+//
+// On exit: (found) MSG_SEND and MSG_SENDSUPER[2]: return imp in eax
+// (found) CACHE_GET: return method triplet in eax
+// (not found) jumps to cacheMissLabel
+//
+/////////////////////////////////////////////////////////////////////
+
+
+// Values to specify to method lookup macros whether the return type of
+// the method is word or structure.
+WORD_RETURN = 0
+STRUCT_RETURN = 1
+
+// Values to specify to method lookup macros whether the first argument
+// is an object/class reference or a 'objc_super' structure.
+MSG_SEND = 0 // first argument is receiver, search the isa
+MSG_SENDSUPER = 1 // first argument is objc_super, search the class
+MSG_SENDSUPER2 = 2 // first argument is objc_super, search the class
+CACHE_GET = 3 // first argument is class, search that class
+
+.macro CacheLookup
+
+// load variables and save caller registers.
+
+ pushl %edi // save scratch register
+ movl cache(%edx), %edi // cache = class->cache
+ pushl %esi // save scratch register
+
+ movl mask(%edi), %esi // mask = cache->mask
+ movl %ecx, %edx // index = selector
+ shrl $$2, %edx // index = selector >> 2
+
+// search the receiver's cache
+// ecx = selector
+// edi = cache
+// esi = mask
+// edx = index
+// eax = method (soon)
+LMsgSendProbeCache_$0_$1_$2:
+ andl %esi, %edx // index &= mask
+ movl buckets(%edi, %edx, 4), %eax // meth = cache->buckets[index]
+
+ testl %eax, %eax // check for end of bucket
+ je LMsgSendCacheMiss_$0_$1_$2 // go to cache miss code
+ cmpl method_name(%eax), %ecx // check for method name match
+ je LMsgSendCacheHit_$0_$1_$2 // go handle cache hit
+ addl $$1, %edx // bump index ...
+ jmp LMsgSendProbeCache_$0_$1_$2 // ... and loop
+
+// not found in cache: restore state and go to callers handler
+LMsgSendCacheMiss_$0_$1_$2:
+
+.if $0 == WORD_RETURN // Regular word return
+.if $1 == MSG_SEND // MSG_SEND
+ popl %esi // restore callers register
+ popl %edi // restore callers register
+ movl self(%esp), %eax // get messaged object
+ movl isa(%eax), %eax // get objects class
+.elseif $1 == MSG_SENDSUPER || $1 == MSG_SENDSUPER2 // MSG_SENDSUPER[2]
+ // replace "super" arg with "receiver"
+ movl super+8(%esp), %edi // get super structure
+ movl receiver(%edi), %esi // get messaged object
+ movl %esi, super+8(%esp) // make it the first argument
+ movl class(%edi), %eax // get messaged class
+ .if $1 == MSG_SENDSUPER2
+ movl superclass(%eax), %eax // get messaged class
+ .endif
+ popl %esi // restore callers register
+ popl %edi // restore callers register
+.else // CACHE_GET
+ popl %esi // restore callers register
+ popl %edi // restore callers register
+.endif
+.else // Struct return
+.if $1 == MSG_SEND // MSG_SEND (stret)
+ popl %esi // restore callers register
+ popl %edi // restore callers register
+ movl self_stret(%esp), %eax // get messaged object
+ movl isa(%eax), %eax // get objects class
+.elseif $1 == MSG_SENDSUPER || $1 == MSG_SENDSUPER2 // MSG_SENDSUPER[2] (stret)
+ // replace "super" arg with "receiver"
+ movl super_stret+8(%esp), %edi// get super structure
+ movl receiver(%edi), %esi // get messaged object
+ movl %esi, super_stret+8(%esp)// make it the first argument
+ movl class(%edi), %eax // get messaged class
+ .if $1 == MSG_SENDSUPER2
+ movl superclass(%eax), %eax // get messaged class
+ .endif
+ popl %esi // restore callers register
+ popl %edi // restore callers register
+.else // CACHE_GET
+ !! This should not happen.
+.endif
+.endif
+
+ jmp $2 // go to callers handler
+
+// eax points to matching cache entry
+ .align 4, 0x90
+LMsgSendCacheHit_$0_$1_$2:
+
+// load implementation address, restore state, and we're done
+.if $1 == CACHE_GET
+ // method triplet is already in eax
+.else
+ movl method_imp(%eax), %eax // imp = method->method_imp
+.endif
+
+.if $0 == WORD_RETURN // Regular word return
+.if $1 == MSG_SENDSUPER || $1 == MSG_SENDSUPER2
+ // replace "super" arg with "self"
+ movl super+8(%esp), %edi
+ movl receiver(%edi), %esi
+ movl %esi, super+8(%esp)
+.endif
+.else // Struct return
+.if $1 == MSG_SENDSUPER || $1 == MSG_SENDSUPER2
+ // replace "super" arg with "self"
+ movl super_stret+8(%esp), %edi
+ movl receiver(%edi), %esi
+ movl %esi, super_stret+8(%esp)
+.endif
+.endif
+
+ // restore caller registers
+ popl %esi
+ popl %edi
+.endmacro
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// MethodTableLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER
+//
+// Takes: WORD_RETURN (first parameter is at sp+4)
+// STRUCT_RETURN (struct address is at sp+4, first parameter at sp+8)
+// MSG_SEND (first parameter is receiver)
+// MSG_SENDSUPER (first parameter is address of objc_super structure)
+//
+// Stack must be at 0xXXXXXXXc on entrance.
+//
+// On exit: Register parameters restored from CacheLookup
+// imp in eax
+//
+/////////////////////////////////////////////////////////////////////
+
+.macro MethodTableLookup
+
+ subl $$4, %esp // 16-byte align the stack
+ // push args (class, selector)
+ pushl %ecx
+ pushl %eax
+ call __class_lookupMethodAndLoadCache
+ addl $$12, %esp // pop parameters and alignment
+.endmacro
+
+
+/********************************************************************
+ * Method _cache_getMethod(Class cls, SEL sel, IMP msgForward_internal_imp)
+ *
+ * If found, returns method triplet pointer.
+ * If not found, returns NULL.
+ *
+ * NOTE: _cache_getMethod never returns any cache entry whose implementation
+ * is _objc_msgForward_internal. It returns 1 instead. This prevents thread-
+ * safety and memory management bugs in _class_lookupMethodAndLoadCache.
+ * See _class_lookupMethodAndLoadCache for details.
+ *
+ * _objc_msgForward_internal is passed as a parameter because it's more
+ * efficient to do the (PIC) lookup once in the caller than repeatedly here.
+ ********************************************************************/
+
+ .private_extern __cache_getMethod
+ ENTRY __cache_getMethod
+
+// load the class and selector
+ movl selector(%esp), %ecx
+ movl self(%esp), %edx
+
+// do lookup
+ CacheLookup WORD_RETURN, CACHE_GET, LGetMethodMiss
+
+// cache hit, method triplet in %eax
+ movl first_arg(%esp), %ecx // check for _objc_msgForward_internal
+ cmpl method_imp(%eax), %ecx // if (imp==_objc_msgForward_internal)
+ je 1f // return (Method)1
+ ret // else return method triplet address
+1: movl $1, %eax
+ ret
+
+LGetMethodMiss:
+// cache miss, return nil
+ xorl %eax, %eax // zero %eax
+ ret
+
+LGetMethodExit:
+ END_ENTRY __cache_getMethod
+
+
+/********************************************************************
+ * IMP _cache_getImp(Class cls, SEL sel)
+ *
+ * If found, returns method implementation.
+ * If not found, returns NULL.
+ ********************************************************************/
+
+ .private_extern __cache_getImp
+ ENTRY __cache_getImp
+
+// load the class and selector
+ movl selector(%esp), %ecx
+ movl self(%esp), %edx
+
+// do lookup
+ CacheLookup WORD_RETURN, CACHE_GET, LGetImpMiss
+
+// cache hit, method triplet in %eax
+ movl method_imp(%eax), %eax // return method imp
+ ret
+
+LGetImpMiss:
+// cache miss, return nil
+ xorl %eax, %eax // zero %eax
+ ret
+
+LGetImpExit:
+ END_ENTRY __cache_getImp
+
+
+/********************************************************************
+ *
+ * id objc_msgSend(id self, SEL _cmd,...);
+ *
+ ********************************************************************/
+
+ ENTRY _objc_msgSend
+
+// load receiver and selector
+ movl selector(%esp), %ecx
+ movl self(%esp), %eax
+
+#if SUPPORT_IGNORED_SELECTOR_CONSTANT
+// check whether selector is ignored
+ cmpl $ kIgnore, %ecx
+ je LMsgSendDone // return self from %eax
+#endif
+
+// check whether receiver is nil
+ testl %eax, %eax
+ je LMsgSendNilSelf
+
+// receiver (in %eax) is non-nil: search the cache
+LMsgSendReceiverOk:
+ movl isa(%eax), %edx // class = self->isa
+ CacheLookup WORD_RETURN, MSG_SEND, LMsgSendCacheMiss
+ xor %edx, %edx // set nonstret for msgForward_internal
+ jmp *%eax
+
+// cache miss: go search the method lists
+LMsgSendCacheMiss:
+ MethodTableLookup WORD_RETURN, MSG_SEND
+ xor %edx, %edx // set nonstret for msgForward_internal
+ jmp *%eax // goto *imp
+
+// message sent to nil: redirect to nil receiver, if any
+LMsgSendNilSelf:
+ call 1f // load new receiver
+1: popl %edx
+ movl __objc_nilReceiver-1b(%edx),%eax
+ testl %eax, %eax // return nil if no new receiver
+ je LMsgSendReturnZero
+ movl %eax, self(%esp) // send to new receiver
+ jmp LMsgSendReceiverOk // receiver must be in %eax
+LMsgSendReturnZero:
+ // %eax is already zero
+ movl $0,%edx
+LMsgSendDone:
+ ret
+LMsgSendExit:
+ END_ENTRY _objc_msgSend
+
+/********************************************************************
+ *
+ * id objc_msgSendSuper(struct objc_super *super, SEL _cmd,...);
+ *
+ * struct objc_super {
+ * id receiver;
+ * Class class;
+ * };
+ ********************************************************************/
+
+ ENTRY _objc_msgSendSuper
+
+// load selector and class to search
+ movl super(%esp), %eax // struct objc_super
+ movl selector(%esp), %ecx
+ movl class(%eax), %edx // struct objc_super->class
+
+#if SUPPORT_IGNORED_SELECTOR_CONSTANT
+// check whether selector is ignored
+ cmpl $ kIgnore, %ecx
+ je LMsgSendSuperIgnored // return self from %eax
+#endif
+
+// search the cache (class in %edx)
+ CacheLookup WORD_RETURN, MSG_SENDSUPER, LMsgSendSuperCacheMiss
+ xor %edx, %edx // set nonstret for msgForward_internal
+ jmp *%eax // goto *imp
+
+// cache miss: go search the method lists
+LMsgSendSuperCacheMiss:
+ MethodTableLookup WORD_RETURN, MSG_SENDSUPER
+ xor %edx, %edx // set nonstret for msgForward_internal
+ jmp *%eax // goto *imp
+
+// ignored selector: return self
+LMsgSendSuperIgnored:
+ movl super(%esp), %eax
+ movl receiver(%eax), %eax
+ ret
+
+LMsgSendSuperExit:
+ END_ENTRY _objc_msgSendSuper
+
+
+ ENTRY _objc_msgSendSuper2
+
+// load selector and class to search
+ movl super(%esp), %eax // struct objc_super
+ movl selector(%esp), %ecx
+ movl class(%eax), %eax // struct objc_super->class
+ mov superclass(%eax), %edx // edx = objc_super->class->super_class
+
+#if SUPPORT_IGNORED_SELECTOR_CONSTANT
+// check whether selector is ignored
+ cmpl $ kIgnore, %ecx
+ je LMsgSendSuperIgnored // return self from %eax
+#endif
+
+// search the cache (class in %edx)
+ CacheLookup WORD_RETURN, MSG_SENDSUPER2, LMsgSendSuper2CacheMiss
+ xor %edx, %edx // set nonstret for msgForward_internal
+ jmp *%eax // goto *imp
+
+// cache miss: go search the method lists
+LMsgSendSuper2CacheMiss:
+ MethodTableLookup WORD_RETURN, MSG_SENDSUPER2
+ xor %edx, %edx // set nonstret for msgForward_internal
+ jmp *%eax // goto *imp
+
+// ignored selector: return self
+LMsgSendSuper2Ignored:
+ movl super(%esp), %eax
+ movl receiver(%eax), %eax
+ ret
+
+LMsgSendSuper2Exit:
+ END_ENTRY _objc_msgSendSuper2
+
+
+/********************************************************************
+ *
+ * double objc_msgSend_fpret(id self, SEL _cmd,...);
+ *
+ ********************************************************************/
+
+ ENTRY _objc_msgSend_fpret
+
+// load receiver and selector
+ movl selector(%esp), %ecx
+ movl self(%esp), %eax
+
+#if SUPPORT_IGNORED_SELECTOR_CONSTANT
+// check whether selector is ignored
+ cmpl $ kIgnore, %ecx
+ je LMsgSendFpretDone // return self from %eax
+#endif
+
+// check whether receiver is nil
+ testl %eax, %eax
+ je LMsgSendFpretNilSelf
+
+// receiver (in %eax) is non-nil: search the cache
+LMsgSendFpretReceiverOk:
+ movl isa(%eax), %edx // class = self->isa
+ CacheLookup WORD_RETURN, MSG_SEND, LMsgSendFpretCacheMiss
+ xor %edx, %edx // set nonstret for msgForward_internal
+ jmp *%eax // goto *imp
+
+// cache miss: go search the method lists
+LMsgSendFpretCacheMiss:
+ MethodTableLookup WORD_RETURN, MSG_SEND
+ xor %edx, %edx // set nonstret for msgForward_internal
+ jmp *%eax // goto *imp
+
+// message sent to nil: redirect to nil receiver, if any
+LMsgSendFpretNilSelf:
+ call 1f // load new receiver
+1: popl %edx
+ movl __objc_nilReceiver-1b(%edx),%eax
+ testl %eax, %eax // return zero if no new receiver
+ je LMsgSendFpretReturnZero
+ movl %eax, self(%esp) // send to new receiver
+ jmp LMsgSendFpretReceiverOk // receiver must be in %eax
+LMsgSendFpretReturnZero:
+ fldz
+LMsgSendFpretDone:
+ ret
+
+LMsgSendFpretExit:
+ END_ENTRY _objc_msgSend_fpret
+
+
+/********************************************************************
+ *
+ * void objc_msgSend_stret(void *st_addr , id self, SEL _cmd, ...);
+ *
+ *
+ * objc_msgSend_stret is the struct-return form of msgSend.
+ * The ABI calls for (sp+4) to be used as the address of the structure
+ * being returned, with the parameters in the succeeding locations.
+ *
+ * On entry: (sp+4)is the address where the structure is returned,
+ * (sp+8) is the message receiver,
+ * (sp+12) is the selector
+ ********************************************************************/
+
+ ENTRY _objc_msgSend_stret
+
+// load receiver and selector
+ movl self_stret(%esp), %eax
+ movl (selector_stret)(%esp), %ecx
+
+// check whether receiver is nil
+ testl %eax, %eax
+ je LMsgSendStretNilSelf
+
+// receiver (in %eax) is non-nil: search the cache
+LMsgSendStretReceiverOk:
+ movl isa(%eax), %edx // class = self->isa
+ CacheLookup STRUCT_RETURN, MSG_SEND, LMsgSendStretCacheMiss
+ movl $1, %edx // set stret for objc_msgForward
+ jmp *%eax // goto *imp
+
+// cache miss: go search the method lists
+LMsgSendStretCacheMiss:
+ MethodTableLookup STRUCT_RETURN, MSG_SEND
+ movl $1, %edx // set stret for objc_msgForward
+ jmp *%eax // goto *imp
+
+// message sent to nil: redirect to nil receiver, if any
+LMsgSendStretNilSelf:
+ call 1f // load new receiver
+1: popl %edx
+ movl __objc_nilReceiver-1b(%edx),%eax
+ testl %eax, %eax // return nil if no new receiver
+ je LMsgSendStretDone
+ movl %eax, self_stret(%esp) // send to new receiver
+ jmp LMsgSendStretReceiverOk // receiver must be in %eax
+LMsgSendStretDone:
+ ret $4 // pop struct return address (#2995932)
+LMsgSendStretExit:
+ END_ENTRY _objc_msgSend_stret
+
+/********************************************************************
+ *
+ * void objc_msgSendSuper_stret(void *st_addr, struct objc_super *super, SEL _cmd, ...);
+ *
+ * struct objc_super {
+ * id receiver;
+ * Class class;
+ * };
+ *
+ * objc_msgSendSuper_stret is the struct-return form of msgSendSuper.
+ * The ABI calls for (sp+4) to be used as the address of the structure
+ * being returned, with the parameters in the succeeding registers.
+ *
+ * On entry: (sp+4)is the address where the structure is returned,
+ * (sp+8) is the address of the objc_super structure,
+ * (sp+12) is the selector
+ *
+ ********************************************************************/
+
+ ENTRY _objc_msgSendSuper_stret
+
+// load selector and class to search
+ movl super_stret(%esp), %eax // struct objc_super
+ movl (selector_stret)(%esp), %ecx // get selector
+ movl class(%eax), %edx // struct objc_super->class
+
+// search the cache (class in %edx)
+ CacheLookup STRUCT_RETURN, MSG_SENDSUPER, LMsgSendSuperStretCacheMiss
+ movl $1, %edx // set stret for objc_msgForward
+ jmp *%eax // goto *imp
+
+// cache miss: go search the method lists
+LMsgSendSuperStretCacheMiss:
+ MethodTableLookup STRUCT_RETURN, MSG_SENDSUPER
+ movl $1, %edx // set stret for objc_msgForward
+ jmp *%eax // goto *imp
+
+LMsgSendSuperStretExit:
+ END_ENTRY _objc_msgSendSuper_stret
+
+
+ ENTRY _objc_msgSendSuper2_stret
+
+// load selector and class to search
+ movl super_stret(%esp), %eax // struct objc_super
+ movl (selector_stret)(%esp), %ecx // get selector
+ movl class(%eax), %eax // struct objc_super->class
+ mov superclass(%eax), %edx // edx = objc_super->class->super_class
+
+// search the cache (class in %edx)
+ CacheLookup STRUCT_RETURN, MSG_SENDSUPER2, LMsgSendSuper2StretCacheMiss
+ movl $1, %edx // set stret for objc_msgForward
+ jmp *%eax // goto *imp
+
+// cache miss: go search the method lists
+LMsgSendSuper2StretCacheMiss:
+ MethodTableLookup STRUCT_RETURN, MSG_SENDSUPER2
+ movl $1, %edx // set stret for objc_msgForward
+ jmp *%eax // goto *imp
+
+LMsgSendSuper2StretExit:
+ END_ENTRY _objc_msgSendSuper2_stret
+
+
+/********************************************************************
+ *
+ * id _objc_msgForward(id self, SEL _cmd,...);
+ *
+ ********************************************************************/
+
+// _FwdSel is @selector(forward::), set up in map_images().
+// ALWAYS dereference _FwdSel to get to "forward::" !!
+ .data
+ .align 2
+ .private_extern _FwdSel
+_FwdSel: .long 0
+
+
+ .cstring
+ .align 2
+LUnkSelStr: .ascii "Does not recognize selector %s\0"
+
+ .data
+ .align 2
+ .private_extern __objc_forward_handler
+__objc_forward_handler: .long 0
+
+ .data
+ .align 2
+ .private_extern __objc_forward_stret_handler
+__objc_forward_stret_handler: .long 0
+
+ ENTRY __objc_msgForward_internal
+ .private_extern __objc_msgForward_internal
+ // Method cache version
+
+ // THIS IS NOT A CALLABLE C FUNCTION
+ // Out-of-band register %edx is nonzero for stret, zero otherwise
+
+ // Check return type (stret or not)
+ testl %edx, %edx
+ jnz __objc_msgForward_stret
+ jmp __objc_msgForward
+
+ END_ENTRY _objc_msgForward_internal
+
+
+ ENTRY __objc_msgForward
+ // Non-struct return version
+
+ // Get PIC base into %edx
+ call L__objc_msgForward$pic_base
+L__objc_msgForward$pic_base:
+ popl %edx
+
+ // Call user handler, if any
+ movl __objc_forward_handler-L__objc_msgForward$pic_base(%edx),%ecx
+ testl %ecx, %ecx // if not NULL
+ je 1f // skip to default handler
+ jmp *%ecx // call __objc_forward_handler
+1:
+ // No user handler
+ // Push stack frame
+ pushl %ebp
+ movl %esp, %ebp
+
+ // Die if forwarding "forward::"
+ movl (selector+4)(%ebp), %eax
+ movl _FwdSel-L__objc_msgForward$pic_base(%edx),%ecx
+ cmpl %ecx, %eax
+ je LMsgForwardError
+
+ // Call [receiver forward:sel :margs]
+ subl $8, %esp // 16-byte align the stack
+ leal (self+4)(%ebp), %ecx
+ pushl %ecx // &margs
+ pushl %eax // sel
+ movl _FwdSel-L__objc_msgForward$pic_base(%edx),%ecx
+ pushl %ecx // forward::
+ pushl (self+4)(%ebp) // receiver
+
+ call _objc_msgSend
+
+ movl %ebp, %esp
+ popl %ebp
+ ret
+
+LMsgForwardError:
+ // Call __objc_error(receiver, "unknown selector %s", "forward::")
+ subl $12, %esp // 16-byte align the stack
+ movl _FwdSel-L__objc_msgForward$pic_base(%edx),%eax
+ pushl %eax
+ leal LUnkSelStr-L__objc_msgForward$pic_base(%edx),%eax
+ pushl %eax
+ pushl (self+4)(%ebp)
+ call ___objc_error // never returns
+
+ END_ENTRY __objc_msgForward
+
+
+ ENTRY __objc_msgForward_stret
+ // Struct return version
+
+ // Get PIC base into %edx
+ call L__objc_msgForwardStret$pic_base
+L__objc_msgForwardStret$pic_base:
+ popl %edx
+
+ // Call user handler, if any
+ movl __objc_forward_stret_handler-L__objc_msgForwardStret$pic_base(%edx), %ecx
+ testl %ecx, %ecx // if not NULL
+ je 1f // skip to default handler
+ jmp *%ecx // call __objc_forward_stret_handler
+1:
+ // No user handler
+ // Push stack frame
+ pushl %ebp
+ movl %esp, %ebp
+
+ // Die if forwarding "forward::"
+ movl (selector_stret+4)(%ebp), %eax
+ movl _FwdSel-L__objc_msgForwardStret$pic_base(%edx), %ecx
+ cmpl %ecx, %eax
+ je LMsgForwardStretError
+
+ // Call [receiver forward:sel :margs]
+ subl $8, %esp // 16-byte align the stack
+ leal (self_stret+4)(%ebp), %ecx
+ pushl %ecx // &margs
+ pushl %eax // sel
+ movl _FwdSel-L__objc_msgForwardStret$pic_base(%edx),%ecx
+ pushl %ecx // forward::
+ pushl (self_stret+4)(%ebp) // receiver
+
+ call _objc_msgSend
+
+ movl %ebp, %esp
+ popl %ebp
+ ret $4 // pop struct return address (#2995932)
+
+LMsgForwardStretError:
+ // Call __objc_error(receiver, "unknown selector %s", "forward::")
+ subl $12, %esp // 16-byte align the stack
+ leal _FwdSel-L__objc_msgForwardStret$pic_base(%edx),%eax
+ pushl %eax
+ leal LUnkSelStr-L__objc_msgForwardStret$pic_base(%edx),%eax
+ pushl %eax
+ pushl (self_stret+4)(%ebp)
+ call ___objc_error // never returns
+
+ END_ENTRY __objc_msgForward_stret
+
+
+ ENTRY _objc_msgSend_debug
+ jmp _objc_msgSend
+ END_ENTRY _objc_msgSend_debug
+
+ ENTRY _objc_msgSendSuper2_debug
+ jmp _objc_msgSendSuper2
+ END_ENTRY _objc_msgSendSuper2_debug
+
+ ENTRY _objc_msgSend_stret_debug
+ jmp _objc_msgSend_stret
+ END_ENTRY _objc_msgSend_stret_debug
+
+ ENTRY _objc_msgSendSuper2_stret_debug
+ jmp _objc_msgSendSuper2_stret
+ END_ENTRY _objc_msgSendSuper2_stret_debug
+
+ ENTRY _objc_msgSend_fpret_debug
+ jmp _objc_msgSend_fpret
+ END_ENTRY _objc_msgSend_fpret_debug
+
+
+ ENTRY _objc_msgSend_noarg
+ jmp _objc_msgSend
+ END_ENTRY _objc_msgSend_noarg
+
+
+ ENTRY _method_invoke
+
+ movl selector(%esp), %ecx
+ movl method_name(%ecx), %edx
+ movl method_imp(%ecx), %eax
+ movl %edx, selector(%esp)
+ jmp *%eax
+
+ END_ENTRY _method_invoke
+
+
+ ENTRY _method_invoke_stret
+
+ movl selector_stret(%esp), %ecx
+ movl method_name(%ecx), %edx
+ movl method_imp(%ecx), %eax
+ movl %edx, selector_stret(%esp)
+ jmp *%eax
+
+ END_ENTRY _method_invoke_stret
+
+#if !defined(NDEBUG)
+ STATIC_ENTRY __objc_ignored_method
+
+ movl self(%esp), %eax
+ ret
+
+ END_ENTRY __objc_ignored_method
+#endif
+
+#endif
#define kFwdMsgSendStret 0\r
\r
// objc_msgSend parameters\r
-#define self 4\r
-#define super 4\r
-#define selector 8\r
-#define first_arg 12\r
+#define SELF 8[ebp]\r
+#define SUPER 8[ebp]\r
+#define SELECTOR 12[ebp]\r
+#define FIRST_ARG 16[ebp]\r
\r
// objc_msgSend_stret parameters\r
-#define struct_addr 4\r
-#define self_stret 8\r
-#define super_stret 8\r
-#define selector_stret 12\r
+#define STRUCT_ADDR 8[ebp]\r
+#define SELF_STRET 12[ebp]\r
+#define SUPER_STRET 12[ebp]\r
+#define SELECTOR_STRET 16[ebp]\r
\r
// objc_super parameter to sendSuper\r
#define super_receiver 0\r
__declspec(naked) Method _cache_getMethod(Class cls, SEL sel, IMP objc_msgForward_imp)\r
{\r
__asm {\r
- mov ecx, selector[esp]\r
- mov edx, self[esp]\r
+ push ebp\r
+ mov ebp, esp\r
+\r
+ mov ecx, SELECTOR\r
+ mov edx, SELF\r
\r
// CacheLookup WORD_RETURN, CACHE_GET\r
push edi\r
xor eax, eax\r
pop esi\r
pop edi\r
+ leave\r
ret\r
\r
HIT:\r
- mov ecx, 8+first_arg[esp]\r
+ mov ecx, FIRST_ARG\r
cmp ecx, method_imp[eax]\r
je MISS\r
pop esi\r
pop edi\r
+ leave\r
ret\r
}\r
}\r
__declspec(naked) IMP _cache_getImp(Class cls, SEL sel)\r
{\r
__asm {\r
- mov ecx, selector[esp]\r
- mov edx, self[esp]\r
+ push ebp\r
+ mov ebp, esp\r
+\r
+ mov ecx, SELECTOR\r
+ mov edx, SELF\r
\r
// CacheLookup WORD_RETURN, CACHE_GET\r
push edi\r
pop esi\r
pop edi\r
xor eax, eax\r
+ leave\r
ret\r
\r
HIT:\r
pop esi\r
pop edi\r
mov eax, method_imp[eax]\r
+ leave\r
ret\r
-\r
}\r
}\r
\r
OBJC_EXPORT __declspec(naked) id objc_msgSend(id a, SEL b, ...)\r
{\r
__asm {\r
+ push ebp\r
+ mov ebp, esp\r
+\r
// load receiver and selector\r
- mov ecx, selector[esp]\r
- mov eax, self[esp]\r
+ mov ecx, SELECTOR\r
+ mov eax, SELF\r
\r
-#if !defined(NO_GC)\r
+#if SUPPORT_GC\r
// check whether selector is ignored\r
#error oops\r
#endif\r
pop esi\r
pop edi\r
mov edx, kFwdMsgSend\r
+ leave\r
jmp eax\r
\r
// cache miss: search method lists\r
MISS:\r
pop esi\r
pop edi\r
- mov eax, self[esp]\r
+ mov eax, SELF\r
mov eax, isa[eax]\r
\r
// MethodTableLookup WORD_RETURN, MSG_SEND\r
- sub esp, 4\r
push ecx\r
push eax\r
call _class_lookupMethodAndLoadCache\r
- add esp, 12\r
\r
mov edx, kFwdMsgSend\r
+ leave\r
jmp eax\r
\r
// message send to nil: return zero\r
NIL:\r
// eax is already zero\r
mov edx, 0\r
+ leave\r
ret\r
}\r
}\r
OBJC_EXPORT __declspec(naked) double objc_msgSend_fpret(id a, SEL b, ...)\r
{\r
__asm {\r
+ push ebp\r
+ mov ebp, esp\r
+\r
// load receiver and selector\r
- mov ecx, selector[esp]\r
- mov eax, self[esp]\r
+ mov ecx, SELECTOR\r
+ mov eax, SELF\r
\r
-#if !defined(NO_GC)\r
+#if SUPPORT_GC\r
// check whether selector is ignored\r
#error oops\r
#endif\r
pop esi\r
pop edi\r
mov edx, kFwdMsgSend\r
+ leave\r
jmp eax\r
\r
// cache miss: search method lists\r
MISS:\r
pop esi\r
pop edi\r
- mov eax, self[esp]\r
+ mov eax, SELF\r
mov eax, isa[eax]\r
\r
// MethodTableLookup WORD_RETURN, MSG_SEND\r
- sub esp, 4\r
push ecx\r
push eax\r
call _class_lookupMethodAndLoadCache\r
- add esp, 12\r
\r
mov edx, kFwdMsgSend\r
+ leave\r
jmp eax\r
\r
// message send to nil: return zero\r
NIL:\r
fldz\r
+ leave\r
ret\r
}\r
}\r
OBJC_EXPORT __declspec(naked) id objc_msgSendSuper(struct objc_super *a, SEL b, ...)\r
{\r
__asm {\r
+ push ebp\r
+ mov ebp, esp\r
+\r
// load class and selector\r
- mov eax, super[esp]\r
- mov ecx, selector[esp]\r
+ mov eax, SUPER\r
+ mov ecx, SELECTOR\r
mov edx, super_class[eax]\r
\r
-#if !defined(NO_GC)\r
+#if SUPPORT_GC\r
// check whether selector is ignored\r
#error oops\r
#endif\r
mov eax, method_imp[eax]\r
pop esi\r
pop edi\r
- mov edx, super[esp]\r
+ mov edx, SUPER\r
mov edx, super_receiver[edx]\r
- mov super[esp], edx\r
+ mov SUPER, edx\r
mov edx, kFwdMsgSend\r
+ leave\r
jmp eax\r
\r
// cache miss: search method lists\r
\r
pop esi\r
pop edi\r
- mov edx, super[esp]\r
+ mov edx, SUPER\r
mov eax, super_receiver[edx]\r
- mov super[esp], eax\r
+ mov SUPER, eax\r
mov eax, super_class[edx]\r
\r
// MethodTableLookup WORD_RETURN, MSG_SENDSUPER\r
- sub esp, 4\r
push ecx\r
push eax\r
call _class_lookupMethodAndLoadCache\r
- add esp, 12\r
\r
mov edx, kFwdMsgSend\r
+ leave\r
jmp eax\r
}\r
}\r
OBJC_EXPORT __declspec(naked) void objc_msgSend_stret(void)\r
{\r
__asm {\r
+ push ebp\r
+ mov ebp, esp\r
+\r
// load receiver and selector\r
- mov ecx, selector_stret[esp]\r
- mov eax, self_stret[esp]\r
+ mov ecx, SELECTOR_STRET\r
+ mov eax, SELF_STRET\r
\r
-#if !defined(NO_GC)\r
+#if SUPPORT_GC\r
// check whether selector is ignored\r
#error oops\r
#endif\r
pop esi\r
pop edi\r
mov edx, kFwdMsgSendStret\r
+ leave\r
jmp eax\r
\r
// cache miss: search method lists\r
MISS:\r
pop esi\r
pop edi\r
- mov eax, self_stret[esp]\r
+ mov eax, SELF_STRET\r
mov eax, isa[eax]\r
\r
// MethodTableLookup WORD_RETURN, MSG_SEND\r
- sub esp, 4\r
push ecx\r
push eax\r
call _class_lookupMethodAndLoadCache\r
- add esp, 12\r
\r
mov edx, kFwdMsgSendStret\r
+ leave\r
jmp eax\r
\r
// message send to nil: return zero\r
NIL:\r
// eax is already zero\r
mov edx, 0\r
+ leave\r
ret\r
}\r
}\r
OBJC_EXPORT __declspec(naked) id objc_msgSendSuper_stret(struct objc_super *a, SEL b, ...)\r
{\r
__asm {\r
+ push ebp\r
+ mov ebp, esp\r
+\r
// load class and selector\r
- mov eax, super_stret[esp]\r
- mov ecx, selector_stret[esp]\r
+ mov eax, SUPER_STRET\r
+ mov ecx, SELECTOR_STRET\r
mov edx, super_class[eax]\r
\r
-#if !defined(NO_GC)\r
+#if SUPPORT_GC\r
// check whether selector is ignored\r
#error oops\r
#endif\r
mov eax, method_imp[eax]\r
pop esi\r
pop edi\r
- mov edx, super[esp]\r
+ mov edx, SUPER_STRET\r
mov edx, super_receiver[edx]\r
- mov super[esp], edx\r
+ mov SUPER_STRET, edx\r
mov edx, kFwdMsgSendStret\r
+ leave\r
jmp eax\r
\r
// cache miss: search method lists\r
\r
pop esi\r
pop edi\r
- mov edx, super_stret[esp]\r
+ mov edx, SUPER_STRET\r
mov eax, super_receiver[edx]\r
- mov super_stret[esp], eax\r
+ mov SUPER_STRET, eax\r
mov eax, super_class[edx]\r
\r
// MethodTableLookup WORD_RETURN, MSG_SENDSUPER\r
- sub esp, 4\r
push ecx\r
push eax\r
call _class_lookupMethodAndLoadCache\r
- add esp, 12\r
\r
mov edx, kFwdMsgSendStret\r
+ leave\r
jmp eax\r
}\r
}\r
OBJC_EXPORT __declspec(naked) void method_invoke(void)\r
{\r
__asm {\r
- mov ecx, selector[esp]\r
+ push ebp\r
+ mov ebp, esp\r
+\r
+ mov ecx, SELECTOR\r
mov edx, method_name[ecx]\r
mov eax, method_imp[ecx]\r
- mov selector[esp], edx\r
+ mov SELECTOR, edx\r
+\r
+ leave\r
jmp eax\r
}\r
}\r
OBJC_EXPORT __declspec(naked) void method_invoke_stret(void)\r
{\r
__asm {\r
- mov ecx, selector_stret[esp]\r
+ push ebp\r
+ mov ebp, esp\r
+\r
+ mov ecx, SELECTOR_STRET\r
mov edx, method_name[ecx]\r
mov eax, method_imp[ecx]\r
- mov selector_stret[esp], edx\r
+ mov SELECTOR_STRET, edx\r
+\r
+ leave\r
jmp eax\r
}\r
}\r
+\r
+\r
+__declspec(naked) id _objc_ignored_method(id obj, SEL sel)\r
+{\r
+ return obj;\r
+}\r
#define __OBJC2__ 1
-#undef OBJC_ASM
-#define OBJC_ASM
-#include "objc-rtp.h"
-
-
/********************************************************************
* Data used by the ObjC runtime.
*
// Substitute receiver for messages sent to nil (usually also nil)
// id _objc_nilReceiver
.align 4
-.globl __objc_nilReceiver
+.private_extern __objc_nilReceiver
__objc_nilReceiver:
.quad 0
// to get the critical regions for which method caches
// cannot be garbage collected.
-.globl _objc_entryPoints
+.private_extern _objc_entryPoints
_objc_entryPoints:
.quad __cache_getImp
.quad __cache_getMethod
.quad _objc_msgSend_stret
.quad _objc_msgSendSuper
.quad _objc_msgSendSuper_stret
+ .quad _objc_msgSendSuper2
+ .quad _objc_msgSendSuper2_stret
.quad 0
-.globl _objc_exitPoints
+.private_extern _objc_exitPoints
_objc_exitPoints:
- .quad LGetImpExit
- .quad LGetMethodExit
- .quad LMsgSendExit
- .quad LMsgSendFpretExit
- .quad LMsgSendFp2retExit
- .quad LMsgSendStretExit
- .quad LMsgSendSuperExit
- .quad LMsgSendSuperStretExit
+ .quad LExit__cache_getImp
+ .quad LExit__cache_getMethod
+ .quad LExit_objc_msgSend
+ .quad LExit_objc_msgSend_fpret
+ .quad LExit_objc_msgSend_fp2ret
+ .quad LExit_objc_msgSend_stret
+ .quad LExit_objc_msgSendSuper
+ .quad LExit_objc_msgSendSuper_stret
+ .quad LExit_objc_msgSendSuper2
+ .quad LExit_objc_msgSendSuper2_stret
.quad 0
/********************************************************************
- *
+ * Recommended multi-byte NOP instructions
+ * (Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2B)
+ ********************************************************************/
+#define nop1 .byte 0x90
+#define nop2 .byte 0x66,0x90
+#define nop3 .byte 0x0F,0x1F,0x00
+#define nop4 .byte 0x0F,0x1F,0x40,0x00
+#define nop5 .byte 0x0F,0x1F,0x44,0x00,0x00
+#define nop6 .byte 0x66,0x0F,0x1F,0x44,0x00,0x00
+#define nop7 .byte 0x0F,0x1F,0x80,0x00,0x00,0x00,0x00
+#define nop8 .byte 0x0F,0x1F,0x84,0x00,0x00,0x00,0x00,0x00
+#define nop9 .byte 0x66,0x0F,0x1F,0x84,0x00,0x00,0x00,0x00,0x00
+
+
+/********************************************************************
* Names for parameter registers.
- *
********************************************************************/
-#define a1 rdi
-#define a2 rsi
-#define a3 rdx
-#define a4 rcx
-#define a5 r8
-#define a6 r9
+#define a1 rdi
+#define a1d edi
+#define a1b dil
+#define a2 rsi
+#define a2d esi
+#define a2b sil
+#define a3 rdx
+#define a3d edx
+#define a4 rcx
+#define a4d ecx
+#define a5 r8
+#define a5d r8d
+#define a6 r9
#define a6d r9d
+/********************************************************************
+ * Names for relative labels
+ * DO NOT USE THESE LABELS ELSEWHERE
+ * Reserved labels: 5: 6: 7: 8: 9:
+ ********************************************************************/
+#define LCacheMiss 5
+#define LCacheMiss_f 5f
+#define LCacheMiss_b 5b
+#define LNilTestDone 6
+#define LNilTestDone_f 6f
+#define LNilTestDone_b 6b
+#define LNilTestSlow 7
+#define LNilTestSlow_f 7f
+#define LNilTestSlow_b 7b
+#define LGetIsaDone 8
+#define LGetIsaDone_f 8f
+#define LGetIsaDone_b 8b
+#define LGetIsaSlow 9
+#define LGetIsaSlow_f 9f
+#define LGetIsaSlow_b 9b
+
+/********************************************************************
+ * Macro parameters
+ ********************************************************************/
+
+#define STRET -1
+#define NORMAL 0
+#define FPRET 1
+#define FP2RET 2
+
+
/********************************************************************
*
* Structure definitions.
********************************************************************/
// objc_super parameter to sendSuper
- receiver = 0
- class = 8
+#define receiver 0
+#define class 8
// Selected field offsets in class structure
- isa = 0
-#if __OBJC2__
- cache = 16
-#else
- cache = 64
-#endif
+// #define isa 0 USE GetIsa INSTEAD
+#define cache 16
// Method descriptor
- method_name = 0
- method_imp = 16
+#define method_name 0
+#define method_imp 16
// Cache header
- mask = 0
- occupied = 8
- buckets = 16 // variable length array
+#define mask 0
+#define occupied 8
+#define buckets 16
// typedef struct {
// uint128_t floatingPointArgs[8]; // xmm0..xmm7
.macro ENTRY
.text
.globl $0
+ .align 6, 0x90
+$0:
+.endmacro
+
+.macro STATIC_ENTRY
+ .text
+ .private_extern $0
.align 2, 0x90
$0:
.endmacro
//////////////////////////////////////////////////////////////////////
.macro END_ENTRY
+LExit$0:
.endmacro
/////////////////////////////////////////////////////////////////////
//
-// SaveRegisters
+// SaveRegisters caller
//
// Pushes a stack frame and saves all registers that might contain
// parameter values.
//
-// On entry:
-// $0 = 0 if normal, 1 if CacheLookup already saved a4, a5, a6
-// stack = ret
+// On entry: %0 = caller's symbol name for DWARF
+// stack = ret
//
// On exit:
-// %rsp is 16-byte aligned
+// %rsp is 16-byte aligned
//
/////////////////////////////////////////////////////////////////////
.macro SaveRegisters
-.if $0 == 0
- movq %a4, -32(%rsp)
- movq %a5, -24(%rsp)
- movq %a6, -16(%rsp)
-.else
- // a4-a6 already saved by CacheLookup
-.endif
-
- DW_FRAME $1
- pushq %rbp
- movq %rsp, %rbp
- subq $$ 128+64, %rsp
-
- movdqa %xmm0, -192(%rbp)
- movdqa %xmm1, -176(%rbp)
- movdqa %xmm2, -160(%rbp)
- movdqa %xmm3, -144(%rbp)
- movdqa %xmm4, -128(%rbp)
- movdqa %xmm5, -112(%rbp)
- movdqa %xmm6, -96(%rbp)
- movdqa %xmm7, -80(%rbp)
- movq %r10, -64(%rbp) // fixme needed?
- movq %rax, -56(%rbp) // might be xmm parameter count
- movq %a1, -48(%rbp)
- movq %a2, -40(%rbp)
- movq %a3, -32(%rbp)
- // movq %a4, -24(%rbp)
- // movq %a5, -16(%rbp)
- // movq %a6, -8(%rbp)
+ DW_FRAME $0
+ enter $$0x80+8, $$0 // +8 for alignment
+ movdqa %xmm0, -0x80(%rbp)
+ push %rax // might be xmm parameter count
+ movdqa %xmm1, -0x70(%rbp)
+ push %a1
+ movdqa %xmm2, -0x60(%rbp)
+ push %a2
+ movdqa %xmm3, -0x50(%rbp)
+ push %a3
+ movdqa %xmm4, -0x40(%rbp)
+ push %a4
+ movdqa %xmm5, -0x30(%rbp)
+ push %a5
+ movdqa %xmm6, -0x20(%rbp)
+ push %a6
+ movdqa %xmm7, -0x10(%rbp)
.endmacro
/////////////////////////////////////////////////////////////////////
//
// Pops a stack frame pushed by SaveRegisters
//
-// On entry:
-// %rbp unchanged since SaveRegisters
+// On entry: $0 = caller's symbol name for DWARF
+// %rbp unchanged since SaveRegisters
//
// On exit:
-// stack = ret
+// stack = ret
//
/////////////////////////////////////////////////////////////////////
.macro RestoreRegisters
- movdqa -192(%rbp), %xmm0
- movdqa -176(%rbp), %xmm1
- movdqa -160(%rbp), %xmm2
- movdqa -144(%rbp), %xmm3
- movdqa -128(%rbp), %xmm4
- movdqa -112(%rbp), %xmm5
- movdqa -96(%rbp), %xmm6
- movdqa -80(%rbp), %xmm7
- movq -64(%rbp), %r10
- movq -56(%rbp), %rax
- movq -48(%rbp), %a1
- movq -40(%rbp), %a2
- movq -32(%rbp), %a3
- movq -24(%rbp), %a4
- movq -16(%rbp), %a5
- movq -8(%rbp), %a6
- movq %rbp, %rsp
- popq %rbp
+ movdqa -0x80(%rbp), %xmm0
+ pop %a6
+ movdqa -0x70(%rbp), %xmm1
+ pop %a5
+ movdqa -0x60(%rbp), %xmm2
+ pop %a4
+ movdqa -0x50(%rbp), %xmm3
+ pop %a3
+ movdqa -0x40(%rbp), %xmm4
+ pop %a2
+ movdqa -0x30(%rbp), %xmm5
+ pop %a1
+ movdqa -0x20(%rbp), %xmm6
+ pop %rax
+ movdqa -0x10(%rbp), %xmm7
+ leave
.endmacro
/////////////////////////////////////////////////////////////////////
//
//
-// CacheLookup selectorRegister, cacheMissLabel
+// CacheLookup return-type
//
// Locate the implementation for a selector in a class method cache.
//
// Takes:
-// $0 = register containing selector (%a1 or %a2 ONLY)
-// $1 = if method is not cached then jmp LCacheMiss$1
+// $0 = NORMAL, FPRET, FP2RET, STRET
+// a2 or a3 (STRET) = selector
// %r11 = class whose cache is to be searched
-// stack = ret
//
-// On exit: (found) method triplet in %r11
-// (not found) jumps to cacheMissLabel
-// stack = ret
-//
+// On exit: (found) method in %r11, stack unchanged, eq/ne set for forwarding
+// (not found) jumps to LCacheMiss, %rax on stack
+//
/////////////////////////////////////////////////////////////////////
-
.macro CacheLookup
-// load variables and save caller registers.
-
- movq %a4, -32(%rsp) // save scratch registers in red zone
- movq %a5, -24(%rsp)
- movq %a6, -16(%rsp)
-
- movq cache(%r11), %a5 // cache = class->cache
-
- movl mask(%a5), %a6d
- shlq $$3, %a6 // %a6 = cache->mask << 3
- mov $0, %a4 // bytes = sel
- andq %a6, %a4 // bytes &= (mask << 3)
+ push %rax
+ movq cache(%r11), %r10 // cache = class->cache
+.if $0 != STRET
+ mov %a2d, %eax // index = sel
+.else
+ mov %a3d, %eax // index = sel
+.endif
// search the receiver's cache
// r11 = method (soon)
-// a4 = bytes
-// a5 = cache
-// a6 = mask << 3
-// $0 = sel
-LMsgSendProbeCache_$1:
- movq buckets(%a5, %a4), %r11 // method = cache->buckets[bytes/8]
+// eax = index
+// r10 = cache
+// a2 or a3 = sel
+1:
+ andl mask(%r10), %eax // index &= mask
+ movq buckets(%r10, %rax, 8), %r11 // method = cache->buckets[index]
+ incl %eax // index++
testq %r11, %r11 // if (method == NULL)
- je LCacheMiss$1 // goto cacheMissLabel
-
- addq $$8, %a4 // bytes += 8
- andq %a6, %a4 // bytes &= (mask << 3)
- cmpq method_name(%r11), $0 // if (method_name != sel)
- jne LMsgSendProbeCache_$1 // goto loop
+ je LCacheMiss_f // goto cacheMissLabel
+.if $0 != STRET
+ cmpq method_name(%r11), %a2 // if (method_name != sel)
+.else
+ cmpq method_name(%r11), %a3 // if (method_name != sel)
+.endif
+ jne 1b // goto loop
// cache hit, r11 = method triplet
-
// restore saved registers
- movq -32(%rsp), %a4
- movq -24(%rsp), %a5
- movq -16(%rsp), %a6
+ pop %rax
+
+.if $0 != STRET
+ // eq (non-stret) flag already set above
+.else
+ // set ne (stret) for forwarding; r11 != 0
+ test %r11, %r11
+.endif
.endmacro
//
// MethodTableLookup classRegister, selectorRegister, fn
//
-// Takes: $0 = class to search (%a1 or %a2 or %r11 ONLY)
-// $1 = selector to search for (%a2 or %a3 ONLY)
+// Takes: $0 = class to search (a1 or a2 or r10 ONLY)
+// $1 = selector to search for (a2 or a3 ONLY)
+// $2 = caller's symbol name for DWARF
+// r11 = class to search
//
-// Stack: ret (%rsp+0), pad, %a4, %a5, %a6 (saved by CacheLookup)
+// Stack: ret, rax (pushed by CacheLookup)
//
-// On exit: restores registers saved by CacheLookup
+// On exit: pops registers pushed by CacheLookup
// imp in %r11
//
/////////////////////////////////////////////////////////////////////
.macro MethodTableLookup
+
+ pop %rax // saved by CacheLookup
+ SaveRegisters $2
- SaveRegisters 1, $2
+ // _class_lookupMethodAndLoadCache3(receiver, selector, class)
- // _class_lookupMethodAndLoadCache(class, selector)
movq $0, %a1
movq $1, %a2
- call __class_lookupMethodAndLoadCache
+ movq %r11, %a3
+ call __class_lookupMethodAndLoadCache3
// IMP is now in %rax
movq %rax, %r11
.endmacro
+/////////////////////////////////////////////////////////////////////
+//
+// GetIsa return-type
+// GetIsaFast return-type
+// GetIsaSupport return-type
+//
+// Sets r11 = obj->isa. Consults the tagged isa table if necessary.
+//
+// Takes: $0 = NORMAL or FPRET or FP2RET or STRET
+// a1 or a2 (STRET) = receiver
+//
+// On exit: r11 = receiver->isa
+// r10 is clobbered
+//
+/////////////////////////////////////////////////////////////////////
+
+.macro GetIsa
+
+.if $0 != STRET
+ testb $$1, %a1b
+ jnz 1f
+ movq (%a1), %r11
+ jmp 2f
+1: movl %a1d, %r10d
+.else
+ testb $$1, %a2b
+ jnz 1f
+ movq (%a2), %r11
+ jmp 2f
+1: movl %a2d, %r10d
+.endif
+ andl $$0xF, %r10d
+ leaq __objc_tagged_isa_table(%rip), %r11
+ movq (%r11, %r10, 8), %r11 // read isa from table
+2:
+.endmacro
+
+.macro GetIsaFast
+.if $0 != STRET
+ testb $$1, %a1b
+ .byte 0x2e // harmless branch hint prefix to align IFETCH blocks
+ jnz LGetIsaSlow_f
+ movq (%a1), %r11
+.else
+ testb $$1, %a2b
+ .byte 0x2e // harmless branch hint prefix to align IFETCH blocks
+ jnz LGetIsaSlow_f
+ movq (%a2), %r11
+.endif
+LGetIsaDone:
+.endmacro
+
+.macro GetIsaSupport
+LGetIsaSlow:
+ leaq __objc_tagged_isa_table(%rip), %r11
+.if $0 != STRET
+ movl %a1d, %r10d
+.else
+ movl %a2d, %r10d
+.endif
+ andl $$0xF, %r10d
+ movq (%r11, %r10, 8), %r11 // read isa from table
+ jmp LGetIsaDone_b
+.endmacro
+
+/////////////////////////////////////////////////////////////////////
+//
+// NilTest return-type
+//
+// Takes: $0 = NORMAL or FPRET or FP2RET or STRET
+// %a1 or %a2 (STRET) = receiver
+//
+// On exit: Loads non-nil receiver in %a1 or %a2 (STRET), or returns zero.
+//
+// NilTestSupport return-type
+//
+// Takes: $0 = NORMAL or FPRET or FP2RET or STRET
+// %a1 or %a2 (STRET) = receiver
+//
+// On exit: Loads non-nil receiver in %a1 or %a2 (STRET), or returns zero.
+//
+/////////////////////////////////////////////////////////////////////
+
+.macro NilTest
+.if $0 != STRET
+ testq %a1, %a1
+.else
+ testq %a2, %a2
+.endif
+ jz LNilTestSlow_f
+LNilTestDone:
+.endmacro
+
+.macro NilTestSupport
+ .align 3
+LNilTestSlow:
+.if $0 != STRET
+ movq __objc_nilReceiver(%rip), %a1
+ testq %a1, %a1 // if (receiver != nil)
+.else
+ movq __objc_nilReceiver(%rip), %a2
+ testq %a2, %a2 // if (receiver != nil)
+.endif
+ jne LNilTestDone_b // send to new receiver
+
+.if $0 == FPRET
+ fldz
+.elseif $0 == FP2RET
+ fldz
+ fldz
+.endif
+.if $0 != STRET
+ xorl %eax, %eax
+ xorl %edx, %edx
+ xorps %xmm0, %xmm0
+ xorps %xmm1, %xmm1
+.endif
+ ret
+.endmacro
+
/********************************************************************
* Method _cache_getMethod(Class cls, SEL sel, IMP msgForward_internal_imp)
* _objc_msgForward_internal is passed as a parameter because it's more
* efficient to do the (PIC) lookup once in the caller than repeatedly here.
********************************************************************/
-
- ENTRY __cache_getMethod
+
+ STATIC_ENTRY __cache_getMethod
DW_START __cache_getMethod
// do lookup
movq %a1, %r11 // move class to r11 for CacheLookup
- CacheLookup %a2, __cache_getMethod
+ CacheLookup NORMAL
// cache hit, method triplet in %r11
- cmpq method_imp(%r11), %a3 // if (imp==_objc_msgForward_internal)
- je 1f // return (Method)1
+ cmpq method_imp(%r11), %a3 // if (imp==_objc_msgForward_internal)
+ je 1f // return (Method)1
movq %r11, %rax // return method triplet address
ret
-1: movq $1, %rax
+1: movl $1, %eax
ret
-LCacheMiss__cache_getMethod:
+LCacheMiss:
// cache miss, return nil
- xorq %rax, %rax // erase %rax
+ pop %rax // pushed by CacheLookup
+ xorl %eax, %eax
ret
LGetMethodExit:
* If not found, returns NULL.
********************************************************************/
- ENTRY __cache_getImp
+ STATIC_ENTRY __cache_getImp
DW_START __cache_getImp
// do lookup
movq %a1, %r11 // move class to r11 for CacheLookup
- CacheLookup %a2, __cache_getImp
+ CacheLookup NORMAL
// cache hit, method triplet in %r11
movq method_imp(%r11), %rax // return method imp address
ret
-LCacheMiss__cache_getImp:
+LCacheMiss:
// cache miss, return nil
- xorq %rax, %rax // erase %rax
+ pop %rax // pushed by CacheLookup
+ xorl %eax, %eax
ret
LGetImpExit:
*
********************************************************************/
+ .data
+ .align 3
+ .private_extern __objc_tagged_isa_table
+__objc_tagged_isa_table:
+ .fill 16, 8, 0
+
ENTRY _objc_msgSend
DW_START _objc_msgSend
-// check whether selector is ignored
- cmpq $ kIgnore, %a2
- je LMsgSendReturnSelf // ignore and return self
+ NilTest NORMAL
-// check whether receiver is nil
- testq %a1, %a1
- je LMsgSendNilSelf
-
-// receiver (in %a1) is non-nil: search the cache
-LMsgSendReceiverOk:
- movq isa(%a1), %r11 // class = self->isa
- CacheLookup %a2, _objc_msgSend
- // CacheLookup placed method in r11
- movq method_imp(%r11), %r11
- cmp %r11, %r11 // set nonstret (eq) for forwarding
- jmp *%r11 // goto *imp
+ GetIsaFast NORMAL // r11 = self->isa
+ CacheLookup NORMAL // r11 = method, eq set (nonstret fwd)
+ jmp *method_imp(%r11) // goto *imp
-// cache miss: go search the method lists
-LCacheMiss_objc_msgSend:
- MethodTableLookup isa(%a1), %a2, _objc_msgSend
- // MethodTableLookup placed IMP in r11
- cmp %r11, %r11 // set nonstret (eq) for forwarding
- jmp *%r11 // goto *imp
+ NilTestSupport NORMAL
-// message sent to nil: redirect to nil receiver, if any
-LMsgSendNilSelf:
- movq __objc_nilReceiver(%rip), %a1
- testq %a1, %a1 // if (receiver != nil)
- jne LMsgSendReceiverOk // send to new receiver
+ GetIsaSupport NORMAL
- // message sent to nil - return 0
- movq $0, %rax
- movq $0, %rdx
- xorps %xmm0, %xmm0
- xorps %xmm1, %xmm1
- ret
-
-LMsgSendReturnSelf:
- movq %a1, %rax
- ret
+// cache miss: go search the method lists
+LCacheMiss:
+ GetIsa NORMAL // r11 = self->isa
+ MethodTableLookup %a1, %a2, _objc_msgSend // r11 = IMP
+ cmp %r11, %r11 // set eq (nonstret) for forwarding
+ jmp *%r11 // goto *imp
-LMsgSendExit:
DW_END _objc_msgSend
END_ENTRY _objc_msgSend
ENTRY _objc_msgSend_fixup
DW_START _objc_msgSend_fixup
- testq %a1, %a1
- je LMsgSendFixupNilSelf
-
- SaveRegisters 0, _objc_msgSend_fixup
+ NilTest NORMAL
+ SaveRegisters _objc_msgSend_fixup
+
// Dereference obj/isa/cache to crash before _objc_fixupMessageRef
- movq 8(%a2), %r11 // selector
- movq isa(%a1), %a6 // isa = *receiver
- movq cache(%a6), %a5 // cache = *isa
+ movq 8(%a2), %a6 // selector
+ GetIsa NORMAL // r11 = isa = *receiver
+ movq cache(%r11), %a5 // cache = *isa
movq mask(%a5), %a4 // *cache
// a1 = receiver
// a2 = address of message ref
movq %a2, %a3
- movq $0, %a2
+ xorl %a2d, %a2d
// __objc_fixupMessageRef(receiver, 0, ref)
call __objc_fixupMessageRef
movq %rax, %r11
cmp %r11, %r11 // set nonstret (eq) for forwarding
jmp *%r11
-LMsgSendFixupNilSelf:
- // message sent to nil - return 0
- movq $0, %rax
- movq $0, %rdx
- xorps %xmm0, %xmm0
- xorps %xmm1, %xmm1
- ret
+ NilTestSupport NORMAL
DW_END _objc_msgSend_fixup
END_ENTRY _objc_msgSend_fixup
- ENTRY _objc_msgSend_fixedup
+ STATIC_ENTRY _objc_msgSend_fixedup
// Load _cmd from the message_ref
movq 8(%a2), %a2
jmp _objc_msgSend
ENTRY _objc_msgSendSuper
DW_START _objc_msgSendSuper
-// check whether selector is ignored
- cmpq $ kIgnore, %a2
- je LMsgSendSuperReturnSelf
-
// search the cache (objc_super in %a1)
movq class(%a1), %r11 // class = objc_super->class
- CacheLookup %a2, _objc_msgSendSuper
- // CacheLookup placed method in r11
- movq method_imp(%r11), %r11
+ CacheLookup NORMAL // r11 = method, eq set (nonstret fwd)
movq receiver(%a1), %a1 // load real receiver
- cmp %r11, %r11 // set nonstret (eq) for forwarding
- jmp *%r11 // goto *imp
+ jmp *method_imp(%r11) // goto *imp
// cache miss: go search the method lists
-LCacheMiss_objc_msgSendSuper:
- MethodTableLookup class(%a1), %a2, _objc_msgSendSuper
- // MethodTableLookup placed IMP in r11
+LCacheMiss:
+ movq receiver(%a1), %r10
+ movq class(%a1), %r11
+ MethodTableLookup %r10, %a2, _objc_msgSendSuper // r11 = IMP
movq receiver(%a1), %a1 // load real receiver
- cmp %r11, %r11 // set nonstret (eq) for forwarding
+ cmp %r11, %r11 // set eq (nonstret) for forwarding
jmp *%r11 // goto *imp
-
-LMsgSendSuperReturnSelf:
- movq receiver(%a1), %rax
- ret
-LMsgSendSuperExit:
DW_END _objc_msgSendSuper
END_ENTRY _objc_msgSendSuper
+
+/********************************************************************
+ * id objc_msgSendSuper2
+ ********************************************************************/
+
#if __OBJC2__
ENTRY _objc_msgSendSuper2_fixup
DW_START _objc_msgSendSuper2_fixup
- SaveRegisters 0, _objc_msgSendSuper2_fixup
+ SaveRegisters _objc_msgSendSuper2_fixup
// a1 = address of objc_super2
// a2 = address of message ref
movq %a2, %a3
END_ENTRY _objc_msgSendSuper2_fixup
- ENTRY _objc_msgSendSuper2_fixedup
- // objc_super->class is superclass of class to search
- movq class(%a1), %r11 // cls = objc_super->class
+ STATIC_ENTRY _objc_msgSendSuper2_fixedup
movq 8(%a2), %a2 // load _cmd from message_ref
- movq 8(%r11), %r11 // cls = cls->superclass
- movq %r11, class(%a1)
- // objc_super->class is now the class to search
- jmp _objc_msgSendSuper
+ jmp _objc_msgSendSuper2
END_ENTRY _objc_msgSendSuper2_fixedup
ENTRY _objc_msgSendSuper2
+ DW_START _objc_msgSendSuper2
// objc_super->class is superclass of class to search
+
+// search the cache (objc_super in %a1)
movq class(%a1), %r11 // cls = objc_super->class
- movq 8(%r11), %r11 // cls = cls->superclass
- movq %r11, class(%a1)
- // objc_super->class is now the class to search
- jmp _objc_msgSendSuper
- END_ENTRY _objc_msgSendSuper2
+ movq 8(%r11), %r11 // cls = class->superclass
+ CacheLookup NORMAL // r11 = method, eq set (nonstret fwd)
+ movq receiver(%a1), %a1 // load real receiver
+ jmp *method_imp(%r11) // goto *imp
+
+// cache miss: go search the method lists
+LCacheMiss:
+ movq receiver(%a1), %r10
+ movq class(%a1), %r11
+ movq 8(%r11), %r11
+ MethodTableLookup %r10, %a2, _objc_msgSendSuper2 // r11 = IMP
+ movq receiver(%a1), %a1 // load real receiver
+ cmp %r11, %r11 // set eq (nonstret) for forwarding
+ jmp *%r11 // goto *imp
+
+ DW_END _objc_msgSendSuper2
+ END_ENTRY _objc_msgSendSuper2
#endif
ENTRY _objc_msgSend_fpret
DW_START _objc_msgSend_fpret
-// check whether selector is ignored
- cmpq $ kIgnore, %a2
- je LMsgSendFpretReturnZero
+ NilTest FPRET
-// check whether receiver is nil
- testq %a1, %a1
- je LMsgSendFpretNilSelf
-
-// receiver (in %a1) is non-nil: search the cache
-LMsgSendFpretReceiverOk:
- movq isa(%a1), %r11 // class = self->isa
- CacheLookup %a2, _objc_msgSend_fpret
- // CacheLookup placed method in r11
- movq method_imp(%r11), %r11
- cmp %r11, %r11 // set nonstret (eq) for forwarding
- jmp *%r11 // goto *imp
+ GetIsaFast FPRET // r11 = self->isa
+ CacheLookup FPRET // r11 = method, eq set (nonstret fwd)
+ jmp *method_imp(%r11) // goto *imp
-// cache miss: go search the method lists
-LCacheMiss_objc_msgSend_fpret:
- MethodTableLookup isa(%a1), %a2, _objc_msgSend_fpret
- // MethodTableLookup placed IMP in r11
- cmp %r11, %r11 // set nonstret (eq) for forwarding
- jmp *%r11 // goto *imp
+ NilTestSupport FPRET
-// message sent to nil: redirect to nil receiver, if any
-LMsgSendFpretNilSelf:
-1: movq __objc_nilReceiver(%rip),%a1
- testq %a1, %a1 // if (receiver != nil)
- jne LMsgSendFpretReceiverOk // send to new receiver
+ GetIsaSupport FPRET
-LMsgSendFpretReturnZero:
- // Long double return.
- fldz
- // Clear int and float/double return too.
- movq $0, %rax
- movq $0, %rdx
- xorps %xmm0, %xmm0
- xorps %xmm1, %xmm1
- ret
+// cache miss: go search the method lists
+LCacheMiss:
+ GetIsa FPRET // r11 = self->isa
+ MethodTableLookup %a1, %a2, _objc_msgSend_fpret // r11 = IMP
+ cmp %r11, %r11 // set eq (nonstret) for forwarding
+ jmp *%r11 // goto *imp
-LMsgSendFpretExit:
DW_END _objc_msgSend_fpret
END_ENTRY _objc_msgSend_fpret
ENTRY _objc_msgSend_fpret_fixup
DW_START _objc_msgSend_fpret_fixup
- testq %a1, %a1
- je LMsgSendFpretFixupNilSelf
+ NilTest FPRET
- SaveRegisters 0, _objc_msgSend_fpret_fixup
+ SaveRegisters _objc_msgSend_fpret_fixup
// Dereference obj/isa/cache to crash before _objc_fixupMessageRef
- movq 8(%a2), %r11 // selector
- movq isa(%a1), %a6 // isa = *receiver
- movq cache(%a6), %a5 // cache = *isa
+ movq 8(%a2), %a6 // selector
+ GetIsa FPRET // r11 = isa = *receiver
+ movq cache(%r11), %a5 // cache = *isa
movq mask(%a5), %a4 // *cache
// a1 = receiver
// a2 = address of message ref
movq %a2, %a3
- movq $0, %a2
+ xorl %a2d, %a2d
// __objc_fixupMessageRef(receiver, 0, ref)
call __objc_fixupMessageRef
movq %rax, %r11
cmp %r11, %r11 // set nonstret (eq) for forwarding
jmp *%r11
-LMsgSendFpretFixupNilSelf:
- // Long double return.
- fldz
- // Clear int and float/double return too.
- movq $0, %rax
- movq $0, %rdx
- xorps %xmm0, %xmm0
- xorps %xmm1, %xmm1
- ret
+ NilTestSupport FPRET
DW_END _objc_msgSend_fpret_fixup
END_ENTRY _objc_msgSend_fpret_fixup
- ENTRY _objc_msgSend_fpret_fixedup
+ STATIC_ENTRY _objc_msgSend_fpret_fixedup
// Load _cmd from the message_ref
movq 8(%a2), %a2
jmp _objc_msgSend_fpret
ENTRY _objc_msgSend_fp2ret
DW_START _objc_msgSend_fp2ret
-// check whether selector is ignored
- cmpq $ kIgnore, %a2
- je LMsgSendFp2retReturnZero
+ NilTest FP2RET
-// check whether receiver is nil
- testq %a1, %a1
- je LMsgSendFp2retNilSelf
-
-// receiver (in %a1) is non-nil: search the cache
-LMsgSendFp2retReceiverOk:
- movq isa(%a1), %r11 // class = self->isa
- CacheLookup %a2, _objc_msgSend_fp2ret
- // CacheLookup placed method in r11
- movq method_imp(%r11), %r11
- cmp %r11, %r11 // set nonstret (eq) for forwarding
- jmp *%r11 // goto *imp
+ GetIsaFast FP2RET // r11 = self->isa
+ CacheLookup FP2RET // r11 = method, eq set (nonstret fwd)
+ jmp *method_imp(%r11) // goto *imp
+ NilTestSupport FP2RET
+
+ GetIsaSupport FP2RET
+
// cache miss: go search the method lists
-LCacheMiss_objc_msgSend_fp2ret:
- MethodTableLookup isa(%a1), %a2, _objc_msgSend_fp2ret
- // MethodTableLookup placed IMP in r11
- cmp %r11, %r11 // set nonstret (eq) for forwarding
+LCacheMiss:
+ GetIsa FP2RET // r11 = self->isa
+ MethodTableLookup %a1, %a2, _objc_msgSend_fp2ret // r11 = IMP
+ cmp %r11, %r11 // set eq (nonstret) for forwarding
jmp *%r11 // goto *imp
-// message sent to nil: redirect to nil receiver, if any
-LMsgSendFp2retNilSelf:
-1: movq __objc_nilReceiver(%rip),%a1
- testq %a1, %a1 // if (receiver != nil)
- jne LMsgSendFp2retReceiverOk // send to new receiver
-
-LMsgSendFp2retReturnZero:
- // complex long double return.
- fldz
- fldz
- // Clear int and float/double return too.
- movq $0, %rax
- movq $0, %rdx
- xorps %xmm0, %xmm0
- xorps %xmm1, %xmm1
- ret
-
-LMsgSendFp2retExit:
DW_END _objc_msgSend_fp2ret
END_ENTRY _objc_msgSend_fp2ret
ENTRY _objc_msgSend_fp2ret_fixup
DW_START _objc_msgSend_fp2ret_fixup
- testq %a1, %a1
- je LMsgSendFp2retFixupNilSelf
+ NilTest FP2RET
- SaveRegisters 0, _objc_msgSend_fp2ret_fixup
+ SaveRegisters _objc_msgSend_fp2ret_fixup
// Dereference obj/isa/cache to crash before _objc_fixupMessageRef
- movq 8(%a2), %r11 // selector
- movq isa(%a1), %a6 // isa = *receiver
- movq cache(%a6), %a5 // cache = *isa
+ movq 8(%a2), %a6 // selector
+ GetIsa FP2RET // r11 = isa = *receiver
+ movq cache(%r11), %a5 // cache = *isa
movq mask(%a5), %a4 // *cache
// a1 = receiver
// a2 = address of message ref
movq %a2, %a3
- movq $0, %a2
+ xorl %a2d, %a2d
// __objc_fixupMessageRef(receiver, 0, ref)
call __objc_fixupMessageRef
movq %rax, %r11
cmp %r11, %r11 // set nonstret (eq) for forwarding
jmp *%r11
-LMsgSendFp2retFixupNilSelf:
- // complex long double return.
- fldz
- fldz
- // Clear int and float/double return too.
- movq $0, %rax
- movq $0, %rdx
- xorps %xmm0, %xmm0
- xorps %xmm1, %xmm1
- ret
+ NilTestSupport FP2RET
DW_END _objc_msgSend_fp2ret_fixup
END_ENTRY _objc_msgSend_fp2ret_fixup
- ENTRY _objc_msgSend_fp2ret_fixedup
+ STATIC_ENTRY _objc_msgSend_fp2ret_fixedup
// Load _cmd from the message_ref
movq 8(%a2), %a2
jmp _objc_msgSend_fp2ret
ENTRY _objc_msgSend_stret
DW_START _objc_msgSend_stret
-// check whether receiver is nil
- testq %a2, %a2
- je LMsgSendStretNilSelf
-
-// receiver (in %a2) is non-nil: search the cache
-LMsgSendStretReceiverOk:
- movq isa(%a2), %r11 // class = self->isa
- CacheLookup %a3, _objc_msgSend_stret
- // CacheLookup placed method in %r11
- movq method_imp(%r11), %r11
- test %r11, %r11 // set stret (ne) for forward; r11!=0
- jmp *%r11 // goto *imp
+ NilTest STRET
+
+ GetIsaFast STRET // r11 = self->isa
+ CacheLookup STRET // r11 = method, ne set (stret fwd)
+ jmp *method_imp(%r11) // goto *imp
+
+ NilTestSupport STRET
+
+ GetIsaSupport STRET
// cache miss: go search the method lists
-LCacheMiss_objc_msgSend_stret:
- MethodTableLookup isa(%a2), %a3, _objc_msgSend_stret
- // MethodTableLookup placed IMP in r11
- test %r11, %r11 // set stret (ne) for forward; r11!=0
+LCacheMiss:
+ GetIsa STRET // r11 = self->isa
+ MethodTableLookup %a2, %a3, _objc_msgSend_stret // r11 = IMP
+ test %r11, %r11 // set ne (stret) for forward; r11!=0
jmp *%r11 // goto *imp
-// message sent to nil: redirect to nil receiver, if any
-LMsgSendStretNilSelf:
- movq __objc_nilReceiver(%rip), %a2
- testq %a2, %a2 // if (receiver != nil)
- jne LMsgSendStretReceiverOk // send to new receiver
- ret // else just return
-
-LMsgSendStretExit:
DW_END _objc_msgSend_stret
END_ENTRY _objc_msgSend_stret
ENTRY _objc_msgSend_stret_fixup
DW_START _objc_msgSend_stret_fixup
- testq %a2, %a2
- je LMsgSendStretFixupNilSelf
+ NilTest STRET
- SaveRegisters 0, _objc_msgSend_stret_fixup
+ SaveRegisters _objc_msgSend_stret_fixup
// Dereference obj/isa/cache to crash before _objc_fixupMessageRef
- movq 8(%a3), %r11 // selector
- movq isa(%a2), %a6 // isa = *receiver
- movq cache(%a6), %a5 // cache = *isa
+ movq 8(%a3), %a6 // selector
+ GetIsa STRET // r11 = isa = *receiver
+ movq cache(%r11), %a5 // cache = *isa
movq mask(%a5), %a4 // *cache
// a2 = receiver
// a3 = address of message ref
movq %a2, %a1
- movq $0, %a2
+ xorl %a2d, %a2d
// __objc_fixupMessageRef(receiver, 0, ref)
call __objc_fixupMessageRef
movq %rax, %r11
test %r11, %r11 // set stret (ne) for forward; r11!=0
jmp *%r11 // goto *imp
-LMsgSendStretFixupNilSelf:
- ret
+ NilTestSupport STRET
DW_END _objc_msgSend_stret_fixup
END_ENTRY _objc_msgSend_stret_fixup
- ENTRY _objc_msgSend_stret_fixedup
+ STATIC_ENTRY _objc_msgSend_stret_fixedup
// Load _cmd from the message_ref
movq 8(%a3), %a3
jmp _objc_msgSend_stret
DW_START _objc_msgSendSuper_stret
// search the cache (objc_super in %a2)
- movq class(%a2), %r11 // class = objc_super->class
- CacheLookup %a3, _objc_msgSendSuper_stret
- // CacheLookup placed method in %r11
- movq method_imp(%r11), %r11
+ movq class(%a2), %r11 // class = objc_super->class
+ CacheLookup STRET // r11 = method, ne set (stret fwd)
movq receiver(%a2), %a2 // load real receiver
- test %r11, %r11 // set stret (ne) for forward; r11!=0
- jmp *%r11 // goto *imp
+ jmp *method_imp(%r11) // goto *imp
// cache miss: go search the method lists
-LCacheMiss_objc_msgSendSuper_stret:
- MethodTableLookup class(%a2), %a3, _objc_msgSendSuper_stret
- // MethodTableLookup placed IMP in r11
+LCacheMiss:
+ movq receiver(%a2), %r10
+ movq class(%a2), %r11
+ MethodTableLookup %r10, %a3, _objc_msgSendSuper_stret // r11 = IMP
movq receiver(%a2), %a2 // load real receiver
- test %r11, %r11 // set stret (ne) for forward; r11!=0
+ test %r11, %r11 // set ne (stret) for forward; r11!=0
jmp *%r11 // goto *imp
-LMsgSendSuperStretExit:
DW_END _objc_msgSendSuper_stret
END_ENTRY _objc_msgSendSuper_stret
+
+/********************************************************************
+ * id objc_msgSendSuper2_stret
+ ********************************************************************/
+
#if __OBJC2__
ENTRY _objc_msgSendSuper2_stret_fixup
DW_START _objc_msgSendSuper2_stret_fixup
- SaveRegisters 0, _objc_msgSendSuper2_stret_fixup
+ SaveRegisters _objc_msgSendSuper2_stret_fixup
// a2 = address of objc_super2
// a3 = address of message ref
movq receiver(%a2), %a1
END_ENTRY _objc_msgSendSuper2_stret_fixup
- ENTRY _objc_msgSendSuper2_stret_fixedup
- // objc_super->class is superclass of class to search
- movq class(%a2), %r11 // cls = objc_super->class
+ STATIC_ENTRY _objc_msgSendSuper2_stret_fixedup
movq 8(%a3), %a3 // load _cmd from message_ref
- movq 8(%r11), %r11 // cls = cls->superclass
- movq %r11, class(%a2)
- // objc_super->class is now the class to search
- jmp _objc_msgSendSuper_stret
+ jmp _objc_msgSendSuper2_stret
END_ENTRY _objc_msgSendSuper2_stret_fixedup
- ENTRY _objc_msgSendSuper2_stret
- // objc_super->class is superclass of class to search
- movq class(%a2), %r11 // cls = objc_super->class
- movq 8(%r11), %r11 // cls = cls->superclass
- movq %r11, class(%a2)
- // objc_super->class is now the class to search
- jmp _objc_msgSendSuper_stret
- END_ENTRY _objc_msgSendSuper2_stret
+ ENTRY _objc_msgSendSuper2_stret
+ DW_START _objc_msgSendSuper2_stret
+
+// search the cache (objc_super in %a2)
+ movq class(%a2), %r11 // class = objc_super->class
+ movq 8(%r11), %r11 // class = class->super_class
+ CacheLookup STRET // r11 = method, ne set (stret fwd)
+ movq receiver(%a2), %a2 // load real receiver
+ jmp *method_imp(%r11) // goto *imp
+
+// cache miss: go search the method lists
+LCacheMiss:
+ movq receiver(%a2), %r10
+ movq class(%a2), %r11
+ movq 8(%r11), %r11
+ MethodTableLookup %r10, %a3, _objc_msgSendSuper2_stret // r11 = IMP
+ movq receiver(%a2), %a2 // load real receiver
+ test %r11, %r11 // set ne (stret) for forward; r11!=0
+ jmp *%r11 // goto *imp
+
+ DW_END _objc_msgSendSuper2_stret
+ END_ENTRY _objc_msgSendSuper2_stret
#endif
__objc_forward_stret_handler: .quad 0
- ENTRY __objc_msgForward_internal
- .private_extern __objc_msgForward_internal
+ STATIC_ENTRY __objc_msgForward_internal
// Method cache version
// THIS IS NOT A CALLABLE C FUNCTION
movq %a6, 40+REG_AREA(%rsp)
// Save side parameter registers
- movq %r10, 0+LINK_AREA(%rsp) // static chain (fixme needed?)
+ // movq %r10, 0+LINK_AREA(%rsp) // static chain pointer == Pascal
movq %rax, 8+LINK_AREA(%rsp) // xmm count
// 16+LINK_AREA is return address
// Retrieve return address from linkage area
movq 16+LINK_AREA(%rsp), %r11
// Pop stack frame
- subq $ 8*16 + 6*8 + (4-1)*8, %rsp
+ addq $ 8*16 + 6*8 + (4-1)*8, %rsp
// Put return address back
movq %r11, (%rsp)
ret
movq %a6, 40+REG_AREA(%rsp)
// Save side parameter registers
- movq %r10, 0+LINK_AREA(%rsp) // static chain (fixme needed?)
+ // movq %r10, 0+LINK_AREA(%rsp) // static chain pointer == Pascal
movq %rax, 8+LINK_AREA(%rsp) // xmm count
// 16+LINK_AREA is return address
// Retrieve return address from linkage area
movq 16+LINK_AREA(%rsp), %r11
// Pop stack frame
- subq $ 8*16 + 6*8 + (4-1)*8, %rsp
+ addq $ 8*16 + 6*8 + (4-1)*8, %rsp
// Put return address back
movq %r11, (%rsp)
ret
END_ENTRY __objc_msgForward_stret
+ ENTRY _objc_msgSend_debug
+ jmp _objc_msgSend
+ END_ENTRY _objc_msgSend_debug
+
+ ENTRY _objc_msgSendSuper2_debug
+ jmp _objc_msgSendSuper2
+ END_ENTRY _objc_msgSendSuper2_debug
+
+ ENTRY _objc_msgSend_stret_debug
+ jmp _objc_msgSend_stret
+ END_ENTRY _objc_msgSend_stret_debug
+
+ ENTRY _objc_msgSendSuper2_stret_debug
+ jmp _objc_msgSendSuper2_stret
+ END_ENTRY _objc_msgSendSuper2_stret_debug
+
+ ENTRY _objc_msgSend_fpret_debug
+ jmp _objc_msgSend_fpret
+ END_ENTRY _objc_msgSend_fpret_debug
+
+ ENTRY _objc_msgSend_fp2ret_debug
+ jmp _objc_msgSend_fp2ret
+ END_ENTRY _objc_msgSend_fp2ret_debug
+
+
+ ENTRY _objc_msgSend_noarg
+ jmp _objc_msgSend
+ END_ENTRY _objc_msgSend_noarg
+
+
ENTRY _method_invoke
movq method_imp(%a2), %r11
END_ENTRY _method_invoke_stret
+
+ STATIC_ENTRY __objc_ignored_method
+
+ movq %a1, %rax
+ ret
+
+ END_ENTRY __objc_ignored_method
+
/********************************************************************
*
* This code is copied to create vtable trampolines.
* The instruction following LvtableIndex is modified to
* insert each vtable index.
+ * The instructions following LvtableTagTable are modified to
+ * load the tagged isa table.
*
* This code is placed in its own section to prevent dtrace from
* instrumenting it. Otherwise, dtrace would insert an INT3, the
* code would be copied, and the copied INT3 would cause a crash.
+ *
+ * ABI WARNING ABI WARNING ABI WARNING ABI WARNING ABI WARNING
+ * vtable_prototype steals %rax and does not clear %rdx on return
+ * in order to precisely pack instructions into ifetch and cache lines
+ * This means vtable dispatch must never be used for vararg calls
+ * or very large return values.
+ * ABI WARNING ABI WARNING ABI WARNING ABI WARNING ABI WARNING
*
********************************************************************/
.macro VTABLE /* byte-offset, name */
- .align 2
+ .align 6
.private_extern _$1
_$1:
test %a1, %a1
- je LvtableReturnZero_$1 // nil check
- movq 8(%a2), %a2 // load _cmd (fixme schedule?)
- movq 0(%a1), %r10 // load isa
- movq 24(%r10), %r11 // load vtable
+ je LvtableReturnZero_$1 // nil check
+ testl $$1, %a1d
+ jne LvtableTaggedPointer_$1 // tag check
+
+ movq (%a1), %rax // load isa (see ABI WARNING)
+ movq 24(%rax), %rax // load vtable
+ movq 8(%a2), %a2 // load _cmd
LvtableIndex_$1:
- movq $0 (%r11), %r10 // load imp (DO NOT CHANGE)
- jmp *%r10
+ jmpq * $0 (%rax) // load imp (DO NOT CHANGE)
+
LvtableReturnZero_$1:
// integer registers only; not used for fpret / stret / etc
- movq $$0, %rax
- movq $$0, %rdx
+ xorl %eax, %eax
+ // xorl %edx, %edx (see ABI WARNING)
ret
-LvtableEnd_$1:
+
nop
+LvtableTaggedPointer_$1:
+ // extract isa (bits 1-2-3) from %a1, bit 0 is kept around for the heck of it
+ movl %a1d, %eax
+ andl $$0xF, %eax
+LvtableTagTable_$1:
+.if $0 == 0x7fff
+ movq $$0x1122334455667788, %r10 // vtable_prototype (DO NOT CHANGE)
+.else
+ leaq __objc_tagged_isa_table(%rip), %r10
+.endif
+LvtableTagTableEnd_$1:
+ movq (%r10, %rax, 8), %r10 // load isa from table (see ABI WARNING
+ movq 24(%r10), %rax // load vtable
+ movq 8(%a2), %a2 // load _cmd
+LvtableIndex2_$1:
+ jmpq * $0 (%rax) // load imp (DO NOT CHANGE)
+
+LvtableEnd_$1:
.endmacro
_vtable_prototype_index_offset:
.long LvtableIndex_vtable_prototype - _vtable_prototype
+ .private_extern _vtable_prototype_index2_offset
+_vtable_prototype_index2_offset:
+ .long LvtableIndex2_vtable_prototype - _vtable_prototype
+
+ .private_extern _vtable_prototype_tagtable_offset
+_vtable_prototype_tagtable_offset:
+ .long LvtableTagTable_vtable_prototype - _vtable_prototype
+
+ .private_extern _vtable_prototype_tagtable_size
+_vtable_prototype_tagtable_size:
+ .long LvtableTagTableEnd_vtable_prototype - LvtableTagTable_vtable_prototype
/********************************************************************
*
*
********************************************************************/
- .text
- .align 2
- .private_extern _vtable_ignored
-_vtable_ignored:
+ STATIC_ENTRY _vtable_ignored
movq %a1, %rax
ret
#if __OBJC2__
+__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA)
@interface Object
{
Class isa; /* A pointer to the instance's class structure */
#else
+__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA)
@interface Object
{
Class isa; /* A pointer to the instance's class structure */
#import <string.h>
#import <malloc/malloc.h>
-#define OLD 1
#import "Object.h"
#import "Protocol.h"
#import "objc-runtime.h"
#ifndef _OBJC_LIST_H_
#define _OBJC_LIST_H_
-#if defined(__OBJC2__)
-
-#warning class List unavailable
-
-#else
-
-#warning The API in this header is obsoleted by NSArray.
+#if !__OBJC2__
#import <objc/Object.h>
-#import <AvailabilityMacros.h>
+#import <Availability.h>
-DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER
+AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED
@interface List : Object
{
@public
- id *dataPtr DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER ; /* data of the List object */
- unsigned numElements DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER ; /* Actual number of elements */
- unsigned maxElements DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER ; /* Total allocated elements */
+ id *dataPtr AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; /* data of the List object */
+ unsigned numElements AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; /* Actual number of elements */
+ unsigned maxElements AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; /* Total allocated elements */
}
/* Creating, freeing */
-- free DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- freeObjects DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- copyFromZone:(void *)z DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
+- free AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- freeObjects AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- copyFromZone:(void *)z AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
/* Initializing */
-- init DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- initCount:(unsigned)numSlots DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
+- init AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- initCount:(unsigned)numSlots AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
/* Comparing two lists */
-- (BOOL)isEqual: anObject DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
+- (BOOL)isEqual: anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
/* Managing the storage capacity */
-- (unsigned)capacity DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- setAvailableCapacity:(unsigned)numSlots DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
+- (unsigned)capacity AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- setAvailableCapacity:(unsigned)numSlots AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
/* Manipulating objects by index */
-- (unsigned)count DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- objectAt:(unsigned)index DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- lastObject DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- addObject:anObject DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- insertObject:anObject at:(unsigned)index DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- removeObjectAt:(unsigned)index DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- removeLastObject DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- replaceObjectAt:(unsigned)index with:newObject DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- appendList: (List *)otherList DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
+- (unsigned)count AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- objectAt:(unsigned)index AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- lastObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- addObject:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- insertObject:anObject at:(unsigned)index AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- removeObjectAt:(unsigned)index AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- removeLastObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- replaceObjectAt:(unsigned)index with:newObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- appendList: (List *)otherList AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
/* Manipulating objects by id */
-- (unsigned)indexOf:anObject DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- addObjectIfAbsent:anObject DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- removeObject:anObject DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- replaceObject:anObject with:newObject DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
+- (unsigned)indexOf:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- addObjectIfAbsent:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- removeObject:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- replaceObject:anObject with:newObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
/* Emptying the list */
-- empty DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
+- empty AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
/* Sending messages to elements of the list */
-- makeObjectsPerform:(SEL)aSelector DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- makeObjectsPerform:(SEL)aSelector with:anObject DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
+- makeObjectsPerform:(SEL)aSelector AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
+- makeObjectsPerform:(SEL)aSelector with:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
/*
* The following new... methods are now obsolete. They remain in this
* and the init... methods defined in this class instead.
*/
-+ new DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-+ newCount:(unsigned)numSlots DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
++ new AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
++ newCount:(unsigned)numSlots AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
@end
typedef struct {
@defs(List)
-} NXListId DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
+} NXListId AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED;
#define NX_ADDRESS(x) (((NXListId *)(x))->dataPtr)
#import <stdio.h>
#import <string.h>
-#define OLD 1
#import <objc/List.h>
#define DATASIZE(count) ((count) * sizeof(id))
/* Warning: All of these methods will disappear in 64-bit. */
+__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
@interface Protocol : Object
{
@private
/* Looking up information specific to a protocol */
-- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
-- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
+- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel
+ DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
+- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel
+ DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
@end
#include <mach-o/dyld.h>
#include <mach-o/ldsyms.h>
-#define OLD 1
#import "Protocol.h"
#import "objc-private.h"
+#import "objc-runtime-old.h"
-
-/* some forward declarations */
-
-#if !__OBJC2__
-__private_extern__ struct objc_method_description * lookup_protocol_method(Protocol *proto, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod);
-#else
-__private_extern__ Method _protocol_getMethod(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod);
-#endif
-
+PRIVATE_EXTERN
+@interface __IncompleteProtocol { id isa; } @end
+@implementation __IncompleteProtocol
++(void) initialize { }
+@end
@implementation Protocol
- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel
{
#if !__OBJC2__
- return lookup_protocol_method(self,aSel, YES/*required*/, YES/*instance*/);
+ return lookup_protocol_method((struct old_protocol *)self, aSel,
+ YES/*required*/, YES/*instance*/);
#else
return method_getDescription(_protocol_getMethod(self, aSel, YES, YES));
#endif
- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel
{
#if !__OBJC2__
- return lookup_protocol_method(self, aSel, YES/*required*/, NO/*instance*/);
+ return lookup_protocol_method((struct old_protocol *)self, aSel,
+ YES/*required*/, NO/*instance*/);
#else
return method_getDescription(_protocol_getMethod(self, aSel, YES, NO));
#endif
--- /dev/null
+#if __arm__
+
+#include <arm/arch.h>
+
+.syntax unified
+
+.text
+
+ .private_extern __a1a2_tramphead
+ .private_extern __a1a2_firsttramp
+ .private_extern __a1a2_nexttramp
+ .private_extern __a1a2_trampend
+
+#ifdef _ARM_ARCH_7
+ .thumb
+ .thumb_func __a1a2_tramphead
+ .thumb_func __a1a2_firsttramp
+ .thumb_func __a1a2_nexttramp
+ .thumb_func __a1a2_trampend
+#else
+ // don't use Thumb-1
+ .arm
+#endif
+
+.align 12
+__a1a2_tramphead:
+ /*
+ r0 == self
+ r1 == pc of trampoline's first instruction + PC bias
+ lr == original return address
+ */
+
+ // calculate the trampoline's index (512 entries, 8 bytes each)
+#ifdef _ARM_ARCH_7
+ // PC bias is only 4, no need to correct with 8-byte trampolines
+ ubfx r1, r1, #3, #9
+#else
+ sub r1, r1, #8 // correct PC bias
+ lsl r1, r1, #20
+ lsr r1, r1, #23
+#endif
+
+ // load block pointer from trampoline's data
+ adr r12, __a1a2_tramphead // text page
+ sub r12, r12, #4096 // data page precedes text page
+ ldr r12, [r12, r1, LSL #3] // load block pointer from data + index*8
+
+ // shuffle parameters
+ mov r1, r0 // _cmd = self
+ mov r0, r12 // self = block pointer
+
+ // tail call block->invoke
+ ldr pc, [r12, #12]
+ // not reached
+
+ // Make v6 and v7 match so they have the same number of TrampolineEntry
+ // below. Debug asserts in objc-block-trampoline.m check this.
+#ifdef _ARM_ARCH_7
+ .space 16
+#endif
+
+.macro TrampolineEntry
+ mov r1, pc
+ b __a1a2_tramphead
+ .align 3
+.endmacro
+
+.align 3
+.private_extern __a1a2_firsttramp
+__a1a2_firsttramp:
+ TrampolineEntry
+
+.private_extern __a1a2_nexttramp
+__a1a2_nexttramp:
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+
+.private_extern __a1a2_trampend
+__a1a2_trampend:
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifdef __i386__
+
+.text
+ .private_extern __a1a2_tramphead
+ .private_extern __a1a2_firsttramp
+ .private_extern __a1a2_nexttramp
+ .private_extern __a1a2_trampend
+
+.align 12
+__a1a2_tramphead:
+ popl %eax
+ andl $0xFFFFFFF8, %eax
+ subl $0x1000, %eax
+ movl 4(%esp), %ecx // self -> ecx
+ movl %ecx, 8(%esp) // ecx -> _cmd
+ movl (%eax), %ecx // blockPtr -> ecx
+ movl %ecx, 4(%esp) // ecx -> self
+ jmp *12(%ecx) // tail to block->invoke
+
+.macro TrampolineEntry
+ call __a1a2_tramphead
+ nop
+ nop
+ nop
+.endmacro
+
+.align 5
+__a1a2_firsttramp:
+ TrampolineEntry
+__a1a2_nexttramp: // used to calculate size of each trampoline
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+
+__a1a2_trampend:
+
+#endif
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifdef __x86_64__
+
+ .text
+ .private_extern __a1a2_tramphead
+ .private_extern __a1a2_firsttramp
+ .private_extern __a1a2_nexttramp
+ .private_extern __a1a2_trampend
+
+.align 12
+__a1a2_tramphead:
+ popq %r10
+ andq $0xFFFFFFFFFFFFFFF8, %r10
+ subq $0x1000, %r10
+ movq %rdi, %rsi // arg1 -> arg2
+ movq (%r10), %rdi // block -> arg1
+ jmp *16(%rdi)
+
+.macro TrampolineEntry
+ callq __a1a2_tramphead
+ nop
+ nop
+ nop
+.endmacro
+
+.align 5
+__a1a2_firsttramp:
+ TrampolineEntry
+__a1a2_nexttramp:
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+
+__a1a2_trampend:
+
+#endif
--- /dev/null
+#if __arm__
+
+#include <arm/arch.h>
+
+.syntax unified
+
+.text
+
+#ifdef _ARM_ARCH_7
+ .thumb
+ .thumb_func __a2a3_tramphead
+ .thumb_func __a2a3_firsttramp
+ .thumb_func __a2a3_nexttramp
+ .thumb_func __a2a3_trampend
+#else
+ // don't use Thumb-1
+ .arm
+#endif
+
+.align 12
+.private_extern __a2a3_tramphead
+__a2a3_tramphead:
+ /*
+ r0 == stret
+ r1 == self
+ r2 == pc of trampoline's first instruction + 4
+ lr == original return address
+ */
+
+ // calculate the trampoline's index (512 entries, 8 bytes each)
+#ifdef _ARM_ARCH_7
+ // PC bias is only 4, no need to correct with 8-byte trampolines
+ ubfx r2, r2, #3, #9
+#else
+ sub r2, r2, #8 // correct PC bias
+ lsl r2, r2, #20
+ lsr r2, r2, #23
+#endif
+
+ // load block pointer from trampoline's data
+ adr r12, __a2a3_tramphead // text page
+ sub r12, r12, #4096 // data page precedes text page
+ ldr r12, [r12, r2, LSL #3] // load block pointer from data + index*8
+
+ // shuffle parameters
+ mov r2, r1 // _cmd = self
+ mov r1, r12 // self = block pointer
+
+ // tail call block->invoke
+ ldr pc, [r12, #12]
+ // not reached
+
+ // Make v6 and v7 match so they have the same number of TrampolineEntry
+ // below. Debug asserts in objc-block-trampoline.m check this.
+#ifdef _ARM_ARCH_7
+ .space 16
+#endif
+
+.macro TrampolineEntry
+ mov r2, pc
+ b __a2a3_tramphead
+ .align 3
+.endmacro
+
+.align 3
+.private_extern __a2a3_firsttramp
+__a2a3_firsttramp:
+ TrampolineEntry
+
+.private_extern __a2a3_nexttramp
+__a2a3_nexttramp:
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+TrampolineEntry
+
+.private_extern __a2a3_trampend
+__a2a3_trampend:
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifdef __i386__
+
+.text
+ .private_extern __a2a3_tramphead
+ .private_extern __a2a3_firsttramp
+ .private_extern __a2a3_nexttramp
+ .private_extern __a2a3_trampend
+
+.align 12
+__a2a3_tramphead:
+ popl %eax
+ andl $0xFFFFFFF8, %eax
+ subl $0x1000, %eax
+ movl 8(%esp), %ecx // self -> ecx
+ movl %ecx, 12(%esp) // ecx -> _cmd
+ movl (%eax), %ecx // blockPtr -> ecx
+ movl %ecx, 8(%esp) // ecx -> self
+ jmp *12(%ecx) // tail to block->invoke
+
+.macro TrampolineEntry
+ call __a2a3_tramphead
+ nop
+ nop
+ nop
+.endmacro
+
+.align 5
+__a2a3_firsttramp:
+ TrampolineEntry
+__a2a3_nexttramp:
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+
+__a2a3_trampend:
+
+#endif
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifdef __x86_64__
+
+ .text
+ .private_extern __a2a3_tramphead
+ .private_extern __a2a3_firsttramp
+ .private_extern __a2a3_nexttramp
+ .private_extern __a2a3_trampend
+
+.align 12
+__a2a3_tramphead:
+ popq %r10
+ andq $0xFFFFFFFFFFFFFFF8, %r10
+ subq $0x1000, %r10
+ // %rdi -- first arg -- is address of return value's space. Don't mess with it.
+ movq %rsi, %rdx // arg2 -> arg3
+ movq (%r10), %rsi // block -> arg2
+ jmp *16(%rsi)
+
+.macro TrampolineEntry
+ callq __a2a3_tramphead
+ nop
+ nop
+ nop
+.endmacro
+
+.align 5
+__a2a3_firsttramp:
+ TrampolineEntry
+__a2a3_nexttramp:
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+ TrampolineEntry
+
+__a2a3_trampend:
+
+#endif
+++ /dev/null
-/*
- * Copyright (c) 1999-2003, 2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/*
- error.h
-
- This file defines the interface to the exception raising scheme.
-
- Copyright (c) 1988-1996 NeXT Software, Inc. as an unpublished work.
- All rights reserved.
-*/
-
-#if defined(__OBJC2__)
-
-// This header contains definitions for Libstreams.
-#warning objc/error.h is unavailable.
-
-#endif
-
-#warning The API in this header is obsoleted by NSException et al.
-
-#ifndef _OBJC_ERROR_H_
-#define _OBJC_ERROR_H_
-
-#include <setjmp.h>
-#include <objc/objc-api.h>
-
-
-typedef struct _NXHandler { /* a node in the handler chain */
- jmp_buf jumpState; /* place to longjmp to */
- struct _NXHandler *next; /* ptr to next handler */
- int code; /* error code of exception */
- const void *data1, *data2; /* blind data for describing error */
-} NXHandler;
-
-
-/* Handles RAISE's with nowhere to longjmp to */
-typedef void NXUncaughtExceptionHandler(int code, const void *data1,
- const void *data2);
-OBJC_EXPORT NXUncaughtExceptionHandler *_NXUncaughtExceptionHandler;
-#define NXGetUncaughtExceptionHandler() _NXUncaughtExceptionHandler
-#define NXSetUncaughtExceptionHandler(proc) \
- (_NXUncaughtExceptionHandler = (proc))
-
-/* NX_DURING, NX_HANDLER and NX_ENDHANDLER are always used like:
-
- NX_DURING
- some code which might raise an error
- NX_HANDLER
- code that will be jumped to if an error occurs
- NX_ENDHANDLER
-
- If any error is raised within the first block of code, the second block
- of code will be jumped to. Typically, this code will clean up any
- resources allocated in the routine, possibly case on the error code
- and perform special processing, and default to RERAISE the error to
- the next handler. Within the scope of the handler, a local variable
- called NXLocalHandler of type NXHandler holds information about the
- error raised.
-
- It is illegal to exit the first block of code by any other means than
- NX_VALRETURN, NX_VOIDRETURN, or just falling out the bottom.
- */
-
-/* private support routines. Do not call directly. */
-OBJC_EXPORT void _NXAddHandler( NXHandler *handler );
-OBJC_EXPORT void _NXRemoveHandler( NXHandler *handler );
-
-#define NX_DURING { NXHandler NXLocalHandler; \
- _NXAddHandler(&NXLocalHandler); \
- if( !_setjmp(NXLocalHandler.jumpState) ) {
-
-#define NX_HANDLER _NXRemoveHandler(&NXLocalHandler); } else {
-
-#define NX_ENDHANDLER }}
-
-#define NX_VALRETURN(val) do { typeof(val) temp = (val); \
- _NXRemoveHandler(&NXLocalHandler); \
- return(temp); } while (0)
-
-#define NX_VOIDRETURN do { _NXRemoveHandler(&NXLocalHandler); \
- return; } while (0)
-
-/* RAISE and RERAISE are called to indicate an error condition. They
- initiate the process of jumping up the chain of handlers.
- */
-
-OBJC_EXPORT
-#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
- volatile /* never returns */
-#endif
-void _NXRaiseError(int code, const void *data1, const void *data2)
-#if defined(__GNUC__)
- __attribute__ ((noreturn))
-#endif
-;
-
-#define NX_RAISE( code, data1, data2 ) \
- _NXRaiseError( (code), (data1), (data2) )
-
-#define NX_RERAISE() _NXRaiseError( NXLocalHandler.code, \
- NXLocalHandler.data1, NXLocalHandler.data2 )
-
-/* These routines set and return the procedure which is called when
- exceptions are raised. This procedure must NEVER return. It will
- usually either longjmp, or call the uncaught exception handler.
- The default exception raiser is also declared
- */
-typedef volatile void NXExceptionRaiser(int code, const void *data1, const void *data2);
-OBJC_EXPORT void NXSetExceptionRaiser(NXExceptionRaiser *proc);
-OBJC_EXPORT NXExceptionRaiser *NXGetExceptionRaiser(void);
-OBJC_EXPORT NXExceptionRaiser NXDefaultExceptionRaiser;
-
-
-/* The error buffer is used to allocate data which is passed up to other
- handlers. Clients should clear the error buffer in their top level
- handler. The Application Kit does this.
- */
-OBJC_EXPORT void NXAllocErrorData(int size, void **data);
-OBJC_EXPORT void NXResetErrorData(void);
-
-#endif /* _OBJC_ERROR_H_ */
#define _OBJC_LITTLE_HASHTABLE_H_
#ifndef _OBJC_PRIVATE_H_
-#warning The API in this header is obsoleted by NSHashtable.h
+# define OBJC_HASH_AVAILABILITY __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_1, __IPHONE_NA,__IPHONE_NA);
+#else
+# define OBJC_HASH_AVAILABILITY
#endif
#include <objc/objc.h>
#include <stdint.h>
#include <TargetConditionals.h>
+__BEGIN_DECLS
+
/*************************************************************************
* Hash tables of arbitrary data
*************************************************************************/
*/
typedef struct {
- const NXHashTablePrototype *prototype;
- unsigned count;
- unsigned nbBuckets;
- void *buckets;
- const void *info;
- } NXHashTable;
+ const NXHashTablePrototype *prototype OBJC_HASH_AVAILABILITY;
+ unsigned count OBJC_HASH_AVAILABILITY;
+ unsigned nbBuckets OBJC_HASH_AVAILABILITY;
+ void *buckets OBJC_HASH_AVAILABILITY;
+ const void *info OBJC_HASH_AVAILABILITY;
+ } NXHashTable OBJC_HASH_AVAILABILITY;
/* private data structure; may change */
-OBJC_EXPORT NXHashTable *NXCreateHashTableFromZone (NXHashTablePrototype prototype, unsigned capacity, const void *info, void *z);
-OBJC_EXPORT NXHashTable *NXCreateHashTable (NXHashTablePrototype prototype, unsigned capacity, const void *info);
+OBJC_EXPORT NXHashTable *NXCreateHashTableFromZone (NXHashTablePrototype prototype, unsigned capacity, const void *info, void *z) OBJC_HASH_AVAILABILITY;
+OBJC_EXPORT NXHashTable *NXCreateHashTable (NXHashTablePrototype prototype, unsigned capacity, const void *info) OBJC_HASH_AVAILABILITY;
/* if hash is 0, pointer hash is assumed */
/* if isEqual is 0, pointer equality is assumed */
/* if free is 0, elements are not freed */
/* capacity is only a hint; 0 creates a small table */
/* info allows call backs to be very general */
-OBJC_EXPORT void NXFreeHashTable (NXHashTable *table);
+OBJC_EXPORT void NXFreeHashTable (NXHashTable *table) OBJC_HASH_AVAILABILITY;
/* calls free for each data, and recovers table */
-OBJC_EXPORT void NXEmptyHashTable (NXHashTable *table);
+OBJC_EXPORT void NXEmptyHashTable (NXHashTable *table) OBJC_HASH_AVAILABILITY;
/* does not deallocate table nor data; keeps current capacity */
-OBJC_EXPORT void NXResetHashTable (NXHashTable *table);
+OBJC_EXPORT void NXResetHashTable (NXHashTable *table) OBJC_HASH_AVAILABILITY;
/* frees each entry; keeps current capacity */
-OBJC_EXPORT BOOL NXCompareHashTables (NXHashTable *table1, NXHashTable *table2);
+OBJC_EXPORT BOOL NXCompareHashTables (NXHashTable *table1, NXHashTable *table2) OBJC_HASH_AVAILABILITY;
/* Returns YES if the two sets are equal (each member of table1 in table2, and table have same size) */
-OBJC_EXPORT NXHashTable *NXCopyHashTable (NXHashTable *table);
+OBJC_EXPORT NXHashTable *NXCopyHashTable (NXHashTable *table) OBJC_HASH_AVAILABILITY;
/* makes a fresh table, copying data pointers, not data itself. */
-OBJC_EXPORT unsigned NXCountHashTable (NXHashTable *table);
+OBJC_EXPORT unsigned NXCountHashTable (NXHashTable *table) OBJC_HASH_AVAILABILITY;
/* current number of data in table */
-OBJC_EXPORT int NXHashMember (NXHashTable *table, const void *data);
+OBJC_EXPORT int NXHashMember (NXHashTable *table, const void *data) OBJC_HASH_AVAILABILITY;
/* returns non-0 iff data is present in table.
Example of use when the hashed data is a struct containing the key,
and when the callee only has a key:
return NXHashMember (myTable, &pseudo)
*/
-OBJC_EXPORT void *NXHashGet (NXHashTable *table, const void *data);
+OBJC_EXPORT void *NXHashGet (NXHashTable *table, const void *data) OBJC_HASH_AVAILABILITY;
/* return original table data or NULL.
Example of use when the hashed data is a struct containing the key,
and when the callee only has a key:
original = NXHashGet (myTable, &pseudo)
*/
-OBJC_EXPORT void *NXHashInsert (NXHashTable *table, const void *data);
+OBJC_EXPORT void *NXHashInsert (NXHashTable *table, const void *data) OBJC_HASH_AVAILABILITY;
/* previous data or NULL is returned. */
-OBJC_EXPORT void *NXHashInsertIfAbsent (NXHashTable *table, const void *data);
+OBJC_EXPORT void *NXHashInsertIfAbsent (NXHashTable *table, const void *data) OBJC_HASH_AVAILABILITY;
/* If data already in table, returns the one in table
else adds argument to table and returns argument. */
-OBJC_EXPORT void *NXHashRemove (NXHashTable *table, const void *data);
+OBJC_EXPORT void *NXHashRemove (NXHashTable *table, const void *data) OBJC_HASH_AVAILABILITY;
/* previous data or NULL is returned */
/* Iteration over all elements of a table consists in setting up an iteration state and then to progress until all entries have been visited. An example of use for counting elements in a table is:
}
*/
-typedef struct {int i; int j;} NXHashState;
+typedef struct {int i; int j;} NXHashState OBJC_HASH_AVAILABILITY;
/* callers should not rely on actual contents of the struct */
-OBJC_EXPORT NXHashState NXInitHashState(NXHashTable *table);
+OBJC_EXPORT NXHashState NXInitHashState(NXHashTable *table) OBJC_HASH_AVAILABILITY;
-OBJC_EXPORT int NXNextHashState(NXHashTable *table, NXHashState *state, void **data);
+OBJC_EXPORT int NXNextHashState(NXHashTable *table, NXHashState *state, void **data) OBJC_HASH_AVAILABILITY;
/* returns 0 when all elements have been visited */
/*************************************************************************
* and common prototypes
*************************************************************************/
-OBJC_EXPORT uintptr_t NXPtrHash(const void *info, const void *data);
+OBJC_EXPORT uintptr_t NXPtrHash(const void *info, const void *data) OBJC_HASH_AVAILABILITY;
/* scrambles the address bits; info unused */
-OBJC_EXPORT uintptr_t NXStrHash(const void *info, const void *data);
+OBJC_EXPORT uintptr_t NXStrHash(const void *info, const void *data) OBJC_HASH_AVAILABILITY;
/* string hashing; info unused */
-OBJC_EXPORT int NXPtrIsEqual(const void *info, const void *data1, const void *data2);
+OBJC_EXPORT int NXPtrIsEqual(const void *info, const void *data1, const void *data2) OBJC_HASH_AVAILABILITY;
/* pointer comparison; info unused */
-OBJC_EXPORT int NXStrIsEqual(const void *info, const void *data1, const void *data2);
+OBJC_EXPORT int NXStrIsEqual(const void *info, const void *data1, const void *data2) OBJC_HASH_AVAILABILITY;
/* string comparison; NULL ok; info unused */
-OBJC_EXPORT void NXNoEffectFree(const void *info, void *data);
+OBJC_EXPORT void NXNoEffectFree(const void *info, void *data) OBJC_HASH_AVAILABILITY;
/* no effect; info unused */
-OBJC_EXPORT void NXReallyFree(const void *info, void *data);
+OBJC_EXPORT void NXReallyFree(const void *info, void *data) OBJC_HASH_AVAILABILITY;
/* frees it; info unused */
/* The two following prototypes are useful for manipulating set of pointers or set of strings; For them free is defined as NXNoEffectFree */
-OBJC_EXPORT const NXHashTablePrototype NXPtrPrototype;
+OBJC_EXPORT const NXHashTablePrototype NXPtrPrototype OBJC_HASH_AVAILABILITY;
/* prototype when data is a pointer (void *) */
-OBJC_EXPORT const NXHashTablePrototype NXStrPrototype;
+OBJC_EXPORT const NXHashTablePrototype NXStrPrototype OBJC_HASH_AVAILABILITY;
/* prototype when data is a string (char *) */
/* following prototypes help describe mappings where the key is the first element of a struct and is either a pointer or a string.
For the following prototypes, free is defined as NXReallyFree.
*/
-OBJC_EXPORT const NXHashTablePrototype NXPtrStructKeyPrototype;
-OBJC_EXPORT const NXHashTablePrototype NXStrStructKeyPrototype;
+OBJC_EXPORT const NXHashTablePrototype NXPtrStructKeyPrototype OBJC_HASH_AVAILABILITY;
+OBJC_EXPORT const NXHashTablePrototype NXStrStructKeyPrototype OBJC_HASH_AVAILABILITY;
#if !__OBJC2__ && !TARGET_OS_WIN32
/* Unique strings allows C users to enjoy the benefits of Lisp's atoms:
A unique string is a string that is allocated once for all (never de-allocated) and that has only one representant (thus allowing comparison with == instead of strcmp). A unique string should never be modified (and in fact some memory protection is done to ensure that). In order to more explicitly insist on the fact that the string has been uniqued, a synonym of (const char *) has been added, NXAtom. */
-typedef const char *NXAtom;
+typedef const char *NXAtom OBJC_HASH_AVAILABILITY;
-OBJC_EXPORT NXAtom NXUniqueString(const char *buffer);
+OBJC_EXPORT NXAtom NXUniqueString(const char *buffer) OBJC_HASH_AVAILABILITY;
/* assumes that buffer is \0 terminated, and returns
a previously created string or a new string that is a copy of buffer.
If NULL is passed returns NULL.
Returned string should never be modified. To ensure this invariant,
allocations are made in a special read only zone. */
-OBJC_EXPORT NXAtom NXUniqueStringWithLength(const char *buffer, int length);
+OBJC_EXPORT NXAtom NXUniqueStringWithLength(const char *buffer, int length) OBJC_HASH_AVAILABILITY;
/* assumes that buffer is a non NULL buffer of at least
length characters. Returns a previously created string or
a new string that is a copy of buffer.
If buffer contains \0, string will be truncated.
As for NXUniqueString, returned string should never be modified. */
-OBJC_EXPORT NXAtom NXUniqueStringNoCopy(const char *string);
+OBJC_EXPORT NXAtom NXUniqueStringNoCopy(const char *string) OBJC_HASH_AVAILABILITY;
/* If there is already a unique string equal to string, returns the original.
Otherwise, string is entered in the table, without making a copy. Argument should then never be modified. */
-OBJC_EXPORT char *NXCopyStringBuffer(const char *buffer);
+OBJC_EXPORT char *NXCopyStringBuffer(const char *buffer) OBJC_HASH_AVAILABILITY;
/* given a buffer, allocates a new string copy of buffer.
Buffer should be \0 terminated; returned string is \0 terminated. */
-OBJC_EXPORT char *NXCopyStringBufferFromZone(const char *buffer, void *z);
+OBJC_EXPORT char *NXCopyStringBufferFromZone(const char *buffer, void *z) OBJC_HASH_AVAILABILITY;
/* given a buffer, allocates a new string copy of buffer.
Buffer should be \0 terminated; returned string is \0 terminated. */
#endif
+__END_DECLS
+
#endif /* _OBJC_LITTLE_HASHTABLE_H_ */
#define PTRSIZE sizeof(void *)
-#ifdef NO_ZONES
+#if !SUPPORT_ZONES
# define DEFAULT_ZONE NULL
# define ZONE_FROM_PTR(p) NULL
# define ALLOCTABLE(z) ((NXHashTable *) malloc (sizeof (NXHashTable)))
# define ALLOCPAIRS(z,nb) ((const void **) malloc_zone_calloc (z, nb, sizeof (void *)))
#endif
-#ifdef NO_MOD
+#if !SUPPORT_MOD
/* nbBuckets must be a power of 2 */
# define BUCKETOF(table, data) (((HashBucket *)table->buckets)+((*table->prototype->hash)(table->info, data) & (table->nbBuckets-1)))
# define GOOD_CAPACITY(c) (c <= 1 ? 1 : 1 << (log2u (c-1)+1))
hashPrototype, isEqualPrototype, NXNoEffectFree, 0
};
-static NXHashTable *prototypes NOBSS = NULL;
+static NXHashTable *prototypes = NULL;
/* table of all prototypes */
static void bootstrap (void) {
return NULL;
}
-__private_extern__ unsigned _NXHashCapacity (NXHashTable *table) {
+PRIVATE_EXTERN unsigned _NXHashCapacity (NXHashTable *table) {
return table->nbBuckets;
}
-__private_extern__ void _NXHashRehashToCapacity (NXHashTable *table, unsigned newCapacity) {
+PRIVATE_EXTERN void _NXHashRehashToCapacity (NXHashTable *table, unsigned newCapacity) {
/* Rehash: we create a pseudo table pointing really to the old guys,
extend self, copy the old pairs, and free the pseudo table */
NXHashTable *old;
};
char *NXCopyStringBufferFromZone (const char *str, void *z) {
-#ifdef NO_ZONES
+#if !SUPPORT_ZONES
return strdup(str);
#else
return strcpy ((char *) malloc_zone_malloc(z, strlen (str) + 1), str);
--- /dev/null
+//===- llvm/ADT/DenseMap.h - Dense probed hash table ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the DenseMap class.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_DENSEMAP_H
+#define LLVM_ADT_DENSEMAP_H
+
+#include "llvm-type_traits.h"
+#include <algorithm>
+#include <iterator>
+#include <new>
+#include <utility>
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <TargetConditionals.h>
+
+
+namespace objc {
+
+#if TARGET_OS_IPHONE
+
+// lifted from <MathExtras.h>:
+
+/// CountLeadingZeros_32 - this function performs the platform optimal form of
+/// counting the number of zeros from the most significant bit to the first one
+/// bit. Ex. CountLeadingZeros_32(0x00F000FF) == 8.
+/// Returns 32 if the word is zero.
+inline unsigned CountLeadingZeros_32(uint32_t Value) {
+ unsigned Count; // result
+#if __GNUC__ >= 4
+ // PowerPC is defined for __builtin_clz(0)
+#if !defined(__ppc__) && !defined(__ppc64__)
+ if (!Value) return 32;
+#endif
+ Count = __builtin_clz(Value);
+#else
+ if (!Value) return 32;
+ Count = 0;
+ // bisection method for count leading zeros
+ for (unsigned Shift = 32 >> 1; Shift; Shift >>= 1) {
+ uint32_t Tmp = Value >> Shift;
+ if (Tmp) {
+ Value = Tmp;
+ } else {
+ Count |= Shift;
+ }
+ }
+#endif
+ return Count;
+}
+/// CountLeadingOnes_32 - this function performs the operation of
+/// counting the number of ones from the most significant bit to the first zero
+/// bit. Ex. CountLeadingOnes_32(0xFF0FFF00) == 8.
+/// Returns 32 if the word is all ones.
+inline unsigned CountLeadingOnes_32(uint32_t Value) {
+ return CountLeadingZeros_32(~Value);
+}
+/// CountLeadingZeros_64 - This function performs the platform optimal form
+/// of counting the number of zeros from the most significant bit to the first
+/// one bit (64 bit edition.)
+/// Returns 64 if the word is zero.
+inline unsigned CountLeadingZeros_64(uint64_t Value) {
+ unsigned Count; // result
+#if __GNUC__ >= 4
+ // PowerPC is defined for __builtin_clzll(0)
+#if !defined(__ppc__) && !defined(__ppc64__)
+ if (!Value) return 64;
+#endif
+ Count = __builtin_clzll(Value);
+#else
+ if (sizeof(long) == sizeof(int64_t)) {
+ if (!Value) return 64;
+ Count = 0;
+ // bisection method for count leading zeros
+ for (unsigned Shift = 64 >> 1; Shift; Shift >>= 1) {
+ uint64_t Tmp = Value >> Shift;
+ if (Tmp) {
+ Value = Tmp;
+ } else {
+ Count |= Shift;
+ }
+ }
+ } else {
+ // get hi portion
+ uint32_t Hi = Hi_32(Value);
+ // if some bits in hi portion
+ if (Hi) {
+ // leading zeros in hi portion plus all bits in lo portion
+ Count = CountLeadingZeros_32(Hi);
+ } else {
+ // get lo portion
+ uint32_t Lo = Lo_32(Value);
+ // same as 32 bit value
+ Count = CountLeadingZeros_32(Lo)+32;
+ }
+ }
+#endif
+ return Count;
+}
+/// CountLeadingOnes_64 - This function performs the operation
+/// of counting the number of ones from the most significant bit to the first
+/// zero bit (64 bit edition.)
+/// Returns 64 if the word is all ones.
+inline unsigned CountLeadingOnes_64(uint64_t Value) {
+ return CountLeadingZeros_64(~Value);
+}
+/// CountTrailingZeros_32 - this function performs the platform optimal form of
+/// counting the number of zeros from the least significant bit to the first one
+/// bit. Ex. CountTrailingZeros_32(0xFF00FF00) == 8.
+/// Returns 32 if the word is zero.
+inline unsigned CountTrailingZeros_32(uint32_t Value) {
+#if __GNUC__ >= 4
+ return Value ? __builtin_ctz(Value) : 32;
+#else
+ static const unsigned Mod37BitPosition[] = {
+ 32, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13,
+ 4, 7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9,
+ 5, 20, 8, 19, 18
+ };
+ return Mod37BitPosition[(-Value & Value) % 37];
+#endif
+}
+/// CountTrailingOnes_32 - this function performs the operation of
+/// counting the number of ones from the least significant bit to the first zero
+/// bit. Ex. CountTrailingOnes_32(0x00FF00FF) == 8.
+/// Returns 32 if the word is all ones.
+inline unsigned CountTrailingOnes_32(uint32_t Value) {
+ return CountTrailingZeros_32(~Value);
+}
+/// CountTrailingZeros_64 - This function performs the platform optimal form
+/// of counting the number of zeros from the least significant bit to the first
+/// one bit (64 bit edition.)
+/// Returns 64 if the word is zero.
+inline unsigned CountTrailingZeros_64(uint64_t Value) {
+#if __GNUC__ >= 4
+ return Value ? __builtin_ctzll(Value) : 64;
+#else
+ static const unsigned Mod67Position[] = {
+ 64, 0, 1, 39, 2, 15, 40, 23, 3, 12, 16, 59, 41, 19, 24, 54,
+ 4, 64, 13, 10, 17, 62, 60, 28, 42, 30, 20, 51, 25, 44, 55,
+ 47, 5, 32, 65, 38, 14, 22, 11, 58, 18, 53, 63, 9, 61, 27,
+ 29, 50, 43, 46, 31, 37, 21, 57, 52, 8, 26, 49, 45, 36, 56,
+ 7, 48, 35, 6, 34, 33, 0
+ };
+ return Mod67Position[(-Value & Value) % 67];
+#endif
+}
+
+/// CountTrailingOnes_64 - This function performs the operation
+/// of counting the number of ones from the least significant bit to the first
+/// zero bit (64 bit edition.)
+/// Returns 64 if the word is all ones.
+inline unsigned CountTrailingOnes_64(uint64_t Value) {
+ return CountTrailingZeros_64(~Value);
+}
+/// CountPopulation_32 - this function counts the number of set bits in a value.
+/// Ex. CountPopulation(0xF000F000) = 8
+/// Returns 0 if the word is zero.
+inline unsigned CountPopulation_32(uint32_t Value) {
+#if __GNUC__ >= 4
+ return __builtin_popcount(Value);
+#else
+ uint32_t v = Value - ((Value >> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
+ return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
+#endif
+}
+/// CountPopulation_64 - this function counts the number of set bits in a value,
+/// (64 bit edition.)
+inline unsigned CountPopulation_64(uint64_t Value) {
+#if __GNUC__ >= 4
+ return __builtin_popcountll(Value);
+#else
+ uint64_t v = Value - ((Value >> 1) & 0x5555555555555555ULL);
+ v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL);
+ v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
+ return unsigned((uint64_t)(v * 0x0101010101010101ULL) >> 56);
+#endif
+}
+/// Log2_32 - This function returns the floor log base 2 of the specified value,
+/// -1 if the value is zero. (32 bit edition.)
+/// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2
+inline unsigned Log2_32(uint32_t Value) {
+ return 31 - CountLeadingZeros_32(Value);
+}
+/// Log2_64 - This function returns the floor log base 2 of the specified value,
+/// -1 if the value is zero. (64 bit edition.)
+inline unsigned Log2_64(uint64_t Value) {
+ return 63 - CountLeadingZeros_64(Value);
+}
+/// Log2_32_Ceil - This function returns the ceil log base 2 of the specified
+/// value, 32 if the value is zero. (32 bit edition).
+/// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3
+inline unsigned Log2_32_Ceil(uint32_t Value) {
+ return 32-CountLeadingZeros_32(Value-1);
+}
+
+#endif /* TARGET_OS_IPHONE */
+
+template<typename T>
+struct DenseMapInfo {
+ //static inline T getEmptyKey();
+ //static inline T getTombstoneKey();
+ //static unsigned getHashValue(const T &Val);
+ //static bool isEqual(const T &LHS, const T &RHS);
+};
+
+// Provide DenseMapInfo for all pointers.
+template<typename T>
+struct DenseMapInfo<T*> {
+ static inline T* getEmptyKey() {
+ intptr_t Val = -1;
+ return reinterpret_cast<T*>(Val);
+ }
+ static inline T* getTombstoneKey() {
+ intptr_t Val = -2;
+ return reinterpret_cast<T*>(Val);
+ }
+ static unsigned getHashValue(const T *PtrVal) {
+ return (unsigned((uintptr_t)PtrVal) >> 4) ^
+ (unsigned((uintptr_t)PtrVal) >> 9);
+ }
+ static bool isEqual(const T *LHS, const T *RHS) { return LHS == RHS; }
+};
+
+// Provide DenseMapInfo for chars.
+template<> struct DenseMapInfo<char> {
+ static inline char getEmptyKey() { return ~0; }
+ static inline char getTombstoneKey() { return ~0 - 1; }
+ static unsigned getHashValue(const char& Val) { return Val * 37; }
+ static bool isEqual(const char &LHS, const char &RHS) {
+ return LHS == RHS;
+ }
+};
+
+// Provide DenseMapInfo for unsigned ints.
+template<> struct DenseMapInfo<unsigned> {
+ static inline unsigned getEmptyKey() { return ~0; }
+ static inline unsigned getTombstoneKey() { return ~0U - 1; }
+ static unsigned getHashValue(const unsigned& Val) { return Val * 37; }
+ static bool isEqual(const unsigned& LHS, const unsigned& RHS) {
+ return LHS == RHS;
+ }
+};
+
+// Provide DenseMapInfo for unsigned longs.
+template<> struct DenseMapInfo<unsigned long> {
+ static inline unsigned long getEmptyKey() { return ~0UL; }
+ static inline unsigned long getTombstoneKey() { return ~0UL - 1L; }
+ static unsigned getHashValue(const unsigned long& Val) {
+ return (unsigned)(Val * 37UL);
+ }
+ static bool isEqual(const unsigned long& LHS, const unsigned long& RHS) {
+ return LHS == RHS;
+ }
+};
+
+// Provide DenseMapInfo for unsigned long longs.
+template<> struct DenseMapInfo<unsigned long long> {
+ static inline unsigned long long getEmptyKey() { return ~0ULL; }
+ static inline unsigned long long getTombstoneKey() { return ~0ULL - 1ULL; }
+ static unsigned getHashValue(const unsigned long long& Val) {
+ return (unsigned)(Val * 37ULL);
+ }
+ static bool isEqual(const unsigned long long& LHS,
+ const unsigned long long& RHS) {
+ return LHS == RHS;
+ }
+};
+
+// Provide DenseMapInfo for ints.
+template<> struct DenseMapInfo<int> {
+ static inline int getEmptyKey() { return 0x7fffffff; }
+ static inline int getTombstoneKey() { return -0x7fffffff - 1; }
+ static unsigned getHashValue(const int& Val) { return (unsigned)(Val * 37); }
+ static bool isEqual(const int& LHS, const int& RHS) {
+ return LHS == RHS;
+ }
+};
+
+// Provide DenseMapInfo for longs.
+template<> struct DenseMapInfo<long> {
+ static inline long getEmptyKey() {
+ return (1UL << (sizeof(long) * 8 - 1)) - 1L;
+ }
+ static inline long getTombstoneKey() { return getEmptyKey() - 1L; }
+ static unsigned getHashValue(const long& Val) {
+ return (unsigned)(Val * 37L);
+ }
+ static bool isEqual(const long& LHS, const long& RHS) {
+ return LHS == RHS;
+ }
+};
+
+// Provide DenseMapInfo for long longs.
+template<> struct DenseMapInfo<long long> {
+ static inline long long getEmptyKey() { return 0x7fffffffffffffffLL; }
+ static inline long long getTombstoneKey() { return -0x7fffffffffffffffLL-1; }
+ static unsigned getHashValue(const long long& Val) {
+ return (unsigned)(Val * 37LL);
+ }
+ static bool isEqual(const long long& LHS,
+ const long long& RHS) {
+ return LHS == RHS;
+ }
+};
+
+// Provide DenseMapInfo for all pairs whose members have info.
+template<typename T, typename U>
+struct DenseMapInfo<std::pair<T, U> > {
+ typedef std::pair<T, U> Pair;
+ typedef DenseMapInfo<T> FirstInfo;
+ typedef DenseMapInfo<U> SecondInfo;
+
+ static inline Pair getEmptyKey() {
+ return std::make_pair(FirstInfo::getEmptyKey(),
+ SecondInfo::getEmptyKey());
+ }
+ static inline Pair getTombstoneKey() {
+ return std::make_pair(FirstInfo::getTombstoneKey(),
+ SecondInfo::getEmptyKey());
+ }
+ static unsigned getHashValue(const Pair& PairVal) {
+ uint64_t key = (uint64_t)FirstInfo::getHashValue(PairVal.first) << 32
+ | (uint64_t)SecondInfo::getHashValue(PairVal.second);
+ key += ~(key << 32);
+ key ^= (key >> 22);
+ key += ~(key << 13);
+ key ^= (key >> 8);
+ key += (key << 3);
+ key ^= (key >> 15);
+ key += ~(key << 27);
+ key ^= (key >> 31);
+ return (unsigned)key;
+ }
+ static bool isEqual(const Pair& LHS, const Pair& RHS) { return LHS == RHS; }
+};
+
+} // end namespace objc
+
+
+
+namespace objc {
+
+template<typename KeyT, typename ValueT,
+ typename KeyInfoT = DenseMapInfo<KeyT>,
+ typename ValueInfoT = DenseMapInfo<ValueT>, bool IsConst = false>
+class DenseMapIterator;
+
+// ZeroValuesArePurgeable=true is used by the refcount table.
+// A key/value pair with value==0 is not required to be stored
+// in the refcount table; it could correctly be erased instead.
+// For performance, we do keep zero values in the table when the
+// true refcount decreases to 1: this makes any future retain faster.
+// For memory size, we allow rehashes and table insertions to
+// remove a zero value as if it were a tombstone.
+
+template<typename KeyT, typename ValueT,
+ bool ZeroValuesArePurgeable = false,
+ typename KeyInfoT = DenseMapInfo<KeyT>,
+ typename ValueInfoT = DenseMapInfo<ValueT> >
+class DenseMap {
+ typedef std::pair<KeyT, ValueT> BucketT;
+ unsigned NumBuckets;
+ BucketT *Buckets;
+
+ unsigned NumEntries;
+ unsigned NumTombstones;
+public:
+ typedef KeyT key_type;
+ typedef ValueT mapped_type;
+ typedef BucketT value_type;
+
+ DenseMap(const DenseMap &other) {
+ NumBuckets = 0;
+ CopyFrom(other);
+ }
+
+ explicit DenseMap(unsigned NumInitBuckets = 64) {
+ init(NumInitBuckets);
+ }
+
+ template<typename InputIt>
+ DenseMap(const InputIt &I, const InputIt &E) {
+ init(64);
+ insert(I, E);
+ }
+
+ ~DenseMap() {
+ const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey();
+ for (BucketT *P = Buckets, *E = Buckets+NumBuckets; P != E; ++P) {
+ if (!KeyInfoT::isEqual(P->first, EmptyKey) &&
+ !KeyInfoT::isEqual(P->first, TombstoneKey))
+ P->second.~ValueT();
+ P->first.~KeyT();
+ }
+#ifndef NDEBUG
+ memset(Buckets, 0x5a, sizeof(BucketT)*NumBuckets);
+#endif
+ operator delete(Buckets);
+ }
+
+ typedef DenseMapIterator<KeyT, ValueT, KeyInfoT> iterator;
+ typedef DenseMapIterator<KeyT, ValueT,
+ KeyInfoT, ValueInfoT, true> const_iterator;
+ inline iterator begin() {
+ // When the map is empty, avoid the overhead of AdvancePastEmptyBuckets().
+ return empty() ? end() : iterator(Buckets, Buckets+NumBuckets);
+ }
+ inline iterator end() {
+ return iterator(Buckets+NumBuckets, Buckets+NumBuckets);
+ }
+ inline const_iterator begin() const {
+ return empty() ? end() : const_iterator(Buckets, Buckets+NumBuckets);
+ }
+ inline const_iterator end() const {
+ return const_iterator(Buckets+NumBuckets, Buckets+NumBuckets);
+ }
+
+ bool empty() const { return NumEntries == 0; }
+ unsigned size() const { return NumEntries; }
+
+ /// Grow the densemap so that it has at least Size buckets. Does not shrink
+ void resize(size_t Size) { grow(Size); }
+
+ void clear() {
+ if (NumEntries == 0 && NumTombstones == 0) return;
+
+ // If the capacity of the array is huge, and the # elements used is small,
+ // shrink the array.
+ if (NumEntries * 4 < NumBuckets && NumBuckets > 64) {
+ shrink_and_clear();
+ return;
+ }
+
+ const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey();
+ for (BucketT *P = Buckets, *E = Buckets+NumBuckets; P != E; ++P) {
+ if (!KeyInfoT::isEqual(P->first, EmptyKey)) {
+ if (!KeyInfoT::isEqual(P->first, TombstoneKey)) {
+ P->second.~ValueT();
+ --NumEntries;
+ }
+ P->first = EmptyKey;
+ }
+ }
+ assert(NumEntries == 0 && "Node count imbalance!");
+ NumTombstones = 0;
+ }
+
+ /// count - Return true if the specified key is in the map.
+ bool count(const KeyT &Val) const {
+ BucketT *TheBucket;
+ return LookupBucketFor(Val, TheBucket);
+ }
+
+ iterator find(const KeyT &Val) {
+ BucketT *TheBucket;
+ if (LookupBucketFor(Val, TheBucket))
+ return iterator(TheBucket, Buckets+NumBuckets);
+ return end();
+ }
+ const_iterator find(const KeyT &Val) const {
+ BucketT *TheBucket;
+ if (LookupBucketFor(Val, TheBucket))
+ return const_iterator(TheBucket, Buckets+NumBuckets);
+ return end();
+ }
+
+ /// lookup - Return the entry for the specified key, or a default
+ /// constructed value if no such entry exists.
+ ValueT lookup(const KeyT &Val) const {
+ BucketT *TheBucket;
+ if (LookupBucketFor(Val, TheBucket))
+ return TheBucket->second;
+ return ValueT();
+ }
+
+ // Inserts key,value pair into the map if the key isn't already in the map.
+ // If the key is already in the map, it returns false and doesn't update the
+ // value.
+ std::pair<iterator, bool> insert(const std::pair<KeyT, ValueT> &KV) {
+ BucketT *TheBucket;
+ if (LookupBucketFor(KV.first, TheBucket))
+ return std::make_pair(iterator(TheBucket, Buckets+NumBuckets),
+ false); // Already in map.
+
+ // Otherwise, insert the new element.
+ TheBucket = InsertIntoBucket(KV.first, KV.second, TheBucket);
+ return std::make_pair(iterator(TheBucket, Buckets+NumBuckets),
+ true);
+ }
+
+ /// insert - Range insertion of pairs.
+ template<typename InputIt>
+ void insert(InputIt I, InputIt E) {
+ for (; I != E; ++I)
+ insert(*I);
+ }
+
+
+ bool erase(const KeyT &Val) {
+ BucketT *TheBucket;
+ if (!LookupBucketFor(Val, TheBucket))
+ return false; // not in map.
+
+ TheBucket->second.~ValueT();
+ TheBucket->first = getTombstoneKey();
+ --NumEntries;
+ ++NumTombstones;
+ return true;
+ }
+ void erase(iterator I) {
+ BucketT *TheBucket = &*I;
+ TheBucket->second.~ValueT();
+ TheBucket->first = getTombstoneKey();
+ --NumEntries;
+ ++NumTombstones;
+ }
+
+ void swap(DenseMap& RHS) {
+ std::swap(NumBuckets, RHS.NumBuckets);
+ std::swap(Buckets, RHS.Buckets);
+ std::swap(NumEntries, RHS.NumEntries);
+ std::swap(NumTombstones, RHS.NumTombstones);
+ }
+
+ value_type& FindAndConstruct(const KeyT &Key) {
+ BucketT *TheBucket;
+ if (LookupBucketFor(Key, TheBucket))
+ return *TheBucket;
+
+ return *InsertIntoBucket(Key, ValueT(), TheBucket);
+ }
+
+ ValueT &operator[](const KeyT &Key) {
+ return FindAndConstruct(Key).second;
+ }
+
+ DenseMap& operator=(const DenseMap& other) {
+ CopyFrom(other);
+ return *this;
+ }
+
+ /// isPointerIntoBucketsArray - Return true if the specified pointer points
+ /// somewhere into the DenseMap's array of buckets (i.e. either to a key or
+ /// value in the DenseMap).
+ bool isPointerIntoBucketsArray(const void *Ptr) const {
+ return Ptr >= Buckets && Ptr < Buckets+NumBuckets;
+ }
+
+ /// getPointerIntoBucketsArray() - Return an opaque pointer into the buckets
+ /// array. In conjunction with the previous method, this can be used to
+ /// determine whether an insertion caused the DenseMap to reallocate.
+ const void *getPointerIntoBucketsArray() const { return Buckets; }
+
+private:
+ void CopyFrom(const DenseMap& other) {
+ if (NumBuckets != 0 &&
+ (!isPodLike<KeyInfoT>::value || !isPodLike<ValueInfoT>::value)) {
+ const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey();
+ for (BucketT *P = Buckets, *E = Buckets+NumBuckets; P != E; ++P) {
+ if (!KeyInfoT::isEqual(P->first, EmptyKey) &&
+ !KeyInfoT::isEqual(P->first, TombstoneKey))
+ P->second.~ValueT();
+ P->first.~KeyT();
+ }
+ }
+
+ NumEntries = other.NumEntries;
+ NumTombstones = other.NumTombstones;
+
+ if (NumBuckets) {
+#ifndef NDEBUG
+ memset(Buckets, 0x5a, sizeof(BucketT)*NumBuckets);
+#endif
+ operator delete(Buckets);
+ }
+ Buckets = static_cast<BucketT*>(operator new(sizeof(BucketT) *
+ other.NumBuckets));
+
+ if (isPodLike<KeyInfoT>::value && isPodLike<ValueInfoT>::value)
+ memcpy(Buckets, other.Buckets, other.NumBuckets * sizeof(BucketT));
+ else
+ for (size_t i = 0; i < other.NumBuckets; ++i) {
+ new (&Buckets[i].first) KeyT(other.Buckets[i].first);
+ if (!KeyInfoT::isEqual(Buckets[i].first, getEmptyKey()) &&
+ !KeyInfoT::isEqual(Buckets[i].first, getTombstoneKey()))
+ new (&Buckets[i].second) ValueT(other.Buckets[i].second);
+ }
+ NumBuckets = other.NumBuckets;
+ }
+
+ BucketT *InsertIntoBucket(const KeyT &Key, const ValueT &Value,
+ BucketT *TheBucket) {
+ // If the load of the hash table is more than 3/4, grow the table.
+ // If fewer than 1/8 of the buckets are empty (meaning that many are
+ // filled with tombstones), rehash the table without growing.
+ //
+ // The later case is tricky. For example, if we had one empty bucket with
+ // tons of tombstones, failing lookups (e.g. for insertion) would have to
+ // probe almost the entire table until it found the empty bucket. If the
+ // table completely filled with tombstones, no lookup would ever succeed,
+ // causing infinite loops in lookup.
+ ++NumEntries;
+ if (NumEntries*4 >= NumBuckets*3) {
+ this->grow(NumBuckets * 2);
+ LookupBucketFor(Key, TheBucket);
+ }
+ else if (NumBuckets-(NumEntries+NumTombstones) < NumBuckets/8) {
+ this->grow(NumBuckets);
+ LookupBucketFor(Key, TheBucket);
+ }
+
+ // If we are writing over a tombstone or zero value, remember this.
+ if (!KeyInfoT::isEqual(TheBucket->first, getEmptyKey())) {
+ if (KeyInfoT::isEqual(TheBucket->first, getTombstoneKey())) {
+ --NumTombstones;
+ } else {
+ assert(ZeroValuesArePurgeable && TheBucket->second == 0);
+ TheBucket->second.~ValueT();
+ --NumEntries;
+ }
+ }
+
+ TheBucket->first = Key;
+ new (&TheBucket->second) ValueT(Value);
+ return TheBucket;
+ }
+
+ static unsigned getHashValue(const KeyT &Val) {
+ return KeyInfoT::getHashValue(Val);
+ }
+ static const KeyT getEmptyKey() {
+ return KeyInfoT::getEmptyKey();
+ }
+ static const KeyT getTombstoneKey() {
+ return KeyInfoT::getTombstoneKey();
+ }
+
+ /// LookupBucketFor - Lookup the appropriate bucket for Val, returning it in
+ /// FoundBucket. If the bucket contains the key and a value, this returns
+ /// true, otherwise it returns a bucket with an empty marker or tombstone
+ /// or zero value and returns false.
+ bool LookupBucketFor(const KeyT &Val, BucketT *&FoundBucket) const {
+ unsigned BucketNo = getHashValue(Val);
+ unsigned ProbeAmt = 1;
+ BucketT *BucketsPtr = Buckets;
+
+ // FoundTombstone - Keep track of whether we find a tombstone or zero value while probing.
+ BucketT *FoundTombstone = 0;
+ const KeyT EmptyKey = getEmptyKey();
+ const KeyT TombstoneKey = getTombstoneKey();
+ assert(!KeyInfoT::isEqual(Val, EmptyKey) &&
+ !KeyInfoT::isEqual(Val, TombstoneKey) &&
+ "Empty/Tombstone value shouldn't be inserted into map!");
+
+ while (1) {
+ BucketT *ThisBucket = BucketsPtr + (BucketNo & (NumBuckets-1));
+ // Found Val's bucket? If so, return it.
+ if (KeyInfoT::isEqual(ThisBucket->first, Val)) {
+ FoundBucket = ThisBucket;
+ return true;
+ }
+
+ // If we found an empty bucket, the key doesn't exist in the set.
+ // Insert it and return the default value.
+ if (KeyInfoT::isEqual(ThisBucket->first, EmptyKey)) {
+ // If we've already seen a tombstone while probing, fill it in instead
+ // of the empty bucket we eventually probed to.
+ if (FoundTombstone) ThisBucket = FoundTombstone;
+ FoundBucket = FoundTombstone ? FoundTombstone : ThisBucket;
+ return false;
+ }
+
+ // If this is a tombstone, remember it. If Val ends up not in the map, we
+ // prefer to return it than something that would require more probing.
+ // Ditto for zero values.
+ if (KeyInfoT::isEqual(ThisBucket->first, TombstoneKey) && !FoundTombstone)
+ FoundTombstone = ThisBucket; // Remember the first tombstone found.
+ if (ZeroValuesArePurgeable &&
+ ThisBucket->second == 0 && !FoundTombstone)
+ FoundTombstone = ThisBucket;
+
+ // Otherwise, it's a hash collision or a tombstone, continue quadratic
+ // probing.
+ BucketNo += ProbeAmt++;
+ }
+ }
+
+ void init(unsigned InitBuckets) {
+ NumEntries = 0;
+ NumTombstones = 0;
+ NumBuckets = InitBuckets;
+ assert(InitBuckets && (InitBuckets & (InitBuckets-1)) == 0 &&
+ "# initial buckets must be a power of two!");
+ Buckets = static_cast<BucketT*>(operator new(sizeof(BucketT)*InitBuckets));
+ // Initialize all the keys to EmptyKey.
+ const KeyT EmptyKey = getEmptyKey();
+ for (unsigned i = 0; i != InitBuckets; ++i)
+ new (&Buckets[i].first) KeyT(EmptyKey);
+ }
+
+ void grow(unsigned AtLeast) {
+ unsigned OldNumBuckets = NumBuckets;
+ BucketT *OldBuckets = Buckets;
+
+ // Double the number of buckets.
+ while (NumBuckets < AtLeast)
+ NumBuckets <<= 1;
+ NumTombstones = 0;
+ Buckets = static_cast<BucketT*>(operator new(sizeof(BucketT)*NumBuckets));
+
+ // Initialize all the keys to EmptyKey.
+ const KeyT EmptyKey = getEmptyKey();
+ for (unsigned i = 0, e = NumBuckets; i != e; ++i)
+ new (&Buckets[i].first) KeyT(EmptyKey);
+
+ // Insert all the old elements.
+ const KeyT TombstoneKey = getTombstoneKey();
+ for (BucketT *B = OldBuckets, *E = OldBuckets+OldNumBuckets; B != E; ++B) {
+ if (!KeyInfoT::isEqual(B->first, EmptyKey) &&
+ !KeyInfoT::isEqual(B->first, TombstoneKey))
+ {
+ // Valid key/value, or zero value
+ if (!ZeroValuesArePurgeable || B->second != 0) {
+ // Insert the key/value into the new table.
+ BucketT *DestBucket;
+ bool FoundVal = LookupBucketFor(B->first, DestBucket);
+ (void)FoundVal; // silence warning.
+ assert(!FoundVal && "Key already in new map?");
+ DestBucket->first = B->first;
+ new (&DestBucket->second) ValueT(B->second);
+ } else {
+ NumEntries--;
+ }
+
+ // Free the value.
+ B->second.~ValueT();
+ }
+ B->first.~KeyT();
+ }
+
+#ifndef NDEBUG
+ memset(OldBuckets, 0x5a, sizeof(BucketT)*OldNumBuckets);
+#endif
+ // Free the old table.
+ operator delete(OldBuckets);
+ }
+
+ void shrink_and_clear() {
+ unsigned OldNumBuckets = NumBuckets;
+ BucketT *OldBuckets = Buckets;
+
+ // Reduce the number of buckets.
+ NumBuckets = NumEntries > 32 ? 1 << (Log2_32_Ceil(NumEntries) + 1)
+ : 64;
+ NumTombstones = 0;
+ Buckets = static_cast<BucketT*>(operator new(sizeof(BucketT)*NumBuckets));
+
+ // Initialize all the keys to EmptyKey.
+ const KeyT EmptyKey = getEmptyKey();
+ for (unsigned i = 0, e = NumBuckets; i != e; ++i)
+ new (&Buckets[i].first) KeyT(EmptyKey);
+
+ // Free the old buckets.
+ const KeyT TombstoneKey = getTombstoneKey();
+ for (BucketT *B = OldBuckets, *E = OldBuckets+OldNumBuckets; B != E; ++B) {
+ if (!KeyInfoT::isEqual(B->first, EmptyKey) &&
+ !KeyInfoT::isEqual(B->first, TombstoneKey)) {
+ // Free the value.
+ B->second.~ValueT();
+ }
+ B->first.~KeyT();
+ }
+
+#ifndef NDEBUG
+ memset(OldBuckets, 0x5a, sizeof(BucketT)*OldNumBuckets);
+#endif
+ // Free the old table.
+ operator delete(OldBuckets);
+
+ NumEntries = 0;
+ }
+};
+
+template<typename KeyT, typename ValueT,
+ typename KeyInfoT, typename ValueInfoT, bool IsConst>
+class DenseMapIterator {
+ typedef std::pair<KeyT, ValueT> Bucket;
+ typedef DenseMapIterator<KeyT, ValueT,
+ KeyInfoT, ValueInfoT, true> ConstIterator;
+ friend class DenseMapIterator<KeyT, ValueT, KeyInfoT, ValueInfoT, true>;
+public:
+ typedef ptrdiff_t difference_type;
+ typedef typename conditional<IsConst, const Bucket, Bucket>::type value_type;
+ typedef value_type *pointer;
+ typedef value_type &reference;
+ typedef std::forward_iterator_tag iterator_category;
+private:
+ pointer Ptr, End;
+public:
+ DenseMapIterator() : Ptr(0), End(0) {}
+
+ DenseMapIterator(pointer Pos, pointer E) : Ptr(Pos), End(E) {
+ AdvancePastEmptyBuckets();
+ }
+
+ // If IsConst is true this is a converting constructor from iterator to
+ // const_iterator and the default copy constructor is used.
+ // Otherwise this is a copy constructor for iterator.
+ DenseMapIterator(const DenseMapIterator<KeyT, ValueT,
+ KeyInfoT, ValueInfoT, false>& I)
+ : Ptr(I.Ptr), End(I.End) {}
+
+ reference operator*() const {
+ return *Ptr;
+ }
+ pointer operator->() const {
+ return Ptr;
+ }
+
+ bool operator==(const ConstIterator &RHS) const {
+ return Ptr == RHS.operator->();
+ }
+ bool operator!=(const ConstIterator &RHS) const {
+ return Ptr != RHS.operator->();
+ }
+
+ inline DenseMapIterator& operator++() { // Preincrement
+ ++Ptr;
+ AdvancePastEmptyBuckets();
+ return *this;
+ }
+ DenseMapIterator operator++(int) { // Postincrement
+ DenseMapIterator tmp = *this; ++*this; return tmp;
+ }
+
+private:
+ void AdvancePastEmptyBuckets() {
+ const KeyT Empty = KeyInfoT::getEmptyKey();
+ const KeyT Tombstone = KeyInfoT::getTombstoneKey();
+
+ while (Ptr != End &&
+ (KeyInfoT::isEqual(Ptr->first, Empty) ||
+ KeyInfoT::isEqual(Ptr->first, Tombstone)))
+ ++Ptr;
+ }
+};
+
+} // end namespace objc
+
+#endif
--- /dev/null
+//===- llvm/Support/type_traits.h - Simplfied type traits -------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides a template class that determines if a type is a class or
+// not. The basic mechanism, based on using the pointer to member function of
+// a zero argument to a function was "boosted" from the boost type_traits
+// library. See http://www.boost.org/ for all the gory details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_TYPE_TRAITS_H
+#define LLVM_SUPPORT_TYPE_TRAITS_H
+
+#include <utility>
+
+// This is actually the conforming implementation which works with abstract
+// classes. However, enough compilers have trouble with it that most will use
+// the one in boost/type_traits/object_traits.hpp. This implementation actually
+// works with VC7.0, but other interactions seem to fail when we use it.
+
+namespace objc {
+
+namespace dont_use
+{
+ // These two functions should never be used. They are helpers to
+ // the is_class template below. They cannot be located inside
+ // is_class because doing so causes at least GCC to think that
+ // the value of the "value" enumerator is not constant. Placing
+ // them out here (for some strange reason) allows the sizeof
+ // operator against them to magically be constant. This is
+ // important to make the is_class<T>::value idiom zero cost. it
+ // evaluates to a constant 1 or 0 depending on whether the
+ // parameter T is a class or not (respectively).
+ template<typename T> char is_class_helper(void(T::*)());
+ template<typename T> double is_class_helper(...);
+}
+
+template <typename T>
+struct is_class
+{
+ // is_class<> metafunction due to Paul Mensonides (leavings@attbi.com). For
+ // more details:
+ // http://groups.google.com/groups?hl=en&selm=000001c1cc83%24e154d5e0%247772e50c%40c161550a&rnum=1
+ public:
+ enum { value = sizeof(char) == sizeof(dont_use::is_class_helper<T>(0)) };
+};
+
+
+/// isPodLike - This is a type trait that is used to determine whether a given
+/// type can be copied around with memcpy instead of running ctors etc.
+template <typename T>
+struct isPodLike {
+ // If we don't know anything else, we can (at least) assume that all non-class
+ // types are PODs.
+ static const bool value = !is_class<T>::value;
+};
+
+// std::pair's are pod-like if their elements are.
+template<typename T, typename U>
+struct isPodLike<std::pair<T, U> > {
+ static const bool value = isPodLike<T>::value & isPodLike<U>::value;
+};
+
+
+/// \brief Metafunction that determines whether the two given types are
+/// equivalent.
+template<typename T, typename U>
+struct is_same {
+ static const bool value = false;
+};
+
+template<typename T>
+struct is_same<T, T> {
+ static const bool value = true;
+};
+
+// enable_if_c - Enable/disable a template based on a metafunction
+template<bool Cond, typename T = void>
+struct enable_if_c {
+ typedef T type;
+};
+
+template<typename T> struct enable_if_c<false, T> { };
+
+// enable_if - Enable/disable a template based on a metafunction
+template<typename Cond, typename T = void>
+struct enable_if : public enable_if_c<Cond::value, T> { };
+
+namespace dont_use {
+ template<typename Base> char base_of_helper(const volatile Base*);
+ template<typename Base> double base_of_helper(...);
+}
+
+/// is_base_of - Metafunction to determine whether one type is a base class of
+/// (or identical to) another type.
+template<typename Base, typename Derived>
+struct is_base_of {
+ static const bool value
+ = is_class<Base>::value && is_class<Derived>::value &&
+ sizeof(char) == sizeof(dont_use::base_of_helper<Base>((Derived*)0));
+};
+
+// remove_pointer - Metafunction to turn Foo* into Foo. Defined in
+// C++0x [meta.trans.ptr].
+template <typename T> struct remove_pointer { typedef T type; };
+template <typename T> struct remove_pointer<T*> { typedef T type; };
+template <typename T> struct remove_pointer<T*const> { typedef T type; };
+template <typename T> struct remove_pointer<T*volatile> { typedef T type; };
+template <typename T> struct remove_pointer<T*const volatile> {
+ typedef T type; };
+
+template <bool, typename T, typename F>
+struct conditional { typedef T type; };
+
+template <typename T, typename F>
+struct conditional<false, T, F> { typedef F type; };
+
+}
+
+#endif
#define _OBJC_MAPTABLE_H_
#ifndef _OBJC_PRIVATE_H_
-#warning the API in this header is obsolete
+# define OBJC_MAP_AVAILABILITY __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_1, __IPHONE_NA,__IPHONE_NA);
+#else
+# define OBJC_MAP_AVAILABILITY
#endif
#include <objc/objc.h>
+__BEGIN_DECLS
+
/*************** Definitions ***************/
/* This module allows hashing of arbitrary associations [key -> value]. Keys and values must be pointers or integers, and client is responsible for allocating/deallocating this data. A deallocation call-back is provided.
/* private data structure; may change */
const struct _NXMapTablePrototype *prototype;
unsigned count;
- unsigned nbBuckets;
+ unsigned nbBucketsMinusOne;
void *buckets;
-} NXMapTable;
+} NXMapTable OBJC_MAP_AVAILABILITY;
typedef struct _NXMapTablePrototype {
unsigned (*hash)(NXMapTable *, const void *key);
int (*isEqual)(NXMapTable *, const void *key1, const void *key2);
void (*free)(NXMapTable *, void *key, void *value);
int style; /* reserved for future expansion; currently 0 */
-} NXMapTablePrototype;
+} NXMapTablePrototype OBJC_MAP_AVAILABILITY;
/* invariants assumed by the implementation:
A - key != -1
/*************** Functions ***************/
-OBJC_EXPORT NXMapTable *NXCreateMapTableFromZone(NXMapTablePrototype prototype, unsigned capacity, void *z);
-OBJC_EXPORT NXMapTable *NXCreateMapTable(NXMapTablePrototype prototype, unsigned capacity);
+OBJC_EXPORT NXMapTable *NXCreateMapTableFromZone(NXMapTablePrototype prototype, unsigned capacity, void *z) OBJC_MAP_AVAILABILITY;
+OBJC_EXPORT NXMapTable *NXCreateMapTable(NXMapTablePrototype prototype, unsigned capacity) OBJC_MAP_AVAILABILITY;
/* capacity is only a hint; 0 creates a small table */
-OBJC_EXPORT void NXFreeMapTable(NXMapTable *table);
+OBJC_EXPORT void NXFreeMapTable(NXMapTable *table) OBJC_MAP_AVAILABILITY;
/* call free for each pair, and recovers table */
-OBJC_EXPORT void NXResetMapTable(NXMapTable *table);
+OBJC_EXPORT void NXResetMapTable(NXMapTable *table) OBJC_MAP_AVAILABILITY;
/* free each pair; keep current capacity */
-OBJC_EXPORT BOOL NXCompareMapTables(NXMapTable *table1, NXMapTable *table2);
+OBJC_EXPORT BOOL NXCompareMapTables(NXMapTable *table1, NXMapTable *table2) OBJC_MAP_AVAILABILITY;
/* Returns YES if the two sets are equal (each member of table1 in table2, and table have same size) */
-OBJC_EXPORT unsigned NXCountMapTable(NXMapTable *table);
+OBJC_EXPORT unsigned NXCountMapTable(NXMapTable *table) OBJC_MAP_AVAILABILITY;
/* current number of data in table */
-OBJC_EXPORT void *NXMapMember(NXMapTable *table, const void *key, void **value);
+OBJC_EXPORT void *NXMapMember(NXMapTable *table, const void *key, void **value) OBJC_MAP_AVAILABILITY;
/* return original table key or NX_MAPNOTAKEY. If key is found, value is set */
-OBJC_EXPORT void *NXMapGet(NXMapTable *table, const void *key);
+OBJC_EXPORT void *NXMapGet(NXMapTable *table, const void *key) OBJC_MAP_AVAILABILITY;
/* return original corresponding value or NULL. When NULL need be stored as value, NXMapMember can be used to test for presence */
-OBJC_EXPORT void *NXMapInsert(NXMapTable *table, const void *key, const void *value);
+OBJC_EXPORT void *NXMapInsert(NXMapTable *table, const void *key, const void *value) OBJC_MAP_AVAILABILITY;
/* override preexisting pair; Return previous value or NULL. */
-OBJC_EXPORT void *NXMapRemove(NXMapTable *table, const void *key);
+OBJC_EXPORT void *NXMapRemove(NXMapTable *table, const void *key) OBJC_MAP_AVAILABILITY;
/* previous value or NULL is returned */
/* Iteration over all elements of a table consists in setting up an iteration state and then to progress until all entries have been visited. An example of use for counting elements in a table is:
}
*/
-typedef struct {int index;} NXMapState;
+typedef struct {int index;} NXMapState OBJC_MAP_AVAILABILITY;
/* callers should not rely on actual contents of the struct */
-OBJC_EXPORT NXMapState NXInitMapState(NXMapTable *table);
+OBJC_EXPORT NXMapState NXInitMapState(NXMapTable *table) OBJC_MAP_AVAILABILITY;
-OBJC_EXPORT int NXNextMapState(NXMapTable *table, NXMapState *state, const void **key, const void **value);
+OBJC_EXPORT int NXNextMapState(NXMapTable *table, NXMapState *state, const void **key, const void **value) OBJC_MAP_AVAILABILITY;
/* returns 0 when all elements have been visited */
/*************** Conveniences ***************/
-OBJC_EXPORT const NXMapTablePrototype NXPtrValueMapPrototype;
+OBJC_EXPORT const NXMapTablePrototype NXPtrValueMapPrototype OBJC_MAP_AVAILABILITY;
/* hashing is pointer/integer hashing;
isEqual is identity;
free is no-op. */
-OBJC_EXPORT const NXMapTablePrototype NXStrValueMapPrototype;
+OBJC_EXPORT const NXMapTablePrototype NXStrValueMapPrototype OBJC_MAP_AVAILABILITY;
/* hashing is string hashing;
isEqual is strcmp;
free is no-op. */
OBJC_EXPORT const NXMapTablePrototype NXObjectMapPrototype OBJC2_UNAVAILABLE;
/* for objects; uses methods: hash, isEqual:, free, all for key. */
+__END_DECLS
+
#endif /* _OBJC_MAPTABLE_H_ */
static unsigned log2u(unsigned x) { return (x<2) ? 0 : log2u(x>>1)+1; };
-static INLINE unsigned exp2m1(unsigned x) { return (1 << x) - 1; };
+static INLINE unsigned exp2u(unsigned x) { return (1 << x); };
+
+static INLINE unsigned xorHash(unsigned hash) {
+ unsigned xored = (hash & 0xffff) ^ (hash >> 16);
+ return ((xored * 65521) + hash);
+}
-/* iff necessary this modulo can be optimized since the nbBuckets is of the form 2**n-1 */
static INLINE unsigned bucketOf(NXMapTable *table, const void *key) {
unsigned hash = (table->prototype->hash)(table, key);
- unsigned xored = (hash & 0xffff) ^ (hash >> 16);
- return ((xored * 65521) + hash) % table->nbBuckets;
+ return hash & table->nbBucketsMinusOne;
}
static INLINE int isEqual(NXMapTable *table, const void *key1, const void *key2) {
}
static INLINE unsigned nextIndex(NXMapTable *table, unsigned index) {
- return (index+1 >= table->nbBuckets) ? 0 : index+1;
+ return (index + 1) & table->nbBucketsMinusOne;
}
static INLINE void *allocBuckets(void *z, unsigned nb) {
(void)NXHashInsert(prototypes, proto);
}
table->prototype = proto; table->count = 0;
- table->nbBuckets = exp2m1(log2u(capacity)+1);
- table->buckets = allocBuckets(z, table->nbBuckets);
+ table->nbBucketsMinusOne = exp2u(log2u(capacity)+1) - 1;
+ table->buckets = allocBuckets(z, table->nbBucketsMinusOne + 1);
return table;
}
void NXResetMapTable(NXMapTable *table) {
MapPair *pairs = table->buckets;
void (*freeProc)(struct _NXMapTable *, void *, void *) = table->prototype->free;
- unsigned index = table->nbBuckets;
+ unsigned index = table->nbBucketsMinusOne + 1;
while (index--) {
if (pairs->key != NX_MAPNOTAKEY) {
freeProc(table, (void *)pairs->key, (void *)pairs->value);
unsigned NXCountMapTable(NXMapTable *table) { return table->count; }
-static int mapSearch = 0;
-static int mapSearchHit = 0;
-static int mapSearchLoop = 0;
-
static INLINE void *_NXMapMember(NXMapTable *table, const void *key, void **value) {
MapPair *pairs = table->buckets;
unsigned index = bucketOf(table, key);
MapPair *pair = pairs + index;
if (pair->key == NX_MAPNOTAKEY) return NX_MAPNOTAKEY;
- mapSearch ++;
if (isEqual(table, pair->key, key)) {
*value = (void *)pair->value;
- mapSearchHit ++;
return (void *)pair->key;
} else {
unsigned index2 = index;
while ((index2 = nextIndex(table, index2)) != index) {
- mapSearchLoop ++;
pair = pairs + index2;
if (pair->key == NX_MAPNOTAKEY) return NX_MAPNOTAKEY;
if (isEqual(table, pair->key, key)) {
return (_NXMapMember(table, key, &value) != NX_MAPNOTAKEY) ? value : NULL;
}
-static int mapRehash = 0;
-static int mapRehashSum = 0;
-
static void _NXMapRehash(NXMapTable *table) {
MapPair *pairs = table->buckets;
MapPair *pair = pairs;
- unsigned index = table->nbBuckets;
+ unsigned numBuckets = table->nbBucketsMinusOne + 1;
+ unsigned index = numBuckets;
unsigned oldCount = table->count;
- table->nbBuckets += table->nbBuckets + 1; /* 2 times + 1 */
+
+ table->nbBucketsMinusOne = 2 * numBuckets - 1;
table->count = 0;
- table->buckets = allocBuckets(malloc_zone_from_ptr(table), table->nbBuckets);
- mapRehash ++;
- mapRehashSum += table->count;
+ table->buckets = allocBuckets(malloc_zone_from_ptr(table), table->nbBucketsMinusOne + 1);
while (index--) {
if (pair->key != NX_MAPNOTAKEY) {
(void)NXMapInsert(table, pair->key, pair->value);
free(pairs);
}
-static int mapInsert = 0;
-static int mapInsertHit = 0;
-static int mapInsertLoop = 0;
-
void *NXMapInsert(NXMapTable *table, const void *key, const void *value) {
MapPair *pairs = table->buckets;
unsigned index = bucketOf(table, key);
_objc_inform("*** NXMapInsert: invalid key: -1\n");
return NULL;
}
- mapInsert ++;
+
+ unsigned numBuckets = table->nbBucketsMinusOne + 1;
+
if (pair->key == NX_MAPNOTAKEY) {
- mapInsertHit ++;
pair->key = key; pair->value = value;
table->count++;
+ if (table->count * 4 > numBuckets * 3) _NXMapRehash(table);
return NULL;
}
+
if (isEqual(table, pair->key, key)) {
const void *old = pair->value;
- mapInsertHit ++;
if (old != value) pair->value = value;/* avoid writing unless needed! */
return (void *)old;
- } else if (table->count == table->nbBuckets) {
+ } else if (table->count == numBuckets) {
/* no room: rehash and retry */
_NXMapRehash(table);
return NXMapInsert(table, key, value);
} else {
unsigned index2 = index;
while ((index2 = nextIndex(table, index2)) != index) {
- mapInsertLoop ++;
pair = pairs + index2;
if (pair->key == NX_MAPNOTAKEY) {
- #if INSERT_TAIL
pair->key = key; pair->value = value;
- #else
- MapPair current = {key, value};
- index2 = index;
- while (current.key != NX_MAPNOTAKEY) {
- MapPair temp;
- pair = pairs + index2;
- temp = *pair;
- *pair = current;
- current = temp;
- index2 = nextIndex(table, index2);
- }
- #endif
table->count++;
- if (table->count * 4 > table->nbBuckets * 3) _NXMapRehash(table);
+ if (table->count * 4 > numBuckets * 3) _NXMapRehash(table);
return NULL;
}
if (isEqual(table, pair->key, key)) {
NXMapState NXInitMapState(NXMapTable *table) {
NXMapState state;
- state.index = table->nbBuckets;
+ state.index = table->nbBucketsMinusOne + 1;
return state;
}
* Like NXMapInsert, but strdups the key if necessary.
* Used to prevent stale pointers when bundles are unloaded.
**********************************************************************/
-__private_extern__ void *NXMapKeyCopyingInsert(NXMapTable *table, const void *key, const void *value)
+PRIVATE_EXTERN void *NXMapKeyCopyingInsert(NXMapTable *table, const void *key, const void *value)
{
void *realKey;
void *realValue = NULL;
* Like NXMapRemove, but frees the existing key if necessary.
* Used to prevent stale pointers when bundles are unloaded.
**********************************************************************/
-__private_extern__ void *NXMapKeyFreeingRemove(NXMapTable *table, const void *key)
+PRIVATE_EXTERN void *NXMapKeyFreeingRemove(NXMapTable *table, const void *key)
{
void *realKey;
void *realValue = NULL;
/**** Conveniences *************************************/
static unsigned _mapPtrHash(NXMapTable *table, const void *key) {
- return (unsigned)((((uintptr_t) key) >> (sizeof(uintptr_t)/2*8)) ^ ((uintptr_t) key));
+#ifdef __LP64__
+ return ((uintptr_t)key) >> 3;
+#else
+ return ((uintptr_t)key) >> 2;
+#endif
}
static unsigned _mapStrHash(NXMapTable *table, const void *key) {
if (*s == '\0') break;
hash ^= *s++ << 24;
}
- return hash;
+ return xorHash(hash);
}
static int _mapPtrIsEqual(NXMapTable *table, const void *key1, const void *key2) {
*
* On some architectures, use objc_msgSend_stret for some struct return types.
* On some architectures, use objc_msgSend_fpret for some float return types.
+ * On some architectures, use objc_msgSend_fp2ret for some float return types.
*
* These functions must be cast to an appropriate function pointer type
* before being called.
*/
-OBJC_EXPORT id objc_msgSend(id self, SEL op, ...);
-OBJC_EXPORT id objc_msgSendSuper(struct objc_super *super, SEL op, ...);
+OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
/* Struct-returning Messaging Primitives
* before being called.
*/
#if defined(__OBJC2__)
-OBJC_EXPORT void objc_msgSend_stret(id self, SEL op, ...);
-OBJC_EXPORT void objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...);
+OBJC_EXPORT void objc_msgSend_stret(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT void objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
#elif defined(__cplusplus)
/* For compatibility with old objc-runtime.h header */
-OBJC_EXPORT id objc_msgSend_stret(id self, SEL op, ...);
-OBJC_EXPORT id objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...);
+OBJC_EXPORT id objc_msgSend_stret(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT id objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
#else
/* For compatibility with old objc-runtime.h header */
-OBJC_EXPORT void objc_msgSend_stret(void * stretAddr, id self, SEL op, ...);
-OBJC_EXPORT void objc_msgSendSuper_stret(void * stretAddr, struct objc_super *super, SEL op, ...);
+OBJC_EXPORT void objc_msgSend_stret(void * stretAddr, id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT void objc_msgSendSuper_stret(void * stretAddr, struct objc_super *super, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
#endif
* on the stack.
* Consult your local function call ABI documentation for details.
*
- * ppc: objc_msgSend_fpret not used
- * ppc64: objc_msgSend_fpret not used
+ * arm: objc_msgSend_fpret not used
* i386: objc_msgSend_fpret used for `float`, `double`, `long double`.
* x86-64: objc_msgSend_fpret used for `long double`.
*
+ * arm: objc_msgSend_fp2ret not used
+ * i386: objc_msgSend_fp2ret not used
+ * x86-64: objc_msgSend_fp2ret used for `_Complex long double`.
+ *
* These functions must be cast to an appropriate function pointer type
* before being called.
*/
#if defined(__i386__)
-OBJC_EXPORT double objc_msgSend_fpret(id self, SEL op, ...);
+
+OBJC_EXPORT double objc_msgSend_fpret(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);
+
/* Use objc_msgSendSuper() for fp-returning messages to super. */
/* See also objc_msgSendv_fpret() below. */
+
#elif defined(__x86_64__)
-OBJC_EXPORT long double objc_msgSend_fpret(id self, SEL op, ...);
+
+OBJC_EXPORT long double objc_msgSend_fpret(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+# if __STDC_VERSION__ >= 199901L
+OBJC_EXPORT _Complex long double objc_msgSend_fp2ret(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+# else
+OBJC_EXPORT void objc_msgSend_fp2ret(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+# endif
+
/* Use objc_msgSendSuper() for fp-returning messages to super. */
/* See also objc_msgSendv_fpret() below. */
+
#endif
* before being called.
*/
OBJC_EXPORT id method_invoke(id receiver, Method m, ...)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void method_invoke_stret(id receiver, Method m, ...)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
/* Message Forwarding Primitives
* but may be compared to other IMP values.
*/
OBJC_EXPORT id _objc_msgForward(id receiver, SEL sel, ...)
- AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT void _objc_msgForward_stret(id receiver, SEL sel, ...)
- AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0);
/* Variable-argument Messaging Primitives
#if !__OBJC2__
-#if defined(__ppc__) || defined(ppc)
-#define marg_prearg_size (13*sizeof(double)+6*sizeof(uintptr_t))
-#else
#define marg_prearg_size 0
-#endif
#define marg_malloc(margs, method) \
do { \
--- /dev/null
+/*
+ * Copyright (c) 2009 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#ifndef _OBJC_ABI_H
+#define _OBJC_ABI_H
+
+/*
+ * WARNING DANGER HAZARD BEWARE EEK
+ *
+ * Everything in this file is for Apple Internal use only.
+ * These will change in arbitrary OS updates and in unpredictable ways.
+ * When your program breaks, you get to keep both pieces.
+ */
+
+/*
+ * objc-abi.h: Declarations for functions used by compiler codegen.
+ */
+
+#include <malloc/malloc.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+#include <objc/message.h>
+
+/* Runtime startup. */
+
+// Old static initializer. Used by old crt1.o and old bug workarounds.
+OBJC_EXPORT void _objcInit(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+
+
+/* Properties */
+
+// Read or write an object property. Not all object properties use these.
+OBJC_EXPORT id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+// Read or write a non-object property. Not all uses are C structs,
+// and not all C struct properties use this.
+OBJC_EXPORT void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+
+/* Classes. */
+#if __OBJC2__
+OBJC_EXPORT IMP _objc_empty_vtable
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+#endif
+OBJC_EXPORT struct objc_cache _objc_empty_cache
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+
+
+/* Messages */
+
+#if __OBJC2__
+// objc_msgSendSuper2() takes the current search class, not its superclass.
+OBJC_EXPORT id objc_msgSendSuper2(struct objc_super *super, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT void objc_msgSendSuper2_stret(struct objc_super *super, SEL op,...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+// objc_msgSend_noarg() may be faster for methods with no additional arguments.
+OBJC_EXPORT id objc_msgSend_noarg(id self, SEL _cmd)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+#endif
+
+#if __OBJC2__
+// Debug messengers. Messengers used by the compiler have a debug flavor that
+// may perform extra sanity checking.
+// Old objc_msgSendSuper() does not have a debug version; this is OBJC2 only.
+// *_fixup() do not have debug versions; use non-fixup only for debug mode.
+OBJC_EXPORT id objc_msgSend_debug(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+OBJC_EXPORT id objc_msgSendSuper2_debug(struct objc_super *super, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+OBJC_EXPORT void objc_msgSend_stret_debug(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+OBJC_EXPORT void objc_msgSendSuper2_stret_debug(struct objc_super *super, SEL op,...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+# if defined(__i386__)
+OBJC_EXPORT double objc_msgSend_fpret_debug(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+# elif defined(__x86_64__)
+OBJC_EXPORT long double objc_msgSend_fpret_debug(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+# if __STDC_VERSION__ >= 199901L
+OBJC_EXPORT _Complex long double objc_msgSend_fp2ret_debug(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+# else
+OBJC_EXPORT void objc_msgSend_fp2ret_debug(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+# endif
+# endif
+
+#endif
+
+#if __OBJC2__ && defined(__x86_64__)
+// objc_msgSend_fixup() is used for vtable-dispatchable call sites.
+OBJC_EXPORT id objc_msgSend_fixup(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+OBJC_EXPORT void objc_msgSend_stret_fixup(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+OBJC_EXPORT id objc_msgSendSuper2_fixup(struct objc_super *super, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+OBJC_EXPORT void objc_msgSendSuper2_stret_fixup(struct objc_super *super, SEL op,...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+OBJC_EXPORT long double objc_msgSend_fpret_fixup(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+# if __STDC_VERSION__ >= 199901L
+OBJC_EXPORT _Complex long double objc_msgSend_fp2ret_fixup(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+# else
+OBJC_EXPORT void objc_msgSend_fp2ret_fixup(id self, SEL op, ...)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+# endif
+#endif
+
+/* C++-compatible exception handling. */
+#if __OBJC2__
+
+// fixme these conflict with C++ compiler's internal definitions
+#if !defined(__cplusplus)
+
+// Vtable for C++ exception typeinfo for Objective-C types.
+OBJC_EXPORT const void *objc_ehtype_vtable[]
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+// C++ exception typeinfo for type `id`.
+OBJC_EXPORT struct objc_typeinfo OBJC_EHTYPE_id
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+#endif
+
+// Exception personality function for Objective-C and Objective-C++ code.
+struct _Unwind_Exception;
+struct _Unwind_Context;
+OBJC_EXPORT int
+__objc_personality_v0(int version,
+ int actions,
+ uint64_t exceptionClass,
+ struct _Unwind_Exception *exceptionObject,
+ struct _Unwind_Context *context)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+#endif
+
+/* ARR */
+
+OBJC_EXPORT id objc_retainBlock(id)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+#endif
#include <AvailabilityMacros.h>
#include <TargetConditionals.h>
+#ifndef __has_feature
+# define __has_feature(x) 0
+#endif
+
/*
* OBJC_API_VERSION 0 or undef: Tiger and earlier API only
* OBJC_API_VERSION 2: Leopard and later API available
# endif
#endif
+/* OBJC_ARC_UNAVAILABLE: unavailable with -fobjc-arc */
+#if !defined(OBJC_ARC_UNAVAILABLE)
+# if __has_feature(objc_arr)
+# define OBJC_ARC_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
+# else
+# define OBJC_ARC_UNAVAILABLE
+# endif
+#endif
+
#if !defined(OBJC_EXTERN)
# if defined(__cplusplus)
# define OBJC_EXTERN extern "C"
# endif
#endif
-#if !defined(OBJC_EXPORT)
+#if !defined(OBJC_VISIBLE)
# if TARGET_OS_WIN32
# if defined(BUILDING_OBJC)
-# define OBJC_EXPORT OBJC_EXTERN __declspec(dllexport)
+# define OBJC_VISIBLE __declspec(dllexport)
# else
-# define OBJC_EXPORT OBJC_EXTERN __declspec(dllimport)
+# define OBJC_VISIBLE __declspec(dllimport)
# endif
# else
-# define OBJC_EXPORT OBJC_EXTERN
+# define OBJC_VISIBLE __attribute__((visibility("default")))
# endif
#endif
+#if !defined(OBJC_EXPORT)
+# define OBJC_EXPORT OBJC_EXTERN OBJC_VISIBLE
+#endif
+
#if !defined(OBJC_IMPORT)
# define OBJC_IMPORT extern
#endif
--- /dev/null
+/*
+ * Copyright (c) 2010-2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "llvm-DenseMap.h"
+
+#import "objc-weak.h"
+#import "objc-private.h"
+#import "objc-internal.h"
+#import "objc-os.h"
+#import "runtime.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+//#include <fcntl.h>
+#include <mach/mach.h>
+#include <mach-o/dyld.h>
+#include <mach-o/nlist.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <libkern/OSAtomic.h>
+#include <Block.h>
+#include <map>
+#include <execinfo.h>
+
+#if SUPPORT_RETURN_AUTORELEASE
+// We cannot peek at where we are returning to unless we always inline this:
+__attribute__((always_inline))
+static bool callerAcceptsFastAutorelease(const void * const ra0);
+#endif
+
+
+/***********************************************************************
+* Weak ivar support
+**********************************************************************/
+
+static bool seen_weak_refs;
+
+@protocol ReferenceCounted
++ (id)alloc;
++ (id)allocWithZone:(malloc_zone_t *)zone;
+- (oneway void)dealloc;
+- (id)retain;
+- (oneway void)release;
+- (id)autorelease;
+- (uintptr_t)retainCount;
+@end
+
+#define ARR_LOGGING 0
+
+#if ARR_LOGGING
+struct {
+ int retains;
+ int releases;
+ int autoreleases;
+ int blockCopies;
+} CompilerGenerated, ExplicitlyCoded;
+
+PRIVATE_EXTERN void (^objc_arr_log)(const char *, id param) =
+ ^(const char *str, id param) { printf("%s %p\n", str, param); };
+#endif
+
+
+namespace {
+
+#if TARGET_OS_EMBEDDED
+# define SIDE_TABLE_STRIPE 1
+#else
+# define SIDE_TABLE_STRIPE 8
+#endif
+
+// should be a multiple of cache line size (64)
+#define SIDE_TABLE_SIZE 64
+
+typedef objc::DenseMap<id,size_t,true> RefcountMap;
+
+class SideTable {
+private:
+ static uint8_t table_buf[SIDE_TABLE_STRIPE * SIDE_TABLE_SIZE];
+
+public:
+ OSSpinLock slock;
+ RefcountMap refcnts;
+ weak_table_t weak_table;
+
+ SideTable() : slock(OS_SPINLOCK_INIT)
+ {
+ memset(&weak_table, 0, sizeof(weak_table));
+ }
+
+ ~SideTable()
+ {
+ // never delete side_table in case other threads retain during exit
+ assert(0);
+ }
+
+ static SideTable *tableForPointer(const void *p)
+ {
+# if SIDE_TABLE_STRIPE == 1
+ return (SideTable *)table_buf;
+# else
+ uintptr_t a = (uintptr_t)p;
+ int index = ((a >> 4) ^ (a >> 9)) & (SIDE_TABLE_STRIPE - 1);
+ return (SideTable *)&table_buf[index * SIDE_TABLE_SIZE];
+# endif
+ }
+
+ static void init() {
+ // use placement new instead of static ctor to avoid dtor at exit
+ for (int i = 0; i < SIDE_TABLE_STRIPE; i++) {
+ new (&table_buf[i * SIDE_TABLE_SIZE]) SideTable;
+ }
+ }
+};
+
+STATIC_ASSERT(sizeof(SideTable) <= SIDE_TABLE_SIZE);
+__attribute__((aligned(SIDE_TABLE_SIZE))) uint8_t
+SideTable::table_buf[SIDE_TABLE_STRIPE * SIDE_TABLE_SIZE];
+
+// Avoid false-negative reports from tools like "leaks"
+#define DISGUISE(x) ((id)~(uintptr_t)(x))
+
+// anonymous namespace
+};
+
+
+//
+// The -fobjc-arr flag causes the compiler to issue calls to objc_{retain/release/autorelease/retain_block}
+//
+
+id objc_retainBlock(id x) {
+#if ARR_LOGGING
+ objc_arr_log("objc_retain_block", x);
+ ++CompilerGenerated.blockCopies;
+#endif
+ return (id)_Block_copy(x);
+}
+
+//
+// The following SHOULD be called by the compiler directly, but the request hasn't been made yet :-)
+//
+
+BOOL objc_should_deallocate(id object) {
+ return YES;
+}
+
+// WORKAROUND:
+// <rdar://problem/9038601> clang remembers variadic bit across function cast
+// <rdar://problem/9048030> Clang thinks that all ObjC vtable dispatches are variadic
+// <rdar://problem/8873428> vararg function defeats tail-call optimization
+id objc_msgSend_hack(id, SEL) asm("_objc_msgSend");
+
+// public API entry points that might be optimized later
+
+__attribute__((aligned(16)))
+id
+objc_retain(id obj)
+{
+ return objc_msgSend_hack(obj, @selector(retain));
+}
+
+__attribute__((aligned(16)))
+void
+objc_release(id obj)
+{
+ objc_msgSend_hack(obj, @selector(release));
+}
+
+__attribute__((aligned(16)))
+id
+objc_autorelease(id obj)
+{
+ return objc_msgSend_hack(obj, @selector(autorelease));
+}
+
+id
+objc_retain_autorelease(id obj)
+{
+ return objc_autorelease(objc_retain(obj));
+}
+
+id
+objc_storeWeak(id *location, id newObj)
+{
+ id oldObj;
+ SideTable *oldTable;
+ SideTable *newTable;
+ OSSpinLock *lock1;
+#if SIDE_TABLE_STRIPE > 1
+ OSSpinLock *lock2;
+#endif
+
+ if (!seen_weak_refs) {
+ seen_weak_refs = true;
+ }
+
+ // Acquire locks for old and new values.
+ // Order by lock address to prevent lock ordering problems.
+ // Retry if the old value changes underneath us.
+ retry:
+ oldObj = *location;
+
+ oldTable = SideTable::tableForPointer(oldObj);
+ newTable = SideTable::tableForPointer(newObj);
+
+ lock1 = &newTable->slock;
+#if SIDE_TABLE_STRIPE > 1
+ lock2 = &oldTable->slock;
+ if (lock1 > lock2) {
+ OSSpinLock *temp = lock1;
+ lock1 = lock2;
+ lock2 = temp;
+ }
+ if (lock1 != lock2) OSSpinLockLock(lock2);
+#endif
+ OSSpinLockLock(lock1);
+
+ if (*location != oldObj) {
+ OSSpinLockUnlock(lock1);
+#if SIDE_TABLE_STRIPE > 1
+ if (lock1 != lock2) OSSpinLockUnlock(lock2);
+#endif
+ goto retry;
+ }
+
+ if (oldObj) {
+ weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
+ }
+ if (newObj) {
+ newObj = weak_register_no_lock(&newTable->weak_table, newObj,location);
+ // weak_register_no_lock returns NULL if weak store should be rejected
+ }
+ // Do not set *location anywhere else. That would introduce a race.
+ *location = newObj;
+
+ OSSpinLockUnlock(lock1);
+#if SIDE_TABLE_STRIPE > 1
+ if (lock1 != lock2) OSSpinLockUnlock(lock2);
+#endif
+
+ return newObj;
+}
+
+id
+objc_loadWeakRetained(id *location)
+{
+ id result;
+
+ SideTable *table;
+ OSSpinLock *lock;
+
+ retry:
+ result = *location;
+ if (!result) return NULL;
+
+ table = SideTable::tableForPointer(result);
+ lock = &table->slock;
+
+ OSSpinLockLock(lock);
+ if (*location != result) {
+ OSSpinLockUnlock(lock);
+ goto retry;
+ }
+
+ result = arr_read_weak_reference(&table->weak_table, location);
+
+ OSSpinLockUnlock(lock);
+ return result;
+}
+
+id
+objc_loadWeak(id *location)
+{
+ return objc_autorelease(objc_loadWeakRetained(location));
+}
+
+id
+objc_initWeak(id *addr, id val)
+{
+ *addr = 0;
+ return objc_storeWeak(addr, val);
+}
+
+void
+objc_destroyWeak(id *addr)
+{
+ objc_storeWeak(addr, 0);
+}
+
+void
+objc_copyWeak(id *to, id *from)
+{
+ id val = objc_loadWeakRetained(from);
+ objc_initWeak(to, val);
+ objc_release(val);
+}
+
+void
+objc_moveWeak(id *to, id *from)
+{
+ objc_copyWeak(to, from);
+ objc_destroyWeak(from);
+}
+
+
+/* Autorelease pool implementation
+ A thread's autorelease pool is a stack of pointers.
+ Each pointer is either an object to release, or POOL_SENTINEL which is
+ an autorelease pool boundary.
+ A pool token is a pointer to the POOL_SENTINEL for that pool. When
+ the pool is popped, every object hotter than the sentinel is released.
+ The stack is divided into a doubly-linked list of pages. Pages are added
+ and deleted as necessary.
+ Thread-local storage points to the hot page, where newly autoreleased
+ objects are stored.
+ */
+
+extern "C" BREAKPOINT_FUNCTION(void objc_autoreleaseNoPool(id obj));
+
+namespace {
+
+struct magic_t {
+ static const uint32_t M0 = 0xA1A1A1A1;
+# define M1 "AUTORELEASE!"
+ static const size_t M1_len = 12;
+ uint32_t m[4];
+
+ magic_t() {
+ assert(M1_len == strlen(M1));
+ assert(M1_len == 3 * sizeof(m[1]));
+
+ m[0] = M0;
+ strncpy((char *)&m[1], M1, M1_len);
+ }
+
+ ~magic_t() {
+ m[0] = m[1] = m[2] = m[3] = 0;
+ }
+
+ bool check() const {
+ return (m[0] == M0 && 0 == strncmp((char *)&m[1], M1, M1_len));
+ }
+
+ bool fastcheck() const {
+#ifdef NDEBUG
+ return (m[0] == M0);
+#else
+ return check();
+#endif
+ }
+
+# undef M1
+};
+
+
+// Set this to 1 to mprotect() autorelease pool contents
+#define PROTECT_AUTORELEASEPOOL 0
+
+class AutoreleasePoolPage
+{
+
+#define POOL_SENTINEL 0
+ static pthread_key_t const key = AUTORELEASE_POOL_KEY;
+ static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
+ static size_t const SIZE =
+#if PROTECT_AUTORELEASEPOOL
+ 4096; // must be multiple of vm page size
+#else
+ 4096; // size and alignment, power of 2
+#endif
+ static size_t const COUNT = SIZE / sizeof(id);
+
+ magic_t const magic;
+ id *next;
+ pthread_t const thread;
+ AutoreleasePoolPage * const parent;
+ AutoreleasePoolPage *child;
+ uint32_t const depth;
+ uint32_t hiwat;
+
+ // SIZE-sizeof(*this) bytes of contents follow
+
+ static void * operator new(size_t size) {
+ return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
+ }
+ static void operator delete(void * p) {
+ return free(p);
+ }
+
+ inline void protect() {
+#if PROTECT_AUTORELEASEPOOL
+ mprotect(this, SIZE, PROT_READ);
+ check();
+#endif
+ }
+
+ inline void unprotect() {
+#if PROTECT_AUTORELEASEPOOL
+ check();
+ mprotect(this, SIZE, PROT_READ | PROT_WRITE);
+#endif
+ }
+
+ AutoreleasePoolPage(AutoreleasePoolPage *newParent)
+ : magic(), next(begin()), thread(pthread_self()),
+ parent(newParent), child(NULL),
+ depth(parent ? 1+parent->depth : 0),
+ hiwat(parent ? parent->hiwat : 0)
+ {
+ if (parent) {
+ parent->check();
+ assert(!parent->child);
+ parent->unprotect();
+ parent->child = this;
+ parent->protect();
+ }
+ protect();
+ }
+
+ ~AutoreleasePoolPage()
+ {
+ check();
+ unprotect();
+ assert(empty());
+
+ // Not recursive: we don't want to blow out the stack
+ // if a thread accumulates a stupendous amount of garbage
+ assert(!child);
+ }
+
+
+ void busted(bool die = true)
+ {
+ (die ? _objc_fatal : _objc_inform)
+ ("autorelease pool page %p corrupted\n"
+ " magic %x %x %x %x\n pthread %p\n",
+ this, magic.m[0], magic.m[1], magic.m[2], magic.m[3],
+ this->thread);
+ }
+
+ void check(bool die = true)
+ {
+ if (!magic.check() || !pthread_equal(thread, pthread_self())) {
+ busted(die);
+ }
+ }
+
+ void fastcheck(bool die = true)
+ {
+ if (! magic.fastcheck()) {
+ busted(die);
+ }
+ }
+
+
+ id * begin() {
+ return (id *) ((uint8_t *)this+sizeof(*this));
+ }
+
+ id * end() {
+ return (id *) ((uint8_t *)this+SIZE);
+ }
+
+ bool empty() {
+ return next == begin();
+ }
+
+ bool full() {
+ return next == end();
+ }
+
+ bool lessThanHalfFull() {
+ return (next - begin() < (end() - begin()) / 2);
+ }
+
+ id *add(id obj)
+ {
+ assert(!full());
+ unprotect();
+ *next++ = obj;
+ protect();
+ return next-1;
+ }
+
+ void releaseAll()
+ {
+ releaseUntil(begin());
+ }
+
+ void releaseUntil(id *stop)
+ {
+ // Not recursive: we don't want to blow out the stack
+ // if a thread accumulates a stupendous amount of garbage
+
+ while (this->next != stop) {
+ // Restart from hotPage() every time, in case -release
+ // autoreleased more objects
+ AutoreleasePoolPage *page = hotPage();
+
+ // fixme I think this `while` can be `if`, but I can't prove it
+ while (page->empty()) {
+ page = page->parent;
+ setHotPage(page);
+ }
+
+ page->unprotect();
+ id obj = *--page->next;
+ memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
+ page->protect();
+
+ if (obj != POOL_SENTINEL) {
+ objc_release(obj);
+ }
+ }
+
+ setHotPage(this);
+
+#ifndef NDEBUG
+ // we expect any children to be completely empty
+ for (AutoreleasePoolPage *page = child; page; page = page->child) {
+ assert(page->empty());
+ }
+#endif
+ }
+
+ void kill()
+ {
+ // Not recursive: we don't want to blow out the stack
+ // if a thread accumulates a stupendous amount of garbage
+ AutoreleasePoolPage *page = this;
+ while (page->child) page = page->child;
+
+ AutoreleasePoolPage *deathptr;
+ do {
+ deathptr = page;
+ page = page->parent;
+ if (page) {
+ page->unprotect();
+ page->child = NULL;
+ page->protect();
+ }
+ delete deathptr;
+ } while (deathptr != this);
+ }
+
+ static void tls_dealloc(void *p)
+ {
+ // reinstate TLS value while we work
+ setHotPage((AutoreleasePoolPage *)p);
+ pop(0);
+ setHotPage(NULL);
+ }
+
+ static AutoreleasePoolPage *pageForPointer(const void *p)
+ {
+ return pageForPointer((uintptr_t)p);
+ }
+
+ static AutoreleasePoolPage *pageForPointer(uintptr_t p)
+ {
+ AutoreleasePoolPage *result;
+ uintptr_t offset = p % SIZE;
+
+ assert(offset >= sizeof(AutoreleasePoolPage));
+
+ result = (AutoreleasePoolPage *)(p - offset);
+ result->fastcheck();
+
+ return result;
+ }
+
+
+ static inline AutoreleasePoolPage *hotPage()
+ {
+ AutoreleasePoolPage *result = (AutoreleasePoolPage *)
+ _pthread_getspecific_direct(key);
+ if (result) result->fastcheck();
+ return result;
+ }
+
+ static inline void setHotPage(AutoreleasePoolPage *page)
+ {
+ if (page) page->fastcheck();
+ _pthread_setspecific_direct(key, (void *)page);
+ }
+
+ static inline AutoreleasePoolPage *coldPage()
+ {
+ AutoreleasePoolPage *result = hotPage();
+ if (result) {
+ while (result->parent) {
+ result = result->parent;
+ result->fastcheck();
+ }
+ }
+ return result;
+ }
+
+
+ static inline id *autoreleaseFast(id obj)
+ {
+ AutoreleasePoolPage *page = hotPage();
+ if (page && !page->full()) {
+ return page->add(obj);
+ } else {
+ return autoreleaseSlow(obj);
+ }
+ }
+
+ static __attribute__((noinline))
+ id *autoreleaseSlow(id obj)
+ {
+ AutoreleasePoolPage *page;
+ page = hotPage();
+
+ // The code below assumes some cases are handled by autoreleaseFast()
+ assert(!page || page->full());
+
+ if (!page) {
+ assert(obj != POOL_SENTINEL);
+ _objc_inform("Object %p of class %s autoreleased "
+ "with no pool in place - just leaking - "
+ "break on objc_autoreleaseNoPool() to debug",
+ obj, object_getClassName(obj));
+ objc_autoreleaseNoPool(obj);
+ return NULL;
+ }
+
+ do {
+ if (page->child) page = page->child;
+ else page = new AutoreleasePoolPage(page);
+ } while (page->full());
+
+ setHotPage(page);
+ return page->add(obj);
+ }
+
+public:
+ static inline id autorelease(id obj)
+ {
+ assert(obj);
+ assert(!OBJC_IS_TAGGED_PTR(obj));
+ id *dest __unused = autoreleaseFast(obj);
+ assert(!dest || *dest == obj);
+ return obj;
+ }
+
+
+ static inline void *push()
+ {
+ if (!hotPage()) {
+ setHotPage(new AutoreleasePoolPage(NULL));
+ }
+ id *dest = autoreleaseFast(POOL_SENTINEL);
+ assert(*dest == POOL_SENTINEL);
+ return dest;
+ }
+
+ static inline void pop(void *token)
+ {
+ AutoreleasePoolPage *page;
+ id *stop;
+
+ if (token) {
+ page = pageForPointer(token);
+ stop = (id *)token;
+ assert(*stop == POOL_SENTINEL);
+ } else {
+ // Token 0 is top-level pool
+ page = coldPage();
+ assert(page);
+ stop = page->begin();
+ }
+
+ if (PrintPoolHiwat) printHiwat();
+
+ page->releaseUntil(stop);
+
+ // memory: delete empty children
+ // hysteresis: keep one empty child if this page is more than half full
+ // special case: delete everything for pop(0)
+ if (!token) {
+ page->kill();
+ setHotPage(NULL);
+ } else if (page->child) {
+ if (page->lessThanHalfFull()) {
+ page->child->kill();
+ }
+ else if (page->child->child) {
+ page->child->child->kill();
+ }
+ }
+ }
+
+ static void init()
+ {
+ int r __unused = pthread_key_init_np(AutoreleasePoolPage::key,
+ AutoreleasePoolPage::tls_dealloc);
+ assert(r == 0);
+ }
+
+ void print()
+ {
+ _objc_inform("[%p] ................ PAGE %s %s %s", this,
+ full() ? "(full)" : "",
+ this == hotPage() ? "(hot)" : "",
+ this == coldPage() ? "(cold)" : "");
+ check(false);
+ for (id *p = begin(); p < next; p++) {
+ if (*p == POOL_SENTINEL) {
+ _objc_inform("[%p] ################ POOL %p", p, p);
+ } else {
+ _objc_inform("[%p] %#16lx %s",
+ p, (unsigned long)*p, object_getClassName(*p));
+ }
+ }
+ }
+
+ static void printAll()
+ {
+ _objc_inform("##############");
+ _objc_inform("AUTORELEASE POOLS for thread %p", pthread_self());
+
+ AutoreleasePoolPage *page;
+ ptrdiff_t objects = 0;
+ for (page = coldPage(); page; page = page->child) {
+ objects += page->next - page->begin();
+ }
+ _objc_inform("%llu releases pending.", (unsigned long long)objects);
+
+ for (page = coldPage(); page; page = page->child) {
+ page->print();
+ }
+
+ _objc_inform("##############");
+ }
+
+ static void printHiwat()
+ {
+ // Check and propagate high water mark
+ // Ignore high water marks under 256 to suppress noise.
+ AutoreleasePoolPage *p = hotPage();
+ uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin());
+ if (mark > p->hiwat && mark > 256) {
+ for( ; p; p = p->parent) {
+ p->unprotect();
+ p->hiwat = mark;
+ p->protect();
+ }
+
+ _objc_inform("POOL HIGHWATER: new high water mark of %u "
+ "pending autoreleases for thread %p:",
+ mark, pthread_self());
+
+ void *stack[128];
+ int count = backtrace(stack, sizeof(stack)/sizeof(stack[0]));
+ char **sym = backtrace_symbols(stack, count);
+ for (int i = 0; i < count; i++) {
+ _objc_inform("POOL HIGHWATER: %s", sym[i]);
+ }
+ free(sym);
+ }
+ }
+
+#undef POOL_SENTINEL
+};
+
+// anonymous namespace
+};
+
+// API to only be called by root classes like NSObject or NSProxy
+
+extern "C" {
+__attribute__((used,noinline,nothrow))
+static id _objc_rootRetain_slow(id obj);
+__attribute__((used,noinline,nothrow))
+static bool _objc_rootReleaseWasZero_slow(id obj);
+};
+
+id
+_objc_rootRetain_slow(id obj)
+{
+ SideTable *table = SideTable::tableForPointer(obj);
+ OSSpinLockLock(&table->slock);
+ table->refcnts[DISGUISE(obj)] += 2;
+ OSSpinLockUnlock(&table->slock);
+
+ return obj;
+}
+
+id
+_objc_rootRetain(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ if (OBJC_IS_TAGGED_PTR(obj)) return obj;
+
+ SideTable *table = SideTable::tableForPointer(obj);
+
+ if (OSSpinLockTry(&table->slock)) {
+ table->refcnts[DISGUISE(obj)] += 2;
+ OSSpinLockUnlock(&table->slock);
+ return obj;
+ }
+ return _objc_rootRetain_slow(obj);
+}
+
+bool
+_objc_rootTryRetain(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ if (OBJC_IS_TAGGED_PTR(obj)) return true;
+
+ SideTable *table = SideTable::tableForPointer(obj);
+
+ // NO SPINLOCK HERE
+ // _objc_rootTryRetain() is called exclusively by _objc_loadWeak(),
+ // which already acquired the lock on our behalf.
+ if (table->slock == 0) {
+ _objc_fatal("Do not call -_tryRetain.");
+ }
+
+ bool result = true;
+ RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
+ if (it == table->refcnts.end()) {
+ table->refcnts[DISGUISE(obj)] = 2;
+ } else if (it->second & 1) {
+ result = false;
+ } else {
+ it->second += 2;
+ }
+
+ return result;
+}
+
+bool
+_objc_rootIsDeallocating(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ if (OBJC_IS_TAGGED_PTR(obj)) return false;
+
+ SideTable *table = SideTable::tableForPointer(obj);
+
+ // NO SPINLOCK HERE
+ // _objc_rootIsDeallocating() is called exclusively by _objc_storeWeak(),
+ // which already acquired the lock on our behalf.
+ if (table->slock == 0) {
+ _objc_fatal("Do not call -_isDeallocating.");
+ }
+
+ RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
+ return (it != table->refcnts.end()) && ((it->second & 1) == 1);
+}
+
+
+void
+objc_clear_deallocating(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ SideTable *table = SideTable::tableForPointer(obj);
+
+ // clear any weak table items
+ // clear extra retain count and deallocating bit
+ // (fixme warn or abort if extra retain count == 0 ?)
+ OSSpinLockLock(&table->slock);
+ if (seen_weak_refs) {
+ arr_clear_deallocating(&table->weak_table, obj);
+ }
+ table->refcnts.erase(DISGUISE(obj));
+ OSSpinLockUnlock(&table->slock);
+}
+
+
+bool
+_objc_rootReleaseWasZero_slow(id obj)
+{
+ SideTable *table = SideTable::tableForPointer(obj);
+
+ bool do_dealloc = false;
+
+ OSSpinLockLock(&table->slock);
+ RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
+ if (it == table->refcnts.end()) {
+ do_dealloc = true;
+ table->refcnts[DISGUISE(obj)] = 1;
+ } else if (it->second == 0) {
+ do_dealloc = true;
+ it->second = 1;
+ } else {
+ it->second -= 2;
+ }
+ OSSpinLockUnlock(&table->slock);
+ return do_dealloc;
+}
+
+bool
+_objc_rootReleaseWasZero(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ if (OBJC_IS_TAGGED_PTR(obj)) return false;
+
+ SideTable *table = SideTable::tableForPointer(obj);
+
+ bool do_dealloc = false;
+
+ if (OSSpinLockTry(&table->slock)) {
+ RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
+ if (it == table->refcnts.end()) {
+ do_dealloc = true;
+ table->refcnts[DISGUISE(obj)] = 1;
+ } else if (it->second == 0) {
+ do_dealloc = true;
+ it->second = 1;
+ } else {
+ it->second -= 2;
+ }
+ OSSpinLockUnlock(&table->slock);
+ return do_dealloc;
+ }
+ return _objc_rootReleaseWasZero_slow(obj);
+}
+
+void
+_objc_rootRelease(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ if (_objc_rootReleaseWasZero(obj) == false) {
+ return;
+ }
+ objc_msgSend_hack(obj, @selector(dealloc));
+}
+
+__attribute__((noinline,used))
+static id _objc_rootAutorelease2(id obj)
+{
+ if (OBJC_IS_TAGGED_PTR(obj)) return obj;
+ return AutoreleasePoolPage::autorelease(obj);
+}
+
+__attribute__((aligned(16)))
+id
+_objc_rootAutorelease(id obj)
+{
+ assert(obj); // root classes shouldn't get here, since objc_msgSend ignores nil
+ assert(!UseGC);
+
+ if (UseGC) {
+ return obj;
+ }
+
+ // no tag check here: tagged pointers DO use fast autoreleasing
+
+#if SUPPORT_RETURN_AUTORELEASE
+ assert(_pthread_getspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY) == NULL);
+
+ if (callerAcceptsFastAutorelease(__builtin_return_address(0))) {
+ _pthread_setspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY, obj);
+ return obj;
+ }
+#endif
+ return _objc_rootAutorelease2(obj);
+}
+
+uintptr_t
+_objc_rootRetainCount(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ // XXX -- There is no way that anybody can use this API race free in a
+ // threaded environment because the result is immediately stale by the
+ // time the caller receives it.
+
+ if (OBJC_IS_TAGGED_PTR(obj)) return (uintptr_t)obj;
+
+ SideTable *table = SideTable::tableForPointer(obj);
+
+ size_t refcnt_result = 1;
+
+ OSSpinLockLock(&table->slock);
+ RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
+ if (it != table->refcnts.end()) {
+ refcnt_result = (it->second >> 1) + 1;
+ }
+ OSSpinLockUnlock(&table->slock);
+ return refcnt_result;
+}
+
+id
+_objc_rootInit(id obj)
+{
+ // In practice, it will be hard to rely on this function.
+ // Many classes do not properly chain -init calls.
+ return obj;
+}
+
+id
+_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
+{
+#if __OBJC2__
+ // allocWithZone under __OBJC2__ ignores the zone parameter
+ (void)zone;
+ return class_createInstance(cls, 0);
+#else
+ if (!zone || UseGC) {
+ return class_createInstance(cls, 0);
+ }
+ return class_createInstanceFromZone(cls, 0, zone);
+#endif
+}
+
+id
+_objc_rootAlloc(Class cls)
+{
+#if 0
+ // once we get a bit in the class, data structure, we can call this directly
+ // because allocWithZone under __OBJC2__ ignores the zone parameter
+ return class_createInstance(cls, 0);
+#else
+ return [cls allocWithZone: nil];
+#endif
+}
+
+void
+_objc_rootDealloc(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ if (OBJC_IS_TAGGED_PTR(obj)) return;
+
+ object_dispose(obj);
+}
+
+void
+_objc_rootFinalize(id obj __unused)
+{
+ assert(obj);
+ assert(UseGC);
+
+ if (UseGC) {
+ return;
+ }
+ _objc_fatal("_objc_rootFinalize called with garbage collection off");
+}
+
+malloc_zone_t *
+_objc_rootZone(id obj)
+{
+ (void)obj;
+ if (gc_zone) {
+ return gc_zone;
+ }
+#if __OBJC2__
+ // allocWithZone under __OBJC2__ ignores the zone parameter
+ return malloc_default_zone();
+#else
+ malloc_zone_t *rval = malloc_zone_from_ptr(obj);
+ return rval ? rval : malloc_default_zone();
+#endif
+}
+
+uintptr_t
+_objc_rootHash(id obj)
+{
+ if (UseGC) {
+ return _object_getExternalHash(obj);
+ }
+ return (uintptr_t)obj;
+}
+
+// make CF link for now
+void *_objc_autoreleasePoolPush(void) { return objc_autoreleasePoolPush(); }
+void _objc_autoreleasePoolPop(void *ctxt) { objc_autoreleasePoolPop(ctxt); }
+
+void *
+objc_autoreleasePoolPush(void)
+{
+ if (UseGC) return NULL;
+ return AutoreleasePoolPage::push();
+}
+
+void
+objc_autoreleasePoolPop(void *ctxt)
+{
+ if (UseGC) return;
+
+ // fixme rdar://9167170
+ if (!ctxt) return;
+
+ AutoreleasePoolPage::pop(ctxt);
+}
+
+void
+_objc_autoreleasePoolPrint(void)
+{
+ if (UseGC) return;
+ AutoreleasePoolPage::printAll();
+}
+
+#if SUPPORT_RETURN_AUTORELEASE
+
+/*
+ Fast handling of returned autoreleased values.
+ The caller and callee cooperate to keep the returned object
+ out of the autorelease pool.
+
+ Caller:
+ ret = callee();
+ objc_retainAutoreleasedReturnValue(ret);
+ // use ret here
+
+ Callee:
+ // compute ret
+ [ret retain];
+ return objc_autoreleaseReturnValue(ret);
+
+ objc_autoreleaseReturnValue() examines the caller's instructions following
+ the return. If the caller's instructions immediately call
+ objc_autoreleaseReturnValue, then the callee omits the -autorelease and saves
+ the result in thread-local storage. If the caller does not look like it
+ cooperates, then the callee calls -autorelease as usual.
+
+ objc_autoreleaseReturnValue checks if the returned value is the same as the
+ one in thread-local storage. If it is, the value is used directly. If not,
+ the value is assumed to be truly autoreleased and is retained again. In
+ either case, the caller now has a retained reference to the value.
+
+ Tagged pointer objects do participate in the fast autorelease scheme,
+ because it saves message sends. They are not entered in the autorelease
+ pool in the slow case.
+*/
+
+# if __x86_64__
+
+static bool callerAcceptsFastAutorelease(const void * const ra0)
+{
+ const uint8_t *ra1 = (const uint8_t *)ra0;
+ const uint16_t *ra2;
+ const uint32_t *ra4 = (const uint32_t *)ra1;
+ const void **sym;
+
+#define PREFER_GOTPCREL 0
+#if PREFER_GOTPCREL
+ // 48 89 c7 movq %rax,%rdi
+ // ff 15 callq *symbol@GOTPCREL(%rip)
+ if (*ra4 != 0xffc78948) {
+ return false;
+ }
+ if (ra1[4] != 0x15) {
+ return false;
+ }
+ ra1 += 3;
+#else
+ // 48 89 c7 movq %rax,%rdi
+ // e8 callq symbol
+ if (*ra4 != 0xe8c78948) {
+ return false;
+ }
+ ra1 += (long)*(const int32_t *)(ra1 + 4) + 8l;
+ ra2 = (const uint16_t *)ra1;
+ // ff 25 jmpq *symbol@DYLDMAGIC(%rip)
+ if (*ra2 != 0x25ff) {
+ return false;
+ }
+#endif
+ ra1 += 6l + (long)*(const int32_t *)(ra1 + 2);
+ sym = (const void **)ra1;
+ if (*sym != objc_retainAutoreleasedReturnValue)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// __x86_64__
+# else
+
+#warning unknown architecture
+
+static bool callerAcceptsFastAutorelease(const void *ra)
+{
+ return false;
+}
+
+# endif
+
+// SUPPORT_RETURN_AUTORELEASE
+#endif
+
+
+id
+objc_autoreleaseReturnValue(id obj)
+{
+#if SUPPORT_RETURN_AUTORELEASE
+ assert(_pthread_getspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY) == NULL);
+
+ if (callerAcceptsFastAutorelease(__builtin_return_address(0))) {
+ _pthread_setspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY, obj);
+ return obj;
+ }
+#endif
+
+ return objc_autorelease(obj);
+}
+
+id
+objc_retainAutoreleaseReturnValue(id obj)
+{
+ return objc_autoreleaseReturnValue(objc_retain(obj));
+}
+
+id
+objc_retainAutoreleasedReturnValue(id obj)
+{
+#if SUPPORT_RETURN_AUTORELEASE
+ if (obj == _pthread_getspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY)) {
+ _pthread_setspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY, 0);
+ return obj;
+ }
+#endif
+ return objc_retain(obj);
+}
+
+void
+objc_storeStrong(id *location, id obj)
+{
+ // XXX FIXME -- GC support?
+ id prev = *location;
+ if (obj == prev) {
+ return;
+ }
+ objc_retain(obj);
+ *location = obj;
+ objc_release(prev);
+}
+
+id
+objc_retainAutorelease(id obj)
+{
+ return objc_autorelease(objc_retain(obj));
+}
+
+void
+_objc_deallocOnMainThreadHelper(void *context)
+{
+ id obj = (id)context;
+ objc_msgSend_hack(obj, @selector(dealloc));
+}
+
+// convert objc_objectptr_t to id, callee must take ownership.
+NS_RETURNS_RETAINED id objc_retainedObject(objc_objectptr_t CF_CONSUMED pointer) { return (id)pointer; }
+
+// convert objc_objectptr_t to id, without ownership transfer.
+NS_RETURNS_NOT_RETAINED id objc_unretainedObject(objc_objectptr_t pointer) { return (id)pointer; }
+
+// convert id to objc_objectptr_t, no ownership transfer.
+CF_RETURNS_NOT_RETAINED objc_objectptr_t objc_unretainedPointer(id object) { return object; }
+
+
+PRIVATE_EXTERN void arr_init(void)
+{
+ AutoreleasePoolPage::init();
+ SideTable::init();
+}
* @APPLE_LICENSE_HEADER_END@
*/
+#import "objc-auto.h"
+
+#ifndef OBJC_NO_GC
+
#import <auto_zone.h>
#import <objc/objc.h>
#import <objc/runtime.h>
/*
Quickly dump heap to a named file in a pretty raw format.
*/
-__private_extern__ BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename) {
+PRIVATE_EXTERN BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename) {
// just write interesting info to disk
int fd = secure_open(filename, O_WRONLY|O_CREAT, geteuid());
if (fd < 0) return NO;
// for each thread...
// do registers first
- void (^dump_registers)(const void *, unsigned long) = ^(const void *base, unsigned long byte_size) {
+ auto_zone_register_dump dump_registers = ^(const void *base, unsigned long byte_size) {
char type = REGISTER;
fwrite(&type, 1, 1, fp);
//fwrite(REGISTER, strlen(REGISTER), 1, fp);
};
// then stacks
- void (^dump_stack)(const void *, unsigned long) = ^(const void *base, unsigned long byte_size) {
+ auto_zone_stack_dump dump_stack = ^(const void *base, unsigned long byte_size) {
char type = THREAD;
fwrite(&type, 1, 1, fp);
//fwrite(THREAD, strlen(THREAD), 1, fp);
// roots
- void (^dump_root)(const void **) = ^(const void **address) {
+ auto_zone_root_dump dump_root = ^(const void **address) {
char type = ROOT;
fwrite(&type, 1, 1, fp);
// write the address so that we can catch misregistered globals
// the nodes
pointer_set_t *classes = new_pointer_set();
- void (^dump_node)(const void *, unsigned long, unsigned int, unsigned long) =
- ^(const void *address, unsigned long size, unsigned int layout, unsigned long refcount) {
- char type = NODE;
- fwrite(&type, 1, 1, fp);
- fwrite(&address, sizeof(address), 1, fp);
- fwrite(&size, sizeof(size), 1, fp);
- fwrite(&layout, sizeof(layout), 1, fp);
- fwrite(&refcount, sizeof(refcount), 1, fp);
- if ((layout & AUTO_UNSCANNED) != AUTO_UNSCANNED) {
- // now the nodes unfiltered content
- fwrite(address, size, 1, fp);
- }
- if ((layout & AUTO_OBJECT) == AUTO_OBJECT) {
- long theClass = *(long *)address;
- if (theClass) pointer_set_add(classes, theClass);
- }
+ auto_zone_node_dump dump_node = ^(const void *address, unsigned long size, unsigned int layout, unsigned long refcount) {
+ char type = NODE;
+ fwrite(&type, 1, 1, fp);
+ fwrite(&address, sizeof(address), 1, fp);
+ fwrite(&size, sizeof(size), 1, fp);
+ fwrite(&layout, sizeof(layout), 1, fp);
+ fwrite(&refcount, sizeof(refcount), 1, fp);
+ if ((layout & AUTO_UNSCANNED) != AUTO_UNSCANNED) {
+ // now the nodes unfiltered content
+ fwrite(address, size, 1, fp);
+ }
+ if ((layout & AUTO_OBJECT) == AUTO_OBJECT) {
+ long theClass = *(long *)address;
+ if (theClass) pointer_set_add(classes, theClass);
+ }
};
// weak
- void (^dump_weak)(const void **, const void *) = ^(const void **address, const void *item) {
+ auto_zone_weak_dump dump_weak = ^(const void **address, const void *item) {
char type = WEAK;
fwrite(&type, 1, 1, fp);
fwrite(&address, sizeof(address), 1, fp);
fwrite(&length, sizeof(length), 1, fp); // n
fwrite(className, length, 1, fp); // n bytes
// strong layout
- const char *layout = class_getIvarLayout((Class)class);
- length = layout ? (int)strlen(layout)+1 : 0; // format is <skipnibble><count nibble> ending with <0><0>
+ const uint8_t *layout = class_getIvarLayout((Class)class);
+ length = layout ? (int)strlen((char *)layout)+1 : 0; // format is <skipnibble><count nibble> ending with <0><0>
fwrite(&length, sizeof(length), 1, fp); // n
fwrite(layout, length, 1, fp); // n bytes
// weak layout
layout = class_getWeakIvarLayout((Class)class);
- length = layout ? (int)strlen(layout)+1 : 0; // format is <skipnibble><count nibble> ending with <0><0>
+ length = layout ? (int)strlen((char *)layout)+1 : 0; // format is <skipnibble><count nibble> ending with <0><0>
fwrite(&length, sizeof(length), 1, fp); // n
fwrite(layout, length, 1, fp); // n bytes
});
}
return YES;
}
+
+#endif
#define _OBJC_AUTO_H_
#include <objc/objc.h>
+#include <malloc/malloc.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
/* GC is unsupported on some architectures. */
-#if TARGET_OS_EMBEDDED || TARGET_OS_WIN32
+#if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32
# define OBJC_NO_GC 1
#endif
-#ifdef OBJC_NO_GC
-# define OBJC_GC_EXPORT static
-#else
-# define OBJC_GC_EXPORT OBJC_EXPORT
-#endif
-
/* objc_collect() options */
enum {
OBJC_CLEAR_RESIDENT_STACK = (1 << 0)
};
+#ifndef OBJC_NO_GC
-/* Collection utilities */
-OBJC_GC_EXPORT void objc_collect(unsigned long options);
-OBJC_GC_EXPORT BOOL objc_collectingEnabled(void);
+/* GC declarations */
+/* Collection utilities */
+
+OBJC_EXPORT void objc_collect(unsigned long options)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
+OBJC_EXPORT BOOL objc_collectingEnabled(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+OBJC_EXPORT malloc_zone_t *objc_collectableZone(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);
/* GC configuration */
/* Tells collector to wait until specified bytes have been allocated before trying to collect again. */
-OBJC_GC_EXPORT void objc_setCollectionThreshold(size_t threshold);
+OBJC_EXPORT void objc_setCollectionThreshold(size_t threshold)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
/* Tells collector to run a full collection for every ratio generational collections. */
-OBJC_GC_EXPORT void objc_setCollectionRatio(size_t ratio);
+OBJC_EXPORT void objc_setCollectionRatio(size_t ratio)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
-/* Tells collector to start collecting on dedicated thread */
-OBJC_GC_EXPORT void objc_startCollectorThread(void);
+//
+// GC-safe compare-and-swap
+//
/* Atomic update, with write barrier. */
-OBJC_GC_EXPORT BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
+OBJC_EXPORT BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA) OBJC_ARC_UNAVAILABLE;
/* "Barrier" version also includes memory barrier. */
-OBJC_GC_EXPORT BOOL objc_atomicCompareAndSwapPtrBarrier(id predicate, id replacement, volatile id *objectLocation) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
+OBJC_EXPORT BOOL objc_atomicCompareAndSwapPtrBarrier(id predicate, id replacement, volatile id *objectLocation)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA) OBJC_ARC_UNAVAILABLE;
// atomic update of a global variable
-OBJC_GC_EXPORT BOOL objc_atomicCompareAndSwapGlobal(id predicate, id replacement, volatile id *objectLocation);
-OBJC_GC_EXPORT BOOL objc_atomicCompareAndSwapGlobalBarrier(id predicate, id replacement, volatile id *objectLocation);
+OBJC_EXPORT BOOL objc_atomicCompareAndSwapGlobal(id predicate, id replacement, volatile id *objectLocation)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA) OBJC_ARC_UNAVAILABLE;
+OBJC_EXPORT BOOL objc_atomicCompareAndSwapGlobalBarrier(id predicate, id replacement, volatile id *objectLocation)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA) OBJC_ARC_UNAVAILABLE;
// atomic update of an instance variable
-OBJC_GC_EXPORT BOOL objc_atomicCompareAndSwapInstanceVariable(id predicate, id replacement, volatile id *objectLocation);
-OBJC_GC_EXPORT BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, id replacement, volatile id *objectLocation);
+OBJC_EXPORT BOOL objc_atomicCompareAndSwapInstanceVariable(id predicate, id replacement, volatile id *objectLocation)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA) OBJC_ARC_UNAVAILABLE;
+OBJC_EXPORT BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, id replacement, volatile id *objectLocation)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA) OBJC_ARC_UNAVAILABLE;
-/* Write barriers. Used by the compiler. */
-OBJC_GC_EXPORT id objc_assign_strongCast(id val, id *dest);
-OBJC_GC_EXPORT id objc_assign_global(id val, id *dest);
-OBJC_GC_EXPORT id objc_assign_ivar(id value, id dest, ptrdiff_t offset);
-OBJC_GC_EXPORT void *objc_memmove_collectable(void *dst, const void *src, size_t size);
+//
+// Read and write barriers
+//
-OBJC_GC_EXPORT id objc_read_weak(id *location);
-OBJC_GC_EXPORT id objc_assign_weak(id value, id *location);
+OBJC_EXPORT id objc_assign_strongCast(id val, id *dest)
+ __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
+OBJC_EXPORT id objc_assign_global(id val, id *dest)
+ __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
+OBJC_EXPORT id objc_assign_threadlocal(id val, id *dest)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);
+OBJC_EXPORT id objc_assign_ivar(id value, id dest, ptrdiff_t offset)
+ __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
+OBJC_EXPORT void *objc_memmove_collectable(void *dst, const void *src, size_t size)
+ __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
+
+OBJC_EXPORT id objc_read_weak(id *location)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+OBJC_EXPORT id objc_assign_weak(id value, id *location)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
//
-// SPI. The following routines are available as debugging and transitional aides.
-//
+// Thread management
+//
-/* Tells runtime to issue finalize calls on the main thread only. */
-OBJC_GC_EXPORT void objc_finalizeOnMainThread(Class cls)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER_BUT_DEPRECATED;
+/* Register the calling thread with the garbage collector. */
+OBJC_EXPORT void objc_registerThreadWithCollector(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
+/* Unregisters the calling thread with the garbage collector.
+ Unregistration also happens automatically at thread exit. */
+OBJC_EXPORT void objc_unregisterThreadWithCollector(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
-/* Returns true if object has been scheduled for finalization. Can be used to avoid operations that may lead to resurrection, which are fatal. */
-OBJC_GC_EXPORT BOOL objc_is_finalized(void *ptr);
+/* To be called from code which must only execute on a registered thread. */
+/* If the calling thread is unregistered then an error message is emitted and the thread is implicitly registered. */
+OBJC_EXPORT void objc_assertRegisteredThreadWithCollector(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
+
+/* Erases any stale references in unused parts of the stack. */
+OBJC_EXPORT void objc_clear_stack(unsigned long options)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
-/* Stack management */
-OBJC_GC_EXPORT void objc_clear_stack(unsigned long options);
//
-// Obsolete. Present only until all uses can be migrated to newer API.
-//
+// Finalization
+//
-OBJC_GC_EXPORT BOOL objc_collecting_enabled(void);
-OBJC_GC_EXPORT void objc_set_collection_threshold(size_t threshold);
-OBJC_GC_EXPORT void objc_set_collection_ratio(size_t ratio);
-OBJC_GC_EXPORT void objc_start_collector_thread(void);
+/* Returns true if object has been scheduled for finalization. Can be used to avoid operations that may lead to resurrection, which are fatal. */
+OBJC_EXPORT BOOL objc_is_finalized(void *ptr)
+ __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
-/* Memory management */
-OBJC_EXPORT id objc_allocate_object(Class cls, int extra);
+// Deprcated. Tells runtime to issue finalize calls on the main thread only.
+OBJC_EXPORT void objc_finalizeOnMainThread(Class cls)
+ AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER_BUT_DEPRECATED;
-/* Register/unregister the current thread with the garbage collector. */
-OBJC_GC_EXPORT void objc_registerThreadWithCollector();
-OBJC_GC_EXPORT void objc_unregisterThreadWithCollector();
-/* To be called from code which must only execute on a registered thread. */
-/* If the calling thread is unregistered then an error message is emitted and the thread is implicitly registered. */
-OBJC_GC_EXPORT void objc_assertRegisteredThreadWithCollector();
+//
+// Deprecated names.
+//
+
+/* Deprecated. Use objc_collectingEnabled() instead. */
+OBJC_EXPORT BOOL objc_collecting_enabled(void)
+ __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4,__MAC_10_5, __IPHONE_NA,__IPHONE_NA);
+/* Deprecated. Use objc_setCollectionThreshold() instead. */
+OBJC_EXPORT void objc_set_collection_threshold(size_t threshold)
+ __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4,__MAC_10_5, __IPHONE_NA,__IPHONE_NA);
+/* Deprecated. Use objc_setCollectionRatio() instead. */
+OBJC_EXPORT void objc_set_collection_ratio(size_t ratio)
+ __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4,__MAC_10_5, __IPHONE_NA,__IPHONE_NA);
+/* Deprecated. Use objc_startCollectorThread() instead. */
+OBJC_EXPORT void objc_start_collector_thread(void)
+ __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4,__MAC_10_5, __IPHONE_NA,__IPHONE_NA);
+/* Deprecated. No replacement. Formerly told the collector to run using a dedicated background thread. */
+OBJC_EXPORT void objc_startCollectorThread(void)
+__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_5,__MAC_10_7, __IPHONE_NA,__IPHONE_NA);
+
+
+/* Deprecated. Use class_createInstance() instead. */
+OBJC_EXPORT id objc_allocate_object(Class cls, int extra)
+ AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER_BUT_DEPRECATED;
+
+
+/* !defined(OBJC_NO_GC) */
+#else
+/* defined(OBJC_NO_GC) */
-#ifdef OBJC_NO_GC
-/* Non-GC versions */
+/* Non-GC declarations */
-static OBJC_INLINE void objc_collect(unsigned long options) { }
+static OBJC_INLINE void objc_collect(unsigned long options __unused) { }
static OBJC_INLINE BOOL objc_collectingEnabled(void) { return NO; }
-static OBJC_INLINE void objc_setCollectionThreshold(size_t threshold) { }
-static OBJC_INLINE void objc_setCollectionRatio(size_t ratio) { }
+static OBJC_INLINE void objc_setCollectionThreshold(size_t threshold __unused) { }
+static OBJC_INLINE void objc_setCollectionRatio(size_t ratio __unused) { }
static OBJC_INLINE void objc_startCollectorThread(void) { }
#if TARGET_OS_WIN32
static OBJC_INLINE id objc_assign_weak(id value, id *location)
{ return (*location = value); }
-
-static OBJC_INLINE void objc_finalizeOnMainThread(Class cls) { }
-static OBJC_INLINE BOOL objc_is_finalized(void *ptr) { return NO; }
-static OBJC_INLINE void objc_clear_stack(unsigned long options) { }
+static OBJC_INLINE void objc_finalizeOnMainThread(Class cls __unused) { }
+static OBJC_INLINE BOOL objc_is_finalized(void *ptr __unused) { return NO; }
+static OBJC_INLINE void objc_clear_stack(unsigned long options __unused) { }
static OBJC_INLINE BOOL objc_collecting_enabled(void) { return NO; }
-static OBJC_INLINE void objc_set_collection_threshold(size_t threshold) { }
-static OBJC_INLINE void objc_set_collection_ratio(size_t ratio) { }
+static OBJC_INLINE void objc_set_collection_threshold(size_t threshold __unused) { }
+static OBJC_INLINE void objc_set_collection_ratio(size_t ratio __unused) { }
static OBJC_INLINE void objc_start_collector_thread(void) { }
-OBJC_EXPORT id class_createInstance(Class cls, size_t extraBytes);
+OBJC_EXPORT id class_createInstance(Class cls, size_t extraBytes)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
static OBJC_INLINE id objc_allocate_object(Class cls, int extra)
{ return class_createInstance(cls, extra); }
static OBJC_INLINE void objc_unregisterThreadWithCollector() { }
static OBJC_INLINE void objc_assertRegisteredThreadWithCollector() { }
+/* defined(OBJC_NO_GC) */
#endif
+
+
+#if TARGET_OS_EMBEDDED
+enum {
+ OBJC_GENERATIONAL = (1 << 0)
+};
+static OBJC_INLINE void objc_collect_if_needed(unsigned long options) __attribute__((deprecated));
+static OBJC_INLINE void objc_collect_if_needed(unsigned long options __unused) { }
+#endif
+
#endif
* @APPLE_LICENSE_HEADER_END@
*/
+#import "objc-config.h"
#import "objc-auto.h"
+#import "objc-accessors.h"
#ifndef OBJC_NO_GC
#import <dlfcn.h>
#import <mach/mach.h>
#import <mach-o/dyld.h>
+#import <mach-o/nlist.h>
#import <sys/types.h>
#import <sys/mman.h>
#import <libkern/OSAtomic.h>
#import <Block_private.h>
#include <dispatch/dispatch.h>
-#define OLD 1
#import "objc-private.h"
#import "objc-references.h"
-#import "objc-rtp.h"
#import "maptable.h"
#import "message.h"
#import "objc-gdb.h"
+#if !defined(NDEBUG) && !__OBJC2__
+#import "objc-exception.h"
+#endif
-static auto_zone_t *gc_zone_init(void);
+static auto_zone_t *gc_zone_init(BOOL wantsCompaction);
static void gc_block_init(void);
static void registeredClassTableInit(void);
+static BOOL objc_isRegisteredClass(Class candidate);
-
-__private_extern__ BOOL UseGC NOBSS = NO;
-static BOOL MultiThreadedGC = NO;
+PRIVATE_EXTERN BOOL UseGC = NO;
+PRIVATE_EXTERN BOOL UseCompaction = NO;
static BOOL WantsMainThreadFinalization = NO;
-__private_extern__ auto_zone_t *gc_zone = NULL;
+PRIVATE_EXTERN auto_zone_t *gc_zone = NULL;
// Pointer magic to make dyld happy. See notes in objc-private.h
-__private_extern__ id (*objc_assign_ivar_internal)(id, id, ptrdiff_t) = objc_assign_ivar;
+PRIVATE_EXTERN id (*objc_assign_ivar_internal)(id, id, ptrdiff_t) = objc_assign_ivar;
/* Method prototypes */
@end
+/***********************************************************************
+* Break-on-error functions
+**********************************************************************/
+
+BREAKPOINT_FUNCTION(
+ void objc_assign_ivar_error(id base, ptrdiff_t offset)
+);
+
+BREAKPOINT_FUNCTION(
+ void objc_assign_global_error(id value, id *slot)
+);
+
+BREAKPOINT_FUNCTION(
+ void objc_exception_during_finalize_error(void)
+);
+
/***********************************************************************
* Utility exports
* Called by various libraries.
void objc_startCollectorThread(void) {
- static int didOnce = 0;
- if (!UseGC) return;
- if (!didOnce) {
- didOnce = 1;
-
- // initialize the batch finalization queue
- MainThreadWorkQ.head = NULL;
- MainThreadWorkQ.tail = NULL;
- pthread_mutex_init(&MainThreadWorkQ.mutex, NULL);
- pthread_cond_init(&MainThreadWorkQ.condition, NULL);
- auto_collect_multithreaded(gc_zone);
- MultiThreadedGC = YES;
- }
}
void objc_start_collector_thread(void) {
- objc_startCollectorThread();
}
static void batchFinalizeOnMainThread(void);
void objc_collect(unsigned long options) {
if (!UseGC) return;
BOOL onMainThread = pthread_main_np() ? YES : NO;
-
- if (MultiThreadedGC || onMainThread) {
- // while we're here, sneak off and do some finalization work (if any)
- if (MultiThreadedGC && onMainThread) batchFinalizeOnMainThread();
- // now on with our normally scheduled programming
- auto_collection_mode_t amode = AUTO_COLLECT_RATIO_COLLECTION;
+
+ // while we're here, sneak off and do some finalization work (if any)
+ if (onMainThread) batchFinalizeOnMainThread();
+ // now on with our normally scheduled programming
+ auto_zone_options_t amode = AUTO_ZONE_COLLECT_NO_OPTIONS;
+ if (!(options & OBJC_COLLECT_IF_NEEDED)) {
switch (options & 0x3) {
- case OBJC_RATIO_COLLECTION: amode = AUTO_COLLECT_RATIO_COLLECTION; break;
- case OBJC_GENERATIONAL_COLLECTION: amode = AUTO_COLLECT_GENERATIONAL_COLLECTION; break;
- case OBJC_FULL_COLLECTION: amode = AUTO_COLLECT_FULL_COLLECTION; break;
- case OBJC_EXHAUSTIVE_COLLECTION: amode = AUTO_COLLECT_EXHAUSTIVE_COLLECTION; break;
+ case OBJC_RATIO_COLLECTION: amode = AUTO_ZONE_COLLECT_RATIO_COLLECTION; break;
+ case OBJC_GENERATIONAL_COLLECTION: amode = AUTO_ZONE_COLLECT_GENERATIONAL_COLLECTION; break;
+ case OBJC_FULL_COLLECTION: amode = AUTO_ZONE_COLLECT_FULL_COLLECTION; break;
+ case OBJC_EXHAUSTIVE_COLLECTION: amode = AUTO_ZONE_COLLECT_EXHAUSTIVE_COLLECTION; break;
}
- if (options & OBJC_COLLECT_IF_NEEDED) amode |= AUTO_COLLECT_IF_NEEDED;
- if (options & OBJC_WAIT_UNTIL_DONE) amode |= AUTO_COLLECT_SYNCHRONOUS; // uses different bits
- auto_collect(gc_zone, amode, NULL);
+ amode |= AUTO_ZONE_COLLECT_COALESCE;
+ amode |= AUTO_ZONE_COLLECT_LOCAL_COLLECTION;
}
- else {
- dispatch_async(dispatch_get_main_queue(), ^{ objc_collect(options); });
+ if (options & OBJC_WAIT_UNTIL_DONE) {
+ __block BOOL done = NO;
+ // If executing on the main thread, use the main thread work queue condition to block,
+ // so main thread finalization can complete. Otherwise, use a thread-local condition.
+ pthread_mutex_t localMutex = PTHREAD_MUTEX_INITIALIZER, *mutex = &localMutex;
+ pthread_cond_t localCondition = PTHREAD_COND_INITIALIZER, *condition = &localCondition;
+ if (onMainThread) {
+ mutex = &MainThreadWorkQ.mutex;
+ condition = &MainThreadWorkQ.condition;
+ }
+ pthread_mutex_lock(mutex);
+ auto_zone_collect_and_notify(gc_zone, amode, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ pthread_mutex_lock(mutex);
+ done = YES;
+ pthread_cond_signal(condition);
+ pthread_mutex_unlock(mutex);
+ });
+ while (!done) {
+ pthread_cond_wait(condition, mutex);
+ if (onMainThread && MainThreadWorkQ.head) {
+ pthread_mutex_unlock(mutex);
+ batchFinalizeOnMainThread();
+ pthread_mutex_lock(mutex);
+ }
+ }
+ pthread_mutex_unlock(mutex);
+ } else {
+ auto_zone_collect(gc_zone, amode);
}
}
return UseGC;
}
+malloc_zone_t *objc_collectableZone(void) {
+ return gc_zone;
+}
+
BOOL objc_dumpHeap(char *filenamebuffer, unsigned long length) {
static int counter = 0;
++counter;
* startup time.
**********************************************************************/
-__private_extern__ id objc_assign_strongCast_gc(id value, id *slot)
-{
+PRIVATE_EXTERN id objc_assign_strongCast_gc(id value, id *slot) {
if (!auto_zone_set_write_barrier(gc_zone, (void*)slot, value)) { // stores & returns true if slot points into GC allocated memory
auto_zone_root_write_barrier(gc_zone, slot, value); // always stores
}
return value;
}
-__private_extern__ id objc_assign_global_gc(id value, id *slot) {
+PRIVATE_EXTERN id objc_assign_global_gc(id value, id *slot) {
// use explicit root registration.
if (value && auto_zone_is_valid_pointer(gc_zone, value)) {
if (auto_zone_is_finalized(gc_zone, value)) {
- __private_extern__ void objc_assign_global_error(id value, id *slot);
-
_objc_inform("GC: storing an already collected object %p into global memory at %p, break on objc_assign_global_error to debug\n", value, slot);
objc_assign_global_error(value, slot);
}
return value;
}
+PRIVATE_EXTERN id objc_assign_threadlocal_gc(id value, id *slot)
+{
+ if (value && auto_zone_is_valid_pointer(gc_zone, value)) {
+ auto_zone_add_root(gc_zone, slot, value);
+ }
+ else {
+ *slot = value;
+ }
+
+ return value;
+}
-__private_extern__ id objc_assign_ivar_gc(id value, id base, ptrdiff_t offset)
+PRIVATE_EXTERN id objc_assign_ivar_gc(id value, id base, ptrdiff_t offset)
{
id *slot = (id*) ((char *)base + offset);
if (value) {
if (!auto_zone_set_write_barrier(gc_zone, (char *)base + offset, value)) {
- __private_extern__ void objc_assign_ivar_error(id base, ptrdiff_t offset);
-
_objc_inform("GC: %p + %tu isn't in the auto_zone, break on objc_assign_ivar_error to debug.\n", base, offset);
objc_assign_ivar_error(base, offset);
}
return value;
}
+PRIVATE_EXTERN id objc_assign_strongCast_non_gc(id value, id *slot) {
+ return (*slot = value);
+}
+
+PRIVATE_EXTERN id objc_assign_global_non_gc(id value, id *slot) {
+ return (*slot = value);
+}
+
+PRIVATE_EXTERN id objc_assign_threadlocal_non_gc(id value, id *slot) {
+ return (*slot = value);
+}
+
+PRIVATE_EXTERN id objc_assign_ivar_non_gc(id value, id base, ptrdiff_t offset) {
+ id *slot = (id*) ((char *)base + offset);
+ return (*slot = value);
+}
/***********************************************************************
* Write barrier exports
* Called by pretty much all GC-supporting code.
-*
-* These "generic" implementations, available in PPC, are thought to be
-* called by Rosetta when it translates the bla instruction.
**********************************************************************/
-// Platform-independent write barriers
-// These contain the UseGC check that the platform-specific
-// runtime-rewritten implementations do not.
-id objc_assign_strongCast_generic(id value, id *dest)
+#if defined(__i386__)
+
+// These 3 functions are defined in objc-auto-i386.s as
+// the non-GC variants. Under GC, rtp_init stomps them with jumps to
+// objc_assign_*_gc.
+
+#else
+
+// use generic implementation until time can be spent on optimizations
+id objc_assign_strongCast(id value, id *dest)
{
if (UseGC) {
return objc_assign_strongCast_gc(value, dest);
}
}
-
-id objc_assign_global_generic(id value, id *dest)
+id objc_assign_global(id value, id *dest)
{
if (UseGC) {
return objc_assign_global_gc(value, dest);
}
}
+id objc_assign_threadlocal(id value, id *dest)
+{
+ if (UseGC) {
+ return objc_assign_threadlocal_gc(value, dest);
+ } else {
+ return (*dest = value);
+ }
+}
-id objc_assign_ivar_generic(id value, id dest, ptrdiff_t offset)
+id objc_assign_ivar(id value, id dest, ptrdiff_t offset)
{
if (UseGC) {
return objc_assign_ivar_gc(value, dest, offset);
}
}
-#if defined(__ppc__) || defined(__i386__)
-
-// PPC write barriers are in objc-auto-ppc.s
-// write_barrier_init conditionally stomps those to jump to the _impl versions.
-
-// These 3 functions are defined in objc-auto-i386.s as
-// the non-GC variants. Under GC, rtp_init stomps them with jumps to
-// objc_assign_*_gc.
+// not defined(__i386__)
+#endif
+#if __LP64__
+ #define LC_SEGMENT_COMMAND LC_SEGMENT_64
+ #define LC_ROUTINES_COMMAND LC_ROUTINES_64
+ typedef struct mach_header_64 macho_header;
+ typedef struct section_64 macho_section;
+ typedef struct nlist_64 macho_nlist;
+ typedef struct segment_command_64 macho_segment_command;
#else
-
-// use generic implementation until time can be spent on optimizations
-id objc_assign_strongCast(id value, id *dest) { return objc_assign_strongCast_generic(value, dest); }
-id objc_assign_global(id value, id *dest) { return objc_assign_global_generic(value, dest); }
-id objc_assign_ivar(id value, id dest, ptrdiff_t offset) { return objc_assign_ivar_generic(value, dest, offset); }
-
-// not (defined(__ppc__)) && not defined(__i386__)
+ #define LC_SEGMENT_COMMAND LC_SEGMENT
+ #define LC_ROUTINES_COMMAND LC_ROUTINES
+ typedef struct mach_header macho_header;
+ typedef struct section macho_section;
+ typedef struct nlist macho_nlist;
+ typedef struct segment_command macho_segment_command;
#endif
+PRIVATE_EXTERN void _objc_update_stubs_in_mach_header(const struct mach_header* mh, uint32_t symbol_count, const char *symbols[], void *functions[]) {
+ uint32_t cmd_index, cmd_count = mh->ncmds;
+ intptr_t slide = 0;
+ const struct load_command* const cmds = (struct load_command*)((char*)mh + sizeof(macho_header));
+ const struct load_command* cmd;
+ const uint8_t *linkEditBase = NULL;
+ const macho_nlist *symbolTable = NULL;
+ uint32_t symbolTableCount = 0;
+ const char *stringTable = NULL;
+ uint32_t stringTableSize = 0;
+ const uint32_t *indirectSymbolTable = NULL;
+ uint32_t indirectSymbolTableCount = 0;
+
+ // first pass at load commands gets linkEditBase
+ for (cmd = cmds, cmd_index = 0; cmd_index < cmd_count; ++cmd_index) {
+ if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
+ const macho_segment_command* seg = (macho_segment_command*)cmd;
+ if ( strcmp(seg->segname,"__TEXT") == 0 )
+ slide = (uintptr_t)mh - seg->vmaddr;
+ else if ( strcmp(seg->segname,"__LINKEDIT") == 0 )
+ linkEditBase = (uint8_t*)(seg->vmaddr + slide - seg->fileoff);
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+
+ for (cmd = cmds, cmd_index = 0; cmd_index < cmd_count; ++cmd_index) {
+ switch ( cmd->cmd ) {
+ case LC_SYMTAB:
+ {
+ const struct symtab_command* symtab = (struct symtab_command*)cmd;
+ symbolTableCount = symtab->nsyms;
+ symbolTable = (macho_nlist*)(&linkEditBase[symtab->symoff]);
+ stringTableSize = symtab->strsize;
+ stringTable = (const char*)&linkEditBase[symtab->stroff];
+ }
+ break;
+ case LC_DYSYMTAB:
+ {
+ const struct dysymtab_command* dsymtab = (struct dysymtab_command*)cmd;
+ indirectSymbolTableCount = dsymtab->nindirectsyms;
+ indirectSymbolTable = (uint32_t*)(&linkEditBase[dsymtab->indirectsymoff]);
+ }
+ break;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+
+ // walk sections to find one with this lazy pointer
+ for (cmd = cmds, cmd_index = 0; cmd_index < cmd_count; ++cmd_index) {
+ if (cmd->cmd == LC_SEGMENT_COMMAND) {
+ const macho_segment_command* seg = (macho_segment_command*)cmd;
+ const macho_section* const sectionsStart = (macho_section*)((char*)seg + sizeof(macho_segment_command));
+ const macho_section* const sectionsEnd = §ionsStart[seg->nsects];
+ const macho_section* sect;
+ for (sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ const uint8_t type = sect->flags & SECTION_TYPE;
+ if (type == S_LAZY_DYLIB_SYMBOL_POINTERS || type == S_LAZY_SYMBOL_POINTERS) { // S_LAZY_DYLIB_SYMBOL_POINTERS
+ uint32_t pointer_index, pointer_count = (uint32_t)(sect->size / sizeof(uintptr_t));
+ uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + slide);
+ for (pointer_index = 0; pointer_index < pointer_count; ++pointer_index) {
+ const uint32_t indirectTableOffset = sect->reserved1;
+ if ((indirectTableOffset + pointer_index) < indirectSymbolTableCount) {
+ uint32_t symbolIndex = indirectSymbolTable[indirectTableOffset + pointer_index];
+ // if symbolIndex is INDIRECT_SYMBOL_LOCAL or INDIRECT_SYMBOL_LOCAL|INDIRECT_SYMBOL_ABS, then it will
+ // by definition be >= symbolTableCount.
+ if (symbolIndex < symbolTableCount) {
+ // found symbol for this lazy pointer, now lookup address
+ uint32_t stringTableOffset = symbolTable[symbolIndex].n_un.n_strx;
+ if (stringTableOffset < stringTableSize) {
+ const char* symbolName = &stringTable[stringTableOffset];
+ uint32_t i;
+ for (i = 0; i < symbol_count; ++i) {
+ if (strcmp(symbols[i], symbolName) == 0) {
+ symbolPointers[pointer_index] = (uintptr_t)functions[i];
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+}
void *objc_memmove_collectable(void *dst, const void *src, size_t size)
{
/***********************************************************************
-* CF-only write barrier exports
-* Called by CF only.
+* Weak ivar support
**********************************************************************/
-// Exported as very private SPI to CF
-void* objc_assign_ivar_address_CF(void *value, void *base, void **slot)
-{
- // CF has already checked that *slot is a gc block so this should never fail
- if (!auto_zone_set_write_barrier(gc_zone, slot, value))
- *slot = value;
- return value;
-}
-
-
-// exported as very private SPI to CF
-void* objc_assign_strongCast_CF(void* value, void **slot)
-{
- // CF has already checked that *slot is a gc block so this should never fail
- if (!auto_zone_set_write_barrier(gc_zone, slot, value))
- *slot = value;
- return value;
-}
-
-__private_extern__ void gc_fixup_weakreferences(id newObject, id oldObject) {
- // fix up weak references if any.
- const unsigned char *weakLayout = (const unsigned char *)class_getWeakIvarLayout(newObject->isa);
- if (weakLayout) {
- void **newPtr = (void **)newObject, **oldPtr = (void **)oldObject;
- unsigned char byte;
- while ((byte = *weakLayout++)) {
- unsigned skips = (byte >> 4);
- unsigned weaks = (byte & 0x0F);
- newPtr += skips, oldPtr += skips;
- while (weaks--) {
- *newPtr = NULL;
- auto_assign_weak_reference(gc_zone, auto_read_weak_reference(gc_zone, oldPtr), newPtr, NULL);
- ++newPtr, ++oldPtr;
- }
- }
+PRIVATE_EXTERN id objc_read_weak_gc(id *location) {
+ id result = *location;
+ if (result) {
+ result = auto_read_weak_reference(gc_zone, (void **)location);
}
+ return result;
}
-/***********************************************************************
-* Weak ivar support
-**********************************************************************/
+PRIVATE_EXTERN id objc_read_weak_non_gc(id *location) {
+ return *location;
+}
id objc_read_weak(id *location) {
id result = *location;
return result;
}
-id objc_assign_weak(id value, id *location) {
- if (UseGC) {
- auto_assign_weak_reference(gc_zone, value, (void **)location, NULL);
- }
- else {
- *location = value;
- }
+PRIVATE_EXTERN id objc_assign_weak_gc(id value, id *location) {
+ auto_assign_weak_reference(gc_zone, value, (const void **)location, NULL);
return value;
}
-/* Associative Reference Support. */
-
-id objc_getAssociatedObject(id object, void *key) {
- if (UseGC) {
- return auto_zone_get_associative_ref(gc_zone, object, key);
- } else {
- return _object_get_associative_reference(object, key);
- }
+PRIVATE_EXTERN id objc_assign_weak_non_gc(id value, id *location) {
+ return (*location = value);
}
-void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy) {
+id objc_assign_weak(id value, id *location) {
if (UseGC) {
- if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) {
- value = objc_msgSend(value, @selector(copy));
- }
- auto_zone_set_associative_ref(gc_zone, object, key, value);
- } else {
- // Note, creates a retained reference in non-GC.
- _object_set_associative_reference(object, key, value, policy);
+ auto_assign_weak_reference(gc_zone, value, (const void **)location, NULL);
}
-}
-
-void objc_removeAssociatedObjects(id object) {
- if (UseGC) {
- auto_zone_erase_associative_refs(gc_zone, object);
- } else {
- if (_class_instancesHaveAssociatedObjects(object->isa)) _object_remove_assocations(object);
+ else {
+ *location = value;
}
+ return value;
}
-BOOL class_instancesHaveAssociatedObjects(Class cls) {
- return _class_instancesHaveAssociatedObjects(cls);
-}
-
-id objc_getAssociatedProperties(id object, Class dataClass) {
- id data = objc_getAssociatedObject(object, dataClass);
- if (data == nil) {
- // FIXME: Need to make this atomic.
- data = objc_msgSend(dataClass, @selector(new));
- objc_setAssociatedObject(object, dataClass, data, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- objc_msgSend(data, @selector(release));
+PRIVATE_EXTERN void gc_fixup_weakreferences(id newObject, id oldObject) {
+ // fix up weak references if any.
+ const unsigned char *weakLayout = (const unsigned char *)class_getWeakIvarLayout(_object_getClass(newObject));
+ if (weakLayout) {
+ void **newPtr = (void **)newObject, **oldPtr = (void **)oldObject;
+ unsigned char byte;
+ while ((byte = *weakLayout++)) {
+ unsigned skips = (byte >> 4);
+ unsigned weaks = (byte & 0x0F);
+ newPtr += skips, oldPtr += skips;
+ while (weaks--) {
+ *newPtr = NULL;
+ auto_assign_weak_reference(gc_zone, auto_read_weak_reference(gc_zone, oldPtr), (const void **)newPtr, NULL);
+ ++newPtr, ++oldPtr;
+ }
+ }
}
- return data;
}
/***********************************************************************
/***********************************************************************
-* Stack management
-* Used to tell clean up dirty stack frames before a thread blocks. To
-* make this more efficient, we really need better support from pthreads.
-* See <rdar://problem/4548631> for more details.
+* Stack clearing.
+* Used by top-level thread loops to reduce false pointers from the stack.
**********************************************************************/
-
-static vm_address_t _stack_resident_base() {
- pthread_t self = pthread_self();
- size_t stack_size = pthread_get_stacksize_np(self);
- vm_address_t stack_base = (vm_address_t)pthread_get_stackaddr_np(self) - stack_size;
- size_t stack_page_count = stack_size / vm_page_size;
- char stack_residency[stack_page_count];
- vm_address_t stack_resident_base = 0;
- if (mincore((void*)stack_base, stack_size, stack_residency) == 0) {
- // we can now tell the degree to which the stack is resident, and use it as our ultimate high water mark.
- size_t i;
- for (i = 0; i < stack_page_count; ++i) {
- if (stack_residency[i]) {
- stack_resident_base = stack_base + i * vm_page_size;
- // malloc_printf("last touched page = %lu\n", stack_page_count - i - 1);
- break;
- }
- }
- }
- return stack_resident_base;
-}
-
void objc_clear_stack(unsigned long options) {
if (!UseGC) return;
auto_zone_clear_stack(gc_zone, 0);
}
+
/***********************************************************************
* Finalization support
**********************************************************************/
// Finalizer crash debugging
static void *finalizing_object;
-static const char *__crashreporter_info__;
-
// finalize a single object without fuss
// When there are no main-thread-only classes this is used directly
static void finalizeOneObject(void *obj, void *ignored) {
id object = (id)obj;
finalizing_object = obj;
- __crashreporter_info__ = object_getClassName(obj);
+
+ Class cls = object_getClass(obj);
+ CRSetCrashLogMessage2(class_getName(cls));
/// call -finalize method.
objc_msgSend(object, @selector(finalize));
- // Call C++ destructors, if any.
- object_cxxDestruct(object);
+
+ // Call C++ destructors.
+ // This would be objc_destructInstance() but for performance.
+ if (_class_hasCxxStructors(cls)) {
+ object_cxxDestruct(object);
+ }
finalizing_object = NULL;
- __crashreporter_info__ = NULL;
+ CRSetCrashLogMessage2(NULL);
}
// finalize object only if it is a main-thread-only object.
// Called only from the main thread.
static void finalizeOneMainThreadOnlyObject(void *obj, void *arg) {
id object = (id)obj;
- Class cls = object->isa;
+ Class cls = _object_getClass(object);
if (cls == NULL) {
_objc_fatal("object with NULL ISA passed to finalizeOneMainThreadOnlyObject: %p\n", obj);
}
// Important: if a main-thread-only object is passed, return that fact in the needsMain argument
static void finalizeOneAnywhereObject(void *obj, void *needsMain) {
id object = (id)obj;
- Class cls = object->isa;
+ Class cls = _object_getClass(object);
bool *needsMainThreadWork = needsMain;
if (cls == NULL) {
_objc_fatal("object with NULL ISA passed to finalizeOneAnywhereObject: %p\n", obj);
size_t cursor_size,
void (*finalizeAnObject)(void *, void*))
{
+#if !defined(NDEBUG) && !__OBJC2__
+ // debug: don't call try/catch before exception handlers are installed
+ objc_exception_functions_t table = {0};
+ objc_exception_get_functions(&table);
+ assert(table.throw_exc);
+#endif
+
bool needsMainThreadWork = false;
for (;;) {
@try {
foreach(cursor, finalizeAnObject, &needsMainThreadWork);
// non-exceptional return means finalization is complete.
break;
- } @catch (id exception) {
+ }
+ @catch (id exception) {
// whoops, note exception, then restart at cursor's position
- __private_extern__ void objc_exception_during_finalize_error(void);
_objc_inform("GC: -finalize resulted in an exception (%p) being thrown, break on objc_exception_during_finalize_error to debug\n\t%s", exception, (const char*)[[exception description] UTF8String]);
objc_exception_during_finalize_error();
+ }
+ @catch (...) {
+ // whoops, note exception, then restart at cursor's position
+ _objc_inform("GC: -finalize resulted in an exception being thrown, break on objc_exception_during_finalize_error to debug");
+ objc_exception_during_finalize_error();
}
}
return needsMainThreadWork;
// wait for the main thread to finish finalizing instances of classes marked CLS_FINALIZE_ON_MAIN_THREAD.
pthread_mutex_lock(&MainThreadWorkQ.mutex);
- while (!bfb.finished) pthread_cond_wait(&MainThreadWorkQ.condition, &MainThreadWorkQ.mutex);
+ while (!bfb.finished) {
+ // the main thread might be blocked waiting for a synchronous collection to complete, so wake it here
+ pthread_cond_signal(&MainThreadWorkQ.condition);
+ pthread_cond_wait(&MainThreadWorkQ.condition, &MainThreadWorkQ.mutex);
+ }
pthread_mutex_unlock(&MainThreadWorkQ.mutex);
//printf("<------ main thread finalize done\n");
}
static BOOL _NSResurrectedObject_resolveClassMethod(id self, SEL _cmd, SEL name) {
- class_addMethod(object_getClass(self), name, (IMP)_NSResurrectedObject_classMethod, "@@:");
+ class_addMethod(_object_getClass(self), name, (IMP)_NSResurrectedObject_classMethod, "@@:");
return YES;
}
_NSResurrectedObjectMap = NXCreateMapTable(NXPtrValueMapPrototype, 128);
_NSResurrectedObjectClass = objc_allocateClassPair(objc_getClass("NSObject"), "_NSResurrectedObject", 0);
class_addMethod(_NSResurrectedObjectClass, @selector(finalize), (IMP)_NSResurrectedObject_finalize, "v@:");
- Class metaClass = object_getClass(_NSResurrectedObjectClass);
+ Class metaClass = _object_getClass(_NSResurrectedObjectClass);
class_addMethod(metaClass, @selector(resolveInstanceMethod:), (IMP)_NSResurrectedObject_resolveInstanceMethod, "c@::");
class_addMethod(metaClass, @selector(resolveClassMethod:), (IMP)_NSResurrectedObject_resolveClassMethod, "c@::");
objc_registerClassPair(_NSResurrectedObjectClass);
static void resurrectZombie(auto_zone_t *zone, void *ptr) {
id object = (id) ptr;
- Class cls = object->isa;
+ Class cls = _object_getClass(object);
if (cls != _NSResurrectedObjectClass) {
// remember the original class for this instance.
pthread_mutex_lock(&_NSResurrectedObjectLock);
NXMapInsert(_NSResurrectedObjectMap, ptr, cls);
pthread_mutex_unlock(&_NSResurrectedObjectLock);
- object->isa = _NSResurrectedObjectClass;
+ object_setClass(object, _NSResurrectedObjectClass);
}
}
return name_for_address(zone, base, offset, false);
}
+static const char* objc_name_for_object(auto_zone_t *zone, void *object) {
+ Class cls = *(Class *)object;
+ if (!objc_isRegisteredClass(cls)) return "";
+ return class_getName(cls);
+}
+
+/* Compaction support */
+
+PRIVATE_EXTERN void objc_disableCompaction() {
+ if (UseCompaction) {
+ UseCompaction = NO;
+ auto_zone_disable_compaction(gc_zone);
+ }
+}
+
/***********************************************************************
* Collection support
**********************************************************************/
static BOOL objc_isRegisteredClass(Class candidate);
-static const unsigned char *objc_layout_for_address(auto_zone_t *zone, void *address)
-{
- Class cls = *(Class *)address;
- if (!objc_isRegisteredClass(cls)) return NULL;
- return (const unsigned char *)class_getIvarLayout(cls);
+static const unsigned char *objc_layout_for_address(auto_zone_t *zone, void *address) {
+ id object = (id)address;
+ Class cls = (volatile Class)_object_getClass(object);
+ return objc_isRegisteredClass(cls) ? _object_getIvarLayout(cls, object) : NULL;
}
-static const unsigned char *objc_weak_layout_for_address(auto_zone_t *zone, void *address)
-{
- Class cls = *(Class *)address;
- if (!objc_isRegisteredClass(cls)) return NULL;
- return (const unsigned char *)class_getWeakIvarLayout(cls);
+static const unsigned char *objc_weak_layout_for_address(auto_zone_t *zone, void *address) {
+ id object = (id)address;
+ Class cls = (volatile Class)_object_getClass(object);
+ return objc_isRegisteredClass(cls) ? class_getWeakIvarLayout(cls) : NULL;
}
-__private_extern__ void gc_register_datasegment(uintptr_t base, size_t size) {
+PRIVATE_EXTERN void gc_register_datasegment(uintptr_t base, size_t size) {
auto_zone_register_datasegment(gc_zone, (void*)base, size);
}
-__private_extern__ void gc_unregister_datasegment(uintptr_t base, size_t size) {
+PRIVATE_EXTERN void gc_unregister_datasegment(uintptr_t base, size_t size) {
auto_zone_unregister_datasegment(gc_zone, (void*)base, size);
}
+#define countof(array) (sizeof(array) / sizeof(array[0]))
+
+// defined in objc-externalref.m.
+extern objc_xref_t _object_addExternalReference_gc(id obj, objc_xref_t type);
+extern objc_xref_t _object_addExternalReference_rr(id obj, objc_xref_t type);
+extern id _object_readExternalReference_gc(objc_xref_t ref);
+extern id _object_readExternalReference_rr(objc_xref_t ref);
+extern void _object_removeExternalReference_gc(objc_xref_t ref);
+extern void _object_removeExternalReference_rr(objc_xref_t ref);
+
+PRIVATE_EXTERN void gc_fixup_barrier_stubs(const struct dyld_image_info *info) {
+ static const char *symbols[] = {
+ "_objc_assign_strongCast", "_objc_assign_ivar",
+ "_objc_assign_global", "_objc_assign_threadlocal",
+ "_objc_read_weak", "_objc_assign_weak",
+ "_objc_getProperty", "_objc_setProperty",
+ "_objc_getAssociatedObject", "_objc_setAssociatedObject",
+ "__object_addExternalReference", "__object_readExternalReference", "__object_removeExternalReference"
+ };
+ if (UseGC) {
+ // resolve barrier symbols using GC functions.
+ static void *gc_functions[] = {
+ &objc_assign_strongCast_gc, &objc_assign_ivar_gc,
+ &objc_assign_global_gc, &objc_assign_threadlocal_gc,
+ &objc_read_weak_gc, &objc_assign_weak_gc,
+ &objc_getProperty_gc, &objc_setProperty_gc,
+ &objc_getAssociatedObject_gc, &objc_setAssociatedObject_gc,
+ &_object_addExternalReference_gc, &_object_readExternalReference_gc, &_object_removeExternalReference_gc
+ };
+ assert(countof(symbols) == countof(gc_functions));
+ _objc_update_stubs_in_mach_header(info->imageLoadAddress, countof(symbols), symbols, gc_functions);
+ } else {
+ // resolve barrier symbols using non-GC functions.
+ static void *nongc_functions[] = {
+ &objc_assign_strongCast_non_gc, &objc_assign_ivar_non_gc,
+ &objc_assign_global_non_gc, &objc_assign_threadlocal_non_gc,
+ &objc_read_weak_non_gc, &objc_assign_weak_non_gc,
+ &objc_getProperty_non_gc, &objc_setProperty_non_gc,
+ &objc_getAssociatedObject_non_gc, &objc_setAssociatedObject_non_gc,
+ &_object_addExternalReference_rr, &_object_readExternalReference_rr, &_object_removeExternalReference_rr
+ };
+ assert(countof(symbols) == countof(nongc_functions));
+ _objc_update_stubs_in_mach_header(info->imageLoadAddress, countof(symbols), symbols, nongc_functions);
+ }
+}
/***********************************************************************
* Initialization
**********************************************************************/
static void objc_will_grow(auto_zone_t *zone, auto_heap_growth_info_t info) {
- if (MultiThreadedGC) {
- //printf("objc_will_grow %d\n", info);
-
- if (auto_zone_is_collecting(gc_zone)) {
- ;
- }
- else {
- auto_collect(gc_zone, AUTO_COLLECT_RATIO_COLLECTION, NULL);
- }
+ if (auto_zone_is_collecting(gc_zone)) {
+ ;
+ }
+ else {
+ auto_zone_collect(gc_zone, AUTO_ZONE_COLLECT_COALESCE|AUTO_ZONE_COLLECT_RATIO_COLLECTION);
}
}
-static auto_zone_t *gc_zone_init(void)
+static auto_zone_t *gc_zone_init(BOOL wantsCompaction)
{
auto_zone_t *result;
-
- // result = auto_zone_create("objc auto collected zone");
+ static int didOnce = 0;
+ if (!didOnce) {
+ didOnce = 1;
+
+ // initialize the batch finalization queue
+ MainThreadWorkQ.head = NULL;
+ MainThreadWorkQ.tail = NULL;
+ pthread_mutex_init(&MainThreadWorkQ.mutex, NULL);
+ pthread_cond_init(&MainThreadWorkQ.condition, NULL);
+ }
+
result = auto_zone_create("auto_zone");
+ if (!wantsCompaction) auto_zone_disable_compaction(result);
+
auto_collection_control_t *control = auto_collection_parameters(result);
// set up the magic control parameters
control->layout_for_address = objc_layout_for_address;
control->weak_layout_for_address = objc_weak_layout_for_address;
control->name_for_address = objc_name_for_address;
+
+ if (control->version >= sizeof(auto_collection_control_t)) {
+ control->name_for_object = objc_name_for_object;
+ }
return result;
}
extern void (*dispatch_begin_thread_4GC)(void);
extern void (*dispatch_end_thread_4GC)(void);
+static void objc_reapThreadLocalBlocks()
+{
+ if (UseGC) auto_zone_reap_all_local_blocks(gc_zone);
+}
+
void objc_registerThreadWithCollector()
{
if (UseGC) auto_zone_register_thread(gc_zone);
}
// Always called by _objcInit, even if GC is off.
-__private_extern__ void gc_init(BOOL on)
+PRIVATE_EXTERN void gc_init(BOOL wantsGC, BOOL wantsCompaction)
{
- UseGC = on;
+ UseGC = wantsGC;
+ UseCompaction = wantsCompaction;
if (PrintGC) {
- _objc_inform("GC: is %s", on ? "ON" : "OFF");
+ _objc_inform("GC: is %s", wantsGC ? "ON" : "OFF");
+ _objc_inform("Compaction: is %s", wantsCompaction ? "ON" : "OFF");
}
if (UseGC) {
- // Add GC state to crash log reports
- _objc_inform_on_crash("garbage collection is ON");
-
// Set up the GC zone
- gc_zone = gc_zone_init();
+ gc_zone = gc_zone_init(wantsCompaction);
+
+ // tell libdispatch to register its threads with the GC.
+ dispatch_begin_thread_4GC = objc_registerThreadWithCollector;
+ dispatch_end_thread_4GC = objc_reapThreadLocalBlocks;
// no NSObject until Foundation calls objc_collect_init()
_NSObject_finalize = &_objc_msgForward_internal;
// tell Blocks to use collectable memory. CF will cook up the classes separately.
gc_block_init();
-
- // tell libdispatch to register its threads with the GC.
- dispatch_begin_thread_4GC = objc_registerThreadWithCollector;
- dispatch_end_thread_4GC = objc_unregisterThreadWithCollector;
- } else {
- auto_zone_start_monitor(false);
- auto_zone_set_class_list((int (*)(void **, int))objc_getClassList);
}
+
+ // Add GC state to crash log reports
+ _objc_inform_on_crash("garbage collection is %s",
+ wantsGC ? "ON" : "OFF");
}
auto_zone_write_barrier_memmove(gc_zone, dst, src, (size_t)size);
}
-
-// Initialize the Block subsystem iff running under GC.
static void gc_block_init(void) {
- // set up the callout functions that enable _Block_copy to do the right thing under GC
_Block_use_GC(
- block_gc_alloc5,
- block_gc_setHasRefcount,
- (void (*)(void *, void **))objc_assign_strongCast_gc,
- (void (*)(const void *, void *))objc_assign_weak,
- block_gc_memmove
+ block_gc_alloc5,
+ block_gc_setHasRefcount,
+ (void (*)(void *, void **))objc_assign_strongCast_gc,
+ (void (*)(const void *, void *))objc_assign_weak,
+ block_gc_memmove
);
}
static void registeredClassTableInit() {
assert(UseGC);
// allocate a collectable (refcount 0) zeroed hunk of unscanned memory
- uintptr_t *table = (uintptr_t *)auto_zone_allocate_object(gc_zone, INITIALSIZE*sizeof(void *), AUTO_MEMORY_UNSCANNED, false, true);
+ uintptr_t *table = (uintptr_t *)auto_zone_allocate_object(gc_zone, INITIALSIZE*sizeof(void *), AUTO_MEMORY_UNSCANNED, true, true);
// set initial capacity (as mask)
table[0] = INITIALSIZE - 1;
// set initial count
table[1] = 0;
- // register it so that the collector will keep it around. We could instead allocate it refcount 1 and then decr when done.
- auto_zone_add_root(gc_zone, &AllClasses, table);
+ // Compaction: we allocate it refcount 1 and then decr when done.
+ AllClasses = (Class *)table;
}
// Verify that a particular pointer is to a class.
// Safe from any thread anytime
static BOOL objc_isRegisteredClass(Class candidate) {
assert(UseGC);
+ // nil is never a valid ISA.
+ if (candidate == nil) return NO;
// We don't care about a race with another thread adding a class to which we randomly might have a pointer
// Get local copy of classes so that we're immune from updates.
// We keep the size of the list as the first element so there is no race as the list & size get updated.
}
// lock held by callers
-__private_extern__
+PRIVATE_EXTERN
void objc_addRegisteredClass(Class candidate) {
if (!UseGC) return;
uintptr_t *table = (uintptr_t *)AllClasses;
if (2*++table[1] > table[0]) { // add to count; check if we cross 50% utilization
// grow
uintptr_t oldSize = table[0]+1;
- uintptr_t *newTable = (uintptr_t *)auto_zone_allocate_object(gc_zone, oldSize*2*sizeof(void *), AUTO_MEMORY_UNSCANNED, false, true);
+ uintptr_t *newTable = (uintptr_t *)auto_zone_allocate_object(gc_zone, oldSize*2*sizeof(void *), AUTO_MEMORY_UNSCANNED, true, true);
uintptr_t i;
newTable[0] = 2*oldSize - 1;
newTable[1] = 0;
if (table[i] && table[i] != REMOVED)
addClassHelper(newTable, table[i]);
}
- // this does the write-barrier. Don't use objc_assignGlobal because it trips a linker error on 64-bit.
- auto_zone_add_root(gc_zone, &AllClasses, newTable);
+ AllClasses = (Class *)newTable;
+ // let the old table be collected when other threads are no longer reading it.
+ auto_zone_release(gc_zone, (void *)table);
}
return;
}
}
// lock held by callers
-__private_extern__
+PRIVATE_EXTERN
void objc_removeRegisteredClass(Class candidate) {
if (!UseGC) return;
uintptr_t *table = (uintptr_t *)AllClasses;
str[0] = '\0';
}
-static void strlcatx(char *str, uintptr_t value, size_t bufSize)
-{
- if ( (bufSize- strlen(str)) < 30)
- return;
- str = _malloc_append_unsigned(value, 16, str + strlen(str));
- str[0] = '\0';
-}
-
static Ivar ivar_for_offset(Class cls, vm_address_t offset)
{
switch (type) {
case AUTO_OBJECT_SCANNED:
- case AUTO_OBJECT_UNSCANNED: {
+ case AUTO_OBJECT_UNSCANNED:
+ case AUTO_OBJECT_ALL_POINTERS: {
const char *class_name = object_getClassName((id)base);
if ((0 == strcmp(class_name, "__NSCFType")) || (0 == strcmp(class_name, "NSCFType"))) {
strlcat(buf, cf_class_for_object((void *)base), sizeof(buf));
strlcat(buf, class_name, sizeof(buf));
}
if (offset) {
- append_ivar_at_offset(buf, object_getClass((id)base), offset, sizeof(buf));
+ append_ivar_at_offset(buf, _object_getClass((id)base), offset, sizeof(buf));
}
APPEND_SIZE(size);
break;
strlcat(buf, "{no-pointers-block}", sizeof(buf));
APPEND_SIZE(size);
break;
+ case AUTO_MEMORY_ALL_POINTERS:
+ strlcat(buf, "{all-pointers-block}", sizeof(buf));
+ APPEND_SIZE(size);
+ break;
+ case AUTO_MEMORY_ALL_WEAK_POINTERS:
+ strlcat(buf, "{all-weak-pointers-block}", sizeof(buf));
+ APPEND_SIZE(size);
+ break;
+ case AUTO_TYPE_UNKNOWN:
+ strlcat(buf, "{uncollectable-memory}", sizeof(buf));
+ break;
default:
- strlcat(buf, "{unallocated-or-stack}", sizeof(buf));
+ strlcat(buf, "{unknown-memory-type}", sizeof(buf));
}
if (withRetainCount && refcount > 0) {
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/***********************************************************************
+ * objc-block-trampolines.m
+ * Author: b.bum
+ *
+ **********************************************************************/
+
+/***********************************************************************
+ * Imports.
+ **********************************************************************/
+#include "objc-private.h"
+#include "runtime.h"
+
+#include <Block.h>
+#include <Block_private.h>
+#include <mach/mach.h>
+
+// symbols defined in assembly files
+// Don't use the symbols directly; they're thumb-biased on some ARM archs.
+#define TRAMP(tramp) \
+ static inline uintptr_t tramp(void) { \
+ extern void *_##tramp; \
+ return ((uintptr_t)&_##tramp) & ~1UL; \
+ }
+// Scalar return
+TRAMP(a1a2_tramphead); // trampoline header code
+TRAMP(a1a2_firsttramp); // first trampoline
+TRAMP(a1a2_nexttramp); // second trampoline
+TRAMP(a1a2_trampend); // after the last trampoline
+
+// Struct return
+TRAMP(a2a3_tramphead);
+TRAMP(a2a3_firsttramp);
+TRAMP(a2a3_nexttramp);
+TRAMP(a2a3_trampend);
+
+// argument mode identifier
+typedef enum {
+ ReturnValueInRegisterArgumentMode,
+ ReturnValueOnStackArgumentMode,
+
+ ArgumentModeMax
+} ArgumentMode;
+
+// slot size is 8 bytes on both i386 and x86_64 (because of bytes-per-call instruction is > 4 for both)
+#define SLOT_SIZE 8
+
+// unsigned value, any value, larger thna # of blocks that fit in the page pair
+#define LAST_SLOT_MARKER 4241
+
+#define TRAMPOLINE_PAGE_PAIR_HEADER_SIZE (sizeof(uint32_t) + sizeof(struct _TrampolineBlockPagePair *) + sizeof(struct _TrampolineBlockPagePair *))
+typedef struct _TrampolineBlockPagePair {
+ struct _TrampolineBlockPagePair *nextPagePair; // linked list of all page pairs
+ struct _TrampolineBlockPagePair *nextAvailablePage; // linked list of pages with available slots
+
+ uint32_t nextAvailable; // index of next available slot, 0 if no more available
+
+ // Data: block pointers and free list.
+ // Bytes parallel with trampoline header are the fields above, or unused.
+ uint8_t blocks[ PAGE_SIZE - TRAMPOLINE_PAGE_PAIR_HEADER_SIZE ]
+ __attribute__((unavailable)) /* always use _headerSize() */;
+
+ // Code: trampoline header followed by trampolines.
+ uint8_t trampolines[PAGE_SIZE];
+
+ // Per-trampoline block data format:
+ // initial value is 0 while page pair is filled sequentially (last slot is LAST_SLOT_MARKER to indicate end of page)
+ // when filled, value is reference to Block_copy()d block
+ // when empty, value is index of next available slot OR LAST_SLOT_MARKER
+
+} TrampolineBlockPagePair;
+
+// two sets of trampoline page pairs; one for stack returns and one for register returns
+static TrampolineBlockPagePair *headPagePairs[2];
+
+#pragma mark Utility Functions
+static inline uint32_t _headerSize() {
+ uint32_t headerSize = (uint32_t) (a1a2_firsttramp() - a1a2_tramphead());
+
+ // make sure stret and non-stret sizes match
+ assert(a2a3_firsttramp() - a2a3_tramphead() == headerSize);
+
+ return headerSize;
+}
+
+static inline uint32_t _slotSize() {
+ uint32_t slotSize = (uint32_t) (a1a2_nexttramp() - a1a2_firsttramp());
+
+ // make sure stret and non-stret sizes match
+ assert(a2a3_nexttramp() - a2a3_firsttramp() == slotSize);
+
+ return slotSize;
+}
+
+static inline bool trampolinesAreThumb(void) {
+ extern void *_a1a2_firsttramp;
+ extern void *_a1a2_nexttramp;
+ extern void *_a2a3_firsttramp;
+ extern void *_a2a3_nexttramp;
+
+ // make sure thumb-edness of all trampolines match
+ assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
+ ((uintptr_t)&_a2a3_firsttramp) % 2);
+ assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
+ ((uintptr_t)&_a1a2_nexttramp) % 2);
+ assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
+ ((uintptr_t)&_a2a3_nexttramp) % 2);
+
+ return ((uintptr_t)&_a1a2_firsttramp) % 2;
+}
+
+static inline uint32_t _slotsPerPagePair() {
+ uint32_t slotSize = _slotSize();
+ uint32_t slotsPerPagePair = PAGE_SIZE / slotSize;
+ return slotsPerPagePair;
+}
+
+static inline uint32_t _paddingSlotCount() {
+ uint32_t headerSize = _headerSize();
+ uint32_t slotSize = _slotSize();
+ uint32_t paddingSlots = headerSize / slotSize;
+ return paddingSlots;
+}
+
+static inline void **_payloadAddressAtIndex(TrampolineBlockPagePair *pagePair, uint32_t index) {
+ uint32_t slotSize = _slotSize();
+ uintptr_t baseAddress = (uintptr_t) pagePair;
+ uintptr_t payloadAddress = baseAddress + (slotSize * index);
+ return (void **)payloadAddress;
+}
+
+static inline IMP _trampolineAddressAtIndex(TrampolineBlockPagePair *pagePair, uint32_t index) {
+ uint32_t slotSize = _slotSize();
+ uintptr_t baseAddress = (uintptr_t) &(pagePair->trampolines);
+ uintptr_t trampolineAddress = baseAddress + (slotSize * index);
+
+#if defined(__arm__)
+ if (trampolinesAreThumb()) trampolineAddress++;
+#endif
+
+ return (IMP)trampolineAddress;
+}
+
+static inline void _lock() {
+#if __OBJC2__
+ rwlock_write(&runtimeLock);
+#else
+ mutex_lock(&classLock);
+#endif
+}
+
+static inline void _unlock() {
+#if __OBJC2__
+ rwlock_unlock_write(&runtimeLock);
+#else
+ mutex_unlock(&classLock);
+#endif
+}
+
+static inline void _assert_locked() {
+#if __OBJC2__
+ rwlock_assert_writing(&runtimeLock);
+#else
+ mutex_assert_locked(&classLock);
+#endif
+}
+
+#pragma mark Trampoline Management Functions
+static TrampolineBlockPagePair *_allocateTrampolinesAndData(ArgumentMode aMode) {
+ _assert_locked();
+
+ vm_address_t dataAddress;
+
+ // make sure certain assumptions are met
+ assert(PAGE_SIZE == 4096);
+ assert(sizeof(TrampolineBlockPagePair) == 2*PAGE_SIZE);
+ assert(_slotSize() == 8);
+ assert(_headerSize() >= TRAMPOLINE_PAGE_PAIR_HEADER_SIZE);
+ assert((_headerSize() % _slotSize()) == 0);
+
+ assert(a1a2_tramphead() % PAGE_SIZE == 0);
+ assert(a1a2_tramphead() + PAGE_SIZE == a1a2_trampend());
+ assert(a2a3_tramphead() % PAGE_SIZE == 0);
+ assert(a2a3_tramphead() + PAGE_SIZE == a2a3_trampend());
+
+ TrampolineBlockPagePair *headPagePair = headPagePairs[aMode];
+
+ if (headPagePair) {
+ assert(headPagePair->nextAvailablePage == NULL);
+ }
+
+ int i;
+ kern_return_t result = KERN_FAILURE;
+ for(i = 0; i < 5; i++) {
+ result = vm_allocate(mach_task_self(), &dataAddress, PAGE_SIZE * 2, TRUE);
+ if (result != KERN_SUCCESS) {
+ mach_error("vm_allocate failed", result);
+ return NULL;
+ }
+
+ vm_address_t codeAddress = dataAddress + PAGE_SIZE;
+ result = vm_deallocate(mach_task_self(), codeAddress, PAGE_SIZE);
+ if (result != KERN_SUCCESS) {
+ mach_error("vm_deallocate failed", result);
+ return NULL;
+ }
+
+ uintptr_t codePage;
+ switch(aMode) {
+ case ReturnValueInRegisterArgumentMode:
+ codePage = a1a2_firsttramp() & ~(PAGE_MASK);
+ break;
+ case ReturnValueOnStackArgumentMode:
+ codePage = a2a3_firsttramp() & ~(PAGE_MASK);
+ break;
+ default:
+ _objc_fatal("unknown return mode %d", (int)aMode);
+ break;
+ }
+ vm_prot_t currentProtection, maxProtection;
+ result = vm_remap(mach_task_self(), &codeAddress, PAGE_SIZE, 0, FALSE, mach_task_self(),
+ codePage, TRUE, ¤tProtection, &maxProtection, VM_INHERIT_SHARE);
+ if (result != KERN_SUCCESS) {
+ result = vm_deallocate(mach_task_self(), dataAddress, PAGE_SIZE);
+ if (result != KERN_SUCCESS) {
+ mach_error("vm_deallocate for retry failed.", result);
+ return NULL;
+ }
+ } else
+ break;
+ }
+
+ if (result != KERN_SUCCESS)
+ return NULL;
+
+ TrampolineBlockPagePair *pagePair = (TrampolineBlockPagePair *) dataAddress;
+ pagePair->nextAvailable = _paddingSlotCount();
+ pagePair->nextPagePair = NULL;
+ pagePair->nextAvailablePage = NULL;
+ void **lastPageBlockPtr = _payloadAddressAtIndex(pagePair, _slotsPerPagePair() - 1);
+ *lastPageBlockPtr = (void*)(uintptr_t) LAST_SLOT_MARKER;
+
+ if (headPagePair) {
+ TrampolineBlockPagePair *lastPage = headPagePair;
+ while(lastPage->nextPagePair)
+ lastPage = lastPage->nextPagePair;
+
+ lastPage->nextPagePair = pagePair;
+ headPagePairs[aMode]->nextAvailablePage = pagePair;
+ } else {
+ headPagePairs[aMode] = pagePair;
+ }
+
+ return pagePair;
+}
+
+static TrampolineBlockPagePair *_getOrAllocatePagePairWithNextAvailable(ArgumentMode aMode) {
+ _assert_locked();
+
+ TrampolineBlockPagePair *headPagePair = headPagePairs[aMode];
+
+ if (!headPagePair)
+ return _allocateTrampolinesAndData(aMode);
+
+ if (headPagePair->nextAvailable) // make sure head page is filled first
+ return headPagePair;
+
+ if (headPagePair->nextAvailablePage) // check if there is a page w/a hole
+ return headPagePair->nextAvailablePage;
+
+ return _allocateTrampolinesAndData(aMode); // tack on a new one
+}
+
+static TrampolineBlockPagePair *_pagePairAndIndexContainingIMP(IMP anImp, uint32_t *outIndex, TrampolineBlockPagePair **outHeadPagePair) {
+ _assert_locked();
+
+ uintptr_t impValue = (uintptr_t) anImp;
+ uint32_t i;
+
+ for(i = 0; i < ArgumentModeMax; i++) {
+ TrampolineBlockPagePair *pagePair = headPagePairs[i];
+
+ while(pagePair) {
+ uintptr_t startOfTrampolines = (uintptr_t) &(pagePair->trampolines);
+ uintptr_t endOfTrampolines = ((uintptr_t) startOfTrampolines) + PAGE_SIZE;
+
+ if ( (impValue >=startOfTrampolines) && (impValue <= endOfTrampolines) ) {
+ if (outIndex) {
+ *outIndex = (uint32_t) ((impValue - startOfTrampolines) / SLOT_SIZE);
+ }
+ if (outHeadPagePair) {
+ *outHeadPagePair = headPagePairs[i];
+ }
+ return pagePair;
+ }
+
+ pagePair = pagePair->nextPagePair;
+ }
+ }
+
+ return NULL;
+}
+
+// `block` must already have been copied
+static IMP _imp_implementationWithBlockNoCopy(ArgumentMode aMode, void *block)
+{
+ _assert_locked();
+
+ TrampolineBlockPagePair *pagePair = _getOrAllocatePagePairWithNextAvailable(aMode);
+ if (!headPagePairs[aMode])
+ headPagePairs[aMode] = pagePair;
+
+ uint32_t index = pagePair->nextAvailable;
+ void **payloadAddress = _payloadAddressAtIndex(pagePair, index);
+ assert((index < 1024) || (index == LAST_SLOT_MARKER));
+
+ uint32_t nextAvailableIndex = (uint32_t) *((uintptr_t *) payloadAddress);
+ if (nextAvailableIndex == 0)
+ // first time through, slots are filled with zeros, fill sequentially
+ pagePair->nextAvailable = index + 1;
+ else if (nextAvailableIndex == LAST_SLOT_MARKER) {
+ // last slot is filled with this as marker
+ // page now full, remove from available page linked list
+ pagePair->nextAvailable = 0;
+ TrampolineBlockPagePair *iteratorPair = headPagePairs[aMode];
+ while(iteratorPair && (iteratorPair->nextAvailablePage != pagePair))
+ iteratorPair = iteratorPair->nextAvailablePage;
+ if (iteratorPair) {
+ iteratorPair->nextAvailablePage = pagePair->nextAvailablePage;
+ pagePair->nextAvailablePage = NULL;
+ }
+ } else {
+ // empty slot at index contains pointer to next available index
+ pagePair->nextAvailable = nextAvailableIndex;
+ }
+
+ *payloadAddress = block;
+ IMP trampoline = _trampolineAddressAtIndex(pagePair, index);
+
+ return trampoline;
+}
+
+static ArgumentMode _argumentModeForBlock(void *block) {
+ ArgumentMode aMode = ReturnValueInRegisterArgumentMode;
+
+ if (_Block_has_signature(block) && _Block_use_stret(block))
+ aMode = ReturnValueOnStackArgumentMode;
+
+ return aMode;
+}
+
+#pragma mark Public API
+IMP imp_implementationWithBlock(void *block)
+{
+ block = Block_copy(block);
+ _lock();
+ IMP returnIMP = _imp_implementationWithBlockNoCopy(_argumentModeForBlock(block), block);
+ _unlock();
+ return returnIMP;
+}
+
+
+void *imp_getBlock(IMP anImp) {
+ uint32_t index;
+ TrampolineBlockPagePair *pagePair;
+
+ if (!anImp) return NULL;
+
+ _lock();
+
+ pagePair = _pagePairAndIndexContainingIMP(anImp, &index, NULL);
+
+ if (!pagePair) {
+ _unlock();
+ return NULL;
+ }
+
+ void *potentialBlock = *_payloadAddressAtIndex(pagePair, index);
+
+ if ((uintptr_t) potentialBlock == (uintptr_t) LAST_SLOT_MARKER) {
+ _unlock();
+ return NULL;
+ }
+
+ if ((uintptr_t) potentialBlock < (uintptr_t) _slotsPerPagePair()) {
+ _unlock();
+ return NULL;
+ }
+
+ _unlock();
+
+ return potentialBlock;
+}
+
+BOOL imp_removeBlock(IMP anImp) {
+ TrampolineBlockPagePair *pagePair;
+ TrampolineBlockPagePair *headPagePair;
+ uint32_t index;
+
+ if (!anImp) return NO;
+
+ _lock();
+ pagePair = _pagePairAndIndexContainingIMP(anImp, &index, &headPagePair);
+
+ if (!pagePair) {
+ _unlock();
+ return NO;
+ }
+
+ void **payloadAddress = _payloadAddressAtIndex(pagePair, index);
+ void *block = *payloadAddress;
+ // block is released below
+
+ if (pagePair->nextAvailable) {
+ *payloadAddress = (void *) (uintptr_t) pagePair->nextAvailable;
+ pagePair->nextAvailable = index;
+ } else {
+ *payloadAddress = (void *) (uintptr_t) LAST_SLOT_MARKER; // nada after this one is used
+ pagePair->nextAvailable = index;
+ }
+
+ // make sure this page is on available linked list
+ TrampolineBlockPagePair *pagePairIterator = headPagePair;
+
+ // see if pagePair is the next available page for any existing pages
+ while(pagePairIterator->nextAvailablePage && (pagePairIterator->nextAvailablePage != pagePair))
+ pagePairIterator = pagePairIterator->nextAvailablePage;
+
+ if (! pagePairIterator->nextAvailablePage) { // if iteration stopped because nextAvail was NULL
+ // add to end of list.
+ pagePairIterator->nextAvailablePage = pagePair;
+ pagePair->nextAvailablePage = NULL;
+ }
+
+ _unlock();
+ Block_release(block);
+ return YES;
+}
#if __OBJC2__
#ifndef __LP64__
-#define CACHE_HASH(sel, mask) (((uintptr_t)(sel)>>2) & (mask))
+# define CACHE_HASH(sel, mask) (((uintptr_t)(sel)>>2) & (mask))
#else
-#define CACHE_HASH(sel, mask) (((unsigned int)((uintptr_t)(sel)>>3)) & (mask))
+# define CACHE_HASH(sel, mask) (((unsigned int)((uintptr_t)(sel)>>0)) & (mask))
#endif
struct objc_cache {
/* Cache filling and flushing instrumentation */
-static int totalCacheFills NOBSS = 0;
+static int totalCacheFills = 0;
#ifdef OBJC_INSTRUMENTED
-__private_extern__ unsigned int LinearFlushCachesCount = 0;
-__private_extern__ unsigned int LinearFlushCachesVisitedCount = 0;
-__private_extern__ unsigned int MaxLinearFlushCachesVisitedCount = 0;
-__private_extern__ unsigned int NonlinearFlushCachesCount = 0;
-__private_extern__ unsigned int NonlinearFlushCachesClassCount = 0;
-__private_extern__ unsigned int NonlinearFlushCachesVisitedCount = 0;
-__private_extern__ unsigned int MaxNonlinearFlushCachesVisitedCount = 0;
-__private_extern__ unsigned int IdealFlushCachesCount = 0;
-__private_extern__ unsigned int MaxIdealFlushCachesCount = 0;
+PRIVATE_EXTERN unsigned int LinearFlushCachesCount = 0;
+PRIVATE_EXTERN unsigned int LinearFlushCachesVisitedCount = 0;
+PRIVATE_EXTERN unsigned int MaxLinearFlushCachesVisitedCount = 0;
+PRIVATE_EXTERN unsigned int NonlinearFlushCachesCount = 0;
+PRIVATE_EXTERN unsigned int NonlinearFlushCachesClassCount = 0;
+PRIVATE_EXTERN unsigned int NonlinearFlushCachesVisitedCount = 0;
+PRIVATE_EXTERN unsigned int MaxNonlinearFlushCachesVisitedCount = 0;
+PRIVATE_EXTERN unsigned int IdealFlushCachesCount = 0;
+PRIVATE_EXTERN unsigned int MaxIdealFlushCachesCount = 0;
#endif
* messenger.
***********************************************************************/
-#ifndef OBJC_INSTRUMENTED
-const struct objc_cache _objc_empty_cache =
-{
- 0, // mask
- 0, // occupied
- { NULL } // buckets
-};
-#else
-// OBJC_INSTRUMENTED requires writable data immediately following emptyCache.
struct objc_cache _objc_empty_cache =
{
0, // mask
0, // occupied
{ NULL } // buckets
};
+#ifdef OBJC_INSTRUMENTED
CacheInstrumentation emptyCacheInstrumentation = {0};
#endif
static int _collecting_in_critical(void);
static void _garbage_make_room(void);
-static void _cache_collect_free(void *data, size_t size, BOOL tryCollect);
+static void _cache_collect_free(void *data, size_t size);
#if defined(CACHE_ALLOCATOR)
static BOOL cache_allocator_is_block(void *block);
#elif !defined(CACHE_ALLOCATOR)
// fixme cache allocator implementation isn't 64-bit clean
new_cache = _calloc_internal(size, 1);
- new_cache->mask = slotCount - 1;
+ new_cache->mask = (unsigned int)(slotCount - 1);
#else
if (size < CACHE_ALLOCATOR_MIN || UseInternalZone) {
new_cache = _calloc_internal(size, 1);
* forward:: entries in the cache ARE freed.
* Cache locks: cacheUpdateLock must NOT be held by the caller.
**********************************************************************/
-__private_extern__ void _cache_free(Cache cache)
+PRIVATE_EXTERN void _cache_free(Cache cache)
{
unsigned int i;
// Deallocate "forward::" entry
if (oldEntry->imp == &_objc_msgForward_internal) {
- _cache_collect_free (oldEntry, sizeof(cache_entry), NO);
+ _cache_collect_free (oldEntry, sizeof(cache_entry));
}
}
for (index = 0; index < old_cache->mask + 1; index++) {
cache_entry *entry = (cache_entry *)old_cache->buckets[index];
if (entry && entry->imp == &_objc_msgForward_internal) {
- _cache_collect_free (entry, sizeof(cache_entry), NO);
+ _cache_collect_free (entry, sizeof(cache_entry));
}
}
_class_setCache(cls, new_cache);
// Deallocate old cache, try freeing all the garbage
- _cache_collect_free (old_cache, old_cache->mask * sizeof(cache_entry *), YES);
+ _cache_collect_free (old_cache, old_cache->mask * sizeof(cache_entry *));
+ _cache_collect(false);
+
return new_cache;
}
*
* Cache locks: cacheUpdateLock must not be held.
**********************************************************************/
-__private_extern__ BOOL _cache_fill(Class cls, Method smt, SEL sel)
+PRIVATE_EXTERN BOOL _cache_fill(Class cls, Method smt, SEL sel)
{
uintptr_t newOccupied;
uintptr_t index;
newOccupied = cache->occupied + 1;
if ((newOccupied * 4) <= (cache->mask + 1) * 3) {
// Cache is less than 3/4 full.
- cache->occupied = newOccupied;
+ cache->occupied = (unsigned int)newOccupied;
} else {
// Cache is too full. Expand it.
cache = _cache_expand (cls);
cache->occupied += 1;
}
- // Insert the new entry. This can be done by either:
- // (a) Scanning for the first unused spot. Easy!
- // (b) Opening up an unused spot by sliding existing
- // entries down by one. The benefit of this
- // extra work is that it puts the most recently
- // loaded entries closest to where the selector
- // hash starts the search.
- //
- // The loop is a little more complicated because there
- // are two kinds of entries, so there have to be two ways
- // to slide them.
+ // Scan for the first unused slot and insert there.
+ // There is guaranteed to be an empty slot because the
+ // minimum size is 4 and we resized at 3/4 full.
buckets = (cache_entry **)cache->buckets;
- index = CACHE_HASH(sel, cache->mask);
- for (;;)
+ for (index = CACHE_HASH(sel, cache->mask);
+ buckets[index] != NULL;
+ index = (index+1) & cache->mask)
{
- // Slide existing entries down by one
- cache_entry *saveEntry;
-
- // Copy current entry to a local
- saveEntry = buckets[index];
-
- // Copy previous entry (or new entry) to current slot
- buckets[index] = entry;
-
- // Done if current slot had been invalid
- if (saveEntry == NULL)
- break;
-
- // Prepare to copy saved value into next slot
- entry = saveEntry;
-
- // Move on to next slot
- index += 1;
- index &= cache->mask;
+ // empty
}
+ buckets[index] = entry;
mutex_unlock(&cacheUpdateLock);
* Called from class_respondsToMethod and _class_lookupMethodAndLoadCache.
* Cache locks: cacheUpdateLock must not be held.
**********************************************************************/
-__private_extern__ void _cache_addForwardEntry(Class cls, SEL sel)
+PRIVATE_EXTERN void _cache_addForwardEntry(Class cls, SEL sel)
{
cache_entry *smt;
}
+/***********************************************************************
+* _cache_addIgnoredEntry
+* Add an entry for the ignored selector to cls's method cache.
+* Does nothing if the cache addition fails for any reason.
+* Returns the ignored IMP.
+* Cache locks: cacheUpdateLock must not be held.
+**********************************************************************/
+#if SUPPORT_GC && !SUPPORT_IGNORED_SELECTOR_CONSTANT
+static cache_entry *alloc_ignored_entries(void)
+{
+ cache_entry *e = malloc(5 * sizeof(cache_entry));
+ e[0] = (cache_entry){ @selector(retain), 0,(IMP)&_objc_ignored_method};
+ e[1] = (cache_entry){ @selector(release), 0,(IMP)&_objc_ignored_method};
+ e[2] = (cache_entry){ @selector(autorelease),0,(IMP)&_objc_ignored_method};
+ e[3] = (cache_entry){ @selector(retainCount),0,(IMP)&_objc_ignored_method};
+ e[4] = (cache_entry){ @selector(dealloc), 0,(IMP)&_objc_ignored_method};
+ return e;
+}
+#endif
+
+PRIVATE_EXTERN IMP _cache_addIgnoredEntry(Class cls, SEL sel)
+{
+ cache_entry *entryp = NULL;
+
+#if !SUPPORT_GC
+ _objc_fatal("selector ignored with GC off");
+#elif SUPPORT_IGNORED_SELECTOR_CONSTANT
+ static cache_entry entry = { (SEL)kIgnore, 0, (IMP)&_objc_ignored_method };
+ entryp = &entry;
+ assert(sel == (SEL)kIgnore);
+#else
+ // hack
+ int i;
+ static cache_entry *entries;
+ INIT_ONCE_PTR(entries, alloc_ignored_entries(), free(v));
+
+ assert(ignoreSelector(sel));
+ for (i = 0; i < 5; i++) {
+ if (sel == entries[i].name) {
+ entryp = &entries[i];
+ break;
+ }
+ }
+ if (!entryp) _objc_fatal("selector %s (%p) is not ignored",
+ sel_getName(sel), sel);
+#endif
+
+ _cache_fill(cls, (Method)entryp, sel);
+ return entryp->imp;
+}
+
+
/***********************************************************************
* _cache_flush. Invalidate all valid entries in the given class' cache.
*
#if __OBJC2__
static
#else
-__private_extern__
+PRIVATE_EXTERN
#endif
void _cache_flush(Class cls)
{
// Deallocate "forward::" entry
if (oldEntry && oldEntry->imp == &_objc_msgForward_internal)
- _cache_collect_free (oldEntry, sizeof(cache_entry), NO);
+ _cache_collect_free (oldEntry, sizeof(cache_entry));
}
// Clear the valid-entry counter
* flush_cache. Flushes the instance method cache for class cls only.
* Use flush_caches() if cls might have in-use subclasses.
**********************************************************************/
-__private_extern__ void flush_cache(Class cls)
+PRIVATE_EXTERN void flush_cache(Class cls)
{
if (cls) {
mutex_lock(&cacheUpdateLock);
#if !TARGET_OS_WIN32
-// A sentinal (magic value) to report bad thread_get_state status
-#define PC_SENTINEL 0
+// A sentinel (magic value) to report bad thread_get_state status.
+// Must not be a valid PC.
+// Must not be zero - thread_get_state() on a new thread returns PC == 0.
+#define PC_SENTINEL 1
// UNIX03 compliance hack (4508809)
#if !__DARWIN_UNIX03
kern_return_t okay = thread_get_state (thread, i386_THREAD_STATE, (thread_state_t)&state, &count);
return (okay == KERN_SUCCESS) ? state.__eip : PC_SENTINEL;
}
-#elif defined(__ppc__)
-{
- ppc_thread_state_t state;
- unsigned int count = PPC_THREAD_STATE_COUNT;
- kern_return_t okay = thread_get_state (thread, PPC_THREAD_STATE, (thread_state_t)&state, &count);
- return (okay == KERN_SUCCESS) ? state.__srr0 : PC_SENTINEL;
-}
-#elif defined(__ppc64__)
-{
- ppc_thread_state64_t state;
- unsigned int count = PPC_THREAD_STATE64_COUNT;
- kern_return_t okay = thread_get_state (thread, PPC_THREAD_STATE64, (thread_state_t)&state, &count);
- return (okay == KERN_SUCCESS) ? state.__srr0 : PC_SENTINEL;
-}
#elif defined(__x86_64__)
{
x86_thread_state64_t state;
* precisely the block's size.
* Cache locks: cacheUpdateLock must be held by the caller.
**********************************************************************/
-static void _cache_collect_free(void *data, size_t size, BOOL tryCollect)
+static void _cache_collect_free(void *data, size_t size)
{
mutex_assert_locked(&cacheUpdateLock);
- // Insert new element in garbage list
- // Note that we do this even if we end up free'ing everything
_garbage_make_room ();
garbage_byte_size += size;
garbage_refs[garbage_count++] = data;
+}
- // Done if caller says not to clean up
- if (!tryCollect) return;
+
+/***********************************************************************
+* _cache_collect. Try to free accumulated dead caches.
+* collectALot tries harder to free memory.
+* Cache locks: cacheUpdateLock must be held by the caller.
+**********************************************************************/
+PRIVATE_EXTERN void _cache_collect(bool collectALot)
+{
+ mutex_assert_locked(&cacheUpdateLock);
// Done if the garbage is not full
- if (garbage_byte_size < garbage_threshold) {
- // if (PrintCaches) {
- // _objc_inform ("CACHES: not collecting; not enough garbage (%zu < %zu)", garbage_byte_size, garbage_threshold);
- // }
+ if (garbage_byte_size < garbage_threshold && !collectALot) {
return;
}
- // Synchronize garbage collection with objc_msgSend and other cache readers
- if (!_collecting_in_critical ()) {
- // No cache readers in progress - garbage is now deletable
-
- // Log our progress
- if (PrintCaches) {
- cache_collections++;
- _objc_inform ("CACHES: COLLECTING %zu bytes (%zu regions, %zu allocations, %zu collections)", garbage_byte_size, cache_allocator_regions, cache_allocations, cache_collections);
- }
-
- // Dispose all refs now in the garbage
- while (garbage_count--) {
- _cache_free_block(garbage_refs[garbage_count]);
+ // Synchronize collection with objc_msgSend and other cache readers
+ if (!collectALot) {
+ if (_collecting_in_critical ()) {
+ // objc_msgSend (or other cache reader) is currently looking in
+ // the cache and might still be using some garbage.
+ if (PrintCaches) {
+ _objc_inform ("CACHES: not collecting; "
+ "objc_msgSend in progress");
+ }
+ return;
}
+ }
+ else {
+ // No excuses.
+ while (_collecting_in_critical())
+ ;
+ }
+
+ // No cache readers in progress - garbage is now deletable
- // Clear the garbage count and total size indicator
- garbage_count = 0;
- garbage_byte_size = 0;
+ // Log our progress
+ if (PrintCaches) {
+ cache_collections++;
+ _objc_inform ("CACHES: COLLECTING %zu bytes (%zu regions, %zu allocations, %zu collections)", garbage_byte_size, cache_allocator_regions, cache_allocations, cache_collections);
}
- else {
- // objc_msgSend (or other cache reader) is currently looking in the
- // cache and might still be using some garbage.
- if (PrintCaches) {
- _objc_inform ("CACHES: not collecting; objc_msgSend in progress");
- }
+
+ // Dispose all refs now in the garbage
+ while (garbage_count--) {
+ _cache_free_block(garbage_refs[garbage_count]);
}
+
+ // Clear the garbage count and total size indicator
+ garbage_count = 0;
+ garbage_byte_size = 0;
if (PrintCaches) {
int i;
if (size < CACHE_REGION_SIZE) size = CACHE_REGION_SIZE;
// Allocate the region
- addr = 0;
- vm_allocate(mach_task_self(), &addr, size, 1);
+ addr = (vm_address_t)calloc(size, 1);
newRegion->start = (cache_allocator_block *)addr;
newRegion->end = (cache_allocator_block *)(addr + size);
/***********************************************************************
* _class_printMethodCaches.
**********************************************************************/
-__private_extern__ void _class_printMethodCaches(Class cls)
+PRIVATE_EXTERN void _class_printMethodCaches(Class cls)
{
if (_cache_isEmpty(_class_getCache(cls))) {
printf("no instance-method cache for class %s\n", _class_getName(cls));
/*
- * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ * Copyright (c) 1999-2009 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#if !__OBJC2__
-#define OLD 1
#include "objc-private.h"
+#include "objc-runtime-old.h"
// Freed objects have their isa set to point to this dummy class.
// This avoids the need to check for Nil classes in the messenger.
static inline struct old_method *_findNamedMethodInList(struct old_method_list * mlist, const char *meth_name) {
int i;
if (!mlist) return NULL;
+ if (ignoreSelectorNamed(meth_name)) return NULL;
for (i = 0; i < mlist->method_count; i++) {
struct old_method *m = &mlist->method_list[i];
- if (m->method_name == (SEL)kRTAddress_ignoredSelector) continue;
- if (*((const char *)m->method_name) == *meth_name && 0 == strcmp((const char *)(m->method_name), meth_name)) {
+ if (0 == strcmp((const char *)(m->method_name), meth_name)) {
return m;
}
}
static void *fixed_up_method_list = OBJC_FIXED_UP;
// sel_init() decided that selectors in the dyld shared cache are untrustworthy
-__private_extern__ void disableSelectorPreoptimization(void)
+PRIVATE_EXTERN void disableSharedCacheOptimizations(void)
{
fixed_up_method_list = OBJC_FIXED_UP_outside_dyld;
}
method->method_name =
sel_registerNameNoLock((const char *)method->method_name, isBundle); // Always copy selector data from bundles.
-#ifndef NO_GC
- if (method->method_name == (SEL)kRTAddress_ignoredSelector) {
+ if (ignoreSelector(method->method_name)) {
method->method_imp = (IMP)&_objc_ignored_method;
}
-#endif
}
sel_unlock();
mlist->obsolete = fixed_up_method_list;
// fixme for gc debugging temporary use
-__private_extern__ IMP findIMPInClass(struct old_class *cls, SEL sel)
+PRIVATE_EXTERN IMP findIMPInClass(struct old_class *cls, SEL sel)
{
struct old_method *m = _findMethodInClass(cls, sel);
if (m) return m->method_imp;
/***********************************************************************
* ABI-specific lookUpMethod helpers.
**********************************************************************/
-__private_extern__ void lockForMethodLookup(void)
+PRIVATE_EXTERN void lockForMethodLookup(void)
{
mutex_lock(&methodListLock);
}
-__private_extern__ void unlockForMethodLookup(void)
+PRIVATE_EXTERN void unlockForMethodLookup(void)
{
mutex_unlock(&methodListLock);
}
-__private_extern__ IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init)
+PRIVATE_EXTERN IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init)
{
mutex_assert_unlocked(&methodListLock);
/***********************************************************************
* class_getVariable. Return the named instance variable.
**********************************************************************/
-__private_extern__
-Ivar _class_getVariable(Class cls_gen, const char *name)
+PRIVATE_EXTERN
+Ivar _class_getVariable(Class cls_gen, const char *name, Class *memberOf)
{
- struct old_class *cls = _class_asOld(cls_gen);
+ struct old_class *cls = oldcls(cls_gen);
for (; cls != Nil; cls = cls->super_class) {
int i;
// (e.g. for anonymous bit fields).
struct old_ivar *ivar = &cls->ivars->ivar_list[i];
if (ivar->ivar_name && 0 == strcmp(name, ivar->ivar_name)) {
+ if (memberOf) *memberOf = (Class)cls;
return (Ivar)ivar;
}
}
}
-/***********************************************************************
-* class_getPropertyList. Return the class's property list
-* Locking: classLock must be held by the caller
-**********************************************************************/
-static struct objc_property_list *
+PRIVATE_EXTERN struct old_property *
+property_list_nth(const struct old_property_list *plist, uint32_t i)
+{
+ return (struct old_property *)(i*plist->entsize + (char *)&plist->first);
+}
+
+PRIVATE_EXTERN struct old_property **
+copyPropertyList(struct old_property_list *plist, unsigned int *outCount)
+{
+ struct old_property **result = NULL;
+ unsigned int count = 0;
+
+ if (plist) {
+ count = plist->count;
+ }
+
+ if (count > 0) {
+ unsigned int i;
+ result = malloc((count+1) * sizeof(struct old_property *));
+
+ for (i = 0; i < count; i++) {
+ result[i] = property_list_nth(plist, i);
+ }
+ result[i] = NULL;
+ }
+
+ if (outCount) *outCount = count;
+ return result;
+}
+
+
+static struct old_property_list *
nextPropertyList(struct old_class *cls, uintptr_t *indexp)
{
- struct objc_property_list *result = NULL;
+ struct old_property_list *result = NULL;
mutex_assert_locked(&classLock);
if (! ((cls->info & CLS_EXT) && cls->ext)) {
} else if (cls->info & CLS_NO_PROPERTY_ARRAY) {
// Only one property list
if (*indexp == 0) {
- result = (struct objc_property_list *)cls->ext->propertyLists;
+ result = (struct old_property_list *)cls->ext->propertyLists;
} else {
result = NULL;
}
* class_getIvarLayout
* NULL means all-scanned. "" means non-scanned.
**********************************************************************/
-const char *
+const uint8_t *
class_getIvarLayout(Class cls_gen)
{
- struct old_class *cls = _class_asOld(cls_gen);
+ struct old_class *cls = oldcls(cls_gen);
if (cls && (cls->info & CLS_EXT)) {
return cls->ivar_layout;
} else {
* class_getWeakIvarLayout
* NULL means no weak ivars.
**********************************************************************/
-const char *
+const uint8_t *
class_getWeakIvarLayout(Class cls_gen)
{
- struct old_class *cls = _class_asOld(cls_gen);
+ struct old_class *cls = oldcls(cls_gen);
if (cls && (cls->info & CLS_EXT) && cls->ext) {
return cls->ext->weak_ivar_layout;
} else {
* class_setIvarLayout
* NULL means all-scanned. "" means non-scanned.
**********************************************************************/
-void class_setIvarLayout(Class cls_gen, const char *layout)
+void class_setIvarLayout(Class cls_gen, const uint8_t *layout)
{
- struct old_class *cls = _class_asOld(cls_gen);
+ struct old_class *cls = oldcls(cls_gen);
if (!cls) return;
if (! (cls->info & CLS_EXT)) {
}
// fixme leak
- cls->ivar_layout = layout ? _strdup_internal(layout) : NULL;
+ cls->ivar_layout = _ustrdup_internal(layout);
}
+// SPI: Instance-specific object layout.
+
+void _class_setIvarLayoutAccessor(Class cls_gen, const uint8_t* (*accessor) (id object)) {
+ struct old_class *cls = oldcls(cls_gen);
+ if (!cls) return;
+
+ if (! (cls->info & CLS_EXT)) {
+ _objc_inform("class '%s' needs to be recompiled", cls->name);
+ return;
+ }
+
+ // fixme leak
+ cls->ivar_layout = (const uint8_t *)accessor;
+ _class_setInfo(cls_gen, CLS_HAS_INSTANCE_SPECIFIC_LAYOUT);
+}
+
+const uint8_t *_object_getIvarLayout(Class cls_gen, id object) {
+ struct old_class *cls = oldcls(cls_gen);
+ if (cls && (cls->info & CLS_EXT)) {
+ const uint8_t* layout = cls->ivar_layout;
+ if (cls->info & CLS_HAS_INSTANCE_SPECIFIC_LAYOUT) {
+ const uint8_t* (*accessor) (id object) = (const uint8_t* (*)(id))layout;
+ layout = accessor(object);
+ }
+ return layout;
+ } else {
+ return NULL;
+ }
+}
/***********************************************************************
* class_setWeakIvarLayout
* NULL means no weak ivars.
**********************************************************************/
-void class_setWeakIvarLayout(Class cls_gen, const char *layout)
+void class_setWeakIvarLayout(Class cls_gen, const uint8_t *layout)
{
- struct old_class *cls = _class_asOld(cls_gen);
+ struct old_class *cls = oldcls(cls_gen);
if (!cls) return;
mutex_lock(&classLock);
allocateExt(cls);
// fixme leak
- cls->ext->weak_ivar_layout = layout ? _strdup_internal(layout) : NULL;
+ cls->ext->weak_ivar_layout = _ustrdup_internal(layout);
mutex_unlock(&classLock);
}
* Atomically sets and clears some bits in cls's info field.
* set and clear must not overlap.
**********************************************************************/
-__private_extern__ void _class_changeInfo(Class cls, long set, long clear)
+PRIVATE_EXTERN void _class_changeInfo(Class cls, long set, long clear)
{
- struct old_class *old = _class_asOld(cls);
+ struct old_class *old = oldcls(cls);
long newinfo;
long oldinfo;
do {
* _class_getInfo
* Returns YES iff all set bits in get are also set in cls's info field.
**********************************************************************/
-__private_extern__ BOOL _class_getInfo(Class cls, int get)
+PRIVATE_EXTERN BOOL _class_getInfo(Class cls, int get)
{
- struct old_class *old = _class_asOld(cls);
+ struct old_class *old = oldcls(cls);
return ((old->info & get) == get) ? YES : NO;
}
* _class_setInfo
* Atomically sets some bits in cls's info field.
**********************************************************************/
-__private_extern__ void _class_setInfo(Class cls, long set)
+PRIVATE_EXTERN void _class_setInfo(Class cls, long set)
{
_class_changeInfo(cls, set, 0);
}
* _class_clearInfo
* Atomically clears some bits in cls's info field.
**********************************************************************/
-__private_extern__ void _class_clearInfo(Class cls, long clear)
+PRIVATE_EXTERN void _class_clearInfo(Class cls, long clear)
{
_class_changeInfo(cls, 0, clear);
}
* Return YES if cls is currently being initialized.
* The initializing bit is stored in the metaclass only.
**********************************************************************/
-__private_extern__ BOOL _class_isInitializing(Class cls)
+PRIVATE_EXTERN BOOL _class_isInitializing(Class cls)
{
return _class_getInfo(_class_getMeta(cls), CLS_INITIALIZING);
}
* Return YES if cls is already initialized.
* The initialized bit is stored in the metaclass only.
**********************************************************************/
-__private_extern__ BOOL _class_isInitialized(Class cls)
+PRIVATE_EXTERN BOOL _class_isInitialized(Class cls)
{
return _class_getInfo(_class_getMeta(cls), CLS_INITIALIZED);
}
* setInitializing
* Mark cls as initialization in progress.
**********************************************************************/
-__private_extern__ void _class_setInitializing(Class cls)
+PRIVATE_EXTERN void _class_setInitializing(Class cls)
{
_class_setInfo(_class_getMeta(cls), CLS_INITIALIZING);
}
* setInitialized
* Atomically mark cls as initialized and not initializing.
**********************************************************************/
-__private_extern__ void _class_setInitialized(Class cls)
+PRIVATE_EXTERN void _class_setInitialized(Class cls)
{
_class_changeInfo(_class_getMeta(cls), CLS_INITIALIZED, CLS_INITIALIZING);
}
}
-__private_extern__ Class _class_getMeta(Class cls)
+PRIVATE_EXTERN Class _class_getMeta(Class cls)
{
if (_class_getInfo(cls, CLS_META)) return cls;
else return ((id)cls)->isa;
}
-__private_extern__ BOOL _class_isMetaClass(Class cls)
+PRIVATE_EXTERN BOOL _class_isMetaClass(Class cls)
{
if (!cls) return NO;
return _class_getInfo(cls, CLS_META);
* Return the ordinary class for this class or metaclass.
* Used by +initialize.
**********************************************************************/
-__private_extern__ Class _class_getNonMetaClass(Class cls)
+PRIVATE_EXTERN Class _class_getNonMetaClass(Class cls)
{
// fixme ick
if (_class_isMetaClass(cls)) {
}
-__private_extern__ Class _class_getSuperclass(Class cls)
+PRIVATE_EXTERN Class _class_getSuperclass(Class cls)
{
if (!cls) return nil;
return (Class)cls->super_class;
}
-__private_extern__ Cache _class_getCache(Class cls)
+PRIVATE_EXTERN Cache _class_getCache(Class cls)
{
return cls->cache;
}
-__private_extern__ void _class_setCache(Class cls, Cache cache)
+PRIVATE_EXTERN void _class_setCache(Class cls, Cache cache)
{
cls->cache = cache;
}
-__private_extern__ size_t _class_getInstanceSize(Class cls)
+PRIVATE_EXTERN size_t _class_getInstanceSize(Class cls)
{
if (!cls) return 0;
- return cls->instance_size;
+ return (cls->instance_size + WORD_MASK) & ~WORD_MASK;
}
-__private_extern__ const char * _class_getName(Class cls)
+PRIVATE_EXTERN const char * _class_getName(Class cls)
{
if (!cls) return "nil";
return cls->name;
-__private_extern__ const char *_category_getName(Category cat)
+PRIVATE_EXTERN const char *_category_getName(Category cat)
{
- return _category_asOld(cat)->category_name;
+ return oldcategory(cat)->category_name;
}
-__private_extern__ const char *_category_getClassName(Category cat)
+PRIVATE_EXTERN const char *_category_getClassName(Category cat)
{
- return _category_asOld(cat)->class_name;
+ return oldcategory(cat)->class_name;
}
-__private_extern__ Class _category_getClass(Category cat)
+PRIVATE_EXTERN Class _category_getClass(Category cat)
{
- return (Class)objc_getClass(_category_asOld(cat)->class_name);
+ return (Class)objc_getClass(oldcategory(cat)->class_name);
}
-__private_extern__ IMP _category_getLoadMethod(Category cat)
+PRIVATE_EXTERN IMP _category_getLoadMethod(Category cat)
{
- struct old_method_list *mlist = _category_asOld(cat)->class_methods;
+ struct old_method_list *mlist = oldcategory(cat)->class_methods;
if (mlist) {
return lookupNamedMethodInMethodList(mlist, "load");
} else {
OBJC_WARN_DEPRECATED;
mutex_lock(&methodListLock);
- result = nextMethodList(_class_asOld(cls), it);
+ result = nextMethodList(oldcls(cls), it);
mutex_unlock(&methodListLock);
return (struct objc_method_list *)result;
}
// Add the methods.
mutex_lock(&methodListLock);
- _objc_insertMethods(_class_asOld(cls), (struct old_method_list *)meths, NULL);
+ _objc_insertMethods(oldcls(cls), (struct old_method_list *)meths, NULL);
mutex_unlock(&methodListLock);
// Must flush when dynamically adding methods. No need to flush
// Remove the methods
mutex_lock(&methodListLock);
- _objc_removeMethods(_class_asOld(cls), (struct old_method_list *)meths);
+ _objc_removeMethods(oldcls(cls), (struct old_method_list *)meths);
mutex_unlock(&methodListLock);
// Must flush when dynamically removing methods. No need to flush
* without fixing up the entire method list.
* The class is not yet in use, so methodListLock is not taken.
**********************************************************************/
-__private_extern__ IMP lookupNamedMethodInMethodList(struct old_method_list *mlist, const char *meth_name)
+PRIVATE_EXTERN IMP lookupNamedMethodInMethodList(struct old_method_list *mlist, const char *meth_name)
{
struct old_method *m;
m = meth_name ? _findNamedMethodInList(mlist, meth_name) : NULL;
return (m ? m->method_imp : NULL);
}
-__private_extern__ Method _class_getMethod(Class cls, SEL sel)
+PRIVATE_EXTERN Method _class_getMethod(Class cls, SEL sel)
{
Method result;
mutex_lock(&methodListLock);
- result = (Method)_getMethod(_class_asOld(cls), sel);
+ result = (Method)_getMethod(oldcls(cls), sel);
mutex_unlock(&methodListLock);
return result;
}
-__private_extern__ Method _class_getMethodNoSuper(Class cls, SEL sel)
+PRIVATE_EXTERN Method _class_getMethodNoSuper(Class cls, SEL sel)
{
Method result;
mutex_lock(&methodListLock);
- result = (Method)_findMethodInClass(_class_asOld(cls), sel);
+ result = (Method)_findMethodInClass(oldcls(cls), sel);
mutex_unlock(&methodListLock);
return result;
}
-__private_extern__ Method _class_getMethodNoSuper_nolock(Class cls, SEL sel)
+PRIVATE_EXTERN Method _class_getMethodNoSuper_nolock(Class cls, SEL sel)
{
mutex_assert_locked(&methodListLock);
- return (Method)_findMethodInClass(_class_asOld(cls), sel);
+ return (Method)_findMethodInClass(oldcls(cls), sel);
}
/***********************************************************************
* objc_getOrigClass.
**********************************************************************/
-__private_extern__ Class _objc_getOrigClass(const char *name)
+PRIVATE_EXTERN Class _objc_getOrigClass(const char *name)
{
Class ret;
* Used by class_poseAs and objc_setFutureClass
* classLock must be locked.
**********************************************************************/
-__private_extern__
+PRIVATE_EXTERN
void change_class_references(struct old_class *imposter,
struct old_class *original,
struct old_class *copy,
**********************************************************************/
Class class_poseAs(Class imposter_gen, Class original_gen)
{
- struct old_class *imposter = _class_asOld(imposter_gen);
- struct old_class *original = _class_asOld(original_gen);
+ struct old_class *imposter = oldcls(imposter_gen);
+ struct old_class *original = oldcls(original_gen);
char * imposterNamePtr;
struct old_class * copy;
*
* Specifying Nil for the class "all classes."
**********************************************************************/
-__private_extern__ void flush_caches(Class target_gen, BOOL flush_meta)
+PRIVATE_EXTERN void flush_caches(Class target_gen, BOOL flush_meta)
{
NXHashState state;
- struct old_class *target = _class_asOld(target_gen);
+ struct old_class *target = oldcls(target_gen);
struct old_class *clsObject;
#ifdef OBJC_INSTRUMENTED
unsigned int classesVisited;
* CLS_FLUSH_CACHE (and all subclasses thereof)
* fixme instrument
**********************************************************************/
-__private_extern__ void flush_marked_caches(void)
+PRIVATE_EXTERN void flush_marked_caches(void)
{
struct old_class *cls;
struct old_class *supercls;
}
-__private_extern__ BOOL _class_hasLoadMethod(Class cls)
+PRIVATE_EXTERN BOOL _class_hasLoadMethod(Class cls)
{
if (oldcls(cls)->isa->info & CLS_HAS_LOAD_METHOD) return YES;
return (_class_getLoadMethod_nocheck(oldcls(cls)) ? YES : NO);
* _class_getLoadMethod
* Returns cls's +load implementation, or NULL if it doesn't have one.
**********************************************************************/
-__private_extern__ IMP _class_getLoadMethod(Class cls_gen)
+PRIVATE_EXTERN IMP _class_getLoadMethod(Class cls_gen)
{
- struct old_class *cls = _class_asOld(cls_gen);
+ struct old_class *cls = oldcls(cls_gen);
if (cls->isa->info & CLS_HAS_LOAD_METHOD) {
return _class_getLoadMethod_nocheck(cls);
}
}
-__private_extern__ BOOL _class_shouldGrowCache(Class cls)
+PRIVATE_EXTERN BOOL _class_shouldGrowCache(Class cls)
{
return _class_getInfo(cls, CLS_GROW_CACHE);
}
-__private_extern__ void _class_setGrowCache(Class cls, BOOL grow)
+PRIVATE_EXTERN void _class_setGrowCache(Class cls, BOOL grow)
{
if (grow) _class_setInfo(cls, CLS_GROW_CACHE);
else _class_clearInfo(cls, CLS_GROW_CACHE);
}
-__private_extern__ BOOL _class_hasCxxStructorsNoSuper(Class cls)
+PRIVATE_EXTERN BOOL _class_hasCxxStructors(Class cls)
{
+ // this DOES check superclasses too, because set_superclass
+ // propagates the flag from the superclass.
return _class_getInfo(cls, CLS_HAS_CXX_STRUCTORS);
}
-__private_extern__ BOOL _class_shouldFinalizeOnMainThread(Class cls) {
+PRIVATE_EXTERN BOOL _class_shouldFinalizeOnMainThread(Class cls) {
return _class_getInfo(cls, CLS_FINALIZE_ON_MAIN_THREAD);
}
-__private_extern__ void _class_setFinalizeOnMainThread(Class cls) {
+PRIVATE_EXTERN void _class_setFinalizeOnMainThread(Class cls) {
_class_setInfo(cls, CLS_FINALIZE_ON_MAIN_THREAD);
}
-__private_extern__ BOOL _class_instancesHaveAssociatedObjects(Class cls) {
+PRIVATE_EXTERN BOOL _class_instancesHaveAssociatedObjects(Class cls) {
return _class_getInfo(cls, CLS_INSTANCES_HAVE_ASSOCIATED_OBJECTS);
}
-__private_extern__ void _class_assertInstancesHaveAssociatedObjects(Class cls) {
+PRIVATE_EXTERN void _class_setInstancesHaveAssociatedObjects(Class cls) {
_class_setInfo(cls, CLS_INSTANCES_HAVE_ASSOCIATED_OBJECTS);
}
-__private_extern__ struct old_ivar *_ivar_asOld(Ivar ivar)
+BOOL _class_usesAutomaticRetainRelease(Class cls)
{
- return (struct old_ivar *)ivar;
+ return NO;
+}
+
+PRIVATE_EXTERN uint32_t _class_getInstanceStart(Class cls)
+{
+ _objc_fatal("_class_getInstanceStart() unimplemented for fragile instance variables");
+ return 0; // PCB: never used just provided for ARR consistency.
}
ptrdiff_t ivar_getOffset(Ivar ivar)
{
- return _ivar_asOld(ivar)->ivar_offset;
+ return oldivar(ivar)->ivar_offset;
}
const char *ivar_getName(Ivar ivar)
{
- return _ivar_asOld(ivar)->ivar_name;
+ return oldivar(ivar)->ivar_name;
}
const char *ivar_getTypeEncoding(Ivar ivar)
{
- return _ivar_asOld(ivar)->ivar_type;
+ return oldivar(ivar)->ivar_type;
}
IMP method_getImplementation(Method m)
{
if (!m) return NULL;
- return _method_asOld(m)->method_imp;
+ return oldmethod(m)->method_imp;
}
SEL method_getName(Method m)
{
if (!m) return NULL;
- return _method_asOld(m)->method_name;
+ return oldmethod(m)->method_name;
}
const char *method_getTypeEncoding(Method m)
{
if (!m) return NULL;
- return _method_asOld(m)->method_types;
+ return oldmethod(m)->method_types;
}
+unsigned int method_getSizeOfArguments(Method m)
+{
+ OBJC_WARN_DEPRECATED;
+ if (!m) return 0;
+ return encoding_getSizeOfArguments(method_getTypeEncoding(m));
+}
+
+unsigned int method_getArgumentInfo(Method m, int arg,
+ const char **type, int *offset)
+{
+ OBJC_WARN_DEPRECATED;
+ if (!m) return 0;
+ return encoding_getArgumentInfo(method_getTypeEncoding(m),
+ arg, type, offset);
+}
+
+
static OSSpinLock impLock = OS_SPINLOCK_INIT;
IMP method_setImplementation(Method m_gen, IMP imp)
{
IMP old;
- struct old_method *m = _method_asOld(m_gen);
+ struct old_method *m = oldmethod(m_gen);
if (!m) return NULL;
if (!imp) return NULL;
- if (m->method_name == (SEL)kIgnore) {
+ if (ignoreSelector(m->method_name)) {
// Ignored methods stay ignored
return m->method_imp;
}
void method_exchangeImplementations(Method m1_gen, Method m2_gen)
{
IMP m1_imp;
- struct old_method *m1 = _method_asOld(m1_gen);
- struct old_method *m2 = _method_asOld(m2_gen);
+ struct old_method *m1 = oldmethod(m1_gen);
+ struct old_method *m2 = oldmethod(m2_gen);
if (!m1 || !m2) return;
- if (m1->method_name == (SEL)kIgnore || m2->method_name == (SEL)kIgnore) {
+ if (ignoreSelector(m1->method_name) || ignoreSelector(m2->method_name)) {
// Ignored methods stay ignored. Now they're both ignored.
m1->method_imp = (IMP)&_objc_ignored_method;
m2->method_imp = (IMP)&_objc_ignored_method;
}
+const char *property_getName(objc_property_t prop)
+{
+ return oldproperty(prop)->name;
+}
+
+const char *property_getAttributes(objc_property_t prop)
+{
+ return oldproperty(prop)->attributes;
+}
+
+objc_property_attribute_t *property_copyAttributeList(objc_property_t prop,
+ unsigned int *outCount)
+{
+ if (!prop) {
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ objc_property_attribute_t *result;
+ mutex_lock(&classLock);
+ result = copyPropertyAttributeList(oldproperty(prop)->attributes,outCount);
+ mutex_unlock(&classLock);
+ return result;
+}
+
+char * property_copyAttributeValue(objc_property_t prop, const char *name)
+{
+ if (!prop || !name || *name == '\0') return NULL;
+
+ char *result;
+ mutex_lock(&classLock);
+ result = copyPropertyAttributeValue(oldproperty(prop)->attributes, name);
+ mutex_unlock(&classLock);
+ return result;
+}
+
+
/***********************************************************************
* class_addMethod
**********************************************************************/
mlist->method_count = 1;
mlist->method_list[0].method_name = name;
mlist->method_list[0].method_types = _strdup_internal(types);
- if (name != (SEL)kIgnore) {
+ if (!ignoreSelector(name)) {
mlist->method_list[0].method_imp = imp;
} else {
mlist->method_list[0].method_imp = (IMP)&_objc_ignored_method;
/***********************************************************************
-* class_addProperty
+* _class_addProperties
+* Internal helper to add properties to a class.
+* Used by category attachment and class_addProperty()
+* Locking: acquires classLock
**********************************************************************/
-__private_extern__ void
+PRIVATE_EXTERN BOOL
_class_addProperties(struct old_class *cls,
- struct objc_property_list *additions)
+ struct old_property_list *additions)
{
- struct objc_property_list *newlist;
+ struct old_property_list *newlist;
- if (!(cls->info & CLS_EXT)) return;
+ if (!(cls->info & CLS_EXT)) return NO;
newlist =
_memdup_internal(additions, sizeof(*newlist) - sizeof(newlist->first)
allocateExt(cls);
if (!cls->ext->propertyLists) {
// cls has no properties - simply use this list
- cls->ext->propertyLists = (struct objc_property_list **)newlist;
+ cls->ext->propertyLists = (struct old_property_list **)newlist;
_class_setInfo((Class)cls, CLS_NO_PROPERTY_ARRAY);
}
else if (cls->info & CLS_NO_PROPERTY_ARRAY) {
// cls has one property list - make a new array
- struct objc_property_list **newarray =
+ struct old_property_list **newarray =
_malloc_internal(3 * sizeof(*newarray));
newarray[0] = newlist;
- newarray[1] = (struct objc_property_list *)cls->ext->propertyLists;
+ newarray[1] = (struct old_property_list *)cls->ext->propertyLists;
newarray[2] = NULL;
cls->ext->propertyLists = newarray;
_class_clearInfo((Class)cls, CLS_NO_PROPERTY_ARRAY);
}
else {
// cls has a property array - make a bigger one
- struct objc_property_list **newarray;
+ struct old_property_list **newarray;
int count = 0;
while (cls->ext->propertyLists[count]) count++;
newarray = _malloc_internal((count+2) * sizeof(*newarray));
}
mutex_unlock(&classLock);
+
+ return YES;
+}
+
+
+/***********************************************************************
+* class_addProperty
+* Adds a property to a class. Returns NO if the proeprty already exists.
+* Locking: acquires classLock
+**********************************************************************/
+static BOOL
+_class_addProperty(Class cls_gen, const char *name,
+ const objc_property_attribute_t *attrs, unsigned int count,
+ BOOL replace)
+{
+ struct old_class *cls = oldcls(cls_gen);
+
+ if (!cls) return NO;
+ if (!name) return NO;
+
+ struct old_property *prop = oldproperty(class_getProperty(cls_gen, name));
+ if (prop && !replace) {
+ // already exists, refuse to replace
+ return NO;
+ }
+ else if (prop) {
+ // replace existing
+ mutex_lock(&classLock);
+ try_free(prop->attributes);
+ prop->attributes = copyPropertyAttributeString(attrs, count);
+ mutex_unlock(&classLock);
+ return YES;
+ }
+ else {
+ // add new
+ struct old_property_list proplist;
+ proplist.entsize = sizeof(struct old_property);
+ proplist.count = 1;
+ proplist.first.name = _strdup_internal(name);
+ proplist.first.attributes = copyPropertyAttributeString(attrs, count);
+
+ return _class_addProperties(cls, &proplist);
+ }
+}
+
+BOOL
+class_addProperty(Class cls_gen, const char *name,
+ const objc_property_attribute_t *attrs, unsigned int n)
+{
+ return _class_addProperty(cls_gen, name, attrs, n, NO);
+}
+
+void
+class_replaceProperty(Class cls_gen, const char *name,
+ const objc_property_attribute_t *attrs, unsigned int n)
+{
+ _class_addProperty(cls_gen, name, attrs, n, YES);
}
* implements no protocols. Caller must free the block.
* Does not copy any superclass's protocols.
**********************************************************************/
-Protocol **class_copyProtocolList(Class cls_gen, unsigned int *outCount)
+Protocol * __unsafe_unretained *
+class_copyProtocolList(Class cls_gen, unsigned int *outCount)
{
struct old_class *cls = oldcls(cls_gen);
struct old_protocol_list *plist;
/***********************************************************************
* class_getProperty. Return the named property.
**********************************************************************/
-Property class_getProperty(Class cls_gen, const char *name)
+objc_property_t class_getProperty(Class cls_gen, const char *name)
{
- Property result;
- struct old_class *cls = _class_asOld(cls_gen);
+ struct old_property *result;
+ struct old_class *cls = oldcls(cls_gen);
if (!cls || !name) return NULL;
mutex_lock(&classLock);
for (result = NULL; cls && !result; cls = cls->super_class) {
uintptr_t iterator = 0;
- struct objc_property_list *plist;
+ struct old_property_list *plist;
while ((plist = nextPropertyList(cls, &iterator))) {
uint32_t i;
for (i = 0; i < plist->count; i++) {
- Property p = property_list_nth(plist, i);
+ struct old_property *p = property_list_nth(plist, i);
if (0 == strcmp(name, p->name)) {
result = p;
goto done;
done:
mutex_unlock(&classLock);
- return result;
+ return (objc_property_t)result;
}
* declares no properties. Caller must free the block.
* Does not copy any superclass's properties.
**********************************************************************/
-Property *class_copyPropertyList(Class cls_gen, unsigned int *outCount)
+objc_property_t *class_copyPropertyList(Class cls_gen, unsigned int *outCount)
{
struct old_class *cls = oldcls(cls_gen);
- struct objc_property_list *plist;
+ struct old_property_list *plist;
uintptr_t iterator = 0;
- Property *result = NULL;
+ struct old_property **result = NULL;
unsigned int count = 0;
unsigned int p, i;
}
if (count > 0) {
- result = malloc((count+1) * sizeof(Property));
+ result = malloc((count+1) * sizeof(struct old_property *));
p = 0;
iterator = 0;
mutex_unlock(&classLock);
if (outCount) *outCount = count;
- return result;
+ return (objc_property_t *)result;
}
int i;
for (i = 0; i < mlist->method_count; i++) {
Method aMethod = (Method)&mlist->method_list[i];
- if (method_getName(aMethod) == (SEL)kIgnore) {
+ if (ignoreSelector(method_getName(aMethod))) {
count--;
continue;
}
/***********************************************************************
* objc_allocateClass.
**********************************************************************/
-__private_extern__
-void set_superclass(struct old_class *cls, struct old_class *supercls)
+PRIVATE_EXTERN
+void set_superclass(struct old_class *cls, struct old_class *supercls,
+ BOOL cls_is_new)
{
struct old_class *meta = cls->isa;
meta->super_class = supercls->isa;
meta->isa = supercls->isa->isa;
+ // Propagate C++ cdtors from superclass.
+ if (supercls->info & CLS_HAS_CXX_STRUCTORS) {
+ if (cls_is_new) cls->info |= CLS_HAS_CXX_STRUCTORS;
+ else _class_setInfo((Class)cls, CLS_HAS_CXX_STRUCTORS);
+ }
+
// Superclass is no longer a leaf for cache flushing
if (supercls->info & CLS_LEAF) {
_class_clearInfo((Class)supercls, CLS_LEAF);
}
}
-void objc_initializeClassPair(Class superclass_gen, const char *name, Class cls_gen, Class meta_gen)
+// &UnsetLayout is the default ivar layout during class construction
+static const uint8_t UnsetLayout = 0;
+
+Class objc_initializeClassPair(Class superclass_gen, const char *name, Class cls_gen, Class meta_gen)
{
struct old_class *supercls = oldcls(superclass_gen);
struct old_class *cls = oldcls(cls_gen);
// Connect to superclasses and metaclasses
cls->isa = meta;
- set_superclass(cls, supercls);
+ set_superclass(cls, supercls, YES);
// Set basic info
cls->name = _strdup_internal(name);
meta->instance_size = sizeof(struct old_class);
}
- // No ivars. No methods. Empty cache. No protocols. No layout. No ext.
+ // No ivars. No methods. Empty cache. No protocols. No layout. Empty ext.
cls->ivars = NULL;
cls->methodLists = NULL;
cls->cache = (Cache)&_objc_empty_cache;
cls->protocols = NULL;
+ cls->ivar_layout = &UnsetLayout;
cls->ext = NULL;
+ allocateExt(cls);
+ cls->ext->weak_ivar_layout = &UnsetLayout;
meta->ivars = NULL;
meta->methodLists = NULL;
meta->cache = (Cache)&_objc_empty_cache;
meta->protocols = NULL;
- cls->ext = NULL;
+ meta->ext = NULL;
+
+ return cls_gen;
}
Class objc_allocateClassPair(Class superclass_gen, const char *name,
// Allocate new classes.
if (supercls) {
- cls = _calloc_class(supercls->isa->instance_size + extraBytes);
- meta = _calloc_class(supercls->isa->isa->instance_size + extraBytes);
+ cls = _calloc_class(_class_getInstanceSize((Class)supercls->isa) + extraBytes);
+ meta = _calloc_class(_class_getInstanceSize((Class)supercls->isa->isa) + extraBytes);
} else {
cls = _calloc_class(sizeof(struct old_class) + extraBytes);
meta = _calloc_class(sizeof(struct old_class) + extraBytes);
// Build ivar layouts
if (UseGC) {
- if (cls->ivar_layout) {
+ if (cls->ivar_layout != &UnsetLayout) {
// Class builder already called class_setIvarLayout.
}
else if (!cls->super_class) {
// Root class. Scan conservatively (should be isa ivar only).
- // ivar_layout is already NULL.
+ cls->ivar_layout = NULL;
}
else if (cls->ivars == NULL) {
// No local ivars. Use superclass's layout.
cls->ivar_layout =
- _strdup_internal(cls->super_class->ivar_layout);
+ _ustrdup_internal(cls->super_class->ivar_layout);
}
else {
// Has local ivars. Build layout based on superclass.
struct old_class *supercls = cls->super_class;
- unsigned char *superlayout =
- (unsigned char *) class_getIvarLayout((Class)supercls);
+ const uint8_t *superlayout =
+ class_getIvarLayout((Class)supercls);
layout_bitmap bitmap =
layout_bitmap_create(superlayout, supercls->instance_size,
cls->instance_size, NO);
struct old_ivar *iv = &cls->ivars->ivar_list[i];
layout_bitmap_set_ivar(bitmap, iv->ivar_type, iv->ivar_offset);
}
- cls->ivar_layout = (char *)layout_string_create(bitmap);
+ cls->ivar_layout = layout_string_create(bitmap);
layout_bitmap_free(bitmap);
}
- if (cls->ext && cls->ext->weak_ivar_layout) {
+ if (cls->ext->weak_ivar_layout != &UnsetLayout) {
// Class builder already called class_setWeakIvarLayout.
}
else if (!cls->super_class) {
// Root class. No weak ivars (should be isa ivar only)
- // weak_ivar_layout is already NULL.
+ cls->ext->weak_ivar_layout = NULL;
}
else if (cls->ivars == NULL) {
// No local ivars. Use superclass's layout.
- const char *weak =
+ const uint8_t *weak =
class_getWeakIvarLayout((Class)cls->super_class);
if (weak) {
- allocateExt(cls);
- cls->ext->weak_ivar_layout = _strdup_internal(weak);
+ cls->ext->weak_ivar_layout = _ustrdup_internal(weak);
+ } else {
+ cls->ext->weak_ivar_layout = NULL;
}
}
else {
// Has local ivars. Build layout based on superclass.
// No way to add weak ivars yet.
- const char *weak =
+ const uint8_t *weak =
class_getWeakIvarLayout((Class)cls->super_class);
if (weak) {
- allocateExt(cls);
- cls->ext->weak_ivar_layout = _strdup_internal(weak);
+ cls->ext->weak_ivar_layout = _ustrdup_internal(weak);
+ } else {
+ cls->ext->weak_ivar_layout = NULL;
}
}
}
// instance_size has historically contained two extra words,
// and instance_size is what objc_getIndexedIvars() actually uses.
struct old_class *duplicate = (struct old_class *)
- _calloc_class(original->isa->instance_size + extraBytes);
+ _calloc_class(_class_getInstanceSize((Class)original->isa) + extraBytes);
duplicate->isa = original->isa;
duplicate->super_class = original->super_class;
}
+
/***********************************************************************
-* _internal_class_createInstance. Allocate an instance of the specified
+* _class_createInstanceFromZone. Allocate an instance of the
+* specified class with the specified number of bytes for indexed
+* variables, in the specified zone. The isa field is set to the
+* class, C++ default constructors are called, and all other fields are zeroed.
+**********************************************************************/
+PRIVATE_EXTERN id
+_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
+{
+ id obj;
+ size_t size;
+
+ // Can't create something for nothing
+ if (!cls) return nil;
+
+ // Allocate and initialize
+ size = _class_getInstanceSize(cls) + extraBytes;
+
+ // CF requires all objects be at least 16 bytes.
+ if (size < 16) size = 16;
+
+#if SUPPORT_GC
+ if (UseGC) {
+ obj = (id)auto_zone_allocate_object(gc_zone, size,
+ AUTO_OBJECT_SCANNED, 0, 1);
+ } else
+#endif
+ if (zone) {
+ obj = (id)malloc_zone_calloc (zone, 1, size);
+ } else {
+ obj = (id)calloc(1, size);
+ }
+ if (!obj) return nil;
+
+ obj->isa = cls;
+
+ if (_class_hasCxxStructors(cls)) {
+ obj = _objc_constructOrFree(cls, obj);
+ }
+
+ return obj;
+}
+
+
+/***********************************************************************
+* _class_createInstance. Allocate an instance of the specified
* class with the specified number of bytes for indexed variables, in
-* the default zone, using _internal_class_createInstanceFromZone.
+* the default zone, using _class_createInstanceFromZone.
**********************************************************************/
-static id _internal_class_createInstance(Class cls, size_t extraBytes)
+static id _class_createInstance(Class cls, size_t extraBytes)
{
- return _internal_class_createInstanceFromZone (cls, extraBytes, NULL);
+ return _class_createInstanceFromZone (cls, extraBytes, NULL);
}
-static id _internal_object_copyFromZone(id oldObj, size_t extraBytes, void *zone)
+static id _object_copyFromZone(id oldObj, size_t extraBytes, void *zone)
{
id obj;
size_t size;
// fixme need C++ copy constructor
objc_memmove_collectable(obj, oldObj, size);
-#if !defined(NO_GC)
+#if SUPPORT_GC
if (UseGC) gc_fixup_weakreferences(obj, oldObj);
#endif
return obj;
}
-static id _internal_object_copy(id oldObj, size_t extraBytes)
+
+/***********************************************************************
+* objc_destructInstance
+* Destroys an instance without freeing memory.
+* Calls C++ destructors.
+* Removes associative references.
+* Returns `obj`. Does nothing if `obj` is nil.
+* Be warned that GC DOES NOT CALL THIS. If you edit this, also edit finalize.
+* CoreFoundation and other clients do call this under GC.
+**********************************************************************/
+void *objc_destructInstance(id obj)
+{
+ if (obj) {
+ Class isa = _object_getClass(obj);
+
+ if (_class_hasCxxStructors(isa)) {
+ object_cxxDestruct(obj);
+ }
+
+ if (_class_instancesHaveAssociatedObjects(isa)) {
+ _object_remove_assocations(obj);
+ }
+
+ if (!UseGC) objc_clear_deallocating(obj);
+ }
+
+ return obj;
+}
+
+static id
+_object_dispose(id anObject)
+{
+ if (anObject==nil) return nil;
+
+ objc_destructInstance(anObject);
+
+#if SUPPORT_GC
+ if (UseGC) {
+ auto_zone_retain(gc_zone, anObject); // gc free expects rc==1
+ } else
+#endif
+ {
+ // only clobber isa for non-gc
+ anObject->isa = _objc_getFreedObjectClass ();
+ }
+ free(anObject);
+ return nil;
+}
+
+static id _object_copy(id oldObj, size_t extraBytes)
{
void *z = malloc_zone_from_ptr(oldObj);
- return _internal_object_copyFromZone(oldObj, extraBytes,
+ return _object_copyFromZone(oldObj, extraBytes,
z ? z : malloc_default_zone());
}
-static id _internal_object_reallocFromZone(id anObject, size_t nBytes,
+static id _object_reallocFromZone(id anObject, size_t nBytes,
void *zone)
{
id newObject;
}
-static id _internal_object_realloc(id anObject, size_t nBytes)
+static id _object_realloc(id anObject, size_t nBytes)
{
void *z = malloc_zone_from_ptr(anObject);
- return _internal_object_reallocFromZone(anObject,
+ return _object_reallocFromZone(anObject,
nBytes,
z ? z : malloc_default_zone());
}
-id (*_alloc)(Class, size_t) = _internal_class_createInstance;
-id (*_copy)(id, size_t) = _internal_object_copy;
-id (*_realloc)(id, size_t) = _internal_object_realloc;
-id (*_dealloc)(id) = _internal_object_dispose;
-id (*_zoneAlloc)(Class, size_t, void *) = _internal_class_createInstanceFromZone;
-id (*_zoneCopy)(id, size_t, void *) = _internal_object_copyFromZone;
-id (*_zoneRealloc)(id, size_t, void *) = _internal_object_reallocFromZone;
+id (*_alloc)(Class, size_t) = _class_createInstance;
+id (*_copy)(id, size_t) = _object_copy;
+id (*_realloc)(id, size_t) = _object_realloc;
+id (*_dealloc)(id) = _object_dispose;
+id (*_zoneAlloc)(Class, size_t, void *) = _class_createInstanceFromZone;
+id (*_zoneCopy)(id, size_t, void *) = _object_copyFromZone;
+id (*_zoneRealloc)(id, size_t, void *) = _object_reallocFromZone;
void (*_error)(id, const char *, va_list) = _objc_error;
id class_createInstance(Class cls, size_t extraBytes)
{
if (UseGC) {
- return _internal_class_createInstance(cls, extraBytes);
+ return _class_createInstance(cls, extraBytes);
} else {
return (*_alloc)(cls, extraBytes);
}
{
OBJC_WARN_DEPRECATED;
if (UseGC) {
- return _internal_class_createInstanceFromZone(cls, extraBytes, z);
+ return _class_createInstanceFromZone(cls, extraBytes, z);
} else {
return (*_zoneAlloc)(cls, extraBytes, z);
}
}
+unsigned class_createInstances(Class cls, size_t extraBytes,
+ id *results, unsigned num_requested)
+{
+ if (UseGC || _alloc == &_class_createInstance) {
+ return _class_createInstancesFromZone(cls, extraBytes, NULL,
+ results, num_requested);
+ } else {
+ // _alloc in use, which isn't understood by the batch allocator
+ return 0;
+ }
+}
+
id object_copy(id obj, size_t extraBytes)
{
- if (UseGC) return _internal_object_copy(obj, extraBytes);
+ if (UseGC) return _object_copy(obj, extraBytes);
else return (*_copy)(obj, extraBytes);
}
id object_copyFromZone(id obj, size_t extraBytes, void *z)
{
OBJC_WARN_DEPRECATED;
- if (UseGC) return _internal_object_copyFromZone(obj, extraBytes, z);
+ if (UseGC) return _object_copyFromZone(obj, extraBytes, z);
else return (*_zoneCopy)(obj, extraBytes, z);
}
id object_dispose(id obj)
{
- if (UseGC) return _internal_object_dispose(obj);
+ if (UseGC) return _object_dispose(obj);
else return (*_dealloc)(obj);
}
id object_realloc(id obj, size_t nBytes)
{
OBJC_WARN_DEPRECATED;
- if (UseGC) return _internal_object_realloc(obj, nBytes);
+ if (UseGC) return _object_realloc(obj, nBytes);
else return (*_realloc)(obj, nBytes);
}
id object_reallocFromZone(id obj, size_t nBytes, void *z)
{
OBJC_WARN_DEPRECATED;
- if (UseGC) return _internal_object_reallocFromZone(obj, nBytes, z);
+ if (UseGC) return _object_reallocFromZone(obj, nBytes, z);
else return (*_zoneRealloc)(obj, nBytes, z);
}
Class class_setSuperclass(Class cls, Class newSuper)
{
Class oldSuper = cls->super_class;
- set_superclass(oldcls(cls), oldcls(newSuper));
+ set_superclass(oldcls(cls), oldcls(newSuper), NO);
flush_caches(cls, YES);
return oldSuper;
}
**********************************************************************/
#include "objc-private.h"
+#include "objc-abi.h"
+#include "objc-auto.h"
#include <objc/message.h>
static int objcMsgLogEnabled = 0;
#endif
+
/***********************************************************************
* Information about multi-thread support:
*
/***********************************************************************
* object_getClass.
+* Locking: None. If you add locking, tell gdb (rdar://7516456).
**********************************************************************/
Class object_getClass(id obj)
{
- if (obj) return obj->isa;
- else return Nil;
+ return _object_getClass(obj);
}
do {
old = obj->isa;
} while (! OSAtomicCompareAndSwapPtrBarrier(old, cls, (void*)&obj->isa));
+
+ if (old && _class_instancesHaveAssociatedObjects(old)) {
+ _class_setInstancesHaveAssociatedObjects(cls);
+ }
+
return old;
}
else return Nil;
**********************************************************************/
const char *object_getClassName(id obj)
{
- if (obj) return _class_getName(obj->isa);
+ Class isa = _object_getClass(obj);
+ if (isa) return _class_getName(isa);
else return "nil";
}
void *object_getIndexedIvars(id obj)
{
// ivars are tacked onto the end of the object
- if (obj) return ((char *) obj) + _class_getInstanceSize(obj->isa);
+ if (obj) return ((char *) obj) + _class_getInstanceSize(_object_getClass(obj));
else return NULL;
}
Ivar ivar = NULL;
if (obj && name) {
- if ((ivar = class_getInstanceVariable(obj->isa, name))) {
- objc_assign_ivar_internal(
- (id)value,
- obj,
- ivar_getOffset(ivar));
+ if ((ivar = class_getInstanceVariable(_object_getClass(obj), name))) {
+ object_setIvar(obj, ivar, (id)value);
}
}
return ivar;
{
if (obj && name) {
Ivar ivar;
- void **ivaridx;
- if ((ivar = class_getInstanceVariable(obj->isa, name))) {
- ivaridx = (void **)((char *)obj + ivar_getOffset(ivar));
- if (value) *value = *ivaridx;
+ if ((ivar = class_getInstanceVariable(_object_getClass(obj), name))) {
+ if (value) *value = (void *)object_getIvar(obj, ivar);
return ivar;
}
}
return NULL;
}
+static BOOL is_scanned_offset(ptrdiff_t ivar_offset, const uint8_t *layout) {
+ ptrdiff_t index = 0, ivar_index = ivar_offset / sizeof(void*);
+ uint8_t byte;
+ while ((byte = *layout++)) {
+ unsigned skips = (byte >> 4);
+ unsigned scans = (byte & 0x0F);
+ index += skips;
+ while (scans--) {
+ if (index == ivar_index) return YES;
+ if (index > ivar_index) return NO;
+ ++index;
+ }
+ }
+ return NO;
+}
+
+// FIXME: this could be optimized.
+
+static Class _ivar_getClass(Class cls, Ivar ivar) {
+ Class ivar_class = NULL;
+ const char *ivar_name = ivar_getName(ivar);
+ Ivar named_ivar = _class_getVariable(cls, ivar_name, &ivar_class);
+ if (named_ivar) {
+ // the same ivar name can appear multiple times along the superclass chain.
+ while (named_ivar != ivar && ivar_class != NULL) {
+ ivar_class = class_getSuperclass(ivar_class);
+ named_ivar = _class_getVariable(cls, ivar_getName(ivar), &ivar_class);
+ }
+ }
+ return ivar_class;
+}
void object_setIvar(id obj, Ivar ivar, id value)
{
- if (obj && ivar) {
- objc_assign_ivar_internal(value, obj, ivar_getOffset(ivar));
+ if (obj && ivar) {
+ Class cls = _ivar_getClass(object_getClass(obj), ivar);
+ ptrdiff_t ivar_offset = ivar_getOffset(ivar);
+ // if this ivar is a member of an ARR compiled class, then issue the correct barrier according to the layout.
+ if (_class_usesAutomaticRetainRelease(cls)) {
+ // for ARR, layout strings are relative to the instance start.
+ uint32_t instanceStart = _class_getInstanceStart(cls);
+ id *location = (id *)((char *)obj + ivar_offset);
+ const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
+ if (weak_layout && is_scanned_offset(ivar_offset - instanceStart, weak_layout)) {
+ // use the weak system to write to this variable.
+ objc_storeWeak(location, value);
+ return;
+ }
+ const uint8_t *strong_layout = class_getIvarLayout(cls);
+ if (strong_layout && is_scanned_offset(ivar_offset - instanceStart, strong_layout)) {
+ objc_storeStrong(location, value);
+ return;
+ }
+ }
+#if SUPPORT_GC
+ if (UseGC) {
+ // for GC, check for weak references.
+ const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
+ if (weak_layout && is_scanned_offset(ivar_offset, weak_layout)) {
+ objc_assign_weak(value, (id *)((char *)obj + ivar_offset));
+ }
+ }
+#endif
+ objc_assign_ivar_internal(value, obj, ivar_offset);
}
}
id object_getIvar(id obj, Ivar ivar)
{
if (obj && ivar) {
- id *idx = (id *)((char *)obj + ivar_getOffset(ivar));
+ Class cls = _object_getClass(obj);
+ ptrdiff_t ivar_offset = ivar_getOffset(ivar);
+ if (_class_usesAutomaticRetainRelease(cls)) {
+ // for ARR, layout strings are relative to the instance start.
+ uint32_t instanceStart = _class_getInstanceStart(cls);
+ const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
+ if (weak_layout && is_scanned_offset(ivar_offset - instanceStart, weak_layout)) {
+ // use the weak system to read this variable.
+ id *location = (id *)((char *)obj + ivar_offset);
+ return objc_loadWeak(location);
+ }
+ }
+ id *idx = (id *)((char *)obj + ivar_offset);
+#if SUPPORT_GC
+ if (UseGC) {
+ const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
+ if (weak_layout && is_scanned_offset(ivar_offset, weak_layout)) {
+ return objc_read_weak(idx);
+ }
+ }
+#endif
return *idx;
}
return NULL;
// Call cls's dtor first, then superclasses's dtors.
for ( ; cls != NULL; cls = _class_getSuperclass(cls)) {
- if (!_class_hasCxxStructorsNoSuper(cls)) continue;
+ if (!_class_hasCxxStructors(cls)) return;
dtor = (void(*)(id))
lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
if (dtor != (void(*)(id))&_objc_msgForward_internal) {
* Call C++ destructors on obj, if any.
* Uses methodListLock and cacheUpdateLock. The caller must hold neither.
**********************************************************************/
-__private_extern__ void object_cxxDestruct(id obj)
+PRIVATE_EXTERN void object_cxxDestruct(id obj)
{
if (!obj) return;
- object_cxxDestructFromClass(obj, obj->isa);
+ if (OBJC_IS_TAGGED_PTR(obj)) return;
+ object_cxxDestructFromClass(obj, obj->isa); // need not be object_getClass
}
static BOOL object_cxxConstructFromClass(id obj, Class cls)
{
id (*ctor)(id);
- Class supercls = _class_getSuperclass(cls);
+ Class supercls;
+
+ // Stop if neither this class nor any superclass has ctors.
+ if (!_class_hasCxxStructors(cls)) return YES; // no ctor - ok
+
+ supercls = _class_getSuperclass(cls);
// Call superclasses' ctors first, if any.
if (supercls) {
}
// Find this class's ctor, if any.
- if (!_class_hasCxxStructorsNoSuper(cls)) return YES; // no ctor - ok
ctor = (id(*)(id))lookupMethodInClassAndLoadCache(cls, SEL_cxx_construct);
if (ctor == (id(*)(id))&_objc_msgForward_internal) return YES; // no ctor - ok
* caught and discarded. Any partial construction is destructed.
* Uses methodListLock and cacheUpdateLock. The caller must hold neither.
**********************************************************************/
-__private_extern__ BOOL object_cxxConstruct(id obj)
+PRIVATE_EXTERN BOOL object_cxxConstruct(id obj)
{
if (!obj) return YES;
- return object_cxxConstructFromClass(obj, obj->isa);
+ if (OBJC_IS_TAGGED_PTR(obj)) return YES;
+ return object_cxxConstructFromClass(obj, obj->isa); // need not be object_getClass
}
* the method added or NULL.
* Assumes the method doesn't exist already.
**********************************************************************/
-__private_extern__ Method _class_resolveMethod(Class cls, SEL sel)
+PRIVATE_EXTERN Method _class_resolveMethod(Class cls, SEL sel)
{
Method meth = NULL;
{
if (!cls || !name) return NULL;
- return _class_getVariable(cls, name);
+ return _class_getVariable(cls, name, NULL);
}
}
-__private_extern__ Property
-property_list_nth(const struct objc_property_list *plist, uint32_t i)
-{
- return (Property)(i*plist->entsize + (char *)&plist->first);
-}
-
-__private_extern__ size_t
-property_list_size(const struct objc_property_list *plist)
-{
- return sizeof(struct objc_property_list) + (plist->count-1)*plist->entsize;
-}
-
-__private_extern__ Property *
-copyPropertyList(struct objc_property_list *plist, unsigned int *outCount)
-{
- Property *result = NULL;
- unsigned int count = 0;
-
- if (plist) {
- count = plist->count;
- }
-
- if (count > 0) {
- unsigned int i;
- result = malloc((count+1) * sizeof(Property));
-
- for (i = 0; i < count; i++) {
- result[i] = property_list_nth(plist, i);
- }
- result[i] = NULL;
- }
-
- if (outCount) *outCount = count;
- return result;
-}
-
-const char *property_getName(Property prop)
-{
- return prop->name;
-}
-
-
-const char *property_getAttributes(Property prop)
-{
- return prop->attributes;
-}
-
-
/***********************************************************************
* gdb_objc_class_changed
* Tell gdb that a class changed. Currently used for OBJC2 ivar layouts only
+* Does nothing; gdb sets a breakpoint on it.
**********************************************************************/
-void gdb_objc_class_changed(Class cls, unsigned long changes, const char *classname)
-{
- // do nothing; gdb sets a breakpoint here to listen
-#if TARGET_OS_WIN32
- __asm { }
-#else
- asm("");
-#endif
-}
+BREAKPOINT_FUNCTION(
+ void gdb_objc_class_changed(Class cls, unsigned long changes, const char *classname)
+);
/***********************************************************************
void _objc_flush_caches(Class cls)
{
flush_caches (cls, YES);
+
+ if (!cls) {
+ // collectALot if cls==nil
+ mutex_lock(&cacheUpdateLock);
+ _cache_collect(true);
+ mutex_unlock(&cacheUpdateLock);
+ }
}
}
-// Ignored selectors get method->imp = &_objc_ignored_method
-__private_extern__ id _objc_ignored_method(id self, SEL _cmd) { return self; }
-
-
/***********************************************************************
* instrumentObjcMessageSends/logObjcMessageSends.
**********************************************************************/
-#if defined(MESSAGE_LOGGING)
+#if !defined(MESSAGE_LOGGING) && defined(__arm__)
+void instrumentObjcMessageSends (BOOL flag)
+{
+}
+#elif defined(MESSAGE_LOGGING)
static int LogObjCMessageSend (BOOL isClassMethod,
const char * objectsClass,
const char * implementingClass,
isClassMethod ? '+' : '-',
objectsClass,
implementingClass,
- (char *) selector);
+ sel_getName(selector));
static OSSpinLock lock = OS_SPINLOCK_INIT;
OSSpinLockLock(&lock);
objcMsgLogEnabled = enabledValue;
}
-__private_extern__ void logObjcMessageSends (ObjCLogProc logProc)
+PRIVATE_EXTERN void logObjcMessageSends (ObjCLogProc logProc)
{
if (logProc)
{
* cls is the method whose cache should be filled.
* implementer is the class that owns the implementation in question.
**********************************************************************/
-__private_extern__ void
+PRIVATE_EXTERN void
log_and_fill_cache(Class cls, Class implementer, Method meth, SEL sel)
{
#if defined(MESSAGE_LOGGING)
* This lookup avoids optimistic cache scan because the dispatcher
* already tried that.
**********************************************************************/
-__private_extern__ IMP _class_lookupMethodAndLoadCache(Class cls, SEL sel)
-{
+PRIVATE_EXTERN IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
+{
+ return lookUpMethod(cls, sel, YES/*initialize*/, NO/*cache*/);
+}
+PRIVATE_EXTERN IMP _class_lookupMethodAndLoadCache(Class cls, SEL sel)
+{
return lookUpMethod(cls, sel, YES/*initialize*/, NO/*cache*/);
}
* May return _objc_msgForward_internal. IMPs destined for external use
* must be converted to _objc_msgForward or _objc_msgForward_stret.
**********************************************************************/
-__private_extern__ IMP lookUpMethod(Class cls, SEL sel,
- BOOL initialize, BOOL cache)
+PRIVATE_EXTERN IMP lookUpMethod(Class cls, SEL sel,
+ BOOL initialize, BOOL cache)
{
Class curClass;
IMP methodPC = NULL;
retry:
lockForMethodLookup();
+ // Ignore GC selectors
+ if (ignoreSelector(sel)) {
+ methodPC = _cache_addIgnoredEntry(cls, sel);
+ goto done;
+ }
+
// Try this class's cache.
methodPC = _cache_getImp(cls, sel);
unlockForMethodLookup();
// paranoia: look for ignored selectors with non-ignored implementations
- assert(!(sel == (SEL)kIgnore && methodPC != (IMP)&_objc_ignored_method));
+ assert(!(ignoreSelector(sel) && methodPC != (IMP)&_objc_ignored_method));
return methodPC;
}
}
-/***********************************************************************
-* _objc_create_zone.
-**********************************************************************/
-
-void *_objc_create_zone(void)
-{
- return malloc_default_zone();
-}
-
-
/***********************************************************************
* _malloc_internal
* _calloc_internal
* _free_internal
* Convenience functions for the internal malloc zone.
**********************************************************************/
-__private_extern__ void *_malloc_internal(size_t size)
+PRIVATE_EXTERN void *_malloc_internal(size_t size)
{
return malloc_zone_malloc(_objc_internal_zone(), size);
}
-__private_extern__ void *_calloc_internal(size_t count, size_t size)
+PRIVATE_EXTERN void *_calloc_internal(size_t count, size_t size)
{
return malloc_zone_calloc(_objc_internal_zone(), count, size);
}
-__private_extern__ void *_realloc_internal(void *ptr, size_t size)
+PRIVATE_EXTERN void *_realloc_internal(void *ptr, size_t size)
{
return malloc_zone_realloc(_objc_internal_zone(), ptr, size);
}
-__private_extern__ char *_strdup_internal(const char *str)
+PRIVATE_EXTERN char *_strdup_internal(const char *str)
{
size_t len;
char *dup;
return dup;
}
+PRIVATE_EXTERN uint8_t *_ustrdup_internal(const uint8_t *str)
+{
+ return (uint8_t *)_strdup_internal((char *)str);
+}
+
// allocate a new string that concatenates s1+s2.
-__private_extern__ char *_strdupcat_internal(const char *s1, const char *s2)
+PRIVATE_EXTERN char *_strdupcat_internal(const char *s1, const char *s2)
{
size_t len1 = strlen(s1);
size_t len2 = strlen(s2);
return dup;
}
-__private_extern__ void *_memdup_internal(const void *mem, size_t len)
+PRIVATE_EXTERN void *_memdup_internal(const void *mem, size_t len)
{
void *dup = malloc_zone_malloc(_objc_internal_zone(), len);
memcpy(dup, mem, len);
return dup;
}
-__private_extern__ void _free_internal(void *ptr)
+PRIVATE_EXTERN void _free_internal(void *ptr)
{
malloc_zone_free(_objc_internal_zone(), ptr);
}
+PRIVATE_EXTERN size_t _malloc_size_internal(void *ptr)
+{
+ malloc_zone_t *zone = _objc_internal_zone();
+ return zone->size(zone, ptr);
+}
-__private_extern__ Class _calloc_class(size_t size)
+PRIVATE_EXTERN Class _calloc_class(size_t size)
{
-#if !defined(NO_GC)
+#if SUPPORT_GC
if (UseGC) return (Class) malloc_zone_calloc(gc_zone, 1, size);
#endif
return (Class) _calloc_internal(1, size);
}
-unsigned int method_getSizeOfArguments(Method m)
-{
- OBJC_WARN_DEPRECATED;
- if (!m) return 0;
- return encoding_getSizeOfArguments(method_getTypeEncoding(m));
-}
-
-
-unsigned int method_getArgumentInfo(Method m, int arg,
- const char **type, int *offset)
-{
- OBJC_WARN_DEPRECATED;
- if (!m) return 0;
- return encoding_getArgumentInfo(method_getTypeEncoding(m),
- arg, type, offset);
-}
-
-
void method_getReturnType(Method m, char *dst, size_t dst_len)
{
encoding_getReturnType(method_getTypeEncoding(m), dst, dst_len);
* The new object's isa is set. Any C++ constructors are called.
* Returns `bytes` if successful. Returns nil if `cls` or `bytes` is
* NULL, or if C++ constructors fail.
+* Note: class_createInstance() and class_createInstances() preflight this.
**********************************************************************/
-id objc_constructInstance(Class cls, void *bytes)
+static id
+_objc_constructInstance(Class cls, void *bytes)
{
- id obj;
-
- if (!cls || !bytes) return nil;
- obj = (id)bytes;
+ id obj = (id)bytes;
// Set the isa pointer
- obj->isa = cls;
+ obj->isa = cls; // need not be object_setClass
// Call C++ constructors, if any.
if (!object_cxxConstruct(obj)) {
}
-/***********************************************************************
-* objc_destructInstance
-* Destroys an instance without freeing memory.
-* Any C++ destructors are called. Any associative references are removed.
-* Returns `obj`. Does nothing if `obj` is nil.
-**********************************************************************/
-void *objc_destructInstance(id obj)
+id
+objc_constructInstance(Class cls, void *bytes)
{
- if (obj) {
- object_cxxDestruct(obj);
+ if (!cls || !bytes) return nil;
+ return _objc_constructInstance(cls, bytes);
+}
+
- // don't call this if the class has never had associative references.
- if (_class_instancesHaveAssociatedObjects(obj->isa)) {
- _object_remove_assocations(obj);
+PRIVATE_EXTERN id
+_objc_constructOrFree(Class cls, void *bytes)
+{
+ id obj = _objc_constructInstance(cls, bytes);
+ if (!obj) {
+#if SUPPORT_GC
+ if (UseGC) {
+ auto_zone_retain(gc_zone, bytes); // gc free expects rc==1
}
+#endif
+ free(bytes);
}
return obj;
/***********************************************************************
-* _internal_class_createInstanceFromZone. Allocate an instance of the
-* specified class with the specified number of bytes for indexed
-* variables, in the specified zone. The isa field is set to the
-* class, C++ default constructors are called, and all other fields are zeroed.
+* _class_createInstancesFromZone
+* Batch-allocating version of _class_createInstanceFromZone.
+* Attempts to allocate num_requested objects, each with extraBytes.
+* Returns the number of allocated objects (possibly zero), with
+* the allocated pointers in *results.
**********************************************************************/
-__private_extern__ id
-_internal_class_createInstanceFromZone(Class cls, size_t extraBytes,
- void *zone)
+PRIVATE_EXTERN unsigned
+_class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone,
+ id *results, unsigned num_requested)
{
- void *bytes;
- id obj;
- size_t size;
-
- // Can't create something for nothing
- if (!cls) return nil;
-
- // Allocate and initialize
- size = _class_getInstanceSize(cls) + extraBytes;
+ unsigned num_allocated;
+ if (!cls) return 0;
+ size_t size = _class_getInstanceSize(cls) + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
-#if !defined(NO_GC)
+#if SUPPORT_GC
if (UseGC) {
- bytes = auto_zone_allocate_object(gc_zone, size,
- AUTO_OBJECT_SCANNED, 0, 1);
+ num_allocated =
+ auto_zone_batch_allocate(gc_zone, size, AUTO_OBJECT_SCANNED, 0, 1,
+ (void**)results, num_requested);
} else
#endif
- if (zone) {
- bytes = malloc_zone_calloc (zone, 1, size);
- } else {
- bytes = calloc(1, size);
- }
- if (!bytes) return nil;
-
- obj = objc_constructInstance(cls, bytes);
- if (!obj) {
-#if !defined(NO_GC)
- if (UseGC) {
- auto_zone_retain(gc_zone, bytes); // gc free expects rc==1
+ {
+ unsigned i;
+ num_allocated =
+ malloc_zone_batch_malloc(zone ? zone : malloc_default_zone(),
+ size, (void**)results, num_requested);
+ for (i = 0; i < num_allocated; i++) {
+ bzero(results[i], size);
}
-#endif
- free(bytes);
- return nil;
}
- return obj;
-}
+ // Construct each object, and delete any that fail construction.
+ unsigned shift = 0;
+ unsigned i;
+ BOOL ctor = _class_hasCxxStructors(cls);
+ for (i = 0; i < num_allocated; i++) {
+ id obj = results[i];
+ if (ctor) obj = _objc_constructOrFree(cls, obj);
+ else if (obj) obj->isa = cls; // need not be object_setClass
-__private_extern__ id
-_internal_object_dispose(id anObject)
-{
- if (anObject==nil) return nil;
-
- objc_destructInstance(anObject);
-
-#if !defined(NO_GC)
- if (UseGC) {
- auto_zone_retain(gc_zone, anObject); // gc free expects rc==1
- } else
-#endif
- {
-#if !__OBJC2__
- // only clobber isa for non-gc
- anObject->isa = _objc_getFreedObjectClass ();
-#endif
+ if (obj) {
+ results[i-shift] = obj;
+ } else {
+ shift++;
+ }
}
- free(anObject);
- return nil;
+
+ return num_allocated - shift;
}
/***********************************************************************
* inform_duplicate. Complain about duplicate class implementations.
**********************************************************************/
-__private_extern__ void
+PRIVATE_EXTERN void
inform_duplicate(const char *name, Class oldCls, Class cls)
{
#if TARGET_OS_WIN32
name, oldName, newName);
#endif
}
+
+#if SUPPORT_TAGGED_POINTERS
+/***********************************************************************
+ * _objc_insert_tagged_isa
+ * Insert an isa into a particular slot in the tagged isa table.
+ * Will error & abort if slot already has an isa that is different.
+ **********************************************************************/
+void _objc_insert_tagged_isa(unsigned char slotNumber, Class isa) {
+ unsigned char actualSlotNumber = (slotNumber << 1) + 1;
+ Class previousIsa = _objc_tagged_isa_table[actualSlotNumber];
+
+ if (actualSlotNumber & 0xF0) {
+ _objc_fatal("%s -- Slot number %uc is too large. Aborting.", __FUNCTION__, slotNumber);
+ }
+
+ if (actualSlotNumber == 0) {
+ _objc_fatal("%s -- Slot number 0 doesn't make sense. Aborting.", __FUNCTION__);
+ }
+
+ if (isa && previousIsa && (previousIsa != isa)) {
+ _objc_fatal("%s -- Tagged pointer table already had an item in that slot (%s). "
+ "Not putting (%s) in table. Aborting instead",
+ __FUNCTION__, class_getName(previousIsa), class_getName(isa));
+ }
+ _objc_tagged_isa_table[actualSlotNumber] = isa;
+}
+#endif
+
+
+PRIVATE_EXTERN const char *
+copyPropertyAttributeString(const objc_property_attribute_t *attrs,
+ unsigned int count)
+{
+ char *result;
+ unsigned int i;
+ if (count == 0) return strdup("");
+
+#ifndef NDEBUG
+ // debug build: sanitize input
+ for (i = 0; i < count; i++) {
+ assert(attrs[i].name);
+ assert(strlen(attrs[i].name) > 0);
+ assert(! strchr(attrs[i].name, ','));
+ assert(! strchr(attrs[i].name, '"'));
+ if (attrs[i].value) assert(! strchr(attrs[i].value, ','));
+ }
+#endif
+
+ size_t len = 0;
+ for (i = 0; i < count; i++) {
+ if (attrs[i].value) {
+ size_t namelen = strlen(attrs[i].name);
+ if (namelen > 1) namelen += 2; // long names get quoted
+ len += namelen + strlen(attrs[i].value) + 1;
+ }
+ }
+
+ result = malloc(len + 1);
+ char *s = result;
+ for (i = 0; i < count; i++) {
+ if (attrs[i].value) {
+ size_t namelen = strlen(attrs[i].name);
+ if (namelen > 1) {
+ s += sprintf(s, "\"%s\"%s,", attrs[i].name, attrs[i].value);
+ } else {
+ s += sprintf(s, "%s%s,", attrs[i].name, attrs[i].value);
+ }
+ }
+ }
+
+ // remove trailing ',' if any
+ if (s > result) s[-1] = '\0';
+
+ return result;
+}
+
+/*
+ Property attribute string format:
+ - Comma-separated name-value pairs.
+ - Name and value may not contain ,
+ - Name may not contain "
+ - Value may be empty
+ - Name is single char, value follows
+ - OR Name is double-quoted string of 2+ chars, value follows
+*/
+static unsigned int
+iteratePropertyAttributes(const char *attrs,
+ BOOL (*fn)(unsigned int index,
+ void *ctx1, void *ctx2,
+ const char *name, size_t nlen,
+ const char *value, size_t vlen),
+ void *ctx1, void *ctx2)
+{
+ if (!attrs) return 0;
+
+#ifndef NDEBUG
+ const char *attrsend = attrs + strlen(attrs);
+#endif
+ unsigned int attrcount = 0;
+
+ while (*attrs) {
+ // Find the next comma-separated attribute
+ const char *start = attrs;
+ const char *end = start + strcspn(attrs, ",");
+
+ // Move attrs past this attribute and the comma (if any)
+ attrs = *end ? end+1 : end;
+
+ assert(attrs <= attrsend);
+ assert(start <= attrsend);
+ assert(end <= attrsend);
+
+ // Skip empty attribute
+ if (start == end) continue;
+
+ // Process one non-empty comma-free attribute [start,end)
+ const char *nameStart;
+ const char *nameEnd;
+
+ assert(start < end);
+ assert(*start);
+ if (*start != '\"') {
+ // single-char short name
+ nameStart = start;
+ nameEnd = start+1;
+ start++;
+ }
+ else {
+ // double-quoted long name
+ nameStart = start+1;
+ nameEnd = nameStart + strcspn(nameStart, "\",");
+ start++; // leading quote
+ start += nameEnd - nameStart; // name
+ if (*start == '\"') start++; // trailing quote, if any
+ }
+
+ // Process one possibly-empty comma-free attribute value [start,end)
+ const char *valueStart;
+ const char *valueEnd;
+
+ assert(start <= end);
+
+ valueStart = start;
+ valueEnd = end;
+
+ BOOL more = (*fn)(attrcount, ctx1, ctx2,
+ nameStart, nameEnd-nameStart,
+ valueStart, valueEnd-valueStart);
+ attrcount++;
+ if (!more) break;
+ }
+
+ return attrcount;
+}
+
+
+static BOOL
+copyOneAttribute(unsigned int index, void *ctxa, void *ctxs,
+ const char *name, size_t nlen, const char *value, size_t vlen)
+{
+ objc_property_attribute_t **ap = (objc_property_attribute_t**)ctxa;
+ char **sp = (char **)ctxs;
+
+ objc_property_attribute_t *a = *ap;
+ char *s = *sp;
+
+ a->name = s;
+ memcpy(s, name, nlen);
+ s += nlen;
+ *s++ = '\0';
+
+ a->value = s;
+ memcpy(s, value, vlen);
+ s += vlen;
+ *s++ = '\0';
+
+ a++;
+
+ *ap = a;
+ *sp = s;
+
+ return YES;
+}
+
+
+PRIVATE_EXTERN objc_property_attribute_t *
+copyPropertyAttributeList(const char *attrs, unsigned int *outCount)
+{
+ if (!attrs) {
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ // Result size:
+ // number of commas plus 1 for the attributes (upper bound)
+ // plus another attribute for the attribute array terminator
+ // plus strlen(attrs) for name/value string data (upper bound)
+ // plus count*2 for the name/value string terminators (upper bound)
+ unsigned int attrcount = 1;
+ const char *s;
+ for (s = attrs; s && *s; s++) {
+ if (*s == ',') attrcount++;
+ }
+
+ size_t size =
+ attrcount * sizeof(objc_property_attribute_t) +
+ sizeof(objc_property_attribute_t) +
+ strlen(attrs) +
+ attrcount * 2;
+ objc_property_attribute_t *result = calloc(size, 1);
+
+ objc_property_attribute_t *ra = result;
+ char *rs = (char *)(ra+attrcount+1);
+
+ attrcount = iteratePropertyAttributes(attrs, copyOneAttribute, &ra, &rs);
+
+ assert((uint8_t *)(ra+1) <= (uint8_t *)result+size);
+ assert((uint8_t *)rs <= (uint8_t *)result+size);
+
+ if (attrcount == 0) {
+ free(result);
+ result = NULL;
+ }
+
+ if (outCount) *outCount = attrcount;
+ return result;
+}
+
+
+static BOOL
+findOneAttribute(unsigned int index, void *ctxa, void *ctxs,
+ const char *name, size_t nlen, const char *value, size_t vlen)
+{
+ const char *query = (char *)ctxa;
+ char **resultp = (char **)ctxs;
+
+ if (strlen(query) == nlen && 0 == strncmp(name, query, nlen)) {
+ char *result = calloc(vlen+1, 1);
+ memcpy(result, value, vlen);
+ result[vlen] = '\0';
+ *resultp = result;
+ return NO;
+ }
+
+ return YES;
+}
+
+PRIVATE_EXTERN
+char *copyPropertyAttributeValue(const char *attrs, const char *name)
+{
+ char *result = NULL;
+
+ iteratePropertyAttributes(attrs, findOneAttribute, (void*)name, &result);
+
+ return result;
+}
#include <TargetConditionals.h>
-// Define NO_GC to disable garbage collection.
+// Define SUPPORT_GC=1 to enable garbage collection.
// Be sure to edit OBJC_NO_GC in objc-auto.h as well.
-#if TARGET_OS_EMBEDDED || TARGET_OS_WIN32
-# define NO_GC 1
+#if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32
+# define SUPPORT_GC 0
+#else
+# define SUPPORT_GC 1
#endif
-// Define NO_ENVIRON to disable getenv().
-#if TARGET_OS_EMBEDDED
-# define NO_ENVIRON 1
+// Define SUPPORT_ENVIRON=1 to enable getenv().
+#if ((TARGET_OS_EMBEDDED || TARGET_OS_IPHONE) && !TARGET_IPHONE_SIMULATOR) && defined(NDEBUG)
+# define SUPPORT_ENVIRON 0
+#else
+# define SUPPORT_ENVIRON 1
#endif
-// Define NO_ZONES to disable malloc zone support in NXHashTable.
-#if TARGET_OS_EMBEDDED
-# define NO_ZONES 1
+// Define SUPPORT_ZONES=1 to enable malloc zone support in NXHashTable.
+#if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
+# define SUPPORT_ZONES 0
+#else
+# define SUPPORT_ZONES 1
#endif
-// Define NO_MOD to avoid the mod operator in NXHashTable and objc-sel-set.
+// Define SUPPORT_MOD=1 to use the mod operator in NXHashTable and objc-sel-set
#if defined(__arm__)
-# define NO_MOD 1
+# define SUPPORT_MOD 0
+#else
+# define SUPPORT_MOD 1
#endif
-// Define NO_BUILTINS to disable the builtin selector table from dyld
+// Define SUPPORT_BUILTINS=1 to enable the builtin selector table from dyld
#if TARGET_OS_WIN32
-# define NO_BUILTINS 1
+# define SUPPORT_BUILTINS 0
+#else
+# define SUPPORT_BUILTINS 1
#endif
-// Define NO_DEBUGGER_MODE to disable lock-avoiding execution for debuggers
+// Define SUPPORT_DEBUGGER_MODE=1 to enable lock-avoiding execution for debuggers
#if TARGET_OS_WIN32
-# define NO_DEBUGGER_MODE 1
+# define SUPPORT_DEBUGGER_MODE 0
+#else
+# define SUPPORT_DEBUGGER_MODE 1
#endif
-#if __OBJC2__
+// Define SUPPORT_TAGGED_POINTERS=1 to enable tagged pointer objects
+// Be sure to edit objc-internal.h as well (_objc_insert_tagged_isa)
+#if !(__OBJC2__ && __LP64__)
+# define SUPPORT_TAGGED_POINTERS 0
+#else
+# define SUPPORT_TAGGED_POINTERS 1
+#endif
-// Define NO_FIXUP to use non-fixup messaging for OBJC2.
-#if defined(__arm__)
-# define NO_FIXUP 1
+// Define SUPPORT_FIXUP=1 to use call-site fixup messaging for OBJC2.
+// Be sure to edit objc-abi.h as well (objc_msgSend*_fixup)
+#if !__OBJC2__ || !defined(__x86_64__)
+# define SUPPORT_FIXUP 0
+#else
+# define SUPPORT_FIXUP 1
+#endif
+
+// Define SUPPORT_VTABLE=1 to enable vtable dispatch for OBJC2.
+// Be sure to edit objc-gdb.h as well (gdb_objc_trampolines)
+#if !SUPPORT_FIXUP
+# define SUPPORT_VTABLE 0
+#else
+# define SUPPORT_VTABLE 1
#endif
-// Define NO_VTABLE to disable vtable dispatch for OBJC2.
-#if defined(NO_FIXUP) || defined(__ppc64__)
-# define NO_VTABLE 1
+// Define SUPPORT_IGNORED_SELECTOR_CONSTANT to remap GC-ignored selectors.
+// Good: fast ignore in objc_msgSend. Bad: disable shared cache optimizations
+// Non-GC does not remap. Fixup dispatch does not remap.
+#if !SUPPORT_GC || SUPPORT_FIXUP
+# define SUPPORT_IGNORED_SELECTOR_CONSTANT 0
+#else
+# define SUPPORT_IGNORED_SELECTOR_CONSTANT 1
+# if defined(__i386__)
+# define kIgnore 0xfffeb010
+# else
+# error unknown architecture
+# endif
#endif
-// Define NO_ZEROCOST_EXCEPTIONS to use sjlj exceptions for OBJC2.
+// Define SUPPORT_ZEROCOST_EXCEPTIONS to use "zero-cost" exceptions for OBJC2.
// Be sure to edit objc-exception.h as well (objc_add/removeExceptionHandler)
-#if defined(__arm__)
-# define NO_ZEROCOST_EXCEPTIONS 1
+#if !__OBJC2__ || defined(__arm__)
+# define SUPPORT_ZEROCOST_EXCEPTIONS 0
+#else
+# define SUPPORT_ZEROCOST_EXCEPTIONS 1
+#endif
+
+// Define SUPPORT_ALT_HANDLERS if you're using zero-cost exceptions
+// but also need to support AppKit's alt-handler scheme
+// Be sure to edit objc-exception.h as well (objc_add/removeExceptionHandler)
+#if !SUPPORT_ZEROCOST_EXCEPTIONS || TARGET_OS_IPHONE || TARGET_OS_EMBEDDED
+# define SUPPORT_ALT_HANDLERS 0
+#else
+# define SUPPORT_ALT_HANDLERS 1
#endif
+// Define SUPPORT_RETURN_AUTORELEASE to optimize autoreleased return values
+#if !__OBJC2__ || TARGET_OS_WIN32
+# define SUPPORT_RETURN_AUTORELEASE 0
+#else
+# define SUPPORT_RETURN_AUTORELEASE 1
#endif
// (by us) on some configurations.
#if defined (__i386__) || defined (i386)
# include <architecture/i386/asm_help.h>
-#elif defined (__ppc__) || defined(ppc)
-# include <architecture/ppc/asm_help.h>
#endif
#include <conio.h>
-__private_extern__ void _objc_inform_on_crash(const char *fmt, ...)
+PRIVATE_EXTERN void _objc_inform_on_crash(const char *fmt, ...)
{
}
-__private_extern__ void _objc_inform(const char *fmt, ...)
+PRIVATE_EXTERN void _objc_inform(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
_cprintf("\n");
}
-__private_extern__ void _objc_fatal(const char *fmt, ...)
+PRIVATE_EXTERN void _objc_fatal(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
abort();
}
-__private_extern__ void __objc_error(id rcv, const char *fmt, ...)
+PRIVATE_EXTERN void __objc_error(id rcv, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
_vcprintf(fmt, args);
va_end(args);
+
+ abort();
}
-__private_extern__ void _objc_error(id rcv, const char *fmt, va_list args)
+PRIVATE_EXTERN void _objc_error(id rcv, const char *fmt, va_list args)
{
_vcprintf(fmt, args);
+
+ abort();
}
#else
-__private_extern__ char *__crashreporter_info__ = NULL;
OBJC_EXPORT void (*_error)(id, const char *, va_list);
}
#endif
- if (!__crashreporter_info__) {
+ static mutex_t crashlog_lock = MUTEX_INITIALIZER;
+ mutex_lock(&crashlog_lock);
+
+ char *oldmsg = (char *)CRGetCrashLogMessage();
+
+ if (!oldmsg) {
newmsg = strdup(message);
} else {
- asprintf(&newmsg, "%s\n%s", __crashreporter_info__, message);
+ asprintf(&newmsg, "%s\n%s", oldmsg, message);
}
if (newmsg) {
char *c = &newmsg[strlen(newmsg)-1];
if (*c == '\n') *c = '\0';
- if (__crashreporter_info__) free(__crashreporter_info__);
- __crashreporter_info__ = newmsg;
+ if (oldmsg) free(oldmsg);
+ CRSetCrashLogMessage(newmsg);
}
+
+ mutex_unlock(&crashlog_lock);
}
// Print "message" to the console.
syslog(LOG_ERR, "%s", message);
}
}
-/*
- * this routine handles errors that involve an object (or class).
- */
-__private_extern__ void __objc_error(id rcv, const char *fmt, ...)
-{
- va_list vp;
-
- va_start(vp,fmt);
-#if !__OBJC2__
- (*_error)(rcv, fmt, vp);
-#endif
- _objc_error (rcv, fmt, vp); /* In case (*_error)() returns. */
- va_end(vp);
-}
/*
* _objc_error is the default *_error handler.
*/
#if __OBJC2__
-__private_extern__
+PRIVATE_EXTERN __attribute__((noreturn))
+#else
+// used by ExceptionHandling.framework
#endif
void _objc_error(id self, const char *fmt, va_list ap)
{
_objc_trap();
}
+/*
+ * this routine handles errors that involve an object (or class).
+ */
+PRIVATE_EXTERN void __objc_error(id rcv, const char *fmt, ...)
+{
+ va_list vp;
+
+ va_start(vp,fmt);
+#if !__OBJC2__
+ (*_error)(rcv, fmt, vp);
+#endif
+ _objc_error (rcv, fmt, vp); /* In case (*_error)() returns. */
+ va_end(vp);
+}
+
/*
* this routine handles severe runtime errors...like not being able
* to read the mach headers, allocate space, etc...very uncommon.
*/
-__private_extern__ void _objc_fatal(const char *fmt, ...)
+PRIVATE_EXTERN void _objc_fatal(const char *fmt, ...)
{
va_list ap;
char *buf1;
* this routine handles soft runtime errors...like not being able
* add a category to a class (because it wasn't linked in).
*/
-__private_extern__ void _objc_inform(const char *fmt, ...)
+PRIVATE_EXTERN void _objc_inform(const char *fmt, ...)
{
va_list ap;
char *buf1;
* Like _objc_inform(), but prints the message only in any
* forthcoming crash log, not to the console.
*/
-__private_extern__ void _objc_inform_on_crash(const char *fmt, ...)
+PRIVATE_EXTERN void _objc_inform_on_crash(const char *fmt, ...)
{
va_list ap;
char *buf1;
/*
* Like calling both _objc_inform and _objc_inform_on_crash.
*/
-__private_extern__ void _objc_inform_now_and_on_crash(const char *fmt, ...)
+PRIVATE_EXTERN void _objc_inform_now_and_on_crash(const char *fmt, ...)
{
va_list ap;
char *buf1;
#endif
-__private_extern__ void _objc_warn_deprecated(const char *oldf, const char *newf)
+BREAKPOINT_FUNCTION(
+ void _objc_warn_deprecated(void)
+);
+
+PRIVATE_EXTERN void _objc_inform_deprecated(const char *oldf, const char *newf)
{
if (PrintDeprecation) {
if (newf) {
_objc_inform("The function %s is obsolete. Do not use it. Set a breakpoint on _objc_warn_deprecated to find the culprit.", oldf);
}
}
+ _objc_warn_deprecated();
}
-
-
-/* Entry points for breakable errors. For some reason, can't inhibit the compiler's inlining aggression.
- */
-
-__private_extern__ void objc_assign_ivar_error(id base, ptrdiff_t offset) {
-}
-
-__private_extern__ void objc_assign_global_error(id value, id *slot) {
-}
-
-__private_extern__ void objc_exception_during_finalize_error(void) {
-}
-
// compiler reserves a setjmp buffer + 4 words as localExceptionData
-OBJC_EXPORT void objc_exception_throw(id exception);
-OBJC_EXPORT void objc_exception_try_enter(void *localExceptionData);
-OBJC_EXPORT void objc_exception_try_exit(void *localExceptionData);
-OBJC_EXPORT id objc_exception_extract(void *localExceptionData);
-OBJC_EXPORT int objc_exception_match(Class exceptionClass, id exception);
+OBJC_EXPORT void objc_exception_throw(id exception)
+ __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
+OBJC_EXPORT void objc_exception_try_enter(void *localExceptionData)
+ __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
+OBJC_EXPORT void objc_exception_try_exit(void *localExceptionData)
+ __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
+OBJC_EXPORT id objc_exception_extract(void *localExceptionData)
+ __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
+OBJC_EXPORT int objc_exception_match(Class exceptionClass, id exception)
+ __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
typedef struct {
void (*try_exit)(void *); // version 0
id (*extract)(void *); // version 0
int (*match)(Class, id); // version 0
-}
- objc_exception_functions_t;
+} objc_exception_functions_t;
// get table; version tells how many
-OBJC_EXPORT void objc_exception_get_functions(objc_exception_functions_t *table);
+OBJC_EXPORT void objc_exception_get_functions(objc_exception_functions_t *table)
+ __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
// set table
-OBJC_EXPORT void objc_exception_set_functions(objc_exception_functions_t *table);
+OBJC_EXPORT void objc_exception_set_functions(objc_exception_functions_t *table)
+ __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
// !__OBJC2__
typedef void (*objc_uncaught_exception_handler)(id exception);
typedef void (*objc_exception_handler)(id unused, void *context);
-OBJC_EXPORT void objc_exception_throw(id exception);
-OBJC_EXPORT void objc_exception_rethrow(void);
-OBJC_EXPORT id objc_begin_catch(void *exc_buf);
-OBJC_EXPORT void objc_end_catch(void);
-
-OBJC_EXPORT objc_exception_preprocessor objc_setExceptionPreprocessor(objc_exception_preprocessor fn);
-OBJC_EXPORT objc_exception_matcher objc_setExceptionMatcher(objc_exception_matcher fn);
-OBJC_EXPORT objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn);
-
-#ifndef __arm__
-OBJC_EXPORT uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context);
-OBJC_EXPORT void objc_removeExceptionHandler(uintptr_t token);
-#endif
+OBJC_EXPORT void objc_exception_throw(id exception)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT void objc_exception_rethrow(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT id objc_begin_catch(void *exc_buf)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT void objc_end_catch(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+OBJC_EXPORT objc_exception_preprocessor objc_setExceptionPreprocessor(objc_exception_preprocessor fn)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT objc_exception_matcher objc_setExceptionMatcher(objc_exception_matcher fn)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+// Not for iOS.
+OBJC_EXPORT uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+OBJC_EXPORT void objc_removeExceptionHandler(uintptr_t token)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
// __OBJC2__
#endif
#include "objc-private.h"
#include <stdlib.h>
#include <setjmp.h>
+#include <execinfo.h>
#include "objc-exception.h"
if (!xtab.throw_exc) {
set_default_handlers();
}
+
+ if (PrintExceptionThrow) {
+ _objc_inform("EXCEPTIONS: throwing %p (%s)",
+ exception, object_getClassName(exception));
+ void* callstack[500];
+ int frameCount = backtrace(callstack, 500);
+ backtrace_symbols_fd(callstack, frameCount, fileno(stderr));
+ }
OBJC_RUNTIME_OBJC_EXCEPTION_THROW(exception); // dtrace probe to log throw activity.
xtab.throw_exc(exception);
static int default_match(Class exceptionClass, id exception) {
//return [exception isKindOfClass:exceptionClass];
Class cls;
- for (cls = exception->isa; nil != cls; cls = _class_getSuperclass(cls))
+ for (cls = _object_getClass(exception); nil != cls; cls = _class_getSuperclass(cls))
if (cls == exceptionClass) return 1;
return 0;
}
}
-__private_extern__ void exception_init(void)
+PRIVATE_EXTERN void exception_init(void)
{
// nothing to do
}
-__private_extern__ void _destroyAltHandlerList(struct alt_handler_list *list)
+PRIVATE_EXTERN void _destroyAltHandlerList(struct alt_handler_list *list)
{
// nothing to do
}
#include "objc-private.h"
#include <objc/objc-exception.h>
-
+#include <execinfo.h>
// unwind library types and functions
// Mostly adapted from Itanium C++ ABI: Exception Handling
static const _Unwind_Action _UA_HANDLER_FRAME = 4;
static const _Unwind_Action _UA_FORCE_UNWIND = 8;
-typedef enum {
- _URC_NO_REASON = 0,
- _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
- _URC_FATAL_PHASE2_ERROR = 2,
- _URC_FATAL_PHASE1_ERROR = 3,
- _URC_NORMAL_STOP = 4,
- _URC_END_OF_STACK = 5,
- _URC_HANDLER_FOUND = 6,
- _URC_INSTALL_CONTEXT = 7,
- _URC_CONTINUE_UNWIND = 8
-} _Unwind_Reason_Code;
+typedef int _Unwind_Reason_Code;
+static const _Unwind_Reason_Code _URC_NO_REASON = 0;
+static const _Unwind_Reason_Code _URC_FOREIGN_EXCEPTION_CAUGHT = 1;
+static const _Unwind_Reason_Code _URC_FATAL_PHASE2_ERROR = 2;
+static const _Unwind_Reason_Code _URC_FATAL_PHASE1_ERROR = 3;
+static const _Unwind_Reason_Code _URC_NORMAL_STOP = 4;
+static const _Unwind_Reason_Code _URC_END_OF_STACK = 5;
+static const _Unwind_Reason_Code _URC_HANDLER_FOUND = 6;
+static const _Unwind_Reason_Code _URC_INSTALL_CONTEXT = 7;
+static const _Unwind_Reason_Code _URC_CONTINUE_UNWIND = 8;
struct dwarf_eh_bases
{
// C++ runtime types and functions
-// Mostly adapted from Itanium C++ ABI: Exception Handling
-// http://www.codesourcery.com/cxx-abi/abi-eh.html
+// copied from cxxabi.h
+#if TARGET_OS_IPHONE
typedef void (*terminate_handler) ();
-
// mangled std::set_terminate()
extern terminate_handler _ZSt13set_terminatePFvvE(terminate_handler);
+#else
+extern void (*__cxa_terminate_handler)(void);
+#endif
extern void *__cxa_allocate_exception(size_t thrown_size);
extern void __cxa_throw(void *exc, void *typeinfo, void (*destructor)(void *)) __attribute__((noreturn));
extern void *__cxa_begin_catch(void *exc);
extern void __cxa_rethrow(void);
extern void *__cxa_current_exception_type(void);
-#ifdef NO_ZEROCOST_EXCEPTIONS
-/* fixme _sj0 for objc too? */
-#define CXX_PERSONALITY __gxx_personality_sj0
-#define OBJC_PERSONALITY __objc_personality_v0
+#if SUPPORT_ZEROCOST_EXCEPTIONS
+# define CXX_PERSONALITY __gxx_personality_v0
#else
-#define CXX_PERSONALITY __gxx_personality_v0
-#define OBJC_PERSONALITY __objc_personality_v0
+# define CXX_PERSONALITY __gxx_personality_sj0
#endif
extern _Unwind_Reason_Code
// objc's internal exception types and data
-extern const void *objc_ehtype_vtable[];
-
struct objc_typeinfo {
// Position of vtable and name fields must match C++ typeinfo object
const void **vtable; // always objc_ehtype_vtable+2
static int _objc_default_exception_matcher(Class catch_cls, id exception)
{
Class cls;
- for (cls = exception->isa;
+ for (cls = _object_getClass(exception);
cls != NULL;
cls = class_getSuperclass(cls))
{
static void call_alt_handlers(struct _Unwind_Context *ctx);
_Unwind_Reason_Code
-OBJC_PERSONALITY(int version,
- _Unwind_Action actions,
- uint64_t exceptionClass,
- struct _Unwind_Exception *exceptionObject,
- struct _Unwind_Context *context)
+__objc_personality_v0(int version,
+ _Unwind_Action actions,
+ uint64_t exceptionClass,
+ struct _Unwind_Exception *exceptionObject,
+ struct _Unwind_Context *context)
{
BOOL unwinding = ((actions & _UA_CLEANUP_PHASE) ||
(actions & _UA_FORCE_UNWIND));
exc->tinfo.vtable = objc_ehtype_vtable+2;
exc->tinfo.name = object_getClassName(obj);
- exc->tinfo.cls = obj ? obj->isa : Nil;
+ exc->tinfo.cls = obj ? _object_getClass(obj) : Nil;
if (PrintExceptions) {
_objc_inform("EXCEPTIONS: throwing %p (object %p, a %s)",
exc, obj, object_getClassName(obj));
}
+
+ if (PrintExceptionThrow) {
+ if (!PrintExceptions)
+ _objc_inform("EXCEPTIONS: throwing %p (object %p, a %s)",
+ exc, obj, object_getClassName(obj));
+ void* callstack[500];
+ int frameCount = backtrace(callstack, 500);
+ backtrace_symbols_fd(callstack, frameCount, fileno(stderr));
+ }
OBJC_RUNTIME_OBJC_EXCEPTION_THROW(obj); // dtrace probe to log throw activity
__cxa_throw(exc, &exc->tinfo, &_objc_exception_destructor);
* 3. If so, call our registered callback with the object.
* 4. Finally, call the previous terminate handler.
**********************************************************************/
-static terminate_handler old_terminate = NULL;
+static void (*old_terminate)(void) = NULL;
static void _objc_terminate(void)
{
if (PrintExceptions) {
* alt handler support - zerocost implementation only
**********************************************************************/
-#ifdef NO_ZEROCOST_EXCEPTIONS
+#if !SUPPORT_ALT_HANDLERS
-__private_extern__ void _destroyAltHandlerList(struct alt_handler_list *list)
+PRIVATE_EXTERN void _destroyAltHandlerList(struct alt_handler_list *list)
{
}
#else
#include <libunwind.h>
+#include <execinfo.h>
+#include <dispatch/dispatch.h>
// Dwarf eh data encodings
#define DW_EH_PE_omit 0xff // no data follows
// This data structure assumes the number of
// active alt handlers per frame is small.
+
+// for OBJC_DEBUG_ALT_HANDLERS, record the call to objc_addExceptionHandler.
+#define BACKTRACE_COUNT 46
+#define THREADNAME_COUNT 64
+struct alt_handler_debug {
+ uintptr_t token;
+ int backtraceSize;
+ void *backtrace[BACKTRACE_COUNT];
+ char thread[THREADNAME_COUNT];
+ char queue[THREADNAME_COUNT];
+};
+
struct alt_handler_data {
uintptr_t ip_start;
uintptr_t ip_end;
uintptr_t cfa;
objc_exception_handler fn;
void *context;
+ struct alt_handler_debug *debug;
};
struct alt_handler_list {
unsigned int allocated;
unsigned int used;
struct alt_handler_data *handlers;
+ struct alt_handler_list *next_DEBUGONLY;
};
+static pthread_mutex_t DebugLock = PTHREAD_MUTEX_INITIALIZER;
+static struct alt_handler_list *DebugLists;
+static uintptr_t DebugCounter;
+
+PRIVATE_EXTERN void alt_handler_error(uintptr_t token) __attribute__((noinline));
static struct alt_handler_list *
fetch_handler_list(BOOL create)
if (!create) return NULL;
list = _calloc_internal(1, sizeof(*list));
data->handlerList = list;
+
+ if (DebugAltHandlers) {
+ // Save this list so the debug code can find it from other threads
+ pthread_mutex_lock(&DebugLock);
+ list->next_DEBUGONLY = DebugLists;
+ DebugLists = list;
+ pthread_mutex_unlock(&DebugLock);
+ }
}
return list;
}
-__private_extern__ void _destroyAltHandlerList(struct alt_handler_list *list)
+PRIVATE_EXTERN void _destroyAltHandlerList(struct alt_handler_list *list)
{
if (list) {
+ if (DebugAltHandlers) {
+ // Detach from the list-of-lists.
+ pthread_mutex_lock(&DebugLock);
+ struct alt_handler_list **listp = &DebugLists;
+ while (*listp && *listp != list) listp = &(*listp)->next_DEBUGONLY;
+ if (*listp) *listp = (*listp)->next_DEBUGONLY;
+ pthread_mutex_unlock(&DebugLock);
+ }
+
if (list->handlers) {
_free_internal(list->handlers);
}
data->context = context;
list->used++;
+ uintptr_t token = i+1;
+
+ if (DebugAltHandlers) {
+ // Record backtrace in case this handler is misused later.
+ pthread_mutex_lock(&DebugLock);
+
+ token = DebugCounter++;
+ if (token == 0) token = DebugCounter++;
+
+ if (!data->debug) {
+ data->debug = _calloc_internal(sizeof(*data->debug), 1);
+ } else {
+ bzero(data->debug, sizeof(*data->debug));
+ }
+
+ pthread_getname_np(pthread_self(), data->debug->thread, THREADNAME_COUNT);
+ strlcpy(data->debug->queue,
+ dispatch_queue_get_label(dispatch_get_current_queue()),
+ THREADNAME_COUNT);
+ data->debug->backtraceSize =
+ backtrace(data->debug->backtrace, BACKTRACE_COUNT);
+ data->debug->token = token;
+
+ pthread_mutex_unlock(&DebugLock);
+ }
+
if (PrintAltHandlers) {
- _objc_inform("ALT HANDLERS: installing alt handler %d %p(%p) on "
- "frame [ip=%p..%p sp=%p]", i+1, data->fn, data->context,
- (void *)data->ip_start, (void *)data->ip_end,
- (void *)data->cfa);
+ _objc_inform("ALT HANDLERS: installing alt handler #%lu %p(%p) on "
+ "frame [ip=%p..%p sp=%p]", (unsigned long)token,
+ data->fn, data->context, (void *)data->ip_start,
+ (void *)data->ip_end, (void *)data->cfa);
}
if (list->used > 1000) {
}
}
- return i+1;
+ return token;
}
// objc_addExceptionHandler failed
return;
}
- unsigned int i = (unsigned int)(token - 1);
struct alt_handler_list *list = fetch_handler_list(NO);
- if (!list || list->used == 0) {
- // no handlers present
- if (PrintAltHandlers) {
- _objc_inform("ALT HANDLERS: *** can't remove alt handler %lu "
- "(no alt handlers present)", token);
+ if (!list || !list->handlers) {
+ // no alt handlers active
+ alt_handler_error(token);
+ __builtin_trap();
+ }
+
+ uintptr_t i = token-1;
+
+ if (DebugAltHandlers) {
+ // search for the token instead of using token-1
+ for (i = 0; i < list->allocated; i++) {
+ struct alt_handler_data *data = &list->handlers[i];
+ if (data->debug && data->debug->token == token) break;
}
- return;
}
+
if (i >= list->allocated) {
- // bogus token
- if (PrintAltHandlers) {
- _objc_inform("ALT HANDLERS: *** can't remove alt handler %lu "
- "(current max is %u)", token, list->allocated);
- }
- return;
+ // token out of range
+ alt_handler_error(token);
+ __builtin_trap();
}
struct alt_handler_data *data = &list->handlers[i];
+
+ if (data->ip_start == 0 && data->ip_end == 0 && data->cfa == 0) {
+ // token in range, but invalid
+ alt_handler_error(token);
+ __builtin_trap();
+ }
+
if (PrintAltHandlers) {
- _objc_inform("ALT HANDLERS: removing alt handler %d %p(%p) on "
- "frame [ip=%p..%p sp=%p]", i+1, data->fn, data->context,
- (void *)data->ip_start, (void *)data->ip_end,
- (void *)data->cfa);
+ _objc_inform("ALT HANDLERS: removing alt handler #%lu %p(%p) on "
+ "frame [ip=%p..%p sp=%p]", (unsigned long)token,
+ data->fn, data->context, (void *)data->ip_start,
+ (void *)data->ip_end, (void *)data->cfa);
}
+
+ if (data->debug) _free_internal(data->debug);
bzero(data, sizeof(*data));
list->used--;
}
+PRIVATE_EXTERN void objc_alt_handler_error(void) __attribute__((noinline));
+
+PRIVATE_EXTERN void alt_handler_error(uintptr_t token)
+{
+ if (!DebugAltHandlers) {
+ _objc_inform_now_and_on_crash
+ ("objc_removeExceptionHandler() called with unknown alt handler; "
+ "this is probably a bug in multithreaded AppKit use. "
+ "Set environment variable OBJC_DEBUG_ALT_HANDLERS=YES "
+ "or break in objc_alt_handler_error() to debug.");
+ objc_alt_handler_error();
+ }
+
+ pthread_mutex_lock(&DebugLock);
+
+ // Search other threads' alt handler lists for this handler.
+ struct alt_handler_list *list;
+ for (list = DebugLists; list; list = list->next_DEBUGONLY) {
+ int h;
+ for (h = 0; h < list->allocated; h++) {
+ struct alt_handler_data *data = &list->handlers[h];
+ if (data->debug && data->debug->token == token) {
+ // found it
+ int i;
+
+ // Build a string from the recorded backtrace
+ char *symbolString;
+ char **symbols =
+ backtrace_symbols(data->debug->backtrace,
+ data->debug->backtraceSize);
+ size_t len = 1;
+ for (i = 0; i < data->debug->backtraceSize; i++){
+ len += 4 + strlen(symbols[i]) + 1;
+ }
+ symbolString = _calloc_internal(len, 1);
+ for (i = 0; i < data->debug->backtraceSize; i++){
+ strcat(symbolString, " ");
+ strcat(symbolString, symbols[i]);
+ strcat(symbolString, "\n");
+ }
+
+ free(symbols);
+
+ _objc_inform_now_and_on_crash
+ ("objc_removeExceptionHandler() called with "
+ "unknown alt handler; this is probably a bug in "
+ "multithreaded AppKit use. \n"
+ "The matching objc_addExceptionHandler() was called by:\n"
+ "Thread '%s': Dispatch queue: '%s': \n%s",
+ data->debug->thread, data->debug->queue, symbolString);
+
+ pthread_mutex_unlock(&DebugLock);
+ _free_internal(symbolString);
+
+ objc_alt_handler_error();
+ }
+ }
+ }
+
+ pthread_mutex_lock(&DebugLock);
+
+ // not found
+ _objc_inform_now_and_on_crash
+ ("objc_removeExceptionHandler() called with unknown alt handler; "
+ "this is probably a bug in multithreaded AppKit use");
+ objc_alt_handler_error();
+}
+
+PRIVATE_EXTERN void objc_alt_handler_error(void)
+{
+ __builtin_trap();
+}
// called in order registered, to match 32-bit _NSAddAltHandler2
// fixme reverse registration order matches c++ destructors better
}
}
-// ! NO_ZEROCOST_EXCEPTIONS
+// SUPPORT_ALT_HANDLERS
#endif
* Initialize libobjc's exception handling system.
* Called by map_images().
**********************************************************************/
-__private_extern__ void exception_init(void)
+PRIVATE_EXTERN void exception_init(void)
{
- // call std::set_terminate
+#if TARGET_OS_IPHONE
old_terminate = _ZSt13set_terminatePFvvE(&_objc_terminate);
+#else
+ old_terminate = __cxa_terminate_handler;
+ __cxa_terminate_handler = &_objc_terminate;
+#endif
}
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <malloc/malloc.h>
+#include <assert.h>
+#include "runtime.h"
+#include "objc-os.h"
+#include "objc-private.h"
+#include "message.h"
+#if SUPPORT_GC
+#include "auto_zone.h"
+#endif
+
+enum {
+ // external references to data segment objects all use this type
+ OBJC_XREF_TYPE_STATIC = 3,
+
+ OBJC_XREF_TYPE_MASK = 3
+};
+
+// Macros to encode/decode reference values and types.
+#define encode_pointer_and_type(pointer, type) (~((uintptr_t)(pointer) | type))
+#define decode_pointer(encoded) ((id)((~(encoded)) & (~OBJC_XREF_TYPE_MASK)))
+#define decode_type(encoded) ((~(encoded)) & OBJC_XREF_TYPE_MASK)
+#define encode_index_and_type(index, type) (~((index<<3) | type))
+#define decode_index(encoded) ((~encoded)>>3)
+
+#if SUPPORT_GC
+
+typedef struct {
+ objc_xref_type_t _type; // type of list.
+ dispatch_queue_t _synchronizer; // a reader/write lock
+ __strong void **_buffer; // a retained all pointers block
+ size_t _size; // number of pointers that fit in _list (buffer size)
+ size_t _count; // count of pointers in _list (in use count)
+ size_t _search; // lowest index in list which *might* be unused
+} external_ref_list;
+
+static external_ref_list _xref_lists[2];
+
+#define is_strong(list) (list->_type == OBJC_XREF_STRONG)
+#define is_weak(list) (list->_type == OBJC_XREF_WEAK)
+
+inline static size_t _index_for_type(objc_xref_type_t ref_type) {
+ assert(ref_type == OBJC_XREF_STRONG || ref_type == OBJC_XREF_WEAK);
+ return (ref_type - 1);
+}
+
+static void _initialize_gc() {
+ static dispatch_once_t init_guard;
+ dispatch_once(&init_guard, ^{
+ external_ref_list *_strong_list = &_xref_lists[_index_for_type(OBJC_XREF_STRONG)];
+ _strong_list->_type = OBJC_XREF_STRONG;
+ _strong_list->_synchronizer = dispatch_queue_create("OBJC_XREF_STRONG synchronizer", DISPATCH_QUEUE_CONCURRENT);
+
+ external_ref_list *_weak_list = &_xref_lists[_index_for_type(OBJC_XREF_WEAK)];
+ _weak_list->_type = OBJC_XREF_WEAK;
+ _weak_list->_synchronizer = dispatch_queue_create("OBJC_XREF_WEAK synchronizer", DISPATCH_QUEUE_CONCURRENT);
+ });
+}
+
+#define EMPTY_SLOT ((void*)0x1)
+
+// grow the buffer by one page
+static bool _grow_list(external_ref_list *list) {
+ auto_memory_type_t memory_type = (is_strong(list) ? AUTO_MEMORY_ALL_POINTERS : AUTO_MEMORY_ALL_WEAK_POINTERS);
+ size_t new_size = list->_size + PAGE_SIZE / sizeof(void *);
+ // auto_realloc() has been enhanced to handle strong and weak memory.
+ void **new_list = (void **)(list->_buffer ? malloc_zone_realloc(gc_zone, list->_buffer, new_size * sizeof(void *)) : auto_zone_allocate_object(gc_zone, new_size * sizeof(void *), memory_type, false, false));
+ if (!new_list) _objc_fatal("unable to allocate, size = %ld\n", new_size);
+
+ list->_search = list->_size;
+ // Fill the newly allocated space with empty slot tokens.
+ for (size_t index = list->_size; index < new_size; ++index)
+ new_list[index] = EMPTY_SLOT;
+ list->_size = new_size;
+ auto_zone_root_write_barrier(gc_zone, &list->_buffer, new_list);
+ return true;
+}
+
+
+// find an unused slot in the list, growing the list if necessary
+static size_t _find_unused_index(external_ref_list *list) {
+ size_t index;
+ if (list->_size == list->_count) {
+ _grow_list(list);
+ }
+ // find the lowest unused index in _list
+ index = list->_search;
+ while (list->_buffer[index] != EMPTY_SLOT)
+ index++;
+ // mark the slot as no longer empty, good form for weak slots.
+ list->_buffer[index] = NULL;
+ return index;
+}
+
+
+// return the strong or weak list
+inline static external_ref_list *_list_for_type(objc_xref_type_t ref_type) {
+ return &_xref_lists[_index_for_type(ref_type)];
+}
+
+
+// create a GC external reference
+PRIVATE_EXTERN objc_xref_t _object_addExternalReference_gc(id obj, objc_xref_type_t ref_type) {
+ _initialize_gc();
+ __block size_t index;
+ objc_xref_t xref;
+
+ if (auto_zone_is_valid_pointer(gc_zone, obj)) {
+ external_ref_list *list = _list_for_type(ref_type);
+
+ // writer lock
+ dispatch_barrier_sync(list->_synchronizer, (dispatch_block_t)^{
+ index = _find_unused_index(list);
+ if (ref_type == OBJC_XREF_STRONG) {
+ auto_zone_set_write_barrier(gc_zone, &list->_buffer[index], obj);
+ } else {
+ auto_assign_weak_reference(gc_zone, obj, (const void **)&list->_buffer[index], NULL);
+ }
+ list->_count++;
+ });
+ xref = encode_index_and_type(index, ref_type);
+ } else {
+ // data segment object
+ xref = encode_pointer_and_type(obj, OBJC_XREF_TYPE_STATIC);
+ }
+ return xref;
+}
+
+PRIVATE_EXTERN id _object_readExternalReference_gc(objc_xref_t ref) {
+ _initialize_gc();
+ __block id result;
+ objc_xref_type_t ref_type = decode_type(ref);
+ if (ref_type != OBJC_XREF_TYPE_STATIC) {
+ size_t index = decode_index(ref);
+ external_ref_list *list = _list_for_type(ref_type);
+
+ dispatch_sync(list->_synchronizer, ^{
+ if (index >= list->_size) {
+ _objc_fatal("attempted to resolve invalid external reference\n");
+ }
+ if (ref_type == OBJC_XREF_STRONG)
+ result = (id)list->_buffer[index];
+ else
+ result = (id)auto_read_weak_reference(gc_zone, &list->_buffer[index]);
+ if (result == (id)EMPTY_SLOT)
+ _objc_fatal("attempted to resolve unallocated external reference\n");
+ });
+ } else {
+ // data segment object
+ result = decode_pointer(ref);
+ }
+ return result;
+}
+
+PRIVATE_EXTERN void _object_removeExternalReference_gc(objc_xref_t ref) {
+ _initialize_gc();
+ objc_xref_type_t ref_type = decode_type(ref);
+ if (ref_type != OBJC_XREF_TYPE_STATIC) {
+ size_t index = decode_index(ref);
+ external_ref_list *list = _list_for_type(ref_type);
+
+ dispatch_barrier_sync(list->_synchronizer, ^{
+ if (index >= list->_size) {
+ _objc_fatal("attempted to destroy invalid external reference\n");
+ }
+ id old_value;
+ if (ref_type == OBJC_XREF_STRONG) {
+ old_value = (id)list->_buffer[index];
+ } else {
+ old_value = (id)auto_read_weak_reference(gc_zone, &list->_buffer[index]);
+ auto_assign_weak_reference(gc_zone, NULL, (const void **)&list->_buffer[index], NULL);
+ }
+ list->_buffer[index] = EMPTY_SLOT;
+ if (old_value == (id)EMPTY_SLOT)
+ _objc_fatal("attempted to destroy unallocated external reference\n");
+ list->_count--;
+ if (list->_search > index)
+ list->_search = index;
+ });
+ } else {
+ // nothing for data segment object
+ }
+}
+
+// SUPPORT_GC
+#endif
+
+PRIVATE_EXTERN objc_xref_t _object_addExternalReference_rr(id obj, objc_xref_type_t ref_type) {
+ switch (ref_type) {
+ case OBJC_XREF_STRONG:
+ objc_msgSend(obj, SEL_retain);
+ break;
+ case OBJC_XREF_WEAK:
+ break;
+ default:
+ _objc_fatal("invalid external reference type: %d", (int)ref_type);
+ break;
+ }
+ return encode_pointer_and_type(obj, ref_type);
+}
+
+PRIVATE_EXTERN id _object_readExternalReference_rr(objc_xref_t ref) {
+ id obj = decode_pointer(ref);
+ return obj;
+}
+
+PRIVATE_EXTERN void _object_removeExternalReference_rr(objc_xref_t ref) {
+ id obj = decode_pointer(ref);
+ objc_xref_type_t ref_type = decode_type(ref);
+ switch (ref_type) {
+ case OBJC_XREF_STRONG:
+ objc_msgSend(obj, SEL_release);
+ break;
+ case OBJC_XREF_WEAK:
+ break;
+ default:
+ _objc_fatal("invalid external reference type: %d", (int)ref_type);
+ break;
+ }
+}
+
+objc_xref_t _object_addExternalReference(id obj, objc_xref_t type) {
+#if SUPPORT_GC
+ if (UseGC)
+ return _object_addExternalReference_gc(obj, type);
+ else
+#endif
+ return _object_addExternalReference_rr(obj, type);
+}
+
+id _object_readExternalReference(objc_xref_t ref) {
+#if SUPPORT_GC
+ if (UseGC)
+ return _object_readExternalReference_gc(ref);
+ else
+#endif
+ return _object_readExternalReference_rr(ref);
+}
+
+void _object_removeExternalReference(objc_xref_t ref) {
+#if SUPPORT_GC
+ if (UseGC)
+ _object_removeExternalReference_gc(ref);
+ else
+#endif
+ _object_removeExternalReference_rr(ref);
+}
+
+uintptr_t _object_getExternalHash(id object) {
+#if SUPPORT_GC
+ if (UseCompaction)
+ return auto_zone_get_associative_hash(gc_zone, object);
+ else
+#endif
+ return (uintptr_t)object;
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef _OBJC_FILE_OLD_H
+#define _OBJC_FILE_OLD_H
+
+#include "objc-os.h"
+
+struct objc_module;
+struct old_protocol;
+struct old_class;
+
+__BEGIN_DECLS
+
+extern struct objc_module *_getObjcModules(const header_info *hi, size_t *nmodules);
+extern SEL *_getObjcSelectorRefs(const header_info *hi, size_t *nmess);
+extern BOOL _hasObjcContents(const header_info *hi);
+extern struct old_protocol **_getObjcProtocols(const header_info *hi, size_t *nprotos);
+extern struct old_class **_getObjcClassRefs(const header_info *hi, size_t *nclasses);
+extern const char *_getObjcClassNames(const header_info *hi, size_t *size);
+
+__END_DECLS
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+// Copyright 1988-1996 NeXT Software, Inc.
+
+#if !__OBJC2__
+
+#include "objc-private.h"
+#include "objc-runtime-old.h"
+
+
+#if TARGET_OS_WIN32
+
+PRIVATE_EXTERN const char *_getObjcHeaderName(const headerType *head)
+{
+ return "??";
+}
+
+/*
+PRIVATE_EXTERN Module
+_getObjcModules(const header_info *hi, size_t *nmodules)
+{
+ if (nmodules) *nmodules = hi->os.moduleCount;
+ return hi->os.modules;
+}
+*/
+PRIVATE_EXTERN SEL *
+_getObjcSelectorRefs(const header_info *hi, size_t *nmess)
+{
+ if (nmess) *nmess = hi->os.selrefCount;
+ return hi->os.selrefs;
+}
+
+PRIVATE_EXTERN struct old_protocol **
+_getObjcProtocols(const header_info *hi, size_t *nprotos)
+{
+ if (nprotos) *nprotos = hi->os.protocolCount;
+ return hi->os.protocols;
+}
+
+PRIVATE_EXTERN struct old_class **
+_getObjcClassRefs(const header_info *hi, size_t *nclasses)
+{
+ if (nclasses) *nclasses = hi->os.clsrefCount;
+ return (struct old_class **)hi->os.clsrefs;
+}
+
+// __OBJC,__class_names section only emitted by CodeWarrior rdar://4951638
+PRIVATE_EXTERN const char *
+_getObjcClassNames(const header_info *hi, size_t *size)
+{
+ if (size) *size = 0;
+ return NULL;
+}
+
+#else
+
+#define GETSECT(name, type, sectname) \
+ PRIVATE_EXTERN type *name(const header_info *hi, size_t *outCount) \
+ { \
+ unsigned long byteCount = 0; \
+ type *data = (type *) \
+ getsectiondata(hi->mhdr, SEG_OBJC, sectname, &byteCount); \
+ *outCount = byteCount / sizeof(type); \
+ return data; \
+ }
+
+GETSECT(_getObjcModules, struct objc_module, "__module_info");
+GETSECT(_getObjcSelectorRefs, SEL, "__message_refs");
+GETSECT(_getObjcClassRefs, struct old_class *, "__cls_refs");
+GETSECT(_getObjcClassNames, const char, "__class_names");
+// __OBJC,__class_names section only emitted by CodeWarrior rdar://4951638
+
+
+PRIVATE_EXTERN objc_image_info *
+_getObjcImageInfo(const headerType *mhdr, size_t *outBytes)
+{
+ unsigned long byteCount = 0;
+ objc_image_info *info = (objc_image_info *)
+ getsectiondata(mhdr, SEG_OBJC, "__image_info", &byteCount);
+ *outBytes = byteCount;
+ return info;
+}
+
+
+PRIVATE_EXTERN struct old_protocol **
+_getObjcProtocols(const header_info *hi, size_t *nprotos)
+{
+ unsigned long size = 0;
+ struct old_protocol *protos = (struct old_protocol *)
+ getsectiondata(hi->mhdr, SEG_OBJC, "__protocol", &size);
+ *nprotos = size / sizeof(struct old_protocol);
+
+ if (!hi->os.proto_refs && *nprotos) {
+ size_t i;
+ header_info *whi = (header_info *)hi;
+ whi->os.proto_refs = (struct old_protocol **)
+ malloc(*nprotos * sizeof(*hi->os.proto_refs));
+ for (i = 0; i < *nprotos; i++) {
+ hi->os.proto_refs[i] = protos+i;
+ }
+ }
+
+ return hi->os.proto_refs;
+}
+
+
+static const segmentType *
+getsegbynamefromheader(const headerType *head, const char *segname)
+{
+ const segmentType *sgp;
+ unsigned long i;
+
+ sgp = (const segmentType *) (head + 1);
+ for (i = 0; i < head->ncmds; i++){
+ if (sgp->cmd == SEGMENT_CMD) {
+ if (strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0) {
+ return sgp;
+ }
+ }
+ sgp = (const segmentType *)((char *)sgp + sgp->cmdsize);
+ }
+ return NULL;
+}
+
+PRIVATE_EXTERN BOOL
+_hasObjcContents(const header_info *hi)
+{
+ // Look for an __OBJC,* section other than __OBJC,__image_info
+ const segmentType *seg = getsegbynamefromheader(hi->mhdr, "__OBJC");
+ const sectionType *sect;
+ uint32_t i;
+ for (i = 0; i < seg->nsects; i++) {
+ sect = ((const sectionType *)(seg+1))+i;
+ if (0 != strncmp(sect->sectname, "__image_info", 12)) {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2009 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef _OBJC_FILE_NEW_H
+#define _OBJC_FILE_NEW_H
+
+#include "objc-runtime-new.h"
+
+
+__BEGIN_DECLS
+
+extern SEL *_getObjc2SelectorRefs(const header_info *hi, size_t *count);
+extern message_ref_t *_getObjc2MessageRefs(const header_info *hi, size_t *count);
+extern class_t **_getObjc2ClassRefs(const header_info *hi, size_t *count);
+extern class_t **_getObjc2SuperRefs(const header_info *hi, size_t *count);
+extern class_t **_getObjc2ClassList(const header_info *hi, size_t *count);
+extern class_t **_getObjc2NonlazyClassList(const header_info *hi, size_t *count);
+extern category_t **_getObjc2CategoryList(const header_info *hi, size_t *count);
+extern category_t **_getObjc2NonlazyCategoryList(const header_info *hi, size_t *count);
+extern protocol_t **_getObjc2ProtocolList(const header_info *hi, size_t *count);
+extern protocol_t **_getObjc2ProtocolRefs(const header_info *hi, size_t *count);
+
+__END_DECLS
+
+#endif
+++ /dev/null
-/*
- * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-// Copyright 1988-1996 NeXT Software, Inc.
-
-#define OLD 1
-#include "objc-private.h"
-
-#if TARGET_OS_WIN32
-
-__private_extern__ const char *_getObjcHeaderName(const headerType *head)
-{
- return "??";
-}
-
-/*
-__private_extern__ Module
-_getObjcModules(const header_info *hi, size_t *nmodules)
-{
- if (nmodules) *nmodules = hi->os.moduleCount;
- return hi->os.modules;
-}
-*/
-__private_extern__ SEL *
-_getObjcSelectorRefs(const header_info *hi, size_t *nmess)
-{
- if (nmess) *nmess = hi->os.selrefCount;
- return hi->os.selrefs;
-}
-
-__private_extern__ struct old_protocol **
-_getObjcProtocols(const header_info *hi, size_t *nprotos)
-{
- if (nprotos) *nprotos = hi->os.protocolCount;
- return hi->os.protocols;
-}
-
-__private_extern__ struct old_class **
-_getObjcClassRefs(const header_info *hi, size_t *nclasses)
-{
- if (nclasses) *nclasses = hi->os.clsrefCount;
- return (struct old_class **)hi->os.clsrefs;
-}
-
-// __OBJC,__class_names section only emitted by CodeWarrior rdar://4951638
-__private_extern__ const char *
-_getObjcClassNames(const header_info *hi, size_t *size)
-{
- if (size) *size = 0;
- return NULL;
-}
-
-#else
-
-#ifndef __LP64__
-#define SEGMENT_CMD LC_SEGMENT
-#define GETSECTDATAFROMHEADER(mh, seg, sect, sizep) \
- getsectdatafromheader(mh, seg, sect, (uint32_t *)sizep)
-#else
-#define SEGMENT_CMD LC_SEGMENT_64
-#define GETSECTDATAFROMHEADER(mh, seg, sect, sizep) \
- getsectdatafromheader_64(mh, seg, sect, (uint64_t *)sizep)
-#endif
-
-__private_extern__ objc_image_info *
-_getObjcImageInfo(const headerType *head, ptrdiff_t slide, size_t *sizep)
-{
- objc_image_info *info = (objc_image_info *)
-#if __OBJC2__
- GETSECTDATAFROMHEADER(head, SEG_DATA, "__objc_imageinfo", sizep);
- if (!info) info = (objc_image_info *)
-#endif
- GETSECTDATAFROMHEADER(head, SEG_OBJC, "__image_info", sizep);
- // size is BYTES, not count!
- if (info) info = (objc_image_info *)((uintptr_t)info + slide);
- return info;
-}
-
-// fixme !objc2 only (used for new-abi paranoia)
-__private_extern__ Module
-_getObjcModules(const header_info *hi, size_t *nmodules)
-{
- size_t size;
- void *mods =
- GETSECTDATAFROMHEADER(hi->mhdr, SEG_OBJC, SECT_OBJC_MODULES, &size);
-#if !__OBJC2__
- *nmodules = size / sizeof(struct objc_module);
-#endif
- if (mods) mods = (void *)((uintptr_t)mods + hi->os.image_slide);
- return (Module)mods;
-}
-
-// fixme !objc2 only (used for new-abi paranoia)
-__private_extern__ SEL *
-_getObjcSelectorRefs(const header_info *hi, size_t *nmess)
-{
- size_t size;
- void *refs =
- GETSECTDATAFROMHEADER (hi->mhdr, SEG_OBJC, "__message_refs", &size);
- if (refs) refs = (void *)((uintptr_t)refs + hi->os.image_slide);
- *nmess = size / sizeof(SEL);
- return (SEL *)refs;
-}
-
-#if !__OBJC2__
-
-__private_extern__ BOOL
-_hasObjcContents(const header_info *hi)
-{
- // Look for an __OBJC,* section other than __OBJC,__image_info
- const segmentType *seg = hi->os.objcSegmentHeader;
- const sectionType *sect;
- uint32_t i;
- for (i = 0; i < seg->nsects; i++) {
- sect = ((const sectionType *)(seg+1))+i;
- if (0 != strncmp(sect->sectname, "__image_info", 12)) {
- return YES;
- }
- }
-
- return NO;
-}
-
-__private_extern__ struct old_protocol **
-_getObjcProtocols(const header_info *hi, size_t *nprotos)
-{
- size_t size;
- struct old_protocol *protos = (struct old_protocol *)
- GETSECTDATAFROMHEADER (hi->mhdr, SEG_OBJC, "__protocol", &size);
- *nprotos = size / sizeof(struct old_protocol);
- if (protos) protos = (struct old_protocol *)((uintptr_t)protos+hi->os.image_slide);
-
- if (!hi->os.proto_refs && *nprotos) {
- size_t i;
- header_info *whi = (header_info *)hi;
- whi->os.proto_refs = malloc(*nprotos * sizeof(*hi->os.proto_refs));
- for (i = 0; i < *nprotos; i++) {
- hi->os.proto_refs[i] = protos+i;
- }
- }
-
- return hi->os.proto_refs;
-}
-
-__private_extern__ struct old_class **
-_getObjcClassRefs(const header_info *hi, size_t *nclasses)
-{
- size_t size;
- void *classes =
- GETSECTDATAFROMHEADER(hi->mhdr, SEG_OBJC, "__cls_refs", &size);
- *nclasses = size / sizeof(struct old_class *);
- if (classes) classes = (void *)((uintptr_t)classes + hi->os.image_slide);
- return (struct old_class **)classes;
-}
-
-// __OBJC,__class_names section only emitted by CodeWarrior rdar://4951638
-__private_extern__ const char *
-_getObjcClassNames(const header_info *hi, size_t *size)
-{
- void *names =
- GETSECTDATAFROMHEADER(hi->mhdr, SEG_OBJC, "__class_names", size);
- if (names) names = (void *)((uintptr_t)names + hi->os.image_slide);
- return (const char *)names;
-}
-
-#endif
-
-#if __OBJC2__
-
-__private_extern__ BOOL
-_hasObjcContents(const header_info *hi)
-{
- // Look for a __DATA,__objc* section other than __DATA,__objc_imageinfo
- const segmentType *seg = hi->os.dataSegmentHeader;
- const sectionType *sect;
- uint32_t i;
- for (i = 0; i < seg->nsects; i++) {
- sect = ((const sectionType *)(seg+1))+i;
- if (0 == strncmp(sect->sectname, "__objc_", 7) &&
- 0 != strncmp(sect->sectname, "__objc_imageinfo", 16))
- {
- return YES;
- }
- }
-
- return NO;
-}
-
-__private_extern__ SEL *
-_getObjc2SelectorRefs(const header_info *hi, size_t *nmess)
-{
- size_t size;
- void *refs =
- GETSECTDATAFROMHEADER (hi->mhdr, SEG_DATA, "__objc_selrefs", &size);
- if (refs) refs = (void *)((uintptr_t)refs + hi->os.image_slide);
- *nmess = size / sizeof(SEL);
- return (SEL *)refs;
-}
-
-__private_extern__ message_ref *
-_getObjc2MessageRefs(const header_info *hi, size_t *nmess)
-{
- size_t size;
- void *refs =
- GETSECTDATAFROMHEADER (hi->mhdr, SEG_DATA, "__objc_msgrefs", &size);
- if (refs) refs = (void *)((uintptr_t)refs + hi->os.image_slide);
- *nmess = size / sizeof(message_ref);
- return (message_ref *)refs;
-}
-
-__private_extern__ struct class_t **
-_getObjc2ClassRefs(const header_info *hi, size_t *nclasses)
-{
- size_t size;
- void *classes =
- GETSECTDATAFROMHEADER(hi->mhdr, SEG_DATA, "__objc_classrefs", &size);
- *nclasses = size / sizeof(struct class_t *);
- if (classes) classes = (void *)((uintptr_t)classes + hi->os.image_slide);
- return (struct class_t **)classes;
-}
-
-__private_extern__ struct class_t **
-_getObjc2SuperRefs(const header_info *hi, size_t *nclasses)
-{
- size_t size;
- void *classes =
- GETSECTDATAFROMHEADER(hi->mhdr, SEG_DATA, "__objc_superrefs", &size);
- *nclasses = size / sizeof(struct class_t *);
- if (classes) classes = (void *)((uintptr_t)classes + hi->os.image_slide);
- return (struct class_t **)classes;
-}
-
-__private_extern__ struct class_t **
-_getObjc2ClassList(const header_info *hi, size_t *nclasses)
-{
- size_t size;
- void *classes =
- GETSECTDATAFROMHEADER(hi->mhdr, SEG_DATA, "__objc_classlist", &size);
- *nclasses = size / sizeof(struct class_t *);
- if (classes) classes = (void *)((uintptr_t)classes + hi->os.image_slide);
- return (struct class_t **)classes;
-}
-
-__private_extern__ struct class_t **
-_getObjc2NonlazyClassList(const header_info *hi, size_t *nclasses)
-{
- size_t size;
- void *classes =
- GETSECTDATAFROMHEADER(hi->mhdr, SEG_DATA, "__objc_nlclslist", &size);
- *nclasses = size / sizeof(struct class_t *);
- if (classes) classes = (void *)((uintptr_t)classes + hi->os.image_slide);
- return (struct class_t **)classes;
-}
-
-__private_extern__ struct category_t **
-_getObjc2CategoryList(const header_info *hi, size_t *ncats)
-{
- size_t size;
- void *cats =
- GETSECTDATAFROMHEADER(hi->mhdr, SEG_DATA, "__objc_catlist", &size);
- *ncats = size / sizeof(struct category_t *);
- if (cats) cats = (void *)((uintptr_t)cats + hi->os.image_slide);
- return (struct category_t **)cats;
-}
-
-__private_extern__ struct category_t **
-_getObjc2NonlazyCategoryList(const header_info *hi, size_t *ncats)
-{
- size_t size;
- void *cats =
- GETSECTDATAFROMHEADER(hi->mhdr, SEG_DATA, "__objc_nlcatlist", &size);
- *ncats = size / sizeof(struct category_t *);
- if (cats) cats = (void *)((uintptr_t)cats + hi->os.image_slide);
- return (struct category_t **)cats;
-}
-
-__private_extern__ struct protocol_t **
-_getObjc2ProtocolList(const header_info *hi, size_t *nprotos)
-{
- size_t size;
- void *protos =
- GETSECTDATAFROMHEADER (hi->mhdr, SEG_DATA, "__objc_protolist", &size);
- *nprotos = size / sizeof(struct protocol_t *);
- if (protos) protos = (struct protocol_t **)((uintptr_t)protos+hi->os.image_slide);
- return (struct protocol_t **)protos;
-}
-
-__private_extern__ struct protocol_t **
-_getObjc2ProtocolRefs(const header_info *hi, size_t *nprotos)
-{
- size_t size;
- void *protos =
- GETSECTDATAFROMHEADER (hi->mhdr, SEG_DATA, "__objc_protorefs", &size);
- *nprotos = size / sizeof(struct protocol_t *);
- if (protos) protos = (struct protocol_t **)((uintptr_t)protos+hi->os.image_slide);
- return (struct protocol_t **)protos;
-}
-
-#endif
-
-__private_extern__ const char *
-_getObjcHeaderName(const headerType *header)
-{
- Dl_info info;
-
- if (dladdr(header, &info)) {
- return info.dli_fname;
- }
- else {
- return (*_NSGetArgv())[0];
- }
-}
-
-
-// 1. Find segment with file offset == 0 and file size != 0. This segment's
-// contents span the Mach-O header. (File size of 0 is .bss, for example)
-// 2. Slide is header's address - segment's preferred address
-__private_extern__ ptrdiff_t
-_getImageSlide(const headerType *header)
-{
- unsigned long i;
- const segmentType *sgp = (const segmentType *)(header + 1);
-
- for (i = 0; i < header->ncmds; i++){
- if (sgp->cmd == SEGMENT_CMD) {
- if (sgp->fileoff == 0 && sgp->filesize != 0) {
- return (uintptr_t)header - (uintptr_t)sgp->vmaddr;
- }
- }
- sgp = (const segmentType *)((char *)sgp + sgp->cmdsize);
- }
-
- // uh-oh
- _objc_fatal("could not calculate VM slide for image '%s'",
- _getObjcHeaderName(header));
- return 0; // not reached
-}
-
-
-#endif
--- /dev/null
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if __OBJC2__
+
+#include "objc-private.h"
+#include "objc-file.h"
+
+#if TARGET_IPHONE_SIMULATOR
+// getsectiondata() not yet available
+
+// 1. Find segment with file offset == 0 and file size != 0. This segment's
+// contents span the Mach-O header. (File size of 0 is .bss, for example)
+// 2. Slide is header's address - segment's preferred address
+static ptrdiff_t
+objc_getImageSlide(const struct mach_header *header)
+{
+ unsigned long i;
+ const struct segment_command *sgp = (const struct segment_command *)(header + 1);
+
+ for (i = 0; i < header->ncmds; i++){
+ if (sgp->cmd == LC_SEGMENT) {
+ if (sgp->fileoff == 0 && sgp->filesize != 0) {
+ return (uintptr_t)header - (uintptr_t)sgp->vmaddr;
+ }
+ }
+ sgp = (const struct segment_command *)((char *)sgp + sgp->cmdsize);
+ }
+
+ // uh-oh
+ _objc_fatal("could not calculate VM slide for image");
+ return 0; // not reached
+}
+
+PRIVATE_EXTERN uint8_t *
+objc_getsectiondata(const struct mach_header *mh, const char *segname, const char *sectname, unsigned long *outSize)
+{
+ uint32_t size = 0;
+
+ char *data = getsectdatafromheader(mh, segname, sectname, &size);
+ if (data) {
+ *outSize = size;
+ return (uint8_t *)data + objc_getImageSlide(mh);
+ } else {
+ *outSize = 0;
+ return NULL;
+ }
+}
+
+static const struct segment_command *
+objc_getsegbynamefromheader(const mach_header *head, const char *segname)
+{
+ const struct segment_command *sgp;
+ unsigned long i;
+
+ sgp = (const struct segment_command *) (head + 1);
+ for (i = 0; i < head->ncmds; i++){
+ if (sgp->cmd == LC_SEGMENT) {
+ if (strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0) {
+ return sgp;
+ }
+ }
+ sgp = (const struct segment_command *)((char *)sgp + sgp->cmdsize);
+ }
+ return NULL;
+}
+
+PRIVATE_EXTERN uint8_t *
+objc_getsegmentdata(const struct mach_header *mh, const char *segname, unsigned long *outSize)
+{
+ const struct segment_command *seg;
+
+ seg = objc_getsegbynamefromheader(mh, segname);
+ if (seg) {
+ *outSize = seg->vmsize;
+ return (uint8_t *)seg->vmaddr + objc_getImageSlide(mh);
+ } else {
+ *outSize = 0;
+ return NULL;
+ }
+}
+
+// TARGET_IPHONE_SIMULATOR
+#endif
+
+#define GETSECT(name, type, sectname) \
+ PRIVATE_EXTERN type *name(const header_info *hi, size_t *outCount) \
+ { \
+ unsigned long byteCount = 0; \
+ type *data = (type *) \
+ getsectiondata(hi->mhdr, SEG_DATA, sectname, &byteCount); \
+ *outCount = byteCount / sizeof(type); \
+ return data; \
+ }
+
+// function name content type section name
+GETSECT(_getObjc2SelectorRefs, SEL, "__objc_selrefs");
+GETSECT(_getObjc2MessageRefs, message_ref_t, "__objc_msgrefs");
+GETSECT(_getObjc2ClassRefs, class_t *, "__objc_classrefs");
+GETSECT(_getObjc2SuperRefs, class_t *, "__objc_superrefs");
+GETSECT(_getObjc2ClassList, class_t *, "__objc_classlist");
+GETSECT(_getObjc2NonlazyClassList, class_t *, "__objc_nlclslist");
+GETSECT(_getObjc2CategoryList, category_t *, "__objc_catlist");
+GETSECT(_getObjc2NonlazyCategoryList, category_t *, "__objc_nlcatlist");
+GETSECT(_getObjc2ProtocolList, protocol_t *, "__objc_protolist");
+GETSECT(_getObjc2ProtocolRefs, protocol_t *, "__objc_protorefs");
+
+
+PRIVATE_EXTERN objc_image_info *
+_getObjcImageInfo(const headerType *mhdr, size_t *outBytes)
+{
+ unsigned long byteCount = 0;
+ objc_image_info *data = (objc_image_info *)
+ getsectiondata(mhdr, SEG_DATA, "__objc_imageinfo", &byteCount);
+ *outBytes = byteCount;
+ return data;
+}
+
+
+static const segmentType *
+getsegbynamefromheader(const headerType *head, const char *segname)
+{
+ const segmentType *sgp;
+ unsigned long i;
+
+ sgp = (const segmentType *) (head + 1);
+ for (i = 0; i < head->ncmds; i++){
+ if (sgp->cmd == SEGMENT_CMD) {
+ if (strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0) {
+ return sgp;
+ }
+ }
+ sgp = (const segmentType *)((char *)sgp + sgp->cmdsize);
+ }
+ return NULL;
+}
+
+PRIVATE_EXTERN BOOL
+_hasObjcContents(const header_info *hi)
+{
+ // Look for a __DATA,__objc* section other than __DATA,__objc_imageinfo
+ const segmentType *seg = getsegbynamefromheader(hi->mhdr, "__DATA");
+ if (!seg) return NO;
+
+ const sectionType *sect;
+ uint32_t i;
+ for (i = 0; i < seg->nsects; i++) {
+ sect = ((const sectionType *)(seg+1))+i;
+ if (0 == strncmp(sect->sectname, "__objc_", 7) &&
+ 0 != strncmp(sect->sectname, "__objc_imageinfo", 16))
+ {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+#endif
#ifdef __APPLE_API_PRIVATE
+#define _OBJC_PRIVATE_H_
#include <stdint.h>
#include <objc/hashtable.h>
#include <objc/maptable.h>
+__BEGIN_DECLS
/***********************************************************************
* Trampoline descriptors for gdb.
**********************************************************************/
+#if __OBJC2__ && defined(__x86_64__)
+
typedef struct {
uint32_t offset; // 0 = unused, else code = (uintptr_t)desc + desc->offset
uint32_t flags;
struct objc_trampoline_header *next;
} objc_trampoline_header;
-extern objc_trampoline_header *gdb_objc_trampolines;
+OBJC_EXPORT objc_trampoline_header *gdb_objc_trampolines
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
-extern void gdb_objc_trampolines_changed(objc_trampoline_header *thdr);
+OBJC_EXPORT void gdb_objc_trampolines_changed(objc_trampoline_header *thdr)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
// Notify gdb that gdb_objc_trampolines has changed.
// thdr itself includes the new descriptors; thdr->next is not new.
+#endif
+
/***********************************************************************
* Debugger mode.
// OBJC_DEBUGMODE_FULL requires more locks so later operations are less
// likely to fail.
#define OBJC_DEBUGMODE_FULL (1<<0)
-extern int gdb_objc_startDebuggerMode(uint32_t flags);
+OBJC_EXPORT int gdb_objc_startDebuggerMode(uint32_t flags)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
// Stop debugger mode. Do not call if startDebuggerMode returned zero.
-extern void gdb_objc_endDebuggerMode(void);
+OBJC_EXPORT void gdb_objc_endDebuggerMode(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
// Failure hook when debugger mode tries something that would block.
// Set a breakpoint here to handle it before the runtime causes a trap.
// Debugger mode is still active; call endDebuggerMode to end it.
-extern void gdb_objc_debuggerModeFailure(void);
+OBJC_EXPORT void gdb_objc_debuggerModeFailure(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
+
+// Older debugger-mode mechanism. Too simplistic.
+OBJC_EXPORT BOOL gdb_objc_isRuntimeLocked(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
+
+// Return cls if it's a valid class, or crash.
+OBJC_EXPORT Class gdb_class_getClass(Class cls)
+#if __OBJC2__
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
+#else
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_3_1);
+#endif
+
+// Same as gdb_class_getClass(object_getClass(cls)).
+OBJC_EXPORT Class gdb_object_getClass(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
/***********************************************************************
#if __OBJC2__
// Maps class name to Class, for in-use classes only. NXStrValueMapPrototype.
-extern NXMapTable *gdb_objc_realized_classes;
+OBJC_EXPORT NXMapTable *gdb_objc_realized_classes
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
#else
// Hashes Classes, for all known classes. Custom prototype.
-extern NXHashTable *_objc_debug_class_hash;
+OBJC_EXPORT NXHashTable *_objc_debug_class_hash
+ __OSX_AVAILABLE_STARTING(__MAC_10_2, __IPHONE_NA);
#endif
+
+#ifndef OBJC_NO_GC
+
/***********************************************************************
* Garbage Collector heap dump
**********************************************************************/
/* Dump GC heap; if supplied the name is returned in filenamebuffer. Returns YES on success. */
-OBJC_GC_EXPORT BOOL objc_dumpHeap(char *filenamebuffer, unsigned long length);
+OBJC_EXPORT BOOL objc_dumpHeap(char *filenamebuffer, unsigned long length)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);
#define OBJC_HEAP_DUMP_FILENAME_FORMAT "/tmp/objc-gc-heap-dump-%d-%d"
+#endif
+
+__END_DECLS
#endif
+
#endif
Class *classes;
data = _objc_fetch_pthread_data(create);
- if (data == NULL && !create) return NULL;
+ if (data == NULL) return NULL;
list = data->initializingClasses;
if (list == NULL) {
* Any part of the list may be NULL.
* Called from _objc_pthread_destroyspecific().
**********************************************************************/
-__private_extern__
+PRIVATE_EXTERN
void _destroyInitializingClassList(struct _objc_initializing_classes *list)
{
if (list != NULL) {
*
* Called only from _class_lookupMethodAndLoadCache (or itself).
**********************************************************************/
-__private_extern__ void _class_initialize(Class cls)
+PRIVATE_EXTERN void _class_initialize(Class cls)
{
Class supercls;
BOOL reallyInitialize = NO;
/*
- * Copyright (c) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2009 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* When your program breaks, you get to keep both pieces.
*/
+/*
+ * objc-internal.h: Private SPI for use by other system frameworks.
+ */
+
#include <objc/objc.h>
#include <Availability.h>
+#include <malloc/malloc.h>
+#include <dispatch/dispatch.h>
+__BEGIN_DECLS
+
+// In-place construction of an Objective-C instance.
OBJC_EXPORT id objc_constructInstance(Class cls, void *bytes)
- __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0)
+ OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT void *objc_destructInstance(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0)
+ OBJC_ARC_UNAVAILABLE;
+
+// In-place construction of an Objective-C class.
+OBJC_EXPORT Class objc_initializeClassPair(Class superclass_gen, const char *name, Class cls_gen, Class meta_gen)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0);
+
+#if __OBJC2__ && __LP64__
+// Register a tagged pointer class.
+OBJC_EXPORT void _objc_insert_tagged_isa(unsigned char slotNumber, Class isa)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+#endif
+
+// Batch object allocation using malloc_zone_batch_malloc().
+OBJC_EXPORT unsigned class_createInstances(Class cls, size_t extraBytes,
+ id *results, unsigned num_requested)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3)
+ OBJC_ARC_UNAVAILABLE;
+
+// Get the isa pointer written into objects just before being freed.
+OBJC_EXPORT Class _objc_getFreedObjectClass(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+
+// Substitute receiver for messages to nil.
+// Not supported for all messages to nil.
+OBJC_EXPORT id _objc_setNilReceiver(id newNilReceiver)
+ __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
+OBJC_EXPORT id _objc_getNilReceiver(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
+
+// Return NO if no instance of `cls` has ever owned an associative reference.
+OBJC_EXPORT BOOL class_instancesHaveAssociatedObjects(Class cls)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0);
+
+// Return YES if GC is on and `object` is a GC allocation.
+OBJC_EXPORT BOOL objc_isAuto(id object)
+ __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
+
+// env NSObjCMessageLoggingEnabled
+OBJC_EXPORT void instrumentObjcMessageSends(BOOL flag)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+
+// GC startup callback from Foundation
+OBJC_EXPORT malloc_zone_t *objc_collect_init(int (*callback)(void))
+ __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
+
+// Plainly-implemented GC barriers. Rosetta used to use these.
+OBJC_EXPORT id objc_assign_strongCast_generic(id value, id *dest)
+ UNAVAILABLE_ATTRIBUTE;
+OBJC_EXPORT id objc_assign_global_generic(id value, id *dest)
+ UNAVAILABLE_ATTRIBUTE;
+OBJC_EXPORT id objc_assign_threadlocal_generic(id value, id *dest)
+ UNAVAILABLE_ATTRIBUTE;
+OBJC_EXPORT id objc_assign_ivar_generic(id value, id dest, ptrdiff_t offset)
+ UNAVAILABLE_ATTRIBUTE;
+
+// Install missing-class callback. Used by the late unlamented ZeroLink.
+OBJC_EXPORT void _objc_setClassLoader(BOOL (*newClassLoader)(const char *)) OBJC2_UNAVAILABLE;
+
+// This can go away when AppKit stops calling it (rdar://7811851)
+#if __OBJC2__
+OBJC_EXPORT void objc_setMultithreaded (BOOL flag)
+ __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_NA,__IPHONE_NA);
+#endif
+
+// Used by ExceptionHandling.framework
+#if !__OBJC2__
+OBJC_EXPORT void _objc_error(id rcv, const char *fmt, va_list args)
+ __attribute__((noreturn))
+ __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_NA,__IPHONE_NA);
+
+#endif
+
+// External Reference support. Used to support compaction.
+
+enum {
+ OBJC_XREF_STRONG = 1,
+ OBJC_XREF_WEAK = 2
+};
+typedef uintptr_t objc_xref_type_t;
+typedef uintptr_t objc_xref_t;
+
+OBJC_EXPORT objc_xref_t _object_addExternalReference(id object, objc_xref_type_t type)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+OBJC_EXPORT void _object_removeExternalReference(objc_xref_t xref)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+OBJC_EXPORT id _object_readExternalReference(objc_xref_t xref)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+
+OBJC_EXPORT uintptr_t _object_getExternalHash(id object)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+// Instance-specific instance variable layout.
+
+OBJC_EXPORT void _class_setIvarLayoutAccessor(Class cls_gen, const uint8_t* (*accessor) (id object))
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);
+OBJC_EXPORT const uint8_t *_object_getIvarLayout(Class cls_gen, id object)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);
+
+OBJC_EXPORT BOOL _class_usesAutomaticRetainRelease(Class cls)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+// API to only be called by root classes like NSObject or NSProxy
+
+OBJC_EXPORT
+id
+_objc_rootRetain(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+void
+_objc_rootRelease(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+bool
+_objc_rootReleaseWasZero(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+bool
+_objc_rootTryRetain(id obj)
+__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+bool
+_objc_rootIsDeallocating(id obj)
+__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+id
+_objc_rootAutorelease(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+uintptr_t
+_objc_rootRetainCount(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+id
+_objc_rootInit(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+id
+_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+id
+_objc_rootAlloc(Class cls)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+void
+_objc_rootDealloc(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+void
+_objc_rootFinalize(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+malloc_zone_t *
+_objc_rootZone(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+uintptr_t
+_objc_rootHash(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+void *
+objc_autoreleasePoolPush(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+void
+objc_autoreleasePoolPop(void *context)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+
+OBJC_EXPORT id objc_retain(id obj)
+ __asm__("_objc_retain")
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT void objc_release(id obj)
+ __asm__("_objc_release")
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT id objc_autorelease(id obj)
+ __asm__("_objc_autorelease")
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+// wraps objc_autorelease(obj) in a useful way when used with return values
+OBJC_EXPORT
+id
+objc_autoreleaseReturnValue(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+// wraps objc_autorelease(objc_retain(obj)) in a useful way when used with return values
+OBJC_EXPORT
+id
+objc_retainAutoreleaseReturnValue(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+// called ONLY by ARR by callers to undo the autorelease (if possible), otherwise objc_retain
+OBJC_EXPORT
+id
+objc_retainAutoreleasedReturnValue(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+void
+objc_storeStrong(id *location, id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+id
+objc_retainAutorelease(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+// obsolete.
+OBJC_EXPORT id objc_retain_autorelease(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+id
+objc_loadWeakRetained(id *location)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+id
+objc_initWeak(id *addr, id val)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+void
+objc_destroyWeak(id *addr)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+void
+objc_copyWeak(id *to, id *from)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+void
+objc_moveWeak(id *to, id *from)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+
+OBJC_EXPORT
+void
+_objc_autoreleasePoolPrint(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT BOOL objc_should_deallocate(id object)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT void objc_clear_deallocating(id object)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+
+// to make CF link for now
+
+OBJC_EXPORT
+void *
+_objc_autoreleasePoolPush(void)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+OBJC_EXPORT
+void
+_objc_autoreleasePoolPop(void *context)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+
+
+// API to only be called by classes that provide their own reference count storage
+
+OBJC_EXPORT
+void
+_objc_deallocOnMainThreadHelper(void *context)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+
+#define _OBJC_SUPPORTED_INLINE_REFCNT_LOGIC(_rc_ivar, _dealloc2main) \
+ -(id)retain { \
+ /* this will fail to compile if _rc_ivar is an unsigned type */ \
+ int _retain_count_ivar_must_not_be_unsigned[0L - (__typeof__(_rc_ivar))-1] __attribute__((unused)); \
+ __typeof__(_rc_ivar) _prev = __sync_fetch_and_add(&_rc_ivar, 2); \
+ if (_prev < 0) { \
+ __builtin_trap(); /* BUG: retain of dealloc'ed ref */ \
+ } \
+ return self; \
+ } \
+ -(oneway void)release { \
+ __typeof__(_rc_ivar) _prev = __sync_fetch_and_sub(&_rc_ivar, 2); \
+ if (_prev == 0) { \
+ if (__sync_bool_compare_and_swap(&_rc_ivar, -2, 1)) { \
+ if (_dealloc2main) { \
+ dispatch_barrier_async_f(dispatch_get_main_queue(), \
+ self, _objc_deallocOnMainThreadHelper); \
+ } else { \
+ [self dealloc]; \
+ } \
+ } else { \
+ __builtin_trap(); /* BUG: dangling ref did a retain */ \
+ } \
+ } else if (_prev < 0) { \
+ __builtin_trap(); /* BUG: over-release */ \
+ } \
+ } \
+ -(NSUInteger)retainCount { \
+ return (_rc_ivar + 2) >> 1; \
+ } \
+ -(BOOL)_tryRetain { \
+ __typeof__(_rc_ivar) _prev; \
+ do { \
+ _prev = _rc_ivar; \
+ if (_prev & 1) { \
+ return 0; \
+ } else if (_prev == -2) { \
+ return 0; \
+ } else if (_prev < -2) { \
+ __builtin_trap(); /* BUG: over-release elsewhere */ \
+ } \
+ } while ( ! __sync_bool_compare_and_swap(&_rc_ivar, _prev, _prev + 2)); \
+ return 1; \
+ } \
+ -(BOOL)_isDeallocating { \
+ if (_rc_ivar == -2) { \
+ return 1; \
+ } else if (_rc_ivar < -2) { \
+ __builtin_trap(); /* BUG: over-release elsewhere */ \
+ } \
+ return _rc_ivar & 1; \
+ }
+
+#define _OBJC_SUPPORTED_INLINE_REFCNT(_rc_ivar) _OBJC_SUPPORTED_INLINE_REFCNT_LOGIC(_rc_ivar, 0)
+#define _OBJC_SUPPORTED_INLINE_REFCNT_WITH_DEALLOC2MAIN(_rc_ivar) _OBJC_SUPPORTED_INLINE_REFCNT_LOGIC(_rc_ivar, 1)
+
+__END_DECLS
#endif
// Count one range each of skip and scan.
while (i < bitmap_bits) {
- uint8_t bit = (bits[i/8] >> (i % 8)) & 1;
+ uint8_t bit = (uint8_t)((bits[i/8] >> (i % 8)) & 1);
if (bit) break;
i++;
skip++;
}
while (i < bitmap_bits) {
- uint8_t bit = (bits[i/8] >> (i % 8)) & 1;
+ uint8_t bit = (uint8_t)((bits[i/8] >> (i % 8)) & 1);
if (!bit) break;
i++;
scan++;
skip -= 0xf;
}
if (skip || scan) {
- *l = skip << 4; // NOT incremented - merges with scan
+ *l = (uint8_t)(skip << 4); // NOT incremented - merges with scan
while (scan > 0xf) {
*l++ |= 0x0f; // May merge with short skip; must calloc
scan -= 0xf;
size_t count)
{
// fixme optimize for byte/word at a time
- // Copy backwards in case of overlap
- assert(dst >= src);
- while (count--) {
- size_t srcbit = src + count;
- size_t dstbit = dst + count;
- if (bits.bits[srcbit/8] & (1 << (srcbit % 8))) {
- bits.bits[dstbit/8] |= 1 << (dstbit % 8);
- } else {
- bits.bits[dstbit/8] &= ~(1 << (dstbit % 8));
+
+ if (dst == src) {
+ return;
+ }
+ else if (dst > src) {
+ // Copy backwards in case of overlap
+ size_t pos = count;
+ while (pos--) {
+ size_t srcbit = src + pos;
+ size_t dstbit = dst + pos;
+ if (bits.bits[srcbit/8] & (1 << (srcbit % 8))) {
+ bits.bits[dstbit/8] |= 1 << (dstbit % 8);
+ } else {
+ bits.bits[dstbit/8] &= ~(1 << (dstbit % 8));
+ }
+ }
+ }
+ else {
+ // Copy forwards in case of overlap
+ size_t pos;
+ for (pos = 0; pos < count; pos++) {
+ size_t srcbit = src + pos;
+ size_t dstbit = dst + pos;
+ if (bits.bits[srcbit/8] & (1 << (srcbit % 8))) {
+ bits.bits[dstbit/8] |= 1 << (dstbit % 8);
+ } else {
+ bits.bits[dstbit/8] &= ~(1 << (dstbit % 8));
+ }
}
}
}
* spans an instance size of layoutStringSize); the rest is zero-filled.
* The returned bitmap must be freed with layout_bitmap_free().
**********************************************************************/
-__private_extern__ layout_bitmap
+PRIVATE_EXTERN layout_bitmap
layout_bitmap_create(const unsigned char *layout_string,
size_t layoutStringInstanceSize,
size_t instanceSize, BOOL weak)
return result;
}
-__private_extern__ void
+
+/***********************************************************************
+ * layout_bitmap_create_empty
+ * Allocate a layout bitmap.
+ * The new bitmap spans the given instance size bytes.
+ * The bitmap is empty, to represent an object whose ivars are completely unscanned.
+ * The returned bitmap must be freed with layout_bitmap_free().
+ **********************************************************************/
+PRIVATE_EXTERN layout_bitmap
+layout_bitmap_create_empty(size_t instanceSize, BOOL weak)
+{
+ layout_bitmap result;
+ size_t words = instanceSize / sizeof(id);
+
+ result.weak = weak;
+ result.bitCount = words;
+ result.bitsAllocated = words;
+ result.bits = _calloc_internal((words+7)/8, 1);
+
+ return result;
+}
+
+PRIVATE_EXTERN void
layout_bitmap_free(layout_bitmap bits)
{
if (bits.bits) _free_internal(bits.bits);
}
-__private_extern__ const unsigned char *
+PRIVATE_EXTERN const unsigned char *
layout_string_create(layout_bitmap bits)
{
- return compress_layout(bits.bits, bits.bitCount, bits.weak);
+ const unsigned char *result =
+ compress_layout(bits.bits, bits.bitCount, bits.weak);
+
+#ifndef NDEBUG
+ // paranoia: cycle to bitmap and back to string again, and compare
+ layout_bitmap check = layout_bitmap_create(result, bits.bitCount*sizeof(id),
+ bits.bitCount*sizeof(id), bits.weak);
+ unsigned char *result2 =
+ compress_layout(check.bits, check.bitCount, check.weak);
+ if (result != result2 && 0 != strcmp((char*)result, (char *)result2)) {
+ layout_bitmap_print(bits);
+ layout_bitmap_print(check);
+ _objc_fatal("libobjc bug: mishandled layout bitmap");
+ }
+ free(result2);
+ layout_bitmap_free(check);
+#endif
+
+ return result;
}
-__private_extern__ void
+PRIVATE_EXTERN void
layout_bitmap_set_ivar(layout_bitmap bits, const char *type, size_t offset)
{
// fixme only handles some types
if (type[0] == '@' || 0 == strcmp(type, "^@")) {
// id
// id *
+ // Block ("@?")
set_bits(bits, bit, 1);
}
else if (type[0] == '[') {
* Expand a layout bitmap to span newCount bits.
* The new bits are undefined.
**********************************************************************/
-__private_extern__ void
+PRIVATE_EXTERN void
layout_bitmap_grow(layout_bitmap *bits, size_t newCount)
{
if (bits->bitCount >= newCount) return;
* The bitmap is expanded and bitCount updated if necessary.
* newPos >= oldPos.
**********************************************************************/
-__private_extern__ void
+PRIVATE_EXTERN void
layout_bitmap_slide(layout_bitmap *bits, size_t oldPos, size_t newPos)
{
size_t shift;
}
+/***********************************************************************
+* layout_bitmap_slide_anywhere
+* Slide the end of a layout bitmap relative to the start.
+* Like layout_bitmap_slide, but can slide backwards too.
+* The end of the bitmap is truncated.
+**********************************************************************/
+PRIVATE_EXTERN void
+layout_bitmap_slide_anywhere(layout_bitmap *bits, size_t oldPos, size_t newPos)
+{
+ size_t shift;
+ size_t count;
+
+ if (oldPos == newPos) return;
+
+ if (oldPos < newPos) {
+ layout_bitmap_slide(bits, oldPos, newPos);
+ return;
+ }
+
+ shift = oldPos - newPos;
+ count = bits->bitCount - oldPos;
+ move_bits(*bits, oldPos, newPos, count); // slide
+ bits->bitCount -= shift;
+}
+
+
/***********************************************************************
* layout_bitmap_splat
* Pastes the contents of bitmap src to the start of bitmap dst.
* dst must be at least as long as src.
* Returns YES if any of dst's bits were changed.
**********************************************************************/
-__private_extern__ BOOL
+PRIVATE_EXTERN BOOL
layout_bitmap_splat(layout_bitmap dst, layout_bitmap src,
size_t oldSrcInstanceSize)
{
* dst must be at least as long as src.
* Returns YES if any of dst's bits were changed.
**********************************************************************/
-__private_extern__ BOOL
+PRIVATE_EXTERN BOOL
layout_bitmap_or(layout_bitmap dst, layout_bitmap src, const char *msg)
{
BOOL changed = NO;
* dst must be at least as long as src.
* Returns YES if any of dst's bits were changed.
**********************************************************************/
-__private_extern__ BOOL
+PRIVATE_EXTERN BOOL
layout_bitmap_clear(layout_bitmap dst, layout_bitmap src, const char *msg)
{
BOOL changed = NO;
}
-__private_extern__ void
+PRIVATE_EXTERN void
layout_bitmap_print(layout_bitmap bits)
{
size_t i;
printf("%zu: ", bits.bitCount);
for (i = 0; i < bits.bitCount; i++) {
int set = bits.bits[i/8] & (1 << (i % 8));
- printf("%c", set ? '#' : '_');
+ printf("%c", set ? '#' : '.');
}
printf("\n");
}
-
#if 0
// The code below may be useful when interpreting ivar types more precisely.
*/
#include "objc-private.h"
+#include "objc-load.h"
#if !__OBJC2__ && !TARGET_OS_WIN32
-extern void (*callbackFunction)( Class, const char * );
+extern void (*callbackFunction)( Class, Category );
/**********************************************************************************
* is if one thread loads a module while another thread tries to access the
* loaded classes (using objc_lookUpClass) before the load is complete.
**********************************************************************************/
-int objc_loadModule(const char *moduleName, void (*class_callback) (Class, const char *categoryName), int *errorCode)
+int objc_loadModule(char *moduleName, void (*class_callback) (Class, Category), int *errorCode)
{
int successFlag = 1;
int locErrorCode;
long objc_loadModules (char * modlist[],
void * errStream,
- void (*class_callback) (Class, const char *),
+ void (*class_callback) (Class, Category),
headerType ** hdr_addr,
char * debug_file)
{
* Support for +load methods.
**********************************************************************/
+#ifndef _OBJC_LOADMETHOD_H
+#define _OBJC_LOADMETHOD_H
+
#include "objc-private.h"
+__BEGIN_DECLS
+
extern void add_class_to_loadable_list(Class cls);
extern void add_category_to_loadable_list(Category cat);
extern void remove_class_from_loadable_list(Class cls);
extern void call_load_methods(void);
+__END_DECLS
+
+#endif
// List of classes that need +load called (pending superclass +load)
// This list always has superclasses first because of the way it is constructed
-static struct loadable_class *loadable_classes NOBSS = NULL;
-static int loadable_classes_used NOBSS = 0;
-static int loadable_classes_allocated NOBSS = 0;
+static struct loadable_class *loadable_classes = NULL;
+static int loadable_classes_used = 0;
+static int loadable_classes_allocated = 0;
// List of categories that need +load called (pending parent class +load)
-static struct loadable_category *loadable_categories NOBSS = NULL;
-static int loadable_categories_used NOBSS = 0;
-static int loadable_categories_allocated NOBSS = 0;
+static struct loadable_category *loadable_categories = NULL;
+static int loadable_categories_used = 0;
+static int loadable_categories_allocated = 0;
/***********************************************************************
* Class cls has just become connected. Schedule it for +load if
* it implements a +load method.
**********************************************************************/
-__private_extern__ void add_class_to_loadable_list(Class cls)
+PRIVATE_EXTERN void add_class_to_loadable_list(Class cls)
{
IMP method;
* to its class. Schedule this category for +load after its parent class
* becomes connected and has its own +load method called.
**********************************************************************/
-__private_extern__ void add_category_to_loadable_list(Category cat)
+PRIVATE_EXTERN void add_category_to_loadable_list(Category cat)
{
IMP method;
* Class cls may have been loadable before, but it is now no longer
* loadable (because its image is being unmapped).
**********************************************************************/
-__private_extern__ void remove_class_from_loadable_list(Class cls)
+PRIVATE_EXTERN void remove_class_from_loadable_list(Class cls)
{
recursive_mutex_assert_locked(&loadMethodLock);
* Category cat may have been loadable before, but it is now no longer
* loadable (because its image is being unmapped).
**********************************************************************/
-__private_extern__ void remove_category_from_loadable_list(Category cat)
+PRIVATE_EXTERN void remove_category_from_loadable_list(Category cat)
{
recursive_mutex_assert_locked(&loadMethodLock);
* Locking: loadMethodLock must be held by the caller
* All other locks must not be held.
**********************************************************************/
-__private_extern__ void call_load_methods(void)
+PRIVATE_EXTERN void call_load_methods(void)
{
static BOOL loading = NO;
BOOL more_categories;
lockcount list[0];
} _objc_lock_list;
+static tls_key_t lock_tls;
+
+static void
+destroyLocks(void *value)
+{
+ _objc_lock_list *locks = (_objc_lock_list *)value;
+ // fixme complain about any still-held locks?
+ if (locks) _free_internal(locks);
+}
+
static struct _objc_lock_list *
getLocks(BOOL create)
{
- _objc_pthread_data *data;
_objc_lock_list *locks;
- data = _objc_fetch_pthread_data(create);
- if (!data && !create) return NULL;
+ // Use a dedicated tls key to prevent differences vs non-debug in
+ // usage of objc's other tls keys (required for some unit tests).
+ INIT_ONCE_PTR(lock_tls, tls_create(&destroyLocks), (void)0);
- locks = data->lockList;
+ locks = (_objc_lock_list *)tls_get(lock_tls);
if (!locks) {
if (!create) {
return NULL;
locks = _calloc_internal(1, sizeof(_objc_lock_list) + sizeof(lockcount) * 16);
locks->allocated = 16;
locks->used = 0;
- data->lockList = locks;
+ tls_set(lock_tls, locks);
}
}
if (!create) {
return locks;
} else {
- data->lockList = _calloc_internal(1, sizeof(_objc_lock_list) + 2 * locks->used * sizeof(lockcount));
- data->lockList->used = locks->used;
- data->lockList->allocated = locks->used * 2;
- memcpy(data->lockList->list, locks->list, locks->used * sizeof(lockcount));
- _free_internal(locks);
- locks = data->lockList;
+ _objc_lock_list *oldlocks = locks;
+ locks = _calloc_internal(1, sizeof(_objc_lock_list) + 2 * oldlocks->used * sizeof(lockcount));
+ locks->used = oldlocks->used;
+ locks->allocated = oldlocks->used * 2;
+ memcpy(locks->list, oldlocks->list, locks->used * sizeof(lockcount));
+ tls_set(lock_tls, locks);
+ _free_internal(oldlocks);
}
}
_objc_fatal("lock not found!");
}
-__private_extern__ void
-_destroyLockList(struct _objc_lock_list *locks)
-{
- // fixme complain about any still-held locks?
- if (locks) _free_internal(locks);
-}
-
/***********************************************************************
* Mutex checking
**********************************************************************/
-__private_extern__ int
+PRIVATE_EXTERN int
_mutex_lock_debug(mutex_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(YES);
return _mutex_lock_nodebug(lock);
}
-__private_extern__ int
+PRIVATE_EXTERN int
_mutex_try_lock_debug(mutex_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(YES);
return result;
}
-__private_extern__ int
+PRIVATE_EXTERN int
_mutex_unlock_debug(mutex_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
return _mutex_unlock_nodebug(lock);
}
-__private_extern__ void
+PRIVATE_EXTERN void
_mutex_assert_locked_debug(mutex_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
}
-__private_extern__ void
+PRIVATE_EXTERN void
_mutex_assert_unlocked_debug(mutex_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
* Recursive mutex checking
**********************************************************************/
-__private_extern__ int
+PRIVATE_EXTERN int
_recursive_mutex_lock_debug(recursive_mutex_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(YES);
return _recursive_mutex_lock_nodebug(lock);
}
-__private_extern__ int
+PRIVATE_EXTERN int
_recursive_mutex_try_lock_debug(recursive_mutex_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(YES);
return result;
}
-__private_extern__ int
+PRIVATE_EXTERN int
_recursive_mutex_unlock_debug(recursive_mutex_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
return _recursive_mutex_unlock_nodebug(lock);
}
-__private_extern__ void
+PRIVATE_EXTERN void
_recursive_mutex_assert_locked_debug(recursive_mutex_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
}
-__private_extern__ void
+PRIVATE_EXTERN void
_recursive_mutex_assert_unlocked_debug(recursive_mutex_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
* Monitor checking
**********************************************************************/
-__private_extern__ int
+PRIVATE_EXTERN int
_monitor_enter_debug(monitor_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(YES);
return _monitor_enter_nodebug(lock);
}
-__private_extern__ int
+PRIVATE_EXTERN int
_monitor_exit_debug(monitor_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
return _monitor_exit_nodebug(lock);
}
-__private_extern__ int
+PRIVATE_EXTERN int
_monitor_wait_debug(monitor_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
return _monitor_wait_nodebug(lock);
}
-__private_extern__ void
+PRIVATE_EXTERN void
_monitor_assert_locked_debug(monitor_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
}
}
-__private_extern__ void
+PRIVATE_EXTERN void
_monitor_assert_unlocked_debug(monitor_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
* rwlock checking
**********************************************************************/
-__private_extern__ void
+PRIVATE_EXTERN void
_rwlock_read_debug(rwlock_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(YES);
_rwlock_read_nodebug(lock);
}
-__private_extern__ int
+PRIVATE_EXTERN int
_rwlock_try_read_debug(rwlock_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(YES);
return result;
}
-__private_extern__ void
+PRIVATE_EXTERN void
_rwlock_unlock_read_debug(rwlock_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
_rwlock_unlock_read_nodebug(lock);
}
-__private_extern__ void
+PRIVATE_EXTERN void
_rwlock_write_debug(rwlock_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(YES);
}
-__private_extern__ int
+PRIVATE_EXTERN int
_rwlock_try_write_debug(rwlock_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(YES);
return result;
}
-__private_extern__ void
+PRIVATE_EXTERN void
_rwlock_unlock_write_debug(rwlock_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
}
-__private_extern__ void
+PRIVATE_EXTERN void
_rwlock_assert_reading_debug(rwlock_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
}
}
-__private_extern__ void
+PRIVATE_EXTERN void
_rwlock_assert_writing_debug(rwlock_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
}
}
-__private_extern__ void
+PRIVATE_EXTERN void
_rwlock_assert_locked_debug(rwlock_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
}
}
-__private_extern__ void
+PRIVATE_EXTERN void
_rwlock_assert_unlocked_debug(rwlock_t *lock, const char *name)
{
_objc_lock_list *locks = getLocks(NO);
#if TARGET_OS_MAC
+# ifndef __STDC_LIMIT_MACROS
+# define __STDC_LIMIT_MACROS
+# endif
+
# include <stdio.h>
# include <stdlib.h>
# include <stdint.h>
# include <pthread.h>
# include <crt_externs.h>
# include <AssertMacros.h>
-# include <Block_private.h>
+# undef check
# include <AvailabilityMacros.h>
# include <TargetConditionals.h>
# include <sys/mman.h>
# include <System/pthread_machdep.h>
# include "objc-probes.h" // generated dtrace probe definitions.
+#define ARR_SPINLOCK_INIT 0
+// XXX -- Careful: OSSpinLock isn't volatile, but should be
+typedef volatile int ARRSpinLock;
+__attribute__((always_inline))
+static inline void ARRSpinLockLock(ARRSpinLock *l)
+{
+ unsigned y;
+again:
+ if (__builtin_expect(__sync_lock_test_and_set(l, 1), 0) == 0) {
+ return;
+ }
+ for (y = 1000; y; y--) {
+#if defined(__i386__) || defined(__x86_64__)
+ asm("pause");
+#endif
+ if (*l == 0) goto again;
+ }
+ thread_switch(THREAD_NULL, SWITCH_OPTION_DEPRESS, 1);
+ goto again;
+}
+__attribute__((always_inline))
+static inline void ARRSpinLockUnlock(ARRSpinLock *l)
+{
+ __sync_lock_release(l);
+}
+
+#define OSSpinLock ARRSpinLock
+#define OSSpinLockTry(l) __sync_bool_compare_and_swap(l, 0, 1)
+#define OSSpinLockLock(l) ARRSpinLockLock(l)
+#define OSSpinLockUnlock(l) ARRSpinLockUnlock(l)
+#undef OS_SPINLOCK_INIT
+#define OS_SPINLOCK_INIT ARR_SPINLOCK_INIT
+
+#if !TARGET_OS_IPHONE
+# include <CrashReporterClient.h>
+#else
+ // CrashReporterClient not yet available on iOS
+ __BEGIN_DECLS
+ extern const char *CRSetCrashLogMessage(const char *msg);
+ extern const char *CRGetCrashLogMessage(void);
+ extern const char *CRSetCrashLogMessage2(const char *msg);
+ __END_DECLS
+#endif
+
+#if TARGET_IPHONE_SIMULATOR
+ // getsectiondata() and getsegmentdata() are unavailable
+ __BEGIN_DECLS
+# define getsectiondata(m, s, n, c) objc_getsectiondata(m, s, n, c)
+# define getsegmentdata(m, s, c) objc_getsegmentdata(m, s, c)
+ extern uint8_t *objc_getsectiondata(const struct mach_header *mh, const char *segname, const char *sectname, unsigned long *outSize);
+ extern uint8_t * objc_getsegmentdata(const struct mach_header *mh, const char *segname, unsigned long *outSize);
+ __END_DECLS
+#endif
+
# if __cplusplus
# include <vector>
# include <algorithm>
using namespace __gnu_cxx;
# endif
+# define PRIVATE_EXTERN __attribute__((visibility("hidden")))
+# undef __private_extern__
+# define __private_extern__ use_PRIVATE_EXTERN_instead
+# undef private_extern
+# define private_extern use_PRIVATE_EXTERN_instead
+
+/* Use this for functions that are intended to be breakpoint hooks.
+ If you do not, the compiler may optimize them away.
+ BREAKPOINT_FUNCTION( void stop_on_error(void) ); */
+# define BREAKPOINT_FUNCTION(prototype) \
+ __attribute__((noinline, visibility("hidden"))) \
+ prototype { asm(""); }
+
#elif TARGET_OS_WIN32
# define WINVER 0x0501 // target Windows XP and later
# define __END_DECLS /*empty*/
# endif
-# define __private_extern__
+# define PRIVATE_EXTERN
# define __attribute__(x)
# define inline __inline
+/* Use this for functions that are intended to be breakpoint hooks.
+ If you do not, the compiler may optimize them away.
+ BREAKPOINT_FUNCTION( void MyBreakpointFunction(void) ); */
+# define BREAKPOINT_FUNCTION(prototype) \
+ __declspec(noinline) prototype { __asm { } }
+
/* stub out dtrace probes */
# define OBJC_RUNTIME_OBJC_EXCEPTION_RETHROW() do {} while(0)
# define OBJC_RUNTIME_OBJC_EXCEPTION_THROW(arg0) do {} while(0)
if (var) break; \
typeof(var) v = create; \
while (!var) { \
- if (OSAtomicCompareAndSwapPtrBarrier(0, v, (void**)&var)) { \
+ if (OSAtomicCompareAndSwapPtrBarrier(0, (void*)v, (void**)&var)){ \
goto done; \
} \
} \
// Thread keys reserved by libc for our use.
// Keys [0..4] are used by autozone.
#if defined(__PTK_FRAMEWORK_OBJC_KEY5)
+# define SUPPORT_DIRECT_THREAD_KEYS 1
# define TLS_DIRECT_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY5)
# define SYNC_DATA_DIRECT_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY6)
# define SYNC_COUNT_DIRECT_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY7)
-// define DIRECT_4_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY8)
-// define DIRECT_5_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY9)
+# define AUTORELEASE_POOL_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY8)
+# if SUPPORT_RETURN_AUTORELEASE
+# define AUTORELEASE_POOL_RECLAIM_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY9)
+# endif
#else
-# define NO_DIRECT_THREAD_KEYS 1
+# define SUPPORT_DIRECT_THREAD_KEYS 0
#endif
DWORD key;
void (*dtor)(void *);
} tls_key_t;
-static __inline void tls_create(tls_key_t *k, void (*dtor)(void*)) {
+static __inline tls_key_t tls_create(void (*dtor)(void*)) {
// fixme need dtor registry for DllMain to call on thread detach
- k->key = TlsAlloc();
- k->dtor = dtor;
+ tls_key_t k;
+ k.key = TlsAlloc();
+ k.dtor = dtor;
+ return k;
}
static __inline void *tls_get(tls_key_t k) {
return TlsGetValue(k.key);
size_t selrefCount;
struct objc_class **clsrefs;
size_t clsrefCount;
+ TCHAR *moduleName;
} os_header_info;
typedef IMAGE_DOS_HEADER headerType;
// OS headers
+#include <mach-o/loader.h>
+#ifndef __LP64__
+# define SEGMENT_CMD LC_SEGMENT
+#else
+# define SEGMENT_CMD LC_SEGMENT_64
+#endif
+
+#ifndef VM_MEMORY_OBJC_DISPATCHERS
+# define VM_MEMORY_OBJC_DISPATCHERS 0
+#endif
// Compiler compatibility
typedef pthread_key_t tls_key_t;
-static inline void tls_create(tls_key_t *k, void (*dtor)(void*)) {
- pthread_key_create(k, dtor);
+static inline tls_key_t tls_create(void (*dtor)(void*)) {
+ tls_key_t k;
+ pthread_key_create(&k, dtor);
+ return k;
}
static inline void *tls_get(tls_key_t k) {
return pthread_getspecific(k);
pthread_setspecific(k, value);
}
-#ifndef NO_DIRECT_THREAD_KEYS
+#if SUPPORT_DIRECT_THREAD_KEYS
static inline void *tls_get_direct(tls_key_t k)
{
assert(k == SYNC_DATA_DIRECT_KEY ||
typedef struct {
Dl_info dl_info;
- const segmentType * objcSegmentHeader;
- const segmentType * dataSegmentHeader;
- ptrdiff_t image_slide;
#if !__OBJC2__
struct old_protocol **proto_refs;
#endif
* OS portability layer.
**********************************************************************/
-#define OLD 1
#include "objc-os.h"
#include "objc-private.h"
#include "objc-loadmethod.h"
#if TARGET_OS_WIN32
+#include "objc-runtime-old.h"
#include "objcrt.h"
malloc_zone_t *_objc_internal_zone(void)
if (hi->os.modules[i]) memcpy(&hi->mod_ptr[hi->mod_count++], hi->os.modules[i], sizeof(struct objc_module));
}
}
+
+ hi->os.moduleName = malloc(MAX_PATH * sizeof(TCHAR));
+ GetModuleFileName((HMODULE)(hi->mhdr), hi->os.moduleName, MAX_PATH * sizeof(TCHAR));
_objc_appendHeader(hi);
}
+PRIVATE_EXTERN bool crashlog_header_name(header_info *hi)
+{
+ return true;
+}
+
+
// TARGET_OS_WIN32
#elif TARGET_OS_MAC
-__private_extern__ void mutex_init(mutex_t *m)
+#if !__OBJC2__
+#include "objc-file-old.h"
+#endif
+
+PRIVATE_EXTERN void mutex_init(mutex_t *m)
{
pthread_mutex_init(m, NULL);
}
-__private_extern__ void recursive_mutex_init(recursive_mutex_t *m)
+PRIVATE_EXTERN void recursive_mutex_init(recursive_mutex_t *m)
{
// fixme error checking
pthread_mutex_t *newmutex;
* bad_magic.
* Return YES if the header has invalid Mach-o magic.
**********************************************************************/
-__private_extern__ BOOL bad_magic(const headerType *mhdr)
+PRIVATE_EXTERN BOOL bad_magic(const headerType *mhdr)
{
return (mhdr->magic != MH_MAGIC && mhdr->magic != MH_MAGIC_64 &&
mhdr->magic != MH_CIGAM && mhdr->magic != MH_CIGAM_64);
}
-static const segmentType *
-getsegbynamefromheader(const headerType *head, const char *segname)
-{
-#ifndef __LP64__
-#define SEGMENT_CMD LC_SEGMENT
-#else
-#define SEGMENT_CMD LC_SEGMENT_64
-#endif
- const segmentType *sgp;
- unsigned long i;
-
- sgp = (const segmentType *) (head + 1);
- for (i = 0; i < head->ncmds; i++){
- if (sgp->cmd == SEGMENT_CMD) {
- if (strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0) {
- return sgp;
- }
- }
- sgp = (const segmentType *)((char *)sgp + sgp->cmdsize);
- }
- return NULL;
-#undef SEGMENT_CMD
-}
-
-
static header_info * _objc_addHeader(const headerType *mhdr)
{
size_t info_size = 0;
- const segmentType *objc_segment;
+ unsigned long seg_size;
+ const uint8_t *objc_segment;
const objc_image_info *image_info;
- const segmentType *data_segment;
header_info *result;
- ptrdiff_t image_slide;
if (bad_magic(mhdr)) return NULL;
}
// Locate the __OBJC segment
- image_slide = _getImageSlide(mhdr);
- image_info = _getObjcImageInfo(mhdr, image_slide, &info_size);
- objc_segment = getsegbynamefromheader(mhdr, SEG_OBJC);
- data_segment = getsegbynamefromheader(mhdr, SEG_DATA);
+ image_info = _getObjcImageInfo(mhdr, &info_size);
+ objc_segment = getsegmentdata(mhdr, SEG_OBJC, &seg_size);
if (!objc_segment && !image_info) return NULL;
// Allocate a header_info entry.
// Set up the new header_info entry.
result->mhdr = mhdr;
- result->os.image_slide = image_slide;
- result->os.objcSegmentHeader = objc_segment;
- result->os.dataSegmentHeader = data_segment;
#if !__OBJC2__
- // mhdr and image_slide must already be set
+ // mhdr must already be set
result->mod_count = 0;
result->mod_ptr = _getObjcModules(result, &result->mod_count);
#endif
}
-#ifndef NO_GC
+#if !SUPPORT_GC
+
+PRIVATE_EXTERN const char *_gcForHInfo(const header_info *hinfo)
+{
+ return "";
+}
+PRIVATE_EXTERN const char *_gcForHInfo2(const header_info *hinfo)
+{
+ return "";
+}
+
+#else
/***********************************************************************
* _gcForHInfo.
**********************************************************************/
-__private_extern__ const char *_gcForHInfo(const header_info *hinfo)
+PRIVATE_EXTERN const char *_gcForHInfo(const header_info *hinfo)
{
- if (_objcHeaderRequiresGC(hinfo)) return "requires GC";
- else if (_objcHeaderSupportsGC(hinfo)) return "supports GC";
- else return "does not support GC";
+ if (_objcHeaderRequiresGC(hinfo)) {
+ if (_objcHeaderSupportsCompaction(hinfo))
+ return "requires GC, supports compaction";
+ else
+ return "requires GC";
+ } else if (_objcHeaderSupportsGC(hinfo)) {
+ if (_objcHeaderSupportsCompaction(hinfo))
+ return "supports GC, supports compaction";
+ else
+ return "supports GC";
+ } else {
+ return "does not support GC";
+ }
}
-__private_extern__ const char *_gcForHInfo2(const header_info *hinfo)
+PRIVATE_EXTERN const char *_gcForHInfo2(const header_info *hinfo)
{
- if (_objcHeaderRequiresGC(hinfo)) return " (requires GC)";
- else if (_objcHeaderSupportsGC(hinfo)) return " (supports GC)";
- else return "";
+ if (_objcHeaderRequiresGC(hinfo)) {
+ if (_objcHeaderSupportsCompaction(hinfo))
+ return "(requires GC) (supports compaction)";
+ else
+ return "(requires GC)";
+ } else if (_objcHeaderSupportsGC(hinfo)) {
+ if (_objcHeaderSupportsCompaction(hinfo))
+ return "(supports GC) (supports compaction)";
+ else
+ return "(supports GC)";
+ }
+ return "";
}
* all already-loaded libraries support the executable's GC mode.
* Returns TRUE if the executable wants GC on.
**********************************************************************/
-static BOOL check_wants_gc(void)
+static void check_wants_gc(BOOL *appWantsGC, BOOL *appSupportsCompaction)
{
const header_info *hi;
- BOOL appWantsGC;
// Environment variables can override the following.
if (DisableGC) {
_objc_inform("GC: forcing GC OFF because OBJC_DISABLE_GC is set");
- appWantsGC = NO;
+ *appWantsGC = NO;
+ *appSupportsCompaction = NO;
}
else {
// Find the executable and check its GC bits.
// If the executable cannot be found, default to NO.
// (The executable will not be found if the executable contains
// no Objective-C code.)
- appWantsGC = NO;
+ *appWantsGC = NO;
+ *appSupportsCompaction = NO;
for (hi = FirstHeader; hi != NULL; hi = hi->next) {
if (hi->mhdr->filetype == MH_EXECUTE) {
- appWantsGC = _objcHeaderSupportsGC(hi) ? YES : NO;
+ *appWantsGC = _objcHeaderSupportsGC(hi) ? YES : NO;
+ *appSupportsCompaction = (*appWantsGC && _objcHeaderSupportsCompaction(hi)) ? YES : NO;
if (PrintGC) {
_objc_inform("GC: executable '%s' %s",
_nameForHeader(hi->mhdr), _gcForHInfo(hi));
}
}
}
- return appWantsGC;
}
* if we want gc, verify that every header describes files compiled
* and presumably ready for gc.
************************************************************************/
-static void verify_gc_readiness(BOOL wantsGC, header_info **hList,
- uint32_t hCount)
+static void verify_gc_readiness(BOOL wantsGC, BOOL *wantsCompaction,
+ header_info **hList, uint32_t hCount)
{
BOOL busted = NO;
uint32_t i;
_nameForHeader(hi->mhdr));
busted = YES;
}
+
+ if (*wantsCompaction && !_objcHeaderSupportsCompaction(hi)) {
+ // App supports compaction, but library doesn't.
+ _objc_inform_now_and_on_crash
+ ("'%s' was not linked with -Xlinker -objc_gc_compaction, "
+ "but the application wants compaction.",
+ _nameForHeader(hi->mhdr));
+ // Simply warn for now until radars are filed. Eventually,
+ // objc_disableCompaction() will block until any current compaction completes.
+ objc_disableCompaction();
+ *wantsCompaction = NO;
+ }
if (PrintGC) {
_objc_inform("GC: library '%s' %s",
}
for (i = 0; i < infoCount; i++) {
+ crashlog_header_name_string(info[i].imageFilePath);
+
const headerType *mhdr = (const headerType *)info[i].imageLoadAddress;
if (bad_magic(mhdr)) continue;
}
#if !__OBJC2__
+ unsigned long seg_size;
// 32-bit: __OBJC seg but no image_info means no GC support
- if (!getsegbynamefromheader(mhdr, SEG_OBJC)) {
+ if (!getsegmentdata(mhdr, "__OBJC", &seg_size)) {
// not objc - assume OK
continue;
}
- image_info = _getObjcImageInfo(mhdr, _getImageSlide(mhdr), &size);
+ image_info = _getObjcImageInfo(mhdr, &size);
if (!image_info) {
// No image_info - assume GC unsupported
if (!UseGC) {
if (PrintImages || PrintGC) {
_objc_inform("IMAGES: rejecting %d images because %s doesn't support GC (no image_info)", infoCount, info[i].imageFilePath);
}
- return "GC capability mismatch";
+ goto reject;
}
}
#else
// 64-bit: no image_info means no objc at all
- image_info = _getObjcImageInfo(mhdr, _getImageSlide(mhdr), &size);
+ image_info = _getObjcImageInfo(mhdr, &size);
if (!image_info) {
// not objc - assume OK
continue;
if (PrintImages || PrintGC) {
_objc_inform("IMAGES: rejecting %d images because %s doesn't support GC", infoCount, info[i].imageFilePath);
}
- return "GC capability mismatch";
+ goto reject;
}
if (!UseGC && _objcInfoRequiresGC(image_info)) {
// GC is OFF, but image requires GC
if (PrintImages || PrintGC) {
_objc_inform("IMAGES: rejecting %d images because %s requires GC", infoCount, info[i].imageFilePath);
}
- return "GC capability mismatch";
+ goto reject;
}
}
+ crashlog_header_name_string(NULL);
return NULL;
+
+ reject:
+ crashlog_header_name_string(NULL);
+ return "GC capability mismatch";
}
-// !defined(NO_GC)
+// SUPPORT_GC
#endif
*
* Locking: loadMethodLock(old) or runtimeLock(new) acquired by map_images.
**********************************************************************/
-__private_extern__ const char *
+PRIVATE_EXTERN const char *
map_images_nolock(enum dyld_image_states state, uint32_t infoCount,
const struct dyld_image_info infoList[])
{
static BOOL firstTime = YES;
- static BOOL wantsGC NOBSS = NO;
+ static BOOL wantsGC = NO;
+ static BOOL wantsCompaction = NO;
uint32_t i;
header_info *hi;
header_info *hList[infoCount];
// This function is called before ordinary library initializers.
// fixme defer initialization until an objc-using image is found?
if (firstTime) {
-#ifndef NO_GC
+#if SUPPORT_GC
InitialDyldRegistration = YES;
dyld_register_image_state_change_handler(dyld_image_state_mapped, 0 /* batch */, &gc_enforcer);
InitialDyldRegistration = NO;
// executable does not contain Objective-C code but Objective-C
// is dynamically loaded later. In that case, check_wants_gc()
// will do the right thing.)
-#ifndef NO_GC
+#if SUPPORT_GC
if (firstTime) {
- wantsGC = check_wants_gc();
+ check_wants_gc(&wantsGC, &wantsCompaction);
- verify_gc_readiness(wantsGC, hList, hCount);
+ verify_gc_readiness(wantsGC, &wantsCompaction, hList, hCount);
- gc_init(wantsGC); // needs executable for GC decision
- rtp_init(); // needs GC decision first
+ gc_init(wantsGC, wantsCompaction); // needs executable for GC decision
+ rtp_init(); // needs GC decision first
} else {
- verify_gc_readiness(wantsGC, hList, hCount);
+ verify_gc_readiness(wantsGC, &wantsCompaction, hList, hCount);
}
if (wantsGC) {
// tell the collector about the data segment ranges.
for (i = 0; i < hCount; ++i) {
+ uint8_t *seg;
+ unsigned long seg_size;
hi = hList[i];
- const segmentType *dataSegment = hi->os.dataSegmentHeader;
- const segmentType *objcSegment = hi->os.objcSegmentHeader;
- if (dataSegment) {
- gc_register_datasegment(dataSegment->vmaddr + hi->os.image_slide, dataSegment->vmsize);
- }
- if (objcSegment) {
- // __OBJC contains no GC data, but pointers to it are
- // used as associated reference values (rdar://6953570)
- gc_register_datasegment(objcSegment->vmaddr + hi->os.image_slide, objcSegment->vmsize);
- }
+
+ seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size);
+ if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
+
+ seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
+ if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
+ // __OBJC contains no GC data, but pointers to it are
+ // used as associated reference values (rdar://6953570)
}
}
+
+ // Need to fixup barriers in all libraries that call into libobjc, whether GC is on or not.
+ for (i = 0; i < infoCount; ++i) {
+ gc_fixup_barrier_stubs(&infoList[i]);
+ }
#endif
if (firstTime) {
extern SEL FwdSel; // in objc-msg-*.s
sel_init(wantsGC);
FwdSel = sel_registerName("forward::");
+
+ arr_init();
}
_read_images(hList, hCount);
*
* Locking: loadMethodLock(both) and runtimeLock(new) acquired by load_images
**********************************************************************/
-__private_extern__ BOOL
+PRIVATE_EXTERN BOOL
load_images_nolock(enum dyld_image_states state,uint32_t infoCount,
const struct dyld_image_info infoList[])
{
*
* Locking: loadMethodLock(both) and runtimeLock(new) acquired by unmap_image.
**********************************************************************/
-__private_extern__ void
-unmap_image_nolock(const struct mach_header *mh, intptr_t vmaddr_slide)
+PRIVATE_EXTERN void
+unmap_image_nolock(const struct mach_header *mh)
{
if (PrintImages) {
_objc_inform("IMAGES: processing 1 newly-unmapped image...\n");
_gcForHInfo2(hi));
}
-#ifndef NO_GC
+#if SUPPORT_GC
if (UseGC) {
- const segmentType *dataSegment = hi->os.dataSegmentHeader;
- const segmentType *objcSegment = hi->os.objcSegmentHeader;
- if (dataSegment) {
- gc_unregister_datasegment(dataSegment->vmaddr + hi->os.image_slide, dataSegment->vmsize);
- }
- if (objcSegment) {
- gc_unregister_datasegment(objcSegment->vmaddr + hi->os.image_slide, objcSegment->vmsize);
- }
+ uint8_t *seg;
+ unsigned long seg_size;
+
+ seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size);
+ if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
+
+ seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
+ if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
}
#endif
tls_init();
lock_init();
exception_init();
-
+
// Register for unmap first, in case some +load unmaps something
_dyld_register_func_for_remove_image(&unmap_image);
dyld_register_image_state_change_handler(dyld_image_state_bound,
**********************************************************************/
static const header_info *_headerForAddress(void *addr)
{
- unsigned long size;
- unsigned long seg;
- header_info * hi;
+#if __OBJC2__
+ const char *segname = "__DATA";
+#else
+ const char *segname = "__OBJC";
+#endif
+ header_info *hi;
// Check all headers in the vector
for (hi = FirstHeader; hi != NULL; hi = hi->next)
{
- // Locate header data, if any
- const segmentType *segHeader;
-#if __OBJC2__
- segHeader = hi->os.dataSegmentHeader;
-#else
- segHeader = hi->os.objcSegmentHeader;
-#endif
- if (!segHeader) continue;
- seg = segHeader->vmaddr + hi->os.image_slide;
- size = segHeader->filesize;
+ uint8_t *seg;
+ unsigned long seg_size;
+
+ seg = getsegmentdata(hi->mhdr, segname, &seg_size);
+ if (!seg) continue;
// Is the class in this header?
- if ((seg <= (unsigned long) addr) &&
- ((unsigned long) addr < (seg + size)))
+ if ((uint8_t *)addr >= seg && (uint8_t *)addr < seg + seg_size)
return hi;
}
* Return the image header containing this class, or NULL.
* Returns NULL on runtime-constructed classes, and the NSCF classes.
**********************************************************************/
-__private_extern__ const header_info *_headerForClass(Class cls)
+PRIVATE_EXTERN const header_info *_headerForClass(Class cls)
{
return _headerForAddress(cls);
}
* 4. link count == 1
* Returns a file descriptor or -1. Errno may or may not be set on error.
**********************************************************************/
-__private_extern__ int secure_open(const char *filename, int flags, uid_t euid)
+PRIVATE_EXTERN int secure_open(const char *filename, int flags, uid_t euid)
{
struct stat fs, ls;
int fd = -1;
* By default this is the default malloc zone, but a dedicated zone is
* used if environment variable OBJC_USE_INTERNAL_ZONE is set.
**********************************************************************/
-__private_extern__ malloc_zone_t *_objc_internal_zone(void)
+PRIVATE_EXTERN malloc_zone_t *_objc_internal_zone(void)
{
static malloc_zone_t *z = (malloc_zone_t *)-1;
if (z == (malloc_zone_t *)-1) {
}
+PRIVATE_EXTERN const char *
+_getObjcHeaderName(const headerType *header)
+{
+ Dl_info info;
+
+ if (dladdr(header, &info)) {
+ return info.dli_fname;
+ }
+ else {
+ return (*_NSGetArgv())[0];
+ }
+}
+
+
+PRIVATE_EXTERN bool crashlog_header_name(header_info *hi)
+{
+ return crashlog_header_name_string(hi ? hi->os.dl_info.dli_fname : NULL);
+}
+
+PRIVATE_EXTERN bool crashlog_header_name_string(const char *name)
+{
+ CRSetCrashLogMessage2(name);
+ return true;
+}
+
+
+#if TARGET_OS_IPHONE
+
+PRIVATE_EXTERN const char *__crashreporter_info__ = NULL;
+
+PRIVATE_EXTERN const char *CRSetCrashLogMessage(const char *msg)
+{
+ __crashreporter_info__ = msg;
+ return msg;
+}
+PRIVATE_EXTERN const char *CRGetCrashLogMessage(void)
+{
+ return __crashreporter_info__;
+}
+
+PRIVATE_EXTERN const char *CRSetCrashLogMessage2(const char *msg)
+{
+ // sorry
+ return msg;
+}
+
+#endif
+
// TARGET_OS_MAC
#else
#include "hashtable2.h"
#include "objc-api.h"
#include "objc-config.h"
-#include "objc-rtp.h"
#include "objc-references.h"
#include "objc-initialize.h"
#include "objc-loadmethod.h"
#include "objc-internal.h"
+#include "objc-abi.h"
#include "objc-auto.h"
__BEGIN_DECLS
-#if defined(OBJC_NO_GC) != defined(NO_GC)
-# error OBJC_NO_GC and NO_GC inconsistent
+#ifdef __LP64__
+# define WORD_SHIFT 3UL
+# define WORD_MASK 7UL
+#else
+# define WORD_SHIFT 2UL
+# define WORD_MASK 3UL
+#endif
+
+#if (defined(OBJC_NO_GC) && SUPPORT_GC) || \
+ (!defined(OBJC_NO_GC) && !SUPPORT_GC)
+# error OBJC_NO_GC and SUPPORT_GC inconsistent
#endif
-#ifndef NO_GC
+#if SUPPORT_GC
# include <auto_zone.h>
- extern BOOL UseGC; // equivalent to calling objc_collecting_enabled()
- extern auto_zone_t *gc_zone; // the GC zone, or NULL if no GC
+ extern PRIVATE_EXTERN BOOL UseGC; // equivalent to calling objc_collecting_enabled()
+ extern PRIVATE_EXTERN BOOL UseCompaction; // if binary has opted-in for compaction.
+ extern PRIVATE_EXTERN auto_zone_t *gc_zone; // the GC zone, or NULL if no GC
extern void objc_addRegisteredClass(Class c);
extern void objc_removeRegisteredClass(Class c);
+ extern void objc_disableCompaction();
#else
# define UseGC NO
+# define UseCompaction NO
# define gc_zone NULL
# define objc_assign_ivar_internal objc_assign_ivar
# define objc_addRegisteredClass(c) do {} while(0)
# define AUTO_OBJECT_SCANNED 0
#endif
-struct old_category;
-struct old_method_list;
-typedef struct {
- IMP imp;
- SEL sel;
-} message_ref;
-
#if __OBJC2__
-
-typedef struct objc_module *Module;
typedef struct objc_cache *Cache;
-
-#endif
-
-
-#if OLD
-
-struct old_class {
- struct old_class *isa;
- struct old_class *super_class;
- const char *name;
- long version;
- long info;
- long instance_size;
- struct old_ivar_list *ivars;
- struct old_method_list **methodLists;
- Cache cache;
- struct old_protocol_list *protocols;
- // CLS_EXT only
- const char *ivar_layout;
- struct old_class_ext *ext;
-};
-
-struct old_class_ext {
- uint32_t size;
- const char *weak_ivar_layout;
- struct objc_property_list **propertyLists;
-};
-
-struct old_category {
- char *category_name;
- char *class_name;
- struct old_method_list *instance_methods;
- struct old_method_list *class_methods;
- struct old_protocol_list *protocols;
- uint32_t size;
- struct objc_property_list *instance_properties;
-};
-
-struct old_ivar {
- char *ivar_name;
- char *ivar_type;
- int ivar_offset;
-#ifdef __LP64__
- int space;
-#endif
-};
-
-struct old_ivar_list {
- int ivar_count;
-#ifdef __LP64__
- int space;
+#else
+// definition in runtime.h
#endif
- /* variable length structure */
- struct old_ivar ivar_list[1];
-};
-struct old_method {
- SEL method_name;
- char *method_types;
- IMP method_imp;
-};
-
-struct old_method_list {
- struct old_method_list *obsolete;
-
- int method_count;
-#ifdef __LP64__
- int space;
-#endif
- /* variable length structure */
- struct old_method method_list[1];
-};
-
-struct old_protocol {
- Class isa;
- const char *protocol_name;
- struct old_protocol_list *protocol_list;
- struct objc_method_description_list *instance_methods;
- struct objc_method_description_list *class_methods;
-};
-
-struct old_protocol_list {
- struct old_protocol_list *next;
- long count;
- struct old_protocol *list[1];
-};
-
-struct old_protocol_ext {
- uint32_t size;
- struct objc_method_description_list *optional_instance_methods;
- struct objc_method_description_list *optional_class_methods;
- struct objc_property_list *instance_properties;
-};
-
-#endif
-
-typedef objc_property_t Property;
-
-struct objc_property {
- const char *name;
- const char *attributes;
-};
-
-struct objc_property_list {
- uint32_t entsize;
- uint32_t count;
- struct objc_property first;
-};
-
typedef struct {
uint32_t version; // currently 0
uint32_t flags;
#define OBJC_IMAGE_SUPPORTS_GC (1<<1)
#define OBJC_IMAGE_REQUIRES_GC (1<<2)
#define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3)
+#define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4)
#define _objcHeaderIsReplacement(h) ((h)->info && ((h)->info->flags & OBJC_IMAGE_IS_REPLACEMENT))
#define _objcInfoSupportsGC(info) (((info)->flags & OBJC_IMAGE_SUPPORTS_GC) ? 1 : 0)
#define _objcInfoRequiresGC(info) (((info)->flags & OBJC_IMAGE_REQUIRES_GC) ? 1 : 0)
+#define _objcInfoSupportsCompaction(info) (((info)->flags & OBJC_IMAGE_SUPPORTS_COMPACTION) ? 1 : 0)
#define _objcHeaderSupportsGC(h) ((h)->info && _objcInfoSupportsGC((h)->info))
#define _objcHeaderRequiresGC(h) ((h)->info && _objcInfoRequiresGC((h)->info))
+#define _objcHeaderSupportsCompaction(h) ((h)->info && _objcInfoSupportsCompaction((h)->info))
/* OBJC_IMAGE_SUPPORTS_GC:
was compiled with -fobjc-gc flag, regardless of whether write-barriers were issued
extern void _objc_removeHeader(header_info *hi);
extern const char *_nameForHeader(const headerType*);
-extern objc_image_info *_getObjcImageInfo(const headerType *head, ptrdiff_t slide, size_t *size);
+extern objc_image_info *_getObjcImageInfo(const headerType *head, size_t *size);
extern const char *_getObjcHeaderName(const headerType *head);
-extern ptrdiff_t _getImageSlide(const headerType *header);
-
-
-extern Module _getObjcModules(const header_info *hi, size_t *count);
-extern SEL *_getObjcSelectorRefs(const header_info *hi, size_t *count);
extern BOOL _hasObjcContents(const header_info *hi);
-#if !__OBJC2__
-extern struct old_protocol **_getObjcProtocols(const header_info *head, size_t *count);
-extern struct old_class **_getObjcClassRefs(const header_info *hi, size_t *count);
-extern const char *_getObjcClassNames(const header_info *hi, size_t *size);
-#endif
-
-#if __OBJC2__
-extern SEL *_getObjc2SelectorRefs(const header_info *hi, size_t *count);
-extern message_ref *_getObjc2MessageRefs(const header_info *hi, size_t *count);extern struct class_t **_getObjc2ClassRefs(const header_info *hi, size_t *count);
-extern struct class_t **_getObjc2SuperRefs(const header_info *hi, size_t *count);
-extern struct class_t **_getObjc2ClassList(const header_info *hi, size_t *count);
-extern struct class_t **_getObjc2NonlazyClassList(const header_info *hi, size_t *count);
-extern struct category_t **_getObjc2CategoryList(const header_info *hi, size_t *count);
-extern struct category_t **_getObjc2NonlazyCategoryList(const header_info *hi, size_t *count);
-extern struct protocol_t **_getObjc2ProtocolList(const header_info *head, size_t *count);
-extern struct protocol_t **_getObjc2ProtocolRefs(const header_info *head, size_t *count);
-#endif
-
-#define END_OF_METHODS_LIST ((struct old_method_list*)-1)
/* selectors */
extern void sel_lock(void);
extern void sel_unlock(void);
extern BOOL sel_preoptimizationValid(const header_info *hi);
-extern void disableSelectorPreoptimization(void);
+extern void disableSharedCacheOptimizations(void);
extern SEL SEL_load;
extern SEL SEL_initialize;
extern SEL SEL_retain;
extern SEL SEL_release;
extern SEL SEL_autorelease;
+extern SEL SEL_retainCount;
+extern SEL SEL_alloc;
extern SEL SEL_copy;
+extern SEL SEL_new;
extern SEL SEL_finalize;
+extern SEL SEL_forwardInvocation;
/* optional malloc zone for runtime data */
extern void *_realloc_internal(void *ptr, size_t size);
extern char *_strdup_internal(const char *str);
extern char *_strdupcat_internal(const char *s1, const char *s2);
+extern uint8_t *_ustrdup_internal(const uint8_t *str);
extern void *_memdup_internal(const void *mem, size_t size);
extern void _free_internal(void *ptr);
+extern size_t _malloc_size_internal(void *ptr);
extern Class _calloc_class(size_t size);
-#if !__OBJC2__
-extern Class objc_getOrigClass (const char *name);
-extern IMP lookupNamedMethodInMethodList(struct old_method_list *mlist, const char *meth_name);
-extern void _objc_insertMethods(struct old_class *cls, struct old_method_list *mlist, struct old_category *cat);
-extern void _objc_removeMethods(struct old_class *cls, struct old_method_list *mlist);
-extern void _objc_flush_caches (Class cls);
-extern void _class_addProperties(struct old_class *cls, struct objc_property_list *additions);
-extern void change_class_references(struct old_class *imposter, struct old_class *original, struct old_class *copy, BOOL changeSuperRefs);
-extern void flush_marked_caches(void);
-extern void set_superclass(struct old_class *cls, struct old_class *supercls);
-#endif
-
extern IMP lookUpMethod(Class, SEL, BOOL initialize, BOOL cache);
extern void lockForMethodLookup(void);
extern void unlockForMethodLookup(void);
extern Method _cache_getMethod(Class cls, SEL sel, IMP objc_msgForward_internal_imp);
/* message dispatcher */
-extern IMP _class_lookupMethodAndLoadCache(Class, SEL);
+extern IMP _class_lookupMethodAndLoadCache3(id, SEL, Class);
extern id _objc_msgForward_internal(id self, SEL sel, ...);
extern id _objc_ignored_method(id self, SEL _cmd);
/* errors */
-extern void __objc_error(id, const char *, ...) __attribute__((format (printf, 2, 3)));
+extern void __objc_error(id, const char *, ...) __attribute__((format (printf, 2, 3), noreturn));
extern void _objc_inform(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
extern void _objc_inform_on_crash(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
extern void _objc_inform_now_and_on_crash(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern void _objc_warn_deprecated(const char *oldname, const char *newname) __attribute__((noinline));
-extern void _objc_error(id, const char *, va_list);
+extern void _objc_inform_deprecated(const char *oldname, const char *newname) __attribute__((noinline));
extern void inform_duplicate(const char *name, Class oldCls, Class cls);
+extern bool crashlog_header_name(header_info *hi);
+extern bool crashlog_header_name_string(const char *name);
/* magic */
extern Class _objc_getFreedObjectClass (void);
-#ifndef OBJC_INSTRUMENTED
-extern const struct objc_cache _objc_empty_cache;
-#else
-extern struct objc_cache _objc_empty_cache;
-#endif
-#if __OBJC2__
-extern IMP _objc_empty_vtable[128];
-#endif
/* map table additions */
extern void *NXMapKeyCopyingInsert(NXMapTable *table, const void *key, const void *value);
extern void *NXMapKeyFreeingRemove(NXMapTable *table, const void *key);
+/* property attribute parsing */
+extern const char *copyPropertyAttributeString(const objc_property_attribute_t *attrs, unsigned int count);
+extern objc_property_attribute_t *copyPropertyAttributeList(const char *attrs, unsigned int *outCount);
+extern char *copyPropertyAttributeValue(const char *attrs, const char *name);
+
+
/* locking */
/* Every lock used anywhere must be declared here.
* Locks not declared here may cause gdb deadlocks. */
#if defined(NDEBUG) || TARGET_OS_WIN32
-#define _destroyLockList(x) do { } while (0)
-
#define mutex_lock(m) _mutex_lock_nodebug(m)
#define mutex_try_lock(m) _mutex_try_lock_nodebug(m)
#define mutex_unlock(m) _mutex_unlock_nodebug(m)
#else
-struct _objc_lock_list;
-extern void _destroyLockList(struct _objc_lock_list *locks);
-
extern int _mutex_lock_debug(mutex_t *lock, const char *name);
extern int _mutex_try_lock_debug(mutex_t *lock, const char *name);
extern int _mutex_unlock_debug(mutex_t *lock, const char *name);
extern void *_objc_forward_handler;
extern void *_objc_forward_stret_handler;
+/* tagged pointer support */
+#if SUPPORT_TAGGED_POINTERS
+
+#define OBJC_IS_TAGGED_PTR(PTR) ((uintptr_t)(PTR) & 0x1)
+extern Class _objc_tagged_isa_table[16];
+
+#else
+
+#define OBJC_IS_TAGGED_PTR(PTR) 0
+
+#endif
+
+
+/* ignored selector support */
+
+/* Non-GC: no ignored selectors
+ GC without fixup dispatch: some selectors ignored, remapped to kIgnore
+ GC with fixup dispatch: some selectors ignored, but not remapped
+*/
+
+static inline int ignoreSelector(SEL sel)
+{
+#if !SUPPORT_GC
+ return NO;
+#elif SUPPORT_IGNORED_SELECTOR_CONSTANT
+ return UseGC && sel == (SEL)kIgnore;
+#else
+ return UseGC &&
+ (sel == @selector(retain) ||
+ sel == @selector(release) ||
+ sel == @selector(autorelease) ||
+ sel == @selector(retainCount) ||
+ sel == @selector(dealloc));
+#endif
+}
+
+static inline int ignoreSelectorNamed(const char *sel)
+{
+#if !SUPPORT_GC
+ return NO;
+#else
+ // release retain retainCount dealloc autorelease
+ return (UseGC &&
+ ( (sel[0] == 'r' && sel[1] == 'e' &&
+ (strcmp(&sel[2], "lease") == 0 ||
+ strcmp(&sel[2], "tain") == 0 ||
+ strcmp(&sel[2], "tainCount") == 0 ))
+ ||
+ (strcmp(sel, "dealloc") == 0)
+ ||
+ (sel[0] == 'a' && sel[1] == 'u' &&
+ strcmp(&sel[2], "torelease") == 0)));
+#endif
+}
+
+/* Protocol implementation */
+#if !__OBJC2__
+struct old_protocol;
+PRIVATE_EXTERN struct objc_method_description * lookup_protocol_method(struct old_protocol *proto, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod);
+#else
+PRIVATE_EXTERN Method _protocol_getMethod(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod);
+#endif
+
/* GC and RTP startup */
-extern void gc_init(BOOL on);
+extern void gc_init(BOOL wantsGC, BOOL wantsCompaction);
extern void rtp_init(void);
/* Exceptions */
/* Write barrier implementations */
extern id objc_assign_strongCast_non_gc(id value, id *dest);
extern id objc_assign_global_non_gc(id value, id *dest);
+extern id objc_assign_threadlocal_non_gc(id value, id *dest);
extern id objc_assign_ivar_non_gc(id value, id dest, ptrdiff_t offset);
extern id objc_assign_strongCast_gc(id val, id *dest);
extern id objc_assign_global_gc(id val, id *dest);
+extern id objc_assign_threadlocal_gc(id val, id *dest);
extern id objc_assign_ivar_gc(id value, id dest, ptrdiff_t offset);
-#ifndef NO_GC
+extern id objc_getAssociatedObject_non_gc(id object, const void *key);
+extern void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy);
+extern id objc_getAssociatedObject_gc(id object, const void *key);
+extern void objc_setAssociatedObject_gc(id object, const void *key, id value, objc_AssociationPolicy policy);
+
+#if SUPPORT_GC
/* GC weak reference fixup. */
extern void gc_fixup_weakreferences(id newObject, id oldObject);
/* GC datasegment registration. */
extern void gc_register_datasegment(uintptr_t base, size_t size);
extern void gc_unregister_datasegment(uintptr_t base, size_t size);
+extern void gc_fixup_barrier_stubs(const struct dyld_image_info *info);
+
+/* objc_dumpHeap implementation */
+extern BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename);
/*
- objc_assign_ivar, objc_assign_global, and objc_assign_strongCast MUST NOT be called directly
+ objc_assign_ivar, objc_assign_global, objc_assign_threadlocal, and objc_assign_strongCast MUST NOT be called directly
from inside libobjc. They live in the data segment, and must be called through the
following pointer(s) for libobjc to exist in the shared cache.
extern size_t objc_write_branch(void *entry, void *target);
extern size_t objc_cond_branch_size(void *entry, void *target, unsigned cond);
extern size_t objc_write_cond_branch(void *entry, void *target, unsigned cond);
-#if defined(__ppc__) || defined(__ppc64__)
-#define COND_ALWAYS 0x02800000 /* BO=10100, BI=00000 */
-#define COND_NE 0x00820000 /* BO=00100, BI=00010 */
-#elif defined(__i386__) || defined(__x86_64__)
+#if defined(__i386__) || defined(__x86_64__)
#define COND_ALWAYS 0xE9 /* JMP rel32 */
#define COND_NE 0x85 /* JNE rel32 (0F 85) */
#endif
-/* Thread-safe info field */
-#if !__OBJC2__
-extern void _class_setInfo(Class cls, long set);
-extern void _class_clearInfo(Class cls, long clear);
-extern void _class_changeInfo(Class cls, long set, long clear);
-#endif
-
-
-#if !defined(SEG_OBJC)
-#define SEG_OBJC "__OBJC" /* objective-C runtime segment */
-#endif
-
-
// Settings from environment variables
-#if NO_ENVIRON
+#if !SUPPORT_ENVIRON
# define ENV(x) enum { x = 0 }
#else
# define ENV(x) extern int x
ENV(PrintPreopt); // env OBJC_PRINT_PREOPTIMIZATION
ENV(PrintCxxCtors); // env OBJC_PRINT_CXX_CTORS
ENV(PrintExceptions); // env OBJC_PRINT_EXCEPTIONS
+ENV(PrintExceptionThrow); // env OBJC_PRINT_EXCEPTION_THROW
ENV(PrintAltHandlers); // env OBJC_PRINT_ALT_HANDLERS
ENV(PrintDeprecation); // env OBJC_PRINT_DEPRECATION_WARNINGS
ENV(PrintReplacedMethods); // env OBJC_PRINT_REPLACED_METHODS
ENV(PrintCaches); // env OBJC_PRINT_CACHE_SETUP
+ENV(PrintPoolHiwat); // env OBJC_PRINT_POOL_HIGHWATER
ENV(UseInternalZone); // env OBJC_USE_INTERNAL_ZONE
ENV(DebugUnload); // env OBJC_DEBUG_UNLOAD
ENV(DebugFragileSuperclasses); // env OBJC_DEBUG_FRAGILE_SUPERCLASSES
ENV(DebugFinalizers); // env OBJC_DEBUG_FINALIZERS
ENV(DebugNilSync); // env OBJC_DEBUG_NIL_SYNC
+ENV(DebugNonFragileIvars); // env OBJC_DEBUG_NONFRAGILE_IVARS
+ENV(DebugAltHandlers); // env OBJC_DEBUG_ALT_HANDLERS
ENV(DisableGC); // env OBJC_DISABLE_GC
ENV(DisableVtables); // env OBJC_DISABLE_VTABLES
ENV(DisablePreopt); // env OBJC_DISABLE_PREOPTIMIZATION
+
#undef ENV
extern void environ_init(void);
extern void logReplacedMethod(const char *className, SEL s, BOOL isMeta, const char *catName, IMP oldImp, IMP newImp);
-
-static __inline int _objc_strcmp(const char *s1, const char *s2) {
- char c1, c2;
- for ( ; (c1 = *s1) == (c2 = *s2); s1++, s2++)
- if (c1 == '\0')
- return 0;
- return (c1 - c2);
-}
-
static __inline uint32_t _objc_strhash(const char *s) {
uint32_t hash = 0;
for (;;) {
// objc per-thread storage
typedef struct {
struct _objc_initializing_classes *initializingClasses; // for +initialize
- struct _objc_lock_list *lockList; // for lock debugging
struct SyncCache *syncCache; // for @synchronize
struct alt_handler_list *handlerList; // for exception alt handlers
extern void tls_init(void);
-// Class state
-#if !__OBJC2__
-#define ISCLASS(cls) (((cls)->info & CLS_CLASS) != 0)
-#define ISMETA(cls) (((cls)->info & CLS_META) != 0)
-#define GETMETA(cls) (ISMETA(cls) ? (cls) : (cls)->isa)
-#endif
-
-
-// Attribute for global variables to keep them out of bss storage
-// To save one page per non-Objective-C process, variables used in
-// the "Objective-C not used" case should not be in bss storage.
-// On Tiger, this reduces the number of touched pages for each
-// CoreFoundation-only process from three to two. See #3857126 and #3857136.
-#define NOBSS __attribute__((section("__DATA,__data")))
-
// cache.h
#if TARGET_OS_WIN32
extern void flush_cache(Class cls);
extern BOOL _cache_fill(Class cls, Method smt, SEL sel);
extern void _cache_addForwardEntry(Class cls, SEL sel);
+extern IMP _cache_addIgnoredEntry(Class cls, SEL sel);
extern void _cache_free(Cache cache);
+extern void _cache_collect(bool collectALot);
extern mutex_t cacheUpdateLock;
-#if !__OBJC2__
-// used by flush_caches outside objc-cache.m
-extern void _cache_flush(Class cls);
-#ifdef OBJC_INSTRUMENTED
-extern unsigned int LinearFlushCachesCount;
-extern unsigned int LinearFlushCachesVisitedCount;
-extern unsigned int MaxLinearFlushCachesVisitedCount;
-extern unsigned int NonlinearFlushCachesCount;
-extern unsigned int NonlinearFlushCachesClassCount;
-extern unsigned int NonlinearFlushCachesVisitedCount;
-extern unsigned int MaxNonlinearFlushCachesVisitedCount;
-extern unsigned int IdealFlushCachesCount;
-extern unsigned int MaxIdealFlushCachesCount;
-#endif
-#endif
-
-// objc-gdb.h
-#if !TARGET_OS_WIN32
-BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename);
-#endif
-
// encoding.h
extern unsigned int encoding_getNumberOfArguments(const char *typedesc);
extern unsigned int encoding_getSizeOfArguments(const char *typedesc);
// sync.h
extern void _destroySyncCache(struct SyncCache *cache);
+// arr
+extern void (^objc_arr_log)(const char *, id param);
+extern void arr_init(void);
+extern id objc_autoreleaseReturnValue(id obj);
+
+
// layout.h
typedef struct {
uint8_t *bits;
BOOL weak;
} layout_bitmap;
extern layout_bitmap layout_bitmap_create(const unsigned char *layout_string, size_t layoutStringInstanceSize, size_t instanceSize, BOOL weak);
+extern layout_bitmap layout_bitmap_create_empty(size_t instanceSize, BOOL weak);
extern void layout_bitmap_free(layout_bitmap bits);
extern const unsigned char *layout_string_create(layout_bitmap bits);
extern void layout_bitmap_set_ivar(layout_bitmap bits, const char *type, size_t offset);
extern void layout_bitmap_grow(layout_bitmap *bits, size_t newCount);
extern void layout_bitmap_slide(layout_bitmap *bits, size_t oldPos, size_t newPos);
+extern void layout_bitmap_slide_anywhere(layout_bitmap *bits, size_t oldPos, size_t newPos);
extern BOOL layout_bitmap_splat(layout_bitmap dst, layout_bitmap src,
size_t oldSrcInstanceSize);
extern BOOL layout_bitmap_or(layout_bitmap dst, layout_bitmap src, const char *msg);
extern const char * load_images(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]);
extern BOOL load_images_nolock(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]);
extern void unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide);
-extern void unmap_image_nolock(const struct mach_header *mh, intptr_t vmaddr_slide);
+extern void unmap_image_nolock(const struct mach_header *mh);
extern void _read_images(header_info **hList, uint32_t hCount);
extern void prepare_load_methods(header_info *hi);
extern void _unload_image(header_info *hi);
extern Class _objc_allocateFutureClass(const char *name);
-extern Property *copyPropertyList(struct objc_property_list *plist, unsigned int *outCount);
-
-
extern const header_info *_headerForClass(Class cls);
-// fixme class
-extern Property property_list_nth(const struct objc_property_list *plist, uint32_t i);
-extern size_t property_list_size(const struct objc_property_list *plist);
-
extern Class _class_getSuperclass(Class cls);
extern BOOL _class_getInfo(Class cls, int info);
extern const char *_class_getName(Class cls);
extern BOOL _class_isLoadable(Class cls);
extern IMP _class_getLoadMethod(Class cls);
extern BOOL _class_hasLoadMethod(Class cls);
-extern BOOL _class_hasCxxStructorsNoSuper(Class cls);
+extern BOOL _class_hasCxxStructors(Class cls);
extern BOOL _class_shouldFinalizeOnMainThread(Class cls);
extern void _class_setFinalizeOnMainThread(Class cls);
extern BOOL _class_instancesHaveAssociatedObjects(Class cls);
-extern void _class_assertInstancesHaveAssociatedObjects(Class cls);
+extern void _class_setInstancesHaveAssociatedObjects(Class cls);
extern BOOL _class_shouldGrowCache(Class cls);
extern void _class_setGrowCache(Class cls, BOOL grow);
-extern Ivar _class_getVariable(Class cls, const char *name);
+extern Ivar _class_getVariable(Class cls, const char *name, Class *memberOf);
+extern BOOL _class_usesAutomaticRetainRelease(Class cls);
+extern uint32_t _class_getInstanceStart(Class cls);
-extern id _internal_class_createInstanceFromZone(Class cls, size_t extraBytes,
- void *zone);
-extern id _internal_object_dispose(id anObject);
-
-extern Class gdb_class_getClass(Class cls);
-extern BOOL class_instancesHaveAssociatedObjects(Class cls);
-OBJC_EXPORT BOOL gdb_objc_isRuntimeLocked();
+extern unsigned _class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone, id *results, unsigned num_requested);
+extern id _objc_constructOrFree(Class cls, void *bytes);
extern const char *_category_getName(Category cat);
extern const char *_category_getClassName(Category cat);
extern Class _category_getClass(Category cat);
extern IMP _category_getLoadMethod(Category cat);
-#if !__OBJC2__
-#define oldcls(cls) ((struct old_class *)cls)
-#define oldprotocol(proto) ((struct old_protocol *)proto)
-#define oldmethod(meth) ((struct old_method *)meth)
-#define oldcategory(cat) ((struct old_category *)cat)
-#define oldivar(ivar) ((struct old_ivar *)ivar)
-
-static inline struct old_method *_method_asOld(Method m) { return (struct old_method *)m; }
-static inline struct old_class *_class_asOld(Class cls) { return (struct old_class *)cls; }
-static inline struct old_category *_category_asOld(Category cat) { return (struct old_category *)cat; }
-
-extern void unload_class(struct old_class *cls);
-#endif
-
extern BOOL object_cxxConstruct(id obj);
extern void object_cxxDestruct(id obj);
extern Method _class_resolveMethod(Class cls, SEL sel);
extern void log_and_fill_cache(Class cls, Class implementer, Method meth, SEL sel);
-#if !__OBJC2__
-#define CLS_CLASS 0x1
-#define CLS_META 0x2
-#define CLS_INITIALIZED 0x4
-#define CLS_POSING 0x8
-#define CLS_MAPPED 0x10
-#define CLS_FLUSH_CACHE 0x20
-#define CLS_GROW_CACHE 0x40
-#define CLS_NEED_BIND 0x80
-#define CLS_METHOD_ARRAY 0x100
-// the JavaBridge constructs classes with these markers
-#define CLS_JAVA_HYBRID 0x200
-#define CLS_JAVA_CLASS 0x400
-// thread-safe +initialize
-#define CLS_INITIALIZING 0x800
-// bundle unloading
-#define CLS_FROM_BUNDLE 0x1000
-// C++ ivar support
-#define CLS_HAS_CXX_STRUCTORS 0x2000
-// Lazy method list arrays
-#define CLS_NO_METHOD_ARRAY 0x4000
-// +load implementation
-#define CLS_HAS_LOAD_METHOD 0x8000
-// objc_allocateClassPair API
-#define CLS_CONSTRUCTING 0x10000
-// visibility=hidden
-#define CLS_HIDDEN 0x20000
-// GC: class has unsafe finalize method
-#define CLS_FINALIZE_ON_MAIN_THREAD 0x40000
-// Lazy property list arrays
-#define CLS_NO_PROPERTY_ARRAY 0x80000
-// +load implementation
-#define CLS_CONNECTED 0x100000
-#define CLS_LOADED 0x200000
-// objc_allocateClassPair API
-#define CLS_CONSTRUCTED 0x400000
-// class is leaf for cache flushing
-#define CLS_LEAF 0x800000
-// class instances may have associative references
-#define CLS_INSTANCES_HAVE_ASSOCIATED_OBJECTS 0x1000000
-#endif
-
#define OBJC_WARN_DEPRECATED \
do { \
static int warned = 0; \
if (!warned) { \
warned = 1; \
- _objc_warn_deprecated(__FUNCTION__, NULL); \
+ _objc_inform_deprecated(__FUNCTION__, NULL); \
} \
} while (0) \
__END_DECLS
+
+#ifndef STATIC_ASSERT
+# define STATIC_ASSERT(x) _STATIC_ASSERT2(x, __LINE__)
+# define _STATIC_ASSERT2(x, line) _STATIC_ASSERT3(x, line)
+# define _STATIC_ASSERT3(x, line) \
+ typedef struct { \
+ int _static_assert[(x) ? 0 : -1]; \
+ } _static_assert_ ## line __attribute__((unavailable))
+#endif
+
+
+/***********************************************************************
+* object_getClass.
+* Locking: None. If you add locking, tell gdb (rdar://7516456).
+**********************************************************************/
+static inline Class _object_getClass(id obj)
+{
+#if SUPPORT_TAGGED_POINTERS
+ if (OBJC_IS_TAGGED_PTR(obj)) {
+ uint8_t slotNumber = ((uint8_t) (uint64_t) obj) & 0x0F;
+ Class isa = _objc_tagged_isa_table[slotNumber];
+ return isa;
+ }
+#endif
+ if (obj) return obj->isa;
+ else return Nil;
+}
+
+
+// Global operator new and delete. We must not use any app overrides.
+// This ALSO REQUIRES each of these be in libobjc's unexported symbol list.
+#if __cplusplus
+#import <new>
+inline void* operator new(std::size_t size) throw (std::bad_alloc) { return _malloc_internal(size); }
+inline void* operator new[](std::size_t size) throw (std::bad_alloc) { return _malloc_internal(size); }
+inline void* operator new(std::size_t size, const std::nothrow_t&) throw() { return _malloc_internal(size); }
+inline void* operator new[](std::size_t size, const std::nothrow_t&) throw() { return _malloc_internal(size); }
+inline void operator delete(void* p) throw() { _free_internal(p); }
+inline void operator delete[](void* p) throw() { _free_internal(p); }
+inline void operator delete(void* p, const std::nothrow_t&) throw() { _free_internal(p); }
+inline void operator delete[](void* p, const std::nothrow_t&) throw() { _free_internal(p); }
+#endif
+
+
#endif /* _OBJC_PRIVATE_H_ */
OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)
};
-__private_extern__ id _object_get_associative_reference(id object, void *key) {
+PRIVATE_EXTERN id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
}
};
-__private_extern__ void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
+PRIVATE_EXTERN void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
uintptr_t old_policy = 0; // NOTE: old_policy is always assigned to when old_value is non-nil.
id new_value = value ? acquireValue(value, policy) : nil, old_value = nil;
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
- _class_assertInstancesHaveAssociatedObjects(object->isa);
+ _class_setInstancesHaveAssociatedObjects(_object_getClass(object));
}
} else {
// setting the association to nil breaks the association.
if (old_value) releaseValue(old_value, old_policy);
}
-__private_extern__ void _object_remove_assocations(id object) {
- vector<ObjcAssociation> elements;
+PRIVATE_EXTERN void _object_remove_assocations(id object) {
+ vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
+++ /dev/null
-/*
- * Copyright (c) 2004-2006 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-/*
- Layout of the "objc runtime pages", an fixed area
- in high memory that can be reached via an absolute branch.
- Basic idea is that, on ppc, a single "bla" instruction
- (branch & link to absolute address)
- can be issued by the compiler to get to high-value routines
- in the runtime, including the basic messenger and to the
- assignment helper intrinsics issued under -fobjc-gc.
- The implementation of the intrinsics is optimized based
- on whether the application (process) is running with GC enabled
- or not.
-*/
-
-#ifndef _OBJC_RTP_H_
-#define _OBJC_RTP_H_
-
-/*********************************************************************
- Layout of the runtime page.
-
- Some of these values must NEVER BE CHANGED, including:
- kRTPagesLo
- kRTPagesHi
- kRTPagesMaxSize
- kRTSize_objc_msgSend
- kRTSize_objc_assign_ivar
- kRTAddress_objc_msgSend ppc 0xfffeff00
- kRTAddress_objc_assign_ivar ppc 0xfffefec0
-
-*********************************************************************/
-
-#undef OBJC_SIZE_T
-#undef OBJC_INTPTR_T
-#undef OBJC_UINTPTR_T
-
-#ifdef OBJC_ASM
-#define OBJC_SIZE_T(x) (x)
-#define OBJC_INTPTR_T(x) (x)
-#define OBJC_UINTPTR_T(x) (x)
-#else
-#define OBJC_SIZE_T(x) ((size_t)(x))
-#define OBJC_INTPTR_T(x) ((intptr_t)(x))
-#define OBJC_UINTPTR_T(x) ((uintptr_t)(x))
-#endif
-
-// Size of RTP area, in bytes (0x1000 = 4k bytes = 1 page)
-#define kRTPagesMaxSize OBJC_SIZE_T(4 * 0x1000) // size reserved for runtime
-#define kRTPagesSize OBJC_SIZE_T(1 * 0x1000) // size actually used
-
-// Address of RTP area: [kRTPagesLo..kRTPagesHi)
-// These are defined in negative numbers to reflect an offset from the highest address,
-// which is what the ppc "bla" instruction is defined to do with "negative" addresses.
-// This definition will establish the correct entry points for 64-bit if this mechanism
-// is adopted for that architecture as well.
-#if defined(__ppc__) || defined(__ppc64__)
-# define kRTPagesLo OBJC_UINTPTR_T(-20 * 0x1000) // ppc 0xfffec000
-#elif defined(__i386__)
-# define kRTPagesLo OBJC_UINTPTR_T(-24 * 0x1000) // i386 0xfffe8000
-#elif defined(__x86_64__)
-# define kRTPagesLo OBJC_UINTPTR_T(-24 * 0x1000) // x86_64 0xfffffffffffe8000
-#elif defined(__arm__) || defined(TARGET_OS_WIN32)
-# define kRTPagesLo OBJC_UINTPTR_T(0)
-#else
- #error unknown architecture
-#endif
-#define kRTPagesHi OBJC_UINTPTR_T(kRTPagesLo + kRTPagesMaxSize) // ppc 0xffff0000
-
-// Sizes reserved for functions in the RTP area, in bytes
-#define kRTSize_objc_msgSend OBJC_SIZE_T(0x0100)
-#define kRTSize_objc_assign_ivar OBJC_SIZE_T(0x0040)
-#define kRTSize_objc_assign_global OBJC_SIZE_T(0x0010)
-#define kRTSize_objc_assign_strongCast OBJC_SIZE_T(0x0010)
-
-// Absolute address of functions in the RTP area
-// These count backwards from the hi end of the RTP area.
-// New additions are added to the low side.
-#define kRTAddress_objc_msgSend OBJC_UINTPTR_T(kRTPagesHi - kRTSize_objc_msgSend) // ppc 0xfffeff00
-#define kRTAddress_objc_assign_ivar OBJC_UINTPTR_T(kRTAddress_objc_msgSend - kRTSize_objc_assign_ivar) // ppc 0xfffefec0
-#define kRTAddress_objc_assign_global OBJC_UINTPTR_T(kRTAddress_objc_assign_ivar - kRTSize_objc_assign_global) // ppc 0xfffefeb0
-#define kRTAddress_objc_assign_strongCast OBJC_UINTPTR_T(kRTAddress_objc_assign_global - kRTSize_objc_assign_strongCast) // ppc 0xfffefea0
-
-// Sizes reserved for data in the RTP area, in bytes
-#define kRTSize_zero OBJC_SIZE_T(16) // 16 zero bytes
-#define kRTSize_ignoredSelector OBJC_SIZE_T(19) // 1+strlen("<ignored selector>")
-
-// Absolute address of data in the RTP area
-// These count forwards from the lo end of the RTP area.
-// These are not locked down and can be moved if necessary.
-#define kRTAddress_zero OBJC_UINTPTR_T(kRTPagesHi-kRTPagesSize)
-#define kRTAddress_ignoredSelector OBJC_UINTPTR_T(kRTAddress_zero+kRTSize_zero)
-
-#define kIgnore kRTAddress_ignoredSelector // ppc 0xfffef000
-
-/*********************************************************************
- End of runtime page layout.
-*********************************************************************/
-
-#endif
*
* @APPLE_LICENSE_HEADER_END@
*/
-/*
- Implementation of the "objc runtime pages", an fixed area
- in high memory that can be reached via an absolute branch.
-*/
#import "objc-private.h"
#import <objc/message.h>
-#if defined(__ppc__)
-
-static size_t rtp_copy_code(unsigned* dest, unsigned* source, size_t max_insns);
-static void rtp_set_up_objc_msgSend(uintptr_t address, size_t maxsize);
-static void rtp_set_up_other(uintptr_t address, size_t maxsize, const char *name, void *gc_code, void *non_gc_code);
-
-/**********************************************************************
-* rtp_init
-* Allocate and initialize the Objective-C runtime pages.
-* Kills the process if something goes wrong.
-**********************************************************************/
-__private_extern__ void rtp_init(void)
-{
- kern_return_t ret;
- vm_address_t objcRTPages = (vm_address_t)(kRTPagesHi - kRTPagesSize);
-
- if (PrintRTP) {
- _objc_inform("RTP: initializing rtp at [%p..%p)",
- (void *)objcRTPages, (void *)kRTPagesHi);
- }
-
- // unprotect the ObjC runtime pages for writing
- ret = vm_protect(mach_task_self(),
- objcRTPages, kRTPagesSize,
- FALSE, VM_PROT_READ | VM_PROT_WRITE);
-
- if (ret != KERN_SUCCESS) {
- if (PrintRTP) {
- _objc_inform("RTP: warning: libSystem/kernel did not allocate Objective-C runtime pages; continuing anyway");
- }
- return;
- }
-
- // initialize code in ObjC runtime pages
- rtp_set_up_objc_msgSend(kRTAddress_objc_msgSend, kRTSize_objc_msgSend);
-#ifdef NO_GC
- #define objc_assign_ivar_gc objc_assign_ivar_non_gc
- #define objc_assign_global_gc objc_assign_global_non_gc
- #define objc_assign_strongCast_gc objc_assign_strongCast_non_gc
-#endif
- rtp_set_up_other(kRTAddress_objc_assign_ivar, kRTSize_objc_assign_ivar,
- "objc_assign_ivar", objc_assign_ivar_gc, objc_assign_ivar_non_gc);
-
- rtp_set_up_other(kRTAddress_objc_assign_global, kRTSize_objc_assign_global,
- "objc_assign_global", objc_assign_global_gc, objc_assign_global_non_gc);
-
- rtp_set_up_other(kRTAddress_objc_assign_strongCast, kRTSize_objc_assign_strongCast,
- "objc_assign_strongCast", objc_assign_strongCast_gc, objc_assign_strongCast_non_gc);
-
- // initialize data in ObjC runtime pages
- memset((char *)kRTAddress_zero, 0, 16);
- strlcpy((char *)kIgnore, "<ignored selector>", OBJC_SIZE_T(19));
-
- // re-protect the ObjC runtime pages for execution
- ret = vm_protect(mach_task_self(),
- objcRTPages, kRTPagesSize,
- FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
- if (ret != KERN_SUCCESS) {
- _objc_inform("RTP: Could not re-protect Objective-C runtime pages!");
- }
-}
-
-
-/**********************************************************************
-* rtp_set_up_objc_msgSend
-*
-* Construct the objc runtime page version of objc_msgSend
-* address is the entry point of the new implementation.
-* maxsize is the number of bytes available for the new implementation.
-**********************************************************************/
-static void rtp_set_up_objc_msgSend(uintptr_t address, size_t maxsize)
-{
- // Location in the runtime pages of the new function.
- unsigned *buffer = (unsigned *)address;
-
- // Location of the original implementation.
- // objc_msgSend is simple enough to copy directly
- unsigned *code = (unsigned *)objc_msgSend;
-
- // If building an instrumented or profiled runtime, simply branch
- // directly to the full implementation.
-#if defined(OBJC_INSTRUMENTED) || defined(PROFILE)
- size_t written = objc_write_branch(buffer, code);
- sys_icache_invalidate(buffer, written*4);
- if (PrintRTP) {
- _objc_inform("RTP: instrumented or profiled libobjc - objc_msgSend "
- "in RTP at %p is a %zu instruction branch",
- buffer, written);
- }
- return;
-#endif
-
- if (PrintRTP) {
- _objc_inform("RTP: writing objc_msgSend at [%p..%p) ...",
- (void *)address, (void *)(address+maxsize));
- }
-
- // Copy instructions from function to runtime pages
- // i is the number of INSTRUCTIONS written so far
- size_t max_insns = maxsize / sizeof(unsigned);
- size_t i = rtp_copy_code(buffer, code, max_insns);
- if (i + objc_branch_size(buffer + i, code + i) > max_insns) {
- // objc_msgSend didn't fit in the alloted space.
- // Branch to ordinary objc_msgSend instead so the program won't crash.
- i = objc_write_branch(buffer, code);
- sys_icache_invalidate(buffer, i*4);
- _objc_inform("RTP: objc_msgSend is too large to fit in the "
- "runtime pages (%zu bytes available)", maxsize);
- return;
- }
-
- {
- // Replace load of _objc_nilReceiver into r11
- // This assumes that the load of _objc_nilReceiver
- // immediately follows the LAST `mflr r0` in objc_msgSend,
- // and that the original load sequence is six instructions long.
-
- // instructions used to load _objc_nilReceiver
- const unsigned op_mflr_r0 = 0x7c0802a6u;
- const unsigned op_nop = 0x60000000u;
-
- // get address of _objc_nilReceiver, and its lo and hi halves
- uintptr_t address = (uintptr_t)&_objc_nilReceiver;
- uint16_t lo = address & 0xffff;
- uint16_t ha = ((address - (int16_t)lo) >> 16) & 0xffff;
-#if defined(__ppc64__)
- uint16_t hi2 = (address >> 32) & 0xffff;
- uint16_t hi3 = (address >> 48) & 0xffff;
-#endif
-
- // search for mflr instruction
- size_t j;
- for (j = i; j-- != 0; ) {
- if (buffer[j] == op_mflr_r0) {
- const unsigned op_lis_r11 = 0x3d600000u;
- const unsigned op_lwz_r11 = 0x816b0000u;
-#if defined(__ppc__)
- // lis r11, ha
- // lwz r11, lo(r11)
- buffer[j + 0] = op_lis_r11 | ha;
- buffer[j + 1] = op_nop;
- buffer[j + 2] = op_nop;
- buffer[j + 3] = op_lwz_r11 | lo;
- buffer[j + 4] = op_nop;
- buffer[j + 5] = op_nop;
-#elif defined(__ppc64__)
- const unsigned op_ori_r11 = 0x616b0000u;
- const unsigned op_oris_r11 = 0x656b0000u;
- const unsigned op_sldi_r11 = 0x796b07c6u;
- // lis r11, hi3
- // ori r11, r11, hi2
- // sldi r11, r11, 32
- // oris r11, r11, ha
- // lwz r11, lo(r11)
- buffer[j + 0] = op_lis_r11 | hi3;
- buffer[j + 1] = op_ori_r11 | hi2;
- buffer[j + 2] = op_sldi_r11;
- buffer[j + 3] = op_oris_r11 | ha;
- buffer[j + 4] = op_lwz_r11 | lo;
- buffer[j + 5] = op_nop;
-#endif
- break;
- }
- }
- }
-
- // branch to the cache miss code
- i += objc_write_branch(buffer + i, code + i);
-
- // flush the instruction cache
- sys_icache_invalidate(buffer, i*4);
-
- if (PrintRTP) {
- _objc_inform("RTP: wrote objc_msgSend at [%p..%p)",
- (void *)address, (void *)(address + i*sizeof(unsigned)));
- }
-}
-
-
-/**********************************************************************
-* rtp_set_up_other
-*
-* construct the objc runtime page version of the supplied code
-* address is the entry point of the new implementation.
-* maxsize is the number of bytes available for the new implementation.
-* name is the c string name of the routine being set up.
-* gc_code is the code to use if collecting is enabled (assumed to be large and requiring a branch.)
-* non_gc_code is the code to use if collecting is not enabled (assumed to be small enough to copy.)
-**********************************************************************/
-static void rtp_set_up_other(uintptr_t address, size_t maxsize, const char *name, void *gc_code, void *non_gc_code) {
- // location in the runtime pages of this function
- unsigned *buffer = (unsigned *)address;
-
- // Location of the original implementation.
- unsigned *code = (unsigned *)(objc_collecting_enabled() ? gc_code : non_gc_code);
-
- if (objc_collecting_enabled()) {
- size_t written = objc_write_branch(buffer, code);
- sys_icache_invalidate(buffer, written*4);
- if (PrintRTP) {
- _objc_inform("RTP: %s in RTP at %p is a %zu instruction branch",
- name, buffer, written);
- }
- return;
- }
-
- if (PrintRTP) {
- _objc_inform("RTP: writing %s at [%p..%p) ...",
- name, (void *)address, (void *)(address + maxsize));
- }
-
- // Copy instructions from function to runtime pages
- // i is the number of INSTRUCTIONS written so far
- size_t max_insns = maxsize / sizeof(unsigned);
- size_t i = rtp_copy_code(buffer, code, max_insns);
- if (i > max_insns) {
- // code didn't fit in the alloted space.
- // Branch to ordinary objc_assign_ivar instead so the program won't crash.
- i = objc_write_branch(buffer, code);
- sys_icache_invalidate(buffer, i*4);
- _objc_inform("RTP: %s is too large to fit in the "
- "runtime pages (%zu bytes available)", name, maxsize);
- return;
- }
-
- // flush the instruction cache
- sys_icache_invalidate(buffer, i*4);
-
- if (PrintRTP) {
- _objc_inform("RTP: wrote %s at [%p..%p)",
- name, (void *)address,
- (void *)(address + i * sizeof(unsigned)));
- }
-}
-
-
-/**********************************************************************
-* rtp_copy_code
-*
-* Copy blr-terminated PPC instructions from source to dest.
-* If a blr is reached then that blr is copied, and the return value is
-* the number of instructions copied ( <= max_insns )
-* If no blr is reached then exactly max_insns instructions are copied,
-* and the return value is max_insns+1.
-**********************************************************************/
-static size_t rtp_copy_code(unsigned* dest, unsigned* source, size_t max_insns)
-{
- const unsigned op_blr = 0x4e800020u;
- size_t i;
-
- // copy instructions until blr is found
- for (i = 0; i < max_insns; i++) {
- dest[i] = source[i];
- if (source[i] == op_blr) break;
- }
-
- // return number of instructions copied
- // OR max_insns+1 if no blr was found
- return i + 1;
-}
-
-
-#elif defined(__i386__)
+#if defined(__i386__)
/**********************************************************************
}
-__private_extern__ void rtp_init(void)
+PRIVATE_EXTERN void rtp_init(void)
{
// At load time, the page on which the objc_assign_* routines live is not
// marked as executable. We fix that here, regardless of the GC choice.
-#ifndef NO_GC
+#if SUPPORT_GC
if (UseGC)
{
rtp_swap_imp((unsigned*)objc_assign_ivar,
objc_assign_ivar_gc, "objc_assign_ivar");
rtp_swap_imp((unsigned*)objc_assign_global,
objc_assign_global_gc, "objc_assign_global");
+ rtp_swap_imp((unsigned*)objc_assign_threadlocal,
+ objc_assign_threadlocal_gc, "objc_assign_threadlocal");
rtp_swap_imp((unsigned*)objc_assign_strongCast,
objc_assign_strongCast_gc, "objc_assign_strongCast");
}
#else
-__private_extern__ void rtp_init(void)
+PRIVATE_EXTERN void rtp_init(void)
{
if (PrintRTP) {
_objc_inform("RTP: no rtp implementation for this platform");
* @APPLE_LICENSE_HEADER_END@
*/
+#ifndef _OBJC_RUNTIME_NEW_H
+#define _OBJC_RUNTIME_NEW_H
+
+__BEGIN_DECLS
+
// Values for class_ro_t->flags
// These are emitted by the compiler and are part of the ABI.
// class is a metaclass
#define RO_HIDDEN (1<<4)
// class has attribute(objc_exception): OBJC_EHTYPE_$_ThisClass is non-weak
#define RO_EXCEPTION (1<<5)
+// this bit is available for reassignment
+// #define RO_REUSE_ME (1<<6)
+// class compiled with -fobjc-arr (automatic retain/release)
+#define RO_IS_ARR (1<<7)
+
// class is in an unloadable bundle - must never be set by compiler
#define RO_FROM_BUNDLE (1<<29)
// class is unrealized future class - must never be set by compiler
#define RW_SPECIALIZED_VTABLE (1<<22)
// class instances may have associative references
#define RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS (1<<21)
+// class or superclass has .cxx_construct/destruct implementations
+#define RW_HAS_CXX_STRUCTORS (1<<20)
+// class has instance-specific GC layout
+#define RW_HAS_INSTANCE_SPECIFIC_LAYOUT (1 << 19)
+// class or superclass has non-trivial .release_ivars implementation
+#define RW_HAS_IVAR_RELEASER (1<<18)
+// class or superclass has custom retain/release/autorelease/retainCount impl
+#define RW_HAS_CUSTOM_RR (1<<17)
-typedef struct method_t {
+struct method_t {
SEL name;
const char *types;
IMP imp;
-} method_t;
+
+ struct SortBySELAddress :
+ public std::binary_function<const method_t&,
+ const method_t&, bool>
+ {
+ bool operator() (const method_t& lhs,
+ const method_t& rhs)
+ { return lhs.name < rhs.name; }
+ };
+};
typedef struct method_list_t {
- uint32_t entsize_NEVER_USE; // low 2 bits used for fixup markers
+ uint32_t entsize_NEVER_USE; // high bits used for fixup markers
uint32_t count;
- struct method_t first;
+ method_t first;
+
+ uint32_t getEntsize() const {
+ return entsize_NEVER_USE & ~(uint32_t)3;
+ }
+ uint32_t getCount() const {
+ return count;
+ }
+ method_t& get(uint32_t i) const {
+ return *(method_t *)((uint8_t *)&first + i*getEntsize());
+ }
+
+ // iterate methods, taking entsize into account
+ // fixme need a proper const_iterator
+ struct method_iterator {
+ uint32_t entsize;
+ uint32_t index; // keeping track of this saves a divide in operator-
+ method_t* method;
+
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef method_t value_type;
+ typedef ptrdiff_t difference_type;
+ typedef method_t* pointer;
+ typedef method_t& reference;
+
+ method_iterator() { }
+
+ method_iterator(const method_list_t& mlist, uint32_t start = 0)
+ : entsize(mlist.getEntsize())
+ , index(start)
+ , method(&mlist.get(start))
+ { }
+
+ const method_iterator& operator += (ptrdiff_t count) {
+ method = (method_t*)((uint8_t *)method + count*entsize);
+ index += (int32_t)count;
+ return *this;
+ }
+ const method_iterator& operator -= (ptrdiff_t count) {
+ method = (method_t*)((uint8_t *)method - count*entsize);
+ index -= (int32_t)count;
+ return *this;
+ }
+ const method_iterator operator + (ptrdiff_t count) const {
+ return method_iterator(*this) += count;
+ }
+ const method_iterator operator - (ptrdiff_t count) const {
+ return method_iterator(*this) -= count;
+ }
+
+ method_iterator& operator ++ () { *this += 1; return *this; }
+ method_iterator& operator -- () { *this -= 1; return *this; }
+ method_iterator operator ++ (int) {
+ method_iterator result(*this); *this += 1; return result;
+ }
+ method_iterator operator -- (int) {
+ method_iterator result(*this); *this -= 1; return result;
+ }
+
+ ptrdiff_t operator - (const method_iterator& rhs) const {
+ return (ptrdiff_t)this->index - (ptrdiff_t)rhs.index;
+ }
+
+ method_t& operator * () const { return *method; }
+ method_t* operator -> () const { return method; }
+
+ operator method_t& () const { return *method; }
+
+ bool operator == (const method_iterator& rhs) {
+ return this->method == rhs.method;
+ }
+ bool operator != (const method_iterator& rhs) {
+ return this->method != rhs.method;
+ }
+
+ bool operator < (const method_iterator& rhs) {
+ return this->method < rhs.method;
+ }
+ bool operator > (const method_iterator& rhs) {
+ return this->method > rhs.method;
+ }
+ };
+
+ method_iterator begin() const { return method_iterator(*this, 0); }
+ method_iterator end() const { return method_iterator(*this, getCount()); }
+
} method_list_t;
typedef struct ivar_t {
typedef struct ivar_list_t {
uint32_t entsize;
uint32_t count;
- struct ivar_t first;
+ ivar_t first;
} ivar_list_t;
+typedef struct objc_property {
+ const char *name;
+ const char *attributes;
+} property_t;
+
+typedef struct property_list_t {
+ uint32_t entsize;
+ uint32_t count;
+ property_t first;
+} property_list_t;
+
typedef uintptr_t protocol_ref_t; // protocol_t *, but unremapped
typedef struct protocol_t {
method_list_t *classMethods;
method_list_t *optionalInstanceMethods;
method_list_t *optionalClassMethods;
- struct objc_property_list *instanceProperties;
+ property_list_t *instanceProperties;
} protocol_t;
typedef struct protocol_list_t {
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
- const struct objc_property_list *baseProperties;
+ const property_list_t *baseProperties;
} class_ro_t;
typedef struct class_rw_t {
const class_ro_t *ro;
- struct method_list_t **methods;
+ method_list_t **methods;
struct chained_property_list *properties;
- struct protocol_list_t ** protocols;
+ const protocol_list_t ** protocols;
struct class_t *firstSubclass;
struct class_t *nextSiblingClass;
} class_rw_t;
+// We cannot store flags in the low bits of the 'data' field until we work with
+// the 'leaks' team to not think that objc is leaking memory. See radar 8955342
+// for more info.
+#define CLASS_FAST_FLAGS_VIA_RW_DATA 0
+
typedef struct class_t {
struct class_t *isa;
struct class_t *superclass;
Cache cache;
IMP *vtable;
- class_rw_t *data;
+ uintptr_t data_NEVER_USE; // class_rw_t * plus flags
+
+ class_rw_t *data() const {
+#if CLASS_FAST_FLAGS_VIA_RW_DATA
+ return (class_rw_t *)(data_NEVER_USE & ~3UL);
+#else
+ return (class_rw_t *)data_NEVER_USE;
+#endif
+ }
+ void setData(class_rw_t *newData) {
+#if CLASS_FAST_FLAGS_VIA_RW_DATA
+ uintptr_t flags = (uintptr_t)data_NEVER_USE & 3UL;
+ data_NEVER_USE = (uintptr_t)newData | flags;
+#else
+ data_NEVER_USE = (uintptr_t)newData;
+#endif
+ }
+
+ bool hasCustomRR() const {
+#if CLASS_FAST_FLAGS_VIA_RW_DATA
+ return data_NEVER_USE & 1UL;
+#else
+ return (data()->flags & RW_HAS_CUSTOM_RR);
+#endif
+ }
+ void setHasCustomRR();
+ void unsetHasCustomRR();
+
+ bool hasIvarReleaser() const {
+ return (data()->flags & RW_HAS_IVAR_RELEASER);
+ }
+ void setHasIvarReleaser();
+
+ bool isRootClass() const {
+ return superclass == NULL;
+ }
+ bool isRootMetaclass() const {
+ return isa == this;
+ }
} class_t;
typedef struct category_t {
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
- struct objc_property_list *instanceProperties;
+ struct property_list_t *instanceProperties;
} category_t;
struct objc_super2 {
id receiver;
Class current_class;
};
+
+typedef struct {
+ IMP imp;
+ SEL sel;
+} message_ref_t;
+
+
+__END_DECLS
+
+#endif
+++ /dev/null
-/*
- * Copyright (c) 2005-2008 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/***********************************************************************
-* objc-runtime-new.m
-* Support for new-ABI classes and images.
-**********************************************************************/
-
-#if __OBJC2__
-
-#include "objc-private.h"
-#include "objc-runtime-new.h"
-#include <objc/message.h>
-
-#define newcls(cls) ((struct class_t *)cls)
-#define newcat(cat) ((struct category_t *)cat)
-#define newmethod(meth) ((struct method_t *)meth)
-#define newivar(ivar) ((struct ivar_t *)ivar)
-#define newcategory(cat) ((struct category_t *)cat)
-#define newprotocol(p) ((struct protocol_t *)p)
-
-#ifdef __LP64__
-#define WORD_SHIFT 3UL
-#define WORD_MASK 7UL
-#else
-#define WORD_SHIFT 2UL
-#define WORD_MASK 3UL
-#endif
-
-static const char *getName(struct class_t *cls);
-static uint32_t instanceSize(struct class_t *cls);
-static BOOL isMetaClass(struct class_t *cls);
-static struct class_t *getSuperclass(struct class_t *cls);
-static void unload_class(class_t *cls, BOOL isMeta);
-static class_t *setSuperclass(class_t *cls, class_t *newSuper);
-static class_t *realizeClass(class_t *cls);
-static void flushCaches(class_t *cls);
-static void flushVtables(class_t *cls);
-static method_t *getMethodNoSuper_nolock(struct class_t *cls, SEL sel);
-static method_t *getMethod_nolock(class_t *cls, SEL sel);
-static void changeInfo(class_t *cls, unsigned int set, unsigned int clear);
-static IMP _method_getImplementation(method_t *m);
-
-
-/***********************************************************************
-* Lock management
-* Every lock used anywhere must be managed here.
-* Locks not managed here may cause gdb deadlocks.
-**********************************************************************/
-__private_extern__ rwlock_t runtimeLock = {0};
-__private_extern__ rwlock_t selLock = {0};
-__private_extern__ mutex_t cacheUpdateLock = MUTEX_INITIALIZER;
-__private_extern__ recursive_mutex_t loadMethodLock = RECURSIVE_MUTEX_INITIALIZER;
-static int debugger_runtimeLock;
-static int debugger_selLock;
-static int debugger_cacheUpdateLock;
-static int debugger_loadMethodLock;
-#define RDONLY 1
-#define RDWR 2
-
-__private_extern__ void lock_init(void)
-{
- rwlock_init(&selLock);
- rwlock_init(&runtimeLock);
- recursive_mutex_init(&loadMethodLock);
-}
-
-
-/***********************************************************************
-* startDebuggerMode
-* Attempt to acquire some locks for debugger mode.
-* Returns 0 if debugger mode failed because too many locks are unavailable.
-*
-* Locks successfully acquired are held until endDebuggerMode().
-* Locks not acquired are off-limits until endDebuggerMode(); any
-* attempt to manipulate them will cause a trap.
-* Locks not handled here may cause deadlocks in gdb.
-**********************************************************************/
-__private_extern__ int startDebuggerMode(void)
-{
- int result = DEBUGGER_FULL;
-
- // runtimeLock is required (can't do much without it)
- if (rwlock_try_write(&runtimeLock)) {
- debugger_runtimeLock = RDWR;
- } else if (rwlock_try_read(&runtimeLock)) {
- debugger_runtimeLock = RDONLY;
- result = DEBUGGER_PARTIAL;
- } else {
- return DEBUGGER_OFF;
- }
-
- // cacheUpdateLock is required (must not fail a necessary cache flush)
- // must be AFTER runtimeLock to avoid lock inversion
- if (mutex_try_lock(&cacheUpdateLock)) {
- debugger_cacheUpdateLock = RDWR;
- } else {
- rwlock_unlock(&runtimeLock, debugger_runtimeLock);
- debugger_runtimeLock = 0;
- return DEBUGGER_OFF;
- }
-
- // selLock is optional
- if (rwlock_try_write(&selLock)) {
- debugger_selLock = RDWR;
- } else if (rwlock_try_read(&selLock)) {
- debugger_selLock = RDONLY;
- result = DEBUGGER_PARTIAL;
- } else {
- debugger_selLock = 0;
- result = DEBUGGER_PARTIAL;
- }
-
- // loadMethodLock is optional
- if (recursive_mutex_try_lock(&loadMethodLock)) {
- debugger_loadMethodLock = RDWR;
- } else {
- debugger_loadMethodLock = 0;
- result = DEBUGGER_PARTIAL;
- }
-
- return result;
-}
-
-/***********************************************************************
-* endDebuggerMode
-* Relinquish locks acquired in startDebuggerMode().
-**********************************************************************/
-__private_extern__ void endDebuggerMode(void)
-{
- assert(debugger_runtimeLock != 0);
-
- rwlock_unlock(&runtimeLock, debugger_runtimeLock);
- debugger_runtimeLock = 0;
-
- rwlock_unlock(&selLock, debugger_selLock);
- debugger_selLock = 0;
-
- assert(debugger_cacheUpdateLock == RDWR);
- mutex_unlock(&cacheUpdateLock);
- debugger_cacheUpdateLock = 0;
-
- if (debugger_loadMethodLock) {
- recursive_mutex_unlock(&loadMethodLock);
- debugger_loadMethodLock = 0;
- }
-}
-
-/***********************************************************************
-* isManagedDuringDebugger
-* Returns YES if the given lock is handled specially during debugger
-* mode (i.e. debugger mode tries to acquire it).
-**********************************************************************/
-__private_extern__ BOOL isManagedDuringDebugger(void *lock)
-{
- if (lock == &selLock) return YES;
- if (lock == &cacheUpdateLock) return YES;
- if (lock == &runtimeLock) return YES;
- if (lock == &loadMethodLock) return YES;
- return NO;
-}
-
-/***********************************************************************
-* isLockedDuringDebugger
-* Returns YES if the given mutex was acquired by debugger mode.
-* Locking a managed mutex during debugger mode causes a trap unless
-* this returns YES.
-**********************************************************************/
-__private_extern__ BOOL isLockedDuringDebugger(mutex_t *lock)
-{
- assert(DebuggerMode);
-
- if (lock == &cacheUpdateLock) return YES;
- if (lock == (mutex_t *)&loadMethodLock) return YES;
-
- return NO;
-}
-
-/***********************************************************************
-* isReadingDuringDebugger
-* Returns YES if the given rwlock was read-locked by debugger mode.
-* Read-locking a managed rwlock during debugger mode causes a trap unless
-* this returns YES.
-**********************************************************************/
-__private_extern__ BOOL isReadingDuringDebugger(rwlock_t *lock)
-{
- assert(DebuggerMode);
-
- // read-lock is allowed even if debugger mode actually write-locked it
- if (debugger_runtimeLock && lock == &runtimeLock) return YES;
- if (debugger_selLock && lock == &selLock) return YES;
-
- return NO;
-}
-
-/***********************************************************************
-* isWritingDuringDebugger
-* Returns YES if the given rwlock was write-locked by debugger mode.
-* Write-locking a managed rwlock during debugger mode causes a trap unless
-* this returns YES.
-**********************************************************************/
-__private_extern__ BOOL isWritingDuringDebugger(rwlock_t *lock)
-{
- assert(DebuggerMode);
-
- if (debugger_runtimeLock == RDWR && lock == &runtimeLock) return YES;
- if (debugger_selLock == RDWR && lock == &selLock) return YES;
-
- return NO;
-}
-
-
-/***********************************************************************
-* vtable dispatch
-*
-* Every class gets a vtable pointer. The vtable is an array of IMPs.
-* The selectors represented in the vtable are the same for all classes
-* (i.e. no class has a bigger or smaller vtable).
-* Each vtable index has an associated trampoline which dispatches to
-* the IMP at that index for the receiver class's vtable (after
-* checking for NULL). Dispatch fixup uses these trampolines instead
-* of objc_msgSend.
-* Fragility: The vtable size and list of selectors is chosen at launch
-* time. No compiler-generated code depends on any particular vtable
-* configuration, or even the use of vtable dispatch at all.
-* Memory size: If a class's vtable is identical to its superclass's
-* (i.e. the class overrides none of the vtable selectors), then
-* the class points directly to its superclass's vtable. This means
-* selectors to be included in the vtable should be chosen so they are
-* (1) frequently called, but (2) not too frequently overridden. In
-* particular, -dealloc is a bad choice.
-* Forwarding: If a class doesn't implement some vtable selector, that
-* selector's IMP is set to objc_msgSend in that class's vtable.
-* +initialize: Each class keeps the default vtable (which always
-* redirects to objc_msgSend) until its +initialize is completed.
-* Otherwise, the first message to a class could be a vtable dispatch,
-* and the vtable trampoline doesn't include +initialize checking.
-* Changes: Categories, addMethod, and setImplementation all force vtable
-* reconstruction for the class and all of its subclasses, if the
-* vtable selectors are affected.
-**********************************************************************/
-
-#define X8(x) \
- x, x, x, x, x, x, x, x
-#define X64(x) \
- X8(x), X8(x), X8(x), X8(x), X8(x), X8(x), X8(x), X8(x)
-#define X128(x) \
- X64(x), X64(x)
-
-#define vtableMax 128
-
-IMP _objc_empty_vtable[vtableMax] = {
- X128(objc_msgSend)
-};
-
-#ifndef NO_VTABLE
-
-// Trampoline descriptors for gdb.
-
-objc_trampoline_header *gdb_objc_trampolines = NULL;
-
-void gdb_objc_trampolines_changed(objc_trampoline_header *thdr) __attribute__((noinline));
-void gdb_objc_trampolines_changed(objc_trampoline_header *thdr)
-{
- rwlock_assert_writing(&runtimeLock);
- assert(thdr == gdb_objc_trampolines);
-
- if (PrintVtables) {
- _objc_inform("VTABLES: gdb_objc_trampolines_changed(%p)", thdr);
- }
-}
-
-// fixme workaround for rdar://6667753
-static void appendTrampolines(objc_trampoline_header *thdr) __attribute__((noinline));
-
-static void appendTrampolines(objc_trampoline_header *thdr)
-{
- rwlock_assert_writing(&runtimeLock);
- assert(thdr->next == NULL);
-
- if (gdb_objc_trampolines != thdr->next) {
- thdr->next = gdb_objc_trampolines;
- }
- gdb_objc_trampolines = thdr;
-
- gdb_objc_trampolines_changed(thdr);
-}
-
-// Vtable management.
-
-static size_t vtableStrlen;
-static size_t vtableCount;
-static SEL *vtableSelectors;
-static IMP *vtableTrampolines;
-static const char * const defaultVtable[] = {
- "allocWithZone:",
- "alloc",
- "class",
- "self",
- "isKindOfClass:",
- "respondsToSelector:",
- "isFlipped",
- "length",
- "objectForKey:",
- "count",
- "objectAtIndex:",
- "isEqualToString:",
- "isEqual:",
- "retain",
- "release",
- "autorelease",
-};
-static const char * const defaultVtableGC[] = {
- "allocWithZone:",
- "alloc",
- "class",
- "self",
- "isKindOfClass:",
- "respondsToSelector:",
- "isFlipped",
- "length",
- "objectForKey:",
- "count",
- "objectAtIndex:",
- "isEqualToString:",
- "isEqual:",
- "hash",
- "addObject:",
- "countByEnumeratingWithState:objects:count:",
-};
-
-extern id objc_msgSend_vtable0(id, SEL, ...);
-extern id objc_msgSend_vtable1(id, SEL, ...);
-extern id objc_msgSend_vtable2(id, SEL, ...);
-extern id objc_msgSend_vtable3(id, SEL, ...);
-extern id objc_msgSend_vtable4(id, SEL, ...);
-extern id objc_msgSend_vtable5(id, SEL, ...);
-extern id objc_msgSend_vtable6(id, SEL, ...);
-extern id objc_msgSend_vtable7(id, SEL, ...);
-extern id objc_msgSend_vtable8(id, SEL, ...);
-extern id objc_msgSend_vtable9(id, SEL, ...);
-extern id objc_msgSend_vtable10(id, SEL, ...);
-extern id objc_msgSend_vtable11(id, SEL, ...);
-extern id objc_msgSend_vtable12(id, SEL, ...);
-extern id objc_msgSend_vtable13(id, SEL, ...);
-extern id objc_msgSend_vtable14(id, SEL, ...);
-extern id objc_msgSend_vtable15(id, SEL, ...);
-
-static IMP const defaultVtableTrampolines[] = {
- objc_msgSend_vtable0,
- objc_msgSend_vtable1,
- objc_msgSend_vtable2,
- objc_msgSend_vtable3,
- objc_msgSend_vtable4,
- objc_msgSend_vtable5,
- objc_msgSend_vtable6,
- objc_msgSend_vtable7,
- objc_msgSend_vtable8,
- objc_msgSend_vtable9,
- objc_msgSend_vtable10,
- objc_msgSend_vtable11,
- objc_msgSend_vtable12,
- objc_msgSend_vtable13,
- objc_msgSend_vtable14,
- objc_msgSend_vtable15,
-};
-extern objc_trampoline_header defaultVtableTrampolineDescriptors;
-
-static void check_vtable_size(void) __unused;
-static void check_vtable_size(void)
-{
- // Fail to compile if vtable sizes don't match.
- int c1[sizeof(defaultVtableTrampolines)-sizeof(defaultVtable)] __unused;
- int c2[sizeof(defaultVtable)-sizeof(defaultVtableTrampolines)] __unused;
- int c3[sizeof(defaultVtableTrampolines)-sizeof(defaultVtableGC)] __unused;
- int c4[sizeof(defaultVtableGC)-sizeof(defaultVtableTrampolines)] __unused;
-
- // Fail to compile if vtableMax is too small
- int c5[vtableMax - sizeof(defaultVtable)] __unused;
- int c6[vtableMax - sizeof(defaultVtableGC)] __unused;
-}
-
-/*
- x86_64
-
- monomorphic (self rdi, sel* rsi, temp r10 and r11) {
- test %rdi, %rdi
- jeq returnZero // nil check
- movq 8(%rsi), %rsi // load _cmd (fixme schedule)
- movq $xxxx, %r10
- cmp 0(%rdi), %r10 // isa check
- jeq imp // fixme long branches
- movq $yyyy, %r10
- cmp 0(%rdi), %r10 // fixme load rdi once for multiple isas
- jeq imp2 // fixme long branches
- jmp objc_msgSend // fixme long branches
- }
-
-*/
-extern uint8_t vtable_prototype;
-extern uint8_t vtable_ignored;
-extern int vtable_prototype_size;
-extern int vtable_prototype_index_offset;
-static size_t makeVtableTrampoline(uint8_t *dst, size_t index)
-{
- // copy boilerplate
- memcpy(dst, &vtable_prototype, vtable_prototype_size);
-
- // insert index
-#if defined(__x86_64__)
- uint16_t *p = (uint16_t *)(dst + vtable_prototype_index_offset + 3);
- if (*p != 0x7fff) _objc_fatal("vtable_prototype busted");
- *p = index * 8;
-#else
-# warning unknown architecture
-#endif
-
- return vtable_prototype_size;
-}
-
-
-static void initVtables(void)
-{
- if (DisableVtables) {
- if (PrintVtables) {
- _objc_inform("VTABLES: vtable dispatch disabled by OBJC_DISABLE_VTABLES");
- }
- vtableCount = 0;
- vtableSelectors = NULL;
- vtableTrampolines = NULL;
- return;
- }
-
- const char * const *names;
- size_t i;
-
- if (UseGC) {
- names = defaultVtableGC;
- vtableCount = sizeof(defaultVtableGC) / sizeof(defaultVtableGC[0]);
- } else {
- names = defaultVtable;
- vtableCount = sizeof(defaultVtable) / sizeof(defaultVtable[0]);
- }
- if (vtableCount > vtableMax) vtableCount = vtableMax;
-
- vtableSelectors = _malloc_internal(vtableCount * sizeof(SEL));
- vtableTrampolines = _malloc_internal(vtableCount * sizeof(IMP));
-
- // Built-in trampolines and their descriptors
-
- size_t defaultVtableTrampolineCount =
- sizeof(defaultVtableTrampolines) / sizeof(defaultVtableTrampolines[0]);
-#ifndef NDEBUG
- // debug: use generated code for 3/4 of the table
- defaultVtableTrampolineCount /= 4;
-#endif
-
- for (i = 0; i < defaultVtableTrampolineCount && i < vtableCount; i++) {
- vtableSelectors[i] = sel_registerName(names[i]);
- vtableTrampolines[i] = defaultVtableTrampolines[i];
- }
- appendTrampolines(&defaultVtableTrampolineDescriptors);
-
-
- // Generated trampolines and their descriptors
-
- if (vtableCount > defaultVtableTrampolineCount) {
- // Memory for trampoline code
- size_t generatedCount =
- vtableCount - defaultVtableTrampolineCount;
-
- const int align = 16;
- size_t codeSize =
- round_page(sizeof(objc_trampoline_header) + align +
- generatedCount * (sizeof(objc_trampoline_descriptor)
- + vtable_prototype_size + align));
- void *codeAddr = mmap(0, codeSize, PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MAP_ANON,
- VM_MAKE_TAG(VM_MEMORY_OBJC_DISPATCHERS), 0);
- uint8_t *t = (uint8_t *)codeAddr;
-
- // Trampoline header
- objc_trampoline_header *thdr = (objc_trampoline_header *)t;
- thdr->headerSize = sizeof(objc_trampoline_header);
- thdr->descSize = sizeof(objc_trampoline_descriptor);
- thdr->descCount = (uint32_t)generatedCount;
- thdr->next = NULL;
-
- // Trampoline descriptors
- objc_trampoline_descriptor *tdesc = (objc_trampoline_descriptor *)(thdr+1);
- t = (uint8_t *)&tdesc[generatedCount];
- t += align - ((uintptr_t)t % align);
-
- // Dispatch code
- size_t tdi;
- for (i = defaultVtableTrampolineCount, tdi = 0;
- i < vtableCount;
- i++, tdi++)
- {
- vtableSelectors[i] = sel_registerName(names[i]);
- if (vtableSelectors[i] == (SEL)kIgnore) {
- vtableTrampolines[i] = (IMP)&vtable_ignored;
- tdesc[tdi].offset = 0;
- tdesc[tdi].flags = 0;
- } else {
- vtableTrampolines[i] = (IMP)t;
- tdesc[tdi].offset =
- (uint32_t)((uintptr_t)t - (uintptr_t)&tdesc[tdi]);
- tdesc[tdi].flags =
- OBJC_TRAMPOLINE_MESSAGE|OBJC_TRAMPOLINE_VTABLE;
-
- t += makeVtableTrampoline(t, i);
- t += align - ((uintptr_t)t % align);
- }
- }
-
- appendTrampolines(thdr);
- sys_icache_invalidate(codeAddr, codeSize);
- mprotect(codeAddr, codeSize, PROT_READ|PROT_EXEC);
- }
-
-
- if (PrintVtables) {
- for (i = 0; i < vtableCount; i++) {
- _objc_inform("VTABLES: vtable[%zu] %p %s",
- i, vtableTrampolines[i],
- sel_getName(vtableSelectors[i]));
- }
- }
-
- if (PrintVtableImages) {
- _objc_inform("VTABLE IMAGES: '#' implemented by class");
- _objc_inform("VTABLE IMAGES: '-' inherited from superclass");
- _objc_inform("VTABLE IMAGES: ' ' not implemented");
- for (i = 0; i <= vtableCount; i++) {
- char spaces[vtableCount+1+1];
- size_t j;
- for (j = 0; j < i; j++) {
- spaces[j] = '|';
- }
- spaces[j] = '\0';
- _objc_inform("VTABLE IMAGES: %s%s", spaces,
- i<vtableCount ? sel_getName(vtableSelectors[i]) : "");
- }
- }
-
- if (PrintVtables || PrintVtableImages) {
- vtableStrlen = 0;
- for (i = 0; i < vtableCount; i++) {
- vtableStrlen += strlen(sel_getName(vtableSelectors[i]));
- }
- }
-}
-
-
-static int vtable_getIndex(SEL sel)
-{
- int i;
- for (i = 0; i < vtableCount; i++) {
- if (vtableSelectors[i] == sel) return i;
- }
- return -1;
-}
-
-static BOOL vtable_containsSelector(SEL sel)
-{
- return (vtable_getIndex(sel) < 0) ? NO : YES;
-}
-
-static void printVtableOverrides(class_t *cls, class_t *supercls)
-{
- char overrideMap[vtableCount+1];
- int i;
-
- if (supercls) {
- size_t overridesBufferSize = vtableStrlen + 2*vtableCount + 1;
- char *overrides =
- _calloc_internal(overridesBufferSize, 1);
- for (i = 0; i < vtableCount; i++) {
- if (vtableSelectors[i] == (SEL)kIgnore) {
- overrideMap[i] = '-';
- continue;
- }
- if (getMethodNoSuper_nolock(cls, vtableSelectors[i])) {
- strlcat(overrides, sel_getName(vtableSelectors[i]), overridesBufferSize);
- strlcat(overrides, ", ", overridesBufferSize);
- overrideMap[i] = '#';
- } else if (getMethod_nolock(cls, vtableSelectors[i])) {
- overrideMap[i] = '-';
- } else {
- overrideMap[i] = ' ';
- }
- }
- if (PrintVtables) {
- _objc_inform("VTABLES: %s%s implements %s",
- getName(cls), isMetaClass(cls) ? "(meta)" : "",
- overrides);
- }
- _free_internal(overrides);
- }
- else {
- for (i = 0; i < vtableCount; i++) {
- overrideMap[i] = '#';
- }
- }
-
- if (PrintVtableImages) {
- overrideMap[vtableCount] = '\0';
- _objc_inform("VTABLE IMAGES: %s %s%s", overrideMap,
- getName(cls), isMetaClass(cls) ? "(meta)" : "");
- }
-}
-
-/***********************************************************************
-* updateVtable
-* Rebuilds vtable for cls, using superclass's vtable if appropriate.
-* Assumes superclass's vtable is up to date.
-* Does nothing to subclass vtables.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static void updateVtable(class_t *cls, BOOL force)
-{
- rwlock_assert_writing(&runtimeLock);
-
- // Keep default vtable until +initialize is complete.
- // Default vtable redirects to objc_msgSend, which
- // enforces +initialize locking.
- if (!force && !_class_isInitialized((Class)cls)) {
- /*
- if (PrintVtables) {
- _objc_inform("VTABLES: KEEPING DEFAULT vtable for "
- "uninitialized class %s%s",
- getName(cls), isMetaClass(cls) ? "(meta)" : "");
- }
- */
- return;
- }
-
- // Decide whether this class can share its superclass's vtable.
-
- struct class_t *supercls = getSuperclass(cls);
- BOOL needVtable = NO;
- int i;
- if (!supercls) {
- // Root classes always need a vtable
- needVtable = YES;
- }
- else if (cls->data->flags & RW_SPECIALIZED_VTABLE) {
- // Once you have your own vtable, you never go back
- needVtable = YES;
- }
- else {
- for (i = 0; i < vtableCount; i++) {
- if (vtableSelectors[i] == (SEL)kIgnore) continue;
- method_t *m = getMethodNoSuper_nolock(cls, vtableSelectors[i]);
- // assume any local implementation differs from super's
- if (m) {
- needVtable = YES;
- break;
- }
- }
- }
-
- // Build a vtable for this class, or not.
-
- if (!needVtable) {
- if (PrintVtables) {
- _objc_inform("VTABLES: USING SUPERCLASS vtable for class %s%s",
- getName(cls), isMetaClass(cls) ? "(meta)" : "");
- }
- cls->vtable = supercls->vtable;
- }
- else {
- if (PrintVtables) {
- _objc_inform("VTABLES: %s vtable for class %s%s",
- (cls->data->flags & RW_SPECIALIZED_VTABLE) ?
- "UPDATING SPECIALIZED" : "CREATING SPECIALIZED",
- getName(cls), isMetaClass(cls) ? "(meta)" : "");
- }
- if (PrintVtables || PrintVtableImages) {
- printVtableOverrides(cls, supercls);
- }
-
- IMP *new_vtable = cls->vtable;
- IMP *super_vtable = supercls ? supercls->vtable : _objc_empty_vtable;
- // fixme use msgForward (instead of msgSend from empty vtable) ?
-
- if (cls->data->flags & RW_SPECIALIZED_VTABLE) {
- // update cls->vtable in place
- new_vtable = cls->vtable;
- assert(new_vtable != _objc_empty_vtable);
- } else {
- // make new vtable
- new_vtable = malloc(vtableCount * sizeof(IMP));
- changeInfo(cls, RW_SPECIALIZED_VTABLE, 0);
- }
-
- for (i = 0; i < vtableCount; i++) {
- if (vtableSelectors[i] == (SEL)kIgnore) {
- new_vtable[i] = (IMP)&vtable_ignored;
- } else {
- method_t *m = getMethodNoSuper_nolock(cls, vtableSelectors[i]);
- if (m) new_vtable[i] = _method_getImplementation(m);
- else new_vtable[i] = super_vtable[i];
- }
- }
-
- if (cls->vtable != new_vtable) {
- // don't let other threads see uninitialized parts of new_vtable
- OSMemoryBarrier();
- cls->vtable = new_vtable;
- }
- }
-}
-
-// ! NO_VTABLE
-#else
-// NO_VTABLE
-
-static void initVtables(void)
-{
- if (PrintVtables) {
- _objc_inform("VTABLES: no vtables on this architecture");
- }
-}
-
-static BOOL vtable_containsSelector(SEL sel)
-{
- return NO;
-}
-
-static void updateVtable(class_t *cls, BOOL force)
-{
-}
-
-// NO_VTABLE
-#endif
-
-typedef struct {
- category_t *cat;
- BOOL fromBundle;
-} category_pair_t;
-
-typedef struct {
- uint32_t count;
- category_pair_t list[0]; // variable-size
-} category_list;
-
-#define FOREACH_METHOD_LIST(_mlist, _cls, code) \
- do { \
- const method_list_t *_mlist; \
- if (_cls->data->methods) { \
- method_list_t **_mlistp; \
- for (_mlistp = _cls->data->methods; *_mlistp; _mlistp++) { \
- _mlist = *_mlistp; \
- code \
- } \
- } \
- } while (0)
-
-
-// fixme don't chain property lists
-typedef struct chained_property_list {
- struct chained_property_list *next;
- uint32_t count;
- struct objc_property list[0]; // variable-size
-} chained_property_list;
-
-/*
- Low two bits of mlist->entsize is used as the fixed-up marker.
- PREOPTIMIZED VERSION:
- Fixed-up method lists get entsize&3 == 3.
- dyld shared cache sets this for method lists it preoptimizes.
- UN-PREOPTIMIZED VERSION:
- Fixed-up method lists get entsize&3 == 1.
- dyld shared cache uses 3, but those aren't trusted.
-*/
-
-static uint32_t fixed_up_method_list = 3;
-
-__private_extern__ void
-disableSelectorPreoptimization(void)
-{
- fixed_up_method_list = 1;
-}
-
-static BOOL isMethodListFixedUp(const method_list_t *mlist)
-{
- return (mlist->entsize_NEVER_USE & 3) == fixed_up_method_list;
-}
-
-static void setMethodListFixedUp(method_list_t *mlist)
-{
- rwlock_assert_writing(&runtimeLock);
- assert(!isMethodListFixedUp(mlist));
- mlist->entsize_NEVER_USE = (mlist->entsize_NEVER_USE & ~3) | fixed_up_method_list;
-}
-
-/*
-static size_t chained_property_list_size(const chained_property_list *plist)
-{
- return sizeof(chained_property_list) +
- plist->count * sizeof(struct objc_property);
-}
-
-static size_t protocol_list_size(const protocol_list_t *plist)
-{
- return sizeof(protocol_list_t) + plist->count * sizeof(protocol_t *);
-}
-*/
-
-// low bit used by dyld shared cache
-static uint32_t method_list_entsize(const method_list_t *mlist)
-{
- return mlist->entsize_NEVER_USE & ~(uint32_t)3;
-}
-
-static size_t method_list_size(const method_list_t *mlist)
-{
- return sizeof(method_list_t) + (mlist->count-1)*method_list_entsize(mlist);
-}
-
-static method_t *method_list_nth(const method_list_t *mlist, uint32_t i)
-{
- return (method_t *)(i*method_list_entsize(mlist) + (char *)&mlist->first);
-}
-
-
-static size_t ivar_list_size(const ivar_list_t *ilist)
-{
- return sizeof(ivar_list_t) + (ilist->count-1) * ilist->entsize;
-}
-
-static ivar_t *ivar_list_nth(const ivar_list_t *ilist, uint32_t i)
-{
- return (ivar_t *)(i*ilist->entsize + (char *)&ilist->first);
-}
-
-
-static method_list_t *cat_method_list(const category_t *cat, BOOL isMeta)
-{
- if (!cat) return NULL;
-
- if (isMeta) return cat->classMethods;
- else return cat->instanceMethods;
-}
-
-static uint32_t cat_method_count(const category_t *cat, BOOL isMeta)
-{
- method_list_t *cmlist = cat_method_list(cat, isMeta);
- return cmlist ? cmlist->count : 0;
-}
-
-static method_t *cat_method_nth(const category_t *cat, BOOL isMeta, uint32_t i)
-{
- method_list_t *cmlist = cat_method_list(cat, isMeta);
- if (!cmlist) return NULL;
-
- return method_list_nth(cmlist, i);
-}
-
-
-// part of ivar_t, with non-deprecated alignment
-typedef struct {
- uintptr_t *offset;
- const char *name;
- const char *type;
- uint32_t alignment;
-} ivar_alignment_t;
-
-static uint32_t ivar_alignment(const ivar_t *ivar)
-{
- uint32_t alignment = ((ivar_alignment_t *)ivar)->alignment;
- if (alignment == (uint32_t)-1) alignment = (uint32_t)WORD_SHIFT;
- return 1<<alignment;
-}
-
-
-static void try_free(const void *p)
-{
- if (p && malloc_size(p)) free((void *)p);
-}
-
-
-/***********************************************************************
-* make_ro_writeable
-* Reallocates rw->ro if necessary to make it writeable.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static class_ro_t *make_ro_writeable(class_rw_t *rw)
-{
- rwlock_assert_writing(&runtimeLock);
-
- if (rw->flags & RW_COPIED_RO) {
- // already writeable, do nothing
- } else {
- class_ro_t *ro = _memdup_internal(rw->ro, sizeof(*rw->ro));
- rw->ro = ro;
- rw->flags |= RW_COPIED_RO;
- }
- return (class_ro_t *)rw->ro;
-}
-
-
-/***********************************************************************
-* unattachedCategories
-* Returns the class => categories map of unattached categories.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static NXMapTable *unattachedCategories(void)
-{
- rwlock_assert_writing(&runtimeLock);
-
- static NXMapTable *category_map = NULL;
-
- if (category_map) return category_map;
-
- // fixme initial map size
- category_map = NXCreateMapTableFromZone(NXPtrValueMapPrototype, 16,
- _objc_internal_zone());
-
- return category_map;
-}
-
-
-/***********************************************************************
-* addUnattachedCategoryForClass
-* Records an unattached category.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static void addUnattachedCategoryForClass(category_t *cat, class_t *cls,
- header_info *catHeader)
-{
- rwlock_assert_writing(&runtimeLock);
-
- BOOL catFromBundle = (catHeader->mhdr->filetype == MH_BUNDLE) ? YES: NO;
-
- // DO NOT use cat->cls!
- // cls may be cat->cls->isa, or cat->cls may have been remapped.
- NXMapTable *cats = unattachedCategories();
- category_list *list;
-
- list = NXMapGet(cats, cls);
- if (!list) {
- list = _calloc_internal(sizeof(*list) + sizeof(list->list[0]), 1);
- } else {
- list = _realloc_internal(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1));
- }
- list->list[list->count++] = (category_pair_t){cat, catFromBundle};
- NXMapInsert(cats, cls, list);
-}
-
-
-/***********************************************************************
-* removeUnattachedCategoryForClass
-* Removes an unattached category.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static void removeUnattachedCategoryForClass(category_t *cat, class_t *cls)
-{
- rwlock_assert_writing(&runtimeLock);
-
- // DO NOT use cat->cls!
- // cls may be cat->cls->isa, or cat->cls may have been remapped.
- NXMapTable *cats = unattachedCategories();
- category_list *list;
-
- list = NXMapGet(cats, cls);
- if (!list) return;
-
- uint32_t i;
- for (i = 0; i < list->count; i++) {
- if (list->list[i].cat == cat) {
- // shift entries to preserve list order
- memmove(&list->list[i], &list->list[i+1],
- (list->count-i-1) * sizeof(list->list[i]));
- list->count--;
- return;
- }
- }
-}
-
-
-/***********************************************************************
-* unattachedCategoriesForClass
-* Returns the list of unattached categories for a class, and
-* deletes them from the list.
-* The result must be freed by the caller.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static category_list *unattachedCategoriesForClass(class_t *cls)
-{
- rwlock_assert_writing(&runtimeLock);
- return NXMapRemove(unattachedCategories(), cls);
-}
-
-
-/***********************************************************************
-* isRealized
-* Returns YES if class cls has been realized.
-* Locking: To prevent concurrent realization, hold runtimeLock.
-**********************************************************************/
-static BOOL isRealized(class_t *cls)
-{
- return (cls->data->flags & RW_REALIZED) ? YES : NO;
-}
-
-
-/***********************************************************************
-* isFuture
-* Returns YES if class cls is an unrealized future class.
-* Locking: To prevent concurrent realization, hold runtimeLock.
-**********************************************************************/
-static BOOL isFuture(class_t *cls)
-{
- return (cls->data->flags & RW_FUTURE) ? YES : NO;
-}
-
-
-/***********************************************************************
-* printReplacements
-* Implementation of PrintReplacedMethods / OBJC_PRINT_REPLACED_METHODS.
-* Warn about methods from cats that override other methods in cats or cls.
-* Assumes no methods from cats have been added to cls yet.
-**********************************************************************/
-static void printReplacements(class_t *cls, category_list *cats)
-{
- uint32_t c;
- BOOL isMeta = isMetaClass(cls);
-
- if (!cats) return;
-
- // Newest categories are LAST in cats
- // Later categories override earlier ones.
- for (c = 0; c < cats->count; c++) {
- category_t *cat = cats->list[c].cat;
- uint32_t cmCount = cat_method_count(cat, isMeta);
- uint32_t m;
- for (m = 0; m < cmCount; m++) {
- uint32_t c2, m2;
- method_t *meth2 = NULL;
- method_t *meth = cat_method_nth(cat, isMeta, m);
- SEL s = sel_registerName((const char *)meth->name);
-
- // Don't warn about GC-ignored selectors
- if (s == (SEL)kIgnore) continue;
-
- // Look for method in earlier categories
- for (c2 = 0; c2 < c; c2++) {
- category_t *cat2 = cats->list[c2].cat;
- uint32_t cm2Count = cat_method_count(cat2, isMeta);
- for (m2 = 0; m2 < cm2Count; m2++) {
- meth2 = cat_method_nth(cat2, isMeta, m2);
- SEL s2 = sel_registerName((const char *)meth2->name);
- if (s == s2) goto whine;
- }
- }
-
- // Look for method in cls
- FOREACH_METHOD_LIST(mlist, cls, {
- for (m2 = 0; m2 < mlist->count; m2++) {
- meth2 = method_list_nth(mlist, m2);
- SEL s2 = sel_registerName((const char *)meth2->name);
- if (s == s2) goto whine;
- }
- });
-
- // Didn't find any override.
- continue;
-
- whine:
- // Found an override.
- logReplacedMethod(getName(cls), s, isMetaClass(cls), cat->name,
- _method_getImplementation(meth2),
- _method_getImplementation(meth));
- }
- }
-}
-
-
-static BOOL isBundleClass(class_t *cls)
-{
- return (cls->data->ro->flags & RO_FROM_BUNDLE) ? YES : NO;
-}
-
-
-static void
-fixupMethodList(method_list_t *mlist, BOOL bundleCopy)
-{
- assert(!isMethodListFixedUp(mlist));
-
- // fixme lock less in attachMethodLists ?
- sel_lock();
-
- uint32_t m;
- for (m = 0; m < mlist->count; m++) {
- method_t *meth = method_list_nth(mlist, m);
- SEL sel = sel_registerNameNoLock((const char *)meth->name, bundleCopy);
- meth->name = sel;
-
- if (sel == (SEL)kIgnore) {
- meth->imp = (IMP)&_objc_ignored_method;
- }
- }
-
- sel_unlock();
-
- setMethodListFixedUp(mlist);
-}
-
-static void
-attachMethodLists(class_t *cls, method_list_t **lists, int count,
- BOOL methodsFromBundle, BOOL *outVtablesAffected)
-{
- rwlock_assert_writing(&runtimeLock);
-
- BOOL vtablesAffected = NO;
- size_t listsSize = count * sizeof(*lists);
-
- // Create or extend method list array
- // Leave `count` empty slots at the start of the array to be filled below.
-
- if (!cls->data->methods) {
- // no bonus method lists yet
- cls->data->methods = _calloc_internal(1 + count, sizeof(*lists));
- } else {
- size_t oldSize = malloc_size(cls->data->methods);
- cls->data->methods =
- _realloc_internal(cls->data->methods, oldSize + listsSize);
- memmove(cls->data->methods + count, cls->data->methods, oldSize);
- }
-
- // Add method lists to array.
- // Reallocate un-fixed method lists.
-
- int i;
- for (i = 0; i < count; i++) {
- method_list_t *mlist = lists[i];
- if (!mlist) continue;
-
- // Fixup selectors if necessary
- if (!isMethodListFixedUp(mlist)) {
- mlist = _memdup_internal(mlist, method_list_size(mlist));
- fixupMethodList(mlist, methodsFromBundle);
- }
-
- // Scan for vtable updates
- if (outVtablesAffected && !vtablesAffected) {
- uint32_t m;
- for (m = 0; m < mlist->count; m++) {
- SEL sel = method_list_nth(mlist, m)->name;
- if (vtable_containsSelector(sel)) vtablesAffected = YES;
- }
- }
-
- // Fill method list array
- cls->data->methods[i] = mlist;
- }
-
- if (outVtablesAffected) *outVtablesAffected = vtablesAffected;
-}
-
-static void
-attachCategoryMethods(class_t *cls, category_list *cats,
- BOOL *outVtablesAffected)
-{
- if (!cats) return;
- if (PrintReplacedMethods) printReplacements(cls, cats);
-
- BOOL isMeta = isMetaClass(cls);
- method_list_t **mlists = _malloc_internal(cats->count * sizeof(*mlists));
-
- // Count backwards through cats to get newest categories first
- int mcount = 0;
- int i = cats->count;
- BOOL fromBundle = NO;
- while (i--) {
- method_list_t *mlist = cat_method_list(cats->list[i].cat, isMeta);
- if (mlist) {
- mlists[mcount++] = mlist;
- fromBundle |= cats->list[i].fromBundle;
- }
- }
-
- attachMethodLists(cls, mlists, mcount, fromBundle, outVtablesAffected);
-
- _free_internal(mlists);
-
-}
-
-
-static chained_property_list *
-buildPropertyList(const struct objc_property_list *plist, category_list *cats, BOOL isMeta)
-{
- // Do NOT use cat->cls! It may have been remapped.
- chained_property_list *newlist;
- uint32_t count = 0;
- uint32_t p, c;
-
- // Count properties in all lists.
- if (plist) count = plist->count;
- if (cats) {
- for (c = 0; c < cats->count; c++) {
- category_t *cat = cats->list[c].cat;
- /*
- if (isMeta && cat->classProperties) {
- count += cat->classProperties->count;
- }
- else*/
- if (!isMeta && cat->instanceProperties) {
- count += cat->instanceProperties->count;
- }
- }
- }
-
- if (count == 0) return NULL;
-
- // Allocate new list.
- newlist = _malloc_internal(sizeof(*newlist) + count * sizeof(struct objc_property));
- newlist->count = 0;
- newlist->next = NULL;
-
- // Copy properties; newest categories first, then ordinary properties
- if (cats) {
- c = cats->count;
- while (c--) {
- struct objc_property_list *cplist;
- category_t *cat = cats->list[c].cat;
- /*
- if (isMeta) {
- cplist = cat->classProperties;
- } else */
- {
- cplist = cat->instanceProperties;
- }
- if (cplist) {
- for (p = 0; p < cplist->count; p++) {
- newlist->list[newlist->count++] =
- *property_list_nth(cplist, p);
- }
- }
- }
- }
- if (plist) {
- for (p = 0; p < plist->count; p++) {
- newlist->list[newlist->count++] = *property_list_nth(plist, p);
- }
- }
-
- assert(newlist->count == count);
-
- return newlist;
-}
-
-
-static protocol_list_t **
-buildProtocolList(category_list *cats, struct protocol_list_t *base,
- struct protocol_list_t **protos)
-{
- // Do NOT use cat->cls! It may have been remapped.
- struct protocol_list_t **p, **newp;
- struct protocol_list_t **newprotos;
- int count = 0;
- int i;
-
- // count protocol list in base
- if (base) count++;
-
- // count protocol lists in cats
- if (cats) for (i = 0; i < cats->count; i++) {
- category_t *cat = cats->list[i].cat;
- if (cat->protocols) count++;
- }
-
- // no base or category protocols? return existing protocols unchanged
- if (count == 0) return protos;
-
- // count protocol lists in protos
- for (p = protos; p && *p; p++) {
- count++;
- }
-
- if (count == 0) return NULL;
-
- newprotos = (struct protocol_list_t **)
- _malloc_internal((count+1) * sizeof(struct protocol_list_t *));
- newp = newprotos;
-
- if (base) {
- *newp++ = base;
- }
-
- for (p = protos; p && *p; p++) {
- *newp++ = *p;
- }
-
- if (cats) for (i = 0; i < cats->count; i++) {
- category_t *cat = cats->list[i].cat;
- if (cat->protocols) {
- *newp++ = cat->protocols;
- }
- }
-
- *newp = NULL;
-
- return newprotos;
-}
-
-
-/***********************************************************************
-* methodizeClass
-* Fixes up cls's method list, protocol list, and property list.
-* Attaches any outstanding categories.
-* Builds vtable.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void methodizeClass(struct class_t *cls)
-{
- category_list *cats;
- BOOL isMeta;
-
- rwlock_assert_writing(&runtimeLock);
-
- isMeta = isMetaClass(cls);
-
- // Methodizing for the first time
- if (PrintConnecting) {
- _objc_inform("CLASS: methodizing class '%s' %s",
- getName(cls), isMeta ? "(meta)" : "");
- }
-
- // Build method and protocol and property lists.
- // Include methods and protocols and properties from categories, if any
- // Do NOT use cat->cls! It may have been remapped.
-
- attachMethodLists(cls, (method_list_t **)&cls->data->ro->baseMethods, 1,
- isBundleClass(cls), NULL);
-
- cats = unattachedCategoriesForClass(cls);
- attachCategoryMethods(cls, cats, NULL);
-
- if (cats || cls->data->ro->baseProperties) {
- cls->data->properties =
- buildPropertyList(cls->data->ro->baseProperties, cats, isMeta);
- }
-
- if (cats || cls->data->ro->baseProtocols) {
- cls->data->protocols =
- buildProtocolList(cats, cls->data->ro->baseProtocols, NULL);
- }
-
- if (PrintConnecting) {
- uint32_t i;
- if (cats) {
- for (i = 0; i < cats->count; i++) {
- _objc_inform("CLASS: attached category %c%s(%s)",
- isMeta ? '+' : '-',
- getName(cls), cats->list[i].cat->name);
- }
- }
- }
-
- if (cats) _free_internal(cats);
-
- // No vtable until +initialize completes
- assert(cls->vtable == _objc_empty_vtable);
-}
-
-
-/***********************************************************************
-* remethodizeClass
-* Attach outstanding categories to an existing class.
-* Fixes up cls's method list, protocol list, and property list.
-* Updates method caches and vtables for cls and its subclasses.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void remethodizeClass(struct class_t *cls)
-{
- category_list *cats;
- BOOL isMeta;
-
- rwlock_assert_writing(&runtimeLock);
-
- isMeta = isMetaClass(cls);
-
- // Re-methodizing: check for more categories
- if ((cats = unattachedCategoriesForClass(cls))) {
- chained_property_list *newproperties;
- struct protocol_list_t **newprotos;
- BOOL vtableAffected = NO;
-
- if (PrintConnecting) {
- _objc_inform("CLASS: attaching categories to class '%s' %s",
- getName(cls), isMeta ? "(meta)" : "");
- }
-
- // Update methods, properties, protocols
-
- attachCategoryMethods(cls, cats, &vtableAffected);
-
- newproperties = buildPropertyList(NULL, cats, isMeta);
- if (newproperties) {
- newproperties->next = cls->data->properties;
- cls->data->properties = newproperties;
- }
-
- newprotos = buildProtocolList(cats, NULL, cls->data->protocols);
- if (cls->data->protocols && cls->data->protocols != newprotos) {
- _free_internal(cls->data->protocols);
- }
- cls->data->protocols = newprotos;
-
- _free_internal(cats);
-
- // Update method caches and vtables
- flushCaches(cls);
- if (vtableAffected) flushVtables(cls);
- }
-}
-
-
-/***********************************************************************
-* changeInfo
-* Atomically sets and clears some bits in cls's info field.
-* set and clear must not overlap.
-**********************************************************************/
-static void changeInfo(class_t *cls, unsigned int set, unsigned int clear)
-{
- uint32_t oldf, newf;
-
- assert(isFuture(cls) || isRealized(cls));
-
- do {
- oldf = cls->data->flags;
- newf = (oldf | set) & ~clear;
- } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&cls->data->flags));
-}
-
-
-/***********************************************************************
-* namedClasses
-* Returns the classname => class map of all non-meta classes.
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-
-NXMapTable *gdb_objc_realized_classes; // exported for debuggers in objc-gdb.h
-
-static NXMapTable *namedClasses(void)
-{
- rwlock_assert_locked(&runtimeLock);
-
- INIT_ONCE_PTR(gdb_objc_realized_classes,
- NXCreateMapTableFromZone(NXStrValueMapPrototype, 1024,
- _objc_internal_zone()),
- NXFreeMapTable(v) );
-
- return gdb_objc_realized_classes;
-}
-
-
-/***********************************************************************
-* addNamedClass
-* Adds name => cls to the named non-meta class map.
-* Warns about duplicate class names and keeps the old mapping.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void addNamedClass(class_t *cls, const char *name)
-{
- rwlock_assert_writing(&runtimeLock);
- class_t *old;
- if ((old = NXMapGet(namedClasses(), name))) {
- inform_duplicate(name, (Class)old, (Class)cls);
- } else {
- NXMapInsert(namedClasses(), name, cls);
- }
- assert(!(cls->data->flags & RO_META));
-
- // wrong: constructed classes are already realized when they get here
- // assert(!isRealized(cls));
-}
-
-
-/***********************************************************************
-* removeNamedClass
-* Removes cls from the name => cls map.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void removeNamedClass(class_t *cls, const char *name)
-{
- rwlock_assert_writing(&runtimeLock);
- assert(!(cls->data->flags & RO_META));
- if (cls == NXMapGet(namedClasses(), name)) {
- NXMapRemove(namedClasses(), name);
- } else {
- // cls has a name collision with another class - don't remove the other
- }
-}
-
-
-/***********************************************************************
-* realizedClasses
-* Returns the class list for realized non-meta classes.
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static NXHashTable *realizedClasses(void)
-{
- static NXHashTable *class_hash = NULL;
-
- rwlock_assert_locked(&runtimeLock);
-
- INIT_ONCE_PTR(class_hash,
- NXCreateHashTableFromZone(NXPtrPrototype, 1024, NULL,
- _objc_internal_zone()),
- NXFreeHashTable(v));
-
- return class_hash;
-}
-
-
-/***********************************************************************
-* realizedMetaclasses
-* Returns the class list for realized metaclasses.
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static NXHashTable *realizedMetaclasses(void)
-{
- static NXHashTable *class_hash = NULL;
-
- rwlock_assert_locked(&runtimeLock);
-
- INIT_ONCE_PTR(class_hash,
- NXCreateHashTableFromZone(NXPtrPrototype, 1024, NULL,
- _objc_internal_zone()),
- NXFreeHashTable(v));
-
- return class_hash;
-}
-
-
-/***********************************************************************
-* addRealizedClass
-* Adds cls to the realized non-meta class hash.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void addRealizedClass(class_t *cls)
-{
- rwlock_assert_writing(&runtimeLock);
- void *old;
- old = NXHashInsert(realizedClasses(), cls);
- objc_addRegisteredClass((Class)cls);
- assert(!isMetaClass(cls));
- assert(!old);
-}
-
-
-/***********************************************************************
-* removeRealizedClass
-* Removes cls from the realized non-meta class hash.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void removeRealizedClass(class_t *cls)
-{
- rwlock_assert_writing(&runtimeLock);
- if (isRealized(cls)) {
- assert(!isMetaClass(cls));
- NXHashRemove(realizedClasses(), cls);
- objc_removeRegisteredClass((Class)cls);
- }
-}
-
-
-/***********************************************************************
-* addRealizedMetaclass
-* Adds cls to the realized metaclass hash.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void addRealizedMetaclass(class_t *cls)
-{
- rwlock_assert_writing(&runtimeLock);
- void *old;
- old = NXHashInsert(realizedMetaclasses(), cls);
- assert(isMetaClass(cls));
- assert(!old);
-}
-
-
-/***********************************************************************
-* removeRealizedMetaclass
-* Removes cls from the realized metaclass hash.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void removeRealizedMetaclass(class_t *cls)
-{
- rwlock_assert_writing(&runtimeLock);
- if (isRealized(cls)) {
- assert(isMetaClass(cls));
- NXHashRemove(realizedMetaclasses(), cls);
- }
-}
-
-
-/***********************************************************************
-* uninitializedClasses
-* Returns the metaclass => class map for un-+initialized classes
-* Replaces the 32-bit cls = objc_getName(metacls) during +initialize.
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static NXMapTable *uninitializedClasses(void)
-{
- static NXMapTable *class_map = NULL;
-
- rwlock_assert_locked(&runtimeLock);
-
- INIT_ONCE_PTR(class_map,
- NXCreateMapTableFromZone(NXPtrValueMapPrototype, 1024,
- _objc_internal_zone()),
- NXFreeMapTable(v) );
-
- return class_map;
-}
-
-
-/***********************************************************************
-* addUninitializedClass
-* Adds metacls => cls to the un-+initialized class map
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void addUninitializedClass(class_t *cls, class_t *metacls)
-{
- rwlock_assert_writing(&runtimeLock);
- void *old;
- old = NXMapInsert(uninitializedClasses(), metacls, cls);
- assert(isRealized(metacls) ? isMetaClass(metacls) : metacls->data->flags & RO_META);
- assert(! (isRealized(cls) ? isMetaClass(cls) : cls->data->flags & RO_META));
- assert(!old);
-}
-
-
-static void removeUninitializedClass(class_t *cls)
-{
- rwlock_assert_writing(&runtimeLock);
- NXMapRemove(uninitializedClasses(), cls->isa);
-}
-
-
-/***********************************************************************
-* getNonMetaClass
-* Return the ordinary class for this class or metaclass.
-* Used by +initialize.
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static class_t *getNonMetaClass(class_t *cls)
-{
- rwlock_assert_locked(&runtimeLock);
- if (isMetaClass(cls)) {
- cls = NXMapGet(uninitializedClasses(), cls);
- }
- return cls;
-}
-
-
-/***********************************************************************
-* _class_getNonMetaClass
-* Return the ordinary class for this class or metaclass.
-* Used by +initialize.
-* Locking: acquires runtimeLock
-**********************************************************************/
-__private_extern__ Class _class_getNonMetaClass(Class cls_gen)
-{
- class_t *cls = newcls(cls_gen);
- rwlock_write(&runtimeLock);
- cls = getNonMetaClass(cls);
- realizeClass(cls);
- rwlock_unlock_write(&runtimeLock);
-
- return (Class)cls;
-}
-
-
-
-/***********************************************************************
-* futureClasses
-* Returns the classname => future class map for unrealized future classes.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static NXMapTable *futureClasses(void)
-{
- rwlock_assert_writing(&runtimeLock);
-
- static NXMapTable *future_class_map = NULL;
-
- if (future_class_map) return future_class_map;
-
- // future_class_map is big enough to hold CF's classes and a few others
- future_class_map = NXCreateMapTableFromZone(NXStrValueMapPrototype, 32,
- _objc_internal_zone());
-
- return future_class_map;
-}
-
-
-/***********************************************************************
-* addFutureClass
-* Installs cls as the class structure to use for the named class if it appears.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void addFutureClass(const char *name, class_t *cls)
-{
- void *old;
-
- rwlock_assert_writing(&runtimeLock);
-
- if (PrintFuture) {
- _objc_inform("FUTURE: reserving %p for %s", cls, name);
- }
-
- cls->data = _calloc_internal(sizeof(*cls->data), 1);
- cls->data->flags = RO_FUTURE;
-
- old = NXMapKeyCopyingInsert(futureClasses(), name, cls);
- assert(!old);
-}
-
-
-/***********************************************************************
-* removeFutureClass
-* Removes the named class from the unrealized future class list,
-* because it has been realized.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void removeFutureClass(const char *name)
-{
- rwlock_assert_writing(&runtimeLock);
-
- NXMapKeyFreeingRemove(futureClasses(), name);
-}
-
-
-/***********************************************************************
-* remappedClasses
-* Returns the oldClass => newClass map for realized future classes.
-* Returns the oldClass => NULL map for ignored weak-linked classes.
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static NXMapTable *remappedClasses(BOOL create)
-{
- static NXMapTable *remapped_class_map = NULL;
-
- rwlock_assert_locked(&runtimeLock);
-
- if (remapped_class_map) return remapped_class_map;
- if (!create) return NULL;
-
- // remapped_class_map is big enough to hold CF's classes and a few others
- INIT_ONCE_PTR(remapped_class_map,
- NXCreateMapTableFromZone(NXPtrValueMapPrototype, 32,
- _objc_internal_zone()),
- NXFreeMapTable(v));
-
- return remapped_class_map;
-}
-
-
-/***********************************************************************
-* noClassesRemapped
-* Returns YES if no classes have been remapped
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static BOOL noClassesRemapped(void)
-{
- rwlock_assert_locked(&runtimeLock);
-
- BOOL result = (remappedClasses(NO) == NULL);
- return result;
-}
-
-
-/***********************************************************************
-* addRemappedClass
-* newcls is a realized future class, replacing oldcls.
-* OR newcls is NULL, replacing ignored weak-linked class oldcls.
-* Locking: runtimeLock must be write-locked by the caller
-**********************************************************************/
-static void addRemappedClass(class_t *oldcls, class_t *newcls)
-{
- rwlock_assert_writing(&runtimeLock);
-
- if (PrintFuture) {
- _objc_inform("FUTURE: using %p instead of %p for %s",
- oldcls, newcls, getName(newcls));
- }
-
- void *old;
- old = NXMapInsert(remappedClasses(YES), oldcls, newcls);
- assert(!old);
-}
-
-
-/***********************************************************************
-* remapClass
-* Returns the live class pointer for cls, which may be pointing to
-* a class struct that has been reallocated.
-* Returns NULL if cls is ignored because of weak linking.
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static class_t *remapClass(class_t *cls)
-{
- rwlock_assert_locked(&runtimeLock);
-
- class_t *c2;
-
- if (!cls) return NULL;
-
- if (NXMapMember(remappedClasses(YES), cls, (void**)&c2) == NX_MAPNOTAKEY) {
- return cls;
- } else {
- return c2;
- }
-}
-
-
-/***********************************************************************
-* remapClassRef
-* Fix up a class ref, in case the class referenced has been reallocated
-* or is an ignored weak-linked class.
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static void remapClassRef(class_t **clsref)
-{
- rwlock_assert_locked(&runtimeLock);
-
- class_t *newcls = remapClass(*clsref);
- if (*clsref != newcls) *clsref = newcls;
-}
-
-
-/***********************************************************************
-* addSubclass
-* Adds subcls as a subclass of supercls.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static void addSubclass(class_t *supercls, class_t *subcls)
-{
- rwlock_assert_writing(&runtimeLock);
-
- if (supercls && subcls) {
- assert(isRealized(supercls));
- assert(isRealized(subcls));
- subcls->data->nextSiblingClass = supercls->data->firstSubclass;
- supercls->data->firstSubclass = subcls;
- }
-}
-
-
-/***********************************************************************
-* removeSubclass
-* Removes subcls as a subclass of supercls.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static void removeSubclass(class_t *supercls, class_t *subcls)
-{
- rwlock_assert_writing(&runtimeLock);
- assert(getSuperclass(subcls) == supercls);
-
- class_t **cp;
- for (cp = &supercls->data->firstSubclass;
- *cp && *cp != subcls;
- cp = &(*cp)->data->nextSiblingClass)
- ;
- assert(*cp == subcls);
- *cp = subcls->data->nextSiblingClass;
-}
-
-
-
-/***********************************************************************
-* protocols
-* Returns the protocol name => protocol map for protocols.
-* Locking: runtimeLock must read- or write-locked by the caller
-**********************************************************************/
-static NXMapTable *protocols(void)
-{
- static NXMapTable *protocol_map = NULL;
-
- rwlock_assert_locked(&runtimeLock);
-
- INIT_ONCE_PTR(protocol_map,
- NXCreateMapTableFromZone(NXStrValueMapPrototype, 16,
- _objc_internal_zone()),
- NXFreeMapTable(v) );
-
- return protocol_map;
-}
-
-
-/***********************************************************************
-* remapProtocol
-* Returns the live protocol pointer for proto, which may be pointing to
-* a protocol struct that has been reallocated.
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static protocol_t *remapProtocol(protocol_ref_t proto)
-{
- rwlock_assert_locked(&runtimeLock);
-
- protocol_t *newproto = NXMapGet(protocols(), ((protocol_t *)proto)->name);
- return newproto ? newproto : (protocol_t *)proto;
-}
-
-
-/***********************************************************************
-* remapProtocolRef
-* Fix up a protocol ref, in case the protocol referenced has been reallocated.
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static void remapProtocolRef(protocol_t **protoref)
-{
- rwlock_assert_locked(&runtimeLock);
-
- protocol_t *newproto = remapProtocol((protocol_ref_t)*protoref);
- if (*protoref != newproto) *protoref = newproto;
-}
-
-
-/***********************************************************************
-* moveIvars
-* Slides a class's ivars to accommodate the given superclass size.
-* Also slides ivar and weak GC layouts if provided.
-* Ivars are NOT compacted to compensate for a superclass that shrunk.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static void moveIvars(class_ro_t *ro, uint32_t superSize,
- layout_bitmap *ivarBitmap, layout_bitmap *weakBitmap)
-{
- rwlock_assert_writing(&runtimeLock);
-
- uint32_t diff;
- uint32_t i;
-
- assert(superSize > ro->instanceStart);
- diff = superSize - ro->instanceStart;
-
- if (ro->ivars) {
- // Find maximum alignment in this class's ivars
- uint32_t maxAlignment = 1;
- for (i = 0; i < ro->ivars->count; i++) {
- ivar_t *ivar = ivar_list_nth(ro->ivars, i);
- if (!ivar->offset) continue; // anonymous bitfield
-
- uint32_t alignment = ivar_alignment(ivar);
- if (alignment > maxAlignment) maxAlignment = alignment;
- }
-
- // Compute a slide value that preserves that alignment
- uint32_t alignMask = maxAlignment - 1;
- if (diff & alignMask) diff = (diff + alignMask) & ~alignMask;
-
- // Slide all of this class's ivars en masse
- for (i = 0; i < ro->ivars->count; i++) {
- ivar_t *ivar = ivar_list_nth(ro->ivars, i);
- if (!ivar->offset) continue; // anonymous bitfield
-
- uint32_t oldOffset = (uint32_t)*ivar->offset;
- uint32_t newOffset = oldOffset + diff;
- *ivar->offset = newOffset;
-
- if (PrintIvars) {
- _objc_inform("IVARS: offset %u -> %u for %s (size %u, align %u)",
- oldOffset, newOffset, ivar->name,
- ivar->size, ivar_alignment(ivar));
- }
- }
-
- // Slide GC layouts
- uint32_t oldOffset = ro->instanceStart;
- uint32_t newOffset = ro->instanceStart + diff;
-
- if (ivarBitmap) {
- layout_bitmap_slide(ivarBitmap,
- oldOffset >> WORD_SHIFT,
- newOffset >> WORD_SHIFT);
- }
- if (weakBitmap) {
- layout_bitmap_slide(weakBitmap,
- oldOffset >> WORD_SHIFT,
- newOffset >> WORD_SHIFT);
- }
- }
-
- *(uint32_t *)&ro->instanceStart += diff;
- *(uint32_t *)&ro->instanceSize += diff;
-
- if (!ro->ivars) {
- // No ivars slid, but superclass changed size.
- // Expand bitmap in preparation for layout_bitmap_splat().
- if (ivarBitmap) layout_bitmap_grow(ivarBitmap, ro->instanceSize >> WORD_SHIFT);
- if (weakBitmap) layout_bitmap_grow(weakBitmap, ro->instanceSize >> WORD_SHIFT);
- }
-}
-
-
-/***********************************************************************
-* getIvar
-* Look up an ivar by name.
-* Locking: runtimeLock must be read- or write-locked by the caller.
-**********************************************************************/
-static ivar_t *getIvar(class_t *cls, const char *name)
-{
- rwlock_assert_locked(&runtimeLock);
-
- const ivar_list_t *ivars;
- assert(isRealized(cls));
- if ((ivars = cls->data->ro->ivars)) {
- uint32_t i;
- for (i = 0; i < ivars->count; i++) {
- struct ivar_t *ivar = ivar_list_nth(ivars, i);
- if (!ivar->offset) continue; // anonymous bitfield
-
- // ivar->name may be NULL for anonymous bitfields etc.
- if (ivar->name && 0 == strcmp(name, ivar->name)) {
- return ivar;
- }
- }
- }
-
- return NULL;
-}
-
-
-/***********************************************************************
-* realizeClass
-* Performs first-time initialization on class cls,
-* including allocating its read-write data.
-* Returns the real class structure for the class.
-* Locking: runtimeLock must be write-locked by the caller
-**********************************************************************/
-static class_t *realizeClass(class_t *cls)
-{
- rwlock_assert_writing(&runtimeLock);
-
- const class_ro_t *ro;
- class_rw_t *rw;
- class_t *supercls;
- class_t *metacls;
- BOOL isMeta;
-
- if (!cls) return NULL;
- if (isRealized(cls)) return cls;
- assert(cls == remapClass(cls));
-
- ro = (const class_ro_t *)cls->data;
- if (ro->flags & RO_FUTURE) {
- // This was a future class. rw data is already allocated.
- rw = cls->data;
- ro = cls->data->ro;
- changeInfo(cls, RW_REALIZED, RW_FUTURE);
- } else {
- // Normal class. Allocate writeable class data.
- rw = _calloc_internal(sizeof(class_rw_t), 1);
- rw->ro = ro;
- rw->flags = RW_REALIZED;
- cls->data = rw;
- }
-
- isMeta = (ro->flags & RO_META) ? YES : NO;
-
- rw->version = isMeta ? 7 : 0; // old runtime went up to 6
-
- if (PrintConnecting) {
- _objc_inform("CLASS: realizing class '%s' %s %p %p",
- ro->name, isMeta ? "(meta)" : "", cls, ro);
- }
-
- // Realize superclass and metaclass, if they aren't already.
- // This needs to be done after RW_REALIZED is set above, for root classes.
- supercls = realizeClass(remapClass(cls->superclass));
- metacls = realizeClass(remapClass(cls->isa));
-
- // Check for remapped superclass
- // fixme doesn't handle remapped metaclass
- assert(metacls == cls->isa);
- if (supercls != cls->superclass) {
- cls->superclass = supercls;
- }
-
- /* debug: print them all
- if (ro->ivars) {
- uint32_t i;
- for (i = 0; i < ro->ivars->count; i++) {
- ivar_t *ivar = ivar_list_nth(ro->ivars, i);
- if (!ivar->offset) continue; // anonymous bitfield
-
- _objc_inform("IVARS: %s.%s (offset %u, size %u, align %u)",
- ro->name, ivar->name,
- *ivar->offset, ivar->size, ivar_alignment(ivar));
- }
- }
- */
-
-
- if (supercls) {
- // Non-fragile ivars - reconcile this class with its superclass
- layout_bitmap ivarBitmap;
- layout_bitmap weakBitmap;
- BOOL layoutsChanged = NO;
-
- if (UseGC) {
- // fixme can optimize for "class has no new ivars", etc
- // WARNING: gcc c++ sets instanceStart/Size=0 for classes with
- // no local ivars, but does provide a layout bitmap.
- // Handle that case specially so layout_bitmap_create doesn't die
- // The other ivar sliding code below still works fine, and
- // the final result is a good class.
- if (ro->instanceStart == 0 && ro->instanceSize == 0) {
- // We can't use ro->ivarLayout because we don't know
- // how long it is. Force a new layout to be created.
- if (PrintIvars) {
- _objc_inform("IVARS: instanceStart/Size==0 for class %s; "
- "disregarding ivar layout", ro->name);
- }
- ivarBitmap =
- layout_bitmap_create(NULL,
- supercls->data->ro->instanceSize,
- supercls->data->ro->instanceSize, NO);
- weakBitmap =
- layout_bitmap_create(NULL,
- supercls->data->ro->instanceSize,
- supercls->data->ro->instanceSize, YES);
- layoutsChanged = YES;
- } else {
- ivarBitmap =
- layout_bitmap_create(ro->ivarLayout,
- ro->instanceSize,
- ro->instanceSize, NO);
- weakBitmap =
- layout_bitmap_create(ro->weakIvarLayout,
- ro->instanceSize,
- ro->instanceSize, YES);
- }
- }
-
- if (ro->instanceStart < supercls->data->ro->instanceSize) {
- // Superclass has changed size. This class's ivars must move.
- // Also slide layout bits in parallel.
- // This code is incapable of compacting the subclass to
- // compensate for a superclass that shrunk, so don't do that.
- if (PrintIvars) {
- _objc_inform("IVARS: sliding ivars for class %s "
- "(superclass was %u bytes, now %u)",
- ro->name, ro->instanceStart,
- supercls->data->ro->instanceSize);
- }
- class_ro_t *ro_w = make_ro_writeable(rw);
- ro = rw->ro;
- moveIvars(ro_w, supercls->data->ro->instanceSize,
- UseGC ? &ivarBitmap : NULL, UseGC ? &weakBitmap : NULL);
- gdb_objc_class_changed((Class)cls, OBJC_CLASS_IVARS_CHANGED, ro->name);
- layoutsChanged = YES;
- }
-
- if (UseGC) {
- // Check superclass's layout against this class's layout.
- // This needs to be done even if the superclass is not bigger.
- layout_bitmap superBitmap =
- layout_bitmap_create(supercls->data->ro->ivarLayout,
- supercls->data->ro->instanceSize,
- supercls->data->ro->instanceSize, NO);
- layoutsChanged |= layout_bitmap_splat(ivarBitmap, superBitmap,
- ro->instanceStart);
- layout_bitmap_free(superBitmap);
-
- superBitmap =
- layout_bitmap_create(supercls->data->ro->weakIvarLayout,
- supercls->data->ro->instanceSize,
- supercls->data->ro->instanceSize, YES);
- layoutsChanged |= layout_bitmap_splat(weakBitmap, superBitmap,
- ro->instanceStart);
- layout_bitmap_free(superBitmap);
-
- if (layoutsChanged) {
- // Rebuild layout strings.
- if (PrintIvars) {
- _objc_inform("IVARS: gc layout changed for class %s",
- ro->name);
- }
- class_ro_t *ro_w = make_ro_writeable(rw);
- ro = rw->ro;
- ro_w->ivarLayout = layout_string_create(ivarBitmap);
- ro_w->weakIvarLayout = layout_string_create(weakBitmap);
- }
-
- layout_bitmap_free(ivarBitmap);
- layout_bitmap_free(weakBitmap);
- }
- }
-
- // Connect this class to its superclass's subclass lists
- if (supercls) {
- addSubclass(supercls, cls);
- }
-
- // Attach categories
- methodizeClass(cls);
-
- if (!isMeta) {
- addRealizedClass(cls);
- } else {
- addRealizedMetaclass(cls);
- }
-
- return cls;
-}
-
-
-/***********************************************************************
-* getClass
-* Looks up a class by name. The class MIGHT NOT be realized.
-* Locking: runtimeLock must be read- or write-locked by the caller.
-**********************************************************************/
-static class_t *getClass(const char *name)
-{
- rwlock_assert_locked(&runtimeLock);
-
- return (class_t *)NXMapGet(namedClasses(), name);
-}
-
-
-/***********************************************************************
-* missingWeakSuperclass
-* Return YES if some superclass of cls was weak-linked and is missing.
-**********************************************************************/
-static BOOL
-missingWeakSuperclass(class_t *cls)
-{
- assert(!isRealized(cls));
-
- if (!cls->superclass) {
- // superclass NULL. This is normal for root classes only.
- return (!(cls->data->flags & RO_ROOT));
- } else {
- // superclass not NULL. Check if a higher superclass is missing.
- class_t *supercls = remapClass(cls->superclass);
- if (!supercls) return YES;
- if (isRealized(supercls)) return NO;
- return missingWeakSuperclass(supercls);
- }
-}
-
-
-/***********************************************************************
-* realizeAllClassesInImage
-* Non-lazily realizes all unrealized classes in the given image.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static void realizeAllClassesInImage(header_info *hi)
-{
- rwlock_assert_writing(&runtimeLock);
-
- size_t count, i;
- class_t **classlist;
-
- if (hi->allClassesRealized) return;
-
- classlist = _getObjc2ClassList(hi, &count);
-
- for (i = 0; i < count; i++) {
- realizeClass(remapClass(classlist[i]));
- }
-
- hi->allClassesRealized = YES;
-}
-
-
-/***********************************************************************
-* realizeAllClasses
-* Non-lazily realizes all unrealized classes in all known images.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static void realizeAllClasses(void)
-{
- rwlock_assert_writing(&runtimeLock);
-
- header_info *hi;
- for (hi = FirstHeader; hi; hi = hi->next) {
- realizeAllClassesInImage(hi);
- }
-}
-
-
-/***********************************************************************
-* _objc_allocateFutureClass
-* Allocate an unresolved future class for the given class name.
-* Returns any existing allocation if one was already made.
-* Assumes the named class doesn't exist yet.
-* Locking: acquires runtimeLock
-**********************************************************************/
-__private_extern__ Class _objc_allocateFutureClass(const char *name)
-{
- rwlock_write(&runtimeLock);
-
- struct class_t *cls;
- NXMapTable *future_class_map = futureClasses();
-
- if ((cls = NXMapGet(future_class_map, name))) {
- // Already have a future class for this name.
- rwlock_unlock_write(&runtimeLock);
- return (Class)cls;
- }
-
- cls = (class_t *)_calloc_class(sizeof(*cls));
- addFutureClass(name, cls);
-
- rwlock_unlock_write(&runtimeLock);
- return (Class)cls;
-}
-
-
-/***********************************************************************
-*
-**********************************************************************/
-void objc_setFutureClass(Class cls, const char *name)
-{
- // fixme hack do nothing - NSCFString handled specially elsewhere
-}
-
-
-#define FOREACH_REALIZED_SUBCLASS(_c, _cls, code) \
- do { \
- rwlock_assert_writing(&runtimeLock); \
- class_t *_top = _cls; \
- class_t *_c = _top; \
- if (_c) { \
- while (1) { \
- code \
- if (_c->data->firstSubclass) { \
- _c = _c->data->firstSubclass; \
- } else { \
- while (!_c->data->nextSiblingClass && _c != _top) { \
- _c = getSuperclass(_c); \
- } \
- if (_c == _top) break; \
- _c = _c->data->nextSiblingClass; \
- } \
- } \
- } else { \
- /* nil means all realized classes */ \
- NXHashTable *_classes = realizedClasses(); \
- NXHashTable *_metaclasses = realizedMetaclasses(); \
- NXHashState _state; \
- _state = NXInitHashState(_classes); \
- while (NXNextHashState(_classes, &_state, (void**)&_c)) \
- { \
- code \
- } \
- _state = NXInitHashState(_metaclasses); \
- while (NXNextHashState(_metaclasses, &_state, (void**)&_c)) \
- { \
- code \
- } \
- } \
- } while (0)
-
-
-/***********************************************************************
-* flushVtables
-* Rebuilds vtables for cls and its realized subclasses.
-* If cls is Nil, all realized classes and metaclasses are touched.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static void flushVtables(class_t *cls)
-{
- rwlock_assert_writing(&runtimeLock);
-
- if (PrintVtables && !cls) {
- _objc_inform("VTABLES: ### EXPENSIVE ### global vtable flush!");
- }
-
- FOREACH_REALIZED_SUBCLASS(c, cls, {
- updateVtable(c, NO);
- });
-}
-
-
-/***********************************************************************
-* flushCaches
-* Flushes caches for cls and its realized subclasses.
-* Does not update vtables.
-* If cls is Nil, all realized and metaclasses classes are touched.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static void flushCaches(class_t *cls)
-{
- rwlock_assert_writing(&runtimeLock);
-
- FOREACH_REALIZED_SUBCLASS(c, cls, {
- flush_cache((Class)c);
- });
-}
-
-
-/***********************************************************************
-* flush_caches
-* Flushes caches and rebuilds vtables for cls, its subclasses,
-* and optionally its metaclass.
-* Locking: acquires runtimeLock
-**********************************************************************/
-__private_extern__ void flush_caches(Class cls_gen, BOOL flush_meta)
-{
- class_t *cls = newcls(cls_gen);
- rwlock_write(&runtimeLock);
- // fixme optimize vtable flushing? (only needed for vtable'd selectors)
- flushCaches(cls);
- flushVtables(cls);
- // don't flush root class's metaclass twice (it's a subclass of the root)
- if (flush_meta && getSuperclass(cls)) {
- flushCaches(cls->isa);
- flushVtables(cls->isa);
- }
- rwlock_unlock_write(&runtimeLock);
-}
-
-
-/***********************************************************************
-* map_images
-* Process the given images which are being mapped in by dyld.
-* Calls ABI-agnostic code after taking ABI-specific locks.
-*
-* Locking: write-locks runtimeLock
-**********************************************************************/
-__private_extern__ const char *
-map_images(enum dyld_image_states state, uint32_t infoCount,
- const struct dyld_image_info infoList[])
-{
- const char *err;
-
- rwlock_write(&runtimeLock);
- err = map_images_nolock(state, infoCount, infoList);
- rwlock_unlock_write(&runtimeLock);
- return err;
-}
-
-
-/***********************************************************************
-* load_images
-* Process +load in the given images which are being mapped in by dyld.
-* Calls ABI-agnostic code after taking ABI-specific locks.
-*
-* Locking: write-locks runtimeLock and loadMethodLock
-**********************************************************************/
-__private_extern__ const char *
-load_images(enum dyld_image_states state, uint32_t infoCount,
- const struct dyld_image_info infoList[])
-{
- BOOL found;
-
- recursive_mutex_lock(&loadMethodLock);
-
- // Discover load methods
- rwlock_write(&runtimeLock);
- found = load_images_nolock(state, infoCount, infoList);
- rwlock_unlock_write(&runtimeLock);
-
- // Call +load methods (without runtimeLock - re-entrant)
- if (found) {
- call_load_methods();
- }
-
- recursive_mutex_unlock(&loadMethodLock);
-
- return NULL;
-}
-
-
-/***********************************************************************
-* unmap_image
-* Process the given image which is about to be unmapped by dyld.
-* mh is mach_header instead of headerType because that's what
-* dyld_priv.h says even for 64-bit.
-*
-* Locking: write-locks runtimeLock and loadMethodLock
-**********************************************************************/
-__private_extern__ void
-unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide)
-{
- recursive_mutex_lock(&loadMethodLock);
- rwlock_write(&runtimeLock);
-
- unmap_image_nolock(mh, vmaddr_slide);
-
- rwlock_unlock_write(&runtimeLock);
- recursive_mutex_unlock(&loadMethodLock);
-}
-
-
-/***********************************************************************
-* _read_images
-* Perform initial processing of the headers in the linked
-* list beginning with headerList.
-*
-* Called by: map_images_nolock
-*
-* Locking: runtimeLock acquired by map_images
-**********************************************************************/
-__private_extern__ void _read_images(header_info **hList, uint32_t hCount)
-{
- header_info *hi;
- uint32_t hIndex;
- size_t count;
- size_t i;
- class_t **resolvedFutureClasses = NULL;
- size_t resolvedFutureClassCount = 0;
- static BOOL doneOnce;
-
- rwlock_assert_writing(&runtimeLock);
-
- if (!doneOnce) {
- initVtables();
- doneOnce = YES;
- }
-
-#define EACH_HEADER \
- hIndex = 0; \
- hIndex < hCount && (hi = hList[hIndex]); \
- hIndex++
-
- // Complain about images that contain old-ABI data
- // fixme new-ABI compiler still emits some bits into __OBJC segment
- for (EACH_HEADER) {
- size_t count;
- if (_getObjcSelectorRefs(hi, &count) || _getObjcModules(hi, &count)) {
- _objc_inform("found old-ABI metadata in image %s !",
- hi->os.dl_info.dli_fname);
- }
- }
-
- // fixme hack
- static BOOL hackedNSCFString = NO;
- if (!hackedNSCFString) {
- // Insert future class __CFConstantStringClassReference == NSCFString
- void *dlh = dlopen("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", RTLD_LAZY | RTLD_NOLOAD | RTLD_FIRST);
- if (dlh) {
- void *addr = dlsym(dlh, "__CFConstantStringClassReference");
- if (addr) {
- addFutureClass("NSCFString", (class_t *)addr);
- hackedNSCFString = YES;
- }
- dlclose(dlh);
- }
- }
-
- // Discover classes. Fix up unresolved future classes. Mark bundle classes.
- NXMapTable *future_class_map = futureClasses();
- for (EACH_HEADER) {
- class_t **classlist = _getObjc2ClassList(hi, &count);
- for (i = 0; i < count; i++) {
- const char *name = getName(classlist[i]);
-
- if (missingWeakSuperclass(classlist[i])) {
- // No superclass (probably weak-linked).
- // Disavow any knowledge of this subclass.
- if (PrintConnecting) {
- _objc_inform("CLASS: IGNORING class '%s' with "
- "missing weak-linked superclass", name);
- }
- addRemappedClass(classlist[i], NULL);
- classlist[i]->superclass = NULL;
- classlist[i] = NULL;
- continue;
- }
-
- if (NXCountMapTable(future_class_map) > 0) {
- class_t *newCls = NXMapGet(future_class_map, name);
- if (newCls) {
- // Copy class_t to future class's struct.
- // Preserve future's rw data block.
- class_rw_t *rw = newCls->data;
- memcpy(newCls, classlist[i], sizeof(class_t));
- rw->ro = (class_ro_t *)newCls->data;
- newCls->data = rw;
-
- removeFutureClass(name);
- addRemappedClass(classlist[i], newCls);
- classlist[i] = newCls;
- // Non-lazily realize the class below.
- resolvedFutureClasses = (class_t **)
- _realloc_internal(resolvedFutureClasses,
- (resolvedFutureClassCount+1)
- * sizeof(class_t *));
- resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
- }
- }
- addNamedClass(classlist[i], name);
- addUninitializedClass(classlist[i], classlist[i]->isa);
- if (hi->mhdr->filetype == MH_BUNDLE) {
- classlist[i]->data->flags |= RO_FROM_BUNDLE;
- classlist[i]->isa->data->flags |= RO_FROM_BUNDLE;
- }
- }
- }
-
- // Fix up remapped classes
- // classlist is up to date, but classrefs may not be
-
- if (!noClassesRemapped()) {
- for (EACH_HEADER) {
- class_t **classrefs = _getObjc2ClassRefs(hi, &count);
- for (i = 0; i < count; i++) {
- remapClassRef(&classrefs[i]);
- }
- // fixme why doesn't test future1 catch the absence of this?
- classrefs = _getObjc2SuperRefs(hi, &count);
- for (i = 0; i < count; i++) {
- remapClassRef(&classrefs[i]);
- }
- }
- }
-
-
- // Fix up @selector references
- sel_lock();
- for (EACH_HEADER) {
- if (PrintPreopt) {
- if (sel_preoptimizationValid(hi)) {
- _objc_inform("PREOPTIMIZATION: honoring preoptimized selectors in %s",
- _nameForHeader(hi->mhdr));
- }
- else if (_objcHeaderOptimizedByDyld(hi)) {
- _objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors in %s",
- _nameForHeader(hi->mhdr));
- }
- }
-
- if (sel_preoptimizationValid(hi)) continue;
-
- SEL *sels = _getObjc2SelectorRefs(hi, &count);
- BOOL isBundle = hi->mhdr->filetype == MH_BUNDLE;
- for (i = 0; i < count; i++) {
- sels[i] = sel_registerNameNoLock((const char *)sels[i], isBundle);
- }
- }
- sel_unlock();
-
- // Discover protocols. Fix up protocol refs.
- NXMapTable *protocol_map = protocols();
- for (EACH_HEADER) {
- extern struct class_t OBJC_CLASS_$_Protocol;
- Class cls = (Class)&OBJC_CLASS_$_Protocol;
- assert(cls);
- protocol_t **protocols = _getObjc2ProtocolList(hi, &count);
- // fixme duplicate protocol from bundle
- for (i = 0; i < count; i++) {
- if (!NXMapGet(protocol_map, protocols[i]->name)) {
- protocols[i]->isa = cls;
- NXMapKeyCopyingInsert(protocol_map,
- protocols[i]->name, protocols[i]);
- if (PrintProtocols) {
- _objc_inform("PROTOCOLS: protocol at %p is %s",
- protocols[i], protocols[i]->name);
- }
- } else {
- if (PrintProtocols) {
- _objc_inform("PROTOCOLS: protocol at %p is %s (duplicate)",
- protocols[i], protocols[i]->name);
- }
- }
- }
- }
- for (EACH_HEADER) {
- protocol_t **protocols;
- protocols = _getObjc2ProtocolRefs(hi, &count);
- for (i = 0; i < count; i++) {
- remapProtocolRef(&protocols[i]);
- }
- }
-
- // Realize non-lazy classes (for +load methods and static instances)
- for (EACH_HEADER) {
- class_t **classlist =
- _getObjc2NonlazyClassList(hi, &count);
- for (i = 0; i < count; i++) {
- realizeClass(remapClass(classlist[i]));
- }
- }
-
- // Realize newly-resolved future classes, in case CF manipulates them
- if (resolvedFutureClasses) {
- for (i = 0; i < resolvedFutureClassCount; i++) {
- realizeClass(resolvedFutureClasses[i]);
- }
- _free_internal(resolvedFutureClasses);
- }
-
- // Discover categories.
- for (EACH_HEADER) {
- category_t **catlist =
- _getObjc2CategoryList(hi, &count);
- for (i = 0; i < count; i++) {
- category_t *cat = catlist[i];
- // Do NOT use cat->cls! It may have been remapped.
- class_t *cls = remapClass(cat->cls);
-
- if (!cls) {
- // Category's target class is missing (probably weak-linked).
- // Disavow any knowledge of this category.
- catlist[i] = NULL;
- if (PrintConnecting) {
- _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
- "missing weak-linked target class",
- cat->name, cat);
- }
- continue;
- }
-
- // Process this category.
- // First, register the category with its target class.
- // Then, rebuild the class's method lists (etc) if
- // the class is realized.
- BOOL classExists = NO;
- if (cat->instanceMethods || cat->protocols
- || cat->instanceProperties)
- {
- addUnattachedCategoryForClass(cat, cls, hi);
- if (isRealized(cls)) {
- remethodizeClass(cls);
- classExists = YES;
- }
- if (PrintConnecting) {
- _objc_inform("CLASS: found category -%s(%s) %s",
- getName(cls), cat->name,
- classExists ? "on existing class" : "");
- }
- }
-
- if (cat->classMethods || cat->protocols
- /* || cat->classProperties */)
- {
- addUnattachedCategoryForClass(cat, cls->isa, hi);
- if (isRealized(cls->isa)) {
- remethodizeClass(cls->isa);
- }
- if (PrintConnecting) {
- _objc_inform("CLASS: found category +%s(%s)",
- getName(cls), cat->name);
- }
- }
- }
- }
-
- // Category discovery MUST BE LAST to avoid potential races
- // when other threads call the new category code before
- // this thread finishes its fixups.
-
- // +load handled by prepare_load_methods()
-
-#undef EACH_HEADER
-}
-
-
-/***********************************************************************
-* prepare_load_methods
-* Schedule +load for classes in this image, any un-+load-ed
-* superclasses in other images, and any categories in this image.
-**********************************************************************/
-// Recursively schedule +load for cls and any un-+load-ed superclasses.
-// cls must already be connected.
-static void schedule_class_load(class_t *cls)
-{
- if (!cls) return;
- assert(isRealized(cls)); // _read_images should realize
-
- if (cls->data->flags & RW_LOADED) return;
-
- // Ensure superclass-first ordering
- schedule_class_load(getSuperclass(cls));
-
- add_class_to_loadable_list((Class)cls);
- changeInfo(cls, RW_LOADED, 0);
-}
-
-__private_extern__ void prepare_load_methods(header_info *hi)
-{
- size_t count, i;
-
- rwlock_assert_writing(&runtimeLock);
-
- class_t **classlist =
- _getObjc2NonlazyClassList(hi, &count);
- for (i = 0; i < count; i++) {
- schedule_class_load(remapClass(classlist[i]));
- }
-
- category_t **categorylist = _getObjc2NonlazyCategoryList(hi, &count);
- for (i = 0; i < count; i++) {
- category_t *cat = categorylist[i];
- // Do NOT use cat->cls! It may have been remapped.
- class_t *cls = remapClass(cat->cls);
- if (!cls) continue; // category for ignored weak-linked class
- realizeClass(cls);
- assert(isRealized(cls->isa));
- add_category_to_loadable_list((Category)cat);
- }
-}
-
-
-/***********************************************************************
-* _unload_image
-* Only handles MH_BUNDLE for now.
-* Locking: write-lock and loadMethodLock acquired by unmap_image
-**********************************************************************/
-__private_extern__ void _unload_image(header_info *hi)
-{
- size_t count, i;
-
- recursive_mutex_assert_locked(&loadMethodLock);
- rwlock_assert_writing(&runtimeLock);
-
- // Unload unattached categories and categories waiting for +load.
-
- category_t **catlist = _getObjc2CategoryList(hi, &count);
- for (i = 0; i < count; i++) {
- category_t *cat = catlist[i];
- if (!cat) continue; // category for ignored weak-linked class
- class_t *cls = remapClass(cat->cls);
- assert(cls); // shouldn't have live category for dead class
-
- // fixme for MH_DYLIB cat's class may have been unloaded already
-
- // unattached list
- removeUnattachedCategoryForClass(cat, cls);
-
- // +load queue
- remove_category_from_loadable_list((Category)cat);
- }
-
- // Unload classes.
-
- class_t **classlist = _getObjc2ClassList(hi, &count);
- for (i = 0; i < count; i++) {
- class_t *cls = classlist[i];
- // fixme remapped classes?
- // fixme ignored weak-linked classes
- if (cls) {
- remove_class_from_loadable_list((Class)cls);
- unload_class(cls->isa, YES);
- unload_class(cls, NO);
- }
- }
-
- // Clean up protocols.
-#warning fixme protocol unload
-
- // fixme DebugUnload
-}
-
-
-/***********************************************************************
-* method_getDescription
-* Returns a pointer to this method's objc_method_description.
-* Locking: none
-**********************************************************************/
-struct objc_method_description *
-method_getDescription(Method m)
-{
- if (!m) return NULL;
- return (struct objc_method_description *)newmethod(m);
-}
-
-
-/***********************************************************************
-* method_getImplementation
-* Returns this method's IMP.
-* Locking: none
-**********************************************************************/
-static IMP
-_method_getImplementation(method_t *m)
-{
- if (!m) return NULL;
- return m->imp;
-}
-
-IMP
-method_getImplementation(Method m)
-{
- return _method_getImplementation(newmethod(m));
-}
-
-
-/***********************************************************************
-* method_getName
-* Returns this method's selector.
-* The method must not be NULL.
-* The method must already have been fixed-up.
-* Locking: none
-**********************************************************************/
-SEL
-method_getName(Method m_gen)
-{
- struct method_t *m = newmethod(m_gen);
- if (!m) return NULL;
-
- assert((SEL)m->name == sel_registerName((char *)m->name));
- return (SEL)m->name;
-}
-
-
-/***********************************************************************
-* method_getTypeEncoding
-* Returns this method's old-style type encoding string.
-* The method must not be NULL.
-* Locking: none
-**********************************************************************/
-const char *
-method_getTypeEncoding(Method m)
-{
- if (!m) return NULL;
- return newmethod(m)->types;
-}
-
-
-/***********************************************************************
-* method_setImplementation
-* Sets this method's implementation to imp.
-* The previous implementation is returned.
-**********************************************************************/
-static IMP
-_method_setImplementation(class_t *cls, method_t *m, IMP imp)
-{
- rwlock_assert_writing(&runtimeLock);
-
- if (!m) return NULL;
- if (!imp) return NULL;
-
- if (m->name == (SEL)kIgnore) {
- // Ignored methods stay ignored
- return m->imp;
- }
-
- IMP old = _method_getImplementation(m);
- m->imp = imp;
-
- // No cache flushing needed - cache contains Methods not IMPs.
-
- if (vtable_containsSelector(newmethod(m)->name)) {
- // Will be slow if cls is NULL (i.e. unknown)
- // fixme build list of classes whose Methods are known externally?
- flushVtables(cls);
- }
-
- // fixme update monomorphism if necessary
-
- return old;
-}
-
-IMP
-method_setImplementation(Method m, IMP imp)
-{
- // Don't know the class - will be slow if vtables are affected
- // fixme build list of classes whose Methods are known externally?
- IMP result;
- rwlock_write(&runtimeLock);
- result = _method_setImplementation(Nil, newmethod(m), imp);
- rwlock_unlock_write(&runtimeLock);
- return result;
-}
-
-
-void method_exchangeImplementations(Method m1_gen, Method m2_gen)
-{
- method_t *m1 = newmethod(m1_gen);
- method_t *m2 = newmethod(m2_gen);
- if (!m1 || !m2) return;
-
- rwlock_write(&runtimeLock);
-
- if (m1->name == (SEL)kIgnore || m2->name == (SEL)kIgnore) {
- // Ignored methods stay ignored. Now they're both ignored.
- m1->imp = (IMP)&_objc_ignored_method;
- m2->imp = (IMP)&_objc_ignored_method;
- rwlock_unlock_write(&runtimeLock);
- return;
- }
-
- IMP m1_imp = m1->imp;
- m1->imp = m2->imp;
- m2->imp = m1_imp;
-
- if (vtable_containsSelector(m1->name) ||
- vtable_containsSelector(m2->name))
- {
- // Don't know the class - will be slow if vtables are affected
- // fixme build list of classes whose Methods are known externally?
- flushVtables(NULL);
- }
-
- // fixme update monomorphism if necessary
-
- rwlock_unlock_write(&runtimeLock);
-}
-
-
-/***********************************************************************
-* ivar_getOffset
-* fixme
-* Locking: none
-**********************************************************************/
-ptrdiff_t
-ivar_getOffset(Ivar ivar)
-{
- if (!ivar) return 0;
- return *newivar(ivar)->offset;
-}
-
-
-/***********************************************************************
-* ivar_getName
-* fixme
-* Locking: none
-**********************************************************************/
-const char *
-ivar_getName(Ivar ivar)
-{
- if (!ivar) return NULL;
- return newivar(ivar)->name;
-}
-
-
-/***********************************************************************
-* ivar_getTypeEncoding
-* fixme
-* Locking: none
-**********************************************************************/
-const char *
-ivar_getTypeEncoding(Ivar ivar)
-{
- if (!ivar) return NULL;
- return newivar(ivar)->type;
-}
-
-
-/***********************************************************************
-* _protocol_getMethod_nolock
-* Locking: runtimeLock must be write-locked by the caller
-**********************************************************************/
-static Method
-_protocol_getMethod_nolock(protocol_t *proto, SEL sel,
- BOOL isRequiredMethod, BOOL isInstanceMethod)
-{
- rwlock_assert_writing(&runtimeLock);
-
- uint32_t i;
- if (!proto || !sel) return NULL;
-
- method_list_t **mlistp = NULL;
-
- if (isRequiredMethod) {
- if (isInstanceMethod) {
- mlistp = &proto->instanceMethods;
- } else {
- mlistp = &proto->classMethods;
- }
- } else {
- if (isInstanceMethod) {
- mlistp = &proto->optionalInstanceMethods;
- } else {
- mlistp = &proto->optionalClassMethods;
- }
- }
-
- if (*mlistp) {
- method_list_t *mlist = *mlistp;
- if (!isMethodListFixedUp(mlist)) {
- mlist = _memdup_internal(mlist, method_list_size(mlist));
- fixupMethodList(mlist, YES/*always copy for simplicity*/);
- *mlistp = mlist;
- }
- for (i = 0; i < mlist->count; i++) {
- method_t *m = method_list_nth(mlist, i);
- if (sel == m->name) return (Method)m;
- }
- }
-
- if (proto->protocols) {
- Method m;
- for (i = 0; i < proto->protocols->count; i++) {
- protocol_t *realProto = remapProtocol(proto->protocols->list[i]);
- m = _protocol_getMethod_nolock(realProto, sel,
- isRequiredMethod, isInstanceMethod);
- if (m) return m;
- }
- }
-
- return NULL;
-}
-
-
-/***********************************************************************
-* _protocol_getMethod
-* fixme
-* Locking: write-locks runtimeLock
-**********************************************************************/
-__private_extern__ Method
-_protocol_getMethod(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod)
-{
- rwlock_write(&runtimeLock);
- Method result = _protocol_getMethod_nolock(newprotocol(p), sel,
- isRequiredMethod,
- isInstanceMethod);
- rwlock_unlock_write(&runtimeLock);
- return result;
-}
-
-
-/***********************************************************************
-* protocol_getName
-* Returns the name of the given protocol.
-* Locking: runtimeLock must not be held by the caller
-**********************************************************************/
-const char *
-protocol_getName(Protocol *proto)
-{
- return newprotocol(proto)->name;
-}
-
-
-/***********************************************************************
-* protocol_getInstanceMethodDescription
-* Returns the description of a named instance method.
-* Locking: runtimeLock must not be held by the caller
-**********************************************************************/
-struct objc_method_description
-protocol_getMethodDescription(Protocol *p, SEL aSel,
- BOOL isRequiredMethod, BOOL isInstanceMethod)
-{
- Method m =
- _protocol_getMethod(p, aSel, isRequiredMethod, isInstanceMethod);
- if (m) return *method_getDescription(m);
- else return (struct objc_method_description){NULL, NULL};
-}
-
-
-/***********************************************************************
-* _protocol_conformsToProtocol_nolock
-* Returns YES if self conforms to other.
-* Locking: runtimeLock must be held by the caller.
-**********************************************************************/
-static BOOL _protocol_conformsToProtocol_nolock(protocol_t *self, protocol_t *other)
-{
- if (!self || !other) {
- return NO;
- }
-
- if (0 == strcmp(self->name, other->name)) {
- return YES;
- }
-
- if (self->protocols) {
- int i;
- for (i = 0; i < self->protocols->count; i++) {
- protocol_t *proto = remapProtocol(self->protocols->list[i]);
- if (0 == strcmp(other->name, proto->name)) {
- return YES;
- }
- if (_protocol_conformsToProtocol_nolock(proto, other)) {
- return YES;
- }
- }
- }
-
- return NO;
-}
-
-
-/***********************************************************************
-* protocol_conformsToProtocol
-* Returns YES if self conforms to other.
-* Locking: acquires runtimeLock
-**********************************************************************/
-BOOL protocol_conformsToProtocol(Protocol *self, Protocol *other)
-{
- BOOL result;
- rwlock_read(&runtimeLock);
- result = _protocol_conformsToProtocol_nolock(newprotocol(self),
- newprotocol(other));
- rwlock_unlock_read(&runtimeLock);
- return result;
-}
-
-
-/***********************************************************************
-* protocol_isEqual
-* Return YES if two protocols are equal (i.e. conform to each other)
-* Locking: acquires runtimeLock
-**********************************************************************/
-BOOL protocol_isEqual(Protocol *self, Protocol *other)
-{
- if (self == other) return YES;
- if (!self || !other) return NO;
-
- if (!protocol_conformsToProtocol(self, other)) return NO;
- if (!protocol_conformsToProtocol(other, self)) return NO;
-
- return YES;
-}
-
-
-/***********************************************************************
-* protocol_copyMethodDescriptionList
-* Returns descriptions of a protocol's methods.
-* Locking: acquires runtimeLock
-**********************************************************************/
-struct objc_method_description *
-protocol_copyMethodDescriptionList(Protocol *p,
- BOOL isRequiredMethod,BOOL isInstanceMethod,
- unsigned int *outCount)
-{
- struct protocol_t *proto = newprotocol(p);
- struct objc_method_description *result = NULL;
- unsigned int count = 0;
-
- if (!proto) {
- if (outCount) *outCount = 0;
- return NULL;
- }
-
- rwlock_read(&runtimeLock);
-
- method_list_t *mlist = NULL;
-
- if (isRequiredMethod) {
- if (isInstanceMethod) {
- mlist = proto->instanceMethods;
- } else {
- mlist = proto->classMethods;
- }
- } else {
- if (isInstanceMethod) {
- mlist = proto->optionalInstanceMethods;
- } else {
- mlist = proto->optionalClassMethods;
- }
- }
-
- if (mlist) {
- unsigned int i;
- count = mlist->count;
- result = calloc(count + 1, sizeof(struct objc_method_description));
- for (i = 0; i < count; i++) {
- method_t *m = method_list_nth(mlist, i);
- result[i].name = sel_registerName((const char *)m->name);
- result[i].types = (char *)m->types;
- }
- }
-
- rwlock_unlock_read(&runtimeLock);
-
- if (outCount) *outCount = count;
- return result;
-}
-
-
-/***********************************************************************
-* protocol_getProperty
-* fixme
-* Locking: acquires runtimeLock
-**********************************************************************/
-static Property
-_protocol_getProperty_nolock(protocol_t *proto, const char *name,
- BOOL isRequiredProperty, BOOL isInstanceProperty)
-{
- if (!isRequiredProperty || !isInstanceProperty) {
- // Only required instance properties are currently supported
- return NULL;
- }
-
- struct objc_property_list *plist;
- if ((plist = proto->instanceProperties)) {
- uint32_t i;
- for (i = 0; i < plist->count; i++) {
- Property prop = property_list_nth(plist, i);
- if (0 == strcmp(name, prop->name)) {
- return prop;
- }
- }
- }
-
- if (proto->protocols) {
- uintptr_t i;
- for (i = 0; i < proto->protocols->count; i++) {
- protocol_t *p = remapProtocol(proto->protocols->list[i]);
- Property prop =
- _protocol_getProperty_nolock(p, name,
- isRequiredProperty,
- isInstanceProperty);
- if (prop) return prop;
- }
- }
-
- return NULL;
-}
-
-Property protocol_getProperty(Protocol *p, const char *name,
- BOOL isRequiredProperty, BOOL isInstanceProperty)
-{
- Property result;
-
- if (!p || !name) return NULL;
-
- rwlock_read(&runtimeLock);
- result = _protocol_getProperty_nolock(newprotocol(p), name,
- isRequiredProperty,
- isInstanceProperty);
- rwlock_unlock_read(&runtimeLock);
-
- return result;
-}
-
-
-/***********************************************************************
-* protocol_copyPropertyList
-* fixme
-* Locking: acquires runtimeLock
-**********************************************************************/
-Property *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
-{
- Property *result = NULL;
-
- if (!proto) {
- if (outCount) *outCount = 0;
- return NULL;
- }
-
- rwlock_read(&runtimeLock);
-
- struct objc_property_list *plist = newprotocol(proto)->instanceProperties;
- result = copyPropertyList(plist, outCount);
-
- rwlock_unlock_read(&runtimeLock);
-
- return result;
-}
-
-
-/***********************************************************************
-* protocol_copyProtocolList
-* Copies this protocol's incorporated protocols.
-* Does not copy those protocol's incorporated protocols in turn.
-* Locking: acquires runtimeLock
-**********************************************************************/
-Protocol **protocol_copyProtocolList(Protocol *p, unsigned int *outCount)
-{
- unsigned int count = 0;
- Protocol **result = NULL;
- protocol_t *proto = newprotocol(p);
-
- if (!proto) {
- if (outCount) *outCount = 0;
- return NULL;
- }
-
- rwlock_read(&runtimeLock);
-
- if (proto->protocols) {
- count = (unsigned int)proto->protocols->count;
- }
- if (count > 0) {
- result = malloc((count+1) * sizeof(Protocol *));
-
- unsigned int i;
- for (i = 0; i < count; i++) {
- result[i] = (Protocol *)remapProtocol(proto->protocols->list[i]);
- }
- result[i] = NULL;
- }
-
- rwlock_unlock_read(&runtimeLock);
-
- if (outCount) *outCount = count;
- return result;
-}
-
-
-/***********************************************************************
-* objc_getClassList
-* Returns pointers to all classes.
-* This requires all classes be realized, which is regretfully non-lazy.
-* Locking: acquires runtimeLock
-**********************************************************************/
-int
-objc_getClassList(Class *buffer, int bufferLen)
-{
- rwlock_write(&runtimeLock);
-
- realizeAllClasses();
-
- int count;
- class_t *cls;
- NXHashState state;
- NXHashTable *classes = realizedClasses();
- int allCount = NXCountHashTable(classes);
-
- if (!buffer) {
- rwlock_unlock_write(&runtimeLock);
- return allCount;
- }
-
- count = 0;
- state = NXInitHashState(classes);
- while (count < bufferLen &&
- NXNextHashState(classes, &state, (void **)&cls))
- {
- buffer[count++] = (Class)cls;
- }
-
- rwlock_unlock_write(&runtimeLock);
-
- return allCount;
-}
-
-
-/***********************************************************************
-* objc_copyProtocolList
-* Returns pointers to all protocols.
-* Locking: read-locks runtimeLock
-**********************************************************************/
-Protocol **
-objc_copyProtocolList(unsigned int *outCount)
-{
- rwlock_read(&runtimeLock);
-
- int count, i;
- Protocol *proto;
- const char *name;
- NXMapState state;
- NXMapTable *protocol_map = protocols();
- Protocol **result;
-
- count = NXCountMapTable(protocol_map);
- if (count == 0) {
- rwlock_unlock_read(&runtimeLock);
- if (outCount) *outCount = 0;
- return NULL;
- }
-
- result = calloc(1 + count, sizeof(Protocol *));
-
- i = 0;
- state = NXInitMapState(protocol_map);
- while (NXNextMapState(protocol_map, &state,
- (const void **)&name, (const void **)&proto))
- {
- result[i++] = proto;
- }
-
- result[i++] = NULL;
- assert(i == count+1);
-
- rwlock_unlock_read(&runtimeLock);
-
- if (outCount) *outCount = count;
- return result;
-}
-
-
-/***********************************************************************
-* objc_getProtocol
-* Get a protocol by name, or return NULL
-* Locking: read-locks runtimeLock
-**********************************************************************/
-Protocol *objc_getProtocol(const char *name)
-{
- rwlock_read(&runtimeLock);
- Protocol *result = (Protocol *)NXMapGet(protocols(), name);
- rwlock_unlock_read(&runtimeLock);
- return result;
-}
-
-
-/***********************************************************************
-* class_copyMethodList
-* fixme
-* Locking: read-locks runtimeLock
-**********************************************************************/
-Method *
-class_copyMethodList(Class cls_gen, unsigned int *outCount)
-{
- struct class_t *cls = newcls(cls_gen);
- unsigned int count = 0;
- Method *result = NULL;
-
- if (!cls) {
- if (outCount) *outCount = 0;
- return NULL;
- }
-
- rwlock_read(&runtimeLock);
-
- assert(isRealized(cls));
-
- FOREACH_METHOD_LIST(mlist, cls, {
- count += mlist->count;
- });
-
- if (count > 0) {
- unsigned int m;
- result = malloc((count + 1) * sizeof(Method));
-
- m = 0;
- FOREACH_METHOD_LIST(mlist, cls, {
- unsigned int i;
- for (i = 0; i < mlist->count; i++) {
- Method aMethod = (Method)method_list_nth(mlist, i);
- if (method_getName(aMethod) == (SEL)kIgnore) {
- count--;
- continue;
- }
- result[m++] = aMethod;
- }
- });
- result[m] = NULL;
- }
-
- rwlock_unlock_read(&runtimeLock);
-
- if (outCount) *outCount = count;
- return result;
-}
-
-
-/***********************************************************************
-* class_copyIvarList
-* fixme
-* Locking: read-locks runtimeLock
-**********************************************************************/
-Ivar *
-class_copyIvarList(Class cls_gen, unsigned int *outCount)
-{
- struct class_t *cls = newcls(cls_gen);
- const ivar_list_t *ivars;
- Ivar *result = NULL;
- unsigned int count = 0;
- unsigned int i;
-
- if (!cls) {
- if (outCount) *outCount = 0;
- return NULL;
- }
-
- rwlock_read(&runtimeLock);
-
- assert(isRealized(cls));
-
- if ((ivars = cls->data->ro->ivars) && ivars->count) {
- result = malloc((ivars->count+1) * sizeof(Ivar));
-
- for (i = 0; i < ivars->count; i++) {
- ivar_t *ivar = ivar_list_nth(ivars, i);
- if (!ivar->offset) continue; // anonymous bitfield
- result[count++] = (Ivar)ivar;
- }
- result[count] = NULL;
- }
-
- rwlock_unlock_read(&runtimeLock);
-
- if (outCount) *outCount = count;
- return result;
-}
-
-
-/***********************************************************************
-* class_copyPropertyList. Returns a heap block containing the
-* properties declared in the class, or NULL if the class
-* declares no properties. Caller must free the block.
-* Does not copy any superclass's properties.
-* Locking: read-locks runtimeLock
-**********************************************************************/
-Property *
-class_copyPropertyList(Class cls_gen, unsigned int *outCount)
-{
- struct class_t *cls = newcls(cls_gen);
- chained_property_list *plist;
- unsigned int count = 0;
- Property *result = NULL;
-
- if (!cls) {
- if (outCount) *outCount = 0;
- return NULL;
- }
-
- rwlock_read(&runtimeLock);
-
- assert(isRealized(cls));
-
- for (plist = cls->data->properties; plist; plist = plist->next) {
- count += plist->count;
- }
-
- if (count > 0) {
- unsigned int p;
- result = malloc((count + 1) * sizeof(Property));
-
- p = 0;
- for (plist = cls->data->properties; plist; plist = plist->next) {
- unsigned int i;
- for (i = 0; i < plist->count; i++) {
- result[p++] = (Property)&plist->list[i];
- }
- }
- result[p] = NULL;
- }
-
- rwlock_unlock_read(&runtimeLock);
-
- if (outCount) *outCount = count;
- return result;
-}
-
-
-/***********************************************************************
-* _class_getLoadMethod
-* fixme
-* Called only from add_class_to_loadable_list.
-* Locking: runtimeLock must be read- or write-locked by the caller.
-**********************************************************************/
-__private_extern__ IMP
-_class_getLoadMethod(Class cls_gen)
-{
- rwlock_assert_locked(&runtimeLock);
-
- struct class_t *cls = newcls(cls_gen);
- const method_list_t *mlist;
- int i;
-
- assert(isRealized(cls));
- assert(isRealized(cls->isa));
- assert(!isMetaClass(cls));
- assert(isMetaClass(cls->isa));
-
- mlist = cls->isa->data->ro->baseMethods;
- if (mlist) for (i = 0; i < mlist->count; i++) {
- method_t *m = method_list_nth(mlist, i);
- if (0 == strcmp((const char *)m->name, "load")) {
- return m->imp;
- }
- }
-
- return NULL;
-}
-
-
-/***********************************************************************
-* _category_getName
-* Returns a category's name.
-* Locking: none
-**********************************************************************/
-__private_extern__ const char *
-_category_getName(Category cat)
-{
- return newcategory(cat)->name;
-}
-
-
-/***********************************************************************
-* _category_getClassName
-* Returns a category's class's name
-* Called only from add_category_to_loadable_list and
-* remove_category_from_loadable_list.
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-__private_extern__ const char *
-_category_getClassName(Category cat)
-{
- rwlock_assert_locked(&runtimeLock);
- // cat->cls may have been remapped
- return getName(remapClass(newcategory(cat)->cls));
-}
-
-
-/***********************************************************************
-* _category_getClass
-* Returns a category's class
-* Called only by call_category_loads.
-* Locking: read-locks runtimeLock
-**********************************************************************/
-__private_extern__ Class
-_category_getClass(Category cat)
-{
- rwlock_read(&runtimeLock);
- // cat->cls may have been remapped
- struct class_t *result = remapClass(newcategory(cat)->cls);
- assert(isRealized(result)); // ok for call_category_loads' usage
- rwlock_unlock_read(&runtimeLock);
- return (Class)result;
-}
-
-
-/***********************************************************************
-* _category_getLoadMethod
-* fixme
-* Called only from add_category_to_loadable_list
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-__private_extern__ IMP
-_category_getLoadMethod(Category cat)
-{
- rwlock_assert_locked(&runtimeLock);
-
- const method_list_t *mlist;
- int i;
-
- mlist = newcategory(cat)->classMethods;
- if (mlist) for (i = 0; i < mlist->count; i++) {
- method_t *m = method_list_nth(mlist, i);
- if (0 == strcmp((const char *)m->name, "load")) {
- return m->imp;
- }
- }
-
- return NULL;
-}
-
-
-/***********************************************************************
-* class_copyProtocolList
-* fixme
-* Locking: read-locks runtimeLock
-**********************************************************************/
-Protocol **
-class_copyProtocolList(Class cls_gen, unsigned int *outCount)
-{
- struct class_t *cls = newcls(cls_gen);
- Protocol **r;
- struct protocol_list_t **p;
- unsigned int count = 0;
- unsigned int i;
- Protocol **result = NULL;
-
- if (!cls) {
- if (outCount) *outCount = 0;
- return NULL;
- }
-
- rwlock_read(&runtimeLock);
-
- assert(isRealized(cls));
-
- for (p = cls->data->protocols; p && *p; p++) {
- count += (uint32_t)(*p)->count;
- }
-
- if (count) {
- result = malloc((count+1) * sizeof(Protocol *));
- r = result;
- for (p = cls->data->protocols; p && *p; p++) {
- for (i = 0; i < (*p)->count; i++) {
- *r++ = (Protocol *)remapProtocol((*p)->list[i]);
- }
- }
- *r++ = NULL;
- }
-
- rwlock_unlock_read(&runtimeLock);
-
- if (outCount) *outCount = count;
- return result;
-}
-
-
-/***********************************************************************
-* _objc_copyClassNamesForImage
-* fixme
-* Locking: read-locks runtimeLock
-**********************************************************************/
-__private_extern__ const char **
-_objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount)
-{
- size_t count, i, shift;
- class_t **classlist;
- const char **names;
-
- rwlock_read(&runtimeLock);
-
- classlist = _getObjc2ClassList(hi, &count);
- names = malloc((count+1) * sizeof(const char *));
-
- shift = 0;
- for (i = 0; i < count; i++) {
- class_t *cls = remapClass(classlist[i]);
- if (cls) {
- names[i-shift] = getName(classlist[i]);
- } else {
- shift++; // ignored weak-linked class
- }
- }
- count -= shift;
- names[count] = NULL;
-
- rwlock_unlock_read(&runtimeLock);
-
- if (outCount) *outCount = (unsigned int)count;
- return names;
-}
-
-
-/***********************************************************************
-* _class_getCache
-* fixme
-* Locking: none
-**********************************************************************/
-__private_extern__ Cache
-_class_getCache(Class cls)
-{
- return newcls(cls)->cache;
-}
-
-
-/***********************************************************************
-* _class_getInstanceSize
-* fixme
-* Locking: none
-**********************************************************************/
-__private_extern__ size_t
-_class_getInstanceSize(Class cls)
-{
- if (!cls) return 0;
- return instanceSize(newcls(cls));
-}
-
-static uint32_t
-instanceSize(struct class_t *cls)
-{
- assert(cls);
- assert(isRealized(cls));
- // fixme rdar://5244378
- return (uint32_t)((cls->data->ro->instanceSize + WORD_MASK) & ~WORD_MASK);
-}
-
-
-/***********************************************************************
-* class_getVersion
-* fixme
-* Locking: none
-**********************************************************************/
-int
-class_getVersion(Class cls)
-{
- if (!cls) return 0;
- assert(isRealized(newcls(cls)));
- return newcls(cls)->data->version;
-}
-
-
-/***********************************************************************
-* _class_setCache
-* fixme
-* Locking: none
-**********************************************************************/
-__private_extern__ void
-_class_setCache(Class cls, Cache cache)
-{
- newcls(cls)->cache = cache;
-}
-
-
-/***********************************************************************
-* class_setVersion
-* fixme
-* Locking: none
-**********************************************************************/
-void
-class_setVersion(Class cls, int version)
-{
- if (!cls) return;
- assert(isRealized(newcls(cls)));
- newcls(cls)->data->version = version;
-}
-
-
-/***********************************************************************
-* _class_getName
-* fixme
-* Locking: acquires runtimeLock
-**********************************************************************/
-__private_extern__ const char *_class_getName(Class cls)
-{
- if (!cls) return "nil";
- // fixme hack rwlock_write(&runtimeLock);
- const char *name = getName(newcls(cls));
- // rwlock_unlock_write(&runtimeLock);
- return name;
-}
-
-
-/***********************************************************************
-* getName
-* fixme
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static const char *
-getName(struct class_t *cls)
-{
- // fixme hack rwlock_assert_writing(&runtimeLock);
- assert(cls);
-
- if (isRealized(cls)) {
- return cls->data->ro->name;
- } else {
- return ((const struct class_ro_t *)cls->data)->name;
- }
-}
-
-
-/***********************************************************************
-* getMethodNoSuper_nolock
-* fixme
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static method_t *
-getMethodNoSuper_nolock(struct class_t *cls, SEL sel)
-{
- rwlock_assert_locked(&runtimeLock);
-
- uint32_t i;
-
- assert(isRealized(cls));
- // fixme nil cls?
- // fixme NULL sel?
-
- FOREACH_METHOD_LIST(mlist, cls, {
- for (i = 0; i < mlist->count; i++) {
- method_t *m = method_list_nth(mlist, i);
- if (m->name == sel) return m;
- }
- });
-
- return NULL;
-}
-
-
-/***********************************************************************
-* _class_getMethodNoSuper
-* fixme
-* Locking: read-locks runtimeLock
-**********************************************************************/
-__private_extern__ Method
-_class_getMethodNoSuper(Class cls, SEL sel)
-{
- rwlock_read(&runtimeLock);
- Method result = (Method)getMethodNoSuper_nolock(newcls(cls), sel);
- rwlock_unlock_read(&runtimeLock);
- return result;
-}
-
-/***********************************************************************
-* _class_getMethodNoSuper
-* For use inside lockForMethodLookup() only.
-* Locking: read-locks runtimeLock
-**********************************************************************/
-__private_extern__ Method
-_class_getMethodNoSuper_nolock(Class cls, SEL sel)
-{
- return (Method)getMethodNoSuper_nolock(newcls(cls), sel);
-}
-
-
-/***********************************************************************
-* getMethod_nolock
-* fixme
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static method_t *
-getMethod_nolock(class_t *cls, SEL sel)
-{
- method_t *m = NULL;
-
- rwlock_assert_locked(&runtimeLock);
-
- // fixme nil cls?
- // fixme NULL sel?
-
- assert(isRealized(cls));
-
- while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == NULL) {
- cls = getSuperclass(cls);
- }
-
- return m;
-}
-
-
-/***********************************************************************
-* _class_getMethod
-* fixme
-* Locking: read-locks runtimeLock
-**********************************************************************/
-__private_extern__ Method _class_getMethod(Class cls, SEL sel)
-{
- Method m;
- rwlock_read(&runtimeLock);
- m = (Method)getMethod_nolock(newcls(cls), sel);
- rwlock_unlock_read(&runtimeLock);
- return m;
-}
-
-
-/***********************************************************************
-* ABI-specific lookUpMethod helpers.
-* Locking: read- and write-locks runtimeLock.
-**********************************************************************/
-__private_extern__ void lockForMethodLookup(void)
-{
- rwlock_read(&runtimeLock);
-}
-__private_extern__ void unlockForMethodLookup(void)
-{
- rwlock_unlock_read(&runtimeLock);
-}
-
-__private_extern__ IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init)
-{
- rwlock_assert_unlocked(&runtimeLock);
-
- if (!isRealized(newcls(cls))) {
- rwlock_write(&runtimeLock);
- realizeClass(newcls(cls));
- rwlock_unlock_write(&runtimeLock);
- }
-
- if (init && !_class_isInitialized(cls)) {
- _class_initialize (cls);
- // If sel == initialize, _class_initialize will send +initialize and
- // then the messenger will send +initialize again after this
- // procedure finishes. Of course, if this is not being called
- // from the messenger then it won't happen. 2778172
- }
-
- return NULL;
-}
-
-
-/***********************************************************************
-* class_getProperty
-* fixme
-* Locking: read-locks runtimeLock
-**********************************************************************/
-Property class_getProperty(Class cls_gen, const char *name)
-{
- Property result = NULL;
- chained_property_list *plist;
- struct class_t *cls = newcls(cls_gen);
-
- if (!cls || !name) return NULL;
-
- rwlock_read(&runtimeLock);
-
- assert(isRealized(cls));
-
- for ( ; cls; cls = getSuperclass(cls)) {
- for (plist = cls->data->properties; plist; plist = plist->next) {
- uint32_t i;
- for (i = 0; i < plist->count; i++) {
- if (0 == strcmp(name, plist->list[i].name)) {
- result = &plist->list[i];
- goto done;
- }
- }
- }
- }
-
- done:
- rwlock_unlock_read(&runtimeLock);
-
- return result;
-}
-
-
-/***********************************************************************
-* Locking: fixme
-**********************************************************************/
-__private_extern__ BOOL _class_isMetaClass(Class cls)
-{
- if (!cls) return NO;
- return isMetaClass(newcls(cls));
-}
-
-static BOOL
-isMetaClass(struct class_t *cls)
-{
- assert(cls);
- assert(isRealized(cls));
- return (cls->data->ro->flags & RO_META) ? YES : NO;
-}
-
-
-__private_extern__ Class _class_getMeta(Class cls)
-{
- assert(cls);
- if (isMetaClass(newcls(cls))) return cls;
- else return ((id)cls)->isa;
-}
-
-Class gdb_class_getClass(Class cls)
-{
- const char *className = strdup(getName(newcls(cls)));
- if(!className) return Nil;
- Class rCls = look_up_class(className, NO, NO);
- free((char*)className);
- return rCls;
-}
-
-BOOL gdb_objc_isRuntimeLocked()
-{
- if (rwlock_try_write(&runtimeLock)) {
- rwlock_unlock_write(&runtimeLock);
- } else
- return YES;
-
- if (mutex_try_lock(&cacheUpdateLock)) {
- mutex_unlock(&cacheUpdateLock);
- } else
- return YES;
-
- return NO;
-}
-
-/***********************************************************************
-* Locking: fixme
-**********************************************************************/
-__private_extern__ BOOL
-_class_isInitializing(Class cls_gen)
-{
- struct class_t *cls = newcls(_class_getMeta(cls_gen));
- return (cls->data->flags & RW_INITIALIZING) ? YES : NO;
-}
-
-
-/***********************************************************************
-* Locking: fixme
-**********************************************************************/
-__private_extern__ BOOL
-_class_isInitialized(Class cls_gen)
-{
- struct class_t *cls = newcls(_class_getMeta(cls_gen));
- return (cls->data->flags & RW_INITIALIZED) ? YES : NO;
-}
-
-
-/***********************************************************************
-* Locking: fixme
-**********************************************************************/
-__private_extern__ void
-_class_setInitializing(Class cls_gen)
-{
- struct class_t *cls = newcls(_class_getMeta(cls_gen));
- changeInfo(cls, RW_INITIALIZING, 0);
-}
-
-
-/***********************************************************************
-* Locking: write-locks runtimeLock
-**********************************************************************/
-__private_extern__ void
-_class_setInitialized(Class cls_gen)
-{
-
- struct class_t *metacls;
- struct class_t *cls;
-
- rwlock_write(&runtimeLock);
- metacls = newcls(_class_getMeta(cls_gen));
- cls = getNonMetaClass(metacls);
-
- // Update vtables (initially postponed pending +initialize completion)
- // Do cls first because root metacls is a subclass of root cls
- updateVtable(cls, YES);
- updateVtable(metacls, YES);
-
- rwlock_unlock_write(&runtimeLock);
-
- changeInfo(metacls, RW_INITIALIZED, RW_INITIALIZING);
-}
-
-
-/***********************************************************************
-* Locking: fixme
-**********************************************************************/
-__private_extern__ BOOL
-_class_shouldGrowCache(Class cls)
-{
- return YES; // fixme good or bad for memory use?
-}
-
-
-/***********************************************************************
-* Locking: fixme
-**********************************************************************/
-__private_extern__ void
-_class_setGrowCache(Class cls, BOOL grow)
-{
- // fixme good or bad for memory use?
-}
-
-
-/***********************************************************************
-* _class_isLoadable
-* fixme
-* Locking: none
-**********************************************************************/
-__private_extern__ BOOL
-_class_isLoadable(Class cls)
-{
- assert(isRealized(newcls(cls)));
- return YES; // any class registered for +load is definitely loadable
-}
-
-
-/***********************************************************************
-* Locking: fixme
-**********************************************************************/
-__private_extern__ BOOL
-_class_hasCxxStructorsNoSuper(Class cls)
-{
- assert(isRealized(newcls(cls)));
- return (newcls(cls)->data->ro->flags & RO_HAS_CXX_STRUCTORS) ? YES : NO;
-}
-
-
-/***********************************************************************
-* Locking: fixme
-**********************************************************************/
-__private_extern__ BOOL
-_class_shouldFinalizeOnMainThread(Class cls)
-{
- assert(isRealized(newcls(cls)));
- return (newcls(cls)->data->flags & RW_FINALIZE_ON_MAIN_THREAD) ? YES : NO;
-}
-
-
-/***********************************************************************
-* Locking: fixme
-**********************************************************************/
-__private_extern__ void
-_class_setFinalizeOnMainThread(Class cls)
-{
- assert(isRealized(newcls(cls)));
- changeInfo(newcls(cls), RW_FINALIZE_ON_MAIN_THREAD, 0);
-}
-
-
-/***********************************************************************
-* _class_instancesHaveAssociatedObjects
-* May manipulate unrealized future classes in the CF-bridged case.
-**********************************************************************/
-__private_extern__ BOOL
-_class_instancesHaveAssociatedObjects(Class cls_gen)
-{
- class_t *cls = newcls(cls_gen);
- assert(isFuture(cls) || isRealized(cls));
- return (cls->data->flags & RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS) ? YES : NO;
-}
-
-
-/***********************************************************************
-* _class_assertInstancesHaveAssociatedObjects
-* May manipulate unrealized future classes in the CF-bridged case.
-**********************************************************************/
-__private_extern__ void
-_class_assertInstancesHaveAssociatedObjects(Class cls_gen)
-{
- class_t *cls = newcls(cls_gen);
- assert(isFuture(cls) || isRealized(cls));
- changeInfo(cls, RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS, 0);
-}
-
-
-/***********************************************************************
-* Locking: none
-* fixme assert realized to get superclass remapping?
-**********************************************************************/
-__private_extern__ Class
-_class_getSuperclass(Class cls)
-{
- return (Class)getSuperclass(newcls(cls));
-}
-
-static struct class_t *
-getSuperclass(struct class_t *cls)
-{
- if (!cls) return NULL;
- return cls->superclass;
-}
-
-
-/***********************************************************************
-* class_getIvarLayout
-* Called by the garbage collector.
-* The class must be NULL or already realized.
-* Locking: none
-**********************************************************************/
-const char *
-class_getIvarLayout(Class cls_gen)
-{
- class_t *cls = newcls(cls_gen);
- if (cls) return (const char *)cls->data->ro->ivarLayout;
- else return NULL;
-}
-
-
-/***********************************************************************
-* class_getWeakIvarLayout
-* Called by the garbage collector.
-* The class must be NULL or already realized.
-* Locking: none
-**********************************************************************/
-const char *
-class_getWeakIvarLayout(Class cls_gen)
-{
- class_t *cls = newcls(cls_gen);
- if (cls) return (const char *)cls->data->ro->weakIvarLayout;
- else return NULL;
-}
-
-
-/***********************************************************************
-* class_setIvarLayout
-* Changes the class's GC scan layout.
-* NULL layout means no unscanned ivars
-* The class must be under construction.
-* fixme: sanity-check layout vs instance size?
-* fixme: sanity-check layout vs superclass?
-* Locking: acquires runtimeLock
-**********************************************************************/
-void
-class_setIvarLayout(Class cls_gen, const char *layout)
-{
- class_t *cls = newcls(cls_gen);
- if (!cls) return;
-
- rwlock_write(&runtimeLock);
-
- // Can only change layout of in-construction classes.
- // note: if modifications to post-construction classes were
- // allowed, there would be a race below (us vs. concurrent GC scan)
- if (!(cls->data->flags & RW_CONSTRUCTING)) {
- _objc_inform("*** Can't set ivar layout for already-registered "
- "class '%s'", getName(cls));
- rwlock_unlock_write(&runtimeLock);
- return;
- }
-
- class_ro_t *ro_w = make_ro_writeable(cls->data);
-
- try_free(ro_w->ivarLayout);
- ro_w->ivarLayout = (unsigned char *)_strdup_internal(layout);
-
- rwlock_unlock_write(&runtimeLock);
-}
-
-
-/***********************************************************************
-* class_setWeakIvarLayout
-* Changes the class's GC weak layout.
-* NULL layout means no weak ivars
-* The class must be under construction.
-* fixme: sanity-check layout vs instance size?
-* fixme: sanity-check layout vs superclass?
-* Locking: acquires runtimeLock
-**********************************************************************/
-void
-class_setWeakIvarLayout(Class cls_gen, const char *layout)
-{
- class_t *cls = newcls(cls_gen);
- if (!cls) return;
-
- rwlock_write(&runtimeLock);
-
- // Can only change layout of in-construction classes.
- // note: if modifications to post-construction classes were
- // allowed, there would be a race below (us vs. concurrent GC scan)
- if (!(cls->data->flags & RW_CONSTRUCTING)) {
- _objc_inform("*** Can't set weak ivar layout for already-registered "
- "class '%s'", getName(cls));
- rwlock_unlock_write(&runtimeLock);
- return;
- }
-
- class_ro_t *ro_w = make_ro_writeable(cls->data);
-
- try_free(ro_w->weakIvarLayout);
- ro_w->weakIvarLayout = (unsigned char *)_strdup_internal(layout);
-
- rwlock_unlock_write(&runtimeLock);
-}
-
-
-/***********************************************************************
-* _class_getVariable
-* fixme
-* Locking: read-locks runtimeLock
-**********************************************************************/
-__private_extern__ Ivar
-_class_getVariable(Class cls, const char *name)
-{
- rwlock_read(&runtimeLock);
-
- for ( ; cls != Nil; cls = class_getSuperclass(cls)) {
- struct ivar_t *ivar = getIvar(newcls(cls), name);
- if (ivar) {
- rwlock_unlock_read(&runtimeLock);
- return (Ivar)ivar;
- }
- }
-
- rwlock_unlock_read(&runtimeLock);
-
- return NULL;
-}
-
-
-/***********************************************************************
-* class_conformsToProtocol
-* fixme
-* Locking: read-locks runtimeLock
-**********************************************************************/
-BOOL class_conformsToProtocol(Class cls_gen, Protocol *proto)
-{
- Protocol **protocols;
- unsigned int count, i;
- BOOL result = NO;
-
- if (!cls_gen) return NO;
- if (!proto) return NO;
-
- // fixme null cls?
-
- protocols = class_copyProtocolList(cls_gen, &count);
-
- for (i = 0; i < count; i++) {
- if (protocols[i] == proto ||
- protocol_conformsToProtocol(protocols[i], proto))
- {
- result = YES;
- break;
- }
- }
-
- if (protocols) free(protocols);
-
- return result;
-}
-
-
-/***********************************************************************
-* class_addMethod
-* fixme
-* Locking: write-locks runtimeLock
-**********************************************************************/
-static IMP
-_class_addMethod(Class cls_gen, SEL name, IMP imp,
- const char *types, BOOL replace)
-{
- struct class_t *cls = newcls(cls_gen);
- IMP result = NULL;
-
- if (!types) types = "";
-
- rwlock_write(&runtimeLock);
-
- assert(isRealized(cls));
-
- method_t *m;
- if ((m = getMethodNoSuper_nolock(cls, name))) {
- // already exists
- if (!replace) {
- result = _method_getImplementation(m);
- } else {
- result = _method_setImplementation(cls, m, imp);
- }
- } else {
- // fixme optimize
- method_list_t *newlist;
- newlist = _calloc_internal(sizeof(*newlist), 1);
- newlist->entsize_NEVER_USE = (uint32_t)sizeof(method_t) | fixed_up_method_list;
- newlist->count = 1;
- newlist->first.name = name;
- newlist->first.types = strdup(types);
- if (name != (SEL)kIgnore) {
- newlist->first.imp = imp;
- } else {
- newlist->first.imp = (IMP)&_objc_ignored_method;
- }
-
- BOOL vtablesAffected;
- attachMethodLists(cls, &newlist, 1, NO, &vtablesAffected);
- flushCaches(cls);
- if (vtablesAffected) flushVtables(cls);
-
- result = NULL;
- }
-
- rwlock_unlock_write(&runtimeLock);
-
- return result;
-}
-
-
-BOOL
-class_addMethod(Class cls, SEL name, IMP imp, const char *types)
-{
- if (!cls) return NO;
-
- IMP old = _class_addMethod(cls, name, imp, types, NO);
- return old ? NO : YES;
-}
-
-
-IMP
-class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
-{
- if (!cls) return NULL;
-
- return _class_addMethod(cls, name, imp, types, YES);
-}
-
-
-/***********************************************************************
-* class_addIvar
-* Adds an ivar to a class.
-* Locking: acquires runtimeLock
-**********************************************************************/
-BOOL
-class_addIvar(Class cls_gen, const char *name, size_t size,
- uint8_t alignment, const char *type)
-{
- struct class_t *cls = newcls(cls_gen);
-
- if (!cls) return NO;
-
- if (!type) type = "";
- if (name && 0 == strcmp(name, "")) name = NULL;
-
- rwlock_write(&runtimeLock);
-
- assert(isRealized(cls));
-
- // No class variables
- if (isMetaClass(cls)) {
- rwlock_unlock_write(&runtimeLock);
- return NO;
- }
-
- // Can only add ivars to in-construction classes.
- if (!(cls->data->flags & RW_CONSTRUCTING)) {
- rwlock_unlock_write(&runtimeLock);
- return NO;
- }
-
- // Check for existing ivar with this name, unless it's anonymous.
- // Check for too-big ivar.
- // fixme check for superclass ivar too?
- if ((name && getIvar(cls, name)) || size > UINT32_MAX) {
- rwlock_unlock_write(&runtimeLock);
- return NO;
- }
-
- class_ro_t *ro_w = make_ro_writeable(cls->data);
-
- // fixme allocate less memory here
-
- ivar_list_t *oldlist, *newlist;
- if ((oldlist = (ivar_list_t *)cls->data->ro->ivars)) {
- size_t oldsize = ivar_list_size(oldlist);
- newlist = _calloc_internal(oldsize + oldlist->entsize, 1);
- memcpy(newlist, oldlist, oldsize);
- _free_internal(oldlist);
- } else {
- newlist = _calloc_internal(sizeof(ivar_list_t), 1);
- newlist->entsize = (uint32_t)sizeof(ivar_t);
- }
-
- uint32_t offset = instanceSize(cls);
- uint32_t alignMask = (1<<alignment)-1;
- offset = (offset + alignMask) & ~alignMask;
-
- ivar_t *ivar = ivar_list_nth(newlist, newlist->count++);
- ivar->offset = _malloc_internal(sizeof(*ivar->offset));
- *ivar->offset = offset;
- ivar->name = name ? _strdup_internal(name) : NULL;
- ivar->type = _strdup_internal(type);
- ivar->alignment = alignment;
- ivar->size = (uint32_t)size;
-
- ro_w->ivars = newlist;
- ro_w->instanceSize = (uint32_t)(offset + size);
-
- // Ivar layout updated in registerClass.
-
- rwlock_unlock_write(&runtimeLock);
-
- return YES;
-}
-
-
-/***********************************************************************
-* class_addProtocol
-* Adds a protocol to a class.
-* Locking: acquires runtimeLock
-**********************************************************************/
-BOOL class_addProtocol(Class cls_gen, Protocol *protocol_gen)
-{
- class_t *cls = newcls(cls_gen);
- protocol_t *protocol = newprotocol(protocol_gen);
- protocol_list_t *plist;
- protocol_list_t **plistp;
-
- if (!cls) return NO;
- if (class_conformsToProtocol(cls_gen, protocol_gen)) return NO;
-
- rwlock_write(&runtimeLock);
-
- assert(isRealized(cls));
-
- // fixme optimize
- plist = _malloc_internal(sizeof(protocol_list_t) + sizeof(protocol_t *));
- plist->count = 1;
- plist->list[0] = (protocol_ref_t)protocol;
-
- unsigned int count = 0;
- for (plistp = cls->data->protocols; plistp && *plistp; plistp++) {
- count++;
- }
-
- cls->data->protocols =
- _realloc_internal(cls->data->protocols,
- (count+2) * sizeof(protocol_list_t *));
- cls->data->protocols[count] = plist;
- cls->data->protocols[count+1] = NULL;
-
- // fixme metaclass?
-
- rwlock_unlock_write(&runtimeLock);
-
- return YES;
-}
-
-
-/***********************************************************************
-* look_up_class
-* Look up a class by name, and realize it.
-* Locking: acquires runtimeLock
-**********************************************************************/
-__private_extern__ id
-look_up_class(const char *name,
- BOOL includeUnconnected __attribute__((unused)),
- BOOL includeClassHandler __attribute__((unused)))
-{
- if (!name) return nil;
-
- rwlock_read(&runtimeLock);
- class_t *result = getClass(name);
- BOOL unrealized = result && !isRealized(result);
- rwlock_unlock_read(&runtimeLock);
- if (unrealized) {
- rwlock_write(&runtimeLock);
- realizeClass(result);
- rwlock_unlock_write(&runtimeLock);
- }
- return (id)result;
-}
-
-
-/***********************************************************************
-* objc_duplicateClass
-* fixme
-* Locking: acquires runtimeLock
-**********************************************************************/
-Class
-objc_duplicateClass(Class original_gen, const char *name,
- size_t extraBytes)
-{
- struct class_t *original = newcls(original_gen);
- struct class_t *duplicate;
-
- rwlock_write(&runtimeLock);
-
- assert(isRealized(original));
- assert(!isMetaClass(original));
-
- duplicate = (struct class_t *)
- _calloc_class(instanceSize(original->isa) + extraBytes);
- if (instanceSize(original->isa) < sizeof(class_t)) {
- _objc_inform("busted! %s\n", original->data->ro->name);
- }
-
-
- duplicate->isa = original->isa;
- duplicate->superclass = original->superclass;
- duplicate->cache = (Cache)&_objc_empty_cache;
- duplicate->vtable = _objc_empty_vtable;
-
- duplicate->data = _calloc_internal(sizeof(*original->data), 1);
- duplicate->data->flags = (original->data->flags | RW_COPIED_RO) & ~RW_SPECIALIZED_VTABLE;
- duplicate->data->version = original->data->version;
- duplicate->data->firstSubclass = NULL;
- duplicate->data->nextSiblingClass = NULL;
-
- duplicate->data->ro =
- _memdup_internal(original->data->ro, sizeof(*original->data->ro));
- *(char **)&duplicate->data->ro->name = _strdup_internal(name);
-
- if (original->data->methods) {
- duplicate->data->methods =
- _memdup_internal(original->data->methods,
- malloc_size(original->data->methods));
- method_list_t **mlistp = duplicate->data->methods;
- for (mlistp = duplicate->data->methods; *mlistp; mlistp++) {
- *mlistp = _memdup_internal(*mlistp, method_list_size(*mlistp));
- }
- }
-
- // fixme dies when categories are added to the base
- duplicate->data->properties = original->data->properties;
- duplicate->data->protocols = original->data->protocols;
-
- if (duplicate->superclass) {
- addSubclass(duplicate->superclass, duplicate);
- }
-
- // Don't methodize class - construction above is correct
-
- addNamedClass(duplicate, duplicate->data->ro->name);
- addRealizedClass(duplicate);
- // no: duplicate->isa == original->isa
- // addRealizedMetaclass(duplicate->isa);
-
- if (PrintConnecting) {
- _objc_inform("CLASS: realizing class '%s' (duplicate of %s) %p %p",
- name, original->data->ro->name,
- duplicate, duplicate->data->ro);
- }
-
- rwlock_unlock_write(&runtimeLock);
-
- return (Class)duplicate;
-}
-
-/***********************************************************************
-* objc_initializeClassPair
-* Locking: runtimeLock must be write-locked by the caller
-**********************************************************************/
-static void objc_initializeClassPair_internal(Class superclass_gen, const char *name, Class cls_gen, Class meta_gen)
-{
- rwlock_assert_writing(&runtimeLock);
-
- class_t *superclass = newcls(superclass_gen);
- class_t *cls = newcls(cls_gen);
- class_t *meta = newcls(meta_gen);
- class_ro_t *cls_ro_w, *meta_ro_w;
-
- cls->data = _calloc_internal(sizeof(class_rw_t), 1);
- meta->data = _calloc_internal(sizeof(class_rw_t), 1);
- cls_ro_w = _calloc_internal(sizeof(class_ro_t), 1);
- meta_ro_w = _calloc_internal(sizeof(class_ro_t), 1);
- cls->data->ro = cls_ro_w;
- meta->data->ro = meta_ro_w;
-
- // Set basic info
- cls->cache = (Cache)&_objc_empty_cache;
- meta->cache = (Cache)&_objc_empty_cache;
- cls->vtable = _objc_empty_vtable;
- meta->vtable = _objc_empty_vtable;
-
- cls->data->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED;
- meta->data->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED;
- cls->data->version = 0;
- meta->data->version = 7;
-
- cls_ro_w->flags = 0;
- meta_ro_w->flags = RO_META;
- if (!superclass) {
- cls_ro_w->flags |= RO_ROOT;
- meta_ro_w->flags |= RO_ROOT;
- }
- if (superclass) {
- cls_ro_w->instanceStart = instanceSize(superclass);
- meta_ro_w->instanceStart = instanceSize(superclass->isa);
- cls_ro_w->instanceSize = cls_ro_w->instanceStart;
- meta_ro_w->instanceSize = meta_ro_w->instanceStart;
- } else {
- cls_ro_w->instanceStart = 0;
- meta_ro_w->instanceStart = (uint32_t)sizeof(class_t);
- cls_ro_w->instanceSize = (uint32_t)sizeof(id); // just an isa
- meta_ro_w->instanceSize = meta_ro_w->instanceStart;
- }
-
- cls_ro_w->name = _strdup_internal(name);
- meta_ro_w->name = _strdup_internal(name);
-
- // Connect to superclasses and metaclasses
- cls->isa = meta;
- if (superclass) {
- meta->isa = superclass->isa->isa;
- cls->superclass = superclass;
- meta->superclass = superclass->isa;
- addSubclass(superclass, cls);
- addSubclass(superclass->isa, meta);
- } else {
- meta->isa = meta;
- cls->superclass = Nil;
- meta->superclass = cls;
- addSubclass(cls, meta);
- }
-}
-
-/***********************************************************************
-* objc_initializeClassPair
-**********************************************************************/
-Class objc_initializeClassPair(Class superclass_gen, const char *name, Class cls_gen, Class meta_gen)
-{
- class_t *superclass = newcls(superclass_gen);
-
- rwlock_write(&runtimeLock);
-
- //
- // Common superclass integrity checks with objc_allocateClassPair
- //
- if (getClass(name)) {
- rwlock_unlock_write(&runtimeLock);
- return NO;
- }
- // fixme reserve class against simmultaneous allocation
-
- if (superclass) assert(isRealized(superclass));
-
- if (superclass && superclass->data->flags & RW_CONSTRUCTING) {
- // Can't make subclass of an in-construction class
- rwlock_unlock_write(&runtimeLock);
- return NO;
- }
-
-
- // just initialize what was supplied
- objc_initializeClassPair_internal(superclass_gen, name, cls_gen, meta_gen);
-
- rwlock_unlock_write(&runtimeLock);
- return cls_gen;
-}
-
-/***********************************************************************
-* objc_allocateClassPair
-* fixme
-* Locking: acquires runtimeLock
-**********************************************************************/
-Class objc_allocateClassPair(Class superclass_gen, const char *name,
- size_t extraBytes)
-{
- class_t *superclass = newcls(superclass_gen);
- Class cls, meta;
-
- rwlock_write(&runtimeLock);
-
- //
- // Common superclass integrity checks with objc_initializeClassPair
- //
- if (getClass(name)) {
- rwlock_unlock_write(&runtimeLock);
- return NO;
- }
- // fixme reserve class against simmultaneous allocation
-
- if (superclass) assert(isRealized(superclass));
-
- if (superclass && superclass->data->flags & RW_CONSTRUCTING) {
- // Can't make subclass of an in-construction class
- rwlock_unlock_write(&runtimeLock);
- return NO;
- }
-
-
-
- // Allocate new classes.
- if (superclass) {
- cls = _calloc_class(instanceSize(superclass->isa) + extraBytes);
- meta = _calloc_class(instanceSize(superclass->isa->isa) + extraBytes);
- } else {
- cls = _calloc_class(sizeof(class_t) + extraBytes);
- meta = _calloc_class(sizeof(class_t) + extraBytes);
- }
-
-
- objc_initializeClassPair_internal(superclass_gen, name, cls, meta);
-
- rwlock_unlock_write(&runtimeLock);
-
- return (Class)cls;
-}
-
-
-/***********************************************************************
-* objc_registerClassPair
-* fixme
-* Locking: acquires runtimeLock
-**********************************************************************/
-void objc_registerClassPair(Class cls_gen)
-{
- class_t *cls = newcls(cls_gen);
-
- rwlock_write(&runtimeLock);
-
- if ((cls->data->flags & RW_CONSTRUCTED) ||
- (cls->isa->data->flags & RW_CONSTRUCTED))
- {
- _objc_inform("objc_registerClassPair: class '%s' was already "
- "registered!", cls->data->ro->name);
- rwlock_unlock_write(&runtimeLock);
- return;
- }
-
- if (!(cls->data->flags & RW_CONSTRUCTING) ||
- !(cls->isa->data->flags & RW_CONSTRUCTING))
- {
- _objc_inform("objc_registerClassPair: class '%s' was not "
- "allocated with objc_allocateClassPair!",
- cls->data->ro->name);
- rwlock_unlock_write(&runtimeLock);
- return;
- }
-
- // Build ivar layouts
- if (UseGC) {
- struct class_t *supercls = getSuperclass(cls);
- class_ro_t *ro_w = (class_ro_t *)cls->data->ro;
-
- if (ro_w->ivarLayout) {
- // Class builder already called class_setIvarLayout.
- }
- else if (!supercls) {
- // Root class. Scan conservatively (should be isa ivar only).
- // ivar_layout is already NULL.
- }
- else if (ro_w->ivars == NULL) {
- // No local ivars. Use superclass's layouts.
- ro_w->ivarLayout = (unsigned char *)
- _strdup_internal((char *)supercls->data->ro->ivarLayout);
- }
- else {
- // Has local ivars. Build layouts based on superclass.
- layout_bitmap bitmap =
- layout_bitmap_create(supercls->data->ro->ivarLayout,
- instanceSize(supercls),
- instanceSize(cls), NO);
- uint32_t i;
- for (i = 0; i < ro_w->ivars->count; i++) {
- ivar_t *ivar = ivar_list_nth(ro_w->ivars, i);
- if (!ivar->offset) continue; // anonymous bitfield
-
- layout_bitmap_set_ivar(bitmap, ivar->type, *ivar->offset);
- }
- ro_w->ivarLayout = layout_string_create(bitmap);
- layout_bitmap_free(bitmap);
- }
-
- if (ro_w->weakIvarLayout) {
- // Class builder already called class_setWeakIvarLayout.
- }
- else if (!supercls) {
- // Root class. No weak ivars (should be isa ivar only).
- // weak_ivar_layout is already NULL.
- }
- else if (ro_w->ivars == NULL) {
- // No local ivars. Use superclass's layout.
- ro_w->weakIvarLayout = (unsigned char *)
- _strdup_internal((char *)supercls->data->ro->weakIvarLayout);
- }
- else {
- // Has local ivars. Build layout based on superclass.
- // No way to add weak ivars yet.
- ro_w->weakIvarLayout = (unsigned char *)
- _strdup_internal((char *)supercls->data->ro->weakIvarLayout);
- }
- }
-
- // Clear "under construction" bit, set "done constructing" bit
- cls->data->flags &= ~RW_CONSTRUCTING;
- cls->isa->data->flags &= ~RW_CONSTRUCTING;
- cls->data->flags |= RW_CONSTRUCTED;
- cls->isa->data->flags |= RW_CONSTRUCTED;
-
- // Add to realized and uninitialized classes
- addNamedClass(cls, cls->data->ro->name);
- addRealizedClass(cls);
- addRealizedMetaclass(cls->isa);
- addUninitializedClass(cls, cls->isa);
-
- rwlock_unlock_write(&runtimeLock);
-}
-
-
-static void unload_class(class_t *cls, BOOL isMeta)
-{
- // Detach class from various lists
-
- // categories not yet attached to this class
- category_list *cats;
- cats = unattachedCategoriesForClass(cls);
- if (cats) free(cats);
-
- // class tables and +load queue
- if (!isMeta) {
- removeNamedClass(cls, getName(cls));
- removeRealizedClass(cls);
- removeUninitializedClass(cls);
- } else {
- removeRealizedMetaclass(cls);
- }
-
- // superclass's subclass list
- if (isRealized(cls)) {
- class_t *supercls = getSuperclass(cls);
- if (supercls) removeSubclass(supercls, cls);
- }
-
-
- // Dispose the class's own data structures
-
- if (isRealized(cls)) {
- uint32_t i;
-
- // Dereferences the cache contents; do this before freeing methods
- if (cls->cache != (Cache)&_objc_empty_cache) _cache_free(cls->cache);
-
- if (cls->data->methods) {
- method_list_t **mlistp;
- for (mlistp = cls->data->methods; *mlistp; mlistp++) {
- for (i = 0; i < (**mlistp).count; i++) {
- method_t *m = method_list_nth(*mlistp, i);
- try_free(m->types);
- }
- try_free(*mlistp);
- }
- try_free(cls->data->methods);
- }
-
- const ivar_list_t *ilist = cls->data->ro->ivars;
- if (ilist) {
- for (i = 0; i < ilist->count; i++) {
- const ivar_t *ivar = ivar_list_nth(ilist, i);
- try_free(ivar->offset);
- try_free(ivar->name);
- try_free(ivar->type);
- }
- try_free(ilist);
- }
-
- protocol_list_t **plistp = cls->data->protocols;
- for (plistp = cls->data->protocols; plistp && *plistp; plistp++) {
- try_free(*plistp);
- }
- try_free(cls->data->protocols);
-
- // fixme:
- // properties
-
- if (cls->vtable != _objc_empty_vtable &&
- cls->data->flags & RW_SPECIALIZED_VTABLE) try_free(cls->vtable);
- try_free(cls->data->ro->ivarLayout);
- try_free(cls->data->ro->weakIvarLayout);
- try_free(cls->data->ro->name);
- try_free(cls->data->ro);
- try_free(cls->data);
- try_free(cls);
- }
-}
-
-void objc_disposeClassPair(Class cls_gen)
-{
- class_t *cls = newcls(cls_gen);
-
- rwlock_write(&runtimeLock);
-
- if (!(cls->data->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING)) ||
- !(cls->isa->data->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING)))
- {
- // class not allocated with objc_allocateClassPair
- // disposing still-unregistered class is OK!
- _objc_inform("objc_disposeClassPair: class '%s' was not "
- "allocated with objc_allocateClassPair!",
- cls->data->ro->name);
- rwlock_unlock_write(&runtimeLock);
- return;
- }
-
- if (isMetaClass(cls)) {
- _objc_inform("objc_disposeClassPair: class '%s' is a metaclass, "
- "not a class!", cls->data->ro->name);
- rwlock_unlock_write(&runtimeLock);
- return;
- }
-
- // Shouldn't have any live subclasses.
- if (cls->data->firstSubclass) {
- _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, "
- "including '%s'!", cls->data->ro->name,
- getName(cls->data->firstSubclass));
- }
- if (cls->isa->data->firstSubclass) {
- _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, "
- "including '%s'!", cls->data->ro->name,
- getName(cls->isa->data->firstSubclass));
- }
-
- // don't remove_class_from_loadable_list()
- // - it's not there and we don't have the lock
- unload_class(cls->isa, YES);
- unload_class(cls, NO);
-
- rwlock_unlock_write(&runtimeLock);
-}
-
-
-
-/***********************************************************************
-* class_createInstanceFromZone
-* fixme
-* Locking: none
-**********************************************************************/
-id
-class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
-{
- if (cls) assert(isRealized(newcls(cls)));
- return _internal_class_createInstanceFromZone(cls, extraBytes, zone);
-}
-
-
-/***********************************************************************
-* class_createInstance
-* fixme
-* Locking: none
-**********************************************************************/
-id
-class_createInstance(Class cls, size_t extraBytes)
-{
- return class_createInstanceFromZone(cls, extraBytes, NULL);
-}
-
-
-/***********************************************************************
-* object_copyFromZone
-* fixme
-* Locking: none
-**********************************************************************/
-id
-object_copyFromZone(id oldObj, size_t extraBytes, void *zone)
-{
- id obj;
- size_t size;
-
- if (!oldObj) return nil;
-
- size = _class_getInstanceSize(oldObj->isa) + extraBytes;
-#if !defined(NO_GC)
- if (UseGC) {
- obj = (id) auto_zone_allocate_object(gc_zone, size,
- AUTO_OBJECT_SCANNED, 0, 1);
- } else
-#endif
- if (zone) {
- obj = malloc_zone_calloc(zone, size, 1);
- } else {
- obj = (id) calloc(1, size);
- }
- if (!obj) return nil;
-
- // fixme this doesn't handle C++ ivars correctly (#4619414)
- objc_memmove_collectable(obj, oldObj, size);
-
-#if !defined(NO_GC)
- if (UseGC) gc_fixup_weakreferences(obj, oldObj);
-#endif
-
- return obj;
-}
-
-
-/***********************************************************************
-* object_copy
-* fixme
-* Locking: none
-**********************************************************************/
-id
-object_copy(id oldObj, size_t extraBytes)
-{
- return object_copyFromZone(oldObj, extraBytes, malloc_default_zone());
-}
-
-
-/***********************************************************************
-* object_dispose
-* fixme
-* Locking: none
-**********************************************************************/
-id
-object_dispose(id obj)
-{
- return _internal_object_dispose(obj);
-}
-
-
-/***********************************************************************
-* _objc_getFreedObjectClass
-* fixme
-* Locking: none
-**********************************************************************/
-Class _objc_getFreedObjectClass (void)
-{
- return nil;
-}
-
-#ifndef NO_FIXUP
-
-extern id objc_msgSend_fixup(id, SEL, ...);
-extern id objc_msgSend_fixedup(id, SEL, ...);
-extern id objc_msgSendSuper2_fixup(id, SEL, ...);
-extern id objc_msgSendSuper2_fixedup(id, SEL, ...);
-extern id objc_msgSend_stret_fixup(id, SEL, ...);
-extern id objc_msgSend_stret_fixedup(id, SEL, ...);
-extern id objc_msgSendSuper2_stret_fixup(id, SEL, ...);
-extern id objc_msgSendSuper2_stret_fixedup(id, SEL, ...);
-#if defined(__i386__) || defined(__x86_64__)
-extern id objc_msgSend_fpret_fixup(id, SEL, ...);
-extern id objc_msgSend_fpret_fixedup(id, SEL, ...);
-#endif
-#if defined(__x86_64__)
-extern id objc_msgSend_fp2ret_fixup(id, SEL, ...);
-extern id objc_msgSend_fp2ret_fixedup(id, SEL, ...);
-#endif
-
-/***********************************************************************
-* _objc_fixupMessageRef
-* Fixes up message ref *msg.
-* obj is the receiver. supr is NULL for non-super messages
-* Locking: acquires runtimeLock
-**********************************************************************/
-__private_extern__ IMP
-_objc_fixupMessageRef(id obj, struct objc_super2 *supr, message_ref *msg)
-{
- IMP imp;
- class_t *isa;
-
- rwlock_assert_unlocked(&runtimeLock);
-
- if (!supr) {
- // normal message - search obj->isa for the method implementation
- isa = (class_t *)obj->isa;
-
- if (!isRealized(isa)) {
- // obj is a class object, isa is its metaclass
- class_t *cls;
- rwlock_write(&runtimeLock);
- cls = realizeClass((class_t *)obj);
- rwlock_unlock_write(&runtimeLock);
-
- // shouldn't have instances of unrealized classes!
- assert(isMetaClass(isa));
- // shouldn't be relocating classes here!
- assert(cls == (class_t *)obj);
- }
- }
- else {
- // this is objc_msgSend_super, and supr->current_class->superclass
- // is the class to search for the method implementation
- assert(isRealized((class_t *)supr->current_class));
- isa = getSuperclass((class_t *)supr->current_class);
- }
-
- msg->sel = sel_registerName((const char *)msg->sel);
-
-#ifndef NO_VTABLE
- int vtableIndex;
- if (msg->imp == (IMP)&objc_msgSend_fixup &&
- (vtableIndex = vtable_getIndex(msg->sel)) >= 0)
- {
- // vtable dispatch
- msg->imp = vtableTrampolines[vtableIndex];
- imp = isa->vtable[vtableIndex];
- }
- else
-#endif
- {
- // ordinary dispatch
- imp = lookUpMethod((Class)isa, msg->sel, YES/*initialize*/, YES/*cache*/);
-
- if (msg->imp == (IMP)&objc_msgSend_fixup) {
- msg->imp = (IMP)&objc_msgSend_fixedup;
- }
- else if (msg->imp == (IMP)&objc_msgSendSuper2_fixup) {
- msg->imp = (IMP)&objc_msgSendSuper2_fixedup;
- }
- else if (msg->imp == (IMP)&objc_msgSend_stret_fixup) {
- msg->imp = (IMP)&objc_msgSend_stret_fixedup;
- }
- else if (msg->imp == (IMP)&objc_msgSendSuper2_stret_fixup) {
- msg->imp = (IMP)&objc_msgSendSuper2_stret_fixedup;
- }
-#if defined(__i386__) || defined(__x86_64__)
- else if (msg->imp == (IMP)&objc_msgSend_fpret_fixup) {
- msg->imp = (IMP)&objc_msgSend_fpret_fixedup;
- }
-#endif
-#if defined(__x86_64__)
- else if (msg->imp == (IMP)&objc_msgSend_fp2ret_fixup) {
- msg->imp = (IMP)&objc_msgSend_fp2ret_fixedup;
- }
-#endif
- else {
- // The ref may already have been fixed up, either by another thread
- // or by +initialize via lookUpMethod above.
- }
- }
-
- return imp;
-}
-
-// ! NO_FIXUP
-#endif
-
-
-#warning fixme delete after #4586306
-Class class_poseAs(Class imposter, Class original)
-{
- _objc_fatal("Don't call class_poseAs.");
-}
-
-
-// ProKit SPI
-static class_t *setSuperclass(class_t *cls, class_t *newSuper)
-{
- class_t *oldSuper;
-
- rwlock_assert_writing(&runtimeLock);
-
- oldSuper = cls->superclass;
- removeSubclass(oldSuper, cls);
- removeSubclass(oldSuper->isa, cls->isa);
-
- cls->superclass = newSuper;
- cls->isa->superclass = newSuper->isa;
- addSubclass(newSuper, cls);
- addSubclass(newSuper->isa, cls->isa);
-
- flushCaches(cls);
- flushCaches(cls->isa);
- flushVtables(cls);
- flushVtables(cls->isa);
-
- return oldSuper;
-}
-
-
-Class class_setSuperclass(Class cls_gen, Class newSuper_gen)
-{
- class_t *cls = newcls(cls_gen);
- class_t *newSuper = newcls(newSuper_gen);
- class_t *oldSuper;
-
- rwlock_write(&runtimeLock);
- oldSuper = setSuperclass(cls, newSuper);
- rwlock_unlock_write(&runtimeLock);
-
- return (Class)oldSuper;
-}
-
-#endif
--- /dev/null
+/*
+ * Copyright (c) 2005-2009 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/***********************************************************************
+* objc-runtime-new.m
+* Support for new-ABI classes and images.
+**********************************************************************/
+
+#if __OBJC2__
+
+#include "objc-private.h"
+#include "objc-runtime-new.h"
+#include "objc-file.h"
+#include <objc/message.h>
+#include <mach/shared_region.h>
+
+#define newcls(cls) ((class_t *)cls)
+#define newmethod(meth) ((method_t *)meth)
+#define newivar(ivar) ((ivar_t *)ivar)
+#define newcategory(cat) ((category_t *)cat)
+#define newprotocol(p) ((protocol_t *)p)
+#define newproperty(p) ((property_t *)p)
+
+static const char *getName(class_t *cls);
+static uint32_t unalignedInstanceSize(class_t *cls);
+static uint32_t alignedInstanceSize(class_t *cls);
+static BOOL isMetaClass(class_t *cls);
+static class_t *getSuperclass(class_t *cls);
+static void unload_class(class_t *cls, BOOL isMeta);
+static class_t *setSuperclass(class_t *cls, class_t *newSuper);
+static class_t *realizeClass(class_t *cls);
+static void flushCaches(class_t *cls);
+static void flushVtables(class_t *cls);
+static method_t *getMethodNoSuper_nolock(class_t *cls, SEL sel);
+static method_t *getMethod_nolock(class_t *cls, SEL sel);
+static void changeInfo(class_t *cls, unsigned int set, unsigned int clear);
+static IMP _method_getImplementation(method_t *m);
+static BOOL hasCxxStructors(class_t *cls);
+static IMP addMethod(class_t *cls, SEL name, IMP imp, const char *types, BOOL replace);
+static NXHashTable *realizedClasses(void);
+static BOOL isRRSelector(SEL sel);
+
+PRIVATE_EXTERN id objc_noop_imp(id self, SEL _cmd __unused) {
+ return self;
+}
+
+/***********************************************************************
+* Lock management
+* Every lock used anywhere must be managed here.
+* Locks not managed here may cause gdb deadlocks.
+**********************************************************************/
+PRIVATE_EXTERN rwlock_t runtimeLock = {0};
+PRIVATE_EXTERN rwlock_t selLock = {0};
+PRIVATE_EXTERN mutex_t cacheUpdateLock = MUTEX_INITIALIZER;
+PRIVATE_EXTERN recursive_mutex_t loadMethodLock = RECURSIVE_MUTEX_INITIALIZER;
+static int debugger_runtimeLock;
+static int debugger_selLock;
+static int debugger_cacheUpdateLock;
+static int debugger_loadMethodLock;
+#define RDONLY 1
+#define RDWR 2
+
+PRIVATE_EXTERN void lock_init(void)
+{
+ rwlock_init(&selLock);
+ rwlock_init(&runtimeLock);
+ recursive_mutex_init(&loadMethodLock);
+}
+
+
+/***********************************************************************
+* startDebuggerMode
+* Attempt to acquire some locks for debugger mode.
+* Returns 0 if debugger mode failed because too many locks are unavailable.
+*
+* Locks successfully acquired are held until endDebuggerMode().
+* Locks not acquired are off-limits until endDebuggerMode(); any
+* attempt to manipulate them will cause a trap.
+* Locks not handled here may cause deadlocks in gdb.
+**********************************************************************/
+PRIVATE_EXTERN int startDebuggerMode(void)
+{
+ int result = DEBUGGER_FULL;
+
+ // runtimeLock is required (can't do much without it)
+ if (rwlock_try_write(&runtimeLock)) {
+ debugger_runtimeLock = RDWR;
+ } else if (rwlock_try_read(&runtimeLock)) {
+ debugger_runtimeLock = RDONLY;
+ result = DEBUGGER_PARTIAL;
+ } else {
+ return DEBUGGER_OFF;
+ }
+
+ // cacheUpdateLock is required (must not fail a necessary cache flush)
+ // must be AFTER runtimeLock to avoid lock inversion
+ if (mutex_try_lock(&cacheUpdateLock)) {
+ debugger_cacheUpdateLock = RDWR;
+ } else {
+ rwlock_unlock(&runtimeLock, debugger_runtimeLock);
+ debugger_runtimeLock = 0;
+ return DEBUGGER_OFF;
+ }
+
+ // selLock is optional
+ if (rwlock_try_write(&selLock)) {
+ debugger_selLock = RDWR;
+ } else if (rwlock_try_read(&selLock)) {
+ debugger_selLock = RDONLY;
+ result = DEBUGGER_PARTIAL;
+ } else {
+ debugger_selLock = 0;
+ result = DEBUGGER_PARTIAL;
+ }
+
+ // loadMethodLock is optional
+ if (recursive_mutex_try_lock(&loadMethodLock)) {
+ debugger_loadMethodLock = RDWR;
+ } else {
+ debugger_loadMethodLock = 0;
+ result = DEBUGGER_PARTIAL;
+ }
+
+ return result;
+}
+
+/***********************************************************************
+* endDebuggerMode
+* Relinquish locks acquired in startDebuggerMode().
+**********************************************************************/
+PRIVATE_EXTERN void endDebuggerMode(void)
+{
+ assert(debugger_runtimeLock != 0);
+
+ rwlock_unlock(&runtimeLock, debugger_runtimeLock);
+ debugger_runtimeLock = 0;
+
+ rwlock_unlock(&selLock, debugger_selLock);
+ debugger_selLock = 0;
+
+ assert(debugger_cacheUpdateLock == RDWR);
+ mutex_unlock(&cacheUpdateLock);
+ debugger_cacheUpdateLock = 0;
+
+ if (debugger_loadMethodLock) {
+ recursive_mutex_unlock(&loadMethodLock);
+ debugger_loadMethodLock = 0;
+ }
+}
+
+/***********************************************************************
+* isManagedDuringDebugger
+* Returns YES if the given lock is handled specially during debugger
+* mode (i.e. debugger mode tries to acquire it).
+**********************************************************************/
+PRIVATE_EXTERN BOOL isManagedDuringDebugger(void *lock)
+{
+ if (lock == &selLock) return YES;
+ if (lock == &cacheUpdateLock) return YES;
+ if (lock == &runtimeLock) return YES;
+ if (lock == &loadMethodLock) return YES;
+ return NO;
+}
+
+/***********************************************************************
+* isLockedDuringDebugger
+* Returns YES if the given mutex was acquired by debugger mode.
+* Locking a managed mutex during debugger mode causes a trap unless
+* this returns YES.
+**********************************************************************/
+PRIVATE_EXTERN BOOL isLockedDuringDebugger(mutex_t *lock)
+{
+ assert(DebuggerMode);
+
+ if (lock == &cacheUpdateLock) return YES;
+ if (lock == (mutex_t *)&loadMethodLock) return YES;
+
+ return NO;
+}
+
+/***********************************************************************
+* isReadingDuringDebugger
+* Returns YES if the given rwlock was read-locked by debugger mode.
+* Read-locking a managed rwlock during debugger mode causes a trap unless
+* this returns YES.
+**********************************************************************/
+PRIVATE_EXTERN BOOL isReadingDuringDebugger(rwlock_t *lock)
+{
+ assert(DebuggerMode);
+
+ // read-lock is allowed even if debugger mode actually write-locked it
+ if (debugger_runtimeLock && lock == &runtimeLock) return YES;
+ if (debugger_selLock && lock == &selLock) return YES;
+
+ return NO;
+}
+
+/***********************************************************************
+* isWritingDuringDebugger
+* Returns YES if the given rwlock was write-locked by debugger mode.
+* Write-locking a managed rwlock during debugger mode causes a trap unless
+* this returns YES.
+**********************************************************************/
+PRIVATE_EXTERN BOOL isWritingDuringDebugger(rwlock_t *lock)
+{
+ assert(DebuggerMode);
+
+ if (debugger_runtimeLock == RDWR && lock == &runtimeLock) return YES;
+ if (debugger_selLock == RDWR && lock == &selLock) return YES;
+
+ return NO;
+}
+
+
+/***********************************************************************
+* vtable dispatch
+*
+* Every class gets a vtable pointer. The vtable is an array of IMPs.
+* The selectors represented in the vtable are the same for all classes
+* (i.e. no class has a bigger or smaller vtable).
+* Each vtable index has an associated trampoline which dispatches to
+* the IMP at that index for the receiver class's vtable (after
+* checking for NULL). Dispatch fixup uses these trampolines instead
+* of objc_msgSend.
+* Fragility: The vtable size and list of selectors is chosen at launch
+* time. No compiler-generated code depends on any particular vtable
+* configuration, or even the use of vtable dispatch at all.
+* Memory size: If a class's vtable is identical to its superclass's
+* (i.e. the class overrides none of the vtable selectors), then
+* the class points directly to its superclass's vtable. This means
+* selectors to be included in the vtable should be chosen so they are
+* (1) frequently called, but (2) not too frequently overridden. In
+* particular, -dealloc is a bad choice.
+* Forwarding: If a class doesn't implement some vtable selector, that
+* selector's IMP is set to objc_msgSend in that class's vtable.
+* +initialize: Each class keeps the default vtable (which always
+* redirects to objc_msgSend) until its +initialize is completed.
+* Otherwise, the first message to a class could be a vtable dispatch,
+* and the vtable trampoline doesn't include +initialize checking.
+* Changes: Categories, addMethod, and setImplementation all force vtable
+* reconstruction for the class and all of its subclasses, if the
+* vtable selectors are affected.
+**********************************************************************/
+
+/***********************************************************************
+* ABI WARNING ABI WARNING ABI WARNING ABI WARNING ABI WARNING
+* vtable_prototype on x86_64 steals %rax and does not clear %rdx on return
+* This means vtable dispatch must never be used for vararg calls
+* or very large return values.
+* ABI WARNING ABI WARNING ABI WARNING ABI WARNING ABI WARNING
+**********************************************************************/
+
+#define X8(x) \
+ x x x x x x x x
+#define X64(x) \
+ X8(x) X8(x) X8(x) X8(x) X8(x) X8(x) X8(x) X8(x)
+#define X128(x) \
+ X64(x) X64(x)
+
+#define vtableMax 128
+
+// hack to avoid conflicts with compiler's internal declaration
+asm("\n .data"
+ "\n .globl __objc_empty_vtable "
+ "\n __objc_empty_vtable:"
+#if __LP64__
+ X128("\n .quad _objc_msgSend")
+#else
+ X128("\n .long _objc_msgSend")
+#endif
+ );
+
+#if SUPPORT_VTABLE
+
+// Trampoline descriptors for gdb.
+
+objc_trampoline_header *gdb_objc_trampolines = NULL;
+
+void gdb_objc_trampolines_changed(objc_trampoline_header *thdr) __attribute__((noinline));
+void gdb_objc_trampolines_changed(objc_trampoline_header *thdr)
+{
+ rwlock_assert_writing(&runtimeLock);
+ assert(thdr == gdb_objc_trampolines);
+
+ if (PrintVtables) {
+ _objc_inform("VTABLES: gdb_objc_trampolines_changed(%p)", thdr);
+ }
+}
+
+// fixme workaround for rdar://6667753
+static void appendTrampolines(objc_trampoline_header *thdr) __attribute__((noinline));
+
+static void appendTrampolines(objc_trampoline_header *thdr)
+{
+ rwlock_assert_writing(&runtimeLock);
+ assert(thdr->next == NULL);
+
+ if (gdb_objc_trampolines != thdr->next) {
+ thdr->next = gdb_objc_trampolines;
+ }
+ gdb_objc_trampolines = thdr;
+
+ gdb_objc_trampolines_changed(thdr);
+}
+
+// Vtable management.
+
+static size_t vtableStrlen;
+static size_t vtableCount;
+static SEL *vtableSelectors;
+static IMP *vtableTrampolines;
+static const char * const defaultVtable[] = {
+ "allocWithZone:",
+ "alloc",
+ "class",
+ "self",
+ "isKindOfClass:",
+ "respondsToSelector:",
+ "isFlipped",
+ "length",
+ "objectForKey:",
+ "count",
+ "objectAtIndex:",
+ "isEqualToString:",
+ "isEqual:",
+ "retain",
+ "release",
+ "autorelease",
+};
+static const char * const defaultVtableGC[] = {
+ "allocWithZone:",
+ "alloc",
+ "class",
+ "self",
+ "isKindOfClass:",
+ "respondsToSelector:",
+ "isFlipped",
+ "length",
+ "objectForKey:",
+ "count",
+ "objectAtIndex:",
+ "isEqualToString:",
+ "isEqual:",
+ "hash",
+ "addObject:",
+ "countByEnumeratingWithState:objects:count:",
+};
+
+OBJC_EXTERN id objc_msgSend_vtable0(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable1(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable2(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable3(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable4(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable5(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable6(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable7(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable8(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable9(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable10(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable11(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable12(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable13(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable14(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_vtable15(id, SEL, ...);
+
+static IMP const defaultVtableTrampolines[] = {
+ objc_msgSend_vtable0,
+ objc_msgSend_vtable1,
+ objc_msgSend_vtable2,
+ objc_msgSend_vtable3,
+ objc_msgSend_vtable4,
+ objc_msgSend_vtable5,
+ objc_msgSend_vtable6,
+ objc_msgSend_vtable7,
+ objc_msgSend_vtable8,
+ objc_msgSend_vtable9,
+ objc_msgSend_vtable10,
+ objc_msgSend_vtable11,
+ objc_msgSend_vtable12,
+ objc_msgSend_vtable13,
+ objc_msgSend_vtable14,
+ objc_msgSend_vtable15,
+};
+extern objc_trampoline_header defaultVtableTrampolineDescriptors;
+
+static void check_vtable_size(void) __unused;
+static void check_vtable_size(void)
+{
+ // Fail to compile if vtable sizes don't match.
+ int c1[sizeof(defaultVtableTrampolines)-sizeof(defaultVtable)] __unused;
+ int c2[sizeof(defaultVtable)-sizeof(defaultVtableTrampolines)] __unused;
+ int c3[sizeof(defaultVtableTrampolines)-sizeof(defaultVtableGC)] __unused;
+ int c4[sizeof(defaultVtableGC)-sizeof(defaultVtableTrampolines)] __unused;
+
+ // Fail to compile if vtableMax is too small
+ int c5[vtableMax - sizeof(defaultVtable)] __unused;
+ int c6[vtableMax - sizeof(defaultVtableGC)] __unused;
+}
+
+
+extern uint8_t vtable_prototype;
+extern uint8_t vtable_ignored;
+extern int vtable_prototype_size;
+extern int vtable_prototype_index_offset;
+extern int vtable_prototype_index2_offset;
+extern int vtable_prototype_tagtable_offset;
+extern int vtable_prototype_tagtable_size;
+static size_t makeVtableTrampoline(uint8_t *dst, size_t index)
+{
+ // copy boilerplate
+ memcpy(dst, &vtable_prototype, vtable_prototype_size);
+
+ // insert indexes
+#if defined(__x86_64__)
+ if (index > 255) _objc_fatal("vtable_prototype busted");
+ {
+ // `jmpq *0x7fff(%rax)` ff a0 ff 7f
+ uint16_t *p = (uint16_t *)(dst + vtable_prototype_index_offset + 2);
+ if (*p != 0x7fff) _objc_fatal("vtable_prototype busted");
+ *p = index * 8;
+ }
+ {
+ uint16_t *p = (uint16_t *)(dst + vtable_prototype_index2_offset + 2);
+ if (*p != 0x7fff) _objc_fatal("vtable_prototype busted");
+ *p = index * 8;
+ }
+#else
+# warning unknown architecture
+#endif
+
+ // insert tagged isa table
+#if defined(__x86_64__)
+ {
+ // `movq $0x1122334455667788, %r10` 49 ba 88 77 66 55 44 33 22 11
+ if (vtable_prototype_tagtable_size != 10) {
+ _objc_fatal("vtable_prototype busted");
+ }
+ uint8_t *p = (uint8_t *)(dst + vtable_prototype_tagtable_offset);
+ if (*p++ != 0x49) _objc_fatal("vtable_prototype busted");
+ if (*p++ != 0xba) _objc_fatal("vtable_prototype busted");
+ if (*(uintptr_t *)p != 0x1122334455667788) {
+ _objc_fatal("vtable_prototype busted");
+ }
+ uintptr_t addr = (uintptr_t)_objc_tagged_isa_table;
+ memcpy(p, &addr, sizeof(addr));
+ }
+#else
+# warning unknown architecture
+#endif
+
+ return vtable_prototype_size;
+}
+
+
+static void initVtables(void)
+{
+ if (DisableVtables) {
+ if (PrintVtables) {
+ _objc_inform("VTABLES: vtable dispatch disabled by OBJC_DISABLE_VTABLES");
+ }
+ vtableCount = 0;
+ vtableSelectors = NULL;
+ vtableTrampolines = NULL;
+ return;
+ }
+
+ const char * const *names;
+ size_t i;
+
+ if (UseGC) {
+ names = defaultVtableGC;
+ vtableCount = sizeof(defaultVtableGC) / sizeof(defaultVtableGC[0]);
+ } else {
+ names = defaultVtable;
+ vtableCount = sizeof(defaultVtable) / sizeof(defaultVtable[0]);
+ }
+ if (vtableCount > vtableMax) vtableCount = vtableMax;
+
+ vtableSelectors = (SEL*)_malloc_internal(vtableCount * sizeof(SEL));
+ vtableTrampolines = (IMP*)_malloc_internal(vtableCount * sizeof(IMP));
+
+ // Built-in trampolines and their descriptors
+
+ size_t defaultVtableTrampolineCount =
+ sizeof(defaultVtableTrampolines) / sizeof(defaultVtableTrampolines[0]);
+#ifndef NDEBUG
+ // debug: use generated code for 3/4 of the table
+ // Disabled even in Debug builds to avoid breaking backtrace symbol names.
+ // defaultVtableTrampolineCount /= 4;
+#endif
+
+ for (i = 0; i < defaultVtableTrampolineCount && i < vtableCount; i++) {
+ vtableSelectors[i] = sel_registerName(names[i]);
+ vtableTrampolines[i] = defaultVtableTrampolines[i];
+ }
+ appendTrampolines(&defaultVtableTrampolineDescriptors);
+
+
+ // Generated trampolines and their descriptors
+
+ if (vtableCount > defaultVtableTrampolineCount) {
+ // Memory for trampoline code
+ size_t generatedCount =
+ vtableCount - defaultVtableTrampolineCount;
+
+ const int align = 16;
+ size_t codeSize =
+ round_page(sizeof(objc_trampoline_header) + align +
+ generatedCount * (sizeof(objc_trampoline_descriptor)
+ + vtable_prototype_size + align));
+ void *codeAddr = mmap(0, codeSize, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANON,
+ VM_MAKE_TAG(VM_MEMORY_OBJC_DISPATCHERS), 0);
+ uint8_t *t = (uint8_t *)codeAddr;
+
+ // Trampoline header
+ objc_trampoline_header *thdr = (objc_trampoline_header *)t;
+ thdr->headerSize = sizeof(objc_trampoline_header);
+ thdr->descSize = sizeof(objc_trampoline_descriptor);
+ thdr->descCount = (uint32_t)generatedCount;
+ thdr->next = NULL;
+
+ // Trampoline descriptors
+ objc_trampoline_descriptor *tdesc = (objc_trampoline_descriptor *)(thdr+1);
+ t = (uint8_t *)&tdesc[generatedCount];
+ t += align - ((uintptr_t)t % align);
+
+ // Dispatch code
+ size_t tdi;
+ for (i = defaultVtableTrampolineCount, tdi = 0;
+ i < vtableCount;
+ i++, tdi++)
+ {
+ vtableSelectors[i] = sel_registerName(names[i]);
+ if (ignoreSelector(vtableSelectors[i])) {
+ vtableTrampolines[i] = (IMP)&vtable_ignored;
+ tdesc[tdi].offset = 0;
+ tdesc[tdi].flags = 0;
+ } else {
+ vtableTrampolines[i] = (IMP)t;
+ tdesc[tdi].offset =
+ (uint32_t)((uintptr_t)t - (uintptr_t)&tdesc[tdi]);
+ tdesc[tdi].flags =
+ OBJC_TRAMPOLINE_MESSAGE|OBJC_TRAMPOLINE_VTABLE;
+
+ t += makeVtableTrampoline(t, i);
+ t += align - ((uintptr_t)t % align);
+ }
+ }
+
+ appendTrampolines(thdr);
+ sys_icache_invalidate(codeAddr, codeSize);
+ mprotect(codeAddr, codeSize, PROT_READ|PROT_EXEC);
+ }
+
+
+ if (PrintVtables) {
+ for (i = 0; i < vtableCount; i++) {
+ _objc_inform("VTABLES: vtable[%zu] %p %s",
+ i, vtableTrampolines[i],
+ sel_getName(vtableSelectors[i]));
+ }
+ }
+
+ if (PrintVtableImages) {
+ _objc_inform("VTABLE IMAGES: '#' implemented by class");
+ _objc_inform("VTABLE IMAGES: '-' inherited from superclass");
+ _objc_inform("VTABLE IMAGES: ' ' not implemented");
+ for (i = 0; i <= vtableCount; i++) {
+ char spaces[vtableCount+1+1];
+ size_t j;
+ for (j = 0; j < i; j++) {
+ spaces[j] = '|';
+ }
+ spaces[j] = '\0';
+ _objc_inform("VTABLE IMAGES: %s%s", spaces,
+ i<vtableCount ? sel_getName(vtableSelectors[i]) : "");
+ }
+ }
+
+ if (PrintVtables || PrintVtableImages) {
+ vtableStrlen = 0;
+ for (i = 0; i < vtableCount; i++) {
+ vtableStrlen += strlen(sel_getName(vtableSelectors[i]));
+ }
+ }
+}
+
+
+static int vtable_getIndex(SEL sel)
+{
+ unsigned int i;
+ for (i = 0; i < vtableCount; i++) {
+ if (vtableSelectors[i] == sel) return i;
+ }
+ return -1;
+}
+
+static BOOL vtable_containsSelector(SEL sel)
+{
+ return (vtable_getIndex(sel) < 0) ? NO : YES;
+}
+
+static void printVtableOverrides(class_t *cls, class_t *supercls)
+{
+ char overrideMap[vtableCount+1];
+ unsigned int i;
+
+ if (supercls) {
+ size_t overridesBufferSize = vtableStrlen + 2*vtableCount + 1;
+ char *overrides =
+ (char *)_calloc_internal(overridesBufferSize, 1);
+ for (i = 0; i < vtableCount; i++) {
+ if (ignoreSelector(vtableSelectors[i])) {
+ overrideMap[i] = '-';
+ continue;
+ }
+ if (getMethodNoSuper_nolock(cls, vtableSelectors[i])) {
+ strlcat(overrides, sel_getName(vtableSelectors[i]), overridesBufferSize);
+ strlcat(overrides, ", ", overridesBufferSize);
+ overrideMap[i] = '#';
+ } else if (getMethod_nolock(cls, vtableSelectors[i])) {
+ overrideMap[i] = '-';
+ } else {
+ overrideMap[i] = ' ';
+ }
+ }
+ if (PrintVtables) {
+ _objc_inform("VTABLES: %s%s implements %s",
+ getName(cls), isMetaClass(cls) ? "(meta)" : "",
+ overrides);
+ }
+ _free_internal(overrides);
+ }
+ else {
+ for (i = 0; i < vtableCount; i++) {
+ overrideMap[i] = '#';
+ }
+ }
+
+ if (PrintVtableImages) {
+ overrideMap[vtableCount] = '\0';
+ _objc_inform("VTABLE IMAGES: %s %s%s", overrideMap,
+ getName(cls), isMetaClass(cls) ? "(meta)" : "");
+ }
+}
+
+/***********************************************************************
+* updateVtable
+* Rebuilds vtable for cls, using superclass's vtable if appropriate.
+* Assumes superclass's vtable is up to date.
+* Does nothing to subclass vtables.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static void updateVtable(class_t *cls, BOOL force)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ // Keep default vtable until +initialize is complete.
+ // Default vtable redirects to objc_msgSend, which
+ // enforces +initialize locking.
+ if (!force && !_class_isInitialized((Class)cls)) {
+ /*
+ if (PrintVtables) {
+ _objc_inform("VTABLES: KEEPING DEFAULT vtable for "
+ "uninitialized class %s%s",
+ getName(cls), isMetaClass(cls) ? "(meta)" : "");
+ }
+ */
+ return;
+ }
+
+ // Decide whether this class can share its superclass's vtable.
+
+ class_t *supercls = getSuperclass(cls);
+ BOOL needVtable = NO;
+ unsigned int i;
+ if (!supercls) {
+ // Root classes always need a vtable
+ needVtable = YES;
+ }
+ else if (cls->data()->flags & RW_SPECIALIZED_VTABLE) {
+ // Once you have your own vtable, you never go back
+ needVtable = YES;
+ }
+ else {
+ for (i = 0; i < vtableCount; i++) {
+ if (ignoreSelector(vtableSelectors[i])) continue;
+ method_t *m = getMethodNoSuper_nolock(cls, vtableSelectors[i]);
+ // assume any local implementation differs from super's
+ if (m) {
+ needVtable = YES;
+ break;
+ }
+ }
+ }
+
+ // Build a vtable for this class, or not.
+
+ if (!needVtable) {
+ if (PrintVtables) {
+ _objc_inform("VTABLES: USING SUPERCLASS vtable for class %s%s",
+ getName(cls), isMetaClass(cls) ? "(meta)" : "");
+ }
+ cls->vtable = supercls->vtable;
+ }
+ else {
+ if (PrintVtables) {
+ _objc_inform("VTABLES: %s vtable for class %s%s",
+ (cls->data()->flags & RW_SPECIALIZED_VTABLE) ?
+ "UPDATING SPECIALIZED" : "CREATING SPECIALIZED",
+ getName(cls), isMetaClass(cls) ? "(meta)" : "");
+ }
+ if (PrintVtables || PrintVtableImages) {
+ printVtableOverrides(cls, supercls);
+ }
+
+ IMP *new_vtable;
+ IMP *super_vtable = supercls ? supercls->vtable : &_objc_empty_vtable;
+ // fixme use msgForward (instead of msgSend from empty vtable) ?
+
+ if (cls->data()->flags & RW_SPECIALIZED_VTABLE) {
+ // update cls->vtable in place
+ new_vtable = cls->vtable;
+ assert(new_vtable != &_objc_empty_vtable);
+ } else {
+ // make new vtable
+ new_vtable = (IMP*)malloc(vtableCount * sizeof(IMP));
+ changeInfo(cls, RW_SPECIALIZED_VTABLE, 0);
+ }
+
+ for (i = 0; i < vtableCount; i++) {
+ if (ignoreSelector(vtableSelectors[i])) {
+ new_vtable[i] = (IMP)&vtable_ignored;
+ } else {
+ method_t *m = getMethodNoSuper_nolock(cls, vtableSelectors[i]);
+ if (m) new_vtable[i] = _method_getImplementation(m);
+ else new_vtable[i] = super_vtable[i];
+ }
+ }
+
+ if (cls->vtable != new_vtable) {
+ // don't let other threads see uninitialized parts of new_vtable
+ OSMemoryBarrier();
+ cls->vtable = new_vtable;
+ }
+ }
+}
+
+// SUPPORT_VTABLE
+#else
+// !SUPPORT_VTABLE
+
+static void initVtables(void)
+{
+ if (PrintVtables) {
+ _objc_inform("VTABLES: no vtables on this architecture");
+ }
+}
+
+static BOOL vtable_containsSelector(SEL sel)
+{
+ return NO;
+}
+
+static void updateVtable(class_t *cls, BOOL force)
+{
+}
+
+// !SUPPORT_VTABLE
+#endif
+
+typedef struct {
+ category_t *cat;
+ BOOL fromBundle;
+} category_pair_t;
+
+typedef struct {
+ uint32_t count;
+ category_pair_t list[0]; // variable-size
+} category_list;
+
+#define FOREACH_METHOD_LIST(_mlist, _cls, code) \
+ do { \
+ const method_list_t *_mlist; \
+ if (_cls->data()->methods) { \
+ method_list_t **_mlistp; \
+ for (_mlistp = _cls->data()->methods; *_mlistp; _mlistp++) { \
+ _mlist = *_mlistp; \
+ code \
+ } \
+ } \
+ } while (0)
+
+#define FOREACH_REALIZED_CLASS_AND_SUBCLASS(_c, _cls, code) \
+ do { \
+ rwlock_assert_writing(&runtimeLock); \
+ class_t *_top = _cls; \
+ class_t *_c = _top; \
+ if (_c) { \
+ while (1) { \
+ code \
+ if (_c->data()->firstSubclass) { \
+ _c = _c->data()->firstSubclass; \
+ } else { \
+ while (!_c->data()->nextSiblingClass && _c != _top) { \
+ _c = getSuperclass(_c); \
+ } \
+ if (_c == _top) break; \
+ _c = _c->data()->nextSiblingClass; \
+ } \
+ } \
+ } else { \
+ /* nil means all realized classes */ \
+ NXHashTable *_classes = realizedClasses(); \
+ NXHashTable *_metaclasses = realizedMetaclasses(); \
+ NXHashState _state; \
+ _state = NXInitHashState(_classes); \
+ while (NXNextHashState(_classes, &_state, (void**)&_c)) \
+ { \
+ code \
+ } \
+ _state = NXInitHashState(_metaclasses); \
+ while (NXNextHashState(_metaclasses, &_state, (void**)&_c)) \
+ { \
+ code \
+ } \
+ } \
+ } while (0)
+
+
+/*
+ Low two bits of mlist->entsize is used as the fixed-up marker.
+ PREOPTIMIZED VERSION:
+ Fixed-up method lists get entsize&3 == 3.
+ dyld shared cache sets this for method lists it preoptimizes.
+ UN-PREOPTIMIZED VERSION:
+ Fixed-up method lists get entsize&3 == 1.
+ dyld shared cache uses 3, but those aren't trusted.
+*/
+
+static uint32_t fixed_up_method_list = 3;
+
+PRIVATE_EXTERN void
+disableSharedCacheOptimizations(void)
+{
+ fixed_up_method_list = 1;
+}
+
+static BOOL isMethodListFixedUp(const method_list_t *mlist)
+{
+ return (mlist->entsize_NEVER_USE & 3) == fixed_up_method_list;
+}
+
+static void setMethodListFixedUp(method_list_t *mlist)
+{
+ rwlock_assert_writing(&runtimeLock);
+ assert(!isMethodListFixedUp(mlist));
+ mlist->entsize_NEVER_USE = (mlist->entsize_NEVER_USE & ~3) | fixed_up_method_list;
+}
+
+/*
+static size_t chained_property_list_size(const chained_property_list *plist)
+{
+ return sizeof(chained_property_list) +
+ plist->count * sizeof(property_t);
+}
+*/
+
+static size_t protocol_list_size(const protocol_list_t *plist)
+{
+ return sizeof(protocol_list_t) + plist->count * sizeof(protocol_t *);
+}
+
+
+// low bit used by dyld shared cache
+static uint32_t method_list_entsize(const method_list_t *mlist)
+{
+ return mlist->entsize_NEVER_USE & ~(uint32_t)3;
+}
+
+static size_t method_list_size(const method_list_t *mlist)
+{
+ return sizeof(method_list_t) + (mlist->count-1)*method_list_entsize(mlist);
+}
+
+static method_t *method_list_nth(const method_list_t *mlist, uint32_t i)
+{
+ return (method_t *)(i*method_list_entsize(mlist) + (char *)&mlist->first);
+}
+
+
+static size_t ivar_list_size(const ivar_list_t *ilist)
+{
+ return sizeof(ivar_list_t) + (ilist->count-1) * ilist->entsize;
+}
+
+static ivar_t *ivar_list_nth(const ivar_list_t *ilist, uint32_t i)
+{
+ return (ivar_t *)(i*ilist->entsize + (char *)&ilist->first);
+}
+
+
+// part of ivar_t, with non-deprecated alignment
+typedef struct {
+ uintptr_t *offset;
+ const char *name;
+ const char *type;
+ uint32_t alignment;
+} ivar_alignment_t;
+
+static uint32_t ivar_alignment(const ivar_t *ivar)
+{
+ uint32_t alignment = ((ivar_alignment_t *)ivar)->alignment;
+ if (alignment == (uint32_t)-1) alignment = (uint32_t)WORD_SHIFT;
+ return 1<<alignment;
+}
+
+
+static method_list_t *cat_method_list(const category_t *cat, BOOL isMeta)
+{
+ if (!cat) return NULL;
+
+ if (isMeta) return cat->classMethods;
+ else return cat->instanceMethods;
+}
+
+static uint32_t cat_method_count(const category_t *cat, BOOL isMeta)
+{
+ method_list_t *cmlist = cat_method_list(cat, isMeta);
+ return cmlist ? cmlist->count : 0;
+}
+
+static method_t *cat_method_nth(const category_t *cat, BOOL isMeta, uint32_t i)
+{
+ method_list_t *cmlist = cat_method_list(cat, isMeta);
+ if (!cmlist) return NULL;
+
+ return method_list_nth(cmlist, i);
+}
+
+
+static property_t *
+property_list_nth(const property_list_t *plist, uint32_t i)
+{
+ return (property_t *)(i*plist->entsize + (char *)&plist->first);
+}
+
+// fixme don't chain property lists
+typedef struct chained_property_list {
+ struct chained_property_list *next;
+ uint32_t count;
+ property_t list[0]; // variable-size
+} chained_property_list;
+
+
+static void try_free(const void *p)
+{
+ if (p && malloc_size(p)) free((void *)p);
+}
+
+
+/***********************************************************************
+* make_ro_writeable
+* Reallocates rw->ro if necessary to make it writeable.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static class_ro_t *make_ro_writeable(class_rw_t *rw)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ if (rw->flags & RW_COPIED_RO) {
+ // already writeable, do nothing
+ } else {
+ class_ro_t *ro = (class_ro_t *)
+ _memdup_internal(rw->ro, sizeof(*rw->ro));
+ rw->ro = ro;
+ rw->flags |= RW_COPIED_RO;
+ }
+ return (class_ro_t *)rw->ro;
+}
+
+
+/***********************************************************************
+* unattachedCategories
+* Returns the class => categories map of unattached categories.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static NXMapTable *unattachedCategories(void)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ static NXMapTable *category_map = NULL;
+
+ if (category_map) return category_map;
+
+ // fixme initial map size
+ category_map = NXCreateMapTableFromZone(NXPtrValueMapPrototype, 16,
+ _objc_internal_zone());
+
+ return category_map;
+}
+
+
+/***********************************************************************
+* addUnattachedCategoryForClass
+* Records an unattached category.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static void addUnattachedCategoryForClass(category_t *cat, class_t *cls,
+ header_info *catHeader)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ BOOL catFromBundle = (catHeader->mhdr->filetype == MH_BUNDLE) ? YES: NO;
+
+ // DO NOT use cat->cls!
+ // cls may be cat->cls->isa, or cat->cls may have been remapped.
+ NXMapTable *cats = unattachedCategories();
+ category_list *list;
+
+ list = (category_list *)NXMapGet(cats, cls);
+ if (!list) {
+ list = (category_list *)
+ _calloc_internal(sizeof(*list) + sizeof(list->list[0]), 1);
+ } else {
+ list = (category_list *)
+ _realloc_internal(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1));
+ }
+ list->list[list->count++] = (category_pair_t){cat, catFromBundle};
+ NXMapInsert(cats, cls, list);
+}
+
+
+/***********************************************************************
+* removeUnattachedCategoryForClass
+* Removes an unattached category.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static void removeUnattachedCategoryForClass(category_t *cat, class_t *cls)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ // DO NOT use cat->cls!
+ // cls may be cat->cls->isa, or cat->cls may have been remapped.
+ NXMapTable *cats = unattachedCategories();
+ category_list *list;
+
+ list = (category_list *)NXMapGet(cats, cls);
+ if (!list) return;
+
+ uint32_t i;
+ for (i = 0; i < list->count; i++) {
+ if (list->list[i].cat == cat) {
+ // shift entries to preserve list order
+ memmove(&list->list[i], &list->list[i+1],
+ (list->count-i-1) * sizeof(list->list[i]));
+ list->count--;
+ return;
+ }
+ }
+}
+
+
+/***********************************************************************
+* unattachedCategoriesForClass
+* Returns the list of unattached categories for a class, and
+* deletes them from the list.
+* The result must be freed by the caller.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static category_list *unattachedCategoriesForClass(class_t *cls)
+{
+ rwlock_assert_writing(&runtimeLock);
+ return (category_list *)NXMapRemove(unattachedCategories(), cls);
+}
+
+
+/***********************************************************************
+* isRealized
+* Returns YES if class cls has been realized.
+* Locking: To prevent concurrent realization, hold runtimeLock.
+**********************************************************************/
+static BOOL isRealized(class_t *cls)
+{
+ return (cls->data()->flags & RW_REALIZED) ? YES : NO;
+}
+
+
+/***********************************************************************
+* isFuture
+* Returns YES if class cls is an unrealized future class.
+* Locking: To prevent concurrent realization, hold runtimeLock.
+**********************************************************************/
+#ifndef NDEBUG
+// currently used in asserts only
+static BOOL isFuture(class_t *cls)
+{
+ return (cls->data()->flags & RW_FUTURE) ? YES : NO;
+}
+#endif
+
+
+/***********************************************************************
+* printReplacements
+* Implementation of PrintReplacedMethods / OBJC_PRINT_REPLACED_METHODS.
+* Warn about methods from cats that override other methods in cats or cls.
+* Assumes no methods from cats have been added to cls yet.
+**********************************************************************/
+static void printReplacements(class_t *cls, category_list *cats)
+{
+ uint32_t c;
+ BOOL isMeta = isMetaClass(cls);
+
+ if (!cats) return;
+
+ // Newest categories are LAST in cats
+ // Later categories override earlier ones.
+ for (c = 0; c < cats->count; c++) {
+ category_t *cat = cats->list[c].cat;
+ uint32_t cmCount = cat_method_count(cat, isMeta);
+ uint32_t m;
+ for (m = 0; m < cmCount; m++) {
+ uint32_t c2, m2;
+ method_t *meth2 = NULL;
+ method_t *meth = cat_method_nth(cat, isMeta, m);
+ SEL s = sel_registerName((const char *)meth->name);
+
+ // Don't warn about GC-ignored selectors
+ if (ignoreSelector(s)) continue;
+
+ // Look for method in earlier categories
+ for (c2 = 0; c2 < c; c2++) {
+ category_t *cat2 = cats->list[c2].cat;
+ uint32_t cm2Count = cat_method_count(cat2, isMeta);
+ for (m2 = 0; m2 < cm2Count; m2++) {
+ meth2 = cat_method_nth(cat2, isMeta, m2);
+ SEL s2 = sel_registerName((const char *)meth2->name);
+ if (s == s2) goto whine;
+ }
+ }
+
+ // Look for method in cls
+ FOREACH_METHOD_LIST(mlist, cls, {
+ for (m2 = 0; m2 < mlist->count; m2++) {
+ meth2 = method_list_nth(mlist, m2);
+ SEL s2 = sel_registerName((const char *)meth2->name);
+ if (s == s2) goto whine;
+ }
+ });
+
+ // Didn't find any override.
+ continue;
+
+ whine:
+ // Found an override.
+ logReplacedMethod(getName(cls), s, isMetaClass(cls), cat->name,
+ _method_getImplementation(meth2),
+ _method_getImplementation(meth));
+ }
+ }
+}
+
+
+static BOOL isBundleClass(class_t *cls)
+{
+ return (cls->data()->ro->flags & RO_FROM_BUNDLE) ? YES : NO;
+}
+
+
+static method_list_t *
+fixupMethodList(method_list_t *mlist, BOOL bundleCopy)
+{
+ assert(!isMethodListFixedUp(mlist));
+
+ mlist = (method_list_t *)
+ _memdup_internal(mlist, method_list_size(mlist));
+
+ // fixme lock less in attachMethodLists ?
+ sel_lock();
+
+ // Unique selectors in list.
+ uint32_t m;
+ for (m = 0; m < mlist->count; m++) {
+ method_t *meth = method_list_nth(mlist, m);
+ SEL sel = sel_registerNameNoLock((const char *)meth->name, bundleCopy);
+ meth->name = sel;
+
+ if (ignoreSelector(sel)) {
+ meth->imp = (IMP)&_objc_ignored_method;
+ }
+ }
+
+ sel_unlock();
+
+ // Sort by selector address.
+ method_t::SortBySELAddress sorter;
+ std::stable_sort(mlist->begin(), mlist->end(), sorter);
+
+ // Mark method list as uniqued and sorted
+ setMethodListFixedUp(mlist);
+
+ return mlist;
+}
+
+
+static void
+attachMethodLists(class_t *cls, method_list_t **addedLists, int addedCount,
+ BOOL methodsFromBundle, BOOL *inoutVtablesAffected)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ // Don't scan redundantly
+ BOOL scanForCustomRR = !UseGC && !cls->hasCustomRR();
+
+ // Method list array is NULL-terminated.
+ // Some elements of lists are NULL; we must filter them out.
+
+ method_list_t **oldLists = cls->data()->methods;
+ int oldCount = 0;
+ if (oldLists) {
+ while (oldLists[oldCount]) oldCount++;
+ }
+
+ int newCount = oldCount + 1; // including NULL terminator
+ for (int i = 0; i < addedCount; i++) {
+ if (addedLists[i]) newCount++; // only non-NULL entries get added
+ }
+
+ method_list_t **newLists = (method_list_t **)
+ _malloc_internal(newCount * sizeof(*newLists));
+
+ // Add method lists to array.
+ // Reallocate un-fixed method lists.
+ // The new methods are PREPENDED to the method list array.
+
+ newCount = 0;
+ int i;
+ for (i = 0; i < addedCount; i++) {
+ method_list_t *mlist = addedLists[i];
+ if (!mlist) continue;
+
+ // Fixup selectors if necessary
+ if (!isMethodListFixedUp(mlist)) {
+ mlist = fixupMethodList(mlist, methodsFromBundle);
+ }
+
+ // Scan for vtable updates
+ if (inoutVtablesAffected && !*inoutVtablesAffected) {
+ uint32_t m;
+ for (m = 0; m < mlist->count; m++) {
+ SEL sel = method_list_nth(mlist, m)->name;
+ if (vtable_containsSelector(sel)) {
+ *inoutVtablesAffected = YES;
+ break;
+ }
+ }
+ }
+
+ // Scan for method implementations tracked by the class's flags
+ if (scanForCustomRR) {
+ uint32_t m;
+ for (m = 0; m < mlist->count; m++) {
+ SEL sel = method_list_nth(mlist, m)->name;
+ if (isRRSelector(sel)) {
+ cls->setHasCustomRR();
+ scanForCustomRR = NO;
+ break;
+ }
+ }
+ }
+
+ // Fill method list array
+ newLists[newCount++] = mlist;
+ }
+
+ // Copy old methods to the method list array
+ for (i = 0; i < oldCount; i++) {
+ newLists[newCount++] = oldLists[i];
+ }
+ if (oldLists) free(oldLists);
+
+ // NULL-terminate
+ newLists[newCount++] = NULL;
+ cls->data()->methods = newLists;
+}
+
+static void
+attachCategoryMethods(class_t *cls, category_list *cats,
+ BOOL *inoutVtablesAffected)
+{
+ if (!cats) return;
+ if (PrintReplacedMethods) printReplacements(cls, cats);
+
+ BOOL isMeta = isMetaClass(cls);
+ method_list_t **mlists = (method_list_t **)
+ _malloc_internal(cats->count * sizeof(*mlists));
+
+ // Count backwards through cats to get newest categories first
+ int mcount = 0;
+ int i = cats->count;
+ BOOL fromBundle = NO;
+ while (i--) {
+ method_list_t *mlist = cat_method_list(cats->list[i].cat, isMeta);
+ if (mlist) {
+ mlists[mcount++] = mlist;
+ fromBundle |= cats->list[i].fromBundle;
+ }
+ }
+
+ attachMethodLists(cls, mlists, mcount, fromBundle, inoutVtablesAffected);
+
+ _free_internal(mlists);
+
+}
+
+
+static chained_property_list *
+buildPropertyList(const property_list_t *plist, category_list *cats, BOOL isMeta)
+{
+ // Do NOT use cat->cls! It may have been remapped.
+ chained_property_list *newlist;
+ uint32_t count = 0;
+ uint32_t p, c;
+
+ // Count properties in all lists.
+ if (plist) count = plist->count;
+ if (cats) {
+ for (c = 0; c < cats->count; c++) {
+ category_t *cat = cats->list[c].cat;
+ /*
+ if (isMeta && cat->classProperties) {
+ count += cat->classProperties->count;
+ }
+ else*/
+ if (!isMeta && cat->instanceProperties) {
+ count += cat->instanceProperties->count;
+ }
+ }
+ }
+
+ if (count == 0) return NULL;
+
+ // Allocate new list.
+ newlist = (chained_property_list *)
+ _malloc_internal(sizeof(*newlist) + count * sizeof(property_t));
+ newlist->count = 0;
+ newlist->next = NULL;
+
+ // Copy properties; newest categories first, then ordinary properties
+ if (cats) {
+ c = cats->count;
+ while (c--) {
+ property_list_t *cplist;
+ category_t *cat = cats->list[c].cat;
+ /*
+ if (isMeta) {
+ cplist = cat->classProperties;
+ } else */
+ {
+ cplist = cat->instanceProperties;
+ }
+ if (cplist) {
+ for (p = 0; p < cplist->count; p++) {
+ newlist->list[newlist->count++] =
+ *property_list_nth(cplist, p);
+ }
+ }
+ }
+ }
+ if (plist) {
+ for (p = 0; p < plist->count; p++) {
+ newlist->list[newlist->count++] = *property_list_nth(plist, p);
+ }
+ }
+
+ assert(newlist->count == count);
+
+ return newlist;
+}
+
+
+static const protocol_list_t **
+buildProtocolList(category_list *cats, const protocol_list_t *base,
+ const protocol_list_t **protos)
+{
+ // Do NOT use cat->cls! It may have been remapped.
+ const protocol_list_t **p, **newp;
+ const protocol_list_t **newprotos;
+ unsigned int count = 0;
+ unsigned int i;
+
+ // count protocol list in base
+ if (base) count++;
+
+ // count protocol lists in cats
+ if (cats) for (i = 0; i < cats->count; i++) {
+ category_t *cat = cats->list[i].cat;
+ if (cat->protocols) count++;
+ }
+
+ // no base or category protocols? return existing protocols unchanged
+ if (count == 0) return protos;
+
+ // count protocol lists in protos
+ for (p = protos; p && *p; p++) {
+ count++;
+ }
+
+ if (count == 0) return NULL;
+
+ newprotos = (const protocol_list_t **)
+ _malloc_internal((count+1) * sizeof(protocol_list_t *));
+ newp = newprotos;
+
+ if (base) {
+ *newp++ = base;
+ }
+
+ for (p = protos; p && *p; p++) {
+ *newp++ = *p;
+ }
+
+ if (cats) for (i = 0; i < cats->count; i++) {
+ category_t *cat = cats->list[i].cat;
+ if (cat->protocols) {
+ *newp++ = cat->protocols;
+ }
+ }
+
+ *newp = NULL;
+
+ return newprotos;
+}
+
+
+/***********************************************************************
+* methodizeClass
+* Fixes up cls's method list, protocol list, and property list.
+* Attaches any outstanding categories.
+* Builds vtable.
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static void methodizeClass(class_t *cls)
+{
+ category_list *cats;
+ BOOL isMeta;
+
+ rwlock_assert_writing(&runtimeLock);
+
+ isMeta = isMetaClass(cls);
+
+ // Methodizing for the first time
+ if (PrintConnecting) {
+ _objc_inform("CLASS: methodizing class '%s' %s",
+ getName(cls), isMeta ? "(meta)" : "");
+ }
+
+ // Build method and protocol and property lists.
+ // Include methods and protocols and properties from categories, if any
+ // Do NOT use cat->cls! It may have been remapped.
+
+ attachMethodLists(cls, (method_list_t **)&cls->data()->ro->baseMethods, 1,
+ isBundleClass(cls), NULL);
+
+ // Root classes get bonus method implementations if they don't have
+ // them already. These apply before category replacements.
+
+ if (cls->isRootClass()) {
+ // root class
+ if (!UseGC) {
+ // Assume custom RR except NSObject, even without MM method imps.
+ if (0 != strcmp(getName(cls), "NSObject")) cls->setHasCustomRR();
+ }
+ }
+ else if (cls->isRootMetaclass()) {
+ // root metaclass
+ addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
+ if (!UseGC) {
+ // Assume custom RR always.
+ cls->setHasCustomRR();
+ }
+ }
+
+ cats = unattachedCategoriesForClass(cls);
+ attachCategoryMethods(cls, cats, NULL);
+
+ if (cats || cls->data()->ro->baseProperties) {
+ cls->data()->properties =
+ buildPropertyList(cls->data()->ro->baseProperties, cats, isMeta);
+ }
+
+ if (cats || cls->data()->ro->baseProtocols) {
+ cls->data()->protocols =
+ buildProtocolList(cats, cls->data()->ro->baseProtocols, NULL);
+ }
+
+ if (PrintConnecting) {
+ uint32_t i;
+ if (cats) {
+ for (i = 0; i < cats->count; i++) {
+ _objc_inform("CLASS: attached category %c%s(%s)",
+ isMeta ? '+' : '-',
+ getName(cls), cats->list[i].cat->name);
+ }
+ }
+ }
+
+ if (cats) _free_internal(cats);
+
+ // No vtable until +initialize completes
+ assert(cls->vtable == &_objc_empty_vtable);
+
+#ifndef NDEBUG
+ // Debug: sanity-check all SELs; log method list contents
+ FOREACH_METHOD_LIST(mlist, cls, {
+ method_list_t::method_iterator iter = mlist->begin();
+ method_list_t::method_iterator end = mlist->end();
+ for ( ; iter != end; ++iter) {
+ if (PrintConnecting) {
+ _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
+ getName(cls), sel_getName(iter->name));
+ }
+ assert(ignoreSelector(iter->name) || sel_registerName(sel_getName(iter->name))==iter->name);
+ }
+ });
+#endif
+}
+
+
+/***********************************************************************
+* remethodizeClass
+* Attach outstanding categories to an existing class.
+* Fixes up cls's method list, protocol list, and property list.
+* Updates method caches and vtables for cls and its subclasses.
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static void remethodizeClass(class_t *cls)
+{
+ category_list *cats;
+ BOOL isMeta;
+
+ rwlock_assert_writing(&runtimeLock);
+
+ isMeta = isMetaClass(cls);
+
+ // Re-methodizing: check for more categories
+ if ((cats = unattachedCategoriesForClass(cls))) {
+ chained_property_list *newproperties;
+ const protocol_list_t **newprotos;
+
+ if (PrintConnecting) {
+ _objc_inform("CLASS: attaching categories to class '%s' %s",
+ getName(cls), isMeta ? "(meta)" : "");
+ }
+
+ // Update methods, properties, protocols
+
+ BOOL vtableAffected = NO;
+ attachCategoryMethods(cls, cats, &vtableAffected);
+
+ newproperties = buildPropertyList(NULL, cats, isMeta);
+ if (newproperties) {
+ newproperties->next = cls->data()->properties;
+ cls->data()->properties = newproperties;
+ }
+
+ newprotos = buildProtocolList(cats, NULL, cls->data()->protocols);
+ if (cls->data()->protocols && cls->data()->protocols != newprotos) {
+ _free_internal(cls->data()->protocols);
+ }
+ cls->data()->protocols = newprotos;
+
+ _free_internal(cats);
+
+ // Update method caches and vtables
+ flushCaches(cls);
+ if (vtableAffected) flushVtables(cls);
+ }
+}
+
+
+/***********************************************************************
+* changeInfo
+* Atomically sets and clears some bits in cls's info field.
+* set and clear must not overlap.
+**********************************************************************/
+static void changeInfo(class_t *cls, unsigned int set, unsigned int clear)
+{
+ uint32_t oldf, newf;
+
+ assert(isFuture(cls) || isRealized(cls));
+
+ do {
+ oldf = cls->data()->flags;
+ newf = (oldf | set) & ~clear;
+ } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&cls->data()->flags));
+}
+
+
+/***********************************************************************
+* namedClasses
+* Returns the classname => class map of all non-meta classes.
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+
+NXMapTable *gdb_objc_realized_classes; // exported for debuggers in objc-gdb.h
+
+static NXMapTable *namedClasses(void)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ // allocated in _read_images
+ assert(gdb_objc_realized_classes);
+
+ return gdb_objc_realized_classes;
+}
+
+
+/***********************************************************************
+* addNamedClass
+* Adds name => cls to the named non-meta class map.
+* Warns about duplicate class names and keeps the old mapping.
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static void addNamedClass(class_t *cls, const char *name)
+{
+ rwlock_assert_writing(&runtimeLock);
+ class_t *old;
+ if ((old = (class_t *)NXMapGet(namedClasses(), name))) {
+ inform_duplicate(name, (Class)old, (Class)cls);
+ } else {
+ NXMapInsert(namedClasses(), name, cls);
+ }
+ assert(!(cls->data()->flags & RO_META));
+
+ // wrong: constructed classes are already realized when they get here
+ // assert(!isRealized(cls));
+}
+
+
+/***********************************************************************
+* removeNamedClass
+* Removes cls from the name => cls map.
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static void removeNamedClass(class_t *cls, const char *name)
+{
+ rwlock_assert_writing(&runtimeLock);
+ assert(!(cls->data()->flags & RO_META));
+ if (cls == NXMapGet(namedClasses(), name)) {
+ NXMapRemove(namedClasses(), name);
+ } else {
+ // cls has a name collision with another class - don't remove the other
+ }
+}
+
+
+/***********************************************************************
+* realizedClasses
+* Returns the class list for realized non-meta classes.
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+static NXHashTable *realized_class_hash = NULL;
+
+static NXHashTable *realizedClasses(void)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ // allocated in _read_images
+ assert(realized_class_hash);
+
+ return realized_class_hash;
+}
+
+
+/***********************************************************************
+* realizedMetaclasses
+* Returns the class list for realized metaclasses.
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+static NXHashTable *realized_metaclass_hash = NULL;
+static NXHashTable *realizedMetaclasses(void)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ // allocated in _read_images
+ assert(realized_metaclass_hash);
+
+ return realized_metaclass_hash;
+}
+
+
+/***********************************************************************
+* addRealizedClass
+* Adds cls to the realized non-meta class hash.
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static void addRealizedClass(class_t *cls)
+{
+ rwlock_assert_writing(&runtimeLock);
+ void *old;
+ old = NXHashInsert(realizedClasses(), cls);
+ objc_addRegisteredClass((Class)cls);
+ assert(!isMetaClass(cls));
+ assert(!old);
+}
+
+
+/***********************************************************************
+* removeRealizedClass
+* Removes cls from the realized non-meta class hash.
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static void removeRealizedClass(class_t *cls)
+{
+ rwlock_assert_writing(&runtimeLock);
+ if (isRealized(cls)) {
+ assert(!isMetaClass(cls));
+ NXHashRemove(realizedClasses(), cls);
+ objc_removeRegisteredClass((Class)cls);
+ }
+}
+
+
+/***********************************************************************
+* addRealizedMetaclass
+* Adds cls to the realized metaclass hash.
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static void addRealizedMetaclass(class_t *cls)
+{
+ rwlock_assert_writing(&runtimeLock);
+ void *old;
+ old = NXHashInsert(realizedMetaclasses(), cls);
+ assert(isMetaClass(cls));
+ assert(!old);
+}
+
+
+/***********************************************************************
+* removeRealizedMetaclass
+* Removes cls from the realized metaclass hash.
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static void removeRealizedMetaclass(class_t *cls)
+{
+ rwlock_assert_writing(&runtimeLock);
+ if (isRealized(cls)) {
+ assert(isMetaClass(cls));
+ NXHashRemove(realizedMetaclasses(), cls);
+ }
+}
+
+
+/***********************************************************************
+* uninitializedClasses
+* Returns the metaclass => class map for un-+initialized classes
+* Replaces the 32-bit cls = objc_getName(metacls) during +initialize.
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+static NXMapTable *uninitialized_class_map = NULL;
+static NXMapTable *uninitializedClasses(void)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ // allocated in _read_images
+ assert(uninitialized_class_map);
+
+ return uninitialized_class_map;
+}
+
+
+/***********************************************************************
+* addUninitializedClass
+* Adds metacls => cls to the un-+initialized class map
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static void addUninitializedClass(class_t *cls, class_t *metacls)
+{
+ rwlock_assert_writing(&runtimeLock);
+ void *old;
+ old = NXMapInsert(uninitializedClasses(), metacls, cls);
+ assert(isRealized(metacls) ? isMetaClass(metacls) : metacls->data()->flags & RO_META);
+ assert(! (isRealized(cls) ? isMetaClass(cls) : cls->data()->flags & RO_META));
+ assert(!old);
+}
+
+
+static void removeUninitializedClass(class_t *cls)
+{
+ rwlock_assert_writing(&runtimeLock);
+ NXMapRemove(uninitializedClasses(), cls->isa);
+}
+
+
+/***********************************************************************
+* getNonMetaClass
+* Return the ordinary class for this class or metaclass.
+* Used by +initialize.
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+static class_t *getNonMetaClass(class_t *cls)
+{
+ rwlock_assert_locked(&runtimeLock);
+ if (isMetaClass(cls)) {
+ cls = (class_t *)NXMapGet(uninitializedClasses(), cls);
+ }
+ return cls;
+}
+
+
+/***********************************************************************
+* _class_getNonMetaClass
+* Return the ordinary class for this class or metaclass.
+* Used by +initialize.
+* Locking: acquires runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN Class _class_getNonMetaClass(Class cls_gen)
+{
+ class_t *cls = newcls(cls_gen);
+ rwlock_write(&runtimeLock);
+ cls = getNonMetaClass(cls);
+ realizeClass(cls);
+ rwlock_unlock_write(&runtimeLock);
+
+ return (Class)cls;
+}
+
+
+
+/***********************************************************************
+* futureClasses
+* Returns the classname => future class map for unrealized future classes.
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static NXMapTable *futureClasses(void)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ static NXMapTable *future_class_map = NULL;
+
+ if (future_class_map) return future_class_map;
+
+ // future_class_map is big enough to hold CF's classes and a few others
+ future_class_map = NXCreateMapTableFromZone(NXStrValueMapPrototype, 32,
+ _objc_internal_zone());
+
+ return future_class_map;
+}
+
+
+/***********************************************************************
+* addFutureClass
+* Installs cls as the class structure to use for the named class if it appears.
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static void addFutureClass(const char *name, class_t *cls)
+{
+ void *old;
+
+ rwlock_assert_writing(&runtimeLock);
+
+ if (PrintFuture) {
+ _objc_inform("FUTURE: reserving %p for %s", cls, name);
+ }
+
+ cls->setData((class_rw_t *)_calloc_internal(sizeof(*cls->data()), 1));
+ cls->data()->flags = RO_FUTURE;
+
+ old = NXMapKeyCopyingInsert(futureClasses(), name, cls);
+ assert(!old);
+}
+
+
+/***********************************************************************
+* removeFutureClass
+* Removes the named class from the unrealized future class list,
+* because it has been realized.
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static void removeFutureClass(const char *name)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ NXMapKeyFreeingRemove(futureClasses(), name);
+}
+
+
+/***********************************************************************
+* remappedClasses
+* Returns the oldClass => newClass map for realized future classes.
+* Returns the oldClass => NULL map for ignored weak-linked classes.
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+static NXMapTable *remappedClasses(BOOL create)
+{
+ static NXMapTable *remapped_class_map = NULL;
+
+ rwlock_assert_locked(&runtimeLock);
+
+ if (remapped_class_map) return remapped_class_map;
+ if (!create) return NULL;
+
+ // remapped_class_map is big enough to hold CF's classes and a few others
+ INIT_ONCE_PTR(remapped_class_map,
+ NXCreateMapTableFromZone(NXPtrValueMapPrototype, 32,
+ _objc_internal_zone()),
+ NXFreeMapTable(v));
+
+ return remapped_class_map;
+}
+
+
+/***********************************************************************
+* noClassesRemapped
+* Returns YES if no classes have been remapped
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+static BOOL noClassesRemapped(void)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ BOOL result = (remappedClasses(NO) == NULL);
+ return result;
+}
+
+
+/***********************************************************************
+* addRemappedClass
+* newcls is a realized future class, replacing oldcls.
+* OR newcls is NULL, replacing ignored weak-linked class oldcls.
+* Locking: runtimeLock must be write-locked by the caller
+**********************************************************************/
+static void addRemappedClass(class_t *oldcls, class_t *newcls)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ if (PrintFuture) {
+ _objc_inform("FUTURE: using %p instead of %p for %s",
+ oldcls, newcls, getName(newcls));
+ }
+
+ void *old;
+ old = NXMapInsert(remappedClasses(YES), oldcls, newcls);
+ assert(!old);
+}
+
+
+/***********************************************************************
+* remapClass
+* Returns the live class pointer for cls, which may be pointing to
+* a class struct that has been reallocated.
+* Returns NULL if cls is ignored because of weak linking.
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+static class_t *remapClass(class_t *cls)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ class_t *c2;
+
+ if (!cls) return NULL;
+
+ if (NXMapMember(remappedClasses(YES), cls, (void**)&c2) == NX_MAPNOTAKEY) {
+ return cls;
+ } else {
+ return c2;
+ }
+}
+
+
+/***********************************************************************
+* remapClassRef
+* Fix up a class ref, in case the class referenced has been reallocated
+* or is an ignored weak-linked class.
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+static void remapClassRef(class_t **clsref)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ class_t *newcls = remapClass(*clsref);
+ if (*clsref != newcls) *clsref = newcls;
+}
+
+
+/***********************************************************************
+* addSubclass
+* Adds subcls as a subclass of supercls.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static void addSubclass(class_t *supercls, class_t *subcls)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ if (supercls && subcls) {
+ assert(isRealized(supercls));
+ assert(isRealized(subcls));
+ subcls->data()->nextSiblingClass = supercls->data()->firstSubclass;
+ supercls->data()->firstSubclass = subcls;
+
+ if (supercls->data()->flags & RW_HAS_CXX_STRUCTORS) {
+ subcls->data()->flags |= RW_HAS_CXX_STRUCTORS;
+ }
+
+ if (supercls->hasCustomRR()) {
+ subcls->setHasCustomRR();
+ }
+ }
+}
+
+
+/***********************************************************************
+* removeSubclass
+* Removes subcls as a subclass of supercls.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static void removeSubclass(class_t *supercls, class_t *subcls)
+{
+ rwlock_assert_writing(&runtimeLock);
+ assert(getSuperclass(subcls) == supercls);
+
+ class_t **cp;
+ for (cp = &supercls->data()->firstSubclass;
+ *cp && *cp != subcls;
+ cp = &(*cp)->data()->nextSiblingClass)
+ ;
+ assert(*cp == subcls);
+ *cp = subcls->data()->nextSiblingClass;
+}
+
+
+
+/***********************************************************************
+* protocols
+* Returns the protocol name => protocol map for protocols.
+* Locking: runtimeLock must read- or write-locked by the caller
+**********************************************************************/
+static NXMapTable *protocols(void)
+{
+ static NXMapTable *protocol_map = NULL;
+
+ rwlock_assert_locked(&runtimeLock);
+
+ INIT_ONCE_PTR(protocol_map,
+ NXCreateMapTableFromZone(NXStrValueMapPrototype, 16,
+ _objc_internal_zone()),
+ NXFreeMapTable(v) );
+
+ return protocol_map;
+}
+
+
+/***********************************************************************
+* remapProtocol
+* Returns the live protocol pointer for proto, which may be pointing to
+* a protocol struct that has been reallocated.
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+static protocol_t *remapProtocol(protocol_ref_t proto)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ protocol_t *newproto = (protocol_t *)
+ NXMapGet(protocols(), ((protocol_t *)proto)->name);
+ return newproto ? newproto : (protocol_t *)proto;
+}
+
+
+/***********************************************************************
+* remapProtocolRef
+* Fix up a protocol ref, in case the protocol referenced has been reallocated.
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+static void remapProtocolRef(protocol_t **protoref)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ protocol_t *newproto = remapProtocol((protocol_ref_t)*protoref);
+ if (*protoref != newproto) *protoref = newproto;
+}
+
+
+/***********************************************************************
+* moveIvars
+* Slides a class's ivars to accommodate the given superclass size.
+* Also slides ivar and weak GC layouts if provided.
+* Ivars are NOT compacted to compensate for a superclass that shrunk.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static void moveIvars(class_ro_t *ro, uint32_t superSize,
+ layout_bitmap *ivarBitmap, layout_bitmap *weakBitmap)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ uint32_t diff;
+ uint32_t i;
+
+ assert(superSize > ro->instanceStart);
+ diff = superSize - ro->instanceStart;
+
+ if (ro->ivars) {
+ // Find maximum alignment in this class's ivars
+ uint32_t maxAlignment = 1;
+ for (i = 0; i < ro->ivars->count; i++) {
+ ivar_t *ivar = ivar_list_nth(ro->ivars, i);
+ if (!ivar->offset) continue; // anonymous bitfield
+
+ uint32_t alignment = ivar_alignment(ivar);
+ if (alignment > maxAlignment) maxAlignment = alignment;
+ }
+
+ // Compute a slide value that preserves that alignment
+ uint32_t alignMask = maxAlignment - 1;
+ if (diff & alignMask) diff = (diff + alignMask) & ~alignMask;
+
+ // Slide all of this class's ivars en masse
+ for (i = 0; i < ro->ivars->count; i++) {
+ ivar_t *ivar = ivar_list_nth(ro->ivars, i);
+ if (!ivar->offset) continue; // anonymous bitfield
+
+ uint32_t oldOffset = (uint32_t)*ivar->offset;
+ uint32_t newOffset = oldOffset + diff;
+ *ivar->offset = newOffset;
+
+ if (PrintIvars) {
+ _objc_inform("IVARS: offset %u -> %u for %s (size %u, align %u)",
+ oldOffset, newOffset, ivar->name,
+ ivar->size, ivar_alignment(ivar));
+ }
+ }
+
+ // Slide GC layouts
+ uint32_t oldOffset = ro->instanceStart;
+ uint32_t newOffset = ro->instanceStart + diff;
+
+ if (ivarBitmap) {
+ layout_bitmap_slide(ivarBitmap,
+ oldOffset >> WORD_SHIFT,
+ newOffset >> WORD_SHIFT);
+ }
+ if (weakBitmap) {
+ layout_bitmap_slide(weakBitmap,
+ oldOffset >> WORD_SHIFT,
+ newOffset >> WORD_SHIFT);
+ }
+ }
+
+ *(uint32_t *)&ro->instanceStart += diff;
+ *(uint32_t *)&ro->instanceSize += diff;
+
+ if (!ro->ivars) {
+ // No ivars slid, but superclass changed size.
+ // Expand bitmap in preparation for layout_bitmap_splat().
+ if (ivarBitmap) layout_bitmap_grow(ivarBitmap, ro->instanceSize >> WORD_SHIFT);
+ if (weakBitmap) layout_bitmap_grow(weakBitmap, ro->instanceSize >> WORD_SHIFT);
+ }
+}
+
+
+/***********************************************************************
+* getIvar
+* Look up an ivar by name.
+* Locking: runtimeLock must be read- or write-locked by the caller.
+**********************************************************************/
+static ivar_t *getIvar(class_t *cls, const char *name)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ const ivar_list_t *ivars;
+ assert(isRealized(cls));
+ if ((ivars = cls->data()->ro->ivars)) {
+ uint32_t i;
+ for (i = 0; i < ivars->count; i++) {
+ ivar_t *ivar = ivar_list_nth(ivars, i);
+ if (!ivar->offset) continue; // anonymous bitfield
+
+ // ivar->name may be NULL for anonymous bitfields etc.
+ if (ivar->name && 0 == strcmp(name, ivar->name)) {
+ return ivar;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static void reconcileInstanceVariables(class_t *cls, class_t *supercls) {
+ class_rw_t *rw = cls->data();
+ const class_ro_t *ro = rw->ro;
+
+ if (supercls) {
+ // Non-fragile ivars - reconcile this class with its superclass
+ // Does this really need to happen for the isMETA case?
+ layout_bitmap ivarBitmap;
+ layout_bitmap weakBitmap;
+ BOOL layoutsChanged = NO;
+ BOOL mergeLayouts = UseGC;
+ const class_ro_t *super_ro = supercls->data()->ro;
+
+ if (DebugNonFragileIvars) {
+ // Debugging: Force non-fragile ivars to slide.
+ // Intended to find compiler, runtime, and program bugs.
+ // If it fails with this and works without, you have a problem.
+
+ // Operation: Reset everything to 0 + misalignment.
+ // Then force the normal sliding logic to push everything back.
+
+ // Exceptions: root classes, metaclasses, *NSCF* classes,
+ // __CF* classes, NSConstantString, NSSimpleCString
+
+ // (already know it's not root because supercls != nil)
+ if (!strstr(getName(cls), "NSCF") &&
+ 0 != strncmp(getName(cls), "__CF", 4) &&
+ 0 != strcmp(getName(cls), "NSConstantString") &&
+ 0 != strcmp(getName(cls), "NSSimpleCString"))
+ {
+ uint32_t oldStart = ro->instanceStart;
+ uint32_t oldSize = ro->instanceSize;
+ class_ro_t *ro_w = make_ro_writeable(rw);
+ ro = rw->ro;
+
+ // Find max ivar alignment in class.
+ // default to word size to simplify ivar update
+ uint32_t alignment = 1<<WORD_SHIFT;
+ if (ro->ivars) {
+ uint32_t i;
+ for (i = 0; i < ro->ivars->count; i++) {
+ ivar_t *ivar = ivar_list_nth(ro->ivars, i);
+ if (ivar_alignment(ivar) > alignment) {
+ alignment = ivar_alignment(ivar);
+ }
+ }
+ }
+ uint32_t misalignment = ro->instanceStart % alignment;
+ uint32_t delta = ro->instanceStart - misalignment;
+ ro_w->instanceStart = misalignment;
+ ro_w->instanceSize -= delta;
+
+ if (PrintIvars) {
+ _objc_inform("IVARS: DEBUG: forcing ivars for class '%s' "
+ "to slide (instanceStart %zu -> %zu)",
+ getName(cls), (size_t)oldStart,
+ (size_t)ro->instanceStart);
+ }
+
+ if (ro->ivars) {
+ uint32_t i;
+ for (i = 0; i < ro->ivars->count; i++) {
+ ivar_t *ivar = ivar_list_nth(ro->ivars, i);
+ if (!ivar->offset) continue; // anonymous bitfield
+ *ivar->offset -= delta;
+ }
+ }
+
+ if (mergeLayouts) {
+ layout_bitmap layout;
+ if (ro->ivarLayout) {
+ layout = layout_bitmap_create(ro->ivarLayout,
+ oldSize, oldSize, NO);
+ layout_bitmap_slide_anywhere(&layout,
+ delta >> WORD_SHIFT, 0);
+ ro_w->ivarLayout = layout_string_create(layout);
+ layout_bitmap_free(layout);
+ }
+ if (ro->weakIvarLayout) {
+ layout = layout_bitmap_create(ro->weakIvarLayout,
+ oldSize, oldSize, YES);
+ layout_bitmap_slide_anywhere(&layout,
+ delta >> WORD_SHIFT, 0);
+ ro_w->weakIvarLayout = layout_string_create(layout);
+ layout_bitmap_free(layout);
+ }
+ }
+ }
+ }
+
+ // fixme can optimize for "class has no new ivars", etc
+ // WARNING: gcc c++ sets instanceStart/Size=0 for classes with
+ // no local ivars, but does provide a layout bitmap.
+ // Handle that case specially so layout_bitmap_create doesn't die
+ // The other ivar sliding code below still works fine, and
+ // the final result is a good class.
+ if (ro->instanceStart == 0 && ro->instanceSize == 0) {
+ // We can't use ro->ivarLayout because we don't know
+ // how long it is. Force a new layout to be created.
+ if (PrintIvars) {
+ _objc_inform("IVARS: instanceStart/Size==0 for class %s; "
+ "disregarding ivar layout", ro->name);
+ }
+ ivarBitmap = layout_bitmap_create_empty(super_ro->instanceSize, NO);
+ weakBitmap = layout_bitmap_create_empty(super_ro->instanceSize, YES);
+ layoutsChanged = YES;
+ } else {
+ ivarBitmap =
+ layout_bitmap_create(ro->ivarLayout,
+ ro->instanceSize,
+ ro->instanceSize, NO);
+ weakBitmap =
+ layout_bitmap_create(ro->weakIvarLayout,
+ ro->instanceSize,
+ ro->instanceSize, YES);
+ }
+
+ if (ro->instanceStart < super_ro->instanceSize) {
+ // Superclass has changed size. This class's ivars must move.
+ // Also slide layout bits in parallel.
+ // This code is incapable of compacting the subclass to
+ // compensate for a superclass that shrunk, so don't do that.
+ if (PrintIvars) {
+ _objc_inform("IVARS: sliding ivars for class %s "
+ "(superclass was %u bytes, now %u)",
+ ro->name, ro->instanceStart,
+ super_ro->instanceSize);
+ }
+ class_ro_t *ro_w = make_ro_writeable(rw);
+ ro = rw->ro;
+ moveIvars(ro_w, super_ro->instanceSize,
+ mergeLayouts ? &ivarBitmap : NULL, mergeLayouts ? &weakBitmap : NULL);
+ gdb_objc_class_changed((Class)cls, OBJC_CLASS_IVARS_CHANGED, ro->name);
+ layoutsChanged = mergeLayouts;
+ }
+
+ if (mergeLayouts) {
+ // Check superclass's layout against this class's layout.
+ // This needs to be done even if the superclass is not bigger.
+ layout_bitmap superBitmap = layout_bitmap_create(super_ro->ivarLayout,
+ super_ro->instanceSize,
+ super_ro->instanceSize, NO);
+ layoutsChanged |= layout_bitmap_splat(ivarBitmap, superBitmap,
+ ro->instanceStart);
+ layout_bitmap_free(superBitmap);
+
+ // check the superclass' weak layout.
+ superBitmap = layout_bitmap_create(super_ro->weakIvarLayout,
+ super_ro->instanceSize,
+ super_ro->instanceSize, YES);
+ layoutsChanged |= layout_bitmap_splat(weakBitmap, superBitmap,
+ ro->instanceStart);
+ layout_bitmap_free(superBitmap);
+ }
+
+ if (layoutsChanged) {
+ // Rebuild layout strings.
+ if (PrintIvars) {
+ _objc_inform("IVARS: gc layout changed for class %s",
+ ro->name);
+ }
+ class_ro_t *ro_w = make_ro_writeable(rw);
+ ro = rw->ro;
+ if (DebugNonFragileIvars) {
+ try_free(ro_w->ivarLayout);
+ try_free(ro_w->weakIvarLayout);
+ }
+ ro_w->ivarLayout = layout_string_create(ivarBitmap);
+ ro_w->weakIvarLayout = layout_string_create(weakBitmap);
+ }
+
+ layout_bitmap_free(ivarBitmap);
+ layout_bitmap_free(weakBitmap);
+ }
+}
+
+/***********************************************************************
+* realizeClass
+* Performs first-time initialization on class cls,
+* including allocating its read-write data.
+* Returns the real class structure for the class.
+* Locking: runtimeLock must be write-locked by the caller
+**********************************************************************/
+static class_t *realizeClass(class_t *cls)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ const class_ro_t *ro;
+ class_rw_t *rw;
+ class_t *supercls;
+ class_t *metacls;
+ BOOL isMeta;
+
+ if (!cls) return NULL;
+ if (isRealized(cls)) return cls;
+ assert(cls == remapClass(cls));
+
+ ro = (const class_ro_t *)cls->data();
+ if (ro->flags & RO_FUTURE) {
+ // This was a future class. rw data is already allocated.
+ rw = cls->data();
+ ro = cls->data()->ro;
+ changeInfo(cls, RW_REALIZED, RW_FUTURE);
+ } else {
+ // Normal class. Allocate writeable class data.
+ rw = (class_rw_t *)_calloc_internal(sizeof(class_rw_t), 1);
+ rw->ro = ro;
+ rw->flags = RW_REALIZED;
+ cls->setData(rw);
+ }
+
+ isMeta = (ro->flags & RO_META) ? YES : NO;
+
+ rw->version = isMeta ? 7 : 0; // old runtime went up to 6
+
+ if (PrintConnecting) {
+ _objc_inform("CLASS: realizing class '%s' %s %p %p",
+ ro->name, isMeta ? "(meta)" : "", cls, ro);
+ }
+
+ // Realize superclass and metaclass, if they aren't already.
+ // This needs to be done after RW_REALIZED is set above, for root classes.
+ supercls = realizeClass(remapClass(cls->superclass));
+ metacls = realizeClass(remapClass(cls->isa));
+
+ // Check for remapped superclass
+ // fixme doesn't handle remapped metaclass
+ assert(metacls == cls->isa);
+ if (supercls != cls->superclass) {
+ cls->superclass = supercls;
+ }
+
+ /* debug: print them all
+ if (ro->ivars) {
+ uint32_t i;
+ for (i = 0; i < ro->ivars->count; i++) {
+ ivar_t *ivar = ivar_list_nth(ro->ivars, i);
+ if (!ivar->offset) continue; // anonymous bitfield
+
+ _objc_inform("IVARS: %s.%s (offset %u, size %u, align %u)",
+ ro->name, ivar->name,
+ *ivar->offset, ivar->size, ivar_alignment(ivar));
+ }
+ }
+ */
+
+ // Reconcile instance variable offsets / layout.
+ if (!isMeta) reconcileInstanceVariables(cls, supercls);
+
+ // Copy some flags from ro to rw
+ if (ro->flags & RO_HAS_CXX_STRUCTORS) rw->flags |= RW_HAS_CXX_STRUCTORS;
+
+ // Connect this class to its superclass's subclass lists
+ if (supercls) {
+ addSubclass(supercls, cls);
+ }
+
+ // Attach categories
+ methodizeClass(cls);
+
+ if (!isMeta) {
+ addRealizedClass(cls);
+ } else {
+ addRealizedMetaclass(cls);
+ }
+
+ return cls;
+}
+
+
+/***********************************************************************
+* getClass
+* Looks up a class by name. The class MIGHT NOT be realized.
+* Locking: runtimeLock must be read- or write-locked by the caller.
+**********************************************************************/
+static class_t *getClass(const char *name)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ return (class_t *)NXMapGet(namedClasses(), name);
+}
+
+
+/***********************************************************************
+* missingWeakSuperclass
+* Return YES if some superclass of cls was weak-linked and is missing.
+**********************************************************************/
+static BOOL
+missingWeakSuperclass(class_t *cls)
+{
+ assert(!isRealized(cls));
+
+ if (!cls->superclass) {
+ // superclass NULL. This is normal for root classes only.
+ return (!(cls->data()->flags & RO_ROOT));
+ } else {
+ // superclass not NULL. Check if a higher superclass is missing.
+ class_t *supercls = remapClass(cls->superclass);
+ if (!supercls) return YES;
+ if (isRealized(supercls)) return NO;
+ return missingWeakSuperclass(supercls);
+ }
+}
+
+
+/***********************************************************************
+* realizeAllClassesInImage
+* Non-lazily realizes all unrealized classes in the given image.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static void realizeAllClassesInImage(header_info *hi)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ size_t count, i;
+ class_t **classlist;
+
+ if (hi->allClassesRealized) return;
+
+ classlist = _getObjc2ClassList(hi, &count);
+
+ for (i = 0; i < count; i++) {
+ realizeClass(remapClass(classlist[i]));
+ }
+
+ hi->allClassesRealized = YES;
+}
+
+
+/***********************************************************************
+* realizeAllClasses
+* Non-lazily realizes all unrealized classes in all known images.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static void realizeAllClasses(void)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ header_info *hi;
+ for (hi = FirstHeader; hi; hi = hi->next) {
+ realizeAllClassesInImage(hi);
+ }
+}
+
+
+/***********************************************************************
+* _objc_allocateFutureClass
+* Allocate an unresolved future class for the given class name.
+* Returns any existing allocation if one was already made.
+* Assumes the named class doesn't exist yet.
+* Locking: acquires runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN Class _objc_allocateFutureClass(const char *name)
+{
+ rwlock_write(&runtimeLock);
+
+ class_t *cls;
+ NXMapTable *future_class_map = futureClasses();
+
+ if ((cls = (class_t *)NXMapGet(future_class_map, name))) {
+ // Already have a future class for this name.
+ rwlock_unlock_write(&runtimeLock);
+ return (Class)cls;
+ }
+
+ cls = (class_t *)_calloc_class(sizeof(*cls));
+ addFutureClass(name, cls);
+
+ rwlock_unlock_write(&runtimeLock);
+ return (Class)cls;
+}
+
+
+/***********************************************************************
+*
+**********************************************************************/
+void objc_setFutureClass(Class cls, const char *name)
+{
+ // fixme hack do nothing - NSCFString handled specially elsewhere
+}
+
+
+/***********************************************************************
+* flushVtables
+* Rebuilds vtables for cls and its realized subclasses.
+* If cls is Nil, all realized classes and metaclasses are touched.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static void flushVtables(class_t *cls)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ if (PrintVtables && !cls) {
+ _objc_inform("VTABLES: ### EXPENSIVE ### global vtable flush!");
+ }
+
+ FOREACH_REALIZED_CLASS_AND_SUBCLASS(c, cls, {
+ updateVtable(c, NO);
+ });
+}
+
+
+/***********************************************************************
+* flushCaches
+* Flushes caches for cls and its realized subclasses.
+* Does not update vtables.
+* If cls is Nil, all realized and metaclasses classes are touched.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static void flushCaches(class_t *cls)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ FOREACH_REALIZED_CLASS_AND_SUBCLASS(c, cls, {
+ flush_cache((Class)c);
+ });
+}
+
+
+/***********************************************************************
+* flush_caches
+* Flushes caches and rebuilds vtables for cls, its subclasses,
+* and optionally its metaclass.
+* Locking: acquires runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN void flush_caches(Class cls_gen, BOOL flush_meta)
+{
+ class_t *cls = newcls(cls_gen);
+ rwlock_write(&runtimeLock);
+ // fixme optimize vtable flushing? (only needed for vtable'd selectors)
+ flushCaches(cls);
+ flushVtables(cls);
+ // don't flush root class's metaclass twice (it's a subclass of the root)
+ if (flush_meta && getSuperclass(cls)) {
+ flushCaches(cls->isa);
+ flushVtables(cls->isa);
+ }
+ rwlock_unlock_write(&runtimeLock);
+}
+
+
+/***********************************************************************
+* map_images
+* Process the given images which are being mapped in by dyld.
+* Calls ABI-agnostic code after taking ABI-specific locks.
+*
+* Locking: write-locks runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN const char *
+map_images(enum dyld_image_states state, uint32_t infoCount,
+ const struct dyld_image_info infoList[])
+{
+ const char *err;
+
+ rwlock_write(&runtimeLock);
+ err = map_images_nolock(state, infoCount, infoList);
+ rwlock_unlock_write(&runtimeLock);
+ return err;
+}
+
+
+/***********************************************************************
+* load_images
+* Process +load in the given images which are being mapped in by dyld.
+* Calls ABI-agnostic code after taking ABI-specific locks.
+*
+* Locking: write-locks runtimeLock and loadMethodLock
+**********************************************************************/
+PRIVATE_EXTERN const char *
+load_images(enum dyld_image_states state, uint32_t infoCount,
+ const struct dyld_image_info infoList[])
+{
+ BOOL found;
+
+ recursive_mutex_lock(&loadMethodLock);
+
+ // Discover load methods
+ rwlock_write(&runtimeLock);
+ found = load_images_nolock(state, infoCount, infoList);
+ rwlock_unlock_write(&runtimeLock);
+
+ // Call +load methods (without runtimeLock - re-entrant)
+ if (found) {
+ call_load_methods();
+ }
+
+ recursive_mutex_unlock(&loadMethodLock);
+
+ return NULL;
+}
+
+
+/***********************************************************************
+* unmap_image
+* Process the given image which is about to be unmapped by dyld.
+* mh is mach_header instead of headerType because that's what
+* dyld_priv.h says even for 64-bit.
+*
+* Locking: write-locks runtimeLock and loadMethodLock
+**********************************************************************/
+PRIVATE_EXTERN void
+unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide)
+{
+ recursive_mutex_lock(&loadMethodLock);
+ rwlock_write(&runtimeLock);
+
+ unmap_image_nolock(mh);
+
+ rwlock_unlock_write(&runtimeLock);
+ recursive_mutex_unlock(&loadMethodLock);
+}
+
+
+
+/***********************************************************************
+* _read_images
+* Perform initial processing of the headers in the linked
+* list beginning with headerList.
+*
+* Called by: map_images_nolock
+*
+* Locking: runtimeLock acquired by map_images
+**********************************************************************/
+PRIVATE_EXTERN void _read_images(header_info **hList, uint32_t hCount)
+{
+ header_info *hi;
+ uint32_t hIndex;
+ size_t count;
+ size_t i;
+ class_t **resolvedFutureClasses = NULL;
+ size_t resolvedFutureClassCount = 0;
+ static BOOL doneOnce;
+
+ rwlock_assert_writing(&runtimeLock);
+
+#define EACH_HEADER \
+ hIndex = 0; \
+ crashlog_header_name(NULL) && hIndex < hCount && (hi = hList[hIndex]) && crashlog_header_name(hi); \
+ hIndex++
+
+ if (!doneOnce) {
+ doneOnce = YES;
+ initVtables();
+
+ // Count classes. Size various table based on the total.
+ unsigned int total = 0;
+ for (EACH_HEADER) {
+ if (_getObjc2ClassList(hi, &count)) {
+ total += (unsigned int)count;
+ }
+ }
+
+ if (PrintConnecting) {
+ _objc_inform("CLASS: found %u classes during launch", total);
+ }
+
+ // namedClasses (NOT realizedClasses)
+ // 4/3 is NXMapTable's load factor
+ gdb_objc_realized_classes =
+ NXCreateMapTableFromZone(NXStrValueMapPrototype, total*4/3,
+ _objc_internal_zone());
+
+ // uninitializedClasses
+ // 4/3 is NXMapTable's load factor
+ uninitialized_class_map =
+ NXCreateMapTableFromZone(NXPtrValueMapPrototype, total*4/3,
+ _objc_internal_zone());
+
+ // realizedClasses and realizedMetaclasses - less than the full total
+ realized_class_hash =
+ NXCreateHashTableFromZone(NXPtrPrototype, total / 8, NULL,
+ _objc_internal_zone());
+ realized_metaclass_hash =
+ NXCreateHashTableFromZone(NXPtrPrototype, total / 8, NULL,
+ _objc_internal_zone());
+ }
+
+
+ // Discover classes. Fix up unresolved future classes. Mark bundle classes.
+ NXMapTable *future_class_map = futureClasses();
+ for (EACH_HEADER) {
+ class_t **classlist = _getObjc2ClassList(hi, &count);
+ for (i = 0; i < count; i++) {
+ const char *name = getName(classlist[i]);
+
+ if (missingWeakSuperclass(classlist[i])) {
+ // No superclass (probably weak-linked).
+ // Disavow any knowledge of this subclass.
+ if (PrintConnecting) {
+ _objc_inform("CLASS: IGNORING class '%s' with "
+ "missing weak-linked superclass", name);
+ }
+ addRemappedClass(classlist[i], NULL);
+ classlist[i]->superclass = NULL;
+ classlist[i] = NULL;
+ continue;
+ }
+
+ if (NXCountMapTable(future_class_map) > 0) {
+ class_t *newCls = (class_t *)NXMapGet(future_class_map, name);
+ if (newCls) {
+ // Copy class_t to future class's struct.
+ // Preserve future's rw data block.
+ class_rw_t *rw = newCls->data();
+ memcpy(newCls, classlist[i], sizeof(class_t));
+ rw->ro = (class_ro_t *)newCls->data();
+ newCls->setData(rw);
+
+ removeFutureClass(name);
+ addRemappedClass(classlist[i], newCls);
+ classlist[i] = newCls;
+ // Non-lazily realize the class below.
+ resolvedFutureClasses = (class_t **)
+ _realloc_internal(resolvedFutureClasses,
+ (resolvedFutureClassCount+1)
+ * sizeof(class_t *));
+ resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
+ }
+ }
+ addNamedClass(classlist[i], name);
+ addUninitializedClass(classlist[i], classlist[i]->isa);
+ if (hi->mhdr->filetype == MH_BUNDLE) {
+ classlist[i]->data()->flags |= RO_FROM_BUNDLE;
+ classlist[i]->isa->data()->flags |= RO_FROM_BUNDLE;
+ }
+ }
+ }
+
+ // Fix up remapped classes
+ // classlist is up to date, but classrefs may not be
+
+ if (!noClassesRemapped()) {
+ for (EACH_HEADER) {
+ class_t **classrefs = _getObjc2ClassRefs(hi, &count);
+ for (i = 0; i < count; i++) {
+ remapClassRef(&classrefs[i]);
+ }
+ // fixme why doesn't test future1 catch the absence of this?
+ classrefs = _getObjc2SuperRefs(hi, &count);
+ for (i = 0; i < count; i++) {
+ remapClassRef(&classrefs[i]);
+ }
+ }
+ }
+
+
+ // Fix up @selector references
+ sel_lock();
+ for (EACH_HEADER) {
+ if (PrintPreopt) {
+ if (sel_preoptimizationValid(hi)) {
+ _objc_inform("PREOPTIMIZATION: honoring preoptimized selectors in %s",
+ _nameForHeader(hi->mhdr));
+ }
+ else if (_objcHeaderOptimizedByDyld(hi)) {
+ _objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors in %s",
+ _nameForHeader(hi->mhdr));
+ }
+ }
+
+ if (sel_preoptimizationValid(hi)) continue;
+
+ SEL *sels = _getObjc2SelectorRefs(hi, &count);
+ BOOL isBundle = hi->mhdr->filetype == MH_BUNDLE;
+ for (i = 0; i < count; i++) {
+ sels[i] = sel_registerNameNoLock((const char *)sels[i], isBundle);
+ }
+ }
+ sel_unlock();
+
+ // Discover protocols. Fix up protocol refs.
+ NXMapTable *protocol_map = protocols();
+ for (EACH_HEADER) {
+ extern class_t OBJC_CLASS_$_Protocol;
+ Class cls = (Class)&OBJC_CLASS_$_Protocol;
+ assert(cls);
+ protocol_t **protocols = _getObjc2ProtocolList(hi, &count);
+ // fixme duplicate protocol from bundle
+ for (i = 0; i < count; i++) {
+ if (!NXMapGet(protocol_map, protocols[i]->name)) {
+ protocols[i]->isa = cls;
+ NXMapKeyCopyingInsert(protocol_map,
+ protocols[i]->name, protocols[i]);
+ if (PrintProtocols) {
+ _objc_inform("PROTOCOLS: protocol at %p is %s",
+ protocols[i], protocols[i]->name);
+ }
+ } else {
+ if (PrintProtocols) {
+ _objc_inform("PROTOCOLS: protocol at %p is %s (duplicate)",
+ protocols[i], protocols[i]->name);
+ }
+ }
+ }
+ }
+ for (EACH_HEADER) {
+ protocol_t **protocols;
+ protocols = _getObjc2ProtocolRefs(hi, &count);
+ for (i = 0; i < count; i++) {
+ remapProtocolRef(&protocols[i]);
+ }
+ }
+
+ // Realize non-lazy classes (for +load methods and static instances)
+ for (EACH_HEADER) {
+ class_t **classlist =
+ _getObjc2NonlazyClassList(hi, &count);
+ for (i = 0; i < count; i++) {
+ realizeClass(remapClass(classlist[i]));
+ }
+ }
+
+ // Realize newly-resolved future classes, in case CF manipulates them
+ if (resolvedFutureClasses) {
+ for (i = 0; i < resolvedFutureClassCount; i++) {
+ realizeClass(resolvedFutureClasses[i]);
+ }
+ _free_internal(resolvedFutureClasses);
+ }
+
+ // Discover categories.
+ for (EACH_HEADER) {
+ category_t **catlist =
+ _getObjc2CategoryList(hi, &count);
+ for (i = 0; i < count; i++) {
+ category_t *cat = catlist[i];
+ // Do NOT use cat->cls! It may have been remapped.
+ class_t *cls = remapClass(cat->cls);
+
+ if (!cls) {
+ // Category's target class is missing (probably weak-linked).
+ // Disavow any knowledge of this category.
+ catlist[i] = NULL;
+ if (PrintConnecting) {
+ _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
+ "missing weak-linked target class",
+ cat->name, cat);
+ }
+ continue;
+ }
+
+ // Process this category.
+ // First, register the category with its target class.
+ // Then, rebuild the class's method lists (etc) if
+ // the class is realized.
+ BOOL classExists = NO;
+ if (cat->instanceMethods || cat->protocols
+ || cat->instanceProperties)
+ {
+ addUnattachedCategoryForClass(cat, cls, hi);
+ if (isRealized(cls)) {
+ remethodizeClass(cls);
+ classExists = YES;
+ }
+ if (PrintConnecting) {
+ _objc_inform("CLASS: found category -%s(%s) %s",
+ getName(cls), cat->name,
+ classExists ? "on existing class" : "");
+ }
+ }
+
+ if (cat->classMethods || cat->protocols
+ /* || cat->classProperties */)
+ {
+ addUnattachedCategoryForClass(cat, cls->isa, hi);
+ if (isRealized(cls->isa)) {
+ remethodizeClass(cls->isa);
+ }
+ if (PrintConnecting) {
+ _objc_inform("CLASS: found category +%s(%s)",
+ getName(cls), cat->name);
+ }
+ }
+ }
+ }
+
+ // Category discovery MUST BE LAST to avoid potential races
+ // when other threads call the new category code before
+ // this thread finishes its fixups.
+
+ // +load handled by prepare_load_methods()
+
+ if (DebugNonFragileIvars) {
+ realizeAllClasses();
+ }
+
+#undef EACH_HEADER
+}
+
+
+/***********************************************************************
+* prepare_load_methods
+* Schedule +load for classes in this image, any un-+load-ed
+* superclasses in other images, and any categories in this image.
+**********************************************************************/
+// Recursively schedule +load for cls and any un-+load-ed superclasses.
+// cls must already be connected.
+static void schedule_class_load(class_t *cls)
+{
+ if (!cls) return;
+ assert(isRealized(cls)); // _read_images should realize
+
+ if (cls->data()->flags & RW_LOADED) return;
+
+ // Ensure superclass-first ordering
+ schedule_class_load(getSuperclass(cls));
+
+ add_class_to_loadable_list((Class)cls);
+ changeInfo(cls, RW_LOADED, 0);
+}
+
+PRIVATE_EXTERN void prepare_load_methods(header_info *hi)
+{
+ size_t count, i;
+
+ rwlock_assert_writing(&runtimeLock);
+
+ class_t **classlist =
+ _getObjc2NonlazyClassList(hi, &count);
+ for (i = 0; i < count; i++) {
+ schedule_class_load(remapClass(classlist[i]));
+ }
+
+ category_t **categorylist = _getObjc2NonlazyCategoryList(hi, &count);
+ for (i = 0; i < count; i++) {
+ category_t *cat = categorylist[i];
+ // Do NOT use cat->cls! It may have been remapped.
+ class_t *cls = remapClass(cat->cls);
+ if (!cls) continue; // category for ignored weak-linked class
+ realizeClass(cls);
+ assert(isRealized(cls->isa));
+ add_category_to_loadable_list((Category)cat);
+ }
+}
+
+
+/***********************************************************************
+* _unload_image
+* Only handles MH_BUNDLE for now.
+* Locking: write-lock and loadMethodLock acquired by unmap_image
+**********************************************************************/
+PRIVATE_EXTERN void _unload_image(header_info *hi)
+{
+ size_t count, i;
+
+ recursive_mutex_assert_locked(&loadMethodLock);
+ rwlock_assert_writing(&runtimeLock);
+
+ // Unload unattached categories and categories waiting for +load.
+
+ category_t **catlist = _getObjc2CategoryList(hi, &count);
+ for (i = 0; i < count; i++) {
+ category_t *cat = catlist[i];
+ if (!cat) continue; // category for ignored weak-linked class
+ class_t *cls = remapClass(cat->cls);
+ assert(cls); // shouldn't have live category for dead class
+
+ // fixme for MH_DYLIB cat's class may have been unloaded already
+
+ // unattached list
+ removeUnattachedCategoryForClass(cat, cls);
+
+ // +load queue
+ remove_category_from_loadable_list((Category)cat);
+ }
+
+ // Unload classes.
+
+ class_t **classlist = _getObjc2ClassList(hi, &count);
+ for (i = 0; i < count; i++) {
+ class_t *cls = classlist[i];
+ // fixme remapped classes?
+ // fixme ignored weak-linked classes
+ if (cls) {
+ remove_class_from_loadable_list((Class)cls);
+ unload_class(cls->isa, YES);
+ unload_class(cls, NO);
+ }
+ }
+
+ // XXX FIXME -- Clean up protocols:
+ // <rdar://problem/9033191> Support unloading protocols at dylib/image unload time
+
+ // fixme DebugUnload
+}
+
+
+/***********************************************************************
+* method_getDescription
+* Returns a pointer to this method's objc_method_description.
+* Locking: none
+**********************************************************************/
+struct objc_method_description *
+method_getDescription(Method m)
+{
+ if (!m) return NULL;
+ return (struct objc_method_description *)newmethod(m);
+}
+
+
+/***********************************************************************
+* method_getImplementation
+* Returns this method's IMP.
+* Locking: none
+**********************************************************************/
+static IMP
+_method_getImplementation(method_t *m)
+{
+ if (!m) return NULL;
+ return m->imp;
+}
+
+IMP
+method_getImplementation(Method m)
+{
+ return _method_getImplementation(newmethod(m));
+}
+
+
+/***********************************************************************
+* method_getName
+* Returns this method's selector.
+* The method must not be NULL.
+* The method must already have been fixed-up.
+* Locking: none
+**********************************************************************/
+SEL
+method_getName(Method m_gen)
+{
+ method_t *m = newmethod(m_gen);
+ if (!m) return NULL;
+
+ assert((SEL)m->name == sel_registerName((char *)m->name));
+ return (SEL)m->name;
+}
+
+
+/***********************************************************************
+* method_getTypeEncoding
+* Returns this method's old-style type encoding string.
+* The method must not be NULL.
+* Locking: none
+**********************************************************************/
+const char *
+method_getTypeEncoding(Method m)
+{
+ if (!m) return NULL;
+ return newmethod(m)->types;
+}
+
+
+/***********************************************************************
+* method_setImplementation
+* Sets this method's implementation to imp.
+* The previous implementation is returned.
+**********************************************************************/
+static IMP
+_method_setImplementation(class_t *cls, method_t *m, IMP imp)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ if (!m) return NULL;
+ if (!imp) return NULL;
+
+ if (ignoreSelector(m->name)) {
+ // Ignored methods stay ignored
+ return m->imp;
+ }
+
+ IMP old = _method_getImplementation(m);
+ m->imp = imp;
+
+ // No cache flushing needed - cache contains Methods not IMPs.
+
+ if (vtable_containsSelector(newmethod(m)->name)) {
+ // Will be slow if cls is NULL (i.e. unknown)
+ // fixme build list of classes whose Methods are known externally?
+ flushVtables(cls);
+ }
+
+ // fixme catch NSObject changing to custom RR
+ // cls->setCustomRR();
+
+ // fixme update monomorphism if necessary
+
+ return old;
+}
+
+IMP
+method_setImplementation(Method m, IMP imp)
+{
+ // Don't know the class - will be slow if vtables are affected
+ // fixme build list of classes whose Methods are known externally?
+ IMP result;
+ rwlock_write(&runtimeLock);
+ result = _method_setImplementation(Nil, newmethod(m), imp);
+ rwlock_unlock_write(&runtimeLock);
+ return result;
+}
+
+
+void method_exchangeImplementations(Method m1_gen, Method m2_gen)
+{
+ method_t *m1 = newmethod(m1_gen);
+ method_t *m2 = newmethod(m2_gen);
+ if (!m1 || !m2) return;
+
+ rwlock_write(&runtimeLock);
+
+ if (ignoreSelector(m1->name) || ignoreSelector(m2->name)) {
+ // Ignored methods stay ignored. Now they're both ignored.
+ m1->imp = (IMP)&_objc_ignored_method;
+ m2->imp = (IMP)&_objc_ignored_method;
+ rwlock_unlock_write(&runtimeLock);
+ return;
+ }
+
+ IMP m1_imp = m1->imp;
+ m1->imp = m2->imp;
+ m2->imp = m1_imp;
+
+ if (vtable_containsSelector(m1->name) ||
+ vtable_containsSelector(m2->name))
+ {
+ // Don't know the class - will be slow if vtables are affected
+ // fixme build list of classes whose Methods are known externally?
+ flushVtables(NULL);
+ }
+
+ // fixme catch NSObject changing to custom RR
+ // cls->setCustomRR();
+
+ // fixme update monomorphism if necessary
+
+ rwlock_unlock_write(&runtimeLock);
+}
+
+
+/***********************************************************************
+* ivar_getOffset
+* fixme
+* Locking: none
+**********************************************************************/
+ptrdiff_t
+ivar_getOffset(Ivar ivar)
+{
+ if (!ivar) return 0;
+ return *newivar(ivar)->offset;
+}
+
+
+/***********************************************************************
+* ivar_getName
+* fixme
+* Locking: none
+**********************************************************************/
+const char *
+ivar_getName(Ivar ivar)
+{
+ if (!ivar) return NULL;
+ return newivar(ivar)->name;
+}
+
+
+/***********************************************************************
+* ivar_getTypeEncoding
+* fixme
+* Locking: none
+**********************************************************************/
+const char *
+ivar_getTypeEncoding(Ivar ivar)
+{
+ if (!ivar) return NULL;
+ return newivar(ivar)->type;
+}
+
+
+
+const char *property_getName(objc_property_t prop)
+{
+ return newproperty(prop)->name;
+}
+
+const char *property_getAttributes(objc_property_t prop)
+{
+ return newproperty(prop)->attributes;
+}
+
+objc_property_attribute_t *property_copyAttributeList(objc_property_t prop,
+ unsigned int *outCount)
+{
+ if (!prop) {
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ objc_property_attribute_t *result;
+ rwlock_read(&runtimeLock);
+ result = copyPropertyAttributeList(newproperty(prop)->attributes,outCount);
+ rwlock_unlock_read(&runtimeLock);
+ return result;
+}
+
+char * property_copyAttributeValue(objc_property_t prop, const char *name)
+{
+ if (!prop || !name || *name == '\0') return NULL;
+
+ char *result;
+ rwlock_read(&runtimeLock);
+ result = copyPropertyAttributeValue(newproperty(prop)->attributes, name);
+ rwlock_unlock_read(&runtimeLock);
+ return result;
+}
+
+
+/***********************************************************************
+* _protocol_getMethod_nolock
+* Locking: runtimeLock must be write-locked by the caller
+**********************************************************************/
+static Method
+_protocol_getMethod_nolock(protocol_t *proto, SEL sel,
+ BOOL isRequiredMethod, BOOL isInstanceMethod)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ uint32_t i;
+ if (!proto || !sel) return NULL;
+
+ method_list_t **mlistp = NULL;
+
+ if (isRequiredMethod) {
+ if (isInstanceMethod) {
+ mlistp = &proto->instanceMethods;
+ } else {
+ mlistp = &proto->classMethods;
+ }
+ } else {
+ if (isInstanceMethod) {
+ mlistp = &proto->optionalInstanceMethods;
+ } else {
+ mlistp = &proto->optionalClassMethods;
+ }
+ }
+
+ if (*mlistp) {
+ method_list_t *mlist = *mlistp;
+ if (!isMethodListFixedUp(mlist)) {
+ mlist = fixupMethodList(mlist, YES/*always copy for simplicity*/);
+ *mlistp = mlist;
+ }
+ for (i = 0; i < mlist->count; i++) {
+ method_t *m = method_list_nth(mlist, i);
+ if (sel == m->name) return (Method)m;
+ }
+ }
+
+ if (proto->protocols) {
+ Method m;
+ for (i = 0; i < proto->protocols->count; i++) {
+ protocol_t *realProto = remapProtocol(proto->protocols->list[i]);
+ m = _protocol_getMethod_nolock(realProto, sel,
+ isRequiredMethod, isInstanceMethod);
+ if (m) return m;
+ }
+ }
+
+ return NULL;
+}
+
+
+/***********************************************************************
+* _protocol_getMethod
+* fixme
+* Locking: write-locks runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN Method
+_protocol_getMethod(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod)
+{
+ rwlock_write(&runtimeLock);
+ Method result = _protocol_getMethod_nolock(newprotocol(p), sel,
+ isRequiredMethod,
+ isInstanceMethod);
+ rwlock_unlock_write(&runtimeLock);
+ return result;
+}
+
+
+/***********************************************************************
+* protocol_getName
+* Returns the name of the given protocol.
+* Locking: runtimeLock must not be held by the caller
+**********************************************************************/
+const char *
+protocol_getName(Protocol *proto)
+{
+ return newprotocol(proto)->name;
+}
+
+
+/***********************************************************************
+* protocol_getInstanceMethodDescription
+* Returns the description of a named instance method.
+* Locking: runtimeLock must not be held by the caller
+**********************************************************************/
+struct objc_method_description
+protocol_getMethodDescription(Protocol *p, SEL aSel,
+ BOOL isRequiredMethod, BOOL isInstanceMethod)
+{
+ Method m =
+ _protocol_getMethod(p, aSel, isRequiredMethod, isInstanceMethod);
+ if (m) return *method_getDescription(m);
+ else return (struct objc_method_description){NULL, NULL};
+}
+
+
+/***********************************************************************
+* _protocol_conformsToProtocol_nolock
+* Returns YES if self conforms to other.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static BOOL _protocol_conformsToProtocol_nolock(protocol_t *self, protocol_t *other)
+{
+ if (!self || !other) {
+ return NO;
+ }
+
+ if (0 == strcmp(self->name, other->name)) {
+ return YES;
+ }
+
+ if (self->protocols) {
+ uintptr_t i;
+ for (i = 0; i < self->protocols->count; i++) {
+ protocol_t *proto = remapProtocol(self->protocols->list[i]);
+ if (0 == strcmp(other->name, proto->name)) {
+ return YES;
+ }
+ if (_protocol_conformsToProtocol_nolock(proto, other)) {
+ return YES;
+ }
+ }
+ }
+
+ return NO;
+}
+
+
+/***********************************************************************
+* protocol_conformsToProtocol
+* Returns YES if self conforms to other.
+* Locking: acquires runtimeLock
+**********************************************************************/
+BOOL protocol_conformsToProtocol(Protocol *self, Protocol *other)
+{
+ BOOL result;
+ rwlock_read(&runtimeLock);
+ result = _protocol_conformsToProtocol_nolock(newprotocol(self),
+ newprotocol(other));
+ rwlock_unlock_read(&runtimeLock);
+ return result;
+}
+
+
+/***********************************************************************
+* protocol_isEqual
+* Return YES if two protocols are equal (i.e. conform to each other)
+* Locking: acquires runtimeLock
+**********************************************************************/
+BOOL protocol_isEqual(Protocol *self, Protocol *other)
+{
+ if (self == other) return YES;
+ if (!self || !other) return NO;
+
+ if (!protocol_conformsToProtocol(self, other)) return NO;
+ if (!protocol_conformsToProtocol(other, self)) return NO;
+
+ return YES;
+}
+
+
+/***********************************************************************
+* protocol_copyMethodDescriptionList
+* Returns descriptions of a protocol's methods.
+* Locking: acquires runtimeLock
+**********************************************************************/
+struct objc_method_description *
+protocol_copyMethodDescriptionList(Protocol *p,
+ BOOL isRequiredMethod,BOOL isInstanceMethod,
+ unsigned int *outCount)
+{
+ protocol_t *proto = newprotocol(p);
+ struct objc_method_description *result = NULL;
+ unsigned int count = 0;
+
+ if (!proto) {
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ rwlock_read(&runtimeLock);
+
+ method_list_t *mlist = NULL;
+
+ if (isRequiredMethod) {
+ if (isInstanceMethod) {
+ mlist = proto->instanceMethods;
+ } else {
+ mlist = proto->classMethods;
+ }
+ } else {
+ if (isInstanceMethod) {
+ mlist = proto->optionalInstanceMethods;
+ } else {
+ mlist = proto->optionalClassMethods;
+ }
+ }
+
+ if (mlist) {
+ unsigned int i;
+ count = mlist->count;
+ result = (struct objc_method_description *)
+ calloc(count + 1, sizeof(struct objc_method_description));
+ for (i = 0; i < count; i++) {
+ method_t *m = method_list_nth(mlist, i);
+ result[i].name = sel_registerName((const char *)m->name);
+ result[i].types = (char *)m->types;
+ }
+ }
+
+ rwlock_unlock_read(&runtimeLock);
+
+ if (outCount) *outCount = count;
+ return result;
+}
+
+
+/***********************************************************************
+* protocol_getProperty
+* fixme
+* Locking: acquires runtimeLock
+**********************************************************************/
+static property_t *
+_protocol_getProperty_nolock(protocol_t *proto, const char *name,
+ BOOL isRequiredProperty, BOOL isInstanceProperty)
+{
+ if (!isRequiredProperty || !isInstanceProperty) {
+ // Only required instance properties are currently supported
+ return NULL;
+ }
+
+ property_list_t *plist;
+ if ((plist = proto->instanceProperties)) {
+ uint32_t i;
+ for (i = 0; i < plist->count; i++) {
+ property_t *prop = property_list_nth(plist, i);
+ if (0 == strcmp(name, prop->name)) {
+ return prop;
+ }
+ }
+ }
+
+ if (proto->protocols) {
+ uintptr_t i;
+ for (i = 0; i < proto->protocols->count; i++) {
+ protocol_t *p = remapProtocol(proto->protocols->list[i]);
+ property_t *prop =
+ _protocol_getProperty_nolock(p, name,
+ isRequiredProperty,
+ isInstanceProperty);
+ if (prop) return prop;
+ }
+ }
+
+ return NULL;
+}
+
+objc_property_t protocol_getProperty(Protocol *p, const char *name,
+ BOOL isRequiredProperty, BOOL isInstanceProperty)
+{
+ property_t *result;
+
+ if (!p || !name) return NULL;
+
+ rwlock_read(&runtimeLock);
+ result = _protocol_getProperty_nolock(newprotocol(p), name,
+ isRequiredProperty,
+ isInstanceProperty);
+ rwlock_unlock_read(&runtimeLock);
+
+ return (objc_property_t)result;
+}
+
+
+/***********************************************************************
+* protocol_copyPropertyList
+* fixme
+* Locking: acquires runtimeLock
+**********************************************************************/
+static property_t **
+copyPropertyList(property_list_t *plist, unsigned int *outCount)
+{
+ property_t **result = NULL;
+ unsigned int count = 0;
+
+ if (plist) {
+ count = plist->count;
+ }
+
+ if (count > 0) {
+ unsigned int i;
+ result = (property_t **)malloc((count+1) * sizeof(property_t *));
+
+ for (i = 0; i < count; i++) {
+ result[i] = property_list_nth(plist, i);
+ }
+ result[i] = NULL;
+ }
+
+ if (outCount) *outCount = count;
+ return result;
+}
+
+objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
+{
+ property_t **result = NULL;
+
+ if (!proto) {
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ rwlock_read(&runtimeLock);
+
+ property_list_t *plist = newprotocol(proto)->instanceProperties;
+ result = copyPropertyList(plist, outCount);
+
+ rwlock_unlock_read(&runtimeLock);
+
+ return (objc_property_t *)result;
+}
+
+
+/***********************************************************************
+* protocol_copyProtocolList
+* Copies this protocol's incorporated protocols.
+* Does not copy those protocol's incorporated protocols in turn.
+* Locking: acquires runtimeLock
+**********************************************************************/
+Protocol * __unsafe_unretained *
+protocol_copyProtocolList(Protocol *p, unsigned int *outCount)
+{
+ unsigned int count = 0;
+ Protocol **result = NULL;
+ protocol_t *proto = newprotocol(p);
+
+ if (!proto) {
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ rwlock_read(&runtimeLock);
+
+ if (proto->protocols) {
+ count = (unsigned int)proto->protocols->count;
+ }
+ if (count > 0) {
+ result = (Protocol **)malloc((count+1) * sizeof(Protocol *));
+
+ unsigned int i;
+ for (i = 0; i < count; i++) {
+ result[i] = (Protocol *)remapProtocol(proto->protocols->list[i]);
+ }
+ result[i] = NULL;
+ }
+
+ rwlock_unlock_read(&runtimeLock);
+
+ if (outCount) *outCount = count;
+ return result;
+}
+
+
+/***********************************************************************
+* objc_allocateProtocol
+* Creates a new protocol. The protocol may not be used until
+* objc_registerProtocol() is called.
+* Returns NULL if a protocol with the same name already exists.
+* Locking: acquires runtimeLock
+**********************************************************************/
+Protocol *
+objc_allocateProtocol(const char *name)
+{
+ rwlock_write(&runtimeLock);
+
+ if (NXMapGet(protocols(), name)) {
+ rwlock_unlock_write(&runtimeLock);
+ return NULL;
+ }
+
+ protocol_t *result = (protocol_t *)_calloc_internal(sizeof(protocol_t), 1);
+
+ extern class_t OBJC_CLASS_$___IncompleteProtocol;
+ Class cls = (Class)&OBJC_CLASS_$___IncompleteProtocol;
+ result->isa = cls;
+ result->name = _strdup_internal(name);
+
+ // fixme reserve name without installing
+
+ rwlock_unlock_write(&runtimeLock);
+
+ return (Protocol *)result;
+}
+
+
+/***********************************************************************
+* objc_registerProtocol
+* Registers a newly-constructed protocol. The protocol is now
+* ready for use and immutable.
+* Locking: acquires runtimeLock
+**********************************************************************/
+void objc_registerProtocol(Protocol *proto_gen)
+{
+ protocol_t *proto = newprotocol(proto_gen);
+
+ rwlock_write(&runtimeLock);
+
+ extern class_t OBJC_CLASS_$___IncompleteProtocol;
+ Class oldcls = (Class)&OBJC_CLASS_$___IncompleteProtocol;
+ extern class_t OBJC_CLASS_$_Protocol;
+ Class cls = (Class)&OBJC_CLASS_$_Protocol;
+
+ if (proto->isa == cls) {
+ _objc_inform("objc_registerProtocol: protocol '%s' was already "
+ "registered!", proto->name);
+ rwlock_unlock_write(&runtimeLock);
+ return;
+ }
+ if (proto->isa != oldcls) {
+ _objc_inform("objc_registerProtocol: protocol '%s' was not allocated "
+ "with objc_allocateProtocol!", proto->name);
+ rwlock_unlock_write(&runtimeLock);
+ return;
+ }
+
+ proto->isa = cls;
+
+ NXMapKeyCopyingInsert(protocols(), proto->name, proto);
+
+ rwlock_unlock_write(&runtimeLock);
+}
+
+
+/***********************************************************************
+* protocol_addProtocol
+* Adds an incorporated protocol to another protocol.
+* No method enforcement is performed.
+* `proto` must be under construction. `addition` must not.
+* Locking: acquires runtimeLock
+**********************************************************************/
+void
+protocol_addProtocol(Protocol *proto_gen, Protocol *addition_gen)
+{
+ protocol_t *proto = newprotocol(proto_gen);
+ protocol_t *addition = newprotocol(addition_gen);
+
+ extern class_t OBJC_CLASS_$___IncompleteProtocol;
+ Class cls = (Class)&OBJC_CLASS_$___IncompleteProtocol;
+
+ if (!proto_gen) return;
+ if (!addition_gen) return;
+
+ rwlock_write(&runtimeLock);
+
+ if (proto->isa != cls) {
+ _objc_inform("protocol_addProtocol: modified protocol '%s' is not "
+ "under construction!", proto->name);
+ rwlock_unlock_write(&runtimeLock);
+ return;
+ }
+ if (addition->isa == cls) {
+ _objc_inform("protocol_addProtocol: added protocol '%s' is still "
+ "under construction!", addition->name);
+ rwlock_unlock_write(&runtimeLock);
+ return;
+ }
+
+ protocol_list_t *protolist = proto->protocols;
+ if (!protolist) {
+ protolist = (protocol_list_t *)
+ _calloc_internal(1, sizeof(protocol_list_t)
+ + sizeof(protolist->list[0]));
+ } else {
+ protolist = (protocol_list_t *)
+ _realloc_internal(protolist, protocol_list_size(protolist)
+ + sizeof(protolist->list[0]));
+ }
+
+ protolist->list[protolist->count++] = (protocol_ref_t)addition;
+ proto->protocols = protolist;
+
+ rwlock_unlock_write(&runtimeLock);
+}
+
+
+/***********************************************************************
+* protocol_addMethodDescription
+* Adds a method to a protocol. The protocol must be under construction.
+* Locking: acquires runtimeLock
+**********************************************************************/
+static void
+_protocol_addMethod(method_list_t **list, SEL name, const char *types)
+{
+ if (!*list) {
+ *list = (method_list_t *)
+ _calloc_internal(sizeof(method_list_t), 1);
+ (*list)->entsize_NEVER_USE = sizeof((*list)->first);
+ setMethodListFixedUp(*list);
+ } else {
+ size_t size = method_list_size(*list) + method_list_entsize(*list);
+ *list = (method_list_t *)
+ _realloc_internal(*list, size);
+ }
+
+ method_t *meth = method_list_nth(*list, (*list)->count++);
+ meth->name = name;
+ meth->types = _strdup_internal(types ? types : "");
+ meth->imp = NULL;
+}
+
+void
+protocol_addMethodDescription(Protocol *proto_gen, SEL name, const char *types,
+ BOOL isRequiredMethod, BOOL isInstanceMethod)
+{
+ protocol_t *proto = newprotocol(proto_gen);
+
+ extern class_t OBJC_CLASS_$___IncompleteProtocol;
+ Class cls = (Class)&OBJC_CLASS_$___IncompleteProtocol;
+
+ if (!proto_gen) return;
+
+ rwlock_write(&runtimeLock);
+
+ if (proto->isa != cls) {
+ _objc_inform("protocol_addMethodDescription: protocol '%s' is not "
+ "under construction!", proto->name);
+ rwlock_unlock_write(&runtimeLock);
+ return;
+ }
+
+ if (isRequiredMethod && isInstanceMethod) {
+ _protocol_addMethod(&proto->instanceMethods, name, types);
+ } else if (isRequiredMethod && !isInstanceMethod) {
+ _protocol_addMethod(&proto->classMethods, name, types);
+ } else if (!isRequiredMethod && isInstanceMethod) {
+ _protocol_addMethod(&proto->optionalInstanceMethods, name, types);
+ } else /* !isRequiredMethod && !isInstanceMethod) */ {
+ _protocol_addMethod(&proto->optionalClassMethods, name, types);
+ }
+
+ rwlock_unlock_write(&runtimeLock);
+}
+
+
+/***********************************************************************
+* protocol_addProperty
+* Adds a property to a protocol. The protocol must be under construction.
+* Locking: acquires runtimeLock
+**********************************************************************/
+static void
+_protocol_addProperty(property_list_t **plist, const char *name,
+ const objc_property_attribute_t *attrs,
+ unsigned int count)
+{
+ if (!*plist) {
+ *plist = (property_list_t *)
+ _calloc_internal(sizeof(property_list_t), 1);
+ (*plist)->entsize = sizeof(property_t);
+ } else {
+ *plist = (property_list_t *)
+ _realloc_internal(*plist, sizeof(property_list_t)
+ + (*plist)->count * (*plist)->entsize);
+ }
+
+ property_t *prop = property_list_nth(*plist, (*plist)->count++);
+ prop->name = _strdup_internal(name);
+ prop->attributes = copyPropertyAttributeString(attrs, count);
+}
+
+void
+protocol_addProperty(Protocol *proto_gen, const char *name,
+ const objc_property_attribute_t *attrs,
+ unsigned int count,
+ BOOL isRequiredProperty, BOOL isInstanceProperty)
+{
+ protocol_t *proto = newprotocol(proto_gen);
+
+ extern class_t OBJC_CLASS_$___IncompleteProtocol;
+ Class cls = (Class)&OBJC_CLASS_$___IncompleteProtocol;
+
+ if (!proto) return;
+ if (!name) return;
+
+ rwlock_write(&runtimeLock);
+
+ if (proto->isa != cls) {
+ _objc_inform("protocol_addProperty: protocol '%s' is not "
+ "under construction!", proto->name);
+ rwlock_unlock_write(&runtimeLock);
+ return;
+ }
+
+ if (isRequiredProperty && isInstanceProperty) {
+ _protocol_addProperty(&proto->instanceProperties, name, attrs, count);
+ }
+ //else if (isRequiredProperty && !isInstanceProperty) {
+ // _protocol_addProperty(&proto->classProperties, name, attrs, count);
+ //} else if (!isRequiredProperty && isInstanceProperty) {
+ // _protocol_addProperty(&proto->optionalInstanceProperties, name, attrs, count);
+ //} else /* !isRequiredProperty && !isInstanceProperty) */ {
+ // _protocol_addProperty(&proto->optionalClassProperties, name, attrs, count);
+ //}
+
+ rwlock_unlock_write(&runtimeLock);
+}
+
+
+/***********************************************************************
+* objc_getClassList
+* Returns pointers to all classes.
+* This requires all classes be realized, which is regretfully non-lazy.
+* Locking: acquires runtimeLock
+**********************************************************************/
+int
+objc_getClassList(Class *buffer, int bufferLen)
+{
+ rwlock_write(&runtimeLock);
+
+ realizeAllClasses();
+
+ int count;
+ class_t *cls;
+ NXHashState state;
+ NXHashTable *classes = realizedClasses();
+ int allCount = NXCountHashTable(classes);
+
+ if (!buffer) {
+ rwlock_unlock_write(&runtimeLock);
+ return allCount;
+ }
+
+ count = 0;
+ state = NXInitHashState(classes);
+ while (count < bufferLen &&
+ NXNextHashState(classes, &state, (void **)&cls))
+ {
+ buffer[count++] = (Class)cls;
+ }
+
+ rwlock_unlock_write(&runtimeLock);
+
+ return allCount;
+}
+
+
+/***********************************************************************
+* objc_copyClassList
+* Returns pointers to all classes.
+* This requires all classes be realized, which is regretfully non-lazy.
+*
+* outCount may be NULL. *outCount is the number of classes returned.
+* If the returned array is not NULL, it is NULL-terminated and must be
+* freed with free().
+* Locking: write-locks runtimeLock
+**********************************************************************/
+Class *
+objc_copyClassList(unsigned int *outCount)
+{
+ rwlock_write(&runtimeLock);
+
+ realizeAllClasses();
+
+ Class *result = NULL;
+ NXHashTable *classes = realizedClasses();
+ unsigned int count = NXCountHashTable(classes);
+
+ if (count > 0) {
+ class_t *cls;
+ NXHashState state = NXInitHashState(classes);
+ result = (Class *)malloc((1+count) * sizeof(Class));
+ count = 0;
+ while (NXNextHashState(classes, &state, (void **)&cls)) {
+ result[count++] = (Class)cls;
+ }
+ result[count] = NULL;
+ }
+
+ rwlock_unlock_write(&runtimeLock);
+
+ if (outCount) *outCount = count;
+ return result;
+}
+
+
+/***********************************************************************
+* objc_copyProtocolList
+* Returns pointers to all protocols.
+* Locking: read-locks runtimeLock
+**********************************************************************/
+Protocol * __unsafe_unretained *
+objc_copyProtocolList(unsigned int *outCount)
+{
+ rwlock_read(&runtimeLock);
+
+ unsigned int count, i;
+ Protocol *proto;
+ const char *name;
+ NXMapState state;
+ NXMapTable *protocol_map = protocols();
+ Protocol **result;
+
+ count = NXCountMapTable(protocol_map);
+ if (count == 0) {
+ rwlock_unlock_read(&runtimeLock);
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ result = (Protocol **)calloc(1 + count, sizeof(Protocol *));
+
+ i = 0;
+ state = NXInitMapState(protocol_map);
+ while (NXNextMapState(protocol_map, &state,
+ (const void **)&name, (const void **)&proto))
+ {
+ result[i++] = proto;
+ }
+
+ result[i++] = NULL;
+ assert(i == count+1);
+
+ rwlock_unlock_read(&runtimeLock);
+
+ if (outCount) *outCount = count;
+ return result;
+}
+
+
+/***********************************************************************
+* objc_getProtocol
+* Get a protocol by name, or return NULL
+* Locking: read-locks runtimeLock
+**********************************************************************/
+Protocol *objc_getProtocol(const char *name)
+{
+ rwlock_read(&runtimeLock);
+ Protocol *result = (Protocol *)NXMapGet(protocols(), name);
+ rwlock_unlock_read(&runtimeLock);
+ return result;
+}
+
+
+/***********************************************************************
+* class_copyMethodList
+* fixme
+* Locking: read-locks runtimeLock
+**********************************************************************/
+Method *
+class_copyMethodList(Class cls_gen, unsigned int *outCount)
+{
+ class_t *cls = newcls(cls_gen);
+ unsigned int count = 0;
+ Method *result = NULL;
+
+ if (!cls) {
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ rwlock_read(&runtimeLock);
+
+ assert(isRealized(cls));
+
+ FOREACH_METHOD_LIST(mlist, cls, {
+ count += mlist->count;
+ });
+
+ if (count > 0) {
+ unsigned int m;
+ result = (Method *)malloc((count + 1) * sizeof(Method));
+
+ m = 0;
+ FOREACH_METHOD_LIST(mlist, cls, {
+ unsigned int i;
+ for (i = 0; i < mlist->count; i++) {
+ Method aMethod = (Method)method_list_nth(mlist, i);
+ if (ignoreSelector(method_getName(aMethod))) {
+ count--;
+ continue;
+ }
+ result[m++] = aMethod;
+ }
+ });
+ result[m] = NULL;
+ }
+
+ rwlock_unlock_read(&runtimeLock);
+
+ if (outCount) *outCount = count;
+ return result;
+}
+
+
+/***********************************************************************
+* class_copyIvarList
+* fixme
+* Locking: read-locks runtimeLock
+**********************************************************************/
+Ivar *
+class_copyIvarList(Class cls_gen, unsigned int *outCount)
+{
+ class_t *cls = newcls(cls_gen);
+ const ivar_list_t *ivars;
+ Ivar *result = NULL;
+ unsigned int count = 0;
+ unsigned int i;
+
+ if (!cls) {
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ rwlock_read(&runtimeLock);
+
+ assert(isRealized(cls));
+
+ if ((ivars = cls->data()->ro->ivars) && ivars->count) {
+ result = (Ivar *)malloc((ivars->count+1) * sizeof(Ivar));
+
+ for (i = 0; i < ivars->count; i++) {
+ ivar_t *ivar = ivar_list_nth(ivars, i);
+ if (!ivar->offset) continue; // anonymous bitfield
+ result[count++] = (Ivar)ivar;
+ }
+ result[count] = NULL;
+ }
+
+ rwlock_unlock_read(&runtimeLock);
+
+ if (outCount) *outCount = count;
+ return result;
+}
+
+
+/***********************************************************************
+* class_copyPropertyList. Returns a heap block containing the
+* properties declared in the class, or NULL if the class
+* declares no properties. Caller must free the block.
+* Does not copy any superclass's properties.
+* Locking: read-locks runtimeLock
+**********************************************************************/
+objc_property_t *
+class_copyPropertyList(Class cls_gen, unsigned int *outCount)
+{
+ class_t *cls = newcls(cls_gen);
+ chained_property_list *plist;
+ unsigned int count = 0;
+ property_t **result = NULL;
+
+ if (!cls) {
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ rwlock_read(&runtimeLock);
+
+ assert(isRealized(cls));
+
+ for (plist = cls->data()->properties; plist; plist = plist->next) {
+ count += plist->count;
+ }
+
+ if (count > 0) {
+ unsigned int p;
+ result = (property_t **)malloc((count + 1) * sizeof(property_t *));
+
+ p = 0;
+ for (plist = cls->data()->properties; plist; plist = plist->next) {
+ unsigned int i;
+ for (i = 0; i < plist->count; i++) {
+ result[p++] = &plist->list[i];
+ }
+ }
+ result[p] = NULL;
+ }
+
+ rwlock_unlock_read(&runtimeLock);
+
+ if (outCount) *outCount = count;
+ return (objc_property_t *)result;
+}
+
+
+/***********************************************************************
+* _class_getLoadMethod
+* fixme
+* Called only from add_class_to_loadable_list.
+* Locking: runtimeLock must be read- or write-locked by the caller.
+**********************************************************************/
+PRIVATE_EXTERN IMP
+_class_getLoadMethod(Class cls_gen)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ class_t *cls = newcls(cls_gen);
+ const method_list_t *mlist;
+ uint32_t i;
+
+ assert(isRealized(cls));
+ assert(isRealized(cls->isa));
+ assert(!isMetaClass(cls));
+ assert(isMetaClass(cls->isa));
+
+ mlist = cls->isa->data()->ro->baseMethods;
+ if (mlist) for (i = 0; i < mlist->count; i++) {
+ method_t *m = method_list_nth(mlist, i);
+ if (0 == strcmp((const char *)m->name, "load")) {
+ return m->imp;
+ }
+ }
+
+ return NULL;
+}
+
+
+/***********************************************************************
+* _category_getName
+* Returns a category's name.
+* Locking: none
+**********************************************************************/
+PRIVATE_EXTERN const char *
+_category_getName(Category cat)
+{
+ return newcategory(cat)->name;
+}
+
+
+/***********************************************************************
+* _category_getClassName
+* Returns a category's class's name
+* Called only from add_category_to_loadable_list and
+* remove_category_from_loadable_list.
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+PRIVATE_EXTERN const char *
+_category_getClassName(Category cat)
+{
+ rwlock_assert_locked(&runtimeLock);
+ // cat->cls may have been remapped
+ return getName(remapClass(newcategory(cat)->cls));
+}
+
+
+/***********************************************************************
+* _category_getClass
+* Returns a category's class
+* Called only by call_category_loads.
+* Locking: read-locks runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN Class
+_category_getClass(Category cat)
+{
+ rwlock_read(&runtimeLock);
+ // cat->cls may have been remapped
+ class_t *result = remapClass(newcategory(cat)->cls);
+ assert(isRealized(result)); // ok for call_category_loads' usage
+ rwlock_unlock_read(&runtimeLock);
+ return (Class)result;
+}
+
+
+/***********************************************************************
+* _category_getLoadMethod
+* fixme
+* Called only from add_category_to_loadable_list
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+PRIVATE_EXTERN IMP
+_category_getLoadMethod(Category cat)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ const method_list_t *mlist;
+ uint32_t i;
+
+ mlist = newcategory(cat)->classMethods;
+ if (mlist) for (i = 0; i < mlist->count; i++) {
+ method_t *m = method_list_nth(mlist, i);
+ if (0 == strcmp((const char *)m->name, "load")) {
+ return m->imp;
+ }
+ }
+
+ return NULL;
+}
+
+
+/***********************************************************************
+* class_copyProtocolList
+* fixme
+* Locking: read-locks runtimeLock
+**********************************************************************/
+Protocol * __unsafe_unretained *
+class_copyProtocolList(Class cls_gen, unsigned int *outCount)
+{
+ class_t *cls = newcls(cls_gen);
+ Protocol **r;
+ const protocol_list_t **p;
+ unsigned int count = 0;
+ unsigned int i;
+ Protocol **result = NULL;
+
+ if (!cls) {
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ rwlock_read(&runtimeLock);
+
+ assert(isRealized(cls));
+
+ for (p = cls->data()->protocols; p && *p; p++) {
+ count += (uint32_t)(*p)->count;
+ }
+
+ if (count) {
+ result = (Protocol **)malloc((count+1) * sizeof(Protocol *));
+ r = result;
+ for (p = cls->data()->protocols; p && *p; p++) {
+ for (i = 0; i < (*p)->count; i++) {
+ *r++ = (Protocol *)remapProtocol((*p)->list[i]);
+ }
+ }
+ *r++ = NULL;
+ }
+
+ rwlock_unlock_read(&runtimeLock);
+
+ if (outCount) *outCount = count;
+ return result;
+}
+
+
+/***********************************************************************
+* _objc_copyClassNamesForImage
+* fixme
+* Locking: read-locks runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN const char **
+_objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount)
+{
+ size_t count, i, shift;
+ class_t **classlist;
+ const char **names;
+
+ rwlock_read(&runtimeLock);
+
+ classlist = _getObjc2ClassList(hi, &count);
+ names = (const char **)malloc((count+1) * sizeof(const char *));
+
+ shift = 0;
+ for (i = 0; i < count; i++) {
+ class_t *cls = remapClass(classlist[i]);
+ if (cls) {
+ names[i-shift] = getName(classlist[i]);
+ } else {
+ shift++; // ignored weak-linked class
+ }
+ }
+ count -= shift;
+ names[count] = NULL;
+
+ rwlock_unlock_read(&runtimeLock);
+
+ if (outCount) *outCount = (unsigned int)count;
+ return names;
+}
+
+
+/***********************************************************************
+* _class_getCache
+* fixme
+* Locking: none
+**********************************************************************/
+PRIVATE_EXTERN Cache
+_class_getCache(Class cls)
+{
+ return newcls(cls)->cache;
+}
+
+
+/***********************************************************************
+* _class_getInstanceSize
+* Uses alignedInstanceSize() to ensure that
+* obj + class_getInstanceSize(obj->isa) == object_getIndexedIvars(obj)
+* Locking: none
+**********************************************************************/
+PRIVATE_EXTERN size_t
+_class_getInstanceSize(Class cls)
+{
+ if (!cls) return 0;
+ return alignedInstanceSize(newcls(cls));
+}
+
+static uint32_t
+unalignedInstanceSize(class_t *cls)
+{
+ assert(cls);
+ assert(isRealized(cls));
+ return (uint32_t)cls->data()->ro->instanceSize;
+}
+
+static uint32_t
+alignedInstanceSize(class_t *cls)
+{
+ assert(cls);
+ assert(isRealized(cls));
+ // fixme rdar://5278267
+ return (uint32_t)((unalignedInstanceSize(cls) + WORD_MASK) & ~WORD_MASK);
+}
+
+/***********************************************************************
+ * _class_getInstanceStart
+ * Uses alignedInstanceStart() to ensure that ARR layout strings are
+ * interpreted relative to the first word aligned ivar of an object.
+ * Locking: none
+ **********************************************************************/
+
+static uint32_t
+alignedInstanceStart(class_t *cls)
+{
+ assert(cls);
+ assert(isRealized(cls));
+ return (uint32_t)((cls->data()->ro->instanceStart + WORD_MASK) & ~WORD_MASK);
+}
+
+PRIVATE_EXTERN
+uint32_t _class_getInstanceStart(Class cls_gen) {
+ class_t *cls = newcls(cls_gen);
+ return alignedInstanceStart(cls);
+}
+
+
+/***********************************************************************
+* class_getVersion
+* fixme
+* Locking: none
+**********************************************************************/
+int
+class_getVersion(Class cls)
+{
+ if (!cls) return 0;
+ assert(isRealized(newcls(cls)));
+ return newcls(cls)->data()->version;
+}
+
+
+/***********************************************************************
+* _class_setCache
+* fixme
+* Locking: none
+**********************************************************************/
+PRIVATE_EXTERN void
+_class_setCache(Class cls, Cache cache)
+{
+ newcls(cls)->cache = cache;
+}
+
+
+/***********************************************************************
+* class_setVersion
+* fixme
+* Locking: none
+**********************************************************************/
+void
+class_setVersion(Class cls, int version)
+{
+ if (!cls) return;
+ assert(isRealized(newcls(cls)));
+ newcls(cls)->data()->version = version;
+}
+
+
+/***********************************************************************
+* _class_getName
+* fixme
+* Locking: acquires runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN const char *_class_getName(Class cls)
+{
+ if (!cls) return "nil";
+ // fixme hack rwlock_write(&runtimeLock);
+ const char *name = getName(newcls(cls));
+ // rwlock_unlock_write(&runtimeLock);
+ return name;
+}
+
+
+/***********************************************************************
+* getName
+* fixme
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static const char *
+getName(class_t *cls)
+{
+ // fixme hack rwlock_assert_writing(&runtimeLock);
+ assert(cls);
+
+ if (isRealized(cls)) {
+ return cls->data()->ro->name;
+ } else {
+ return ((const class_ro_t *)cls->data())->name;
+ }
+}
+
+static method_t *findMethodInSortedMethodList(SEL key, const method_list_t *list)
+{
+ const method_t * const first = &list->first;
+ const method_t *base = first;
+ const method_t *probe;
+ uintptr_t keyValue = (uintptr_t)key;
+ uint32_t count;
+
+ for (count = list->count; count != 0; count >>= 1) {
+ probe = base + (count >> 1);
+
+ uintptr_t probeValue = (uintptr_t)probe->name;
+
+ if (keyValue == probeValue) {
+ // `probe` is a match.
+ // Rewind looking for the *first* occurrence of this value.
+ // This is required for correct category overrides.
+ while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
+ probe--;
+ }
+ return (method_t *)probe;
+ }
+
+ if (keyValue > probeValue) {
+ base = probe + 1;
+ count--;
+ }
+ }
+
+ return NULL;
+}
+
+/***********************************************************************
+* getMethodNoSuper_nolock
+* fixme
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+static method_t *search_method_list(const method_list_t *mlist, SEL sel)
+{
+ int methodListIsFixedUp = isMethodListFixedUp(mlist);
+ int methodListHasExpectedSize = mlist->getEntsize() == sizeof(method_t);
+
+ if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
+ return findMethodInSortedMethodList(sel, mlist);
+ } else {
+ // Linear search of unsorted method list
+ method_list_t::method_iterator iter = mlist->begin();
+ method_list_t::method_iterator end = mlist->end();
+ for ( ; iter != end; ++iter) {
+ if (iter->name == sel) return &*iter;
+ }
+ }
+
+#ifndef NDEBUG
+ // sanity-check negative results
+ if (isMethodListFixedUp(mlist)) {
+ method_list_t::method_iterator iter = mlist->begin();
+ method_list_t::method_iterator end = mlist->end();
+ for ( ; iter != end; ++iter) {
+ if (iter->name == sel) {
+ _objc_fatal("linear search worked when binary search did not");
+ }
+ }
+ }
+#endif
+
+ return NULL;
+}
+
+static method_t *
+getMethodNoSuper_nolock(class_t *cls, SEL sel)
+{
+ rwlock_assert_locked(&runtimeLock);
+
+ assert(isRealized(cls));
+ // fixme nil cls?
+ // fixme NULL sel?
+
+ FOREACH_METHOD_LIST(mlist, cls, {
+ method_t *m = search_method_list(mlist, sel);
+ if (m) return m;
+ });
+
+ return NULL;
+}
+
+
+/***********************************************************************
+* _class_getMethodNoSuper
+* fixme
+* Locking: read-locks runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN Method
+_class_getMethodNoSuper(Class cls, SEL sel)
+{
+ rwlock_read(&runtimeLock);
+ Method result = (Method)getMethodNoSuper_nolock(newcls(cls), sel);
+ rwlock_unlock_read(&runtimeLock);
+ return result;
+}
+
+/***********************************************************************
+* _class_getMethodNoSuper
+* For use inside lockForMethodLookup() only.
+* Locking: read-locks runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN Method
+_class_getMethodNoSuper_nolock(Class cls, SEL sel)
+{
+ return (Method)getMethodNoSuper_nolock(newcls(cls), sel);
+}
+
+
+/***********************************************************************
+* getMethod_nolock
+* fixme
+* Locking: runtimeLock must be read- or write-locked by the caller
+**********************************************************************/
+static method_t *
+getMethod_nolock(class_t *cls, SEL sel)
+{
+ method_t *m = NULL;
+
+ rwlock_assert_locked(&runtimeLock);
+
+ // fixme nil cls?
+ // fixme NULL sel?
+
+ assert(isRealized(cls));
+
+ while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == NULL) {
+ cls = getSuperclass(cls);
+ }
+
+ return m;
+}
+
+
+/***********************************************************************
+* _class_getMethod
+* fixme
+* Locking: read-locks runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN Method _class_getMethod(Class cls, SEL sel)
+{
+ Method m;
+ rwlock_read(&runtimeLock);
+ m = (Method)getMethod_nolock(newcls(cls), sel);
+ rwlock_unlock_read(&runtimeLock);
+ return m;
+}
+
+
+/***********************************************************************
+* ABI-specific lookUpMethod helpers.
+* Locking: read- and write-locks runtimeLock.
+**********************************************************************/
+PRIVATE_EXTERN void lockForMethodLookup(void)
+{
+ rwlock_read(&runtimeLock);
+}
+PRIVATE_EXTERN void unlockForMethodLookup(void)
+{
+ rwlock_unlock_read(&runtimeLock);
+}
+
+PRIVATE_EXTERN IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init)
+{
+ rwlock_assert_unlocked(&runtimeLock);
+
+ if (!isRealized(newcls(cls))) {
+ rwlock_write(&runtimeLock);
+ realizeClass(newcls(cls));
+ rwlock_unlock_write(&runtimeLock);
+ }
+
+ if (init && !_class_isInitialized(cls)) {
+ _class_initialize (cls);
+ // If sel == initialize, _class_initialize will send +initialize and
+ // then the messenger will send +initialize again after this
+ // procedure finishes. Of course, if this is not being called
+ // from the messenger then it won't happen. 2778172
+ }
+
+ return NULL;
+}
+
+
+/***********************************************************************
+* class_getProperty
+* fixme
+* Locking: read-locks runtimeLock
+**********************************************************************/
+objc_property_t class_getProperty(Class cls_gen, const char *name)
+{
+ property_t *result = NULL;
+ chained_property_list *plist;
+ class_t *cls = newcls(cls_gen);
+
+ if (!cls || !name) return NULL;
+
+ rwlock_read(&runtimeLock);
+
+ assert(isRealized(cls));
+
+ for ( ; cls; cls = getSuperclass(cls)) {
+ for (plist = cls->data()->properties; plist; plist = plist->next) {
+ uint32_t i;
+ for (i = 0; i < plist->count; i++) {
+ if (0 == strcmp(name, plist->list[i].name)) {
+ result = &plist->list[i];
+ goto done;
+ }
+ }
+ }
+ }
+
+ done:
+ rwlock_unlock_read(&runtimeLock);
+
+ return (objc_property_t)result;
+}
+
+
+/***********************************************************************
+* Locking: fixme
+**********************************************************************/
+PRIVATE_EXTERN BOOL _class_isMetaClass(Class cls)
+{
+ if (!cls) return NO;
+ return isMetaClass(newcls(cls));
+}
+
+static BOOL
+isMetaClass(class_t *cls)
+{
+ assert(cls);
+ assert(isRealized(cls));
+ return (cls->data()->ro->flags & RO_META) ? YES : NO;
+}
+
+
+PRIVATE_EXTERN Class _class_getMeta(Class cls)
+{
+ assert(cls);
+ if (isMetaClass(newcls(cls))) return cls;
+ else return ((id)cls)->isa;
+}
+
+Class gdb_class_getClass(Class cls)
+{
+ const char *className = getName(newcls(cls));
+ if(!className || !strlen(className)) return Nil;
+ Class rCls = look_up_class(className, NO, NO);
+ return rCls;
+}
+
+Class gdb_object_getClass(id obj)
+{
+ Class cls = _object_getClass(obj);
+ return gdb_class_getClass(cls);
+}
+
+BOOL gdb_objc_isRuntimeLocked()
+{
+ if (rwlock_try_write(&runtimeLock)) {
+ rwlock_unlock_write(&runtimeLock);
+ } else
+ return YES;
+
+ if (mutex_try_lock(&cacheUpdateLock)) {
+ mutex_unlock(&cacheUpdateLock);
+ } else
+ return YES;
+
+ return NO;
+}
+
+/***********************************************************************
+* Locking: fixme
+**********************************************************************/
+PRIVATE_EXTERN BOOL
+_class_isInitializing(Class cls_gen)
+{
+ class_t *cls = newcls(_class_getMeta(cls_gen));
+ return (cls->data()->flags & RW_INITIALIZING) ? YES : NO;
+}
+
+
+/***********************************************************************
+* Locking: fixme
+**********************************************************************/
+PRIVATE_EXTERN BOOL
+_class_isInitialized(Class cls_gen)
+{
+ class_t *cls = newcls(_class_getMeta(cls_gen));
+ return (cls->data()->flags & RW_INITIALIZED) ? YES : NO;
+}
+
+
+/***********************************************************************
+* Locking: fixme
+**********************************************************************/
+PRIVATE_EXTERN void
+_class_setInitializing(Class cls_gen)
+{
+ class_t *cls = newcls(_class_getMeta(cls_gen));
+ changeInfo(cls, RW_INITIALIZING, 0);
+}
+
+
+/***********************************************************************
+* Locking: write-locks runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN void
+_class_setInitialized(Class cls_gen)
+{
+
+ class_t *metacls;
+ class_t *cls;
+
+ rwlock_write(&runtimeLock);
+ metacls = newcls(_class_getMeta(cls_gen));
+ cls = getNonMetaClass(metacls);
+
+ // Update vtables (initially postponed pending +initialize completion)
+ // Do cls first because root metacls is a subclass of root cls
+ updateVtable(cls, YES);
+ updateVtable(metacls, YES);
+
+ rwlock_unlock_write(&runtimeLock);
+
+ changeInfo(metacls, RW_INITIALIZED, RW_INITIALIZING);
+}
+
+
+/***********************************************************************
+* Locking: fixme
+**********************************************************************/
+PRIVATE_EXTERN BOOL
+_class_shouldGrowCache(Class cls)
+{
+ return YES; // fixme good or bad for memory use?
+}
+
+
+/***********************************************************************
+* Locking: fixme
+**********************************************************************/
+PRIVATE_EXTERN void
+_class_setGrowCache(Class cls, BOOL grow)
+{
+ // fixme good or bad for memory use?
+}
+
+
+/***********************************************************************
+* _class_isLoadable
+* fixme
+* Locking: none
+**********************************************************************/
+PRIVATE_EXTERN BOOL
+_class_isLoadable(Class cls)
+{
+ assert(isRealized(newcls(cls)));
+ return YES; // any class registered for +load is definitely loadable
+}
+
+
+/***********************************************************************
+* Locking: fixme
+**********************************************************************/
+static BOOL
+hasCxxStructors(class_t *cls)
+{
+ // this DOES check superclasses too, because addSubclass()
+ // propagates the flag from the superclass.
+ assert(isRealized(cls));
+ return (cls->data()->flags & RW_HAS_CXX_STRUCTORS) ? YES : NO;
+}
+
+PRIVATE_EXTERN BOOL
+_class_hasCxxStructors(Class cls)
+{
+ return hasCxxStructors(newcls(cls));
+}
+
+
+/***********************************************************************
+* Locking: fixme
+**********************************************************************/
+PRIVATE_EXTERN BOOL
+_class_shouldFinalizeOnMainThread(Class cls)
+{
+ assert(isRealized(newcls(cls)));
+ return (newcls(cls)->data()->flags & RW_FINALIZE_ON_MAIN_THREAD) ? YES : NO;
+}
+
+
+/***********************************************************************
+* Locking: fixme
+**********************************************************************/
+PRIVATE_EXTERN void
+_class_setFinalizeOnMainThread(Class cls)
+{
+ assert(isRealized(newcls(cls)));
+ changeInfo(newcls(cls), RW_FINALIZE_ON_MAIN_THREAD, 0);
+}
+
+
+/***********************************************************************
+* _class_instancesHaveAssociatedObjects
+* May manipulate unrealized future classes in the CF-bridged case.
+**********************************************************************/
+PRIVATE_EXTERN BOOL
+_class_instancesHaveAssociatedObjects(Class cls_gen)
+{
+ class_t *cls = newcls(cls_gen);
+ assert(isFuture(cls) || isRealized(cls));
+ return (cls->data()->flags & RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS) ? YES : NO;
+}
+
+
+/***********************************************************************
+* _class_setInstancesHaveAssociatedObjects
+* May manipulate unrealized future classes in the CF-bridged case.
+**********************************************************************/
+PRIVATE_EXTERN void
+_class_setInstancesHaveAssociatedObjects(Class cls_gen)
+{
+ class_t *cls = newcls(cls_gen);
+ assert(isFuture(cls) || isRealized(cls));
+ changeInfo(cls, RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS, 0);
+}
+
+
+/***********************************************************************
+ * _class_usesAutomaticRetainRelease
+ * Returns YES if class was compiled with -fobjc-arr
+ **********************************************************************/
+BOOL _class_usesAutomaticRetainRelease(Class cls_gen)
+{
+ class_t *cls = newcls(cls_gen);
+ return (cls->data()->ro->flags & RO_IS_ARR) ? YES : NO;
+}
+
+
+/***********************************************************************
+* Return YES if sel is used by retain/release implementors
+**********************************************************************/
+static BOOL isRRSelector(SEL sel)
+{
+ return (sel == SEL_retain || sel == SEL_release ||
+ sel == SEL_autorelease || sel == SEL_retainCount) ? YES : NO;
+}
+
+
+/***********************************************************************
+* Mark this class and all of its subclasses as implementors or
+* inheritors of custom RR (retain/release/autorelease/retainCount)
+**********************************************************************/
+void class_t::setHasCustomRR()
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ if (hasCustomRR()) return;
+
+ FOREACH_REALIZED_CLASS_AND_SUBCLASS(c, this, {
+ // rdar://8955342 c->data_NEVER_USE |= 1UL;
+ c->data()->flags |= RW_HAS_CUSTOM_RR;
+ });
+}
+
+
+/***********************************************************************
+* Unmark custom RR. Not recursive. Almost never used.
+**********************************************************************/
+void class_t::unsetHasCustomRR()
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ this->data_NEVER_USE &= ~1UL;
+}
+
+
+/***********************************************************************
+* Locking: none
+* fixme assert realized to get superclass remapping?
+**********************************************************************/
+PRIVATE_EXTERN Class
+_class_getSuperclass(Class cls)
+{
+ return (Class)getSuperclass(newcls(cls));
+}
+
+static class_t *
+getSuperclass(class_t *cls)
+{
+ if (!cls) return NULL;
+ return cls->superclass;
+}
+
+
+/***********************************************************************
+* class_getIvarLayout
+* Called by the garbage collector.
+* The class must be NULL or already realized.
+* Locking: none
+**********************************************************************/
+const uint8_t *
+class_getIvarLayout(Class cls_gen)
+{
+ class_t *cls = newcls(cls_gen);
+ if (cls) return cls->data()->ro->ivarLayout;
+ else return NULL;
+}
+
+
+/***********************************************************************
+* class_getWeakIvarLayout
+* Called by the garbage collector.
+* The class must be NULL or already realized.
+* Locking: none
+**********************************************************************/
+const uint8_t *
+class_getWeakIvarLayout(Class cls_gen)
+{
+ class_t *cls = newcls(cls_gen);
+ if (cls) return cls->data()->ro->weakIvarLayout;
+ else return NULL;
+}
+
+
+/***********************************************************************
+* class_setIvarLayout
+* Changes the class's GC scan layout.
+* NULL layout means no unscanned ivars
+* The class must be under construction.
+* fixme: sanity-check layout vs instance size?
+* fixme: sanity-check layout vs superclass?
+* Locking: acquires runtimeLock
+**********************************************************************/
+void
+class_setIvarLayout(Class cls_gen, const uint8_t *layout)
+{
+ class_t *cls = newcls(cls_gen);
+ if (!cls) return;
+
+ rwlock_write(&runtimeLock);
+
+ // Can only change layout of in-construction classes.
+ // note: if modifications to post-construction classes were
+ // allowed, there would be a race below (us vs. concurrent GC scan)
+ if (!(cls->data()->flags & RW_CONSTRUCTING)) {
+ _objc_inform("*** Can't set ivar layout for already-registered "
+ "class '%s'", getName(cls));
+ rwlock_unlock_write(&runtimeLock);
+ return;
+ }
+
+ class_ro_t *ro_w = make_ro_writeable(cls->data());
+
+ try_free(ro_w->ivarLayout);
+ ro_w->ivarLayout = _ustrdup_internal(layout);
+
+ rwlock_unlock_write(&runtimeLock);
+}
+
+// SPI: Instance-specific object layout.
+
+void
+_class_setIvarLayoutAccessor(Class cls_gen, const uint8_t* (*accessor) (id object)) {
+ class_t *cls = newcls(cls_gen);
+ if (!cls) return;
+
+ rwlock_write(&runtimeLock);
+
+ class_ro_t *ro_w = make_ro_writeable(cls->data());
+
+ // FIXME: this really isn't safe to free if there are instances of this class already.
+ if (!(cls->data()->flags & RW_HAS_INSTANCE_SPECIFIC_LAYOUT)) try_free(ro_w->ivarLayout);
+ ro_w->ivarLayout = (uint8_t *)accessor;
+ changeInfo(cls, RW_HAS_INSTANCE_SPECIFIC_LAYOUT, 0);
+
+ rwlock_unlock_write(&runtimeLock);
+}
+
+const uint8_t *
+_object_getIvarLayout(Class cls_gen, id object) {
+ class_t *cls = newcls(cls_gen);
+ if (cls) {
+ const uint8_t* layout = cls->data()->ro->ivarLayout;
+ if (cls->data()->flags & RW_HAS_INSTANCE_SPECIFIC_LAYOUT) {
+ const uint8_t* (*accessor) (id object) = (const uint8_t* (*)(id))layout;
+ layout = accessor(object);
+ }
+ return layout;
+ }
+ return NULL;
+}
+
+/***********************************************************************
+* class_setWeakIvarLayout
+* Changes the class's GC weak layout.
+* NULL layout means no weak ivars
+* The class must be under construction.
+* fixme: sanity-check layout vs instance size?
+* fixme: sanity-check layout vs superclass?
+* Locking: acquires runtimeLock
+**********************************************************************/
+void
+class_setWeakIvarLayout(Class cls_gen, const uint8_t *layout)
+{
+ class_t *cls = newcls(cls_gen);
+ if (!cls) return;
+
+ rwlock_write(&runtimeLock);
+
+ // Can only change layout of in-construction classes.
+ // note: if modifications to post-construction classes were
+ // allowed, there would be a race below (us vs. concurrent GC scan)
+ if (!(cls->data()->flags & RW_CONSTRUCTING)) {
+ _objc_inform("*** Can't set weak ivar layout for already-registered "
+ "class '%s'", getName(cls));
+ rwlock_unlock_write(&runtimeLock);
+ return;
+ }
+
+ class_ro_t *ro_w = make_ro_writeable(cls->data());
+
+ try_free(ro_w->weakIvarLayout);
+ ro_w->weakIvarLayout = _ustrdup_internal(layout);
+
+ rwlock_unlock_write(&runtimeLock);
+}
+
+
+/***********************************************************************
+* _class_getVariable
+* fixme
+* Locking: read-locks runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN Ivar
+_class_getVariable(Class cls, const char *name, Class *memberOf)
+{
+ rwlock_read(&runtimeLock);
+
+ for ( ; cls != Nil; cls = class_getSuperclass(cls)) {
+ ivar_t *ivar = getIvar(newcls(cls), name);
+ if (ivar) {
+ rwlock_unlock_read(&runtimeLock);
+ if (memberOf) *memberOf = cls;
+ return (Ivar)ivar;
+ }
+ }
+
+ rwlock_unlock_read(&runtimeLock);
+
+ return NULL;
+}
+
+
+/***********************************************************************
+* class_conformsToProtocol
+* fixme
+* Locking: read-locks runtimeLock
+**********************************************************************/
+BOOL class_conformsToProtocol(Class cls_gen, Protocol *proto_gen)
+{
+ class_t *cls = newcls(cls_gen);
+ protocol_t *proto = newprotocol(proto_gen);
+ const protocol_list_t **plist;
+ unsigned int i;
+ BOOL result = NO;
+
+ if (!cls_gen) return NO;
+ if (!proto_gen) return NO;
+
+ rwlock_read(&runtimeLock);
+
+ assert(isRealized(cls));
+
+ for (plist = cls->data()->protocols; plist && *plist; plist++) {
+ for (i = 0; i < (*plist)->count; i++) {
+ protocol_t *p = remapProtocol((*plist)->list[i]);
+ if (p == proto || _protocol_conformsToProtocol_nolock(p, proto)) {
+ result = YES;
+ goto done;
+ }
+ }
+ }
+
+ done:
+ rwlock_unlock_read(&runtimeLock);
+
+ return result;
+}
+
+
+/***********************************************************************
+* addMethod
+* fixme
+* Locking: runtimeLock must be held by the caller
+**********************************************************************/
+static IMP
+addMethod(class_t *cls, SEL name, IMP imp, const char *types, BOOL replace)
+{
+ IMP result = NULL;
+
+ rwlock_assert_writing(&runtimeLock);
+
+ assert(types);
+ assert(isRealized(cls));
+
+ method_t *m;
+ if ((m = getMethodNoSuper_nolock(cls, name))) {
+ // already exists
+ if (!replace) {
+ result = _method_getImplementation(m);
+ } else {
+ result = _method_setImplementation(cls, m, imp);
+ }
+ } else {
+ // fixme optimize
+ method_list_t *newlist;
+ newlist = (method_list_t *)_calloc_internal(sizeof(*newlist), 1);
+ newlist->entsize_NEVER_USE = (uint32_t)sizeof(method_t) | fixed_up_method_list;
+ newlist->count = 1;
+ newlist->first.name = name;
+ newlist->first.types = strdup(types);
+ if (!ignoreSelector(name)) {
+ newlist->first.imp = imp;
+ } else {
+ newlist->first.imp = (IMP)&_objc_ignored_method;
+ }
+
+ BOOL vtablesAffected = NO;
+ attachMethodLists(cls, &newlist, 1, NO, &vtablesAffected);
+ flushCaches(cls);
+ if (vtablesAffected) flushVtables(cls);
+
+ result = NULL;
+ }
+
+ return result;
+}
+
+
+BOOL
+class_addMethod(Class cls, SEL name, IMP imp, const char *types)
+{
+ if (!cls) return NO;
+
+ rwlock_write(&runtimeLock);
+ IMP old = addMethod(newcls(cls), name, imp, types ?: "", NO);
+ rwlock_unlock_write(&runtimeLock);
+ return old ? NO : YES;
+}
+
+
+IMP
+class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
+{
+ if (!cls) return NULL;
+
+ rwlock_write(&runtimeLock);
+ IMP old = addMethod(newcls(cls), name, imp, types ?: "", YES);
+ rwlock_unlock_write(&runtimeLock);
+ return old;
+}
+
+
+/***********************************************************************
+* class_addIvar
+* Adds an ivar to a class.
+* Locking: acquires runtimeLock
+**********************************************************************/
+BOOL
+class_addIvar(Class cls_gen, const char *name, size_t size,
+ uint8_t alignment, const char *type)
+{
+ class_t *cls = newcls(cls_gen);
+
+ if (!cls) return NO;
+
+ if (!type) type = "";
+ if (name && 0 == strcmp(name, "")) name = NULL;
+
+ rwlock_write(&runtimeLock);
+
+ assert(isRealized(cls));
+
+ // No class variables
+ if (isMetaClass(cls)) {
+ rwlock_unlock_write(&runtimeLock);
+ return NO;
+ }
+
+ // Can only add ivars to in-construction classes.
+ if (!(cls->data()->flags & RW_CONSTRUCTING)) {
+ rwlock_unlock_write(&runtimeLock);
+ return NO;
+ }
+
+ // Check for existing ivar with this name, unless it's anonymous.
+ // Check for too-big ivar.
+ // fixme check for superclass ivar too?
+ if ((name && getIvar(cls, name)) || size > UINT32_MAX) {
+ rwlock_unlock_write(&runtimeLock);
+ return NO;
+ }
+
+ class_ro_t *ro_w = make_ro_writeable(cls->data());
+
+ // fixme allocate less memory here
+
+ ivar_list_t *oldlist, *newlist;
+ if ((oldlist = (ivar_list_t *)cls->data()->ro->ivars)) {
+ size_t oldsize = ivar_list_size(oldlist);
+ newlist = (ivar_list_t *)
+ _calloc_internal(oldsize + oldlist->entsize, 1);
+ memcpy(newlist, oldlist, oldsize);
+ _free_internal(oldlist);
+ } else {
+ newlist = (ivar_list_t *)
+ _calloc_internal(sizeof(ivar_list_t), 1);
+ newlist->entsize = (uint32_t)sizeof(ivar_t);
+ }
+
+ uint32_t offset = unalignedInstanceSize(cls);
+ uint32_t alignMask = (1<<alignment)-1;
+ offset = (offset + alignMask) & ~alignMask;
+
+ ivar_t *ivar = ivar_list_nth(newlist, newlist->count++);
+ ivar->offset = (uintptr_t *)_malloc_internal(sizeof(*ivar->offset));
+ *ivar->offset = offset;
+ ivar->name = name ? _strdup_internal(name) : NULL;
+ ivar->type = _strdup_internal(type);
+ ivar->alignment = alignment;
+ ivar->size = (uint32_t)size;
+
+ ro_w->ivars = newlist;
+ ro_w->instanceSize = (uint32_t)(offset + size);
+
+ // Ivar layout updated in registerClass.
+
+ rwlock_unlock_write(&runtimeLock);
+
+ return YES;
+}
+
+
+/***********************************************************************
+* class_addProtocol
+* Adds a protocol to a class.
+* Locking: acquires runtimeLock
+**********************************************************************/
+BOOL class_addProtocol(Class cls_gen, Protocol *protocol_gen)
+{
+ class_t *cls = newcls(cls_gen);
+ protocol_t *protocol = newprotocol(protocol_gen);
+ protocol_list_t *plist;
+ const protocol_list_t **plistp;
+
+ if (!cls) return NO;
+ if (class_conformsToProtocol(cls_gen, protocol_gen)) return NO;
+
+ rwlock_write(&runtimeLock);
+
+ assert(isRealized(cls));
+
+ // fixme optimize
+ plist = (protocol_list_t *)
+ _malloc_internal(sizeof(protocol_list_t) + sizeof(protocol_t *));
+ plist->count = 1;
+ plist->list[0] = (protocol_ref_t)protocol;
+
+ unsigned int count = 0;
+ for (plistp = cls->data()->protocols; plistp && *plistp; plistp++) {
+ count++;
+ }
+
+ cls->data()->protocols = (const protocol_list_t **)
+ _realloc_internal(cls->data()->protocols,
+ (count+2) * sizeof(protocol_list_t *));
+ cls->data()->protocols[count] = plist;
+ cls->data()->protocols[count+1] = NULL;
+
+ // fixme metaclass?
+
+ rwlock_unlock_write(&runtimeLock);
+
+ return YES;
+}
+
+
+/***********************************************************************
+* class_addProperty
+* Adds a property to a class.
+* Locking: acquires runtimeLock
+**********************************************************************/
+static BOOL
+_class_addProperty(Class cls_gen, const char *name,
+ const objc_property_attribute_t *attrs, unsigned int count,
+ BOOL replace)
+{
+ class_t *cls = newcls(cls_gen);
+ chained_property_list *plist;
+
+ if (!cls) return NO;
+ if (!name) return NO;
+
+ property_t *prop = class_getProperty(cls_gen, name);
+ if (prop && !replace) {
+ // already exists, refuse to replace
+ return NO;
+ }
+ else if (prop) {
+ // replace existing
+ rwlock_write(&runtimeLock);
+ try_free(prop->attributes);
+ prop->attributes = copyPropertyAttributeString(attrs, count);
+ rwlock_unlock_write(&runtimeLock);
+ return YES;
+ }
+ else {
+ rwlock_write(&runtimeLock);
+
+ assert(isRealized(cls));
+
+ plist = (chained_property_list *)
+ _malloc_internal(sizeof(*plist) + sizeof(plist->list[0]));
+ plist->count = 1;
+ plist->list[0].name = _strdup_internal(name);
+ plist->list[0].attributes = copyPropertyAttributeString(attrs, count);
+
+ plist->next = cls->data()->properties;
+ cls->data()->properties = plist;
+
+ rwlock_unlock_write(&runtimeLock);
+
+ return YES;
+ }
+}
+
+BOOL
+class_addProperty(Class cls_gen, const char *name,
+ const objc_property_attribute_t *attrs, unsigned int n)
+{
+ return _class_addProperty(cls_gen, name, attrs, n, NO);
+}
+
+void
+class_replaceProperty(Class cls_gen, const char *name,
+ const objc_property_attribute_t *attrs, unsigned int n)
+{
+ _class_addProperty(cls_gen, name, attrs, n, YES);
+}
+
+
+/***********************************************************************
+* look_up_class
+* Look up a class by name, and realize it.
+* Locking: acquires runtimeLock
+**********************************************************************/
+PRIVATE_EXTERN id
+look_up_class(const char *name,
+ BOOL includeUnconnected __attribute__((unused)),
+ BOOL includeClassHandler __attribute__((unused)))
+{
+ if (!name) return nil;
+
+ rwlock_read(&runtimeLock);
+ class_t *result = getClass(name);
+ BOOL unrealized = result && !isRealized(result);
+ rwlock_unlock_read(&runtimeLock);
+ if (unrealized) {
+ rwlock_write(&runtimeLock);
+ realizeClass(result);
+ rwlock_unlock_write(&runtimeLock);
+ }
+ return (id)result;
+}
+
+
+/***********************************************************************
+* objc_duplicateClass
+* fixme
+* Locking: acquires runtimeLock
+**********************************************************************/
+Class
+objc_duplicateClass(Class original_gen, const char *name,
+ size_t extraBytes)
+{
+ class_t *original = newcls(original_gen);
+ class_t *duplicate;
+
+ rwlock_write(&runtimeLock);
+
+ assert(isRealized(original));
+ assert(!isMetaClass(original));
+
+ duplicate = (class_t *)
+ _calloc_class(alignedInstanceSize(original->isa) + extraBytes);
+ if (unalignedInstanceSize(original->isa) < sizeof(class_t)) {
+ _objc_inform("busted! %s\n", original->data()->ro->name);
+ }
+
+
+ duplicate->isa = original->isa;
+ duplicate->superclass = original->superclass;
+ duplicate->cache = (Cache)&_objc_empty_cache;
+ duplicate->vtable = &_objc_empty_vtable;
+
+ duplicate->setData((class_rw_t *)_calloc_internal(sizeof(*original->data()), 1));
+ duplicate->data()->flags = (original->data()->flags | RW_COPIED_RO) & ~RW_SPECIALIZED_VTABLE;
+ duplicate->data()->version = original->data()->version;
+ duplicate->data()->firstSubclass = NULL;
+ duplicate->data()->nextSiblingClass = NULL;
+
+ duplicate->data()->ro = (class_ro_t *)
+ _memdup_internal(original->data()->ro, sizeof(*original->data()->ro));
+ *(char **)&duplicate->data()->ro->name = _strdup_internal(name);
+
+ if (original->data()->methods) {
+ duplicate->data()->methods = (method_list_t **)
+ _memdup_internal(original->data()->methods,
+ malloc_size(original->data()->methods));
+ method_list_t **mlistp;
+ for (mlistp = duplicate->data()->methods; *mlistp; mlistp++) {
+ *mlistp = (method_list_t *)
+ _memdup_internal(*mlistp, method_list_size(*mlistp));
+ }
+ }
+
+ // fixme dies when categories are added to the base
+ duplicate->data()->properties = original->data()->properties;
+ duplicate->data()->protocols = original->data()->protocols;
+
+ if (duplicate->superclass) {
+ addSubclass(duplicate->superclass, duplicate);
+ }
+
+ // Don't methodize class - construction above is correct
+
+ addNamedClass(duplicate, duplicate->data()->ro->name);
+ addRealizedClass(duplicate);
+ // no: duplicate->isa == original->isa
+ // addRealizedMetaclass(duplicate->isa);
+
+ if (PrintConnecting) {
+ _objc_inform("CLASS: realizing class '%s' (duplicate of %s) %p %p",
+ name, original->data()->ro->name,
+ duplicate, duplicate->data()->ro);
+ }
+
+ rwlock_unlock_write(&runtimeLock);
+
+ return (Class)duplicate;
+}
+
+/***********************************************************************
+* objc_initializeClassPair
+* Locking: runtimeLock must be write-locked by the caller
+**********************************************************************/
+
+// &UnsetLayout is the default ivar layout during class construction
+static const uint8_t UnsetLayout = 0;
+
+static void objc_initializeClassPair_internal(Class superclass_gen, const char *name, Class cls_gen, Class meta_gen)
+{
+ rwlock_assert_writing(&runtimeLock);
+
+ class_t *superclass = newcls(superclass_gen);
+ class_t *cls = newcls(cls_gen);
+ class_t *meta = newcls(meta_gen);
+ class_ro_t *cls_ro_w, *meta_ro_w;
+
+ cls->setData((class_rw_t *)_calloc_internal(sizeof(class_rw_t), 1));
+ meta->setData((class_rw_t *)_calloc_internal(sizeof(class_rw_t), 1));
+ cls_ro_w = (class_ro_t *)_calloc_internal(sizeof(class_ro_t), 1);
+ meta_ro_w = (class_ro_t *)_calloc_internal(sizeof(class_ro_t), 1);
+ cls->data()->ro = cls_ro_w;
+ meta->data()->ro = meta_ro_w;
+
+ // Set basic info
+ cls->cache = (Cache)&_objc_empty_cache;
+ meta->cache = (Cache)&_objc_empty_cache;
+ cls->vtable = &_objc_empty_vtable;
+ meta->vtable = &_objc_empty_vtable;
+
+ cls->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED;
+ meta->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED;
+ cls->data()->version = 0;
+ meta->data()->version = 7;
+
+ cls_ro_w->flags = 0;
+ meta_ro_w->flags = RO_META;
+ if (!superclass) {
+ cls_ro_w->flags |= RO_ROOT;
+ meta_ro_w->flags |= RO_ROOT;
+ }
+ if (superclass) {
+ cls_ro_w->instanceStart = unalignedInstanceSize(superclass);
+ meta_ro_w->instanceStart = unalignedInstanceSize(superclass->isa);
+ cls_ro_w->instanceSize = cls_ro_w->instanceStart;
+ meta_ro_w->instanceSize = meta_ro_w->instanceStart;
+ } else {
+ cls_ro_w->instanceStart = 0;
+ meta_ro_w->instanceStart = (uint32_t)sizeof(class_t);
+ cls_ro_w->instanceSize = (uint32_t)sizeof(id); // just an isa
+ meta_ro_w->instanceSize = meta_ro_w->instanceStart;
+ }
+
+ cls_ro_w->name = _strdup_internal(name);
+ meta_ro_w->name = _strdup_internal(name);
+
+ cls_ro_w->ivarLayout = &UnsetLayout;
+ cls_ro_w->weakIvarLayout = &UnsetLayout;
+
+ // Connect to superclasses and metaclasses
+ cls->isa = meta;
+ if (superclass) {
+ meta->isa = superclass->isa->isa;
+ cls->superclass = superclass;
+ meta->superclass = superclass->isa;
+ addSubclass(superclass, cls);
+ addSubclass(superclass->isa, meta);
+ } else {
+ meta->isa = meta;
+ cls->superclass = Nil;
+ meta->superclass = cls;
+ addSubclass(cls, meta);
+ }
+}
+
+/***********************************************************************
+* objc_initializeClassPair
+**********************************************************************/
+Class objc_initializeClassPair(Class superclass_gen, const char *name, Class cls_gen, Class meta_gen)
+{
+ class_t *superclass = newcls(superclass_gen);
+
+ rwlock_write(&runtimeLock);
+
+ //
+ // Common superclass integrity checks with objc_allocateClassPair
+ //
+ if (getClass(name)) {
+ rwlock_unlock_write(&runtimeLock);
+ return Nil;
+ }
+ // fixme reserve class against simultaneous allocation
+
+ if (superclass) assert(isRealized(superclass));
+
+ if (superclass && superclass->data()->flags & RW_CONSTRUCTING) {
+ // Can't make subclass of an in-construction class
+ rwlock_unlock_write(&runtimeLock);
+ return Nil;
+ }
+
+
+ // just initialize what was supplied
+ objc_initializeClassPair_internal(superclass_gen, name, cls_gen, meta_gen);
+
+ rwlock_unlock_write(&runtimeLock);
+ return cls_gen;
+}
+
+/***********************************************************************
+* objc_allocateClassPair
+* fixme
+* Locking: acquires runtimeLock
+**********************************************************************/
+Class objc_allocateClassPair(Class superclass_gen, const char *name,
+ size_t extraBytes)
+{
+ class_t *superclass = newcls(superclass_gen);
+ Class cls, meta;
+
+ rwlock_write(&runtimeLock);
+
+ //
+ // Common superclass integrity checks with objc_initializeClassPair
+ //
+ if (getClass(name)) {
+ rwlock_unlock_write(&runtimeLock);
+ return Nil;
+ }
+ // fixme reserve class against simmultaneous allocation
+
+ if (superclass) assert(isRealized(superclass));
+
+ if (superclass && superclass->data()->flags & RW_CONSTRUCTING) {
+ // Can't make subclass of an in-construction class
+ rwlock_unlock_write(&runtimeLock);
+ return Nil;
+ }
+
+
+
+ // Allocate new classes.
+ size_t size = sizeof(class_t);
+ size_t metasize = sizeof(class_t);
+ if (superclass) {
+ size = alignedInstanceSize(superclass->isa);
+ metasize = alignedInstanceSize(superclass->isa->isa);
+ }
+ cls = _calloc_class(size + extraBytes);
+ meta = _calloc_class(metasize + extraBytes);
+
+ objc_initializeClassPair_internal(superclass_gen, name, cls, meta);
+
+ rwlock_unlock_write(&runtimeLock);
+
+ return (Class)cls;
+}
+
+
+/***********************************************************************
+* objc_registerClassPair
+* fixme
+* Locking: acquires runtimeLock
+**********************************************************************/
+void objc_registerClassPair(Class cls_gen)
+{
+ class_t *cls = newcls(cls_gen);
+
+ rwlock_write(&runtimeLock);
+
+ if ((cls->data()->flags & RW_CONSTRUCTED) ||
+ (cls->isa->data()->flags & RW_CONSTRUCTED))
+ {
+ _objc_inform("objc_registerClassPair: class '%s' was already "
+ "registered!", cls->data()->ro->name);
+ rwlock_unlock_write(&runtimeLock);
+ return;
+ }
+
+ if (!(cls->data()->flags & RW_CONSTRUCTING) ||
+ !(cls->isa->data()->flags & RW_CONSTRUCTING))
+ {
+ _objc_inform("objc_registerClassPair: class '%s' was not "
+ "allocated with objc_allocateClassPair!",
+ cls->data()->ro->name);
+ rwlock_unlock_write(&runtimeLock);
+ return;
+ }
+
+ // Build ivar layouts
+ if (UseGC) {
+ class_t *supercls = getSuperclass(cls);
+ class_ro_t *ro_w = (class_ro_t *)cls->data()->ro;
+
+ if (ro_w->ivarLayout != &UnsetLayout) {
+ // Class builder already called class_setIvarLayout.
+ }
+ else if (!supercls) {
+ // Root class. Scan conservatively (should be isa ivar only).
+ ro_w->ivarLayout = NULL;
+ }
+ else if (ro_w->ivars == NULL) {
+ // No local ivars. Use superclass's layouts.
+ ro_w->ivarLayout =
+ _ustrdup_internal(supercls->data()->ro->ivarLayout);
+ }
+ else {
+ // Has local ivars. Build layouts based on superclass.
+ layout_bitmap bitmap =
+ layout_bitmap_create(supercls->data()->ro->ivarLayout,
+ unalignedInstanceSize(supercls),
+ unalignedInstanceSize(cls), NO);
+ uint32_t i;
+ for (i = 0; i < ro_w->ivars->count; i++) {
+ ivar_t *ivar = ivar_list_nth(ro_w->ivars, i);
+ if (!ivar->offset) continue; // anonymous bitfield
+
+ layout_bitmap_set_ivar(bitmap, ivar->type, *ivar->offset);
+ }
+ ro_w->ivarLayout = layout_string_create(bitmap);
+ layout_bitmap_free(bitmap);
+ }
+
+ if (ro_w->weakIvarLayout != &UnsetLayout) {
+ // Class builder already called class_setWeakIvarLayout.
+ }
+ else if (!supercls) {
+ // Root class. No weak ivars (should be isa ivar only).
+ ro_w->weakIvarLayout = NULL;
+ }
+ else if (ro_w->ivars == NULL) {
+ // No local ivars. Use superclass's layout.
+ ro_w->weakIvarLayout =
+ _ustrdup_internal(supercls->data()->ro->weakIvarLayout);
+ }
+ else {
+ // Has local ivars. Build layout based on superclass.
+ // No way to add weak ivars yet.
+ ro_w->weakIvarLayout =
+ _ustrdup_internal(supercls->data()->ro->weakIvarLayout);
+ }
+ }
+
+ // Clear "under construction" bit, set "done constructing" bit
+ cls->data()->flags &= ~RW_CONSTRUCTING;
+ cls->isa->data()->flags &= ~RW_CONSTRUCTING;
+ cls->data()->flags |= RW_CONSTRUCTED;
+ cls->isa->data()->flags |= RW_CONSTRUCTED;
+
+ // Add to realized and uninitialized classes
+ addNamedClass(cls, cls->data()->ro->name);
+ addRealizedClass(cls);
+ addRealizedMetaclass(cls->isa);
+ addUninitializedClass(cls, cls->isa);
+
+ rwlock_unlock_write(&runtimeLock);
+}
+
+
+static void unload_class(class_t *cls, BOOL isMeta)
+{
+ // Detach class from various lists
+
+ // categories not yet attached to this class
+ category_list *cats;
+ cats = unattachedCategoriesForClass(cls);
+ if (cats) free(cats);
+
+ // class tables and +load queue
+ if (!isMeta) {
+ removeNamedClass(cls, getName(cls));
+ removeRealizedClass(cls);
+ removeUninitializedClass(cls);
+ } else {
+ removeRealizedMetaclass(cls);
+ }
+
+ // superclass's subclass list
+ if (isRealized(cls)) {
+ class_t *supercls = getSuperclass(cls);
+ if (supercls) removeSubclass(supercls, cls);
+ }
+
+
+ // Dispose the class's own data structures
+
+ if (isRealized(cls)) {
+ uint32_t i;
+
+ // Dereferences the cache contents; do this before freeing methods
+ if (cls->cache != (Cache)&_objc_empty_cache) _cache_free(cls->cache);
+
+ if (cls->data()->methods) {
+ method_list_t **mlistp;
+ for (mlistp = cls->data()->methods; *mlistp; mlistp++) {
+ for (i = 0; i < (**mlistp).count; i++) {
+ method_t *m = method_list_nth(*mlistp, i);
+ try_free(m->types);
+ }
+ try_free(*mlistp);
+ }
+ try_free(cls->data()->methods);
+ }
+
+ const ivar_list_t *ilist = cls->data()->ro->ivars;
+ if (ilist) {
+ for (i = 0; i < ilist->count; i++) {
+ const ivar_t *ivar = ivar_list_nth(ilist, i);
+ try_free(ivar->offset);
+ try_free(ivar->name);
+ try_free(ivar->type);
+ }
+ try_free(ilist);
+ }
+
+ const protocol_list_t **plistp;
+ for (plistp = cls->data()->protocols; plistp && *plistp; plistp++) {
+ try_free(*plistp);
+ }
+ try_free(cls->data()->protocols);
+
+ const chained_property_list *proplist = cls->data()->properties;
+ while (proplist) {
+ for (uint32_t i = 0; i < proplist->count; i++) {
+ const property_t *prop = proplist->list+i;
+ try_free(prop->name);
+ try_free(prop->attributes);
+ }
+ {
+ const chained_property_list *temp = proplist;
+ proplist = proplist->next;
+ try_free(temp);
+ }
+ }
+
+
+ if (cls->vtable != &_objc_empty_vtable &&
+ cls->data()->flags & RW_SPECIALIZED_VTABLE) try_free(cls->vtable);
+ try_free(cls->data()->ro->ivarLayout);
+ try_free(cls->data()->ro->weakIvarLayout);
+ try_free(cls->data()->ro->name);
+ try_free(cls->data()->ro);
+ try_free(cls->data());
+ try_free(cls);
+ }
+}
+
+void objc_disposeClassPair(Class cls_gen)
+{
+ class_t *cls = newcls(cls_gen);
+
+ rwlock_write(&runtimeLock);
+
+ if (!(cls->data()->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING)) ||
+ !(cls->isa->data()->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING)))
+ {
+ // class not allocated with objc_allocateClassPair
+ // disposing still-unregistered class is OK!
+ _objc_inform("objc_disposeClassPair: class '%s' was not "
+ "allocated with objc_allocateClassPair!",
+ cls->data()->ro->name);
+ rwlock_unlock_write(&runtimeLock);
+ return;
+ }
+
+ if (isMetaClass(cls)) {
+ _objc_inform("objc_disposeClassPair: class '%s' is a metaclass, "
+ "not a class!", cls->data()->ro->name);
+ rwlock_unlock_write(&runtimeLock);
+ return;
+ }
+
+ // Shouldn't have any live subclasses.
+ if (cls->data()->firstSubclass) {
+ _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, "
+ "including '%s'!", cls->data()->ro->name,
+ getName(cls->data()->firstSubclass));
+ }
+ if (cls->isa->data()->firstSubclass) {
+ _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, "
+ "including '%s'!", cls->data()->ro->name,
+ getName(cls->isa->data()->firstSubclass));
+ }
+
+ // don't remove_class_from_loadable_list()
+ // - it's not there and we don't have the lock
+ unload_class(cls->isa, YES);
+ unload_class(cls, NO);
+
+ rwlock_unlock_write(&runtimeLock);
+}
+
+
+/***********************************************************************
+* class_createInstance
+* fixme
+* Locking: none
+**********************************************************************/
+static id
+_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
+ __attribute__((always_inline));
+
+static id
+_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
+{
+ if (!cls) return nil;
+
+ assert(isRealized(newcls(cls)));
+
+ size_t size = alignedInstanceSize(newcls(cls)) + extraBytes;
+
+ // CF requires all object be at least 16 bytes.
+ if (size < 16) size = 16;
+
+ id obj;
+#if SUPPORT_GC
+ if (UseGC) {
+ obj = (id)auto_zone_allocate_object(gc_zone, size,
+ AUTO_OBJECT_SCANNED, 0, 1);
+ } else
+#endif
+ if (zone) {
+ obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
+ } else {
+ obj = (id)calloc(1, size);
+ }
+ if (!obj) return nil;
+
+ obj->isa = cls; // need not be object_setClass
+
+ if (_class_hasCxxStructors(cls)) {
+ obj = _objc_constructOrFree(cls, obj);
+ }
+
+ return obj;
+}
+
+
+id
+class_createInstance(Class cls, size_t extraBytes)
+{
+ return _class_createInstanceFromZone(cls, extraBytes, NULL);
+}
+
+/***********************************************************************
+* class_createInstances
+* fixme
+* Locking: none
+**********************************************************************/
+unsigned
+class_createInstances(Class cls, size_t extraBytes,
+ id *results, unsigned num_requested)
+{
+ return _class_createInstancesFromZone(cls, extraBytes, NULL,
+ results, num_requested);
+}
+
+static BOOL classOrSuperClassesUseARR(Class cls) {
+ while (cls) {
+ if (_class_usesAutomaticRetainRelease(cls)) return true;
+ cls = class_getSuperclass(cls);
+ }
+ return false;
+}
+
+static void arr_fixup_copied_references(id newObject, id oldObject)
+{
+ // use ARR layouts to correctly copy the references from old object to new, both strong and weak.
+ Class cls = oldObject->isa;
+ while (cls) {
+ if (_class_usesAutomaticRetainRelease(cls)) {
+ // FIXME: align the instance start to nearest id boundary. This currently handles the case where
+ // the the compiler folds a leading BOOL (char, short, etc.) into the alignment slop of a superclass.
+ size_t instanceStart = _class_getInstanceStart(cls);
+ const uint8_t *strongLayout = class_getIvarLayout(cls);
+ if (strongLayout) {
+ id *newPtr = (id *)((char*)newObject + instanceStart);
+ unsigned char byte;
+ while ((byte = *strongLayout++)) {
+ unsigned skips = (byte >> 4);
+ unsigned scans = (byte & 0x0F);
+ newPtr += skips;
+ while (scans--) {
+ // ensure strong references are properly retained.
+ id value = *newPtr++;
+ if (value) objc_retain(value);
+ }
+ }
+ }
+ const uint8_t *weakLayout = class_getWeakIvarLayout(cls);
+ // fix up weak references if any.
+ if (weakLayout) {
+ id *newPtr = (id *)((char*)newObject + instanceStart), *oldPtr = (id *)((char*)oldObject + instanceStart);
+ unsigned char byte;
+ while ((byte = *weakLayout++)) {
+ unsigned skips = (byte >> 4);
+ unsigned weaks = (byte & 0x0F);
+ newPtr += skips, oldPtr += skips;
+ while (weaks--) {
+ *newPtr = nil;
+ objc_storeWeak(newPtr, objc_loadWeak(oldPtr));
+ ++newPtr, ++oldPtr;
+ }
+ }
+ }
+ }
+ cls = class_getSuperclass(cls);
+ }
+}
+
+/***********************************************************************
+* object_copyFromZone
+* fixme
+* Locking: none
+**********************************************************************/
+static id
+_object_copyFromZone(id oldObj, size_t extraBytes, void *zone)
+{
+ id obj;
+ size_t size;
+
+ if (!oldObj) return nil;
+ if (OBJC_IS_TAGGED_PTR(oldObj)) return oldObj;
+
+ size = _class_getInstanceSize(oldObj->isa) + extraBytes;
+#if SUPPORT_GC
+ if (UseGC) {
+ obj = (id) auto_zone_allocate_object(gc_zone, size,
+ AUTO_OBJECT_SCANNED, 0, 1);
+ } else
+#endif
+ if (zone) {
+ obj = (id) malloc_zone_calloc((malloc_zone_t *)zone, size, 1);
+ } else {
+ obj = (id) calloc(1, size);
+ }
+ if (!obj) return nil;
+
+ // fixme this doesn't handle C++ ivars correctly (#4619414)
+ objc_memmove_collectable(obj, oldObj, size);
+
+#if SUPPORT_GC
+ if (UseGC)
+ gc_fixup_weakreferences(obj, oldObj);
+ else if (classOrSuperClassesUseARR(obj->isa))
+ arr_fixup_copied_references(obj, oldObj);
+#else
+ if (classOrSuperClassesUseARR(obj->isa))
+ arr_fixup_copied_references(obj, oldObj);
+#endif
+
+ return obj;
+}
+
+
+/***********************************************************************
+* object_copy
+* fixme
+* Locking: none
+**********************************************************************/
+id
+object_copy(id oldObj, size_t extraBytes)
+{
+ return _object_copyFromZone(oldObj, extraBytes, malloc_default_zone());
+}
+
+
+#if !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
+
+/***********************************************************************
+* class_createInstanceFromZone
+* fixme
+* Locking: none
+**********************************************************************/
+id
+class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
+{
+ return _class_createInstanceFromZone(cls, extraBytes, zone);
+}
+
+/***********************************************************************
+* object_copyFromZone
+* fixme
+* Locking: none
+**********************************************************************/
+id
+object_copyFromZone(id oldObj, size_t extraBytes, void *zone)
+{
+ return _object_copyFromZone(oldObj, extraBytes, zone);
+}
+
+#endif
+
+
+/***********************************************************************
+* objc_destructInstance
+* Destroys an instance without freeing memory.
+* Calls C++ destructors.
+* Calls ARR ivar cleanup.
+* Removes associative references.
+* Returns `obj`. Does nothing if `obj` is nil.
+* Be warned that GC DOES NOT CALL THIS. If you edit this, also edit finalize.
+* CoreFoundation and other clients do call this under GC.
+**********************************************************************/
+void *objc_destructInstance(id obj)
+{
+ if (obj) {
+ Class isa_gen = _object_getClass(obj);
+ class_t *isa = newcls(isa_gen);
+
+ // Read all of the flags at once for performance.
+ bool cxx = hasCxxStructors(isa);
+ bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen);
+
+ // This order is important.
+ if (cxx) object_cxxDestruct(obj);
+ if (assoc) _object_remove_assocations(obj);
+
+ if (!UseGC) objc_clear_deallocating(obj);
+ }
+
+ return obj;
+}
+
+
+/***********************************************************************
+* object_dispose
+* fixme
+* Locking: none
+**********************************************************************/
+id
+object_dispose(id obj)
+{
+ if (!obj) return nil;
+
+ objc_destructInstance(obj);
+
+#if SUPPORT_GC
+ if (UseGC) {
+ auto_zone_retain(gc_zone, obj); // gc free expects rc==1
+ }
+#endif
+
+ free(obj);
+
+ return nil;
+}
+
+
+/***********************************************************************
+* _objc_getFreedObjectClass
+* fixme
+* Locking: none
+**********************************************************************/
+Class _objc_getFreedObjectClass (void)
+{
+ return nil;
+}
+
+#if SUPPORT_FIXUP
+
+OBJC_EXTERN id objc_msgSend_fixedup(id, SEL, ...);
+OBJC_EXTERN id objc_msgSendSuper2_fixedup(id, SEL, ...);
+OBJC_EXTERN id objc_msgSend_stret_fixedup(id, SEL, ...);
+OBJC_EXTERN id objc_msgSendSuper2_stret_fixedup(id, SEL, ...);
+#if defined(__i386__) || defined(__x86_64__)
+OBJC_EXTERN id objc_msgSend_fpret_fixedup(id, SEL, ...);
+#endif
+#if defined(__x86_64__)
+OBJC_EXTERN id objc_msgSend_fp2ret_fixedup(id, SEL, ...);
+#endif
+
+/***********************************************************************
+* _objc_fixupMessageRef
+* Fixes up message ref *msg.
+* obj is the receiver. supr is NULL for non-super messages
+* Locking: acquires runtimeLock
+**********************************************************************/
+OBJC_EXTERN PRIVATE_EXTERN IMP
+_objc_fixupMessageRef(id obj, struct objc_super2 *supr, message_ref_t *msg)
+{
+ IMP imp;
+ class_t *isa;
+#if SUPPORT_VTABLE
+ int vtableIndex;
+#endif
+
+ rwlock_assert_unlocked(&runtimeLock);
+
+ if (!supr) {
+ // normal message - search obj->isa for the method implementation
+ isa = (class_t *) _object_getClass(obj);
+
+ if (!isRealized(isa)) {
+ // obj is a class object, isa is its metaclass
+ class_t *cls;
+ rwlock_write(&runtimeLock);
+ cls = realizeClass((class_t *)obj);
+ rwlock_unlock_write(&runtimeLock);
+
+ // shouldn't have instances of unrealized classes!
+ assert(isMetaClass(isa));
+ // shouldn't be relocating classes here!
+ assert(cls == (class_t *)obj);
+ }
+ }
+ else {
+ // this is objc_msgSend_super, and supr->current_class->superclass
+ // is the class to search for the method implementation
+ assert(isRealized((class_t *)supr->current_class));
+ isa = getSuperclass((class_t *)supr->current_class);
+ }
+
+ msg->sel = sel_registerName((const char *)msg->sel);
+
+ if (ignoreSelector(msg->sel)) {
+ // ignored selector - bypass dispatcher
+ msg->imp = (IMP)&vtable_ignored;
+ imp = (IMP)&_objc_ignored_method;
+ }
+#if SUPPORT_VTABLE
+ else if (msg->imp == (IMP)&objc_msgSend_fixup &&
+ (vtableIndex = vtable_getIndex(msg->sel)) >= 0)
+ {
+ // vtable dispatch
+ msg->imp = vtableTrampolines[vtableIndex];
+ imp = isa->vtable[vtableIndex];
+ }
+#endif
+ else {
+ // ordinary dispatch
+ imp = lookUpMethod((Class)isa, msg->sel, YES/*initialize*/, YES/*cache*/);
+
+ if (msg->imp == (IMP)&objc_msgSend_fixup) {
+ msg->imp = (IMP)&objc_msgSend_fixedup;
+ }
+ else if (msg->imp == (IMP)&objc_msgSendSuper2_fixup) {
+ msg->imp = (IMP)&objc_msgSendSuper2_fixedup;
+ }
+ else if (msg->imp == (IMP)&objc_msgSend_stret_fixup) {
+ msg->imp = (IMP)&objc_msgSend_stret_fixedup;
+ }
+ else if (msg->imp == (IMP)&objc_msgSendSuper2_stret_fixup) {
+ msg->imp = (IMP)&objc_msgSendSuper2_stret_fixedup;
+ }
+#if defined(__i386__) || defined(__x86_64__)
+ else if (msg->imp == (IMP)&objc_msgSend_fpret_fixup) {
+ msg->imp = (IMP)&objc_msgSend_fpret_fixedup;
+ }
+#endif
+#if defined(__x86_64__)
+ else if (msg->imp == (IMP)&objc_msgSend_fp2ret_fixup) {
+ msg->imp = (IMP)&objc_msgSend_fp2ret_fixedup;
+ }
+#endif
+ else {
+ // The ref may already have been fixed up, either by another thread
+ // or by +initialize via lookUpMethod above.
+ }
+ }
+
+ return imp;
+}
+
+// SUPPORT_FIXUP
+#endif
+
+
+// ProKit SPI
+static class_t *setSuperclass(class_t *cls, class_t *newSuper)
+{
+ class_t *oldSuper;
+
+ rwlock_assert_writing(&runtimeLock);
+
+ oldSuper = cls->superclass;
+ removeSubclass(oldSuper, cls);
+ removeSubclass(oldSuper->isa, cls->isa);
+
+ cls->superclass = newSuper;
+ cls->isa->superclass = newSuper->isa;
+ addSubclass(newSuper, cls);
+ addSubclass(newSuper->isa, cls->isa);
+
+ flushCaches(cls);
+ flushCaches(cls->isa);
+ flushVtables(cls);
+ flushVtables(cls->isa);
+
+ return oldSuper;
+}
+
+
+Class class_setSuperclass(Class cls_gen, Class newSuper_gen)
+{
+ class_t *cls = newcls(cls_gen);
+ class_t *newSuper = newcls(newSuper_gen);
+ class_t *oldSuper;
+
+ rwlock_write(&runtimeLock);
+ oldSuper = setSuperclass(cls, newSuper);
+ rwlock_unlock_write(&runtimeLock);
+
+ return (Class)oldSuper;
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef _OBJC_RUNTIME_OLD_H
+#define _OBJC_RUNTIME_OLD_H
+
+#include "objc-private.h"
+#include "objc-file-old.h"
+
+
+struct old_class {
+ struct old_class *isa;
+ struct old_class *super_class;
+ const char *name;
+ long version;
+ long info;
+ long instance_size;
+ struct old_ivar_list *ivars;
+ struct old_method_list **methodLists;
+ Cache cache;
+ struct old_protocol_list *protocols;
+ // CLS_EXT only
+ const uint8_t *ivar_layout;
+ struct old_class_ext *ext;
+};
+
+struct old_class_ext {
+ uint32_t size;
+ const uint8_t *weak_ivar_layout;
+ struct old_property_list **propertyLists;
+};
+
+struct old_category {
+ char *category_name;
+ char *class_name;
+ struct old_method_list *instance_methods;
+ struct old_method_list *class_methods;
+ struct old_protocol_list *protocols;
+ uint32_t size;
+ struct old_property_list *instance_properties;
+};
+
+struct old_ivar {
+ char *ivar_name;
+ char *ivar_type;
+ int ivar_offset;
+#ifdef __LP64__
+ int space;
+#endif
+};
+
+struct old_ivar_list {
+ int ivar_count;
+#ifdef __LP64__
+ int space;
+#endif
+ /* variable length structure */
+ struct old_ivar ivar_list[1];
+};
+
+
+struct old_method {
+ SEL method_name;
+ char *method_types;
+ IMP method_imp;
+};
+
+struct old_method_list {
+ struct old_method_list *obsolete;
+
+ int method_count;
+#ifdef __LP64__
+ int space;
+#endif
+ /* variable length structure */
+ struct old_method method_list[1];
+};
+
+struct old_protocol {
+ Class isa;
+ const char *protocol_name;
+ struct old_protocol_list *protocol_list;
+ struct objc_method_description_list *instance_methods;
+ struct objc_method_description_list *class_methods;
+};
+
+struct old_protocol_list {
+ struct old_protocol_list *next;
+ long count;
+ struct old_protocol *list[1];
+};
+
+struct old_protocol_ext {
+ uint32_t size;
+ struct objc_method_description_list *optional_instance_methods;
+ struct objc_method_description_list *optional_class_methods;
+ struct old_property_list *instance_properties;
+};
+
+
+struct old_property {
+ const char *name;
+ const char *attributes;
+};
+
+struct old_property_list {
+ uint32_t entsize;
+ uint32_t count;
+ struct old_property first;
+};
+
+
+#define CLS_CLASS 0x1
+#define CLS_META 0x2
+#define CLS_INITIALIZED 0x4
+#define CLS_POSING 0x8
+#define CLS_MAPPED 0x10
+#define CLS_FLUSH_CACHE 0x20
+#define CLS_GROW_CACHE 0x40
+#define CLS_NEED_BIND 0x80
+#define CLS_METHOD_ARRAY 0x100
+// the JavaBridge constructs classes with these markers
+#define CLS_JAVA_HYBRID 0x200
+#define CLS_JAVA_CLASS 0x400
+// thread-safe +initialize
+#define CLS_INITIALIZING 0x800
+// bundle unloading
+#define CLS_FROM_BUNDLE 0x1000
+// C++ ivar support
+#define CLS_HAS_CXX_STRUCTORS 0x2000
+// Lazy method list arrays
+#define CLS_NO_METHOD_ARRAY 0x4000
+// +load implementation
+#define CLS_HAS_LOAD_METHOD 0x8000
+// objc_allocateClassPair API
+#define CLS_CONSTRUCTING 0x10000
+// visibility=hidden
+#define CLS_HIDDEN 0x20000
+// GC: class has unsafe finalize method
+#define CLS_FINALIZE_ON_MAIN_THREAD 0x40000
+// Lazy property list arrays
+#define CLS_NO_PROPERTY_ARRAY 0x80000
+// +load implementation
+#define CLS_CONNECTED 0x100000
+#define CLS_LOADED 0x200000
+// objc_allocateClassPair API
+#define CLS_CONSTRUCTED 0x400000
+// class is leaf for cache flushing
+#define CLS_LEAF 0x800000
+// class instances may have associative references
+#define CLS_INSTANCES_HAVE_ASSOCIATED_OBJECTS 0x1000000
+// class has instance-specific GC layout
+#define CLS_HAS_INSTANCE_SPECIFIC_LAYOUT 0x2000000
+
+
+// Terminator for array of method lists
+#define END_OF_METHODS_LIST ((struct old_method_list*)-1)
+
+#define ISCLASS(cls) (((cls)->info & CLS_CLASS) != 0)
+#define ISMETA(cls) (((cls)->info & CLS_META) != 0)
+#define GETMETA(cls) (ISMETA(cls) ? (cls) : (cls)->isa)
+
+
+__BEGIN_DECLS
+
+#define oldcls(cls) ((struct old_class *)cls)
+#define oldprotocol(proto) ((struct old_protocol *)proto)
+#define oldmethod(meth) ((struct old_method *)meth)
+#define oldcategory(cat) ((struct old_category *)cat)
+#define oldivar(ivar) ((struct old_ivar *)ivar)
+#define oldproperty(prop) ((struct old_property *)prop)
+
+extern void unload_class(struct old_class *cls);
+
+extern Class objc_getOrigClass (const char *name);
+extern IMP lookupNamedMethodInMethodList(struct old_method_list *mlist, const char *meth_name);
+extern void _objc_insertMethods(struct old_class *cls, struct old_method_list *mlist, struct old_category *cat);
+extern void _objc_removeMethods(struct old_class *cls, struct old_method_list *mlist);
+extern void _objc_flush_caches (Class cls);
+extern BOOL _class_addProperties(struct old_class *cls, struct old_property_list *additions);
+extern void change_class_references(struct old_class *imposter, struct old_class *original, struct old_class *copy, BOOL changeSuperRefs);
+extern void flush_marked_caches(void);
+extern void set_superclass(struct old_class *cls, struct old_class *supercls, BOOL cls_is_new);
+extern void try_free(const void *p);
+
+extern struct old_property *property_list_nth(const struct old_property_list *plist, uint32_t i);
+extern struct old_property **copyPropertyList(struct old_property_list *plist, unsigned int *outCount);
+
+extern void _class_setInfo(Class cls, long set);
+extern void _class_clearInfo(Class cls, long clear);
+extern void _class_changeInfo(Class cls, long set, long clear);
+
+
+// used by flush_caches outside objc-cache.m
+extern void _cache_flush(Class cls);
+#ifdef OBJC_INSTRUMENTED
+extern unsigned int LinearFlushCachesCount;
+extern unsigned int LinearFlushCachesVisitedCount;
+extern unsigned int MaxLinearFlushCachesVisitedCount;
+extern unsigned int NonlinearFlushCachesCount;
+extern unsigned int NonlinearFlushCachesClassCount;
+extern unsigned int NonlinearFlushCachesVisitedCount;
+extern unsigned int MaxNonlinearFlushCachesVisitedCount;
+extern unsigned int IdealFlushCachesCount;
+extern unsigned int MaxIdealFlushCachesCount;
+#endif
+
+__END_DECLS
+
+#endif
#if !__OBJC2__
-#define OLD 1
#include "objc-private.h"
+#include "objc-runtime-old.h"
#include "objc-loadmethod.h"
/* NXHashTable SPI */
// Function called when a class is loaded from an image
-__private_extern__ void (*callbackFunction)(Class, const char *) = 0;
+PRIVATE_EXTERN void (*callbackFunction)(Class, Category) = 0;
// Hash table of classes
-__private_extern__ NXHashTable * class_hash NOBSS = 0;
+PRIVATE_EXTERN NXHashTable * class_hash = 0;
static NXHashTablePrototype classHashPrototype =
{
(uintptr_t (*) (const void *, const void *)) classHash,
};
// Hash table of unconnected classes
-static NXHashTable *unconnected_class_hash NOBSS = NULL;
+static NXHashTable *unconnected_class_hash = NULL;
// Exported copy of class_hash variable (hook for debugging tools)
NXHashTable *_objc_debug_class_hash = NULL;
/***********************************************************************
* objc_dump_class_hash. Log names of all known classes.
**********************************************************************/
-__private_extern__ void objc_dump_class_hash(void)
+PRIVATE_EXTERN void objc_dump_class_hash(void)
{
NXHashTable *table;
unsigned count;
* _objc_init_class_hash. Return the class lookup table, create it if
* necessary.
**********************************************************************/
-__private_extern__ void _objc_init_class_hash(void)
+PRIVATE_EXTERN void _objc_init_class_hash(void)
{
// Do nothing if class hash table already exists
if (class_hash)
}
+/***********************************************************************
+* objc_copyClassList
+* Returns pointers to all classes.
+* This requires all classes be realized, which is regretfully non-lazy.
+*
+* outCount may be NULL. *outCount is the number of classes returned.
+* If the returned array is not NULL, it is NULL-terminated and must be
+* freed with free().
+* Locking: acquires classLock
+**********************************************************************/
+Class *
+objc_copyClassList(unsigned int *outCount)
+{
+ Class *result;
+ unsigned int count;
+
+ mutex_lock(&classLock);
+ result = NULL;
+ count = class_hash ? NXCountHashTable(class_hash) : 0;
+
+ if (count > 0) {
+ Class cls;
+ NXHashState state = NXInitHashState(class_hash);
+ result = malloc((1+count) * sizeof(Class));
+ count = 0;
+ while (NXNextHashState(class_hash, &state, (void **)&cls)) {
+ result[count++] = cls;
+ }
+ result[count] = NULL;
+ }
+ mutex_unlock(&classLock);
+
+ if (outCount) *outCount = count;
+ return result;
+}
+
+
/***********************************************************************
* objc_copyProtocolList
* Returns pointers to all protocols.
* Locking: acquires classLock
**********************************************************************/
-Protocol **
+Protocol * __unsafe_unretained *
objc_copyProtocolList(unsigned int *outCount)
{
int count, i;
static int classIsEqual(void *info, Class name, Class cls)
{
// Standard string comparison
- // Our local inlined version is significantly shorter on PPC and avoids the
- // mflr/mtlr and dyld_stub overhead when calling strcmp.
- return _objc_strcmp(_class_getName(name), _class_getName(cls)) == 0;
+ return strcmp(_class_getName(name), _class_getName(cls)) == 0;
}
* Assumes the named class doesn't exist yet.
* Not thread safe.
**********************************************************************/
-__private_extern__ Class _objc_allocateFutureClass(const char *name)
+PRIVATE_EXTERN Class _objc_allocateFutureClass(const char *name)
{
struct old_class *cls;
struct old_class *oldcls;
struct old_class *newcls = (struct old_class *)cls; // Not a real class!
- if ((oldcls = _class_asOld((Class)look_up_class(name, NO/*unconnected*/, NO/*classhandler*/)))) {
+ if ((oldcls = oldcls((Class)look_up_class(name, NO/*unconnected*/, NO/*classhandler*/)))) {
setOriginalClassForFutureClass(newcls, oldcls);
// fixme hack
memcpy(newcls, oldcls, sizeof(struct objc_class));
* 3. classLoader callback
* 4. classHandler callback (optional)
**********************************************************************/
-__private_extern__ id look_up_class(const char *aClassName, BOOL includeUnconnected, BOOL includeClassHandler)
+PRIVATE_EXTERN id look_up_class(const char *aClassName, BOOL includeUnconnected, BOOL includeClassHandler)
{
BOOL includeClassLoader = YES; // class loader cannot be skipped
id result = nil;
* Returns TRUE if class cls is ready for its +load method to be called.
* A class is ready for +load if it is connected.
**********************************************************************/
-__private_extern__ BOOL _class_isLoadable(Class cls)
+PRIVATE_EXTERN BOOL _class_isLoadable(Class cls)
{
- return class_is_connected(_class_asOld(cls));
+ return class_is_connected(oldcls(cls));
}
struct old_class *oldCls;
// Connect superclass pointers.
- set_superclass(cls, supercls);
+ set_superclass(cls, supercls, YES);
// Update GC layouts
// For paranoia, this is a conservative update:
struct old_class *supercls;
// YES unconnected, YES class handler
- if (NULL == (supercls = _class_asOld((Class)look_up_class(supercls_name, YES, YES)))) {
+ if (NULL == (supercls = oldcls((Class)look_up_class(supercls_name, YES, YES)))) {
// Superclass does not exist yet.
// pendClassInstallation will handle duplicate pends of this class
pendClassInstallation(cls, supercls_name);
// field for [super ...] use, but otherwise perform
// fixups on the new class struct only.
const char *super_name = (const char *) cls->super_class;
- if (super_name) cls->super_class = _class_asOld((Class)objc_getClass(super_name));
+ if (super_name) cls->super_class = oldcls((Class)objc_getClass(super_name));
cls = futureCls;
}
connected = connect_class(cls);
// And metaclass's super_class (#5351107)
const char *super_name = (const char *) cls->super_class;
if (super_name) {
- cls->super_class = _class_asOld((Class)objc_getClass(super_name));
+ cls->super_class = oldcls((Class)objc_getClass(super_name));
// metaclass's superclass is superclass's metaclass
cls->isa->super_class = cls->super_class->isa;
} else {
// Get pointer to class of this name
// NO unconnected, YES class loader
// (real class with weak-missing superclass is unconnected now)
- cls = _class_asOld((Class)look_up_class(name, NO, YES));
+ cls = oldcls((Class)look_up_class(name, NO, YES));
if (cls) {
// Referenced class exists. Fix up the reference.
*ref = isMeta ? cls->isa : cls;
* Recursively search for a selector in a protocol
* (and all incorporated protocols)
**********************************************************************/
-__private_extern__ struct objc_method_description *
+PRIVATE_EXTERN struct objc_method_description *
lookup_protocol_method(struct old_protocol *proto, SEL aSel,
BOOL isRequiredMethod, BOOL isInstanceMethod)
{
}
-Property protocol_getProperty(Protocol *p, const char *name,
+objc_property_t protocol_getProperty(Protocol *p, const char *name,
BOOL isRequiredProperty, BOOL isInstanceProperty)
{
struct old_protocol *proto = oldprotocol(p);
}
if ((ext = ext_for_protocol(proto))) {
- struct objc_property_list *plist;
+ struct old_property_list *plist;
if ((plist = ext->instance_properties)) {
uint32_t i;
for (i = 0; i < plist->count; i++) {
- Property prop = property_list_nth(plist, i);
+ struct old_property *prop = property_list_nth(plist, i);
if (0 == strcmp(name, prop->name)) {
- return prop;
+ return (objc_property_t)prop;
}
}
}
if ((plist = proto->protocol_list)) {
int i;
for (i = 0; i < plist->count; i++) {
- Property prop =
+ objc_property_t prop =
protocol_getProperty((Protocol *)plist->list[i], name,
isRequiredProperty, isInstanceProperty);
if (prop) return prop;
}
-Property *protocol_copyPropertyList(Protocol *p, unsigned int *outCount)
+objc_property_t *protocol_copyPropertyList(Protocol *p, unsigned int *outCount)
{
- Property *result = NULL;
+ struct old_property **result = NULL;
struct old_protocol_ext *ext;
- struct objc_property_list *plist;
+ struct old_property_list *plist;
struct old_protocol *proto = oldprotocol(p);
if (! (ext = ext_for_protocol(proto))) {
plist = ext->instance_properties;
result = copyPropertyList(plist, outCount);
- return result;
+ return (objc_property_t *)result;
}
* Copies this protocol's incorporated protocols.
* Does not copy those protocol's incorporated protocols in turn.
**********************************************************************/
-Protocol **protocol_copyProtocolList(Protocol *p, unsigned int *outCount)
+Protocol * __unsafe_unretained *
+protocol_copyProtocolList(Protocol *p, unsigned int *outCount)
{
unsigned int count = 0;
Protocol **result = NULL;
}
+/***********************************************************************
+* objc_allocateProtocol
+* Creates a new protocol. The protocol may not be used until
+* objc_registerProtocol() is called.
+* Returns NULL if a protocol with the same name already exists.
+* Locking: acquires classLock
+**********************************************************************/
+Protocol *
+objc_allocateProtocol(const char *name)
+{
+ Class cls = (Class)objc_getClass("__IncompleteProtocol");
+
+ mutex_lock(&classLock);
+
+ if (NXMapGet(protocol_map, name)) {
+ mutex_unlock(&classLock);
+ return NULL;
+ }
+
+ struct old_protocol *result = (struct old_protocol *)
+ _calloc_internal(1, sizeof(struct old_protocol)
+ + sizeof(struct old_protocol_ext));
+ struct old_protocol_ext *ext = (struct old_protocol_ext *)(result+1);
+
+ result->isa = cls;
+ result->protocol_name = _strdup_internal(name);
+ ext->size = sizeof(*ext);
+
+ // fixme reserve name without installing
+
+ NXMapInsert(protocol_ext_map, result, result+1);
+
+ mutex_unlock(&classLock);
+
+ return (Protocol *)result;
+}
+
+
+/***********************************************************************
+* objc_registerProtocol
+* Registers a newly-constructed protocol. The protocol is now
+* ready for use and immutable.
+* Locking: acquires classLock
+**********************************************************************/
+void objc_registerProtocol(Protocol *proto_gen)
+{
+ struct old_protocol *proto = oldprotocol(proto_gen);
+
+ Class oldcls = (Class)objc_getClass("__IncompleteProtocol");
+ Class cls = (Class)objc_getClass("Protocol");
+
+ mutex_lock(&classLock);
+
+ if (proto->isa == cls) {
+ _objc_inform("objc_registerProtocol: protocol '%s' was already "
+ "registered!", proto->protocol_name);
+ mutex_unlock(&classLock);
+ return;
+ }
+ if (proto->isa != oldcls) {
+ _objc_inform("objc_registerProtocol: protocol '%s' was not allocated "
+ "with objc_allocateProtocol!", proto->protocol_name);
+ mutex_unlock(&classLock);
+ return;
+ }
+
+ proto->isa = cls;
+
+ NXMapKeyCopyingInsert(protocol_map, proto->protocol_name, proto);
+
+ mutex_unlock(&classLock);
+}
+
+
+/***********************************************************************
+* protocol_addProtocol
+* Adds an incorporated protocol to another protocol.
+* No method enforcement is performed.
+* `proto` must be under construction. `addition` must not.
+* Locking: acquires classLock
+**********************************************************************/
+void
+protocol_addProtocol(Protocol *proto_gen, Protocol *addition_gen)
+{
+ struct old_protocol *proto = oldprotocol(proto_gen);
+ struct old_protocol *addition = oldprotocol(addition_gen);
+
+ Class cls = (Class)objc_getClass("__IncompleteProtocol");
+
+ if (!proto_gen) return;
+ if (!addition_gen) return;
+
+ mutex_lock(&classLock);
+
+ if (proto->isa != cls) {
+ _objc_inform("protocol_addProtocol: modified protocol '%s' is not "
+ "under construction!", proto->protocol_name);
+ mutex_unlock(&classLock);
+ return;
+ }
+ if (addition->isa == cls) {
+ _objc_inform("protocol_addProtocol: added protocol '%s' is still "
+ "under construction!", addition->protocol_name);
+ mutex_unlock(&classLock);
+ return;
+ }
+
+ struct old_protocol_list *protolist = proto->protocol_list;
+ if (protolist) {
+ size_t size = sizeof(*protolist)
+ + protolist->count * sizeof(protolist->list[0]);
+ protolist = (struct old_protocol_list *)
+ _realloc_internal(protolist, size);
+ } else {
+ protolist = (struct old_protocol_list *)
+ _calloc_internal(1, sizeof(struct old_protocol_list));
+ }
+
+ protolist->list[protolist->count++] = addition;
+ proto->protocol_list = protolist;
+
+ mutex_unlock(&classLock);
+}
+
+
+/***********************************************************************
+* protocol_addMethodDescription
+* Adds a method to a protocol. The protocol must be under construction.
+* Locking: acquires classLock
+**********************************************************************/
+static void
+_protocol_addMethod(struct objc_method_description_list **list, SEL name, const char *types)
+{
+ if (!*list) {
+ *list = (struct objc_method_description_list *)
+ _calloc_internal(sizeof(struct objc_method_description_list), 1);
+ } else {
+ size_t size = sizeof(struct objc_method_description_list)
+ + (*list)->count * sizeof(struct objc_method_description);
+ *list = (struct objc_method_description_list *)
+ _realloc_internal(*list, size);
+ }
+
+ struct objc_method_description *desc = &(*list)->list[(*list)->count++];
+ desc->name = name;
+ desc->types = _strdup_internal(types ?: "");
+}
+
+void
+protocol_addMethodDescription(Protocol *proto_gen, SEL name, const char *types,
+ BOOL isRequiredMethod, BOOL isInstanceMethod)
+{
+ struct old_protocol *proto = oldprotocol(proto_gen);
+
+ Class cls = (Class)objc_getClass("__IncompleteProtocol");
+
+ if (!proto_gen) return;
+
+ mutex_lock(&classLock);
+
+ if (proto->isa != cls) {
+ _objc_inform("protocol_addMethodDescription: protocol '%s' is not "
+ "under construction!", proto->protocol_name);
+ mutex_unlock(&classLock);
+ return;
+ }
+
+ if (isRequiredMethod && isInstanceMethod) {
+ _protocol_addMethod(&proto->instance_methods, name, types);
+ } else if (isRequiredMethod && !isInstanceMethod) {
+ _protocol_addMethod(&proto->class_methods, name, types);
+ } else if (!isRequiredMethod && isInstanceMethod) {
+ struct old_protocol_ext *ext = (struct old_protocol_ext *)(proto+1);
+ _protocol_addMethod(&ext->optional_instance_methods, name, types);
+ } else /* !isRequiredMethod && !isInstanceMethod) */ {
+ struct old_protocol_ext *ext = (struct old_protocol_ext *)(proto+1);
+ _protocol_addMethod(&ext->optional_class_methods, name, types);
+ }
+
+ mutex_unlock(&classLock);
+}
+
+
+/***********************************************************************
+* protocol_addProperty
+* Adds a property to a protocol. The protocol must be under construction.
+* Locking: acquires classLock
+**********************************************************************/
+static void
+_protocol_addProperty(struct old_property_list **plist, const char *name,
+ const objc_property_attribute_t *attrs,
+ unsigned int count)
+{
+ if (!*plist) {
+ *plist = (struct old_property_list *)
+ _calloc_internal(sizeof(struct old_property_list), 1);
+ (*plist)->entsize = sizeof(struct old_property);
+ } else {
+ *plist = (struct old_property_list *)
+ _realloc_internal(*plist, sizeof(struct old_property_list)
+ + (*plist)->count * (*plist)->entsize);
+ }
+
+ struct old_property *prop = property_list_nth(*plist, (*plist)->count++);
+ prop->name = _strdup_internal(name);
+ prop->attributes = copyPropertyAttributeString(attrs, count);
+}
+
+void
+protocol_addProperty(Protocol *proto_gen, const char *name,
+ const objc_property_attribute_t *attrs,
+ unsigned int count,
+ BOOL isRequiredProperty, BOOL isInstanceProperty)
+{
+ struct old_protocol *proto = oldprotocol(proto_gen);
+
+ Class cls = (Class)objc_getClass("__IncompleteProtocol");
+
+ if (!proto) return;
+ if (!name) return;
+
+ mutex_lock(&classLock);
+
+ if (proto->isa != cls) {
+ _objc_inform("protocol_addProperty: protocol '%s' is not "
+ "under construction!", proto->protocol_name);
+ mutex_unlock(&classLock);
+ return;
+ }
+
+ struct old_protocol_ext *ext = ext_for_protocol(proto);
+
+ if (isRequiredProperty && isInstanceProperty) {
+ _protocol_addProperty(&ext->instance_properties, name, attrs, count);
+ }
+ //else if (isRequiredProperty && !isInstanceProperty) {
+ // _protocol_addProperty(&ext->class_properties, name, attrs, count);
+ //} else if (!isRequiredProperty && isInstanceProperty) {
+ // _protocol_addProperty(&ext->optional_instance_properties, name, attrs, count);
+ //} else /* !isRequiredProperty && !isInstanceProperty) */ {
+ // _protocol_addProperty(&ext->optional_class_properties, name, attrs, count);
+ //}
+
+ mutex_unlock(&classLock);
+}
+
+
/***********************************************************************
* _objc_fixup_protocol_objects_for_image. For each protocol in the
* specified image, selectorize the method names and add to the protocol hash.
#endif
}
+#if !TARGET_OS_WIN32
/***********************************************************************
* unmap_image
* Process the given image which is about to be unmapped by dyld.
* mh is mach_header instead of headerType because that's what
* dyld_priv.h says even for 64-bit.
**********************************************************************/
-__private_extern__ void
+PRIVATE_EXTERN void
unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide)
{
recursive_mutex_lock(&loadMethodLock);
- unmap_image_nolock(mh, vmaddr_slide);
+ unmap_image_nolock(mh);
recursive_mutex_unlock(&loadMethodLock);
}
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
**********************************************************************/
-__private_extern__ const char *
+PRIVATE_EXTERN const char *
map_images(enum dyld_image_states state, uint32_t infoCount,
const struct dyld_image_info infoList[])
{
*
* Locking: acquires classLock and loadMethodLock
**********************************************************************/
-__private_extern__ const char *
+PRIVATE_EXTERN const char *
load_images(enum dyld_image_states state, uint32_t infoCount,
const struct dyld_image_info infoList[])
{
return NULL;
}
+#endif
/***********************************************************************
* _read_images
* Perform metadata processing for hCount images starting with firstNewHeader
**********************************************************************/
-__private_extern__ void _read_images(header_info **hList, uint32_t hCount)
+PRIVATE_EXTERN void _read_images(header_info **hList, uint32_t hCount)
{
uint32_t i;
BOOL categoriesLoaded = NO;
cls->info |= CLS_LOADED;
}
-__private_extern__ void prepare_load_methods(header_info *hi)
+PRIVATE_EXTERN void prepare_load_methods(header_info *hi)
{
Module mods;
unsigned int midx;
#if TARGET_OS_WIN32
-__private_extern__ void unload_class(struct old_class *cls)
+PRIVATE_EXTERN void unload_class(struct old_class *cls)
{
}
}
-static void try_free(const void *p)
+PRIVATE_EXTERN void try_free(const void *p)
{
if (p && malloc_size(p)) free((void *)p);
}
try_free(mlist);
}
+static void unload_property_list(struct old_property_list *proplist)
+{
+ uint32_t i;
+
+ if (!proplist) return;
+
+ for (i = 0; i < proplist->count; i++) {
+ struct old_property *prop = property_list_nth(proplist, i);
+ try_free(prop->name);
+ try_free(prop->attributes);
+ }
+ try_free(proplist);
+}
+
+
// Deallocate all memory in a class.
-__private_extern__ void unload_class(struct old_class *cls)
+PRIVATE_EXTERN void unload_class(struct old_class *cls)
{
// Free method cache
// This dereferences the cache contents; do this before freeing methods
// more than zero property lists
if (cls->info & CLS_NO_PROPERTY_ARRAY) {
// one property list
- try_free(cls->ext->propertyLists);
+ struct old_property_list *proplist =
+ (struct old_property_list *)cls->ext->propertyLists;
+ unload_property_list(proplist);
} else {
// more than one property list
- struct objc_property_list **plistp;
+ struct old_property_list **plistp;
for (plistp = cls->ext->propertyLists;
*plistp != NULL;
plistp++)
{
- try_free(*plistp);
+ unload_property_list(*plistp);
}
try_free(cls->ext->propertyLists);
}
// Un-fix and re-pend any such class refs.
// Get the location of the dying image's __OBJC segment
- uintptr_t seg = hi->os.objcSegmentHeader->vmaddr + hi->os.image_slide;
- size_t seg_size = hi->os.objcSegmentHeader->filesize;
+ uintptr_t seg;
+ unsigned long seg_size;
+ seg = (uintptr_t)getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
header_info *other_hi;
for (other_hi = FirstHeader; other_hi != NULL; other_hi = other_hi->next) {
static void unload_paranoia(header_info *hi)
{
// Get the location of the dying image's __OBJC segment
- uintptr_t seg = hi->os.objcSegmentHeader->vmaddr + hi->os.image_slide;
- size_t seg_size = hi->os.objcSegmentHeader->filesize;
+ uintptr_t seg;
+ unsigned long seg_size;
+ seg = (uintptr_t)getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
_objc_inform("UNLOAD DEBUG: unloading image '%s' [%p..%p]",
_nameForHeader(hi->mhdr), (void *)seg, (void*)(seg+seg_size));
* Only handles MH_BUNDLE for now.
* Locking: loadMethodLock acquired by unmap_image
**********************************************************************/
-__private_extern__ void _unload_image(header_info *hi)
+PRIVATE_EXTERN void _unload_image(header_info *hi)
{
recursive_mutex_assert_locked(&loadMethodLock);
**********************************************************************/
void objc_addClass (Class cls_gen)
{
- struct old_class *cls = _class_asOld(cls_gen);
+ struct old_class *cls = oldcls(cls_gen);
OBJC_WARN_DEPRECATED;
* Does not take any locks.
* If the class is already in use, use class_addMethods() instead.
**********************************************************************/
-__private_extern__ void _objc_insertMethods(struct old_class *cls,
+PRIVATE_EXTERN void _objc_insertMethods(struct old_class *cls,
struct old_method_list *mlist,
struct old_category *cat)
{
* Does not flush any method caches.
* If the class is currently in use, use class_removeMethods() instead.
**********************************************************************/
-__private_extern__ void _objc_removeMethods(struct old_class *cls,
+PRIVATE_EXTERN void _objc_removeMethods(struct old_class *cls,
struct old_method_list *mlist)
{
struct old_method_list ***list;
**********************************************************************/
void _objc_resolve_categories_for_class(Class cls_gen)
{
- struct old_class *cls = _class_asOld(cls_gen);
+ struct old_class *cls = oldcls(cls_gen);
// If cls is a metaclass, get the class.
// resolve_categories_for_class() requires a real class to work correctly.
// Posee's meta's name is smashed and isn't in the class_hash,
// so objc_getClass doesn't work.
const char *baseName = strchr(cls->name, '%'); // get posee's real name
- cls = _class_asOld((Class)objc_getClass(baseName));
+ cls = oldcls((Class)objc_getClass(baseName));
} else {
- cls = _class_asOld((Class)objc_getClass(cls->name));
+ cls = oldcls((Class)objc_getClass(cls->name));
}
}
struct old_class *theClass;
// If the category's class exists, attach the category.
- if ((theClass = _class_asOld((Class)objc_lookUpClass(cat->class_name)))) {
+ if ((theClass = oldcls((Class)objc_lookUpClass(cat->class_name)))) {
return _objc_add_category_flush_caches(theClass, cat, version);
}
// then attach the category to the class but don't bother
// flushing any method caches (because they must be empty).
// YES unconnected, NO class_handler
- if ((theClass = _class_asOld((Class)look_up_class(cat->class_name, YES, NO)))) {
+ if ((theClass = oldcls((Class)look_up_class(cat->class_name, YES, NO)))) {
_objc_add_category(theClass, cat, version);
return NO;
}
}
-__private_extern__ const char **
+PRIVATE_EXTERN const char **
_objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount)
{
Module mods;
return list;
}
+Class gdb_class_getClass(Class cls)
+{
+ const char *className = cls->name;
+ if(!className || !strlen(className)) return Nil;
+ Class rCls = look_up_class(className, NO, NO);
+ return rCls;
+
+}
+
+Class gdb_object_getClass(id obj)
+{
+ Class cls = _object_getClass(obj);
+ return gdb_class_getClass(cls);
+}
+
BOOL gdb_objc_isRuntimeLocked()
{
if (mutex_try_lock(&methodListLock)) {
* Every lock used anywhere must be managed here.
* Locks not managed here may cause gdb deadlocks.
**********************************************************************/
-__private_extern__ rwlock_t selLock = {0};
-__private_extern__ mutex_t classLock = MUTEX_INITIALIZER;
-__private_extern__ mutex_t methodListLock = MUTEX_INITIALIZER;
-__private_extern__ mutex_t cacheUpdateLock = MUTEX_INITIALIZER;
-__private_extern__ recursive_mutex_t loadMethodLock = RECURSIVE_MUTEX_INITIALIZER;
+PRIVATE_EXTERN rwlock_t selLock = {0};
+PRIVATE_EXTERN mutex_t classLock = MUTEX_INITIALIZER;
+PRIVATE_EXTERN mutex_t methodListLock = MUTEX_INITIALIZER;
+PRIVATE_EXTERN mutex_t cacheUpdateLock = MUTEX_INITIALIZER;
+PRIVATE_EXTERN recursive_mutex_t loadMethodLock = RECURSIVE_MUTEX_INITIALIZER;
static int debugger_selLock;
static int debugger_loadMethodLock;
#define RDONLY 1
#define RDWR 2
-__private_extern__ void lock_init(void)
+PRIVATE_EXTERN void lock_init(void)
{
rwlock_init(&selLock);
recursive_mutex_init(&loadMethodLock);
}
-#ifndef NO_DEBUGGER_MODE
+#if SUPPORT_DEBUGGER_MODE
/***********************************************************************
* startDebuggerMode
* attempt to manipulate them will cause a trap.
* Locks not handled here may cause deadlocks in gdb.
**********************************************************************/
-__private_extern__ int startDebuggerMode(void)
+PRIVATE_EXTERN int startDebuggerMode(void)
{
int result = DEBUGGER_FULL;
* endDebuggerMode
* Relinquish locks acquired in startDebuggerMode().
**********************************************************************/
-__private_extern__ void endDebuggerMode(void)
+PRIVATE_EXTERN void endDebuggerMode(void)
{
if (debugger_loadMethodLock) {
recursive_mutex_unlock(&loadMethodLock);
* Returns YES if the given lock is handled specially during debugger
* mode (i.e. debugger mode tries to acquire it).
**********************************************************************/
-__private_extern__ BOOL isManagedDuringDebugger(void *lock)
+PRIVATE_EXTERN BOOL isManagedDuringDebugger(void *lock)
{
if (lock == &selLock) return YES;
if (lock == &classLock) return YES;
* Locking a managed mutex during debugger mode causes a trap unless
* this returns YES.
**********************************************************************/
-__private_extern__ BOOL isLockedDuringDebugger(mutex_t *lock)
+PRIVATE_EXTERN BOOL isLockedDuringDebugger(mutex_t *lock)
{
assert(DebuggerMode);
* Read-locking a managed rwlock during debugger mode causes a trap unless
* this returns YES.
**********************************************************************/
-__private_extern__ BOOL isReadingDuringDebugger(rwlock_t *lock)
+PRIVATE_EXTERN BOOL isReadingDuringDebugger(rwlock_t *lock)
{
assert(DebuggerMode);
* Write-locking a managed rwlock during debugger mode causes a trap unless
* this returns YES.
**********************************************************************/
-__private_extern__ BOOL isWritingDuringDebugger(rwlock_t *lock)
+PRIVATE_EXTERN BOOL isWritingDuringDebugger(rwlock_t *lock)
{
assert(DebuggerMode);
return NO;
}
-// !defined(NO_DEBUGGER_MODE)
+// SUPPORT_DEBUGGER_MODE
#endif
#endif
#include "objc-private.h"
#include "objc-loadmethod.h"
+#include "message.h"
OBJC_EXPORT Class getOriginalClassForPosingClass(Class);
**********************************************************************/
// Settings from environment variables
-#ifndef NO_ENVIRON
-__private_extern__ int PrintImages = -1; // env OBJC_PRINT_IMAGES
-__private_extern__ int PrintLoading = -1; // env OBJC_PRINT_LOAD_METHODS
-__private_extern__ int PrintInitializing = -1; // env OBJC_PRINT_INITIALIZE_METHODS
-__private_extern__ int PrintResolving = -1; // env OBJC_PRINT_RESOLVED_METHODS
-__private_extern__ int PrintConnecting = -1; // env OBJC_PRINT_CLASS_SETUP
-__private_extern__ int PrintProtocols = -1; // env OBJC_PRINT_PROTOCOL_SETUP
-__private_extern__ int PrintIvars = -1; // env OBJC_PRINT_IVAR_SETUP
-__private_extern__ int PrintVtables = -1; // env OBJC_PRINT_VTABLE_SETUP
-__private_extern__ int PrintVtableImages = -1;//env OBJC_PRINT_VTABLE_IMAGES
-__private_extern__ int PrintFuture = -1; // env OBJC_PRINT_FUTURE_CLASSES
-__private_extern__ int PrintRTP = -1; // env OBJC_PRINT_RTP
-__private_extern__ int PrintGC = -1; // env OBJC_PRINT_GC
-__private_extern__ int PrintPreopt = -1; // env OBJC_PRINT_PREOPTIMIZATION
-__private_extern__ int PrintCxxCtors = -1; // env OBJC_PRINT_CXX_CTORS
-__private_extern__ int PrintExceptions = -1; // env OBJC_PRINT_EXCEPTIONS
-__private_extern__ int PrintAltHandlers = -1; // env OBJC_PRINT_ALT_HANDLERS
-__private_extern__ int PrintDeprecation = -1;// env OBJC_PRINT_DEPRECATION_WARNINGS
-__private_extern__ int PrintReplacedMethods = -1; // env OBJC_PRINT_REPLACED_METHODS
-__private_extern__ int PrintCaches = -1; // env OBJC_PRINT_CACHE_SETUP
-
-__private_extern__ int UseInternalZone = -1; // env OBJC_USE_INTERNAL_ZONE
-
-__private_extern__ int DebugUnload = -1; // env OBJC_DEBUG_UNLOAD
-__private_extern__ int DebugFragileSuperclasses = -1; // env OBJC_DEBUG_FRAGILE_SUPERCLASSES
-__private_extern__ int DebugNilSync = -1; // env OBJC_DEBUG_NIL_SYNC
-
-__private_extern__ int DisableGC = -1; // env OBJC_DISABLE_GC
-__private_extern__ int DisableVtables = -1; // env OBJC_DISABLE_VTABLES
-__private_extern__ int DisablePreopt = -1; // env OBJC_DISABLE_PREOPTIMIZATION
-__private_extern__ int DebugFinalizers = -1; // env OBJC_DEBUG_FINALIZERS
+#if SUPPORT_ENVIRON
+PRIVATE_EXTERN int PrintImages = -1; // env OBJC_PRINT_IMAGES
+PRIVATE_EXTERN int PrintLoading = -1; // env OBJC_PRINT_LOAD_METHODS
+PRIVATE_EXTERN int PrintInitializing = -1; // env OBJC_PRINT_INITIALIZE_METHODS
+PRIVATE_EXTERN int PrintResolving = -1; // env OBJC_PRINT_RESOLVED_METHODS
+PRIVATE_EXTERN int PrintConnecting = -1; // env OBJC_PRINT_CLASS_SETUP
+PRIVATE_EXTERN int PrintProtocols = -1; // env OBJC_PRINT_PROTOCOL_SETUP
+PRIVATE_EXTERN int PrintIvars = -1; // env OBJC_PRINT_IVAR_SETUP
+PRIVATE_EXTERN int PrintVtables = -1; // env OBJC_PRINT_VTABLE_SETUP
+PRIVATE_EXTERN int PrintVtableImages = -1;//env OBJC_PRINT_VTABLE_IMAGES
+PRIVATE_EXTERN int PrintFuture = -1; // env OBJC_PRINT_FUTURE_CLASSES
+PRIVATE_EXTERN int PrintRTP = -1; // env OBJC_PRINT_RTP
+PRIVATE_EXTERN int PrintGC = -1; // env OBJC_PRINT_GC
+PRIVATE_EXTERN int PrintPreopt = -1; // env OBJC_PRINT_PREOPTIMIZATION
+PRIVATE_EXTERN int PrintCxxCtors = -1; // env OBJC_PRINT_CXX_CTORS
+PRIVATE_EXTERN int PrintExceptions = -1; // env OBJC_PRINT_EXCEPTIONS
+PRIVATE_EXTERN int PrintExceptionThrow = -1; // env OBJC_PRINT_EXCEPTION_THROW
+PRIVATE_EXTERN int PrintAltHandlers = -1; // env OBJC_PRINT_ALT_HANDLERS
+PRIVATE_EXTERN int PrintDeprecation = -1;// env OBJC_PRINT_DEPRECATION_WARNINGS
+PRIVATE_EXTERN int PrintReplacedMethods = -1; // env OBJC_PRINT_REPLACED_METHODS
+PRIVATE_EXTERN int PrintCaches = -1; // env OBJC_PRINT_CACHE_SETUP
+PRIVATE_EXTERN int PrintPoolHiwat = -1; // env OBJC_PRINT_POOL_HIGHWATER
+
+PRIVATE_EXTERN int UseInternalZone = -1; // env OBJC_USE_INTERNAL_ZONE
+
+PRIVATE_EXTERN int DebugUnload = -1; // env OBJC_DEBUG_UNLOAD
+PRIVATE_EXTERN int DebugFragileSuperclasses = -1; // env OBJC_DEBUG_FRAGILE_SUPERCLASSES
+PRIVATE_EXTERN int DebugNilSync = -1; // env OBJC_DEBUG_NIL_SYNC
+PRIVATE_EXTERN int DebugNonFragileIvars = -1; // env OBJC_DEBUG_NONFRAGILE_IVARS
+PRIVATE_EXTERN int DebugAltHandlers = -1;// env OBJC_DEBUG_ALT_HANDLERS
+
+PRIVATE_EXTERN int DisableGC = -1; // env OBJC_DISABLE_GC
+PRIVATE_EXTERN int DisableVtables = -1; // env OBJC_DISABLE_VTABLES
+PRIVATE_EXTERN int DisablePreopt = -1; // env OBJC_DISABLE_PREOPTIMIZATION
+PRIVATE_EXTERN int DebugFinalizers = -1; // env OBJC_DEBUG_FINALIZERS
#endif
static tls_key_t _objc_pthread_key;
// Selectors
-__private_extern__ SEL SEL_load = NULL;
-__private_extern__ SEL SEL_initialize = NULL;
-__private_extern__ SEL SEL_resolveInstanceMethod = NULL;
-__private_extern__ SEL SEL_resolveClassMethod = NULL;
-__private_extern__ SEL SEL_cxx_construct = NULL;
-__private_extern__ SEL SEL_cxx_destruct = NULL;
-__private_extern__ SEL SEL_retain = NULL;
-__private_extern__ SEL SEL_release = NULL;
-__private_extern__ SEL SEL_autorelease = NULL;
-__private_extern__ SEL SEL_copy = NULL;
-__private_extern__ SEL SEL_finalize = NULL;
-
-__private_extern__ header_info *FirstHeader NOBSS = 0; // NULL means empty list
-__private_extern__ header_info *LastHeader NOBSS = 0; // NULL means invalid; recompute it
-__private_extern__ int HeaderCount NOBSS = 0;
+PRIVATE_EXTERN SEL SEL_load = NULL;
+PRIVATE_EXTERN SEL SEL_initialize = NULL;
+PRIVATE_EXTERN SEL SEL_resolveInstanceMethod = NULL;
+PRIVATE_EXTERN SEL SEL_resolveClassMethod = NULL;
+PRIVATE_EXTERN SEL SEL_cxx_construct = NULL;
+PRIVATE_EXTERN SEL SEL_cxx_destruct = NULL;
+PRIVATE_EXTERN SEL SEL_retain = NULL;
+PRIVATE_EXTERN SEL SEL_release = NULL;
+PRIVATE_EXTERN SEL SEL_autorelease = NULL;
+PRIVATE_EXTERN SEL SEL_retainCount = NULL;
+PRIVATE_EXTERN SEL SEL_alloc = NULL;
+PRIVATE_EXTERN SEL SEL_copy = NULL;
+PRIVATE_EXTERN SEL SEL_new = NULL;
+PRIVATE_EXTERN SEL SEL_finalize = NULL;
+PRIVATE_EXTERN SEL SEL_forwardInvocation = NULL;
+
+PRIVATE_EXTERN header_info *FirstHeader = 0; // NULL means empty list
+PRIVATE_EXTERN header_info *LastHeader = 0; // NULL means invalid; recompute it
+PRIVATE_EXTERN int HeaderCount = 0;
/***********************************************************************
* _nameForHeader.
**********************************************************************/
-__private_extern__ const char *_nameForHeader(const headerType *header)
+PRIVATE_EXTERN const char *_nameForHeader(const headerType *header)
{
return _getObjcHeaderName ((headerType *) header);
}
/***********************************************************************
* _objc_appendHeader. Add a newly-constructed header_info to the list.
**********************************************************************/
-__private_extern__ void _objc_appendHeader(header_info *hi)
+PRIVATE_EXTERN void _objc_appendHeader(header_info *hi)
{
// Add the header to the header list.
// The header is appended to the list, to preserve the bottom-up order.
* LastHeader is set to NULL. Any code that uses LastHeader must
* detect this NULL and recompute LastHeader by traversing the list.
**********************************************************************/
-__private_extern__ void _objc_removeHeader(header_info *hi)
+PRIVATE_EXTERN void _objc_removeHeader(header_info *hi)
{
header_info **hiP;
* Read environment variables that affect the runtime.
* Also print environment variable help, if requested.
**********************************************************************/
-__private_extern__ void environ_init(void)
+PRIVATE_EXTERN void environ_init(void)
{
-#ifndef NO_ENVIRON
+#if SUPPORT_ENVIRON
int PrintHelp = (getenv("OBJC_HELP") != NULL);
int PrintOptions = (getenv("OBJC_PRINT_OPTIONS") != NULL);
int secure = issetugid();
"log calls to C++ ctors and dtors for instance variables");
OPTION(PrintExceptions, OBJC_PRINT_EXCEPTIONS,
"log exception handling");
+ OPTION(PrintExceptionThrow, OBJC_PRINT_EXCEPTION_THROW,
+ "log backtrace of every objc_exception_throw()");
OPTION(PrintAltHandlers, OBJC_PRINT_ALT_HANDLERS,
"log processing of exception alt handlers");
OPTION(PrintReplacedMethods, OBJC_PRINT_REPLACED_METHODS,
"log methods replaced by category implementations");
OPTION(PrintDeprecation, OBJC_PRINT_DEPRECATION_WARNINGS,
"warn about calls to deprecated runtime functions");
+ OPTION(PrintPoolHiwat, OBJC_PRINT_POOL_HIGHWATER,
+ "print high-water marks for autorelease pools");
OPTION(DebugUnload, OBJC_DEBUG_UNLOAD,
"warn about poorly-behaving bundles when unloaded");
"warn about classes that implement -dealloc but not -finalize");
OPTION(DebugNilSync, OBJC_DEBUG_NIL_SYNC,
"warn about @synchronized(nil), which does no synchronization");
+ OPTION(DebugNonFragileIvars, OBJC_DEBUG_NONFRAGILE_IVARS,
+ "capriciously rearrange non-fragile ivars");
+ OPTION(DebugAltHandlers, OBJC_DEBUG_ALT_HANDLERS,
+ "record more info about bad alt handler use");
OPTION(UseInternalZone, OBJC_USE_INTERNAL_ZONE,
"allocate runtime data in a dedicated malloc zone");
* logReplacedMethod
* OBJC_PRINT_REPLACED_METHODS implementation
**********************************************************************/
-__private_extern__ void
+PRIVATE_EXTERN void
logReplacedMethod(const char *className, SEL s,
BOOL isMeta, const char *catName,
IMP oldImp, IMP newImp)
* If the data doesn't exist yet and create is NO, return NULL.
* If the data doesn't exist yet and create is YES, allocate and return it.
**********************************************************************/
-__private_extern__ _objc_pthread_data *_objc_fetch_pthread_data(BOOL create)
+PRIVATE_EXTERN _objc_pthread_data *_objc_fetch_pthread_data(BOOL create)
{
_objc_pthread_data *data;
* arg shouldn't be NULL, but we check anyway.
**********************************************************************/
extern void _destroyInitializingClassList(struct _objc_initializing_classes *list);
-__private_extern__ void _objc_pthread_destroyspecific(void *arg)
+PRIVATE_EXTERN void _objc_pthread_destroyspecific(void *arg)
{
_objc_pthread_data *data = (_objc_pthread_data *)arg;
if (data != NULL) {
_destroyInitializingClassList(data->initializingClasses);
- _destroyLockList(data->lockList);
_destroySyncCache(data->syncCache);
_destroyAltHandlerList(data->handlerList);
}
-__private_extern__ void tls_init(void)
+PRIVATE_EXTERN void tls_init(void)
{
-#ifdef NO_DIRECT_THREAD_KEYS
- tls_create(&_objc_pthread_key, &_objc_pthread_destroyspecific);
-#else
+#if SUPPORT_DIRECT_THREAD_KEYS
_objc_pthread_key = TLS_DIRECT_KEY;
pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
+#else
+ _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
}
-#if !TARGET_OS_WIN32
+#if !(TARGET_OS_WIN32 || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
/***********************************************************************
* _objc_setNilReceiver
**********************************************************************/
}
-#if defined(__ppc__) || defined(__ppc64__)
-
-// Test to see if either the displacement or destination is within
-// the +/- 2^25 range needed for a PPC branch immediate instruction.
-// Shifting the high bit of the displacement (or destination)
-// left 6 bits and then 6 bits arithmetically to the right does a
-// sign extend of the 26th bit. If that result is equivalent to the
-// original value, then the displacement (or destination) will fit
-// into a simple branch. Otherwise a larger branch sequence is required.
-// ppc64: max displacement is still +/- 2^25, but intptr_t is bigger
-
-// tiny: bc*
-// small: b, ba (unconditional only)
-// 32: bctr with lis+ori only
-static BOOL ppc_tiny_displacement(intptr_t displacement)
-{
- size_t shift = sizeof(intptr_t) - 16; // ilp32=16, lp64=48
- return (((displacement << shift) >> shift) == displacement);
-}
-
-static BOOL ppc_small_displacement(intptr_t displacement)
-{
- size_t shift = sizeof(intptr_t) - 26; // ilp32=6, lp64=38
- return (((displacement << shift) >> shift) == displacement);
-}
-
-#if defined(__ppc64__)
-// Same as ppc_small_displacement, but decides whether 32 bits is big enough.
-static BOOL ppc_32bit_displacement(intptr_t displacement)
-{
- size_t shift = sizeof(intptr_t) - 32;
- return (((displacement << shift) >> shift) == displacement);
-}
-#endif
-
-/**********************************************************************
-* objc_branch_size
-* Returns the number of instructions needed
-* for a branch from entry to target.
-**********************************************************************/
-__private_extern__ size_t objc_branch_size(void *entry, void *target)
-{
- return objc_cond_branch_size(entry, target, COND_ALWAYS);
-}
-
-__private_extern__ size_t
-objc_cond_branch_size(void *entry, void *target, unsigned cond)
-{
- intptr_t destination = (intptr_t)target;
- intptr_t displacement = (intptr_t)destination - (intptr_t)entry;
-
- if (cond == COND_ALWAYS && ppc_small_displacement(displacement)) {
- // fits in unconditional relative branch immediate
- return 1;
- }
- if (cond == COND_ALWAYS && ppc_small_displacement(destination)) {
- // fits in unconditional absolute branch immediate
- return 1;
- }
- if (ppc_tiny_displacement(displacement)) {
- // fits in conditional relative branch immediate
- return 1;
- }
- if (ppc_tiny_displacement(destination)) {
- // fits in conditional absolute branch immediate
- return 1;
- }
-#if defined(__ppc64__)
- if (!ppc_32bit_displacement(destination)) {
- // fits in 64-bit absolute branch through CTR
- return 7;
- }
-#endif
-
- // fits in 32-bit absolute branch through CTR
- return 4;
-}
-
-/**********************************************************************
-* objc_write_branch
-* Writes at entry a PPC branch instruction sequence that branches to target.
-* The sequence written will be objc_branch_size(entry, target) instructions.
-* Returns the number of instructions written.
-**********************************************************************/
-__private_extern__ size_t objc_write_branch(void *entry, void *target)
-{
- return objc_write_cond_branch(entry, target, COND_ALWAYS);
-}
-
-__private_extern__ size_t
-objc_write_cond_branch(void *entry, void *target, unsigned cond)
-{
- unsigned *address = (unsigned *)entry; // location to store the 32 bit PPC instructions
- intptr_t destination = (intptr_t)target; // destination as an absolute address
- intptr_t displacement = (intptr_t)destination - (intptr_t)address; // destination as a branch relative offset
-
- if (cond == COND_ALWAYS && ppc_small_displacement(displacement)) {
- // use unconditional relative branch with the displacement
- address[0] = 0x48000000 | (unsigned)(displacement & 0x03fffffc); // b *+displacement
- // issued 1 instruction
- return 1;
- }
- if (cond == COND_ALWAYS && ppc_small_displacement(destination)) {
- // use unconditional absolute branch with the destination
- address[0] = 0x48000000 | (unsigned)(destination & 0x03fffffc) | 2; // ba destination (2 is the absolute flag)
- // issued 1 instruction
- return 1;
- }
-
- if (ppc_tiny_displacement(displacement)) {
- // use conditional relative branch with the displacement
- address[0] = 0x40000000 | cond | (unsigned)(displacement & 0x0000fffc); // b *+displacement
- // issued 1 instruction
- return 1;
- }
- if (ppc_tiny_displacement(destination)) {
- // use conditional absolute branch with the destination
- address[0] = 0x40000000 | cond | (unsigned)(destination & 0x0000fffc) | 2; // ba destination (2 is the absolute flag)
- // issued 1 instruction
- return 1;
- }
-
-
- // destination is large and far away.
- // Use an absolute branch via CTR.
-
-#if defined(__ppc64__)
- if (!ppc_32bit_displacement(destination)) {
- uint16_t lo = destination & 0xffff;
- uint16_t hi = (destination >> 16) & 0xffff;
- uint16_t hi2 = (destination >> 32) & 0xffff;
- uint16_t hi3 = (destination >> 48) & 0xffff;
-
- address[0] = 0x3d800000 | hi3; // lis r12, hi3
- address[1] = 0x618c0000 | hi2; // ori r12, r12, hi2
- address[2] = 0x798c07c6; // sldi r12, r12, 32
- address[3] = 0x658c0000 | hi; // oris r12, r12, hi
- address[4] = 0x618c0000 | lo; // ori r12, r12, lo
- address[5] = 0x7d8903a6; // mtctr r12
- address[6] = 0x4c000420 | cond; // bctr
- // issued 7 instructions
- return 7;
- }
-#endif
-
- {
- uint16_t lo = destination & 0xffff;
- uint16_t hi = (destination >> 16) & 0xffff;
-
- address[0] = 0x3d800000 | hi; // lis r12,hi
- address[1] = 0x618c0000 | lo; // ori r12,r12,lo
- address[2] = 0x7d8903a6; // mtctr r12
- address[3] = 0x4c000420 | cond; // bctr
- // issued 4 instructions
- return 4;
- }
-}
-
-// defined(__ppc__) || defined(__ppc64__)
-#endif
-
#if defined(__i386__) || defined(__x86_64__)
/**********************************************************************
* Returns the number of BYTES needed
* for a branch from entry to target.
**********************************************************************/
-__private_extern__ size_t objc_branch_size(void *entry, void *target)
+PRIVATE_EXTERN size_t objc_branch_size(void *entry, void *target)
{
return objc_cond_branch_size(entry, target, COND_ALWAYS);
}
-__private_extern__ size_t
+PRIVATE_EXTERN size_t
objc_cond_branch_size(void *entry, void *target, unsigned cond)
{
// For simplicity, always use 32-bit relative jumps.
* The sequence written will be objc_branch_size(entry, target) BYTES.
* Returns the number of BYTES written.
**********************************************************************/
-__private_extern__ size_t objc_write_branch(void *entry, void *target)
+PRIVATE_EXTERN size_t objc_write_branch(void *entry, void *target)
{
return objc_write_cond_branch(entry, target, COND_ALWAYS);
}
-__private_extern__ size_t
+PRIVATE_EXTERN size_t
objc_write_cond_branch(void *entry, void *target, unsigned cond)
{
uint8_t *address = (uint8_t *)entry; // instructions written to here
#endif
#if TARGET_OS_WIN32
charactersCopied = 0;
- szFileName = malloc(MAX_PATH);
+ szFileName = malloc(MAX_PATH * sizeof(TCHAR));
origCls = objc_getOrigClass(class_getName(cls));
classModule = NULL;
res = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)origCls, &classModule);
if (res && classModule) {
- charactersCopied = GetModuleFileName(classModule, szFileName, MAX_PATH);
+ charactersCopied = GetModuleFileName(classModule, szFileName, MAX_PATH * sizeof(TCHAR));
}
if (classModule) FreeLibrary(classModule);
if (charactersCopied) {
header_info *hi;
int count = 0;
int max = HeaderCount;
+#if TARGET_OS_WIN32
+ const TCHAR **names = calloc(max+1, sizeof(TCHAR *));
+#else
const char **names = calloc(max+1, sizeof(char *));
+#endif
for (hi = FirstHeader; hi != NULL && count < max; hi = hi->next) {
#if TARGET_OS_WIN32
- TCHAR *szFileName;
- DWORD charactersCopied;
-
- szFileName = malloc(MAX_PATH);
-
- charactersCopied = GetModuleFileName((HMODULE)(hi->mhdr), szFileName, MAX_PATH);
- if (charactersCopied)
- names[count++] = (const char *)szFileName;
- else
- free(szFileName);
+ if (hi->os.moduleName) {
+ names[count++] = hi->os.moduleName;
+ }
#else
if (hi->os.dl_info.dli_fname) {
names[count++] = hi->os.dl_info.dli_fname;
// Find the image.
for (hi = FirstHeader; hi != NULL; hi = hi->next) {
#if TARGET_OS_WIN32
- // fixme
+ if (0 == wcscmp((TCHAR *)image, hi->os.moduleName)) break;
#else
if (0 == strcmp(image, hi->os.dl_info.dli_fname)) break;
#endif
}
+/**********************************************************************
+* Associative Reference Support
+**********************************************************************/
+
+#if SUPPORT_GC
+PRIVATE_EXTERN id objc_getAssociatedObject_gc(id object, const void *key) {
+ return auto_zone_get_associative_ref(gc_zone, object, (void *)key);
+}
+#endif
+
+PRIVATE_EXTERN id objc_getAssociatedObject_non_gc(id object, const void *key) {
+ return _object_get_associative_reference(object, (void *)key);
+}
+
+id objc_getAssociatedObject(id object, const void *key) {
+#if SUPPORT_GC
+ if (UseGC) {
+ return auto_zone_get_associative_ref(gc_zone, object, (void *)key);
+ } else
+#endif
+ {
+ return _object_get_associative_reference(object, (void *)key);
+ }
+}
+
+#if SUPPORT_GC
+PRIVATE_EXTERN void objc_setAssociatedObject_gc(id object, const void *key, id value, objc_AssociationPolicy policy) {
+ if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) {
+ value = objc_msgSend(value, SEL_copy);
+ }
+ auto_zone_set_associative_ref(gc_zone, object, (void *)key, value);
+}
+#endif
+
+PRIVATE_EXTERN void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy) {
+ _object_set_associative_reference(object, (void *)key, value, policy);
+}
+
+void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
+#if SUPPORT_GC
+ if (UseGC) {
+ if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) {
+ value = objc_msgSend(value, SEL_copy);
+ }
+ auto_zone_set_associative_ref(gc_zone, object, (void *)key, value);
+ } else
+#endif
+ {
+ // Note, creates a retained reference in non-GC.
+ _object_set_associative_reference(object, (void *)key, value, policy);
+ }
+}
+
+void objc_removeAssociatedObjects(id object) {
+#if SUPPORT_GC
+ if (UseGC) {
+ auto_zone_erase_associative_refs(gc_zone, object);
+ } else
+#endif
+ {
+ if (_class_instancesHaveAssociatedObjects(_object_getClass(object))) _object_remove_assocations(object);
+ }
+}
+
+BOOL class_instancesHaveAssociatedObjects(Class cls) {
+ return _class_instancesHaveAssociatedObjects(cls);
+}
+
+
/**********************************************************************
* Debugger mode
*
* handled there is a potential gdb deadlock.
**********************************************************************/
-#ifndef NO_DEBUGGER_MODE
+#if SUPPORT_DEBUGGER_MODE
-__private_extern__ int DebuggerMode = DEBUGGER_OFF;
-__private_extern__ objc_thread_t DebuggerModeThread = 0;
+PRIVATE_EXTERN int DebuggerMode = DEBUGGER_OFF;
+PRIVATE_EXTERN objc_thread_t DebuggerModeThread = 0;
static int DebuggerModeCount;
/**********************************************************************
_objc_fatal("DEBUGGER MODE: failed");
}
-// !defined(NO_DEBUGGER_MODE)
+// SUPPORT_DEBUGGER_MODE
#endif
#include "objc-private.h"
#include "objc-sel-set.h"
-#ifdef NO_MOD
+#if !SUPPORT_MOD
// mod-free power of 2 version
#define CONSTRAIN(val, range) ((val) & ((range)-1))
if (!currentSel) {
ret.nomatch = probe;
return ret;
- } else if (!ret.match && 0 == _objc_strcmp((const char *)currentSel, (const char *)candidate)) {
+ } else if (!ret.match && 0 == strcmp((const char *)currentSel, (const char *)candidate)) {
ret.match = currentSel;
}
probe++;
}
// create a set with given starting capacity, will resize as needed
-__private_extern__ struct __objc_sel_set *__objc_sel_set_create(uint32_t capacity) {
+PRIVATE_EXTERN struct __objc_sel_set *__objc_sel_set_create(uint32_t capacity) {
uint32_t idx;
struct __objc_sel_set *sset = _malloc_internal(sizeof(struct __objc_sel_set));
}
// returns 0 on failure; candidate may not be 0
-__private_extern__ SEL __objc_sel_set_get(struct __objc_sel_set *sset, SEL candidate) {
+PRIVATE_EXTERN SEL __objc_sel_set_get(struct __objc_sel_set *sset, SEL candidate) {
return __objc_sel_set_findBuckets(sset, candidate).match;
}
// value may not be 0; should not be called unless it is known the value is not in the set
-__private_extern__ void __objc_sel_set_add(struct __objc_sel_set *sset, SEL value) {
+PRIVATE_EXTERN void __objc_sel_set_add(struct __objc_sel_set *sset, SEL value) {
if (sset->_count == sset->_capacity) {
SEL *oldbuckets = sset->_buckets;
uint32_t oldnbuckets = sset->_bucketsNum;
/* This file is automatically generated. Do not edit. */
-.section __TEXT,__objc_selopt
-.private_extern __objc_selopt_data
-__objc_selopt_data:
-.long 3 /* table.version */
-.long 4 /* table.capacity */
-.long 4 /* table.occupied */
-.long 63 /* table.shift */
-.long 3 /* table.mask */
-.long 0 /* table.zero */
-.quad 0 /* table.salt */
-.quad 0 /* table.base */
-.space 256*4 /* table.scramble */
-.long 0 /* table.tab */
-.long 20 /* table.offsets */
-.long 20
-.long 20
-.long 20
-/* space for smax/capacity=131072, blen/mask=65535+1 */
+.section __TEXT,__objc_opt_ro
+.align 3
+.private_extern __objc_opt_data
+__objc_opt_data:
+.long 10 /* table.version */
+.long 0 /* table.selopt_offset */
+.space 4096-8
+
+/* space for selopt, smax/capacity=131072, blen/mask=65535+1 */
.space 65536
-.space 131072*4
+.space 131072*4 /* offsets */
+.space 131072 /* checkbytes */
+
+#if __arm__
+.section __DATA,__objc_opt_rw
+.align 3
+.space 256*1024
+#else
+/* optimization performed in linker, not shared cache */
+#endif
#include "objc-auto.h"
#include "objc-sel-set.h"
-#ifndef NO_BUILTINS
+#if SUPPORT_BUILTINS
#include "objc-selopt.h"
#endif
__BEGIN_DECLS
-#ifndef NO_BUILTINS
-// builtins: the actual table used at runtime
-// _objc_selopt_data: the usual builtin table, possibly rewritten by dyld
-// empty_selopt_data: an empty table to use if DisablePreopt is set
-using namespace objc_selopt;
+#if SUPPORT_BUILTINS
+// opt: the actual opt used at runtime
+// builtins: the actual selector table used at runtime
+// _objc_opt_data: opt data possibly written by dyld
+// empty_opt_data: empty data to use if dyld didn't cooperate or DisablePreopt
+using namespace objc_opt;
static const objc_selopt_t *builtins = NULL;
-extern const objc_selopt_t _objc_selopt_data; // in __TEXT, __objc_selopt
-static const uint32_t empty_selopt_data[] = SELOPT_INITIALIZER;
+static const objc_opt_t *opt = NULL;
+static BOOL preoptimized;
+
+extern const objc_opt_t _objc_opt_data; // in __TEXT, __objc_selopt
+static const uint32_t empty_opt_data[] = OPT_INITIALIZER;
#endif
static struct __objc_sel_set *_objc_selectors = NULL;
-#ifndef NO_GC
-static inline int ignore_selector(const char *sel)
-{
- // force retain/release/autorelease to be a constant value when GC is on
- // note that the selectors for "Protocol" are registered before we can
- // see the executable image header that sets _WantsGC, so we can't cache
- // this result (sigh).
- return (UseGC &&
- ( (sel[0] == 'r' && sel[1] == 'e' &&
- (_objc_strcmp(&sel[2], "lease") == 0 ||
- _objc_strcmp(&sel[2], "tain") == 0 ||
- _objc_strcmp(&sel[2], "tainCount") == 0 ))
- ||
- (_objc_strcmp(sel, "dealloc") == 0)
- ||
- (sel[0] == 'a' && sel[1] == 'u' &&
- _objc_strcmp(&sel[2], "torelease") == 0)));
-}
-#endif
-
-
-#ifndef NO_BUILTINS
-__private_extern__ void dump_builtins(void)
+#if SUPPORT_BUILTINS
+PRIVATE_EXTERN void dump_builtins(void)
{
- if (builtins->version != VERSION) {
- _objc_inform("BUILTIN SELECTORS: unknown version %d (want %d)",
- builtins->version, VERSION);
- return;
- }
-
uint32_t occupied = builtins->occupied;
uint32_t capacity = builtins->capacity;
- const int32_t *offsets = builtins->offsets();
- uint32_t i;
- for (i = 0; i < capacity; i++) {
- if (offsets[i] != offsetof(objc_selopt_t, zero)) {
- const char *str = (const char *)builtins + offsets[i];
- _objc_inform("BUILTIN SELECTORS: %6d: %+8d %s",
- i, offsets[i], str);
- } else {
- _objc_inform("BUILTIN SELECTORS: %6d: ", i);
- }
- }
-
_objc_inform("BUILTIN SELECTORS: %d selectors", occupied);
_objc_inform("BUILTIN SELECTORS: %d/%d (%d%%) hash table occupancy",
occupied, capacity, (int)(occupied/(double)capacity * 100));
_objc_inform("BUILTIN SELECTORS: using __TEXT,__objc_selopt at %p",
builtins);
- _objc_inform("BUILTIN SELECTORS: version: %u", builtins->version);
_objc_inform("BUILTIN SELECTORS: capacity: %u", builtins->capacity);
_objc_inform("BUILTIN SELECTORS: occupied: %u", builtins->occupied);
_objc_inform("BUILTIN SELECTORS: shift: %u", builtins->shift);
_objc_inform("BUILTIN SELECTORS: mask: 0x%x", builtins->mask);
_objc_inform("BUILTIN SELECTORS: zero: %u", builtins->zero);
_objc_inform("BUILTIN SELECTORS: salt: 0x%llx", builtins->salt);
- _objc_inform("BUILTIN SELECTORS: base: 0x%llx", builtins->base);
+
+ const int32_t *offsets = builtins->offsets();
+ uint32_t i;
+ for (i = 0; i < capacity; i++) {
+ if (offsets[i] != offsetof(objc_selopt_t, zero)) {
+ const char *str = (const char *)builtins + offsets[i];
+ _objc_inform("BUILTIN SELECTORS: %6d: %+8d %s",
+ i, offsets[i], str);
+ } else {
+ _objc_inform("BUILTIN SELECTORS: %6d: ", i);
+ }
+ }
}
#endif
#endif
if (!key) return (SEL)0;
-#ifndef NO_GC
+#if SUPPORT_IGNORED_SELECTOR_CONSTANT
if ((uintptr_t)key == kIgnore) return (SEL)kIgnore;
- if (ignore_selector(key)) return (SEL)kIgnore;
+ if (ignoreSelectorNamed(key)) return (SEL)kIgnore;
#endif
if ('\0' == *key) return (SEL)_objc_empty_selector;
-#ifndef NO_BUILTINS
+#if SUPPORT_BUILTINS
return (SEL)builtins->get(key);
#endif
const char *sel_getName(SEL sel) {
-#ifndef NO_GC
+#if SUPPORT_IGNORED_SELECTOR_CONSTANT
if ((uintptr_t)sel == kIgnore) return "<ignored selector>";
#endif
return sel ? (const char *)sel : "<null selector>";
SEL result;
if (!name) return NO;
+#if SUPPORT_IGNORED_SELECTOR_CONSTANT
if ((uintptr_t)name == kIgnore) return YES;
+#endif
result = _objc_search_builtins((const char *)name);
if (result) return YES;
return __sel_registerName(name, 1, 1); // YES lock, YES copy
}
-__private_extern__ SEL sel_registerNameNoLock(const char *name, BOOL copy) {
+PRIVATE_EXTERN SEL sel_registerNameNoLock(const char *name, BOOL copy) {
return __sel_registerName(name, 0, copy); // NO lock, maybe copy
}
-__private_extern__ void sel_lock(void)
+PRIVATE_EXTERN void sel_lock(void)
{
rwlock_write(&selLock);
}
-__private_extern__ void sel_unlock(void)
+PRIVATE_EXTERN void sel_unlock(void)
{
rwlock_unlock_write(&selLock);
}
* Return YES if this image's selector fixups are valid courtesy
* of the dyld shared cache.
**********************************************************************/
-__private_extern__ BOOL sel_preoptimizationValid(const header_info *hi)
+PRIVATE_EXTERN BOOL sel_preoptimizationValid(const header_info *hi)
{
-#ifdef NO_BUILTINS
+#if !SUPPORT_BUILTINS
return NO;
#else
-# ifndef NO_GC
- // shared cache can't fix ignored selectors
+# if SUPPORT_IGNORED_SELECTOR_CONSTANT
+ // shared cache can't fix constant ignored selectors
if (UseGC) return NO;
# endif
+ // preoptimization disabled for some reason
+ if (!preoptimized) return NO;
+
// image not from shared cache, or not fixed inside shared cache
if (!_objcHeaderOptimizedByDyld(hi)) return NO;
- // libobjc not from shared cache, or from shared cache but slid
- if (builtins->base != (uintptr_t)builtins) return NO;
-
return YES;
#endif
* sel_init
* Initialize selector tables and register selectors used internally.
**********************************************************************/
-__private_extern__ void sel_init(BOOL wantsGC)
+PRIVATE_EXTERN void sel_init(BOOL wantsGC)
{
-#ifdef NO_BUILTINS
+#if !SUPPORT_BUILTINS
- disableSelectorPreoptimization();
+ disableSharedCacheOptimizations();
#else
// not set at compile time in order to detect too-early selector operations
- builtins = &_objc_selopt_data;
+ const char *failure = NULL;
+ opt = &_objc_opt_data;
+
+ if (DisablePreopt) {
+ // OBJC_DISABLE_PREOPTIMIZATION is set
+ // If opt->version != VERSION then you continue at your own risk.
+ failure = "(by OBJC_DISABLE_PREOPTIMIZATION)";
+ }
+ else if (opt->version != objc_opt::VERSION) {
+ // This shouldn't happen. You probably forgot to
+ // change OPT_INITIALIZER and objc-sel-table.s.
+ // If dyld really did write the wrong optimization version,
+ // then we must halt because we don't know what bits dyld twiddled.
+ _objc_fatal("bad objc opt version (want %d, got %d)",
+ objc_opt::VERSION, opt->version);
+ }
+ else if (!opt->selopt()) {
+ // No selector table. dyld must not have written one.
+ failure = "(dyld shared cache is absent or out of date)";
+ }
+#if SUPPORT_IGNORED_SELECTOR_CONSTANT
+ else if (UseGC) {
+ // GC is on, which renames some selectors
+ // Non-selector optimizations are still valid, but we don't have
+ // any of those yet
+ failure = "(GC is on)";
+ }
+#endif
+
+ if (failure) {
+ // All preoptimized selector references are invalid.
+ preoptimized = NO;
+ opt = (objc_opt_t *)empty_opt_data;
+ builtins = opt->selopt();
+ disableSharedCacheOptimizations();
- // Check selector table (possibly built by dyld shared cache)
- if (builtins->base == (uintptr_t)builtins && !UseGC && !DisablePreopt) {
- // Valid selector table written by dyld shared cache
if (PrintPreopt) {
- _objc_inform("PREOPTIMIZATION: selector preoptimization ENABLED "
- "(version %d)", builtins->version);
+ _objc_inform("PREOPTIMIZATION: is DISABLED %s", failure);
}
}
else {
- // Selector table written by dyld shared cache, but slid
- // OR selector table not written by dyld shared cache
- // OR gc is on which renames ignored selectors
- // OR disabled by environment variable
- // All preoptimized selector references are invalid.
-
- // But keep the builtins table itself unless disabled by environment
- if (DisablePreopt) builtins = (objc_selopt_t *)empty_selopt_data;
-
- disableSelectorPreoptimization();
+ // Valid optimization data written by dyld shared cache
+ preoptimized = YES;
+ builtins = opt->selopt();
if (PrintPreopt) {
- const char *why;
- if (DisablePreopt) why = "(by OBJC_DISABLE_PREOPTIMIZATION)";
- else if (UseGC) why = "(GC is on)";
- else why = "(dyld shared cache is absent or out of date)";
- _objc_inform("PREOPTIMIZATION: selector preoptimization DISABLED %s", why);
+ _objc_inform("PREOPTIMIZATION: is ENABLED "
+ "(version %d)", opt->version);
}
}
- // Die if the table looks bad.
- // We should always end up with a good dyld table,
- // or the compiled-in table, or the compiled-in empty table.
- // Failure probably means you forgot to update the compiled-in table data.
- // Don't do this before checking DisablePreopt.
- if (builtins->version != VERSION) {
- _objc_fatal("bad objc selector table (want %d, got %d)",
- VERSION, builtins->version);
- }
-
#endif
// Register selectors used by libobjc
s(retain);
s(release);
s(autorelease);
+ s(retainCount);
+ s(alloc);
s(copy);
+ s(new);
s(finalize);
+ t(forwardInvocation:, forwardInvocation);
sel_unlock();
dyld USES THIS FILE AND CANNOT SEE THEM
*/
-// #define SELOPT_DEBUG
+#ifndef STATIC_ASSERT
+# define STATIC_ASSERT(x) _STATIC_ASSERT2(x, __LINE__)
+# define _STATIC_ASSERT2(x, line) _STATIC_ASSERT3(x, line)
+# define _STATIC_ASSERT3(x, line) \
+ typedef struct { \
+ int _static_assert[(x) ? 0 : -1]; \
+ } _static_assert_ ## line __attribute__((unavailable))
+#endif
+
+#define SELOPT_DEBUG 0
-namespace objc_selopt {
+namespace objc_opt {
typedef int32_t objc_selopt_offset_t;
+ typedef uint8_t objc_selopt_check_t;
#ifdef SELOPT_WRITE
static uint64_t lookup8( uint8_t *k, size_t length, uint64_t level);
-enum { VERSION = 3 };
-
+// Edit objc-sel-table.s and OPT_INITIALIZER if you change this structure.
struct objc_selopt_t {
-
- uint32_t version; /* this is version 3: external cstrings */
uint32_t capacity;
uint32_t occupied;
uint32_t shift;
uint32_t mask;
uint32_t zero;
+ uint32_t unused; // alignment pad
uint64_t salt;
- uint64_t base;
uint32_t scramble[256];
- uint8_t tab[0]; /* tab[mask+1] (always power-of-2) */
- // int32_t offsets[capacity]; /* offsets from &version to cstrings */
+ uint8_t tab[0]; /* tab[mask+1] (always power-of-2) */
+ // int32_t offsets[capacity]; /* offsets from &capacity to cstrings */
+ // uint8_t checkbytes[capacity]; /* check byte for each selector string */
objc_selopt_offset_t *offsets() { return (objc_selopt_offset_t *)&tab[mask+1]; }
const objc_selopt_offset_t *offsets() const { return (const objc_selopt_offset_t *)&tab[mask+1]; }
+ objc_selopt_check_t *checkbytes() { return (objc_selopt_check_t *)&offsets()[capacity]; }
+ const objc_selopt_check_t *checkbytes() const { return (const objc_selopt_check_t *)&offsets()[capacity]; }
+
uint32_t hash(const char *key) const
{
uint64_t val = lookup8((uint8_t*)key, strlen(key), salt);
return index;
}
+ // The check bytes areused to reject strings that aren't in the table
+ // without paging in the table's cstring data. This checkbyte calculation
+ // catches 4785/4815 rejects when launching Safari; a perfect checkbyte
+ // would catch 4796/4815.
+ objc_selopt_check_t checkbyte(const char *key) const
+ {
+ return
+ ((key[0] & 0x7) << 5)
+ |
+ (strlen(key) & 0x1f);
+ }
+
const char *get(const char *key) const
{
- const char *result = (const char *)this + offsets()[hash(key)];
- if (0 == strcmp(key, result)) return result;
- else return NULL;
+ uint32_t h = hash(key);
+
+ // Use check byte to reject without paging in the table's cstrings
+ objc_selopt_check_t h_check = checkbytes()[h];
+ objc_selopt_check_t key_check = checkbyte(key);
+ bool check_fail = (h_check != key_check);
+#if ! SELOPT_DEBUG
+ if (check_fail) return NULL;
+#endif
+
+ const char *result = (const char *)this + offsets()[h];
+ if (0 != strcmp(key, result)) return NULL;
+
+#if SELOPT_DEBUG
+ if (check_fail) abort();
+#endif
+
+ return result;
}
#ifdef SELOPT_WRITE
void set(const char *key, objc_selopt_offset_t value)
{
- offsets()[hash(key)] = value;
+ uint32_t h = hash(key);
+ offsets()[h] = value;
+ checkbytes()[h] = checkbyte(key);
}
#endif
};
-// Initializer for empty table of type uint32_t[].
+
+// Edit objc-sel-table.s if you change this value.
+enum { VERSION = 10 };
+
+// Edit objc-sel-table.s and OPT_INITIALIZER if you change this structure.
+struct objc_opt_t {
+ uint32_t version;
+ uint32_t selopt_offset;
+
+ const objc_selopt_t* selopt() const {
+ if (selopt_offset == 0) return NULL;
+ return (objc_selopt_t *)((uint8_t *)this + selopt_offset);
+ }
+ objc_selopt_t* selopt() {
+ if (selopt_offset == 0) return NULL;
+ return (objc_selopt_t *)((uint8_t *)this + selopt_offset);
+ }
+};
+
+// sizeof(objc_opt_t) must be pointer-aligned
+STATIC_ASSERT(sizeof(objc_opt_t) % sizeof(void*) == 0);
+
+// Initializer for empty opt of type uint32_t[].
#define X8(x) x, x, x, x, x, x, x, x
#define X64(x) X8(x), X8(x), X8(x), X8(x), X8(x), X8(x), X8(x), X8(x)
#define X256(x) X64(x), X64(x), X64(x), X64(x)
-#define SELOPT_INITIALIZER \
- { objc_selopt::VERSION, 4, 4, 63, 3, 0, 0,0, 0,0, X256(0), 0, 20, 20, 20, 20 };
+#define OPT_INITIALIZER { \
+ /* objc_opt_t */ \
+ objc_opt::VERSION, 8, \
+ /* objc_selopt_t */ \
+ 4, 4, 63, 3, 0, 0, 0,0, X256(0), 0, 16, 16, 16, 16, 0 \
+}
/*
return "perfect hash failed (selectors not optimized)";
}
- size_t size = sizeof(objc_selopt_t) + (phash.mask+1) + phash.capacity * sizeof(objc_selopt_offset_t);
+ size_t size = sizeof(objc_selopt_t)
+ + phash.mask+1
+ + phash.capacity * sizeof(objc_selopt_offset_t)
+ + phash.capacity * sizeof(objc_selopt_check_t);
if (size > dstSize) {
return "selector section too small (selectors not optimized)";
}
objc_selopt_t *selopt = (objc_selopt_t *)buf;
// Set header
- selopt->version = VERSION;
selopt->capacity = phash.capacity;
selopt->occupied = phash.occupied;
selopt->shift = phash.shift;
selopt->mask = phash.mask;
selopt->zero = 0;
+ selopt->unused = 0;
selopt->salt = phash.salt;
- selopt->base = base;
// Set hash data
for (uint32_t i = 0; i < 256; i++) {
selopt->offsets()[i] =
(objc_selopt_offset_t)offsetof(objc_selopt_t, zero);
}
+ // Set checkbytes to 0
+ for (uint32_t i = 0; i < phash.capacity; i++) {
+ selopt->checkbytes()[i] = 0;
+ }
- // Set real string offsets
+ // Set real string offsets and checkbytes
# define SHIFT (64 - 8*sizeof(objc_selopt_offset_t))
string_map::const_iterator s;
for (s = strings.begin(); s != strings.end(); ++s) {
S32(selopt->offsets()[i]);
}
- S32(selopt->version);
S32(selopt->capacity);
S32(selopt->occupied);
S32(selopt->shift);
S32(selopt->mask);
S32(selopt->zero);
S64(selopt->salt);
- S64(selopt->base);
#undef S32
#undef S64
ub4 maxkeys; /* maximum number of keys for any b */
ub4 i, j;
-#ifdef SELOPT_DEBUG
+#if SELOPT_DEBUG
fprintf(stderr, " blen %d smax %d nkeys %d\n", blen, smax, nkeys);
#endif
// Begin synchronizing on 'obj'.
// Allocates recursive pthread_mutex associated with 'obj' if needed.
// Returns OBJC_SYNC_SUCCESS once lock is acquired.
-OBJC_EXPORT int objc_sync_enter(id obj);
+OBJC_EXPORT int objc_sync_enter(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_2_0);
// End synchronizing on 'obj'.
// Returns OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERROR
-OBJC_EXPORT int objc_sync_exit(id obj);
-
-OBJC_EXPORT int objc_sync_wait(id obj, long long milliSecondsMaxWait) DEPRECATED_ATTRIBUTE;
-OBJC_EXPORT int objc_sync_notify(id obj) DEPRECATED_ATTRIBUTE;
-OBJC_EXPORT int objc_sync_notifyAll(id obj) DEPRECATED_ATTRIBUTE;
+OBJC_EXPORT int objc_sync_exit(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_2_0);
+
+// The wait/notify functions have never worked correctly and no longer exist.
+OBJC_EXPORT int objc_sync_wait(id obj, long long milliSecondsMaxWait)
+ UNAVAILABLE_ATTRIBUTE;
+OBJC_EXPORT int objc_sync_notify(id obj)
+ UNAVAILABLE_ATTRIBUTE;
+OBJC_EXPORT int objc_sync_notifyAll(id obj)
+ UNAVAILABLE_ATTRIBUTE;
enum {
OBJC_SYNC_SUCCESS = 0,
_objc_pthread_data *data;
data = _objc_fetch_pthread_data(create);
- if (!data && !create) return NULL;
+ if (!data) return NULL;
if (!data->syncCache) {
if (!create) {
}
-__private_extern__ void _destroySyncCache(struct SyncCache *cache)
+PRIVATE_EXTERN void _destroySyncCache(struct SyncCache *cache)
{
if (cache) free(cache);
}
SyncData **listp = &LIST_FOR_OBJ(object);
SyncData* result = NULL;
-#ifndef NO_DIRECT_THREAD_KEYS
+#if SUPPORT_DIRECT_THREAD_KEYS
// Check per-thread single-entry fast cache for matching object
BOOL fastCacheOccupied = NO;
SyncData *data = tls_get_direct(SYNC_DATA_DIRECT_KEY);
require_action_string(result->object == object, really_done,
result = NULL, "id2data is buggy");
-#ifndef NO_DIRECT_THREAD_KEYS
+#if SUPPORT_DIRECT_THREAD_KEYS
if (!fastCacheOccupied) {
// Save in fast thread cache
tls_set_direct(SYNC_DATA_DIRECT_KEY, result);
}
-__private_extern__ __attribute__((noinline))
-int objc_sync_nil(void)
-{
- return OBJC_SYNC_SUCCESS; // something to foil the optimizer
-}
+BREAKPOINT_FUNCTION(
+ void objc_sync_nil(void)
+);
// Begin synchronizing on 'obj'.
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
- result = objc_sync_nil();
+ objc_sync_nil();
}
done:
return result;
}
-
-#if TARGET_OS_MAC && !TARGET_OS_EMBEDDED
-
-int objc_sync_wait(id obj, long long max)
-{
- _objc_fatal("Do not call objc_sync_wait.");
-}
-
-int objc_sync_notify(id obj)
-{
- _objc_fatal("Do not call objc_sync_notify.");
-}
-
-int objc_sync_notifyAll(id obj)
-{
- _objc_fatal("Do not call objc_sync_notifyAll.");
-}
-
-#endif
case '^': /* pointers */
break;
+ case '@': /* objects */
+ if (type[0] == '?') type++; /* Blocks */
+ return type;
+
/* arrays */
case '[':
while ((*type >= '0') && (*type <= '9'))
/***********************************************************************
* encoding_getNumberOfArguments.
**********************************************************************/
-__private_extern__ unsigned int
+PRIVATE_EXTERN unsigned int
encoding_getNumberOfArguments(const char *typedesc)
{
unsigned nargs;
/***********************************************************************
* encoding_getSizeOfArguments.
**********************************************************************/
-__private_extern__ unsigned
+PRIVATE_EXTERN unsigned
encoding_getSizeOfArguments(const char *typedesc)
{
unsigned stack_size;
-#if defined(__ppc__) || defined(ppc)
- unsigned trueBaseOffset;
- unsigned foundBaseOffset;
-#endif
// Get our starting points
stack_size = 0;
// Skip the return type
-#if defined (__ppc__) || defined(ppc)
- // Struct returns cause the parameters to be bumped
- // by a register, so the offset to the receiver is
- // 4 instead of the normal 0.
- trueBaseOffset = (*typedesc == '{') ? (unsigned)sizeof(void *) : 0;
-#endif
typedesc = SkipFirstType (typedesc);
// Convert ASCII number string to integer
while ((*typedesc >= '0') && (*typedesc <= '9'))
stack_size = (stack_size * 10) + (*typedesc++ - '0');
-#if defined (__ppc__) || defined(ppc)
- // NOTE: This is a temporary measure pending a compiler fix.
- // Work around PowerPC compiler bug wherein the method argument
- // string contains an incorrect value for the "stack size."
- // Generally, the size is reported 4 bytes too small, so we apply
- // that fudge factor. Unfortunately, there is at least one case
- // where the error is something other than -4: when the last
- // parameter is a double, the reported stack is much too high
- // (about 32 bytes). We do not attempt to detect that case.
- // The result of returning a too-high value is that objc_msgSendv
- // can bus error if the destination of the marg_list copying
- // butts up against excluded memory.
- // This fix disables itself when it sees a correctly built
- // type string (i.e. the offset for the Id is correct). This
- // keeps us out of lockstep with the compiler.
-
- // skip the '@' marking the Id field
- typedesc = SkipFirstType (typedesc);
-
- // Skip GNU runtime's register parameter hint
- if (*typedesc == '+') typedesc++;
-
- // pick up the offset for the Id field
- foundBaseOffset = 0;
- while ((*typedesc >= '0') && (*typedesc <= '9'))
- foundBaseOffset = (foundBaseOffset * 10) + (*typedesc++ - '0');
-
- // add fudge factor iff the Id field offset was wrong
- if (foundBaseOffset != trueBaseOffset)
- stack_size += 4;
-#endif
return stack_size;
}
/***********************************************************************
* encoding_getArgumentInfo.
**********************************************************************/
-__private_extern__ unsigned int
+PRIVATE_EXTERN unsigned int
encoding_getArgumentInfo(const char *typedesc, int arg,
const char **type, int *offset)
{
}
-__private_extern__ void
+PRIVATE_EXTERN void
encoding_getReturnType(const char *t, char *dst, size_t dst_len)
{
size_t len;
* encoding_copyReturnType. Returns the method's return type string
* on the heap.
**********************************************************************/
-__private_extern__ char *
+PRIVATE_EXTERN char *
encoding_copyReturnType(const char *t)
{
size_t len;
}
-__private_extern__ void
+PRIVATE_EXTERN void
encoding_getArgumentType(const char *t, unsigned int index,
char *dst, size_t dst_len)
{
* encoding_copyArgumentType. Returns a single argument's type string
* on the heap. Argument 0 is `self`; argument 1 is `_cmd`.
**********************************************************************/
-__private_extern__ char *
+PRIVATE_EXTERN char *
encoding_copyArgumentType(const char *t, unsigned int index)
{
size_t len;
--- /dev/null
+/*
+ * Copyright (c) 2010-2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <objc/objc.h>
+#import "objc-config.h"
+
+__BEGIN_DECLS
+
+/*
+The weak table is a hash table governed by a single spin lock.
+An allocated blob of memory, most often an object, but under GC any such allocation,
+may have its address stored in a __weak marked storage location through use of
+compiler generated write-barriers or hand coded uses of the register weak primitive.
+Associated with the registration can be a callback block for the case when one of
+ the allocated chunks of memory is reclaimed.
+The table is hashed on the address of the allocated memory. When __weak marked memory
+ changes its reference, we count on the fact that we can still see its previous reference.
+
+So, in the hash table, indexed by the weakly referenced item, is a list of all locations
+ where this address is currently being stored.
+
+For ARR, we also keep track of whether an arbitrary object is being deallocated by
+ briefly placing it in the table just prior to invoking dealloc, and removing it
+ via objc_clear_deallocating just prior to memory reclamation.
+
+*/
+
+struct weak_referrer_t {
+ id *referrer; // clear this address
+};
+typedef struct weak_referrer_t weak_referrer_t;
+
+struct weak_referrer_array_t {
+ weak_referrer_t *refs;
+ size_t num_refs;
+ size_t num_allocated;
+ size_t max_hash_displacement;
+};
+typedef struct weak_referrer_array_t weak_referrer_array_t;
+
+struct weak_entry_t {
+ id referent;
+ weak_referrer_array_t referrers;
+};
+typedef struct weak_entry_t weak_entry_t;
+
+struct weak_table_t {
+ size_t num_weak_refs;
+ size_t max_weak_refs;
+ struct weak_entry_t *weak_entries;
+};
+typedef struct weak_table_t weak_table_t;
+
+extern id weak_register_no_lock(weak_table_t *weak_table, id referent, id *referrer);
+extern void weak_unregister_no_lock(weak_table_t *weak_table, id referent, id *referrer);
+
+extern id arr_read_weak_reference(weak_table_t *weak_table, id *referrer);
+extern void arr_clear_deallocating(weak_table_t *weak_table, id referent);
+
+__END_DECLS
--- /dev/null
+/*
+ * Copyright (c) 2010-2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#import "objc-weak.h"
+#import "objc-os.h"
+#import "objc-private.h"
+
+#import <stdint.h>
+#import <stdbool.h>
+#import <sys/types.h>
+#import <libkern/OSAtomic.h>
+
+
+template <typename T> struct WeakAllocator {
+ typedef T value_type;
+ typedef value_type* pointer;
+ typedef const value_type *const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+
+ template <typename U> struct rebind { typedef WeakAllocator<U> other; };
+
+ template <typename U> WeakAllocator(const WeakAllocator<U>&) {}
+ WeakAllocator() {}
+ WeakAllocator(const WeakAllocator&) {}
+ ~WeakAllocator() {}
+
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const {
+ return x;
+ }
+
+ pointer allocate(size_type n, const_pointer = 0) {
+ return static_cast<pointer>(::_malloc_internal(n * sizeof(T)));
+ }
+
+ void deallocate(pointer p, size_type) { ::_free_internal(p); }
+
+ size_type max_size() const {
+ return static_cast<size_type>(-1) / sizeof(T);
+ }
+
+ void construct(pointer p, const value_type& x) {
+ new(p) value_type(x);
+ }
+
+ void destroy(pointer p) { p->~value_type(); }
+
+ void operator=(const WeakAllocator&);
+
+};
+
+class Range {
+private:
+ void *_address; // start of range
+ void *_end; // end of the range (one byte beyond last usable space)
+public:
+ static void *displace(void *address, ptrdiff_t offset) { return (void *)((char *)address + offset); }
+
+ //
+ // Constructors
+ //
+ Range() : _address(NULL), _end(NULL) {}
+ Range(void *address) : _address(address), _end(address) {}
+ Range(void *address, void *end) : _address(address), _end(end) {}
+ Range(void *address, size_t size) : _address(address), _end(displace(address, size)) {}
+
+ //
+ // Accessors
+ //
+ inline Range& range() { return *this; }
+ inline void *address() const { return _address; }
+ inline void *end() const { return _end; }
+ inline const size_t size() const { return (uintptr_t)_end - (uintptr_t)_address; }
+ inline void set_address(void *address) { _address = address; }
+ inline void set_end(void *end) { _end = end; }
+ inline void set_size(size_t size) { _end = displace(_address, size); }
+ inline void set_range(void *address, void *end) { _address = address; _end = end; }
+ inline void set_range(void *address, size_t size) { _address = address; _end = displace(address, size); }
+ inline void set_range(Range range) { _address = range.address(); _end = range.end(); }
+ inline void adjust_address(intptr_t delta) { _address = displace(_address, delta); }
+ inline void adjust_end(intptr_t delta) { _end = displace(_end, delta); }
+ inline void adjust(intptr_t delta) { _address = displace(_address, delta), _end = displace(_end, delta); }
+
+
+ //
+ // is_empty
+ //
+ // Returns true if the range is empty.
+ //
+ inline bool is_empty() { return _address == _end; }
+
+
+ //
+ // in_range
+ //
+ // Returns true if the specified address is in range.
+ // This form reduces the number of branches. Works well with invariant lo and hi.
+ //
+ static inline const bool in_range(void *lo, void *hi, void *address) {
+ uintptr_t lo_as_int = (uintptr_t)lo;
+ uintptr_t hi_as_int = (uintptr_t)hi;
+ uintptr_t diff = hi_as_int - lo_as_int;
+ uintptr_t address_as_int = (uintptr_t)address;
+ return (address_as_int - lo_as_int) < diff;
+ }
+ inline const bool in_range(void *address) const { return in_range(_address, _end, address); }
+
+
+ //
+ // operator ==
+ //
+ // Used to locate entry in list or hash table (use is_range for exaxt match.)
+ inline const bool operator==(const Range *range) const { return _address == range->_address; }
+ inline const bool operator==(const Range &range) const { return _address == range._address; }
+
+
+ //
+ // is_range
+ //
+ // Return true if the ranges are equivalent.
+ //
+ inline const bool is_range(const Range& range) const { return _address == range._address && _end == range._end; }
+
+
+ //
+ // clear
+ //
+ // Initialize the range to zero.
+ //
+ inline void clear() { bzero(address(), size()); }
+
+ //
+ // expand_range
+ //
+ // Expand the bounds with the specified range.
+ //
+ inline void expand_range(void *address) {
+ if (_address > address) _address = address;
+ if (_end < address) _end = address;
+ }
+ inline void expand_range(Range& range) {
+ expand_range(range.address());
+ expand_range(range.end());
+ }
+
+
+ //
+ // relative_address
+ //
+ // Converts an absolute address to an address relative to this address.
+ //
+ inline void *relative_address(void *address) const { return (void *)((uintptr_t)address - (uintptr_t)_address); }
+
+
+ //
+ // absolute_address
+ //
+ // Converts an address relative to this address to an absolute address.
+ //
+ inline void *absolute_address(void *address) const { return (void *)((uintptr_t)address + (uintptr_t)_address); }
+};
+
+
+template<> struct WeakAllocator<void> {
+ typedef void value_type;
+ typedef void* pointer;
+ typedef const void *const_pointer;
+ template <typename U> struct rebind { typedef WeakAllocator<U> other; };
+};
+
+typedef std::pair<id, id *> WeakPair;
+typedef std::vector<WeakPair, WeakAllocator<WeakPair> > WeakPairVector;
+typedef std::vector<weak_referrer_t, WeakAllocator<WeakPair> > WeakReferrerVector;
+
+static void append_referrer_no_lock(weak_referrer_array_t *list, id *new_referrer);
+
+static inline uintptr_t hash_pointer(void *key) {
+ uintptr_t k = (uintptr_t)key;
+
+ // Code from CFSet.c
+#if __LP64__
+ uintptr_t a = 0x4368726973746F70ULL;
+ uintptr_t b = 0x686572204B616E65ULL;
+#else
+ uintptr_t a = 0x4B616E65UL;
+ uintptr_t b = 0x4B616E65UL;
+#endif
+ uintptr_t c = 1;
+ a += k;
+#if __LP64__
+ a -= b; a -= c; a ^= (c >> 43);
+ b -= c; b -= a; b ^= (a << 9);
+ c -= a; c -= b; c ^= (b >> 8);
+ a -= b; a -= c; a ^= (c >> 38);
+ b -= c; b -= a; b ^= (a << 23);
+ c -= a; c -= b; c ^= (b >> 5);
+ a -= b; a -= c; a ^= (c >> 35);
+ b -= c; b -= a; b ^= (a << 49);
+ c -= a; c -= b; c ^= (b >> 11);
+ a -= b; a -= c; a ^= (c >> 12);
+ b -= c; b -= a; b ^= (a << 18);
+ c -= a; c -= b; c ^= (b >> 22);
+#else
+ a -= b; a -= c; a ^= (c >> 13);
+ b -= c; b -= a; b ^= (a << 8);
+ c -= a; c -= b; c ^= (b >> 13);
+ a -= b; a -= c; a ^= (c >> 12);
+ b -= c; b -= a; b ^= (a << 16);
+ c -= a; c -= b; c ^= (b >> 5);
+ a -= b; a -= c; a ^= (c >> 3);
+ b -= c; b -= a; b ^= (a << 10);
+ c -= a; c -= b; c ^= (b >> 15);
+#endif
+ return c;
+}
+
+// Up until this size the weak referrer array grows one slot at a time. Above this size it grows by doubling.
+#define WEAK_TABLE_DOUBLE_SIZE 8
+
+// Grow the refs list. Rehashes the entries.
+static void grow_refs(weak_referrer_array_t *list)
+{
+ size_t old_num_allocated = list->num_allocated;
+ size_t num_refs = list->num_refs;
+ weak_referrer_t *old_refs = list->refs;
+ size_t new_allocated = old_num_allocated < WEAK_TABLE_DOUBLE_SIZE ? old_num_allocated + 1 : old_num_allocated + old_num_allocated;
+ list->refs = (weak_referrer_t *)_malloc_internal(new_allocated * sizeof(weak_referrer_t));
+ list->num_allocated = _malloc_size_internal(list->refs)/sizeof(weak_referrer_t);
+ bzero(list->refs, list->num_allocated * sizeof(weak_referrer_t));
+ // for larger tables drop one entry from the end to give an odd number of hash buckets for better hashing
+ if ((list->num_allocated > WEAK_TABLE_DOUBLE_SIZE) && !(list->num_allocated & 1)) list->num_allocated--;
+ list->num_refs = 0;
+ list->max_hash_displacement = 0;
+
+ size_t i;
+ for (i=0; i < old_num_allocated && num_refs > 0; i++) {
+ if (old_refs[i].referrer != NULL) {
+ append_referrer_no_lock(list, old_refs[i].referrer);
+ num_refs--;
+ }
+ }
+ if (old_refs)
+ _free_internal(old_refs);
+}
+
+// Add the given referrer to list
+// Does not perform duplicate checking.
+static void append_referrer_no_lock(weak_referrer_array_t *list, id *new_referrer)
+{
+ if ((list->num_refs == list->num_allocated) || ((list->num_refs >= WEAK_TABLE_DOUBLE_SIZE) && (list->num_refs >= list->num_allocated * 2 / 3))) {
+ grow_refs(list);
+ }
+ size_t index = hash_pointer(new_referrer) % list->num_allocated, hash_displacement = 0;
+ while (list->refs[index].referrer != NULL) {
+ index++;
+ hash_displacement++;
+ if (index == list->num_allocated)
+ index = 0;
+ }
+ if (list->max_hash_displacement < hash_displacement) {
+ list->max_hash_displacement = hash_displacement;
+ //malloc_printf("max_hash_displacement: %d allocated: %d\n", list->max_hash_displacement, list->num_allocated);
+ }
+ weak_referrer_t &ref = list->refs[index];
+ ref.referrer = new_referrer;
+ list->num_refs++;
+}
+
+
+// Remove old_referrer from list, if it's present.
+// Does not remove duplicates.
+// fixme this is slow if old_referrer is not present.
+static void remove_referrer_no_lock(weak_referrer_array_t *list, id *old_referrer)
+{
+ size_t index = hash_pointer(old_referrer) % list->num_allocated;
+ size_t start_index = index, hash_displacement = 0;
+ while (list->refs[index].referrer != old_referrer) {
+ index++;
+ hash_displacement++;
+ if (index == list->num_allocated)
+ index = 0;
+ if (index == start_index || hash_displacement > list->max_hash_displacement) {
+ malloc_printf("attempted to remove unregistered weak referrer %p\n", old_referrer);
+ return;
+ }
+ }
+ list->refs[index].referrer = NULL;
+ list->num_refs--;
+}
+
+
+// Add new_entry to the zone's table of weak references.
+// Does not check whether the referent is already in the table.
+// Does not update num_weak_refs.
+static void weak_entry_insert_no_lock(weak_table_t *weak_table, weak_entry_t *new_entry)
+{
+ weak_entry_t *weak_entries = weak_table->weak_entries;
+ assert(weak_entries != NULL);
+
+ size_t table_size = weak_table->max_weak_refs;
+ size_t hash_index = hash_pointer(new_entry->referent) % table_size;
+ size_t index = hash_index;
+
+ do {
+ weak_entry_t *entry = weak_entries + index;
+ if (entry->referent == NULL) {
+ *entry = *new_entry;
+ return;
+ }
+ index++; if (index == table_size) index = 0;
+ } while (index != hash_index);
+ malloc_printf("no room for new entry in auto weak ref table!\n");
+}
+
+
+// Remove entry from the zone's table of weak references, and rehash
+// Does not update num_weak_refs.
+static void weak_entry_remove_no_lock(weak_table_t *weak_table, weak_entry_t *entry)
+{
+ // remove entry
+ entry->referent = NULL;
+ if (entry->referrers.refs) _free_internal(entry->referrers.refs);
+ entry->referrers.refs = NULL;
+ entry->referrers.num_refs = 0;
+ entry->referrers.num_allocated = 0;
+
+ // rehash after entry
+ weak_entry_t *weak_entries = weak_table->weak_entries;
+ size_t table_size = weak_table->max_weak_refs;
+ size_t hash_index = entry - weak_entries;
+ size_t index = hash_index;
+
+ if (!weak_entries) return;
+
+ do {
+ index++; if (index == table_size) index = 0;
+ if (!weak_entries[index].referent) return;
+ weak_entry_t entry = weak_entries[index];
+ weak_entries[index].referent = NULL;
+ weak_entry_insert_no_lock(weak_table, &entry);
+ } while (index != hash_index);
+}
+
+
+// Grow the given zone's table of weak references if it is full.
+static void weak_grow_maybe_no_lock(weak_table_t *weak_table)
+{
+ if (weak_table->num_weak_refs >= weak_table->max_weak_refs * 3 / 4) {
+ // grow table
+ size_t old_max = weak_table->max_weak_refs;
+ size_t new_max = old_max ? old_max * 2 + 1 : 15;
+ weak_entry_t *old_entries = weak_table->weak_entries;
+ weak_entry_t *new_entries = (weak_entry_t *)_calloc_internal(new_max, sizeof(weak_entry_t));
+ weak_table->max_weak_refs = new_max;
+ weak_table->weak_entries = new_entries;
+
+ if (old_entries) {
+ weak_entry_t *entry;
+ weak_entry_t *end = old_entries + old_max;
+ for (entry = old_entries; entry < end; entry++) {
+ weak_entry_insert_no_lock(weak_table, entry);
+ }
+ _free_internal(old_entries);
+ }
+ }
+}
+
+// Return the weak reference table entry for the given referent.
+// If there is no entry for referent, return NULL.
+static weak_entry_t *weak_entry_for_referent(weak_table_t *weak_table, id referent)
+{
+ weak_entry_t *weak_entries = weak_table->weak_entries;
+
+ if (!weak_entries) return NULL;
+
+ size_t table_size = weak_table->max_weak_refs;
+ size_t hash_index = hash_pointer(referent) % table_size;
+ size_t index = hash_index;
+
+ do {
+ weak_entry_t *entry = weak_entries + index;
+ if (entry->referent == referent) return entry;
+ if (entry->referent == NULL) return NULL;
+ index++; if (index == table_size) index = 0;
+ } while (index != hash_index);
+
+ return NULL;
+}
+
+// Unregister an already-registered weak reference.
+// This is used when referrer's storage is about to go away, but referent
+// isn't dead yet. (Otherwise, zeroing referrer later would be a
+// bad memory access.)
+// Does nothing if referent/referrer is not a currently active weak reference.
+// Does not zero referrer.
+// fixme currently requires old referent value to be passed in (lame)
+// fixme unregistration should be automatic if referrer is collected
+PRIVATE_EXTERN void weak_unregister_no_lock(weak_table_t *weak_table, id referent, id *referrer)
+{
+ weak_entry_t *entry;
+
+ if ((entry = weak_entry_for_referent(weak_table, referent))) {
+ remove_referrer_no_lock(&entry->referrers, referrer);
+ if (entry->referrers.num_refs == 0) {
+ weak_entry_remove_no_lock(weak_table, entry);
+ weak_table->num_weak_refs--;
+ }
+ }
+
+ // Do not set *referrer = NULL. objc_storeWeak() requires that the
+ // value not change.
+}
+
+PRIVATE_EXTERN void
+arr_clear_deallocating(weak_table_t *weak_table, id referent) {
+ {
+ weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
+ if (entry == NULL) {
+ /// XXX shouldn't happen, but does with mismatched CF/objc
+ //printf("XXX no entry for clear deallocating %p\n", referent);
+ return;
+ }
+ // zero out references
+ for (int i = 0; i < entry->referrers.num_allocated; ++i) {
+ id *referrer = entry->referrers.refs[i].referrer;
+ if (referrer) {
+ if (*referrer == referent) {
+ *referrer = nil;
+ }
+ else if (*referrer) {
+ _objc_inform("__weak variable @ %p holds %p instead of %p\n", referrer, *referrer, referent);
+ }
+ }
+ }
+
+ weak_entry_remove_no_lock(weak_table, entry);
+ weak_table->num_weak_refs--;
+ }
+}
+
+
+PRIVATE_EXTERN id weak_register_no_lock(weak_table_t *weak_table, id referent, id *referrer) {
+ if (referent) {
+ // ensure that the referenced object is viable
+ BOOL (*allowsWeakReference)(id, SEL) = (BOOL(*)(id, SEL))
+ class_getMethodImplementation(object_getClass(referent),
+ @selector(allowsWeakReference));
+ if ((IMP)allowsWeakReference != _objc_msgForward) {
+ if (! (*allowsWeakReference)(referent, @selector(allowsWeakReference))) {
+ _objc_fatal("cannot form weak reference to instance (%p) of class %s", referent, object_getClassName(referent));
+ }
+ }
+ else {
+ return NULL;
+ }
+ // now remember it and where it is being stored
+ weak_entry_t *entry;
+ if ((entry = weak_entry_for_referent(weak_table, referent))) {
+ append_referrer_no_lock(&entry->referrers, referrer);
+ }
+ else {
+ weak_entry_t new_entry;
+ new_entry.referent = referent;
+ new_entry.referrers.refs = NULL;
+ new_entry.referrers.num_refs = 0;
+ new_entry.referrers.num_allocated = 0;
+ append_referrer_no_lock(&new_entry.referrers, referrer);
+ weak_table->num_weak_refs++;
+ weak_grow_maybe_no_lock(weak_table);
+ weak_entry_insert_no_lock(weak_table, &new_entry);
+ }
+ }
+
+ // Do not set *referrer. objc_storeWeak() requires that the
+ // value not change.
+
+ return referent;
+}
+
+
+// Automated Retain Release (ARR) support
+
+PRIVATE_EXTERN id
+arr_read_weak_reference(weak_table_t *weak_table, id *referrer) {
+ id referent;
+ // find entry and mark that it needs retaining
+ {
+ referent = *referrer;
+ weak_entry_t *entry;
+ if (referent == NULL || !(entry = weak_entry_for_referent(weak_table, referent))) {
+ *referrer = NULL;
+ return NULL;
+ }
+ BOOL (*tryRetain)(id, SEL) = (BOOL(*)(id, SEL))
+ class_getMethodImplementation(object_getClass(referent),
+ @selector(retainWeakReference));
+ if ((IMP)tryRetain != _objc_msgForward) {
+ //printf("sending _tryRetain for %p\n", referent);
+ if (! (*tryRetain)(referent, @selector(retainWeakReference))) {
+ //printf("_tryRetain(%p) tried and failed!\n", referent);
+ return NULL;
+ }
+ //else printf("_tryRetain(%p) succeeded\n", referent);
+ }
+ else {
+ *referrer = NULL;
+ return NULL;
+ }
+ }
+ return referent;
+}
+
#define _OBJC_OBJC_H_
#include <sys/types.h> // for __DARWIN_NULL
+#include <Availability.h>
#include <objc/objc-api.h>
#define nil __DARWIN_NULL /* id of Nil instance */
#endif
-#ifndef __OBJC_GC__
+#if ! (defined(__OBJC_GC__) || __has_feature(objc_arr))
#define __strong /* empty */
#endif
-OBJC_EXPORT const char *sel_getName(SEL sel);
-OBJC_EXPORT SEL sel_registerName(const char *str);
-OBJC_EXPORT const char *object_getClassName(id obj);
-OBJC_EXPORT void *object_getIndexedIvars(id obj);
+#if !__has_feature(objc_arr)
+#define __unsafe_unretained /* empty */
+#define __autoreleasing /* empty */
+#endif
+
+OBJC_EXPORT const char *sel_getName(SEL sel)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT SEL sel_registerName(const char *str)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT const char *object_getClassName(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT void *object_getIndexedIvars(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT BOOL sel_isMapped(SEL sel)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT SEL sel_getUid(const char *str)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+
+
+#ifndef CF_CONSUMED
+#if __has_feature(attribute_cf_consumed)
+#define CF_CONSUMED __attribute__((cf_consumed))
+#else
+#define CF_CONSUMED
+#endif
+#endif
+
+#ifndef CF_RETURNS_NOT_RETAINED
+#if __has_feature(attribute_cf_not_retained)
+#define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained))
+#else
+#define CF_RETURNS_NOT_RETAINED
+#endif
+#endif
+
+#ifndef NS_RETURNS_RETAINED
+#if __has_feature(attribute_ns_returns_retained)
+#define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
+#else
+#define NS_RETURNS_RETAINED
+#endif
+#endif
+
+#ifndef NS_RETURNS_NOT_RETAINED
+#if __has_feature(attribute_ns_returns_not_retained)
+#define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained))
+#else
+#define NS_RETURNS_NOT_RETAINED
+#endif
+#endif
+
+typedef const void* objc_objectptr_t;
+
+OBJC_EXPORT NS_RETURNS_RETAINED id objc_retainedObject(objc_objectptr_t CF_CONSUMED pointer)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+OBJC_EXPORT NS_RETURNS_NOT_RETAINED id objc_unretainedObject(objc_objectptr_t pointer)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+OBJC_EXPORT CF_RETURNS_NOT_RETAINED objc_objectptr_t objc_unretainedPointer(id object)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
#if !__OBJC2__
# define ARITH_SHIFT 16
#endif
-OBJC_EXPORT BOOL sel_isMapped(SEL sel);
-OBJC_EXPORT SEL sel_getUid(const char *str);
-
typedef char *STR;
#define ISSELECTOR(sel) sel_isMapped(sel)
#pragma data_seg(".CRT$XIAA")\r
static void *__objc_init_fn = &__objc_init;\r
\r
-// run _objc_load_image (+load methods) after init_seg(compiler) but before init_seg(library) or init_seg(user)\r
-#pragma section(".CRT$XCI",long,read,write)\r
-#pragma data_seg(".CRT$XCI")\r
+// run _objc_load_image (+load methods) after all other initializers; \r
+// otherwise constant NSStrings are not initialized yet\r
+#pragma section(".CRT$XCUO",long,read,write)\r
+#pragma data_seg(".CRT$XCUO")\r
static void *__objc_load_fn = &__objc_load;\r
\r
// _objc_unload_image is called by atexit(), not by an image terminator\r
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
+#include <Availability.h>
#include <AvailabilityMacros.h>
#include <TargetConditionals.h>
char *types;
};
+typedef struct {
+ const char *name;
+ const char *value;
+} objc_property_attribute_t;
+
/* Functions */
-OBJC_EXPORT id object_copy(id obj, size_t size);
-OBJC_EXPORT id object_dispose(id obj);
+OBJC_EXPORT id object_copy(id obj, size_t size)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_ARC_UNAVAILABLE;
+OBJC_EXPORT id object_dispose(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT Class object_getClass(id obj)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT Class object_setClass(id obj, Class cls)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
-OBJC_EXPORT const char *object_getClassName(id obj);
-OBJC_EXPORT void *object_getIndexedIvars(id obj);
+OBJC_EXPORT const char *object_getClassName(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT void *object_getIndexedIvars(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT id object_getIvar(id obj, Ivar ivar)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void object_setIvar(id obj, Ivar ivar, id value)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
-
-OBJC_EXPORT Ivar object_setInstanceVariable(id obj, const char *name, void *value);
-OBJC_EXPORT Ivar object_getInstanceVariable(id obj, const char *name, void **outValue);
-
-OBJC_EXPORT id objc_getClass(const char *name);
-OBJC_EXPORT id objc_getMetaClass(const char *name);
-OBJC_EXPORT id objc_lookUpClass(const char *name);
-OBJC_EXPORT id objc_getRequiredClass(const char *name);
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+OBJC_EXPORT Ivar object_setInstanceVariable(id obj, const char *name, void *value)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_ARC_UNAVAILABLE;
+OBJC_EXPORT Ivar object_getInstanceVariable(id obj, const char *name, void **outValue)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_ARC_UNAVAILABLE;
+
+OBJC_EXPORT id objc_getClass(const char *name)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT id objc_getMetaClass(const char *name)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT id objc_lookUpClass(const char *name)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT id objc_getRequiredClass(const char *name)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT Class objc_getFutureClass(const char *name)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void objc_setFutureClass(Class cls, const char *name)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
-OBJC_EXPORT int objc_getClassList(Class *buffer, int bufferCount);
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT int objc_getClassList(Class *buffer, int bufferCount)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT Class *objc_copyClassList(unsigned int *outCount)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_3_1);
OBJC_EXPORT Protocol *objc_getProtocol(const char *name)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
-OBJC_EXPORT Protocol **objc_copyProtocolList(unsigned int *outCount)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT Protocol * __unsafe_unretained *objc_copyProtocolList(unsigned int *outCount)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT const char *class_getName(Class cls)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT BOOL class_isMetaClass(Class cls)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT Class class_getSuperclass(Class cls)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT Class class_setSuperclass(Class cls, Class newSuper)
AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER_BUT_DEPRECATED;
-OBJC_EXPORT int class_getVersion(Class cls);
-OBJC_EXPORT void class_setVersion(Class cls, int version);
+OBJC_EXPORT int class_getVersion(Class cls)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT void class_setVersion(Class cls, int version)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT size_t class_getInstanceSize(Class cls)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
-OBJC_EXPORT Ivar class_getInstanceVariable(Class cls, const char *name);
+OBJC_EXPORT Ivar class_getInstanceVariable(Class cls, const char *name)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT Ivar class_getClassVariable(Class cls, const char *name)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
-OBJC_EXPORT Method class_getInstanceMethod(Class cls, SEL name);
-OBJC_EXPORT Method class_getClassMethod(Class cls, SEL name);
+OBJC_EXPORT Method class_getInstanceMethod(Class cls, SEL name)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT Method class_getClassMethod(Class cls, SEL name)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT IMP class_getMethodImplementation(Class cls, SEL name)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT IMP class_getMethodImplementation_stret(Class cls, SEL name)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT BOOL class_respondsToSelector(Class cls, SEL sel)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT Method *class_copyMethodList(Class cls, unsigned int *outCount)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT BOOL class_conformsToProtocol(Class cls, Protocol *protocol)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
-OBJC_EXPORT Protocol **class_copyProtocolList(Class cls, unsigned int *outCount)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT Protocol * __unsafe_unretained *class_copyProtocolList(Class cls, unsigned int *outCount)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT objc_property_t class_getProperty(Class cls, const char *name)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
-
-OBJC_EXPORT const char *class_getIvarLayout(Class cls)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
-OBJC_EXPORT const char *class_getWeakIvarLayout(Class cls)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
-
-OBJC_EXPORT id class_createInstance(Class cls, size_t extraBytes);
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+OBJC_EXPORT const uint8_t *class_getIvarLayout(Class cls)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT const uint8_t *class_getWeakIvarLayout(Class cls)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+OBJC_EXPORT id class_createInstance(Class cls, size_t extraBytes)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_ARC_UNAVAILABLE;
+OBJC_EXPORT id objc_constructInstance(Class cls, void *bytes)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0)
+ OBJC_ARC_UNAVAILABLE;
+OBJC_EXPORT void *objc_destructInstance(id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0)
+ OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT Class objc_allocateClassPair(Class superclass, const char *name,
size_t extraBytes)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void objc_registerClassPair(Class cls)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT Class objc_duplicateClass(Class original, const char *name,
size_t extraBytes)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void objc_disposeClassPair(Class cls)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp,
const char *types)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT IMP class_replaceMethod(Class cls, SEL name, IMP imp,
const char *types)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT BOOL class_addIvar(Class cls, const char *name, size_t size,
uint8_t alignment, const char *types)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT BOOL class_addProtocol(Class cls, Protocol *protocol)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
-OBJC_EXPORT void class_setIvarLayout(Class cls, const char *layout)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
-OBJC_EXPORT void class_setWeakIvarLayout(Class cls, const char *layout)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+OBJC_EXPORT void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+OBJC_EXPORT void class_setIvarLayout(Class cls, const uint8_t *layout)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT void class_setWeakIvarLayout(Class cls, const uint8_t *layout)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT SEL method_getName(Method m)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT IMP method_getImplementation(Method m)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT const char *method_getTypeEncoding(Method m)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
-OBJC_EXPORT unsigned int method_getNumberOfArguments(Method m);
+OBJC_EXPORT unsigned int method_getNumberOfArguments(Method m)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT char *method_copyReturnType(Method m)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT char *method_copyArgumentType(Method m, unsigned int index)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void method_getReturnType(Method m, char *dst, size_t dst_len)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void method_getArgumentType(Method m, unsigned int index,
char *dst, size_t dst_len)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT struct objc_method_description *method_getDescription(Method m)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT IMP method_setImplementation(Method m, IMP imp)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT const char *ivar_getName(Ivar v)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT const char *ivar_getTypeEncoding(Ivar v)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT ptrdiff_t ivar_getOffset(Ivar v)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT const char *property_getName(objc_property_t property)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT const char *property_getAttributes(objc_property_t property)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+OBJC_EXPORT char *property_copyAttributeValue(objc_property_t property, const char *attributeName)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
OBJC_EXPORT BOOL protocol_conformsToProtocol(Protocol *proto, Protocol *other)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT BOOL protocol_isEqual(Protocol *proto, Protocol *other)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT const char *protocol_getName(Protocol *p)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT struct objc_method_description protocol_getMethodDescription(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
-OBJC_EXPORT Protocol **protocol_copyProtocolList(Protocol *proto, unsigned int *outCount)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT Protocol * __unsafe_unretained *protocol_copyProtocolList(Protocol *proto, unsigned int *outCount)
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+OBJC_EXPORT Protocol *objc_allocateProtocol(const char *name)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+OBJC_EXPORT void objc_registerProtocol(Protocol *proto)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+OBJC_EXPORT void protocol_addMethodDescription(Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+OBJC_EXPORT void protocol_addProtocol(Protocol *proto, Protocol *addition)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+OBJC_EXPORT void protocol_addProperty(Protocol *proto, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount, BOOL isRequiredProperty, BOOL isInstanceProperty)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
OBJC_EXPORT const char **objc_copyImageNames(unsigned int *outCount)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT const char *class_getImageName(Class cls)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT const char **objc_copyClassNamesForImage(const char *image,
unsigned int *outCount)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
-
-OBJC_EXPORT const char *sel_getName(SEL sel);
-OBJC_EXPORT SEL sel_getUid(const char *str);
-OBJC_EXPORT SEL sel_registerName(const char *str);
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+OBJC_EXPORT const char *sel_getName(SEL sel)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT SEL sel_getUid(const char *str)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+OBJC_EXPORT SEL sel_registerName(const char *str)
+ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT BOOL sel_isEqual(SEL lhs, SEL rhs)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void objc_enumerationMutation(id)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void objc_setEnumerationMutationHandler(void (*handler)(id))
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void objc_setForwardHandler(void *fwd, void *fwd_stret)
- AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+
+OBJC_EXPORT IMP imp_implementationWithBlock(void *block)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+OBJC_EXPORT void *imp_getBlock(IMP anImp)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+OBJC_EXPORT BOOL imp_removeBlock(IMP anImp)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
/* Associated Object support. */
};
typedef uintptr_t objc_AssociationPolicy;
-OBJC_EXPORT void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
- AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER;
-OBJC_EXPORT id objc_getAssociatedObject(id object, void *key)
- AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER;
+OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
+OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
OBJC_EXPORT void objc_removeAssociatedObjects(id object)
- AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER;
+ __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
+
+
+// API to be called by clients of objects
+
+OBJC_EXPORT id objc_loadWeak(id *location)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+// returns value stored (either obj or NULL)
+OBJC_EXPORT id objc_storeWeak(id *location, id obj)
+ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
#define _C_ID '@'
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method buckets[1] OBJC2_UNAVAILABLE;
-} /* GrP fixme should be OBJC2_UNAVAILABLE, but isn't because of spurious warnings in [super ...] calls */;
+};
typedef struct objc_module *Module OBJC2_UNAVAILABLE;
/* Obsolete functions */
-OBJC_EXPORT BOOL sel_isMapped(SEL sel) OBJC2_UNAVAILABLE;
+OBJC_EXPORT IMP class_lookupMethod(Class cls, SEL sel)
+ __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_2_0,__IPHONE_2_0);
+OBJC_EXPORT BOOL class_respondsToMethod(Class cls, SEL sel)
+ __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_2_0,__IPHONE_2_0);
+OBJC_EXPORT void _objc_flush_caches(Class cls)
+ __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_2_0,__IPHONE_2_0);
-OBJC_EXPORT id object_copyFromZone(id anObject, size_t nBytes, void *z) DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
+OBJC_EXPORT id object_copyFromZone(id anObject, size_t nBytes, void *z)
+ __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_NA,__IPHONE_NA)
+ OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT id object_realloc(id anObject, size_t nBytes) OBJC2_UNAVAILABLE;
OBJC_EXPORT id object_reallocFromZone(id anObject, size_t nBytes, void *z) OBJC2_UNAVAILABLE;
OBJC_EXPORT void objc_setClassHandler(int (*)(const char *)) OBJC2_UNAVAILABLE;
OBJC_EXPORT void objc_setMultithreaded (BOOL flag) OBJC2_UNAVAILABLE;
-OBJC_EXPORT id class_createInstanceFromZone(Class, size_t idxIvars, void *z) DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER;
+OBJC_EXPORT id class_createInstanceFromZone(Class, size_t idxIvars, void *z)
+ __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_NA,__IPHONE_NA)
+ OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT void class_addMethods(Class, struct objc_method_list *) OBJC2_UNAVAILABLE;
OBJC_EXPORT void class_removeMethods(Class, struct objc_method_list *) OBJC2_UNAVAILABLE;
+OBJC_EXPORT void _objc_resolve_categories_for_class(Class cls) OBJC2_UNAVAILABLE;
OBJC_EXPORT Class class_poseAs(Class imposter, Class original) OBJC2_UNAVAILABLE;
// struct objc_method_list *mlist;
// while ( mlist = class_nextMethodList( cls, &iterator ) )
// ;
-
+
OBJC_EXPORT id (*_alloc)(Class, size_t) OBJC2_UNAVAILABLE;
OBJC_EXPORT id (*_copy)(id, size_t) OBJC2_UNAVAILABLE;
OBJC_EXPORT id (*_realloc)(id, size_t) OBJC2_UNAVAILABLE;
--- /dev/null
+//
+// ARRBase.h
+// TestARRLayouts
+//
+// Created by Patrick Beard on 3/8/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/NSObject.h>
+
+@interface ARRBase : NSObject
+@property double number;
+@property(retain) id object;
+@property void *pointer;
+@property(weak) __weak id delegate;
+@end
--- /dev/null
+//
+// ARRBase.m
+// TestARRLayouts
+//
+// Created by Patrick Beard on 3/8/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "ARRBase.h"
+
+#if 1
+@interface ARRBase () {
+@private
+ double number;
+ id object;
+ void *pointer;
+ __weak id delegate;
+}
+@end
+#endif
+
+@implementation ARRBase
+@synthesize number, object, pointer, delegate;
+@end
--- /dev/null
+// clang -objc-arr -print-ivar-layout
+/*
+TEST_DISABLED
+TEST_CONFIG GC=0 CC=clang
+TEST_BUILD
+ clang -c $DIR/MRRBase.m $DIR/MRRARR.m
+ libtool -static MRRBase.o MRRARR.o -framework Foundation -o libMRR.a
+ clang -fobjc-arr -c $DIR/ARRBase.m $DIR/ARRMRR.m
+ libtool -static ARRBase.o ARRMRR.o -framework Foundation -o libARR.a
+ $C{COMPILE} -fobjc-arr $DIR/ARRLayouts.m -L . -lMRR -lARR -framework Foundation -o ARRLayouts.out
+END
+*/
+
+#include "test.h"
+#import <stdio.h>
+#import <assert.h>
+#import <Foundation/Foundation.h>
+#import <objc/runtime.h>
+
+#import "ARRMRR.h"
+#import "MRRARR.h"
+
+@interface NSObject (Layouts)
++ (const char *)strongLayout;
++ (const char *)weakLayout;
+@end
+
+void printlayout(const char *name, const uint8_t *layout)
+{
+ printf("%s: ", name);
+
+ if (!layout) {
+ printf("NULL\n");
+ return;
+ }
+
+ const uint8_t *c;
+ for (c = layout; *c; c++) {
+ printf("%02x ", *c);
+ }
+
+ printf("00\n");
+}
+
+@implementation NSObject (Layouts)
+
++ (const char *)strongLayout {
+ const uint8_t *layout = class_getIvarLayout(self);
+ // printlayout("strong", layout);
+ return (const char *)layout;
+}
+
++ (const char *)weakLayout {
+ const uint8_t *weakLayout = class_getWeakIvarLayout(self);
+ // printlayout("weak", weakLayout);
+ return (const char *)weakLayout;
+}
+
++ (Ivar)instanceVariable:(const char *)name {
+ return class_getInstanceVariable(self, name);
+}
+
+@end
+
+int main (int argc __unused, const char * argv[] __unused) {
+ // Under ARR, layout strings are relative to the class' own ivars.
+ assert(strcmp([ARRBase strongLayout], "\x11\x20") == 0);
+ assert(strcmp([ARRBase weakLayout], "\x31") == 0);
+ assert([MRRBase strongLayout] == NULL);
+ assert([MRRBase weakLayout] == NULL);
+ assert(strcmp([ARRMRR strongLayout], "\x01") == 0);
+ assert([ARRMRR weakLayout] == NULL);
+ assert([MRRARR strongLayout] == NULL);
+ assert([MRRARR weakLayout] == NULL);
+
+ // now check consistency between dynamic accessors and KVC, etc.
+ ARRMRR *am = [ARRMRR new];
+ MRRARR *ma = [MRRARR new];
+
+ NSString *am_description = [[NSString alloc] initWithFormat:@"%s %p", "ARRMRR", am];
+ NSString *ma_description = [[NSString alloc] initWithFormat:@"%s %p", "MRRARR", ma];
+
+ am.number = M_PI;
+ object_setIvar(am, [ARRMRR instanceVariable:"object"], am_description);
+ assert(CFGetRetainCount(objc_unretainedPointer(am_description)) == 1);
+ am.pointer = @selector(ARRMRR);
+ object_setIvar(am, [ARRMRR instanceVariable:"delegate"], ma);
+ assert(CFGetRetainCount(objc_unretainedPointer(ma)) == 1);
+
+ ma.number = M_E;
+ object_setIvar(ma, [MRRARR instanceVariable:"object"], ma_description);
+ assert(CFGetRetainCount(objc_unretainedPointer(ma_description)) == 2);
+ ma.pointer = @selector(MRRARR);
+ ma.delegate = am;
+ object_setIvar(ma, [MRRARR instanceVariable:"delegate"], am);
+ assert(CFGetRetainCount(objc_unretainedPointer(am)) == 1);
+
+ succeed(__FILE__);
+ return 0;
+}
--- /dev/null
+//
+// ARRMRR.h
+// TestARRLayouts
+//
+// Created by Patrick Beard on 3/8/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "MRRBase.h"
+
+@interface ARRMRR : MRRBase
+@property(retain) id dataSource;
+@end
--- /dev/null
+//
+// ARRMRR.m
+//
+
+#import "ARRMRR.h"
+
+@implementation ARRMRR
+
+@synthesize dataSource;
+
+@end
--- /dev/null
+//
+// MRRARR.h
+// TestARRLayouts
+//
+// Created by Patrick Beard on 3/8/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "ARRBase.h"
+
+@interface MRRARR : ARRBase
+@property(retain) id dataSource;
+@end
--- /dev/null
+//
+// MRRARR.m
+//
+
+#import "MRRARR.h"
+
+@implementation MRRARR
+
+@synthesize dataSource;
+
+@end
--- /dev/null
+//
+// MRRBase.h
+// TestARRLayouts
+//
+// Created by Patrick Beard on 3/8/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/NSObject.h>
+
+@interface MRRBase : NSObject
+@property double number;
+@property(retain) id object;
+@property void *pointer;
+@property(weak) __weak id delegate;
+@end
--- /dev/null
+//
+// MRRBase.m
+// TestARRLayouts
+//
+// Created by Patrick Beard on 3/8/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "MRRBase.h"
+
+#if 1
+@interface MRRBase () {
+@private
+ double number;
+ id object;
+ void *pointer;
+ __weak id delegate;
+}
+@end
+#endif
+
+@implementation MRRBase
+@synthesize number, object, pointer, delegate;
+@end
-# a "simple test" contains a single .m file
-# and optionally an expected-stderr file
-SIMPLE_TESTS = category classname classpair classversion copyIvarList \
- copyMethodList copyPropertyList createInstance \
- definitions duplicateClass exc exchangeImp \
- forward gdb getMethod layout ignoredSelector initialize \
- instanceSize ismeta ivar load \
- msgSend property protocol protocol_copyMethodList addMethod \
- protocol_copyPropertyList protocol_cw sel super setSuper methodArgs \
- nilAPIArgs resolve runtime zone weakcopy
+# quick test
+all:
+ perl test.pl $(MAKEFLAGS)
-# a "simple foundation test" is a simple test with -framework Foundation
-SIMPLE_FOUNDATION_TESTS = nsobject foreach accessors \
- classgetclass gdb-lock unwind method_getName \
- synchronized synchronized-counter synchronized-grid
+# default-arch but otherwise comprehensive test for buildbot
+buildbot:
+ perl test.pl $(MAKEFLAGS) GC=0,1 CC=clang,llvm-gcc-4.2,gcc-4.2 LANGUAGE=objc,objc++
-# a "simple CF test" is a simple test with -framework CoreFoundation
-SIMPLE_CF_TESTS = association-cf
+# comprehensive tests
+mac macos macosx:
+ perl test.pl $(MAKEFLAGS) ARCH=x86_64,i386 GC=0,1 CC=clang,llvm-gcc-4.2,gcc-4.2 LANGUAGE=objc,objc++
-# a "complex build test" has a dedicated testname.out build rule below
-COMPLEX_BUILD_TESTS = cacheflush future unload ivarSlide ivarSlidexx \
- gcenforcer imageorder nsexc concurrentcat load-parallel load-order \
- load-reentrant
+iphonesimulator:
+ perl test.pl $(MAKEFLAGS) ARCH=i386 SDK=iphonesimulator GC=0 CC=clang,llvm-gcc-4.2,gcc-4.2 LANGUAGE=objc,objc++
-# a "complex test" has a dedicated testname run rule below
-COMPLEX_RUN_TESTS = gcenforcer_noobjc gcenforcer_nogc gcenforcer_supportsgc \
- gcenforcer_requiresgc weak-missing weak-not-missing
-
-# `make fail` just fails
-FAIL_TESTS = fail
-
-# Test-specific flags
-CFLAGS_gdb = -Wno-deprecated-declarations -Wno-\#warnings
-CFLAGS_setSuper = -Wno-deprecated-declarations
-CFLAGS_duplicateClass = -Wno-deprecated-declarations
-CFLAGS_protocol = -Wno-deprecated-declarations
-CFLAGS_protocol_cw = -Wno-deprecated-declarations
-CFLAGS_methodArgs = -Wno-deprecated-declarations
-CFLAGS_ignoredSelector = -Wno-deprecated-declarations
-CFLAGS_method_getName = -Wno-deprecated-declarations
-
-# `make OBJC_LIB=/path/to/libobjc.A.dylib` tests a specific objc build
-ifndef OBJC_LIB
-OBJC_LIB = -lobjc
-endif
-ifneq "$(OBJC_LIB)" "-lobjc"
-ENV_PREFIX += DYLD_LIBRARY_PATH=$(dir $(OBJC_LIB))
-endif
-
-# `make ARCHS=cpu` tests with the specified architecture (only one allowed)
-ifndef ARCHS
-ARCHS=$(shell /usr/bin/arch)
-endif
-
-# `make ARCH` is unsupported
-ifdef ARCH
-echo "*** use ARCHS not ARCH!"
-exit 1
-endif
-
-# `make OTHER_CFLAGS=x` tests with specified flags
-CFLAGS = -W -Wall -Wshorten-64-to-32 -g -O0 -fobjc-new-property
-CFLAGS += $(OTHER_CFLAGS) -arch $(ARCHS)
-LIBS = $(OBJC_LIB) -lauto
-
-# `make GC=YES` tests with GC on
-GC_state = nogc
-ifdef GC
-ifneq "$(GC)" "no"
-ifneq "$(GC)" "NO"
-GC_state = gc
-CFLAGS += -fobjc-gc
-endif
-endif
-endif
-
-# `make LEAK_CHECK=NO` disables leak checking
-# `make VALGRIND=YES` runs tests under Valgrind
-ifdef VALGRIND
-ifneq "$(VALGRIND)" "NO"
-VALGRIND_PREFIX = ~public/bin/valgrind --db-listen=no --track-origins=yes
-ifeq "$(LEAK_CHECK)" "NO"
-VALGRIND_PREFIX += --leak-check=no
-else
-VALGRIND_PREFIX += --leak-check=full --leak-resolution=high
-endif
-endif
-endif
-
-# `make GUARDMALLOC=YES` runs tests with libgmalloc
-ifdef GUARDMALLOC
-ifneq "$(GUARDMALLOC)" "NO"
-ENV_PREFIX += DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib
-endif
-endif
-
-
-RUN = $(ENV_PREFIX) $(VALGRIND_PREFIX)
-ERR_CHECK = 2>&1 | perl errcheck.pl
-
-# `make HALT=YES` halts on the first test to fail, this is easier for automated harnesses to detect and report
-ifneq "$(HALT)" "YES"
-# if we are supposed to continue after error (the default), do something successful if errcheck.pl failed
-EAT_ERR = || sh -c ""
-endif
-
-FOUNDATION = -framework Foundation
-CF = -framework CoreFoundation
-
-# Rebuild executables every time in case arch or cflags changed
-.PHONY: $(wildcard *.out) $(wildcard *.dylib)
-
-CC=cc $(CFLAGS)
-CXX=c++ $(CFLAGS) -fobjc-call-cxx-cdtors
-CC_C=$(filter-out -fobjc-gc,$(CC))
-
-all: print $(SIMPLE_TESTS) $(COMPLEX_BUILD_TESTS) $(COMPLEX_RUN_TESTS) $(SIMPLE_CF_TESTS) $(SIMPLE_FOUNDATION_TESTS)
-
-print:
- @echo "Testing library $(OBJC_LIB) ..."
- @echo "CFLAGS = $(CFLAGS)"
-
-debug:
- @ make OBJC_LIB=`pwd`/../build/Debug/libobjc.A.dylib
-release:
- @ make OBJC_LIB=`pwd`/../build/Release/libobjc.A.dylib
-buildit:
- @ make OBJC_LIB=/tmp/objc.roots/objc~sym/libobjc.A.dylib
-buildit-%:
- @ make OBJC_LIB=/tmp/objc-$*.roots/objc-$*~sym/libobjc.A.dylib
-clean:
- rm -f *.out *.o *.dylib
- rm -rf *.dSYM
-
-reallyclean: clean
- rm -f *~
-
-$(SIMPLE_TESTS) $(FAIL_TESTS) $(COMPLEX_BUILD_TESTS) $(SIMPLE_FOUNDATION_TESTS) $(SIMPLE_CF_TESTS) : % : %.out
- @ $(RUN) ./$@.out $(ERR_CHECK) $@ $(EAT_ERR)
-
-$(addsuffix .out,$(SIMPLE_TESTS) $(FAIL_TESTS)) : %.out : %.m test.h Makefile
- @ $(CC) $(CFLAGS_$*) $< -o $@ $(LIBS)
-
-$(addsuffix .out,$(SIMPLE_FOUNDATION_TESTS)) : %.out : %.m test.h Makefile
- @ $(CC) $(CFLAGS_$*) $(FOUNDATION) $< -o $@ $(LIBS)
-
-$(addsuffix .out,$(SIMPLE_CF_TESTS)) : %.out : %.m test.h Makefile
- @ $(CC) $(CFLAGS_$*) $(CF) $< -o $@ $(LIBS)
-
-nsexc.out: exc.m test.h Makefile
- @ $(CC) $(FOUNDATION) exc.m -o nsexc.out $(LIBS) -DUSE_FOUNDATION=1
-
-imageorder.out: imageorder.m imageorder.h imageorder3.out test.h Makefile
- @ $(CC) imageorder.m imageorder3.out imageorder2.out imageorder1.out -o imageorder.out $(LIBS)
-imageorder3.out: imageorder3.m imageorder.h imageorder2.out test.h Makefile
- @ $(CC) -dynamiclib imageorder3.m imageorder2.out imageorder1.out -o imageorder3.out $(LIBS)
-imageorder2.out: imageorder2.m imageorder.h imageorder1.out test.h Makefile
- @ $(CC) -dynamiclib imageorder2.m imageorder1.out -o imageorder2.out $(LIBS)
-imageorder1.out: imageorder1.m imageorder.h test.h Makefile
- @ $(CC) -dynamiclib imageorder1.m -o imageorder1.out $(LIBS)
-
-ivarSlide.out: ivarSlide1.m ivarSlide2.m ivarSlide.h test.h Makefile
- @ $(CC) ivarSlide1.m ivarSlide2.m -o ivarSlide.out $(LIBS)
-
-ivarSlidexx.out: ivarSlide1.m ivarSlide2.m ivarSlide.h test.h Makefile
- @ $(CXX) -x objective-c++ ivarSlide1.m ivarSlide2.m -x none -o ivarSlidexx.out $(LIBS)
-
-future.out: future1.m future.h future0.out future2.out test.h Makefile
- @ $(CC) future1.m future0.out -o future.out $(LIBS)
-future0.out: future0.m future.h test.h Makefile
- @ $(CC) -dynamiclib future0.m -o future0.out $(LIBS)
-future2.out: future2.m future.h future0.out test.h Makefile
- @ $(CC) -dynamiclib future2.m future0.out -o future2.out $(LIBS)
-
-weak.out: weak.m weak2.m weak.h test.h Makefile
- @ $(CC) weak2.m -dynamiclib -o libweak.dylib $(LIBS)
- @ $(CC) weak2.m -DEMPTY= -dynamiclib -o libweak_empty.dylib $(LIBS)
- @ $(CC) weak.m -L. -weak-lweak -o weak.out $(LIBS)
-
-weak-not-missing: weak.out
- @ $(RUN) ./weak.out $(ERR_CHECK) weak-not-missing $(EAT_ERR)
-weak-missing: weak.out
- @ DYLD_IMAGE_SUFFIX=_empty $(RUN) ./weak.out $(ERR_CHECK) weak-missing $(EAT_ERR)
-
-CONCURRENT_IN=cc1 cc2 cc3 cc4 cc5 cc6 cc7 cc8 cc9 cc10 cc11 cc12 cc13 cc14 cc15
-CONCURRENT_DYLIBS=$(addsuffix .out,$(CONCURRENT_IN))
-
-$(CONCURRENT_DYLIBS) : %.out : concurrentcat_category.m test.h Makefile
- @ $(CC) -undefined dynamic_lookup -dynamiclib -DTN=$* $< -o $@ $(LIBS)
-
-concurrentcat.out: concurrentcat.m $(CONCURRENT_DYLIBS)
- @ $(CC) concurrentcat.m -o concurrentcat.out $(LIBS) $(FOUNDATION)
-
-cacheflush.out: cacheflush.m cacheflush.h cacheflush0.out cacheflush2.out cacheflush3.out test.h Makefile
- @ $(CC) cacheflush.m cacheflush0.out -o cacheflush.out $(LIBS)
-cacheflush0.out: cacheflush0.m cacheflush.h test.h Makefile
- @ $(CC) -dynamiclib cacheflush0.m -o cacheflush0.out $(LIBS)
-cacheflush2.out: cacheflush2.m cacheflush.h cacheflush0.out test.h Makefile
- @ $(CC) -dynamiclib cacheflush2.m cacheflush0.out -o cacheflush2.out $(LIBS)
-cacheflush3.out: cacheflush3.m cacheflush.h cacheflush0.out test.h Makefile
- @ $(CC) -dynamiclib cacheflush3.m cacheflush0.out -o cacheflush3.out $(LIBS)
-
-
-unload.out: unload.m unload.h unload2.out unload3.out unload4.out test.h Makefile
- @ $(CC) unload.m -o unload.out $(LIBS)
-unload2.out: unload2.m unload.h test.h Makefile
- @ $(CC) -bundle unload2.m -o unload2.out $(LIBS)
-unload3.out: unload3.m Makefile
- @ $(CC) -dynamiclib unload3.m -o unload3.out $(LIBS)
-unload4.out: unload4.m Makefile
- @ $(CC) -dynamiclib unload4.m -o unload4.out $(LIBS)
-
-load-reentrant.out: load-reentrant.m load-reentrant2.m
- @ $(CC) load-reentrant.m -o load-reentrant.out $(LIBS)
- @ $(CC) -bundle load-reentrant2.m -o libload-reentrant2.dylib -bundle_loader load-reentrant.out $(LIBS)
-
-load-order.out: load-order.m libload-order1.dylib
- @ $(CC) load-order.m -o load-order.out -L. -lload-order1 -lload-order2 -lload-order3 $(LIBS)
-libload-order1.dylib: load-order1.m libload-order2.dylib
- @ $(CC) -dynamiclib load-order1.m -o libload-order1.dylib -L. -lload-order2 -lload-order3 $(LIBS)
-libload-order2.dylib: load-order2.m libload-order3.dylib
- @ $(CC) -dynamiclib load-order2.m -o libload-order2.dylib -L. -lload-order3 $(LIBS)
-libload-order3.dylib: load-order3.m
- @ $(CC) -dynamiclib load-order3.m -o libload-order3.dylib $(LIBS)
-
-load-parallel.out: load-parallel.m libload-parallel00.dylib load-parallel0.out load-parallel1.out load-parallel2.out load-parallel3.out load-parallel4.out load-parallel5.out load-parallel6.out load-parallel7.out load-parallel8.out load-parallel9.out
- @ $(CC) -DCOUNT=10 load-parallel.m -o load-parallel.out -L. -lload-parallel00 $(LIBS)
-libload-parallel00.dylib: load-parallel00.m Makefile
- @ $(CC) -dynamiclib load-parallel00.m -o libload-parallel00.dylib $(LIBS)
-load-parallel0.out: load-parallel0.m Makefile libload-parallel00.dylib
- @ $(CC) -dynamiclib load-parallel0.m -DN=0 -o load-parallel0.out -L. -lload-parallel00 $(LIBS)
-load-parallel1.out: load-parallel0.m Makefile libload-parallel00.dylib
- @ $(CC) -dynamiclib load-parallel0.m -DN=1 -o load-parallel1.out -L. -lload-parallel00 $(LIBS)
-load-parallel2.out: load-parallel0.m Makefile libload-parallel00.dylib
- @ $(CC) -dynamiclib load-parallel0.m -DN=2 -o load-parallel2.out -L. -lload-parallel00 $(LIBS)
-load-parallel3.out: load-parallel0.m Makefile libload-parallel00.dylib
- @ $(CC) -dynamiclib load-parallel0.m -DN=3 -o load-parallel3.out -L. -lload-parallel00 $(LIBS)
-load-parallel4.out: load-parallel0.m Makefile libload-parallel00.dylib
- @ $(CC) -dynamiclib load-parallel0.m -DN=4 -o load-parallel4.out -L. -lload-parallel00 $(LIBS)
-load-parallel5.out: load-parallel0.m Makefile libload-parallel00.dylib
- @ $(CC) -dynamiclib load-parallel0.m -DN=5 -o load-parallel5.out -L. -lload-parallel00 $(LIBS)
-load-parallel6.out: load-parallel0.m Makefile libload-parallel00.dylib
- @ $(CC) -dynamiclib load-parallel0.m -DN=6 -o load-parallel6.out -L. -lload-parallel00 $(LIBS)
-load-parallel7.out: load-parallel0.m Makefile libload-parallel00.dylib
- @ $(CC) -dynamiclib load-parallel0.m -DN=7 -o load-parallel7.out -L. -lload-parallel00 $(LIBS)
-load-parallel8.out: load-parallel0.m Makefile libload-parallel00.dylib
- @ $(CC) -dynamiclib load-parallel0.m -DN=8 -o load-parallel8.out -L. -lload-parallel00 $(LIBS)
-load-parallel9.out: load-parallel0.m Makefile libload-parallel00.dylib
- @ $(CC) -dynamiclib load-parallel0.m -DN=9 -o load-parallel9.out -L. -lload-parallel00 $(LIBS)
-
-libnoobjc.dylib: gc.c Makefile
- @ $(CC_C) gc.c -dynamiclib -o libnoobjc.dylib
-libnogc.dylib: gc.m Makefile
- @ $(CC) gc.m -dynamiclib -o libnogc.dylib -fno-objc-gc $(LIBS)
-libsupportsgc.dylib: gc.m Makefile
- @ $(CC) gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc $(LIBS)
-librequiresgc.real.dylib: gc.m Makefile
- @ $(CC) gc.m -dynamiclib -o librequiresgc.real.dylib -install_name librequiresgc.dylib -fobjc-gc-only $(LIBS)
-librequiresgc.fake.dylib: gc.m Makefile
- @ $(CC) gc.m -dynamiclib -o librequiresgc.fake.dylib -install_name librequiresgc.dylib -fobjc-gc $(LIBS)
-librequiresgc.dylib: librequiresgc.real.dylib librequiresgc.fake.dylib
- @ cp -f librequiresgc.real.dylib librequiresgc.dylib
-
-gcenforcer.out: gcenforcer.m libsupportsgc.dylib librequiresgc.dylib libnogc.dylib libnoobjc.dylib test.h Makefile
- @ $(CC) gcenforcer.m -o gcenforcer.out $(LIBS)
-
-gcenforcer_noobjc.out: main.m libnoobjc.dylib test.h Makefile
- @ $(CC) main.m libnoobjc.dylib -o gcenforcer_noobjc.out $(LIBS)
-gcenforcer_noobjc: gcenforcer_noobjc.out Makefile
- @ $(RUN) ./gcenforcer_noobjc.out $(ERR_CHECK) $@ $(EAT_ERR)
-
-gcenforcer_nogc.out: main.m libnogc.dylib test.h Makefile
- @ $(CC) main.m libnogc.dylib -o gcenforcer_nogc.out $(LIBS)
-gcenforcer_nogc: gcenforcer_nogc.out Makefile
- @ $(RUN) ./gcenforcer_nogc.out $(ERR_CHECK) $@ gcenforcer_nogc.$(GC_state).expected-stderr $(EAT_ERR)
-
-gcenforcer_supportsgc.out: main.m libsupportsgc.dylib test.h Makefile
- @ $(CC) main.m libsupportsgc.dylib -o gcenforcer_supportsgc.out $(LIBS)
-gcenforcer_supportsgc: gcenforcer_supportsgc.out Makefile
- @ $(RUN) ./gcenforcer_supportsgc.out $(ERR_CHECK) $@ $(EAT_ERR)
-
-# Linker sees librequiresgc.fake.dylib, runtime uses librequiresgc.real.dylib
-gcenforcer_requiresgc.out: main.m librequiresgc.dylib libsupportsgc.dylib test.h Makefile
- @ $(CC) main.m librequiresgc.fake.dylib -o gcenforcer_requiresgc.out $(LIBS)
-
-gcenforcer_requiresgc: gcenforcer_requiresgc.out Makefile
- @ $(RUN) ./gcenforcer_requiresgc.out $(ERR_CHECK) $@ gcenforcer_requiresgc.$(GC_state).expected-stderr $(EAT_ERR)
+iphoneos:
+ perl test.pl $(MAKEFLAGS) ARCH=armv6,armv7 SDK=iphoneos GC=0 CC=clang,llvm-gcc-4.2,gcc-4.2 LANGUAGE=objc,objc++
+clean:
+ @ perl test.pl clean
+++ /dev/null
-objc4 test suite README
-
-To run the tests:
- `make` in this directory, or `make test` in the top-level directory
-
-Correct test output consists of 'PASS: testname' for each test.
-No other output should be seen.
-
-Other options:
- `make` tests the installed libobjc
- `make buildit` tests the libobjc in /tmp/objc4-roots/objc4~sym/
- `make local` tests the libobjc in ..
-
- `make GC=YES` runs with garbage collection on
- `make ARCHS=cpu` tests with the specified architecture (only one allowed)
- `make VALGRIND=YES` runs with valgrind on (memcheck and leak detection)
- `make GUARDMALLOC=YES` runs with GuardMalloc on
-
- `make OBJC_LIB=/path/to/libobjc.A.dylib` tests a specific objc4 build
- `make OTHER_CFLAGS=x` tests with specified flags
-
- `make VERBOSE=` logs progress of some tests
+// TEST_CFLAGS -framework Foundation
+
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
+#import <objc/objc-abi.h>
#include "test.h"
-extern id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic);
-extern void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy);
-
@interface Test : NSObject {
NSString *_value;
// _object is at the last optimized property offset
id _object __attribute__((aligned(64)));
}
-@property(readonly) Class class;
+@property(readonly) Class cls;
@property(copy) NSString *value;
@property(assign) id object;
@end
[super dealloc];
}
-- (Class)class { return objc_getProperty(self, _cmd, 0, YES); }
+- (Class)cls { return objc_getProperty(self, _cmd, 0, YES); }
- (NSString*)value { return (NSString*) objc_getProperty(self, _cmd, offsetof(TestDefs, _value), YES); }
- (void)setValue:(NSString*)inValue { objc_setProperty(self, _cmd, offsetof(TestDefs, _value), inValue, YES, YES); }
testassert([t.value isEqualToString:@"test"]);
Class testClass = [Test class];
- Class cls = t.class;
+ Class cls = t.cls;
testassert(testClass == cls);
- cls = t.class;
+ cls = t.cls;
testassert(testClass == cls);
t.object = object;
+// TEST_CONFIG
+
#include "test.h"
#include <objc/runtime.h>
--- /dev/null
+/*
+TEST_RUN_OUTPUT
+objc\[\d+\]: protocol_addProtocol: added protocol 'EmptyProto' is still under construction!
+objc\[\d+\]: objc_registerProtocol: protocol 'Proto1' was already registered!
+objc\[\d+\]: protocol_addProtocol: modified protocol 'Proto1' is not under construction!
+objc\[\d+\]: protocol_addMethodDescription: protocol 'Proto1' is not under construction!
+objc\[\d+\]: objc_registerProtocol: protocol 'SuperProto' was already registered!
+objc\[\d+\]: protocol_addProtocol: modified protocol 'SuperProto' is not under construction!
+objc\[\d+\]: protocol_addMethodDescription: protocol 'SuperProto' is not under construction!
+OK: addProtocol.m
+END
+*/
+
+#include "test.h"
+
+#include <objc/runtime.h>
+
+@protocol SuperProto @end
+@protocol SuperProto2 @end
+@protocol UnrelatedProto @end
+
+int main()
+{
+ Protocol *proto, *proto2;
+ Protocol * const *protolist;
+ struct objc_method_description *desclist;
+ objc_property_t *proplist;
+ unsigned int count;
+
+ // make sure binary contains hard copies of these protocols
+ proto = @protocol(SuperProto);
+ proto = @protocol(SuperProto2);
+
+ // Adding a protocol
+
+ char *name = strdup("Proto1");
+ proto = objc_allocateProtocol(name);
+ testassert(proto);
+ testassert(!objc_getProtocol(name));
+
+ protocol_addProtocol(proto, @protocol(SuperProto));
+ protocol_addProtocol(proto, @protocol(SuperProto2));
+ // no inheritance cycles
+ proto2 = objc_allocateProtocol("EmptyProto");
+ protocol_addProtocol(proto, proto2); // fails
+ objc_registerProtocol(proto2);
+ protocol_addProtocol(proto, proto2); // succeeds
+
+ char *types = strdup("@:");
+ protocol_addMethodDescription(proto, @selector(ReqInst0), types, YES, YES);
+ protocol_addMethodDescription(proto, @selector(ReqInst1), types, YES, YES);
+ protocol_addMethodDescription(proto, @selector(ReqInst2), types, YES, YES);
+ protocol_addMethodDescription(proto, @selector(ReqInst3), types, YES, YES);
+
+ protocol_addMethodDescription(proto, @selector(ReqClas0), types, YES, NO);
+ protocol_addMethodDescription(proto, @selector(ReqClas1), types, YES, NO);
+ protocol_addMethodDescription(proto, @selector(ReqClas2), types, YES, NO);
+ protocol_addMethodDescription(proto, @selector(ReqClas3), types, YES, NO);
+
+ protocol_addMethodDescription(proto, @selector(OptInst0), types, NO, YES);
+ protocol_addMethodDescription(proto, @selector(OptInst1), types, NO, YES);
+ protocol_addMethodDescription(proto, @selector(OptInst2), types, NO, YES);
+ protocol_addMethodDescription(proto, @selector(OptInst3), types, NO, YES);
+
+ protocol_addMethodDescription(proto, @selector(OptClas0), types, NO, NO);
+ protocol_addMethodDescription(proto, @selector(OptClas1), types, NO, NO);
+ protocol_addMethodDescription(proto, @selector(OptClas2), types, NO, NO);
+ protocol_addMethodDescription(proto, @selector(OptClas3), types, NO, NO);
+
+ char *name0 = strdup("ReqInst0");
+ char *name1 = strdup("ReqInst1");
+ char *name2 = strdup("ReqInst2");
+ char *name3 = strdup("ReqInst3");
+ char *attrname = strdup("T");
+ char *attrvalue = strdup("i");
+ objc_property_attribute_t attrs[] = {{attrname, attrvalue}};
+ int attrcount = sizeof(attrs) / sizeof(attrs[0]);
+ protocol_addProperty(proto, name0, attrs, attrcount, YES, YES);
+ protocol_addProperty(proto, name1, attrs, attrcount, YES, YES);
+ protocol_addProperty(proto, name2, attrs, attrcount, YES, YES);
+ protocol_addProperty(proto, name3, attrs, attrcount, YES, YES);
+
+ objc_registerProtocol(proto);
+ testassert(0 == strcmp(protocol_getName(proto), "Proto1"));
+
+ // Use of added protocols
+
+ testassert(proto == objc_getProtocol("Proto1"));
+ strcpy(name, "XXXXXX"); // name is copied
+ testassert(0 == strcmp(protocol_getName(proto), "Proto1"));
+
+ protolist = protocol_copyProtocolList(proto, &count);
+ testassert(protolist);
+ testassert(count == 3);
+ // note this order is not required
+ testassert(protolist[0] == @protocol(SuperProto) &&
+ protolist[1] == @protocol(SuperProto2) &&
+ protolist[2] == proto2);
+ free((void*)protolist);
+
+ testassert(protocol_conformsToProtocol(proto, proto2));
+ testassert(protocol_conformsToProtocol(proto, @protocol(SuperProto)));
+ testassert(!protocol_conformsToProtocol(proto, @protocol(UnrelatedProto)));
+
+ strcpy(types, "XX"); // types is copied
+ desclist = protocol_copyMethodDescriptionList(proto, YES, YES, &count);
+ testassert(desclist && count == 4);
+ testassert(desclist[0].name == @selector(ReqInst0));
+ testassert(0 == strcmp(desclist[0].types, "@:"));
+ free(desclist);
+ desclist = protocol_copyMethodDescriptionList(proto, YES, NO, &count);
+ testassert(desclist && count == 4);
+ testassert(desclist[1].name == @selector(ReqClas1));
+ testassert(0 == strcmp(desclist[1].types, "@:"));
+ free(desclist);
+ desclist = protocol_copyMethodDescriptionList(proto, NO, YES, &count);
+ testassert(desclist && count == 4);
+ testassert(desclist[2].name == @selector(OptInst2));
+ testassert(0 == strcmp(desclist[2].types, "@:"));
+ free(desclist);
+ desclist = protocol_copyMethodDescriptionList(proto, NO, NO, &count);
+ testassert(desclist && count == 4);
+ testassert(desclist[3].name == @selector(OptClas3));
+ testassert(0 == strcmp(desclist[3].types, "@:"));
+ free(desclist);
+
+ strcpy(name0, "XXXXXXXX"); // name is copied
+ strcpy(name1, "XXXXXXXX"); // name is copied
+ strcpy(name2, "XXXXXXXX"); // name is copied
+ strcpy(name3, "XXXXXXXX"); // name is copied
+ strcpy(attrname, "X"); // description is copied
+ strcpy(attrvalue, "X"); // description is copied
+ memset(attrs, 'X', sizeof(attrs)); // description is copied
+ proplist = protocol_copyPropertyList(proto, &count);
+ testassert(proplist);
+ testassert(count == 4);
+ // note this order is not required
+ testassert(0 == strcmp(property_getName(proplist[0]), "ReqInst0"));
+ testassert(0 == strcmp(property_getName(proplist[1]), "ReqInst1"));
+ testassert(0 == strcmp(property_getName(proplist[2]), "ReqInst2"));
+ testassert(0 == strcmp(property_getName(proplist[3]), "ReqInst3"));
+ testassert(0 == strcmp(property_getAttributes(proplist[0]), "Ti"));
+ testassert(0 == strcmp(property_getAttributes(proplist[1]), "Ti"));
+ testassert(0 == strcmp(property_getAttributes(proplist[2]), "Ti"));
+ testassert(0 == strcmp(property_getAttributes(proplist[3]), "Ti"));
+ free(proplist);
+
+
+ testassert(proto2 == objc_getProtocol("EmptyProto"));
+ testassert(0 == strcmp(protocol_getName(proto2), "EmptyProto"));
+
+ protolist = protocol_copyProtocolList(proto2, &count);
+ testassert(!protolist);
+ testassert(count == 0);
+
+ testassert(!protocol_conformsToProtocol(proto2, proto));
+ testassert(!protocol_conformsToProtocol(proto2,@protocol(SuperProto)));
+ testassert(!protocol_conformsToProtocol(proto2,@protocol(UnrelatedProto)));
+
+ desclist = protocol_copyMethodDescriptionList(proto2, YES, YES, &count);
+ testassert(!desclist && count == 0);
+ desclist = protocol_copyMethodDescriptionList(proto2, YES, NO, &count);
+ testassert(!desclist && count == 0);
+ desclist = protocol_copyMethodDescriptionList(proto2, NO, YES, &count);
+ testassert(!desclist && count == 0);
+ desclist = protocol_copyMethodDescriptionList(proto2, NO, NO, &count);
+ testassert(!desclist && count == 0);
+
+ // Immutability of existing protocols
+
+ objc_registerProtocol(proto);
+ protocol_addProtocol(proto, @protocol(SuperProto2));
+ protocol_addMethodDescription(proto, @selector(foo), "", YES, YES);
+
+ objc_registerProtocol(@protocol(SuperProto));
+ protocol_addProtocol(@protocol(SuperProto), @protocol(SuperProto2));
+ protocol_addMethodDescription(@protocol(SuperProto), @selector(foo), "", YES, YES);
+
+ // No duplicates
+
+ proto = objc_allocateProtocol("SuperProto");
+ testassert(!proto);
+ proto = objc_allocateProtocol("Proto1");
+ testassert(!proto);
+
+ // NULL protocols ignored
+
+ protocol_addProtocol((Protocol *)1, NULL);
+ protocol_addProtocol(NULL, (Protocol *)1);
+ protocol_addProtocol(NULL, NULL);
+ protocol_addMethodDescription(NULL, @selector(foo), "", YES, YES);
+
+ succeed(__FILE__);
+}
--- /dev/null
+// TEST_CFLAGS -framework Foundation
+// TEST_CONFIG GC=0
+
+#include "test.h"
+#include <Foundation/Foundation.h>
+
+static id weak;
+static id weak2;
+static bool did_dealloc;
+
+@interface Test : NSObject @end
+@implementation Test
+-(void)dealloc {
+ testassert(weak == self);
+ testassert(weak2 == self);
+
+ testprintf("Weak store fails while deallocating\n");
+ id result = objc_storeWeak(&weak, self);
+ testassert(result == NULL);
+ testassert(weak == NULL);
+
+ testprintf("Weak references clear during super dealloc\n");
+ testassert(weak2 != NULL);
+ [super dealloc];
+ testassert(weak2 == NULL);
+
+ did_dealloc = true;
+}
+@end
+
+int main()
+{
+ Test *obj = [Test new];
+ Test *obj2 = [Test new];
+ id result;
+
+ testprintf("Weak assignment\n");
+ result = objc_storeWeak(&weak, obj);
+ testassert(result == obj);
+ testassert(weak == obj);
+
+ testprintf("Weak assignment to the same value\n");
+ result = objc_storeWeak(&weak, obj);
+ testassert(result == obj);
+ testassert(weak == obj);
+
+ testprintf("Weak assignment to different value\n");
+ result = objc_storeWeak(&weak, obj2);
+ testassert(result == obj2);
+ testassert(weak == obj2);
+
+ testprintf("Weak assignment to NULL\n");
+ result = objc_storeWeak(&weak, NULL);
+ testassert(result == NULL);
+ testassert(weak == NULL);
+
+ testprintf("Weak clear\n");
+
+ result = objc_storeWeak(&weak, obj);
+ testassert(result == obj);
+ testassert(weak == obj);
+
+ result = objc_storeWeak(&weak2, obj);
+ testassert(result == obj);
+ testassert(weak2 == obj);
+
+ did_dealloc = false;
+ [obj release];
+ testassert(did_dealloc);
+ testassert(weak == NULL);
+ testassert(weak2 == NULL);
+
+ succeed(__FILE__);
+}
+// TEST_CFLAGS -framework CoreFoundation
+
#include <CoreFoundation/CoreFoundation.h>
#include <objc/runtime.h>
--- /dev/null
+// TEST_CFLAGS -framework Foundation
+
+#include "test.h"
+#include <Foundation/Foundation.h>
+#include <objc/runtime.h>
+
+static int values;
+static int subs;
+
+static const char *key = "key";
+
+
+@interface Value : NSObject @end
+@interface Super : NSObject @end
+@interface Sub : NSObject @end
+
+@implementation Super
+-(id) init
+{
+ // rdar://8270243 don't lose associations after isa swizzling
+
+ id value = [Value new];
+ objc_setAssociatedObject(self, &key, value, OBJC_ASSOCIATION_RETAIN);
+ [value release];
+
+ object_setClass(self, [Sub class]);
+
+ return self;
+}
+
+@end
+
+@implementation Sub
+-(void) dealloc
+{
+ subs++;
+ [super dealloc];
+}
+-(void) finalize
+{
+ subs++;
+ [super finalize];
+}
+@end
+
+@implementation Value
+-(void) dealloc {
+ values++;
+ [super dealloc];
+}
+-(void) finalize {
+ values++;
+ [super finalize];
+}
+@end
+
+int main()
+{
+ int i;
+ for (i = 0; i < 100; i++) {
+ [[[Super alloc] init] release];
+ }
+
+ testcollect();
+
+ testassert(subs > 0);
+ testassert(subs == values);
+
+ succeed(__FILE__);
+}
--- /dev/null
+// for OBJC2 mac only
+/* TEST_CONFIG SDK=macos ARCH=x86_64
+ TEST_CRASHES
+
+TEST_RUN_OUTPUT
+objc\[\d+\]: objc_removeExceptionHandler\(\) called with unknown alt handler; this is probably a bug in multithreaded AppKit use. Set environment variable OBJC_DEBUG_ALT_HANDLERS=YES or break in objc_alt_handler_error\(\) to debug.
+CRASHED: SIGILL
+END
+*/
+
+#include "test.h"
+
+#include <pthread.h>
+#include <objc/objc-exception.h>
+
+/*
+ rdar://6888838
+ Mail installs an alt handler on one thread and deletes it on another.
+ This confuses the alt handler machinery, which halts the process.
+*/
+
+uintptr_t Token;
+
+void handler(id unused __unused, void *context __unused)
+{
+}
+
+void *fn(void *arg __unused)
+{
+ @try {
+ Token = objc_addExceptionHandler(&handler, NULL);
+ } @catch (...) {
+ }
+
+ return NULL;
+}
+
+int main()
+{
+#if __clang__ && __cplusplus
+ // alt handlers need the objc personality
+ // catch (id) workaround forces the objc personality
+ @try {
+ testwarn("rdar://9183014 clang uses wrong exception personality");
+ } @catch (id e __unused) {
+ }
+#endif
+
+ @try {
+ // Install 4 alt handlers
+ uintptr_t t1, t2, t3, t4;
+ t1 = objc_addExceptionHandler(&handler, NULL);
+ t2 = objc_addExceptionHandler(&handler, NULL);
+ t3 = objc_addExceptionHandler(&handler, NULL);
+ t4 = objc_addExceptionHandler(&handler, NULL);
+
+ // Remove 3 of them.
+ objc_removeExceptionHandler(t1);
+ objc_removeExceptionHandler(t2);
+ objc_removeExceptionHandler(t3);
+
+ // Create an alt handler on another thread
+ // that collides with one of the removed handlers
+ pthread_t th;
+ pthread_create(&th, NULL, &fn, NULL);
+ pthread_join(th, NULL);
+
+ // Incorrectly remove the other thread's handler
+ objc_removeExceptionHandler(Token);
+ // Remove the 4th handler
+ objc_removeExceptionHandler(t4);
+
+ // Install 8 more handlers.
+ // If the other thread's handler was not ignored,
+ // this will fail.
+ objc_addExceptionHandler(&handler, NULL);
+ objc_addExceptionHandler(&handler, NULL);
+ objc_addExceptionHandler(&handler, NULL);
+ objc_addExceptionHandler(&handler, NULL);
+ objc_addExceptionHandler(&handler, NULL);
+ objc_addExceptionHandler(&handler, NULL);
+ objc_addExceptionHandler(&handler, NULL);
+ objc_addExceptionHandler(&handler, NULL);
+ } @catch (...) {
+ }
+
+ // This should have crashed earlier.
+ fail(__FILE__);
+}
--- /dev/null
+// TEST_CFLAGS -framework Foundation
+
+#include "test.h"
+#include <objc/runtime.h>
+#import <Foundation/Foundation.h>
+
+#include <Block_private.h>
+
+#if !__clang__ && !__llvm__
+ // gcc will never support struct-return marking
+# define STRET_OK 0
+#elif !clang
+// llvm-gcc waiting for rdar://8143947
+# define STRET_OK 0
+#else
+# define STRET_OK 1
+#endif
+
+typedef uint32_t (*funcptr)();
+
+typedef struct BigStruct {
+ unsigned int datums[200];
+} BigStruct;
+
+@interface Foo:NSObject
+@end
+@implementation Foo
+- (BigStruct) methodThatReturnsBigStruct: (BigStruct) b
+{
+ return b;
+}
+@end
+
+@interface Foo(bar)
+- (int) boo: (int) a;
+- (BigStruct) structThatIsBig: (BigStruct) b;
+- (BigStruct) methodThatReturnsBigStruct: (BigStruct) b;
+- (float) methodThatReturnsFloat: (float) aFloat;
+@end
+
+typedef uint32_t (*FuncPtr)(id, SEL);
+typedef BigStruct (*BigStructFuncPtr)(id, SEL, BigStruct);
+typedef float (*FloatFuncPtr)(id, SEL, float);
+
+BigStruct bigfunc(BigStruct a) {
+ return a;
+}
+
+@interface Deallocator : NSObject @end
+@implementation Deallocator
+-(void) methodThatNobodyElseCalls1 { }
+-(void) methodThatNobodyElseCalls2 { }
+-(id) retain {
+ _objc_flush_caches([Deallocator class]);
+ [self methodThatNobodyElseCalls1];
+ return [super retain];
+}
+-(void) dealloc {
+ _objc_flush_caches([Deallocator class]);
+ [self methodThatNobodyElseCalls2];
+ [super dealloc];
+}
+@end
+
+/* Code copied from objc-block-trampolines.m to test Block innards */
+typedef enum {
+ ReturnValueInRegisterArgumentMode,
+ ReturnValueOnStackArgumentMode,
+
+ ArgumentModeMax
+} ArgumentMode;
+
+static ArgumentMode _argumentModeForBlock(void *block) {
+ ArgumentMode aMode = ReturnValueInRegisterArgumentMode;
+ if ( _Block_use_stret(block) )
+ aMode = ReturnValueOnStackArgumentMode;
+
+ return aMode;
+}
+/* End copied code */
+
+int main () {
+#if __llvm__ && !__clang__
+ // edit STRET_OK above when you remove this
+ testwarn("<rdar://8143947> struct-return blocks not yet integrated in llvm-gcc");
+#endif
+
+ // make sure the bits are in place
+ int (^registerReturn)() = ^(){ return 42; };
+ ArgumentMode aMode;
+
+ aMode = _argumentModeForBlock(registerReturn);
+ testassert(aMode == ReturnValueInRegisterArgumentMode);
+
+#if STRET_OK
+ BigStruct (^stackReturn)() = ^() { BigStruct k; return k; };
+ aMode = _argumentModeForBlock(stackReturn);
+ testassert(aMode == ReturnValueOnStackArgumentMode);
+#endif
+
+#define TEST_QUANTITY 100000
+ static FuncPtr funcArray[TEST_QUANTITY];
+
+ uint32_t i;
+ for(i = 0; i<TEST_QUANTITY; i++) {
+ uint32_t (^block)(id self) = ^uint32_t(id self) {
+ testassert((vm_address_t) self == (vm_address_t) i);
+ return i;
+ };
+ block = Block_copy(block);
+
+ funcArray[i] = (FuncPtr) imp_implementationWithBlock(block);
+
+ testassert(block((id)(uintptr_t) i) == i);
+
+ void *blockFromIMPResult = imp_getBlock((IMP)funcArray[i]);
+ testassert(blockFromIMPResult == block);
+
+ Block_release(block);
+ }
+
+ for(i = 0; i<TEST_QUANTITY; i++) {
+ uint32_t result = funcArray[i]((id)(uintptr_t) i, 0);
+ testassert(i == result);
+ }
+
+ for(i = 0; i < TEST_QUANTITY; i= i + 3) {
+ imp_removeBlock((IMP)funcArray[i]);
+ void *shouldBeNull = imp_getBlock((IMP)funcArray[i]);
+ assert(shouldBeNull == NULL);
+ }
+
+ for(i = 0; i < TEST_QUANTITY; i= i + 3) {
+ uint32_t j = i * i;
+
+ uint32_t (^block)(id self) = ^uint32_t(id self) {
+ uint32_t value = (uint32_t)(uintptr_t) self;
+ testassert(j == value);
+ return j;
+ };
+ funcArray[i] = (FuncPtr) imp_implementationWithBlock(block);
+
+ testassert(block((id)(uintptr_t)j) == j);
+ testassert(funcArray[i]((id)(uintptr_t)j, 0) == j);
+ }
+
+ for(i = 0; i < TEST_QUANTITY; i= i + 3) {
+ uint32_t j = i * i;
+ uint32_t result = funcArray[i]((id)(uintptr_t) j, 0);
+ testassert(j == result);
+ }
+
+ int (^implBlock)(id, int);
+
+ implBlock = ^(id self __attribute__((unused)), int a){
+ return -1 * a;
+ };
+
+ NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
+
+ IMP methodImp = imp_implementationWithBlock(implBlock);
+
+ BOOL success = class_addMethod([Foo class], @selector(boo:), methodImp, "i@:i");
+ if (!success) {
+ fprintf(stdout, "class_addMethod failed\n");
+ abort();
+ }
+ Foo *f = [Foo new];
+ int (*impF)(id self, SEL _cmd, int x) = (int(*)(id, SEL, int)) [Foo instanceMethodForSelector: @selector(boo:)];
+
+ int x = impF(f, @selector(boo:), -42);
+
+ testassert(x == 42);
+ testassert([f boo: -42] == 42);
+
+#if STRET_OK
+ BigStruct a;
+ for(i=0; i<200; i++)
+ a.datums[i] = i;
+
+ // slightly more straightforward here
+ __block unsigned int state = 0;
+ BigStruct (^structBlock)(id, BigStruct) = ^BigStruct(id self __attribute__((unused)), BigStruct c) {
+ state++;
+ return c;
+ };
+ BigStruct blockDirect = structBlock(nil, a);
+ testassert(!memcmp(&a, &blockDirect, sizeof(BigStruct)));
+ testassert(state==1);
+
+ IMP bigStructIMP = imp_implementationWithBlock(structBlock);
+
+ class_addMethod([Foo class], @selector(structThatIsBig:), bigStructIMP, "oh, type strings, how I hate thee. Fortunately, the runtime doesn't generally care.");
+
+ BigStruct b;
+
+ BigStructFuncPtr bFunc;
+
+ b = bigfunc(a);
+ testassert(!memcmp(&a, &b, sizeof(BigStruct)));
+ b = bigfunc(a);
+ testassert(!memcmp(&a, &b, sizeof(BigStruct)));
+
+ bFunc = (BigStructFuncPtr) [Foo instanceMethodForSelector: @selector(methodThatReturnsBigStruct:)];
+
+ b = bFunc(f, @selector(methodThatReturnsBigStruct:), a);
+ testassert(!memcmp(&a, &b, sizeof(BigStruct)));
+
+ b = [f methodThatReturnsBigStruct: a];
+ testassert(!memcmp(&a, &b, sizeof(BigStruct)));
+
+ bFunc = (BigStructFuncPtr) [Foo instanceMethodForSelector: @selector(structThatIsBig:)];
+
+ b = bFunc(f, @selector(structThatIsBig:), a);
+ testassert(!memcmp(&a, &b, sizeof(BigStruct)));
+ testassert(state==2);
+
+ b = [f structThatIsBig: a];
+ testassert(!memcmp(&a, &b, sizeof(BigStruct)));
+ testassert(state==3);
+// STRET_OK
+#endif
+
+
+ IMP floatIMP = imp_implementationWithBlock(^float (id self __attribute__((unused)), float aFloat ) {
+ return aFloat;
+ });
+ class_addMethod([Foo class], @selector(methodThatReturnsFloat:), floatIMP, "ooh.. type string unspecified again... oh noe... runtime might punish. not.");
+
+ float e = (float)0.001;
+ float retF = (float)[f methodThatReturnsFloat: 37.1212f];
+ testassert( ((retF - e) < 37.1212) && ((retF + e) > 37.1212) );
+
+
+ // Make sure imp_implementationWithBlock() and imp_removeBlock()
+ // don't deadlock while calling Block_copy() and Block_release()
+ Deallocator *dead = [[Deallocator alloc] init];
+ IMP deadlockImp = imp_implementationWithBlock(^{ [dead self]; });
+ [dead release];
+ imp_removeBlock(deadlockImp);
+
+ [p drain];
+ succeed(__FILE__);
+}
+
+/*
+TEST_BUILD
+ $C{COMPILE} $DIR/cacheflush0.m -o cacheflush0.dylib -dynamiclib
+ $C{COMPILE} $DIR/cacheflush2.m -x none cacheflush0.dylib -o cacheflush2.dylib -dynamiclib
+ $C{COMPILE} $DIR/cacheflush3.m -x none cacheflush0.dylib -o cacheflush3.dylib -dynamiclib
+ $C{COMPILE} $DIR/cacheflush.m -x none cacheflush0.dylib -o cacheflush.out
+END
+*/
+
#include "test.h"
#include <objc/runtime.h>
#include <dlfcn.h>
testassert(1 == [(Sub *)buf2 instanceMethod]);
// Dynamically load a category
- dlopen("cacheflush2.out", 0);
+ dlopen("cacheflush2.dylib", 0);
// Make sure old cache results are gone
testassert(2 == [Super classMethod]);
testassert(2 == [(Sub *)buf2 instanceMethod]);
// Dynamically load another category
- dlopen("cacheflush3.out", 0);
+ dlopen("cacheflush3.dylib", 0);
// Make sure old cache results are gone
testassert(3 == [Super classMethod]);
+// TEST_CONFIG
+
#include "test.h"
#include <string.h>
#include <objc/objc-runtime.h>
--- /dev/null
+// TEST_CONFIG
+
+#include <pthread.h>
+#include "test.h"
+#include "objc/objc-internal.h"
+
+static unsigned ctors1 = 0;
+static unsigned dtors1 = 0;
+static unsigned ctors2 = 0;
+static unsigned dtors2 = 0;
+
+class cxx1 {
+ unsigned & ctors;
+ unsigned& dtors;
+
+ public:
+ cxx1() : ctors(ctors1), dtors(dtors1) { ctors++; }
+
+ ~cxx1() { dtors++; }
+};
+class cxx2 {
+ unsigned& ctors;
+ unsigned& dtors;
+
+ public:
+ cxx2() : ctors(ctors2), dtors(dtors2) { ctors++; }
+
+ ~cxx2() { dtors++; }
+};
+
+/*
+ Class hierarchy:
+ Base
+ CXXBase
+ NoCXXSub
+ CXXSub
+
+ This has two cxx-wielding classes, and a class in between without cxx.
+*/
+
+
+@interface Base { id isa; }
++class;
++new;
+-(void)dealloc;
+@end
+@implementation Base
++(void)initialize { }
++class { return self; }
+-class { return self->isa; }
++new { return class_createInstance(self, 0); }
+-(void)dealloc { object_dispose(self); }
+-(void)finalize { }
+@end
+
+@interface CXXBase : Base {
+ cxx1 baseIvar;
+}
+@end
+@implementation CXXBase @end
+
+@interface NoCXXSub : CXXBase {
+ int nocxxIvar;
+}
+@end
+@implementation NoCXXSub @end
+
+@interface CXXSub : NoCXXSub {
+ cxx2 subIvar;
+}
+@end
+@implementation CXXSub @end
+
+
+void *test_single(void *arg __unused)
+{
+ volatile id o;
+
+ // Single allocation
+
+ objc_registerThreadWithCollector();
+
+ ctors1 = dtors1 = ctors2 = dtors2 = 0;
+ o = [Base new];
+ testassert(ctors1 == 0 && dtors1 == 0 &&
+ ctors2 == 0 && dtors2 == 0);
+ testassert([o class] == [Base class]);
+ [o dealloc], o = nil;
+ testcollect();
+ testassert(ctors1 == 0 && dtors1 == 0 &&
+ ctors2 == 0 && dtors2 == 0);
+
+ ctors1 = dtors1 = ctors2 = dtors2 = 0;
+ o = [CXXBase new];
+ testassert(ctors1 == 1 && dtors1 == 0 &&
+ ctors2 == 0 && dtors2 == 0);
+ testassert([o class] == [CXXBase class]);
+ [o dealloc], o = nil;
+ testcollect();
+ testassert(ctors1 == 1 && dtors1 == 1 &&
+ ctors2 == 0 && dtors2 == 0);
+
+ ctors1 = dtors1 = ctors2 = dtors2 = 0;
+ o = [NoCXXSub new];
+ testassert(ctors1 == 1 && dtors1 == 0 &&
+ ctors2 == 0 && dtors2 == 0);
+ testassert([o class] == [NoCXXSub class]);
+ [o dealloc], o = nil;
+ testcollect();
+ testassert(ctors1 == 1 && dtors1 == 1 &&
+ ctors2 == 0 && dtors2 == 0);
+
+ ctors1 = dtors1 = ctors2 = dtors2 = 0;
+ o = [CXXSub new];
+ testassert(ctors1 == 1 && dtors1 == 0 &&
+ ctors2 == 1 && dtors2 == 0);
+ testassert([o class] == [CXXSub class]);
+ [o dealloc], o = nil;
+ testcollect();
+ testassert(ctors1 == 1 && dtors1 == 1 &&
+ ctors2 == 1 && dtors2 == 1);
+
+ return NULL;
+}
+
+void *test_inplace(void *arg __unused)
+{
+ volatile id o;
+ char o2[64];
+
+ // In-place allocation
+
+ objc_registerThreadWithCollector();
+
+ ctors1 = dtors1 = ctors2 = dtors2 = 0;
+ o = objc_constructInstance([Base class], o2);
+ testassert(ctors1 == 0 && dtors1 == 0 &&
+ ctors2 == 0 && dtors2 == 0);
+ testassert([o class] == [Base class]);
+ objc_destructInstance(o), o = nil;
+ testcollect();
+ testassert(ctors1 == 0 && dtors1 == 0 &&
+ ctors2 == 0 && dtors2 == 0);
+
+ ctors1 = dtors1 = ctors2 = dtors2 = 0;
+ o = objc_constructInstance([CXXBase class], o2);
+ testassert(ctors1 == 1 && dtors1 == 0 &&
+ ctors2 == 0 && dtors2 == 0);
+ testassert([o class] == [CXXBase class]);
+ objc_destructInstance(o), o = nil;
+ testcollect();
+ testassert(ctors1 == 1 && dtors1 == 1 &&
+ ctors2 == 0 && dtors2 == 0);
+
+ ctors1 = dtors1 = ctors2 = dtors2 = 0;
+ o = objc_constructInstance([NoCXXSub class], o2);
+ testassert(ctors1 == 1 && dtors1 == 0 &&
+ ctors2 == 0 && dtors2 == 0);
+ testassert([o class] == [NoCXXSub class]);
+ objc_destructInstance(o), o = nil;
+ testcollect();
+ testassert(ctors1 == 1 && dtors1 == 1 &&
+ ctors2 == 0 && dtors2 == 0);
+
+ ctors1 = dtors1 = ctors2 = dtors2 = 0;
+ o = objc_constructInstance([CXXSub class], o2);
+ testassert(ctors1 == 1 && dtors1 == 0 &&
+ ctors2 == 1 && dtors2 == 0);
+ testassert([o class] == [CXXSub class]);
+ objc_destructInstance(o), o = nil;
+ testcollect();
+ testassert(ctors1 == 1 && dtors1 == 1 &&
+ ctors2 == 1 && dtors2 == 1);
+
+ return NULL;
+}
+
+
+void *test_batch(void *arg __unused)
+{
+ id o2[100];
+ unsigned int count, i;
+
+ // Batch allocation
+
+ objc_registerThreadWithCollector();
+
+ for (i = 0; i < 100; i++) {
+ o2[i] = (id)malloc(class_getInstanceSize([Base class]));
+ }
+ for (i = 0; i < 100; i++) {
+ free(o2[i]);
+ }
+
+ ctors1 = dtors1 = ctors2 = dtors2 = 0;
+ count = class_createInstances([Base class], 0, o2, 10);
+ testassert(count > 0);
+ testassert(ctors1 == 0 && dtors1 == 0 &&
+ ctors2 == 0 && dtors2 == 0);
+ for (i = 0; i < count; i++) testassert([o2[i] class] == [Base class]);
+ for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
+ testcollect();
+ testassert(ctors1 == 0 && dtors1 == 0 &&
+ ctors2 == 0 && dtors2 == 0);
+
+ for (i = 0; i < 100; i++) {
+ // prime batch allocator
+ free(malloc(class_getInstanceSize([Base class])));
+ }
+
+ ctors1 = dtors1 = ctors2 = dtors2 = 0;
+ count = class_createInstances([CXXBase class], 0, o2, 10);
+ testassert(count > 0);
+ testassert(ctors1 == count && dtors1 == 0 &&
+ ctors2 == 0 && dtors2 == 0);
+ for (i = 0; i < count; i++) testassert([o2[i] class] == [CXXBase class]);
+ for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
+ testcollect();
+ testassert(ctors1 == count && dtors1 == count &&
+ ctors2 == 0 && dtors2 == 0);
+
+ for (i = 0; i < 100; i++) {
+ // prime batch allocator
+ free(malloc(class_getInstanceSize([Base class])));
+ }
+
+ ctors1 = dtors1 = ctors2 = dtors2 = 0;
+ count = class_createInstances([NoCXXSub class], 0, o2, 10);
+ testassert(count > 0);
+ testassert(ctors1 == count && dtors1 == 0 &&
+ ctors2 == 0 && dtors2 == 0);
+ for (i = 0; i < count; i++) testassert([o2[i] class] == [NoCXXSub class]);
+ for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
+ testcollect();
+ testassert(ctors1 == count && dtors1 == count &&
+ ctors2 == 0 && dtors2 == 0);
+
+ for (i = 0; i < 100; i++) {
+ // prime batch allocator
+ free(malloc(class_getInstanceSize([Base class])));
+ }
+
+ ctors1 = dtors1 = ctors2 = dtors2 = 0;
+ count = class_createInstances([CXXSub class], 0, o2, 10);
+ testassert(count > 0);
+ testassert(ctors1 == count && dtors1 == 0 &&
+ ctors2 == count && dtors2 == 0);
+ for (i = 0; i < count; i++) testassert([o2[i] class] == [CXXSub class]);
+ for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
+ testcollect();
+ testassert(ctors1 == count && dtors1 == count &&
+ ctors2 == count && dtors2 == count);
+
+ return NULL;
+}
+
+int main()
+{
+ pthread_t th;
+
+ testassert(0 == pthread_create(&th, NULL, test_single, NULL));
+ pthread_join(th, NULL);
+
+ testassert(0 == pthread_create(&th, NULL, test_inplace, NULL));
+ pthread_join(th, NULL);
+
+ leak_mark();
+
+ testassert(0 == pthread_create(&th, NULL, test_batch, NULL));
+ pthread_join(th, NULL);
+
+ // fixme can't get this to zero; may or may not be a real leak
+ leak_check(64);
+
+ // fixme ctor exceptions aren't caught inside .cxx_construct ?
+ // Single allocation, ctors fail
+ // In-place allocation, ctors fail
+ // Batch allocation, ctors fail for every object
+ // Batch allocation, ctors fail for every other object
+
+ succeed(__FILE__);
+}
+// TEST_CFLAGS -framework Foundation
+
#include "test.h"
#include <objc/objc-runtime.h>
+#include <objc/objc-gdb.h>
#import <Foundation/Foundation.h>
@interface Foo:NSObject
@implementation Foo
@end
-extern Class gdb_class_getClass(Class cls);
-
int main()
{
#if __OBJC2__
+// TEST_CONFIG
+
#include "test.h"
#include <string.h>
#include <objc/objc-runtime.h>
testassert(obj->isa == [Super class]);
testassert(object_setClass(nil, [Super class]) == nil);
+ bzero(buf, sizeof(buf));
+ testassert(object_setClass(obj, [Super class]) == nil);
+
testassert(object_getClass(obj) == buf[0]);
testassert(object_getClass([Super class]) == [Super class]->isa);
testassert(object_getClass(nil) == Nil);
+// TEST_CFLAGS -Wno-deprecated-declarations
+
#include "test.h"
#include <objc/runtime.h>
#include <string.h>
+(void) classMethod2;
@end
-@interface Super { @public id isa; } @end
+static int super_initialize;
+
+@interface Super { @public id isa; }
+@property int superProp;
+@end
@implementation Super
-+(void)initialize { }
+@dynamic superProp;
++(void)initialize { super_initialize++; }
+class { return self; }
+(id) new { return class_createInstance(self, 0); }
-(void) free { object_dispose(self); }
state++;
}
+static void fail_fn(id self __attribute__((unused)), SEL _cmd)
+{
+ fail("fail_fn '%s' called", sel_getName(_cmd));
+}
+
static void cycle(void)
{
Class cls;
BOOL ok;
+ objc_property_t prop;
+ char namebuf[256];
testassert(!objc_getClass("Sub"));
testassert([Super class]);
cls = objc_allocateClassPair([Super class], "Sub", 0);
testassert(cls);
#ifndef OBJC_NO_GC
- if (objc_collecting_enabled()) {
- testassert(auto_zone_size(auto_zone(), cls));
- testassert(auto_zone_size(auto_zone(), cls->isa));
+ if (objc_collectingEnabled()) {
+ testassert(auto_zone_size(objc_collectableZone(), cls));
+ testassert(auto_zone_size(objc_collectableZone(), cls->isa));
}
#endif
(IMP)&instance_fn, "v@:");
class_addMethod(cls->isa, @selector(classMethod),
(IMP)&class_fn, "v@:");
+ class_addMethod(cls->isa, @selector(initialize),
+ (IMP)&class_fn, "v@:");
+ class_addMethod(cls->isa, @selector(load),
+ (IMP)&fail_fn, "v@:");
ok = class_addProtocol(cls, @protocol(Proto));
testassert(ok);
ok = class_addProtocol(cls, @protocol(Proto));
testassert(!ok);
+ char attrname[2];
+ char attrvalue[2];
+ objc_property_attribute_t attrs[1];
+ unsigned int attrcount = sizeof(attrs) / sizeof(attrs[0]);
+
+ attrs[0].name = attrname;
+ attrs[0].value = attrvalue;
+ strcpy(attrname, "T");
+ strcpy(attrvalue, "x");
+
+ strcpy(namebuf, "subProp");
+ ok = class_addProperty(cls, namebuf, attrs, attrcount);
+ testassert(ok);
+ strcpy(namebuf, "subProp");
+ ok = class_addProperty(cls, namebuf, attrs, attrcount);
+ testassert(!ok);
+ strcpy(attrvalue, "i");
+ class_replaceProperty(cls, namebuf, attrs, attrcount);
+ strcpy(namebuf, "superProp");
+ ok = class_addProperty(cls, namebuf, attrs, attrcount);
+ testassert(!ok);
+ bzero(namebuf, sizeof(namebuf));
+ bzero(attrs, sizeof(attrs));
+ bzero(attrname, sizeof(attrname));
+ bzero(attrvalue, sizeof(attrvalue));
+
#ifndef __LP64__
# define size 4
# define align 2
# define align 3
#endif
+ /*
+ {
+ int ivar;
+ id ivarid;
+ id* ivaridstar;
+ Block_t ivarblock;
+ }
+ */
ok = class_addIvar(cls, "ivar", 4, 2, "i");
testassert(ok);
ok = class_addIvar(cls, "ivarid", size, align, "@");
testassert(ok);
ok = class_addIvar(cls, "ivaridstar", size, align, "^@");
testassert(ok);
+ ok = class_addIvar(cls, "ivarblock", size, align, "@?");
+ testassert(ok);
+
ok = class_addIvar(cls, "ivar", 4, 2, "i");
testassert(!ok);
ok = class_addIvar(cls->isa, "classvar", 4, 2, "i");
objc_registerClassPair(cls);
-
+ // should call cls's +initialize, not super's
+ super_initialize = 0;
+ state = 0;
+ [cls class];
+ testassert(super_initialize == 0);
+ testassert(state == 1);
+
testassert(cls == [cls class]);
testassert(cls == objc_getClass("Sub"));
testassert(class_getSuperclass(cls) == [Super class]);
testassert(class_getSuperclass(cls->isa) == [Super class]->isa);
- testassert(class_getInstanceSize(cls) >= sizeof(Class) + 4 + 2*size);
+ testassert(class_getInstanceSize(cls) >= sizeof(Class) + 4 + 3*size);
testassert(class_conformsToProtocol(cls, @protocol(Proto)));
- if (objc_collecting_enabled()) {
- testassert(0 == strcmp(class_getIvarLayout(cls), "\x01\x12"));
+ if (objc_collectingEnabled()) {
+ testassert(0 == strcmp((char *)class_getIvarLayout(cls), "\x01\x13"));
testassert(NULL == class_getWeakIvarLayout(cls));
}
ok = class_addProtocol(cls, @protocol(Proto));
testassert(!ok);
+ attrs[0].name = attrname;
+ attrs[0].value = attrvalue;
+ strcpy(attrname, "T");
+ strcpy(attrvalue, "i");
+
+ strcpy(namebuf, "subProp2");
+ ok = class_addProperty(cls, namebuf, attrs, attrcount);
+ testassert(ok);
+ strcpy(namebuf, "subProp");
+ ok = class_addProperty(cls, namebuf, attrs, attrcount);
+ testassert(!ok);
+ strcpy(namebuf, "superProp");
+ ok = class_addProperty(cls, namebuf, attrs, attrcount);
+ testassert(!ok);
+ bzero(namebuf, sizeof(namebuf));
+ bzero(attrs, sizeof(attrs));
+ bzero(attrname, sizeof(attrname));
+ bzero(attrvalue, sizeof(attrvalue));
+
+ prop = class_getProperty(cls, "subProp");
+ testassert(prop);
+ testassert(0 == strcmp(property_getName(prop), "subProp"));
+ testassert(0 == strcmp(property_getAttributes(prop), "Ti"));
+ prop = class_getProperty(cls, "subProp2");
+ testassert(prop);
+ testassert(0 == strcmp(property_getName(prop), "subProp2"));
+ testassert(0 == strcmp(property_getAttributes(prop), "Ti"));
+
// note: adding more methods here causes a false leak check failure
state = 0;
[cls classMethod];
testassert(state == 2);
[obj free];
-
// Test ivar layouts of sub-subclass
Class cls2 = objc_allocateClassPair(cls, "SubSub", 0);
testassert(cls2);
-
+
+ /*
+ {
+ id ivarid2;
+ id idarray[16];
+ void* ptrarray[16];
+ char a;
+ char b;
+ char c;
+ }
+ */
ok = class_addIvar(cls2, "ivarid2", size, align, "@");
testassert(ok);
ok = class_addIvar(cls2, "idarray", 16*sizeof(id), align, "[16@]");
testassert(ok);
- ok = class_addIvar(cls2, "intarray", 16*sizeof(void*), align, "[16^]");
+ ok = class_addIvar(cls2, "ptrarray", 16*sizeof(void*), align, "[16^]");
+ testassert(ok);
+ ok = class_addIvar(cls2, "a", 1, 0, "c");
+ testassert(ok);
+ ok = class_addIvar(cls2, "b", 1, 0, "c");
+ testassert(ok);
+ ok = class_addIvar(cls2, "c", 1, 0, "c");
testassert(ok);
objc_registerClassPair(cls2);
- if (objc_collecting_enabled()) {
- testassert(0 == strcmp((char *)class_getIvarLayout(cls2), "\x01\x1f\x04\xf0\x10"));
+ if (objc_collectingEnabled()) {
+ testassert(0 == strcmp((char *)class_getIvarLayout(cls2), "\x01\x1f\x05\xf0\x10"));
testassert(NULL == class_getWeakIvarLayout(cls2));
}
+ // 1-byte ivars should be well packed
+ testassert(ivar_getOffset(class_getInstanceVariable(cls2, "b")) ==
+ ivar_getOffset(class_getInstanceVariable(cls2, "a")) + 1);
+ testassert(ivar_getOffset(class_getInstanceVariable(cls2, "c")) ==
+ ivar_getOffset(class_getInstanceVariable(cls2, "b")) + 1);
+
objc_disposeClassPair(cls2);
objc_disposeClassPair(cls);
cls = objc_allocateClassPair([Super class], "Sub2", 0);
testassert(cls);
objc_registerClassPair(cls);
- if (objc_collecting_enabled()) {
+ if (objc_collectingEnabled()) {
const char *l1, *l2;
- l1 = class_getIvarLayout([Super class]);
- l2 = class_getIvarLayout(cls);
+ l1 = (char *)class_getIvarLayout([Super class]);
+ l2 = (char *)class_getIvarLayout(cls);
testassert(l1 == l2 || 0 == strcmp(l1, l2));
- l1 = class_getWeakIvarLayout([Super class]);
- l2 = class_getWeakIvarLayout(cls);
+ l1 = (char *)class_getWeakIvarLayout([Super class]);
+ l2 = (char *)class_getWeakIvarLayout(cls);
testassert(l1 == l2 || 0 == strcmp(l1, l2));
}
objc_disposeClassPair(cls);
cls = objc_allocateClassPair([WeakSuper class], "Sub3", 0);
testassert(cls);
objc_registerClassPair(cls);
- if (objc_collecting_enabled()) {
+ if (objc_collectingEnabled()) {
const char *l1, *l2;
- l1 = class_getIvarLayout([WeakSuper class]);
- l2 = class_getIvarLayout(cls);
+ l1 = (char *)class_getIvarLayout([WeakSuper class]);
+ l2 = (char *)class_getIvarLayout(cls);
testassert(l1 == l2 || 0 == strcmp(l1, l2));
- l1 = class_getWeakIvarLayout([WeakSuper class]);
- l2 = class_getWeakIvarLayout(cls);
+ l1 = (char *)class_getWeakIvarLayout([WeakSuper class]);
+ l2 = (char *)class_getWeakIvarLayout(cls);
testassert(l1 == l2 || 0 == strcmp(l1, l2));
}
objc_disposeClassPair(cls);
// Test layout setters
- if (objc_collecting_enabled()) {
+ if (objc_collectingEnabled()) {
cls = objc_allocateClassPair([Super class], "Sub4", 0);
testassert(cls);
- class_setIvarLayout(cls, "foo");
+ class_setIvarLayout(cls, (uint8_t *)"foo");
class_setWeakIvarLayout(cls, NULL);
objc_registerClassPair(cls);
- testassert(0 == strcmp("foo", class_getIvarLayout(cls)));
+ testassert(0 == strcmp("foo", (char *)class_getIvarLayout(cls)));
testassert(NULL == class_getWeakIvarLayout(cls));
objc_disposeClassPair(cls);
cls = objc_allocateClassPair([Super class], "Sub5", 0);
testassert(cls);
class_setIvarLayout(cls, NULL);
- class_setWeakIvarLayout(cls, "bar");
+ class_setWeakIvarLayout(cls, (uint8_t *)"bar");
objc_registerClassPair(cls);
testassert(NULL == class_getIvarLayout(cls));
- testassert(0 == strcmp("bar", class_getWeakIvarLayout(cls)));
+ testassert(0 == strcmp("bar", (char *)class_getWeakIvarLayout(cls)));
objc_disposeClassPair(cls);
}
}
int main()
{
- int count = 100;
+ int count = 1000;
cycle();
leak_mark();
while (count--) {
+// TEST_CONFIG
+
#include "test.h"
#include <objc/objc-runtime.h>
+/*
+TEST_BUILD
+ $C{COMPILE} $DIR/concurrentcat.m -o concurrentcat.out -framework Foundation
+
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc1 -o cc1.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc2 -o cc2.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc3 -o cc3.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc4 -o cc4.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc5 -o cc5.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc6 -o cc6.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc7 -o cc7.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc8 -o cc8.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc9 -o cc9.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc10 -o cc10.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc11 -o cc11.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc12 -o cc12.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc13 -o cc13.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc14 -o cc14.dylib
+ $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc15 -o cc15.dylib
+END
+*/
+
#include "test.h"
#include <objc/runtime.h>
+#include <objc/objc-auto.h>
#include <dlfcn.h>
#include <unistd.h>
#include <pthread.h>
+(id)new {
return class_createInstance(self, 0);
}
-- (void) m0; { fail("shoulda been loaded!"); }
-- (void) m1; { fail("shoulda been loaded!"); }
-- (void) m2; { fail("shoulda been loaded!"); }
-- (void) m3; { fail("shoulda been loaded!"); }
-- (void) m4; { fail("shoulda been loaded!"); }
-- (void) m5; { fail("shoulda been loaded!"); }
-- (void) m6; { fail("shoulda been loaded!"); }
+- (void) m0 { fail("shoulda been loaded!"); }
+- (void) m1 { fail("shoulda been loaded!"); }
+- (void) m2 { fail("shoulda been loaded!"); }
+- (void) m3 { fail("shoulda been loaded!"); }
+- (void) m4 { fail("shoulda been loaded!"); }
+- (void) m5 { fail("shoulda been loaded!"); }
+- (void) m6 { fail("shoulda been loaded!"); }
@end
void *threadFun(void *aTargetClassName) {
- const char *className = aTargetClassName;
+ const char *className = (const char *)aTargetClassName;
objc_registerThreadWithCollector();
for(i=1; i<16; i++) {
pthread_t t;
char dlName[100];
- sprintf(dlName, "cc%d.out", i);
+ sprintf(dlName, "cc%d.dylib", i);
dylib = dlopen(dlName, RTLD_LAZY);
char className[100];
sprintf(className, "cc%d", i);
@end
@implementation TN
-- (void) m1; { [super m1]; }
-- (void) m3; { [self m1]; }
+- (void) m1 { [super m1]; }
+- (void) m3 { [self m1]; }
- (void) m2
{
@end
@implementation TargetClass(LoadedMethods)
-- (void) m0;{ ; }
-- (void) m1;{ ; }
-- (void) m2;{ ; }
-- (void) m3;{ ; }
-- (void) m4;{ ; }
-- (void) m5;{ ; }
-- (void) m6;{ ; }
-- (void) m7;{ ; }
-- (void) m8;{ ; }
-- (void) m9;{ ; }
-- (void) m10;{ ; }
-- (void) m11;{ ; }
-- (void) m12;{ ; }
-- (void) m13;{ ; }
-- (void) m14;{ ; }
-- (void) m15;{ ; }
+- (void) m0 { ; }
+- (void) m1 { ; }
+- (void) m2 { ; }
+- (void) m3 { ; }
+- (void) m4 { ; }
+- (void) m5 { ; }
+- (void) m6 { ; }
+- (void) m7 { ; }
+- (void) m8 { ; }
+- (void) m9 { ; }
+- (void) m10 { ; }
+- (void) m11 { ; }
+- (void) m12 { ; }
+- (void) m13 { ; }
+- (void) m14 { ; }
+- (void) m15 { ; }
@end
+// TEST_CONFIG
+
#include "test.h"
#include <string.h>
#include <malloc/malloc.h>
+// TEST_CONFIG
+
#include "test.h"
#include <malloc/malloc.h>
#include <objc/objc-runtime.h>
+#if __OBJC2__
+// methods added by the runtime: +initialize
+# define MC 1 // class methods
+# define MI 0 // instance methods
+#else
+// no magic
+# define MC 0
+# define MI 0
+#endif
+
@interface SuperMethods { } @end
@implementation SuperMethods
-+(void)SuperMethodClass { }
-+(void)SuperMethodClass2 { }
--(void)SuperMethodInstance { }
--(void)SuperMethodInstance2 { }
++(BOOL)SuperMethodClass { return NO; }
++(BOOL)SuperMethodClass2 { return NO; }
+-(BOOL)SuperMethodInstance { return NO; }
+-(BOOL)SuperMethodInstance2 { return NO; }
@end
@interface SubMethods { } @end
@implementation SubMethods
-+(void)SubMethodClass { }
-+(void)SubMethodClass2 { }
--(void)SubMethodInstance { }
--(void)SubMethodInstance2 { }
++(BOOL)SubMethodClass { return NO; }
++(BOOL)SubMethodClass2 { return NO; }
+-(BOOL)SubMethodInstance { return NO; }
+-(BOOL)SubMethodInstance2 { return NO; }
@end
@interface SuperMethods (Category) @end
@implementation SuperMethods (Category)
-+(void)SuperMethodClassCat { }
-+(void)SuperMethodClassCat2 { }
--(void)SuperMethodInstanceCat { }
--(void)SuperMethodInstanceCat2 { }
++(BOOL)SuperMethodClass { return YES; }
++(BOOL)SuperMethodClass2 { return YES; }
+-(BOOL)SuperMethodInstance { return YES; }
+-(BOOL)SuperMethodInstance2 { return YES; }
@end
@interface SubMethods (Category) @end
@implementation SubMethods (Category)
-+(void)SubMethodClassCat { }
-+(void)SubMethodClassCat2 { }
--(void)SubMethodInstanceCat { }
--(void)SubMethodInstanceCat2 { }
++(BOOL)SubMethodClass { return YES; }
++(BOOL)SubMethodClass2 { return YES; }
+-(BOOL)SubMethodInstance { return YES; }
+-(BOOL)SubMethodInstance2 { return YES; }
@end
@interface NoMethods @end
@implementation NoMethods @end
-static int isNamed(Method m, const char *name)
+static void checkReplacement(Method *list, const char *name)
{
- return (method_getName(m) == sel_registerName(name));
+ Method first = NULL, second = NULL;
+ SEL sel = sel_registerName(name);
+ int i;
+
+ testassert(list);
+
+ // Find the methods. There should be two.
+ for (i = 0; list[i]; i++) {
+ if (method_getName(list[i]) == sel) {
+ if (!first) first = list[i];
+ else if (!second) second = list[i];
+ else testassert(0);
+ }
+ }
+
+ // Call the methods. The first should be the category (returns YES).
+ BOOL isCat;
+ isCat = ((BOOL(*)(id, Method))method_invoke)(NULL, first);
+ testassert(isCat);
+ isCat = ((BOOL(*)(id, Method))method_invoke)(NULL, second);
+ testassert(! isCat);
}
int main()
count = 100;
methods = class_copyMethodList(cls, &count);
testassert(methods);
- testassert(count == 4);
- // First two methods should be the category methods,
- // followed by the class methods
- testassert((isNamed(methods[0], "SubMethodInstanceCat") &&
- isNamed(methods[1], "SubMethodInstanceCat2"))
- ||
- (isNamed(methods[1], "SubMethodInstanceCat") &&
- isNamed(methods[0], "SubMethodInstanceCat2")));
- testassert((isNamed(methods[2], "SubMethodInstance") &&
- isNamed(methods[3], "SubMethodInstance2"))
- ||
- (isNamed(methods[3], "SubMethodInstance") &&
- isNamed(methods[2], "SubMethodInstance2")));
+ testassert(count == 4+MI);
// methods[] should be null-terminated
- testassert(methods[4] == NULL);
+ testassert(methods[4+MI] == NULL);
+ // Class and category methods may be mixed in the method list thanks
+ // to linker / shared cache sorting, but a category's replacement should
+ // always precede the class's implementation.
+ checkReplacement(methods, "SubMethodInstance");
+ checkReplacement(methods, "SubMethodInstance2");
free(methods);
testprintf("calling class_copyMethodList(SubMethods(meta)) (should be unmethodized)\n");
count = 100;
methods = class_copyMethodList(cls->isa, &count);
testassert(methods);
- testassert(count == 4);
- // First two methods should be the category methods,
- // followed by the class methods
- testassert((isNamed(methods[0], "SubMethodClassCat") &&
- isNamed(methods[1], "SubMethodClassCat2"))
- ||
- (isNamed(methods[1], "SubMethodClassCat") &&
- isNamed(methods[0], "SubMethodClassCat2")));
- testassert((isNamed(methods[2], "SubMethodClass") &&
- isNamed(methods[3], "SubMethodClass2"))
- ||
- (isNamed(methods[3], "SubMethodClass") &&
- isNamed(methods[2], "SubMethodClass2")));
+ testassert(count == 4+MC);
// methods[] should be null-terminated
- testassert(methods[4] == NULL);
+ testassert(methods[4+MC] == NULL);
+ // Class and category methods may be mixed in the method list thanks
+ // to linker / shared cache sorting, but a category's replacement should
+ // always precede the class's implementation.
+ checkReplacement(methods, "SubMethodClass");
+ checkReplacement(methods, "SubMethodClass2");
free(methods);
// Check null-termination - this method list block would be 16 bytes
cls = objc_getClass("FourMethods");
methods = class_copyMethodList(cls, &count);
testassert(methods);
- testassert(count == 4);
- testassert(malloc_size(methods) >= 5 * sizeof(Method));
- testassert(methods[3] != NULL);
- testassert(methods[4] == NULL);
+ testassert(count == 4+MI);
+ testassert(malloc_size(methods) >= (4+MI+1) * sizeof(Method));
+ testassert(methods[3+MI] != NULL);
+ testassert(methods[4+MI] == NULL);
free(methods);
// Check NULL count parameter
methods = class_copyMethodList(cls, NULL);
testassert(methods);
- testassert(methods[4] == NULL);
- testassert(methods[3] != NULL);
+ testassert(methods[4+MI] == NULL);
+ testassert(methods[3+MI] != NULL);
free(methods);
// Check NULL class parameter
count = 100;
cls = objc_getClass("NoMethods");
methods = class_copyMethodList(cls, &count);
- testassert(!methods);
- testassert(count == 0);
+ if (MI == 0) {
+ testassert(!methods);
+ testassert(count == 0);
+ } else {
+ testassert(methods);
+ testassert(count == MI);
+ testassert(methods[MI] == NULL);
+ testassert(methods[MI-1] != NULL);
+ }
succeed(__FILE__);
}
+// TEST_CONFIG
+
#include "test.h"
#include <string.h>
#include <malloc/malloc.h>
+// TEST_CFLAGS -Wno-deprecated-declarations
+
#import <objc/runtime.h>
#import <objc/objc-auto.h>
#ifndef OBJC_NO_GC
#include <auto_zone.h>
+#else
+static void* objc_collectableZone(void) { return NULL; }
+static BOOL auto_zone_is_valid_pointer(void *a, void *b) { return a||b; }
#endif
#include "test.h"
testassert(s);
testassert(s->isa == [Super class]);
testassert(malloc_size(s) >= class_getInstanceSize([Super class]));
- if (objc_collecting_enabled()) testassert(auto_zone_is_valid_pointer(auto_zone(), s));
+ if (objc_collectingEnabled()) testassert(auto_zone_is_valid_pointer(objc_collectableZone(), s));
object_dispose(s);
testassert(s);
testassert(s->isa == [Sub class]);
testassert(malloc_size(s) >= class_getInstanceSize([Sub class]));
- if (objc_collecting_enabled()) testassert(auto_zone_is_valid_pointer(auto_zone(), s));
+ if (objc_collectingEnabled()) testassert(auto_zone_is_valid_pointer(objc_collectableZone(), s));
object_dispose(s);
testassert(s);
testassert(s->isa == [Super class]);
testassert(malloc_size(s) >= class_getInstanceSize([Super class]) + 100);
- if (objc_collecting_enabled()) testassert(auto_zone_is_valid_pointer(auto_zone(), s));
+ if (objc_collectingEnabled()) testassert(auto_zone_is_valid_pointer(objc_collectableZone(), s));
object_dispose(s);
testassert(s);
testassert(s->isa == [Sub class]);
testassert(malloc_size(s) >= class_getInstanceSize([Sub class]) + 100);
- if (objc_collecting_enabled()) testassert(auto_zone_is_valid_pointer(auto_zone(), s));
+ if (objc_collectingEnabled()) testassert(auto_zone_is_valid_pointer(objc_collectableZone(), s));
object_dispose(s);
+// rdar://6401639, waiting for rdar://5648998
+// TEST_DISABLED
+
#include "test.h"
#include <objc/objc.h>
#include <mach/mach.h>
#define _OBJC_PRIVATE_H_
#include <objc/objc-gdb.h>
-extern void _objc_flush_caches(Class cls, BOOL flushMeta);
-
-
@interface Super { id isa; } @end
@implementation Super
[Super method];
_objc_flush_caches(0, YES);
}
+
+ return NULL;
}
+// TEST_CONFIG
+
// DO NOT include anything else here
#include <objc/objc.h>
// DO NOT include anything else here
+// TEST_CFLAGS -Wno-deprecated-declarations
+
#include "test.h"
#include <objc/objc-runtime.h>
#ifndef OBJC_NO_GC
cls = [Super class];
clone = objc_duplicateClass(cls, "Super_copy", 0);
#ifndef OBJC_NO_GC
- if (objc_collecting_enabled()) {
- testassert(auto_zone_size(auto_zone(), clone));
+ if (objc_collectingEnabled()) {
+ testassert(auto_zone_size(objc_collectableZone(), clone));
// objc_duplicateClass() doesn't duplicate the metaclass
- // no: testassert(auto_zone_size(auto_zone(), clone->isa));
+ // no: testassert(auto_zone_size(objc_collectableZone(), clone->isa));
}
#endif
free(i2);
// Check protocol list
- Protocol **p1 = class_copyProtocolList(cls, NULL);
- Protocol **p2 = class_copyProtocolList(clone, NULL);
+ Protocol * const *p1 = class_copyProtocolList(cls, NULL);
+ Protocol * const *p2 = class_copyProtocolList(clone, NULL);
testassert(p1);
testassert(p2);
for (i = 0; p1[i] && p2[i]; i++) {
testassert(p1[i] == p2[i]); // protocols are not deep-copied
}
testassert(p1[i] == NULL && p2[i] == NULL);
- free(p1);
- free(p2);
+ free((void*)p1);
+ free((void*)p2);
// Check property list
objc_property_t *o1 = class_copyPropertyList(cls, NULL);
+++ /dev/null
-#!/usr/bin/perl
-use strict;
-
-# errcheck.pl
-# Check test output for errors.
-# usage: test.out | errcheck.pl test [stderr-file]
-
-my $testname = shift || die;
-my $errfile = shift || "$testname.expected-stderr";
-
-my @input;
-my @original_input;
-while (my $line = <>) {
- chomp $line;
- push @input, $line;
- push @original_input, $line;
-}
-
-# Run result-checking passes, reducing @input each time
-my $xit = 0;
-my $bad = "";
-$bad |= filter_valgrind() if ($ENV{VALGRIND});
-$bad = filter_expected() if ($bad eq "" && -e $errfile);
-$bad = filter_bad() if ($bad eq "");
-
-# OK line should be the only one left
-$bad = "(output not 'OK: $testname')" if ($bad eq "" && (scalar(@input) != 1 || $input[0] !~ /^OK: $testname/));
-
-if ($bad ne "") {
- my $red = "\e[41;37m";
- my $def = "\e[0m";
- $xit = 1;
- print "${red}BAD: /// test '$testname' \\\\\\$def\n";
- for my $line (@original_input) {
- print "$red $def$line\n";
- }
- print "${red}BAD: \\\\\\ test '$testname' ///$def\n";
- print "${red}FAIL: ## $testname: $bad$def\n";
-} else {
- print "PASS: $testname\n";
-}
-
-exit $xit;
-
-sub filter_expected
-{
- my $bad = "";
-
- open(my $checkfile, $errfile)
- || die "can't find $errfile\n";
- my $check = join('', <$checkfile>);
- close($checkfile);
-
- my $input = join("\n", @input) . "\n";
- if ($input !~ /^$check$/s) {
- $bad = "(didn't match $errfile)";
- @input = "BAD: $testname";
- } else {
- @input = "OK: $testname"; # pacify later filter
- }
-
- return $bad;
-}
-
-sub filter_bad
-{
- my $bad = "";
-
- my @new_input;
- for my $line (@input) {
- chomp $line;
- if ($line =~ /^BAD: (.*)/) {
- $bad = "(failed)";
- } else {
- push @new_input, $line;
- }
- }
- @input = @new_input;
- return $bad;
-}
-
-sub filter_valgrind
-{
- my $errors = 0;
- my $leaks = 0;
-
- my @new_input;
- for my $line (@input) {
- if ($line =~ /^Approx: do_origins_Dirty\([RW]\): missed \d bytes$/) {
- # --track-origins warning (harmless)
- next;
- }
- if ($line !~ /^^\.*==\d+==/) {
- # not valgrind output
- push @new_input, $line;
- next;
- }
-
- my ($errcount) = ($line =~ /==\d+== ERROR SUMMARY: (\d+) errors/);
- if (defined $errcount && $errcount > 0) {
- $errors = 1;
- }
-
- (my $leakcount) = ($line =~ /==\d+==\s+(?:definitely|possibly) lost:\s+([0-9,]+)/);
- if (defined $leakcount && $leakcount > 0) {
- $leaks = 1;
- }
- }
-
- @input = @new_input;
-
- my $bad = "";
- $bad .= "(valgrind errors)" if ($errors);
- $bad .= "(valgrind leaks)" if ($leaks);
- return $bad;
-}
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-category-0.m -dynamiclib -o libevil.dylib -framework Foundation
+ $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-category-0.out
+END
+*/
+
+// NOT EVIL version
+
+#define EVIL_INSTANCE_METHOD 0
+#define EVIL_CLASS_METHOD 0
+
+#define OMIT_CAT 0
+#define OMIT_NL_CAT 0
+
+#include "evil-category-def.m"
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG SDK=iphoneos
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-category-00.m $DIR/evil-main.m -framework Foundation -o evil-category-00.out
+END
+
+TEST_RUN_OUTPUT
+CRASHED: SIGSEGV
+END
+*/
+
+// NOT EVIL version: apps are allowed through (then crash in +load)
+
+#define EVIL_INSTANCE_METHOD 1
+#define EVIL_CLASS_METHOD 1
+
+#define OMIT_CAT 0
+#define OMIT_NL_CAT 0
+
+#include "evil-category-def.m"
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-category-000.m -dynamiclib -o libevil.dylib -framework Foundation
+ $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-category-000.out
+END
+*/
+
+// NOT EVIL version: category omitted from all lists
+
+#define EVIL_INSTANCE_METHOD 1
+#define EVIL_CLASS_METHOD 1
+
+#define OMIT_CAT 1
+#define OMIT_NL_CAT 1
+
+#include "evil-category-def.m"
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG SDK=iphoneos
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-category-1.m -dynamiclib -o libevil.dylib -framework Foundation
+ $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-1.out
+END
+
+TEST_RUN_OUTPUT
+objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
+CRASHED: SIG(ILL|TRAP)
+END
+*/
+
+#define EVIL_INSTANCE_METHOD 1
+#define EVIL_CLASS_METHOD 0
+
+#define OMIT_CAT 0
+#define OMIT_NL_CAT 0
+
+#include "evil-category-def.m"
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG SDK=iphoneos
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-category-2.m -dynamiclib -o libevil.dylib -framework Foundation
+ $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-2.out
+END
+
+TEST_RUN_OUTPUT
+objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
+CRASHED: SIG(ILL|TRAP)
+END
+*/
+
+#define EVIL_INSTANCE_METHOD 0
+#define EVIL_CLASS_METHOD 1
+
+#define OMIT_CAT 0
+#define OMIT_NL_CAT 0
+
+#include "evil-category-def.m"
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG SDK=iphoneos
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-category-3.m -dynamiclib -o libevil.dylib -framework Foundation
+ $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-3.out
+END
+
+TEST_RUN_OUTPUT
+objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
+CRASHED: SIG(ILL|TRAP)
+END
+*/
+
+#define EVIL_INSTANCE_METHOD 0
+#define EVIL_CLASS_METHOD 1
+
+#define OMIT_CAT 1
+#define OMIT_NL_CAT 0
+
+#include "evil-category-def.m"
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG SDK=iphoneos
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-category-4.m -dynamiclib -o libevil.dylib -framework Foundation
+ $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-4.out
+END
+
+TEST_RUN_OUTPUT
+objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
+CRASHED: SIG(ILL|TRAP)
+END
+*/
+
+#define EVIL_INSTANCE_METHOD 0
+#define EVIL_CLASS_METHOD 1
+
+#define OMIT_CAT 0
+#define OMIT_NL_CAT 1
+
+#include "evil-category-def.m"
--- /dev/null
+
+#if __OBJC2__
+
+#include <mach/shared_region.h>
+
+#if __LP64__
+# define PTR " .quad "
+#else
+# define PTR " .long "
+#endif
+
+#define str(x) #x
+#define str2(x) str(x)
+
+__BEGIN_DECLS
+void nop(void) { }
+__END_DECLS
+
+asm(
+ ".globl L_category \n"
+ ".section __DATA,__objc_data \n"
+ ".align 3 \n"
+ "L_category: \n"
+ PTR "L_cat_name \n"
+ PTR "_OBJC_CLASS_$_NSObject \n"
+#if EVIL_INSTANCE_METHOD
+ PTR "L_evil_methods \n"
+#else
+ PTR "L_good_methods \n"
+#endif
+#if EVIL_CLASS_METHOD
+ PTR "L_evil_methods \n"
+#else
+ PTR "L_good_methods \n"
+#endif
+ PTR "0 \n"
+ PTR "0 \n"
+
+ "L_evil_methods: \n"
+ ".long 24 \n"
+ ".long 1 \n"
+ PTR "L_load \n"
+ PTR "L_load \n"
+ PTR str2(SHARED_REGION_BASE+SHARED_REGION_SIZE-0x1000) " \n"
+
+ "L_good_methods: \n"
+ ".long 24 \n"
+ ".long 1 \n"
+ PTR "L_load \n"
+ PTR "L_load \n"
+ PTR "_nop \n"
+
+ ".cstring \n"
+ "L_cat_name: .ascii \"Evil\\0\" \n"
+ "L_load: .ascii \"load\\0\" \n"
+
+ ".section __DATA,__objc_catlist \n"
+#if !OMIT_CAT
+ PTR "L_category \n"
+#endif
+
+ ".section __DATA,__objc_nlcatlist \n"
+#if !OMIT_NL_CAT
+ PTR "L_category \n"
+#endif
+
+ );
+
+// __OBJC2__
+#endif
+
+void fn(void) { }
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-class-0.m -dynamiclib -o libevil.dylib
+ $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-class-0.out
+END
+*/
+
+// NOT EVIL version
+
+#define EVIL_SUPER 0
+#define EVIL_SUPER_META 0
+#define EVIL_SUB 0
+#define EVIL_SUB_META 0
+
+#define OMIT_SUPER 0
+#define OMIT_NL_SUPER 0
+#define OMIT_SUB 0
+#define OMIT_NL_SUB 0
+
+#include "evil-class-def.m"
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG SDK=iphoneos
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-class-00.m $DIR/evil-main.m -o evil-class-00.out
+END
+
+TEST_RUN_OUTPUT
+CRASHED: SIGSEGV
+END
+*/
+
+// NOT EVIL version: apps are allowed through (then crash in +load)
+
+#define EVIL_SUPER 0
+#define EVIL_SUPER_META 1
+#define EVIL_SUB 0
+#define EVIL_SUB_META 0
+
+#define OMIT_SUPER 1
+#define OMIT_NL_SUPER 1
+#define OMIT_SUB 1
+#define OMIT_NL_SUB 0
+
+#include "evil-class-def.m"
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-class-000.m -dynamiclib -o libevil.dylib
+ $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-class-000.out
+END
+*/
+
+// NOT EVIL version: all classes omitted from all lists
+
+#define EVIL_SUPER 1
+#define EVIL_SUPER_META 1
+#define EVIL_SUB 1
+#define EVIL_SUB_META 1
+
+#define OMIT_SUPER 1
+#define OMIT_NL_SUPER 1
+#define OMIT_SUB 1
+#define OMIT_NL_SUB 1
+
+#include "evil-class-def.m"
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG SDK=iphoneos
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-class-1.m -dynamiclib -o libevil.dylib
+ $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-1.out
+END
+
+TEST_RUN_OUTPUT
+objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
+CRASHED: SIG(ILL|TRAP)
+END
+*/
+
+#define EVIL_SUPER 1
+#define EVIL_SUPER_META 0
+#define EVIL_SUB 0
+#define EVIL_SUB_META 0
+
+#define OMIT_SUPER 0
+#define OMIT_NL_SUPER 0
+#define OMIT_SUB 0
+#define OMIT_NL_SUB 0
+
+#include "evil-class-def.m"
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG SDK=iphoneos
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-class-2.m -dynamiclib -o libevil.dylib
+ $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-2.out
+END
+
+TEST_RUN_OUTPUT
+objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
+CRASHED: SIG(ILL|TRAP)
+END
+*/
+
+#define EVIL_SUPER 0
+#define EVIL_SUPER_META 1
+#define EVIL_SUB 0
+#define EVIL_SUB_META 0
+
+#define OMIT_SUPER 0
+#define OMIT_NL_SUPER 0
+#define OMIT_SUB 0
+#define OMIT_NL_SUB 0
+
+#include "evil-class-def.m"
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG SDK=iphoneos
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-class-3.m -dynamiclib -o libevil.dylib
+ $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-3.out
+END
+
+TEST_RUN_OUTPUT
+objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
+CRASHED: SIG(ILL|TRAP)
+END
+*/
+
+#define EVIL_SUPER 0
+#define EVIL_SUPER_META 0
+#define EVIL_SUB 1
+#define EVIL_SUB_META 0
+
+#define OMIT_SUPER 0
+#define OMIT_NL_SUPER 0
+#define OMIT_SUB 0
+#define OMIT_NL_SUB 0
+
+#include "evil-class-def.m"
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG SDK=iphoneos
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-class-4.m -dynamiclib -o libevil.dylib
+ $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-4.out
+END
+
+TEST_RUN_OUTPUT
+objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
+CRASHED: SIG(ILL|TRAP)
+END
+*/
+
+#define EVIL_SUPER 0
+#define EVIL_SUPER_META 0
+#define EVIL_SUB 0
+#define EVIL_SUB_META 1
+
+#define OMIT_SUPER 0
+#define OMIT_NL_SUPER 0
+#define OMIT_SUB 0
+#define OMIT_NL_SUB 0
+
+#include "evil-class-def.m"
--- /dev/null
+/*
+rdar://8553305
+
+TEST_CONFIG SDK=iphoneos
+TEST_CRASHES
+
+TEST_BUILD
+ $C{COMPILE} $DIR/evil-class-5.m -dynamiclib -o libevil.dylib
+ $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-5.out
+END
+
+TEST_RUN_OUTPUT
+objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
+CRASHED: SIG(ILL|TRAP)
+END
+*/
+
+#define EVIL_SUPER 0
+#define EVIL_SUPER_META 1
+#define EVIL_SUB 0
+#define EVIL_SUB_META 0
+
+#define OMIT_SUPER 1
+#define OMIT_NL_SUPER 1
+#define OMIT_SUB 1
+#define OMIT_NL_SUB 0
+
+#include "evil-class-def.m"
--- /dev/null
+#if __OBJC2__
+
+#include <mach/shared_region.h>
+
+#if __LP64__
+# define PTR " .quad "
+#else
+# define PTR " .long "
+#endif
+
+#define str(x) #x
+#define str2(x) str(x)
+
+__BEGIN_DECLS
+void nop(void) { }
+__END_DECLS
+
+asm(
+ ".globl _OBJC_CLASS_$_Super \n"
+ ".section __DATA,__objc_data \n"
+ ".align 3 \n"
+ "_OBJC_CLASS_$_Super: \n"
+ PTR "_OBJC_METACLASS_$_Super \n"
+ PTR "0 \n"
+ PTR "__objc_empty_cache \n"
+ PTR "__objc_empty_vtable \n"
+ PTR "L_ro \n"
+ ""
+ "_OBJC_METACLASS_$_Super: \n"
+ PTR "_OBJC_METACLASS_$_Super \n"
+ PTR "_OBJC_CLASS_$_Super \n"
+ PTR "__objc_empty_cache \n"
+ PTR "__objc_empty_vtable \n"
+ PTR "L_meta_ro \n"
+ ""
+ "L_ro: \n"
+ ".long 2 \n"
+ ".long 0 \n"
+ ".long 0 \n"
+#if __LP64__
+ ".long 0 \n"
+#endif
+ PTR "0 \n"
+ PTR "L_super_name \n"
+#if EVIL_SUPER
+ PTR "L_evil_methods \n"
+#else
+ PTR "L_good_methods \n"
+#endif
+ PTR "0 \n"
+ PTR "0 \n"
+ PTR "0 \n"
+ PTR "0 \n"
+ ""
+ "L_meta_ro: \n"
+ ".long 3 \n"
+ ".long 40 \n"
+ ".long 40 \n"
+#if __LP64__
+ ".long 0 \n"
+#endif
+ PTR "0 \n"
+ PTR "L_super_name \n"
+#if EVIL_SUPER_META
+ PTR "L_evil_methods \n"
+#else
+ PTR "L_good_methods \n"
+#endif
+ PTR "0 \n"
+ PTR "0 \n"
+ PTR "0 \n"
+ PTR "0 \n"
+
+ ".globl _OBJC_CLASS_$_Sub \n"
+ ".section __DATA,__objc_data \n"
+ ".align 3 \n"
+ "_OBJC_CLASS_$_Sub: \n"
+ PTR "_OBJC_METACLASS_$_Sub \n"
+ PTR "_OBJC_CLASS_$_Super \n"
+ PTR "__objc_empty_cache \n"
+ PTR "__objc_empty_vtable \n"
+ PTR "L_sub_ro \n"
+ ""
+ "_OBJC_METACLASS_$_Sub: \n"
+ PTR "_OBJC_METACLASS_$_Super \n"
+ PTR "_OBJC_METACLASS_$_Super \n"
+ PTR "__objc_empty_cache \n"
+ PTR "__objc_empty_vtable \n"
+ PTR "L_sub_meta_ro \n"
+ ""
+ "L_sub_ro: \n"
+ ".long 2 \n"
+ ".long 0 \n"
+ ".long 0 \n"
+#if __LP64__
+ ".long 0 \n"
+#endif
+ PTR "0 \n"
+ PTR "L_sub_name \n"
+#if EVIL_SUB
+ PTR "L_evil_methods \n"
+#else
+ PTR "L_good_methods \n"
+#endif
+ PTR "0 \n"
+ PTR "0 \n"
+ PTR "0 \n"
+ PTR "0 \n"
+ ""
+ "L_sub_meta_ro: \n"
+ ".long 3 \n"
+ ".long 40 \n"
+ ".long 40 \n"
+#if __LP64__
+ ".long 0 \n"
+#endif
+ PTR "0 \n"
+ PTR "L_sub_name \n"
+#if EVIL_SUB_META
+ PTR "L_evil_methods \n"
+#else
+ PTR "L_good_methods \n"
+#endif
+ PTR "0 \n"
+ PTR "0 \n"
+ PTR "0 \n"
+ PTR "0 \n"
+
+ "L_evil_methods: \n"
+ ".long 24 \n"
+ ".long 1 \n"
+ PTR "L_load \n"
+ PTR "L_load \n"
+ PTR str2(SHARED_REGION_BASE+SHARED_REGION_SIZE-0x1000) " \n"
+
+ "L_good_methods: \n"
+ ".long 24 \n"
+ ".long 1 \n"
+ PTR "L_load \n"
+ PTR "L_load \n"
+ PTR "_nop \n"
+
+ ".cstring \n"
+ "L_super_name: .ascii \"Super\\0\" \n"
+ "L_sub_name: .ascii \"Sub\\0\" \n"
+ "L_load: .ascii \"load\\0\" \n"
+
+
+ ".section __DATA,__objc_classlist \n"
+#if !OMIT_SUPER
+ PTR "_OBJC_CLASS_$_Super \n"
+#endif
+#if !OMIT_SUB
+ PTR "_OBJC_CLASS_$_Sub \n"
+#endif
+
+ ".section __DATA,__objc_nlclslist \n"
+#if !OMIT_NL_SUPER
+ PTR "_OBJC_CLASS_$_Super \n"
+#endif
+#if !OMIT_NL_SUB
+ PTR "_OBJC_CLASS_$_Sub \n"
+#endif
+);
+
+// __OBJC2__
+#endif
+
+void fn(void) { }
--- /dev/null
+#include "test.h"
+
+extern void fn(void);
+
+int main(int argc __unused, char **argv)
+{
+ fn();
+
+#if TARGET_OS_EMBEDDED && !defined(NOT_EVIL)
+#pragma unused (argv)
+ fail("All that is necessary for the triumph of evil is that good men do nothing.");
+#else
+ succeed(basename(argv[0]));
+#endif
+}
+// TEST_CONFIG
+
#include "test.h"
#include <objc/runtime.h>
#include <objc/objc-exception.h>
@end
-#if __OBJC2__
+#if __OBJC2__ && !TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
void altHandlerFail(id unused __unused, void *context __unused)
{
{
@try {
state++;
- uintptr_t token = objc_addExceptionHandler(altHandler3, altHandler3);
+ uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3);
// state++ inside alt handler
@throw [Super new];
state = BAD;
{
@try {
state++;
- uintptr_t token = objc_addExceptionHandler(altHandler3, altHandler3);
+ uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3);
// state++ inside alt handler
@throw [Super new];
state = BAD;
state++;
@throw;
state = BAD;
- } @catch (Sub *e) {
+ }
+ @catch (Sub *e) {
state = BAD;
- } @finally {
+ }
+ @finally {
state++;
}
}
testassert(state == 7);
-#if __OBJC2__
+
+#if __OBJC2__ && !TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE
// alt handlers
// run a lot to catch failed unregistration (runtime complains at 1000)
#define ALT_HANDLER_REPEAT 2000
state++;
@try {
state++;
- uintptr_t token = objc_addExceptionHandler(altHandler2, altHandler2);
+ uintptr_t token = objc_addExceptionHandler(altHandler2, (void*)altHandler2);
// state++ inside alt handler
@throw [Super new];
state = BAD;
@try {
state++;
// same-level handlers called in FIFO order (not stack-like)
- uintptr_t token = objc_addExceptionHandler(altHandler4, altHandler4);
+ uintptr_t token = objc_addExceptionHandler(altHandler4, (void*)altHandler4);
// state++ inside alt handler
- uintptr_t token2 = objc_addExceptionHandler(altHandler5, altHandler5);
+ uintptr_t token2 = objc_addExceptionHandler(altHandler5, (void*)altHandler5);
// state++ inside alt handler
throwWithAltHandler(); // state += 2 inside
state = BAD;
@try {
state++;
// same-level handlers called in FIFO order (not stack-like)
- uintptr_t token = objc_addExceptionHandler(altHandler5, altHandler5);
+ uintptr_t token = objc_addExceptionHandler(altHandler5, (void*)altHandler5);
// state++ inside alt handler
- uintptr_t token2 = objc_addExceptionHandler(altHandler6, altHandler6);
+ uintptr_t token2 = objc_addExceptionHandler(altHandler6, (void*)altHandler6);
// state++ inside alt handler
throwWithAltHandlerAndRethrow(); // state += 3 inside
state = BAD;
succeed("exc.m");
#endif
}
+
+// TEST_CONFIG
+
#include "test.h"
#include <objc/objc-runtime.h>
+++ /dev/null
-#include "test.h"
-
-int main()
-{
- fail("always fails");
-}
+// TEST_CFLAGS -framework Foundation
+#include "test.h"
#import <Foundation/Foundation.h>
/* foreach tester */
-int Verbosity = 0;
int Errors = 0;
-bool testHandwritten(char *style, char *test, char *message, id collection, NSSet *reference) {
+bool testHandwritten(const char *style, const char *test, const char *message, id collection, NSSet *reference) {
unsigned int counter = 0;
bool result = true;
- if (Verbosity) {
- printf("testing: %s %s %s\n", style, test, message);
- }
+ testprintf("testing: %s %s %s\n", style, test, message);
/*
for (id elem in collection)
if ([reference member:elem]) ++counter;
if (counter == [reference count]) {
- if (Verbosity) {
- printf("success: %s %s %s\n", style, test, message);
- }
+ testprintf("success: %s %s %s\n", style, test, message);
}
else {
result = false;
return result;
}
-bool testCompiler(char *style, char *test, char *message, id collection, NSSet *reference) {
+bool testCompiler(const char *style, const char *test, const char *message, id collection, NSSet *reference) {
unsigned int counter = 0;
bool result = true;
- if (Verbosity) {
- printf("testing: %s %s %s\n", style, test, message);
- }
+ testprintf("testing: %s %s %s\n", style, test, message);
for (id elem in collection)
if ([reference member:elem]) ++counter;
if (counter == [reference count]) {
- if (Verbosity) {
- printf("success: %s %s %s\n", style, test, message);
- }
+ testprintf("success: %s %s %s\n", style, test, message);
}
else {
result = false;
void testContinue(NSArray *array) {
bool broken = false;
- if (Verbosity) {
- printf("testing: continue statements\n");
- }
- for (id elem in array) {
+ testprintf("testing: continue statements\n");
+ for (id __unused elem in array) {
if ([array count])
continue;
broken = true;
unsigned int counter = 0;
NSAutoreleasePool *pool = [NSAutoreleasePool new];
id enumerator = [array objectEnumerator];
- for (id elem in enumerator) {
+ for (id __unused elem in enumerator) {
if (++counter == where)
break;
}
printf("*** break at %d didn't work (actual was %d)\n", where, counter);
return false;
}
- for (id elem in enumerator)
+ for (id __unused elem in enumerator)
++counter;
if (counter != [array count]) {
++Errors;
bool testBreaks(NSArray *array) {
bool result = true;
- if (Verbosity) printf("testing breaks\n");
+ testprintf("testing breaks\n");
unsigned int counter = 0;
for (counter = 1; counter < [array count]; ++counter) {
result = testBreak(counter, array) && result;
return result;
}
-bool testCompleteness(char *test, char *message, id collection, NSSet *reference) {
+bool testCompleteness(const char *test, const char *message, id collection, NSSet *reference) {
bool result = true;
result = result && testHandwritten("handwritten", test, message, collection, reference);
result = result && testCompiler("compiler", test, message, collection, reference);
return result;
}
-bool testEnumerator(char *test, char *message, id collection, NSSet *reference) {
+bool testEnumerator(const char *test, const char *message, id collection, NSSet *reference) {
bool result = true;
result = result && testHandwritten("handwritten", test, message, [collection objectEnumerator], reference);
result = result && testCompiler("compiler", test, message, [collection objectEnumerator], reference);
}
}
-void testCollections(char *test, NSArray *array, NSSet *set) {
+void testCollections(const char *test, NSArray *array, NSSet *set) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
id collection;
collection = [NSMutableArray arrayWithArray:array];
[pool drain];
}
-void testInnerDecl(char *test, char *message, id collection) {
+void testInnerDecl(const char *test, const char *message, id collection) {
unsigned int counter = 0;
- for (id x in collection)
+ for (id __unused x in collection)
++counter;
if (counter != [collection count]) {
printf("** failed: %s %s\n", test, message);
}
-void testOuterDecl(char *test, char *message, id collection) {
+void testOuterDecl(const char *test, const char *message, id collection) {
unsigned int counter = 0;
id x;
for (x in collection)
++Errors;
}
}
-void testInnerExpression(char *test, char *message, id collection) {
+void testInnerExpression(const char *test, const char *message, id collection) {
unsigned int counter = 0;
- for (id x in [collection self])
+ for (id __unused x in [collection self])
++counter;
if (counter != [collection count]) {
printf("** failed: %s %s\n", test, message);
++Errors;
}
}
-void testOuterExpression(char *test, char *message, id collection) {
+void testOuterExpression(const char *test, const char *message, id collection) {
unsigned int counter = 0;
id x;
for (x in [collection self])
}
}
-void testExpressions(char *message, id collection) {
+void testExpressions(const char *message, id collection) {
testInnerDecl("inner", message, collection);
testOuterDecl("outer", message, collection);
testInnerExpression("outer expression", message, collection);
int main() {
- Verbosity = (getenv("VERBOSE") != NULL);
NSAutoreleasePool *pool = [NSAutoreleasePool new];
testCollections("nil", nil, nil);
testCollections("empty", [NSArray array], [NSSet set]);
testExpressions("array", ReferenceArray);
testBreaks(ReferenceArray);
testContinue(ReferenceArray);
- if (Errors == 0) printf("OK: foreach\n");
- else printf("BAD: foreach %d errors detected\n", Errors);
+ if (Errors == 0) succeed(__FILE__);
+ else fail("foreach %d errors detected\n", Errors);
[pool drain];
exit(Errors);
}
+// TEST_CFLAGS -Wno-deprecated-declarations
+
#include "test.h"
+
+#ifdef __cplusplus
+
+int main()
+{
+ testwarn("c++ rdar://8366474 @selector(foo::)");
+ succeed(__FILE__);
+}
+
+#else
+
#include <objc/runtime.h>
#include <objc/message.h>
-extern void _objc_flush_caches(Class cls, BOOL flushMeta);
-
struct stret {
int a;
int b;
int c;
int d;
int e;
- int pad[100]; // force stack return on ppc64
};
BOOL stret_equal(struct stret a, struct stret b)
id ID_RESULT = (id)0x12345678;
long long LL_RESULT = __LONG_LONG_MAX__ - 2LL*__INT_MAX__;
-struct stret STRET_RESULT = {1, 2, 3, 4, 5, {0}};
+struct stret STRET_RESULT = {1, 2, 3, 4, 5};
double FP_RESULT = __DBL_MIN__ + __DBL_EPSILON__;
long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
{
testassert(state == 15);
state = 16;
-#if defined(__ppc__) || defined(__ppc64__)
- __asm__ volatile("lfd f1,%0" : : "m" (FP_RESULT));
-#elif defined(__i386__)
+#if defined(__i386__)
__asm__ volatile("fldl %0" : : "m" (FP_RESULT));
#elif defined(__x86_64__)
__asm__ volatile("movsd %0, %%xmm0" : : "m" (FP_RESULT));
#if defined(__i386__)
struct_addr = ((struct stret **)args)[-1];
-#elif defined(__ppc__)
- struct_addr = *(struct stret **)((char *)args + 128);
-#elif defined(__ppc64__)
- struct_addr = *(struct stret **)((char *)args + 152);
#elif defined(__x86_64__)
struct_addr = *(struct stret **)((char *)args + 8*16+4*8);
#elif defined(__arm__)
testassert(_cmd == sel_registerName("forward::"));
p = args;
-#if defined(__ppc__) || defined(__ppc64__)
- p += 13*sizeof(double) + 6*sizeof(void*); // skip over fp and linkage
- if (sel == @selector(stret::::::::::::::::::::::::::::) ||
- sel == @selector(stre2::::::::::::::::::::::::::::) ||
- sel == @selector(stre3::::::::::::::::::::::::::::))
- {
- p += sizeof(void *); // struct return
- }
-#elif defined(__x86_64__)
+#if defined(__x86_64__)
p += 8*16 + 4*8; // skip over xmm and linkage
if (sel == @selector(stret::::::::::::::::::::::::::::) ||
sel == @selector(stre2::::::::::::::::::::::::::::) ||
testassert(*gp++ == 12);
testassert(*gp++ == 13);
-#if defined(__i386__) || defined(__ppc__) || defined(__ppc64__) || defined(__arm__)
+#if defined(__i386__) || defined(__arm__)
-# if defined(__ppc__) || defined(__ppc64__)
- fp = (double *)args;
-# elif defined(__i386__) || defined(__arm__)
fp = (double *)gp;
-# else
-# error unknown architecture
-# endif
testassert(*fp++ == 1.0);
testassert(*fp++ == 2.0);
testassert(*fp++ == 3.0);
testassert(*fp++ == 11.0);
testassert(*fp++ == 12.0);
testassert(*fp++ == 13.0);
-# if defined(__ppc__) || defined(__ppc64__)
- fp = 13 + (double *)gp;
-# endif
testassert(*fp++ == 14.0);
testassert(*fp++ == 15.0);
{
testassert(state == 5);
state = 6;
-#if defined(__ppc__) || defined(__ppc64__)
- __asm__ volatile("lfd f1,%0" : : "m" (FP_RESULT));
-#elif defined(__i386__)
+#if defined(__i386__)
__asm__ volatile("fldl %0" : : "m" (FP_RESULT));
#elif defined(__x86_64__)
__asm__ volatile("movsd %0, %%xmm0" : : "m" (FP_RESULT));
typedef struct stret (*st_fn_t)(id self, SEL _cmd, long i1, long i2, long i3, long i4, long i5, long i6, long i7, long i8, long i9, long i10, long i11, long i12, long i13, double f1, double f2, double f3, double f4, double f5, double f6, double f7, double f8, double f9, double f10, double f11, double f12, double f13, double f14, double f15);
+
+extern void *getSP(void);
+
+#if defined(__x86_64__)
+ asm(".text \n _getSP: movq %rsp, %rax \n retq \n");
+#elif defined(__i386__)
+ asm(".text \n _getSP: movl %esp, %eax \n ret \n");
+#elif defined(__arm__)
+ asm(".text \n _getSP: mov r0, sp \n bx lr \n");
+#else
+# error unknown architecture
+#endif
+
int main()
{
id idval;
long long llval;
struct stret stval;
double fpval;
+ void *sp1 = (void*)1;
+ void *sp2 = (void*)2;
receiver = [Super class];
// Test default forward handler
state = 1;
+ sp1 = getSP();
idval = [Super idret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 2);
testassert(idval == ID_RESULT);
state = 3;
+ sp1 = getSP();
llval = [Super llret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 4);
testassert(llval == LL_RESULT);
state = 5;
+ sp1 = getSP();
fpval = [Super fpret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 6);
testassert(fpval == FP_RESULT);
state = 7;
+ sp1 = getSP();
stval = [Super stret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 8);
testassert(stret_equal(stval, STRET_RESULT));
// Test default forward handler, cached
state = 1;
+ sp1 = getSP();
idval = [Super idret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 2);
testassert(idval == ID_RESULT);
state = 3;
+ sp1 = getSP();
llval = [Super llret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 4);
testassert(llval == LL_RESULT);
state = 5;
+ sp1 = getSP();
fpval = [Super fpret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 6);
testassert(fpval == FP_RESULT);
state = 7;
+ sp1 = getSP();
stval = [Super stret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 8);
testassert(stret_equal(stval, STRET_RESULT));
// Test default forward handler, uncached but fixed-up
- _objc_flush_caches(nil, YES);
+ _objc_flush_caches(nil);
state = 1;
+ sp1 = getSP();
idval = [Super idret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 2);
testassert(idval == ID_RESULT);
state = 3;
+ sp1 = getSP();
llval = [Super llret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 4);
testassert(llval == LL_RESULT);
state = 5;
+ sp1 = getSP();
fpval = [Super fpret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 6);
testassert(fpval == FP_RESULT);
state = 7;
+ sp1 = getSP();
stval = [Super stret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 8);
testassert(stret_equal(stval, STRET_RESULT));
// Test manual forwarding
state = 1;
+ sp1 = getSP();
idval = ((id_fn_t)_objc_msgForward)(receiver, @selector(idre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 2);
testassert(idval == ID_RESULT);
state = 3;
+ sp1 = getSP();
llval = ((ll_fn_t)_objc_msgForward)(receiver, @selector(llre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 4);
testassert(llval == LL_RESULT);
state = 5;
+ sp1 = getSP();
fpval = ((fp_fn_t)_objc_msgForward)(receiver, @selector(fpre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 6);
testassert(fpval == FP_RESULT);
state = 7;
+ sp1 = getSP();
stval = ((st_fn_t)_objc_msgForward_stret)(receiver, @selector(stre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 8);
testassert(stret_equal(stval, STRET_RESULT));
// Test manual forwarding, cached
state = 1;
+ sp1 = getSP();
idval = ((id_fn_t)_objc_msgForward)(receiver, @selector(idre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 2);
testassert(idval == ID_RESULT);
state = 3;
+ sp1 = getSP();
llval = ((ll_fn_t)_objc_msgForward)(receiver, @selector(llre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 4);
testassert(llval == LL_RESULT);
state = 5;
+ sp1 = getSP();
fpval = ((fp_fn_t)_objc_msgForward)(receiver, @selector(fpre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 6);
testassert(fpval == FP_RESULT);
state = 7;
+ sp1 = getSP();
stval = ((st_fn_t)_objc_msgForward_stret)(receiver, @selector(stre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 8);
testassert(stret_equal(stval, STRET_RESULT));
// Test manual forwarding, uncached but fixed-up
- _objc_flush_caches(nil, YES);
+ _objc_flush_caches(nil);
state = 1;
+ sp1 = getSP();
idval = ((id_fn_t)_objc_msgForward)(receiver, @selector(idre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 2);
testassert(idval == ID_RESULT);
state = 3;
+ sp1 = getSP();
llval = ((ll_fn_t)_objc_msgForward)(receiver, @selector(llre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 4);
testassert(llval == LL_RESULT);
state = 5;
+ sp1 = getSP();
fpval = ((fp_fn_t)_objc_msgForward)(receiver, @selector(fpre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 6);
testassert(fpval == FP_RESULT);
state = 7;
+ sp1 = getSP();
stval = ((st_fn_t)_objc_msgForward_stret)(receiver, @selector(stre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 8);
testassert(stret_equal(stval, STRET_RESULT));
objc_setForwardHandler(&forward_handler, &forward_stret_handler);
state = 11;
+ sp1 = getSP();
idval = [Super idre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 12);
testassert(idval == ID_RESULT);
state = 13;
+ sp1 = getSP();
llval = [Super llre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 14);
testassert(llval == LL_RESULT);
state = 15;
+ sp1 = getSP();
fpval = [Super fpre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 16);
testassert(fpval == FP_RESULT);
state = 17;
+ sp1 = getSP();
stval = [Super stre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 18);
testassert(stret_equal(stval, STRET_RESULT));
// Test user-defined forward handler, cached
state = 11;
+ sp1 = getSP();
idval = [Super idre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 12);
testassert(idval == ID_RESULT);
state = 13;
+ sp1 = getSP();
llval = [Super llre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 14);
testassert(llval == LL_RESULT);
state = 15;
+ sp1 = getSP();
fpval = [Super fpre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 16);
testassert(fpval == FP_RESULT);
state = 17;
+ sp1 = getSP();
stval = [Super stre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 18);
testassert(stret_equal(stval, STRET_RESULT));
// Test user-defined forward handler, uncached but fixed-up
- _objc_flush_caches(nil, YES);
+ _objc_flush_caches(nil);
state = 11;
+ sp1 = getSP();
idval = [Super idre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 12);
testassert(idval == ID_RESULT);
state = 13;
+ sp1 = getSP();
llval = [Super llre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 14);
testassert(llval == LL_RESULT);
state = 15;
+ sp1 = getSP();
fpval = [Super fpre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 16);
testassert(fpval == FP_RESULT);
state = 17;
+ sp1 = getSP();
stval = [Super stre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ sp2 = getSP();
+ testassert(sp1 == sp2);
testassert(state == 18);
testassert(stret_equal(stval, STRET_RESULT));
succeed(__FILE__);
}
+
+#endif
--- /dev/null
+/*
+TEST_BUILD
+ $C{COMPILE} $DIR/future0.m -o future0.dylib -dynamiclib
+ $C{COMPILE} $DIR/future2.m -x none future0.dylib -o future2.dylib -dynamiclib
+ $C{COMPILE} $DIR/future.m -x none future0.dylib -o future.out
+END
+*/
+
+#include "test.h"
+#include <objc/objc-runtime.h>
+#include <malloc/malloc.h>
+#include <string.h>
+#include <dlfcn.h>
+#include "future.h"
+
+@implementation Sub2
++(int)method {
+ return 2;
+}
++(Class)classref {
+ return [Sub2 class];
+}
+@end
+
+@implementation SubSub2
++(int)method {
+ return 1 + [super method];
+}
+@end
+
+int main()
+{
+ Class oldSuper;
+ Class oldSub1;
+ Class newSub1;
+#if !__OBJC2__
+ Class oldSub2;
+ Class newSub2;
+ uintptr_t buf[20];
+#endif
+
+ // objc_getFutureClass with existing class
+ oldSuper = objc_getFutureClass("Super");
+ testassert(oldSuper == [Super class]);
+
+ // objc_getFutureClass with missing class
+ oldSub1 = objc_getFutureClass("Sub1");
+ testassert(oldSub1);
+ testassert(malloc_size(oldSub1) > 0);
+ testassert(objc_getClass("Sub1") == Nil);
+
+ // objc_getFutureClass a second time
+ testassert(oldSub1 == objc_getFutureClass("Sub1"));
+
+#if !__OBJC2__
+ // objc_setFutureClass with existing class
+ oldSub2 = objc_getClass("Sub2");
+ testassert(oldSub2 == [Sub2 class]);
+ testassert(oldSub2 == class_getSuperclass(objc_getClass("SubSub2")));
+ objc_setFutureClass((Class)buf, "Sub2");
+ testassert(0 == strcmp(class_getName((Class)buf), "Sub2"));
+ newSub2 = objc_getClass("Sub2");
+ testassert(newSub2 == (Class)buf);
+ testassert(newSub2 != oldSub2);
+ // check classrefs
+ testassert(newSub2 == [Sub2 class]);
+ testassert(newSub2 == [newSub2 class]);
+ testassert(newSub2 == [newSub2 classref]);
+ testassert(newSub2 != [oldSub2 class]);
+ // check superclass chains
+ testassert(newSub2 == class_getSuperclass(objc_getClass("SubSub2")));
+#else
+ // 64-bit ABI ignores objc_setFutureClass.
+#endif
+
+ // Load class Sub1
+ dlopen("future2.dylib", 0);
+
+ // Verify use of future class
+ newSub1 = objc_getClass("Sub1");
+ testassert(oldSub1 == newSub1);
+ testassert(newSub1 == [newSub1 classref]);
+ testassert(newSub1 == class_getSuperclass(objc_getClass("SubSub1")));
+
+ testassert(1 == [oldSub1 method]);
+ testassert(1 == [newSub1 method]);
+#if !__OBJC2__
+ testassert(2 == [newSub2 method]);
+ testassert(2 == [oldSub2 method]);
+ testassert(3 == [SubSub2 method]);
+#endif
+
+ succeed(__FILE__);
+}
+++ /dev/null
-#include "test.h"
-#include <objc/objc-runtime.h>
-#include <malloc/malloc.h>
-#include <string.h>
-#include <dlfcn.h>
-#include "future.h"
-
-@implementation Sub2
-+(int)method {
- return 2;
-}
-+(Class)classref {
- return [Sub2 class];
-}
-@end
-
-@implementation SubSub2
-+(int)method {
- return 1 + [super method];
-}
-@end
-
-int main()
-{
- Class oldSuper;
- Class oldSub1;
- Class newSub1;
-#if !__OBJC2__
- Class oldSub2;
- Class newSub2;
- uintptr_t buf[20];
-#endif
-
- // objc_getFutureClass with existing class
- oldSuper = objc_getFutureClass("Super");
- testassert(oldSuper == [Super class]);
-
- // objc_getFutureClass with missing class
- oldSub1 = objc_getFutureClass("Sub1");
- testassert(oldSub1);
- testassert(malloc_size(oldSub1) > 0);
- testassert(objc_getClass("Sub1") == Nil);
-
- // objc_getFutureClass a second time
- testassert(oldSub1 == objc_getFutureClass("Sub1"));
-
-#if !__OBJC2__
- // objc_setFutureClass with existing class
- oldSub2 = objc_getClass("Sub2");
- testassert(oldSub2 == [Sub2 class]);
- testassert(oldSub2 == class_getSuperclass(objc_getClass("SubSub2")));
- objc_setFutureClass((Class)buf, "Sub2");
- testassert(0 == strcmp(class_getName((Class)buf), "Sub2"));
- newSub2 = objc_getClass("Sub2");
- testassert(newSub2 == (Class)buf);
- testassert(newSub2 != oldSub2);
- // check classrefs
- testassert(newSub2 == [Sub2 class]);
- testassert(newSub2 == [newSub2 class]);
- testassert(newSub2 == [newSub2 classref]);
- testassert(newSub2 != [oldSub2 class]);
- // check superclass chains
- testassert(newSub2 == class_getSuperclass(objc_getClass("SubSub2")));
-#else
- // 64-bit ABI ignores objc_setFutureClass.
-#endif
-
- // Load class Sub1
- dlopen("future2.out", 0);
-
- // Verify use of future class
- newSub1 = objc_getClass("Sub1");
- testassert(oldSub1 == newSub1);
- testassert(newSub1 == [newSub1 classref]);
- testassert(newSub1 == class_getSuperclass(objc_getClass("SubSub1")));
-
- testassert(1 == [oldSub1 method]);
- testassert(1 == [newSub1 method]);
-#if !__OBJC2__
- testassert(2 == [newSub2 method]);
- testassert(2 == [oldSub2 method]);
- testassert(3 == [SubSub2 method]);
-#endif
-
- succeed(__FILE__);
-}
--- /dev/null
+#include "test.h"
+
+@interface Main @end
+@implementation Main @end
+
+int main(int argc __attribute__((unused)), char **argv)
+{
+ succeed(basename(argv[0]));
+}
@interface GC @end
@implementation GC @end
+
+// silence "no debug symbols in executable" warning
+void foo(void) { }
--- /dev/null
+// gc-off app loading gc-off dylib: should work
+
+/*
+TEST_CONFIG GC=0 SDK=macos
+
+TEST_BUILD
+ $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
+ $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
+
+ $C{COMPILE} $DIR/gc-main.m -x none libnogc.dylib -o gcenforcer-nogc-1.out
+END
+*/
--- /dev/null
+// gc-on app loading gc-off dylib: should crash
+
+/*
+TEST_CONFIG GC=1 SDK=macos
+TEST_CRASHES
+
+TEST_RUN_OUTPUT
+objc\[\d+\]: '.*libnogc.dylib' was not compiled with -fobjc-gc or -fobjc-gc-only, but the application requires GC
+objc\[\d+\]: \*\*\* GC capability of application and some libraries did not match
+CRASHED: SIGILL
+END
+
+TEST_BUILD
+ $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
+ $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
+
+ $C{COMPILE} $DIR/gc-main.m -x none libnogc.dylib -o gcenforcer-nogc-2.out
+END
+*/
--- /dev/null
+/*
+TEST_CONFIG SDK=macos
+
+TEST_BUILD
+ $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
+ $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
+
+ $C{COMPILE} $DIR/gc-main.m -x none libnoobjc.dylib -o gcenforcer-noobjc.out
+END
+*/
--- /dev/null
+// gc-off app loading gc-required dylib: should crash
+// linker sees librequiresgc.fake.dylib, runtime uses librequiresgc.dylib
+
+/*
+TEST_CONFIG GC=0 SDK=macos
+TEST_CRASHES
+
+TEST_RUN_OUTPUT
+objc\[\d+\]: '.*librequiresgc.dylib' was compiled with -fobjc-gc-only, but the application does not support GC
+objc\[\d+\]: \*\*\* GC capability of application and some libraries did not match
+CRASHED: SIGILL
+END
+
+TEST_BUILD
+ $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
+ $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
+
+ $C{COMPILE} $DIR/gc-main.m -x none librequiresgc.fake.dylib -o gcenforcer-requiresgc-1.out
+END
+*/
--- /dev/null
+// gc-off app loading gc-required dylib: should crash
+// linker sees librequiresgc.fake.dylib, runtime uses librequiresgc.dylib
+
+/*
+TEST_CONFIG GC=1 SDK=macos
+
+TEST_BUILD
+ $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
+ $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
+
+ $C{COMPILE} $DIR/gc-main.m -x none librequiresgc.fake.dylib -o gcenforcer-requiresgc-2.out
+END
+*/
--- /dev/null
+/*
+TEST_CONFIG SDK=macos
+
+TEST_BUILD
+ $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
+ $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
+
+ $C{COMPILE} $DIR/gc-main.m -x none libsupportsgc.dylib -o gcenforcer-supportsgc.out
+END
+*/
+/*
+TEST_CONFIG SDK=macos
+
+TEST_BUILD
+ $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
+ $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
+ $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
+
+ $C{COMPILE} $DIR/gcenforcer.m -o gcenforcer.out
+END
+*/
+
#include "test.h"
#include <objc/objc-auto.h>
#include <dlfcn.h>
testassert(dlopen_preflight("libsupportsgc.dylib"));
testassert(dlopen_preflight("libnoobjc.dylib"));
- if (objc_collecting_enabled()) {
+ if (objc_collectingEnabled()) {
testassert(dlopen_preflight("librequiresgc.dylib"));
testassert(! dlopen_preflight("libnogc.dylib"));
} else {
+++ /dev/null
-objc\[\d+\]: '.*libnogc.dylib' was not compiled with -fobjc-gc or -fobjc-gc-only, but the application requires GC
-objc\[\d+\]: \*\*\* GC capability of application and some libraries did not match
+++ /dev/null
-OK: gcenforcer_nogc.out
+++ /dev/null
-OK: gcenforcer_requiresgc.out
+++ /dev/null
-objc\[\d+\]: '.*librequiresgc.dylib' was compiled with -fobjc-gc-only, but the application does not support GC
-objc\[\d+\]: \*\*\* GC capability of application and some libraries did not match
+// TEST_CFLAGS -framework Foundation
+
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
+#import <objc/objc-gdb.h>
#include "test.h"
-// gcc -arch ppc -arch i386 -arch x86_64 -x objective-c gdb-lock.m -framework Foundation
-// CONFIG GC RR
-
-#if __cplusplus
-extern "C"
-#endif
- BOOL gdb_objc_isRuntimeLocked();
-
@interface Foo : NSObject
@end
@implementation Foo
-- (void) foo;
+- (void) foo
{
}
succeed(__FILE__);
return 0;
-}
\ No newline at end of file
+}
+// TEST_CFLAGS -Wno-deprecated-declarations
+
#include "test.h"
+
+#if TARGET_OS_IPHONE
+
+int main()
+{
+ succeed(__FILE__);
+}
+
+#else
+
#include <objc/objc-gdb.h>
#include <objc/runtime.h>
[Super class];
// Now class should be realized
- result = NXMapGet(gdb_objc_realized_classes, "Super");
+ result = (Class)NXMapGet(gdb_objc_realized_classes, "Super");
testassert(result);
testassert(result == [Super class]);
- result = NXMapGet(gdb_objc_realized_classes, "DoesNotExist");
+ result = (Class)NXMapGet(gdb_objc_realized_classes, "DoesNotExist");
testassert(!result);
#else
struct objc_class query;
- struct objc_class *result;
+ Class result;
query.name = "Super";
- result = NXHashGet(_objc_debug_class_hash, &query);
+ result = (Class)NXHashGet(_objc_debug_class_hash, &query);
testassert(result);
- testassert(result == [Super class]);
+ testassert((id)result == [Super class]);
query.name = "DoesNotExist";
- result = NXHashGet(_objc_debug_class_hash, &query);
+ result = (Class)NXHashGet(_objc_debug_class_hash, &query);
testassert(!result);
#endif
succeed(__FILE__);
}
+
+#endif
+// TEST_CONFIG
+
#include "test.h"
#include <objc/runtime.h>
#include <objc/message.h>
+// TEST_CFLAGS -Wno-deprecated-declarations
+
#include "test.h"
#include <objc/runtime.h>
#include <objc/message.h>
id idVal;
uintptr_t intVal;
- if (objc_collecting_enabled()) {
- // GC: all ignored selectors are identical
+#if defined(__i386__)
+ if (objc_collectingEnabled()) {
+ // i386 GC: all ignored selectors are identical
testassert(@selector(retain) == @selector(release) &&
@selector(retain) == @selector(autorelease) &&
@selector(retain) == @selector(dealloc) &&
@selector(retain) == @selector(retainCount) );
}
- else {
- // no GC: all ignored selectors are distinct
+ else
+#endif
+ {
+ // x86_64 GC or no GC: all ignored selectors are distinct
testassert(@selector(retain) != @selector(release) &&
@selector(retain) != @selector(autorelease) &&
@selector(retain) != @selector(dealloc) &&
getImp(dealloc);
getImp(retainCount);
- if (objc_collecting_enabled()) {
+ if (objc_collectingEnabled()) {
// GC: all ignored selector IMPs are identical
testassert(retain == release &&
retain == autorelease &&
state = 0;
idVal = ((id(*)(id, Method))method_invoke)(cls, retainMethod);
- testassert(state == (objc_collecting_enabled() ? 0 : 2));
+ testassert(state == (objc_collectingEnabled() ? 0 : 2));
testassert(idVal == cls);
idVal = ((id(*)(id, Method))method_invoke)(cls, releaseMethod);
- testassert(state == (objc_collecting_enabled() ? 0 : 3));
+ testassert(state == (objc_collectingEnabled() ? 0 : 3));
testassert(idVal == cls);
idVal = ((id(*)(id, Method))method_invoke)(cls, autoreleaseMethod);
- testassert(state == (objc_collecting_enabled() ? 0 : 4));
+ testassert(state == (objc_collectingEnabled() ? 0 : 4));
testassert(idVal == cls);
(void) ((void(*)(id, Method))method_invoke)(cls, deallocMethod);
- testassert(state == (objc_collecting_enabled() ? 0 : 5));
+ testassert(state == (objc_collectingEnabled() ? 0 : 5));
intVal = ((uintptr_t(*)(id, Method))method_invoke)(cls, retainCountMethod);
- testassert(state == (objc_collecting_enabled() ? 0 : 6));
- testassert(intVal == (objc_collecting_enabled() ? (uintptr_t)cls : 6));
+ testassert(state == (objc_collectingEnabled() ? 0 : 6));
+ testassert(intVal == (objc_collectingEnabled() ? (uintptr_t)cls : 6));
- // Test calls via objc_msgSend
+ // Test calls via compiled objc_msgSend
state = 0;
idVal = [cls normal];
state = 0;
idVal = [cls retain];
- testassert(state == (objc_collecting_enabled() ? 0 : 2));
+ testassert(state == (objc_collectingEnabled() ? 0 : 2));
testassert(idVal == cls);
idVal = [cls release];
- testassert(state == (objc_collecting_enabled() ? 0 : 3));
+ testassert(state == (objc_collectingEnabled() ? 0 : 3));
testassert(idVal == cls);
idVal = [cls autorelease];
- testassert(state == (objc_collecting_enabled() ? 0 : 4));
+ testassert(state == (objc_collectingEnabled() ? 0 : 4));
testassert(idVal == cls);
(void) [cls dealloc];
- testassert(state == (objc_collecting_enabled() ? 0 : 5));
+ testassert(state == (objc_collectingEnabled() ? 0 : 5));
intVal = [cls retainCount];
- testassert(state == (objc_collecting_enabled() ? 0 : 6));
- testassert(intVal == (objc_collecting_enabled() ? (uintptr_t)cls : 6));
+ testassert(state == (objc_collectingEnabled() ? 0 : 6));
+ testassert(intVal == (objc_collectingEnabled() ? (uintptr_t)cls : 6));
+
+ // Test calls via handwritten objc_msgSend
+
+ state = 0;
+ idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(normal));
+ testassert(state == 1);
+ testassert(idVal == cls);
+
+ state = 0;
+ idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(retain));
+ testassert(state == (objc_collectingEnabled() ? 0 : 2));
+ testassert(idVal == cls);
+
+ idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(release));
+ testassert(state == (objc_collectingEnabled() ? 0 : 3));
+ testassert(idVal == cls);
+
+ idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(autorelease));
+ testassert(state == (objc_collectingEnabled() ? 0 : 4));
+ testassert(idVal == cls);
+
+ (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(dealloc));
+ testassert(state == (objc_collectingEnabled() ? 0 : 5));
+
+ intVal = ((uintptr_t(*)(id,SEL))objc_msgSend)(cls, @selector(retainCount));
+ testassert(state == (objc_collectingEnabled() ? 0 : 6));
+ testassert(intVal == (objc_collectingEnabled() ? (uintptr_t)cls : 6));
}
int main()
testassert(sel_registerName("retain") == @selector(retain));
testassert(sel_getUid("retain") == @selector(retain));
- if (objc_collecting_enabled()) {
+#if defined(__i386__)
+ if (objc_collectingEnabled()) {
+ // only i386's GC currently remaps these
testassert(0 == strcmp(sel_getName(@selector(retain)), "<ignored selector>"));
- } else {
+ } else
+#endif
+ {
testassert(0 == strcmp(sel_getName(@selector(retain)), "retain"));
}
#if !__OBJC2__
testassert(cls);
cycle(cls);
- if (objc_collecting_enabled()) {
+ if (objc_collectingEnabled()) {
// rdar://6200570 Method manipulation shouldn't affect ignored methods.
cls = [Super class];
// Test calls via objc_msgSend - ignored selectors are ignored
// under GC even if the class provides no implementation for them
- if (objc_collecting_enabled()) {
+ if (objc_collectingEnabled()) {
Class cls;
id idVal;
uintptr_t intVal;
idVal = [Empty normal];
testassert(state == 1);
testassert(idVal == nil);
+
+ state = 0;
+
+ idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(retain));
+ testassert(state == 0);
+ testassert(idVal == cls);
+
+ idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(release));
+ testassert(state == 0);
+ testassert(idVal == cls);
+
+ idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(autorelease));
+ testassert(state == 0);
+ testassert(idVal == cls);
+
+ (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(dealloc));
+ testassert(state == 0);
+
+ intVal = ((uintptr_t(*)(id,SEL))objc_msgSend)(cls, @selector(retainCount));
+ testassert(state == 0);
+ testassert(intVal == (uintptr_t)cls);
+
+ idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(normal));
+ testassert(state == 1);
+ testassert(idVal == nil);
}
succeed(__FILE__);
+/*
+TEST_BUILD
+ $C{COMPILE} $DIR/imageorder1.m -o imageorder1.dylib -dynamiclib
+ $C{COMPILE} $DIR/imageorder2.m -x none imageorder1.dylib -o imageorder2.dylib -dynamiclib
+ $C{COMPILE} $DIR/imageorder3.m -x none imageorder2.dylib imageorder1.dylib -o imageorder3.dylib -dynamiclib
+ $C{COMPILE} $DIR/imageorder.m -x none imageorder3.dylib imageorder2.dylib imageorder1.dylib -o imageorder.out
+END
+*/
+
#include "test.h"
#include "imageorder.h"
#include <objc/runtime.h>
[Super method3];
testassert(state == 3);
- // make sure imageorder3.out is the last category to attach
+ // make sure imageorder3.dylib is the last category to attach
state = 0;
[Super method];
testassert(state == 3);
+// TEST_CONFIG
+
// initialize.m
// Test basic +initialize behavior
// * +initialize before class method
+// TEST_CONFIG
+
#include "test.h"
#include <objc/runtime.h>
+// TEST_CONFIG
+
#include "test.h"
#include <objc/objc-runtime.h>
+// TEST_CONFIG
+
#include "test.h"
#include <stdint.h>
#include <string.h>
ivar = class_getInstanceVariable([Sub class], "subIvar");
testassert(ivar);
- testassert(2*sizeof(intptr_t) == ivar_getOffset(ivar));
+ testassert(2*sizeof(intptr_t) == (size_t)ivar_getOffset(ivar));
testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
#if __LP64__
testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "Q"));
+#elif __clang__
+ testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "L"));
#else
testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "I"));
#endif
ivar = class_getInstanceVariable([Super class], "superIvar");
testassert(ivar);
- testassert(sizeof(intptr_t) == ivar_getOffset(ivar));
+ testassert(sizeof(intptr_t) == (size_t)ivar_getOffset(ivar));
testassert(0 == strcmp(ivar_getName(ivar), "superIvar"));
testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "c"));
testassert(ivar == class_getInstanceVariable([Sub class], "superIvar"));
--- /dev/null
+/*
+TEST_BUILD
+ $C{COMPILE} $DIR/ivarSlide1.m $DIR/ivarSlide.m -o ivarSlide.out
+END
+*/
+
+#include "test.h"
+#include <string.h>
+#include <stdint.h>
+#include <objc/objc-runtime.h>
+#include <objc/objc-auto.h>
+
+#define OLD 1
+#include "ivarSlide.h"
+
+#define ustrcmp(a, b) strcmp((char *)a, (char *)b)
+
+#ifdef __cplusplus
+class CXX {
+ public:
+ static uintptr_t count;
+ uintptr_t magic;
+ CXX() : magic(1) { }
+ ~CXX() { count += magic; }
+};
+uintptr_t CXX::count;
+#endif
+
+@interface Bitfields : Super {
+ uint8_t uint8_ivar;
+ uint8_t uint8_bitfield1 :7;
+ uint8_t uint8_bitfield2 :1;
+
+ id id_ivar;
+
+ uintptr_t uintptr_ivar;
+ uintptr_t /*uintptr_bitfield1*/ :31; // anonymous (rdar://5723893)
+ uintptr_t uintptr_bitfield2 :1;
+
+ id id_ivar2;
+}
+@end
+
+@implementation Bitfields @end
+
+
+@interface Sub : Super {
+ @public
+ uintptr_t subIvar;
+ __strong void* subIvar2;
+ __weak void* subIvar3;
+#ifdef __cplusplus
+ CXX cxx;
+#else
+ // same layout as cxx
+ uintptr_t cxx_magic;
+#endif
+}
+@end
+
+@implementation Sub @end
+
+
+@interface Sub2 : ShrinkingSuper {
+ @public
+ __weak void* subIvar;
+ __strong void* subIvar2;
+}
+@end
+
+@implementation Sub2 @end
+
+@interface MoreStrongSub : MoreStrongSuper { id subIvar; } @end
+@interface LessStrongSub : LessStrongSuper { id subIvar; } @end
+@interface MoreWeakSub : MoreWeakSuper { id subIvar; } @end
+@interface MoreWeak2Sub : MoreWeak2Super { id subIvar; } @end
+@interface LessWeakSub : LessWeakSuper { id subIvar; } @end
+@interface LessWeak2Sub : LessWeak2Super { id subIvar; } @end
+
+@implementation MoreStrongSub @end
+@implementation LessStrongSub @end
+@implementation MoreWeakSub @end
+@implementation MoreWeak2Sub @end
+@implementation LessWeakSub @end
+@implementation LessWeak2Sub @end
+
+@interface NoGCChangeSub : NoGCChangeSuper {
+ @public
+ char subc3;
+}
+@end
+@implementation NoGCChangeSub @end
+
+@interface RunsOf15Sub : RunsOf15 {
+ @public
+ char sub;
+}
+@end
+@implementation RunsOf15Sub @end
+
+
+int main(int argc __attribute__((unused)), char **argv)
+{
+#if __OBJC2__
+
+ /*
+ Bitfield ivars.
+ rdar://5723893 anonymous bitfield ivars crash when slid
+ rdar://5724385 bitfield ivar alignment incorrect
+
+ Compile-time layout of Bitfields:
+ [0 scan] isa
+ [1 skip] uint8_ivar, uint8_bitfield
+ [2 scan] id_ivar
+ [3 skip] uintptr_ivar
+ [4 skip] uintptr_bitfield
+ [5 scan] id_ivar2
+
+ Runtime layout of Bitfields:
+ [0 scan] isa
+ [1 skip] superIvar
+ [2 skip] uint8_ivar, uint8_bitfield
+ [3 scan] id_ivar
+ [4 skip] uintptr_ivar
+ [5 skip] uintptr_bitfield
+ [6 scan] id_ivar2
+ */
+
+ [Bitfields class];
+
+ testassert(class_getInstanceSize([Bitfields class]) == 7*sizeof(void*));
+
+ if (objc_collectingEnabled()) {
+ const uint8_t *bitfieldlayout;
+ bitfieldlayout = class_getIvarLayout([Bitfields class]);
+ testassert(0 == ustrcmp(bitfieldlayout, "\x01\x21\x21"));
+
+ bitfieldlayout = class_getWeakIvarLayout([Bitfields class]);
+ testassert(bitfieldlayout == NULL);
+ }
+
+ /*
+ Compile-time layout of Sub:
+ [0 scan] isa
+ [1 skip] subIvar
+ [2 scan] subIvar2
+ [3 weak] subIvar3
+ [6 skip] cxx
+
+ Runtime layout of Sub:
+ [0 scan] isa
+ [1 skip] superIvar
+ [2 skip] subIvar
+ [3 scan] subIvar2
+ [4 weak] subIvar3
+ [6 skip] cxx
+
+ Also, superIvar is only one byte, so subIvar's alignment must
+ be handled correctly.
+
+ fixme test more layouts
+ */
+
+ Ivar ivar;
+ static Sub * volatile sub;
+ sub = [Sub new];
+ sub->subIvar = 10;
+ testassert(((uintptr_t *)sub)[2] == 10);
+
+#ifdef __cplusplus
+ testassert(((uintptr_t *)sub)[5] == 1);
+ testassert(sub->cxx.magic == 1);
+ sub->cxx.magic++;
+ testassert(((uintptr_t *)sub)[5] == 2);
+ testassert(sub->cxx.magic == 2);
+ if (! objc_collectingEnabled()) {
+ [sub dealloc];
+ } else {
+ // hack - can't get collector to reliably delete the object
+ object_dispose(sub);
+ }
+ testassert(CXX::count == 2);
+#endif
+
+ testassert(class_getInstanceSize([Sub class]) == 6*sizeof(void*));
+
+ ivar = class_getInstanceVariable([Sub class], "subIvar");
+ testassert(ivar);
+ testassert(2*sizeof(void*) == (size_t)ivar_getOffset(ivar));
+ testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
+ // rdar://7466570 clang miscompiles assert(#if __LP64__ ... #endif)
+#if __LP64__
+ testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "Q"));
+#elif __clang__
+ testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "L"));
+#else
+ testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "I"));
+#endif
+
+#ifdef __cplusplus
+ ivar = class_getInstanceVariable([Sub class], "cxx");
+ testassert(ivar);
+#endif
+
+ ivar = class_getInstanceVariable([Super class], "superIvar");
+ testassert(ivar);
+ testassert(sizeof(void*) == (size_t)ivar_getOffset(ivar));
+ testassert(0 == strcmp(ivar_getName(ivar), "superIvar"));
+ testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "c"));
+
+ ivar = class_getInstanceVariable([Super class], "subIvar");
+ testassert(!ivar);
+
+ if (objc_collectingEnabled()) {
+ const uint8_t *superlayout;
+ const uint8_t *sublayout;
+ superlayout = class_getIvarLayout([Super class]);
+ sublayout = class_getIvarLayout([Sub class]);
+ testassert(0 == ustrcmp(superlayout, "\x01\x10"));
+ testassert(0 == ustrcmp(sublayout, "\x01\x21\x20"));
+
+ superlayout = class_getWeakIvarLayout([Super class]);
+ sublayout = class_getWeakIvarLayout([Sub class]);
+ testassert(superlayout == NULL);
+ testassert(0 == ustrcmp(sublayout, "\x41\x10"));
+ }
+
+ /*
+ Shrinking superclass.
+ Subclass ivars do not compact, but the GC layout needs to
+ update, including the gap that the superclass no longer spans.
+
+ Compile-time layout of Sub2:
+ [0 scan] isa
+ [1-5 scan] superIvar
+ [6-10 weak] superIvar2
+ [11 weak] subIvar
+ [12 scan] subIvar2
+
+ Runtime layout of Sub2:
+ [0 scan] isa
+ [1-10 skip] was superIvar
+ [11 weak] subIvar
+ [12 scan] subIvar2
+ */
+
+ Sub2 *sub2 = [Sub2 new];
+ sub2->isa = [Sub2 class];
+ sub2->subIvar = (void *)10;
+ testassert(((uintptr_t *)sub2)[11] == 10);
+
+ testassert(class_getInstanceSize([Sub2 class]) == 13*sizeof(void*));
+
+ ivar = class_getInstanceVariable([Sub2 class], "subIvar");
+ testassert(ivar);
+ testassert(11*sizeof(void*) == (size_t)ivar_getOffset(ivar));
+ testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
+
+ ivar = class_getInstanceVariable([ShrinkingSuper class], "superIvar");
+ testassert(!ivar);
+
+ if (objc_collectingEnabled()) {
+ const uint8_t *superlayout;
+ const uint8_t *sublayout;
+ superlayout = class_getIvarLayout([ShrinkingSuper class]);
+ sublayout = class_getIvarLayout([Sub2 class]);
+ // only `isa` is left; superIvar[] and superIvar2[] are gone
+ testassert(superlayout == NULL || 0 == ustrcmp(superlayout, "\x01"));
+ testassert(0 == ustrcmp(sublayout, "\x01\xb1"));
+
+ superlayout = class_getWeakIvarLayout([ShrinkingSuper class]);
+ sublayout = class_getWeakIvarLayout([Sub2 class]);
+ testassert(superlayout == NULL);
+ testassert(0 == ustrcmp(sublayout, "\xb1\x10"));
+ }
+
+ /*
+ Ivars slide but GC layouts stay the same
+ Here, the last word of the superclass is misaligned, but
+ its GC layout includes a bit for that whole word.
+ Additionally, all of the subclass ivars fit into that word too,
+ both before and after sliding.
+ The runtime will try to slide the GC layout and must not be
+ confused (rdar://6851700). Note that the second skip-word may or may
+ not actually be included, because it crosses the end of the object.
+
+
+ Compile-time layout of NoGCChangeSub:
+ [0 scan] isa
+ [1 skip] d
+ [2 skip] superc1, subc3
+
+ Runtime layout of NoGCChangeSub:
+ [0 scan] isa
+ [1 skip] d
+ [2 skip] superc1, superc2, subc3
+ */
+ if (objc_collectingEnabled()) {
+ Ivar ivar1 = class_getInstanceVariable([NoGCChangeSub class], "superc1");
+ testassert(ivar1);
+ Ivar ivar2 = class_getInstanceVariable([NoGCChangeSub class], "superc2");
+ testassert(ivar2);
+ Ivar ivar3 = class_getInstanceVariable([NoGCChangeSub class], "subc3");
+ testassert(ivar3);
+ testassert(ivar_getOffset(ivar1) != ivar_getOffset(ivar2) &&
+ ivar_getOffset(ivar1) != ivar_getOffset(ivar3) &&
+ ivar_getOffset(ivar2) != ivar_getOffset(ivar3));
+ }
+
+ /* Ivar layout includes runs of 15 words.
+ rdar://6859875 this would generate a truncated GC layout.
+ */
+ if (objc_collectingEnabled()) {
+ const uint8_t *layout =
+ class_getIvarLayout(objc_getClass("RunsOf15Sub"));
+ testassert(layout);
+ int totalSkip = 0;
+ int totalScan = 0;
+ // should find 30+ each of skip and scan
+ uint8_t c;
+ while ((c = *layout++)) {
+ totalSkip += c>>4;
+ totalScan += c&0xf;
+ }
+ testassert(totalSkip >= 30);
+ testassert(totalScan >= 30);
+ }
+
+// __OBJC2__
+#endif
+
+
+ /*
+ Non-strong -> strong
+ Classes do not change size, but GC layouts must be updated.
+ Both new and old ABI detect this case (rdar://5774578)
+
+ Compile-time layout of MoreStrongSub:
+ [0 scan] isa
+ [1 skip] superIvar
+ [2 scan] subIvar
+
+ Runtime layout of MoreStrongSub:
+ [0 scan] isa
+ [1 scan] superIvar
+ [2 scan] subIvar
+ */
+ testassert(class_getInstanceSize([MoreStrongSub class]) == 3*sizeof(void*));
+ if (objc_collectingEnabled()) {
+ const uint8_t *layout;
+ layout = class_getIvarLayout([MoreStrongSub class]);
+ testassert(layout == NULL);
+
+ layout = class_getWeakIvarLayout([MoreStrongSub class]);
+ testassert(layout == NULL);
+ }
+
+
+ /*
+ Strong -> weak
+ Classes do not change size, but GC layouts must be updated.
+ Old ABI intentionally does not detect this case (rdar://5774578)
+
+ Compile-time layout of MoreWeakSub:
+ [0 scan] isa
+ [1 scan] superIvar
+ [2 scan] subIvar
+
+ Runtime layout of MoreWeakSub:
+ [0 scan] isa
+ [1 weak] superIvar
+ [2 scan] subIvar
+ */
+ testassert(class_getInstanceSize([MoreWeakSub class]) == 3*sizeof(void*));
+ if (objc_collectingEnabled()) {
+ const uint8_t *layout;
+ layout = class_getIvarLayout([MoreWeakSub class]);
+#if __OBJC2__
+ // fixed version: scan / weak / scan
+ testassert(0 == ustrcmp(layout, "\x01\x11"));
+#else
+ // unfixed version: scan / scan / scan
+ testassert(layout == NULL || 0 == ustrcmp(layout, "\x03"));
+#endif
+
+ layout = class_getWeakIvarLayout([MoreWeakSub class]);
+#if __OBJC2__
+ testassert(0 == ustrcmp(layout, "\x11\x10"));
+#else
+ testassert(layout == NULL);
+#endif
+ }
+
+
+ /*
+ Non-strong -> weak
+ Classes do not change size, but GC layouts must be updated.
+ Old ABI intentionally does not detect this case (rdar://5774578)
+
+ Compile-time layout of MoreWeak2Sub:
+ [0 scan] isa
+ [1 skip] superIvar
+ [2 scan] subIvar
+
+ Runtime layout of MoreWeak2Sub:
+ [0 scan] isa
+ [1 weak] superIvar
+ [2 scan] subIvar
+ */
+ testassert(class_getInstanceSize([MoreWeak2Sub class]) == 3*sizeof(void*));
+ if (objc_collectingEnabled()) {
+ const uint8_t *layout;
+ layout = class_getIvarLayout([MoreWeak2Sub class]);
+ testassert(0 == ustrcmp(layout, "\x01\x11") ||
+ 0 == ustrcmp(layout, "\x01\x10\x01"));
+
+ layout = class_getWeakIvarLayout([MoreWeak2Sub class]);
+#if __OBJC2__
+ testassert(0 == ustrcmp(layout, "\x11\x10"));
+#else
+ testassert(layout == NULL);
+#endif
+ }
+
+
+ /*
+ Strong -> non-strong
+ Classes do not change size, but GC layouts must be updated.
+ Old ABI intentionally does not detect this case (rdar://5774578)
+
+ Compile-time layout of LessStrongSub:
+ [0 scan] isa
+ [1 scan] superIvar
+ [2 scan] subIvar
+
+ Runtime layout of LessStrongSub:
+ [0 scan] isa
+ [1 skip] superIvar
+ [2 scan] subIvar
+ */
+ testassert(class_getInstanceSize([LessStrongSub class]) == 3*sizeof(void*));
+ if (objc_collectingEnabled()) {
+ const uint8_t *layout;
+ layout = class_getIvarLayout([LessStrongSub class]);
+#if __OBJC2__
+ // fixed version: scan / skip / scan
+ testassert(0 == ustrcmp(layout, "\x01\x11"));
+#else
+ // unfixed version: scan / scan / scan
+ testassert(layout == NULL || 0 == ustrcmp(layout, "\x03"));
+#endif
+
+ layout = class_getWeakIvarLayout([LessStrongSub class]);
+ testassert(layout == NULL);
+ }
+
+
+ /*
+ Weak -> strong
+ Classes do not change size, but GC layouts must be updated.
+ Both new and old ABI detect this case (rdar://5774578 rdar://6924114)
+
+ Compile-time layout of LessWeakSub:
+ [0 scan] isa
+ [1 weak] superIvar
+ [2 scan] subIvar
+
+ Runtime layout of LessWeakSub:
+ [0 scan] isa
+ [1 scan] superIvar
+ [2 scan] subIvar
+ */
+ testassert(class_getInstanceSize([LessWeakSub class]) == 3*sizeof(void*));
+ if (objc_collectingEnabled()) {
+ const uint8_t *layout;
+ layout = class_getIvarLayout([LessWeakSub class]);
+ testassert(layout == NULL);
+
+ layout = class_getWeakIvarLayout([LessWeakSub class]);
+ testassert(layout == NULL);
+ }
+
+
+ /*
+ Weak -> non-strong
+ Classes do not change size, but GC layouts must be updated.
+ Old ABI intentionally does not detect this case (rdar://5774578)
+
+ Compile-time layout of LessWeak2Sub:
+ [0 scan] isa
+ [1 weak] superIvar
+ [2 scan] subIvar
+
+ Runtime layout of LessWeak2Sub:
+ [0 scan] isa
+ [1 skip] superIvar
+ [2 scan] subIvar
+ */
+ testassert(class_getInstanceSize([LessWeak2Sub class]) == 3*sizeof(void*));
+ if (objc_collectingEnabled()) {
+ const uint8_t *layout;
+ layout = class_getIvarLayout([LessWeak2Sub class]);
+ testassert(0 == ustrcmp(layout, "\x01\x11") ||
+ 0 == ustrcmp(layout, "\x01\x10\x01"));
+
+ layout = class_getWeakIvarLayout([LessWeak2Sub class]);
+#if __OBJC2__
+ testassert(layout == NULL);
+#else
+ testassert(0 == ustrcmp(layout, "\x11\x10"));
+#endif
+ }
+
+
+ succeed(basename(argv[0]));
+ return 0;
+}
+++ /dev/null
-#include "test.h"
-#include <string.h>
-#include <stdint.h>
-#include <objc/objc-runtime.h>
-#include <objc/objc-auto.h>
-#include <auto_zone.h>
-
-#define OLD 1
-#include "ivarSlide.h"
-
-#ifdef __cplusplus
-class CXX {
- public:
- static uintptr_t count;
- uintptr_t magic;
- CXX() : magic(1) { }
- ~CXX() { count += magic; }
-};
-uintptr_t CXX::count;
-#endif
-
-@interface Bitfields : Super {
- uint8_t uint8_ivar;
- uint8_t uint8_bitfield1 :7;
- uint8_t uint8_bitfield2 :1;
-
- id id_ivar;
-
- uintptr_t uintptr_ivar;
- uintptr_t /*uintptr_bitfield1*/ :31; // anonymous (rdar://5723893)
- uintptr_t uintptr_bitfield2 :1;
-
- id id_ivar2;
-}
-@end
-
-@implementation Bitfields @end
-
-
-@interface Sub : Super {
- @public
- uintptr_t subIvar;
- __strong uintptr_t subIvar2;
- __weak uintptr_t subIvar3;
-#ifdef __cplusplus
- CXX cxx;
-#else
- // same layout as cxx
- uintptr_t cxx_magic;
-#endif
-}
-@end
-
-@implementation Sub @end
-
-
-@interface Sub2 : ShrinkingSuper {
- @public
- __weak uintptr_t subIvar;
- __strong uintptr_t subIvar2;
-}
-@end
-
-@implementation Sub2 @end
-
-@interface MoreStrongSub : MoreStrongSuper { id subIvar; } @end
-@interface LessStrongSub : LessStrongSuper { id subIvar; } @end
-@interface MoreWeakSub : MoreWeakSuper { id subIvar; } @end
-@interface MoreWeak2Sub : MoreWeak2Super { id subIvar; } @end
-@interface LessWeakSub : LessWeakSuper { id subIvar; } @end
-@interface LessWeak2Sub : LessWeak2Super { id subIvar; } @end
-
-@implementation MoreStrongSub @end
-@implementation LessStrongSub @end
-@implementation MoreWeakSub @end
-@implementation MoreWeak2Sub @end
-@implementation LessWeakSub @end
-@implementation LessWeak2Sub @end
-
-@interface NoGCChangeSub : NoGCChangeSuper {
- @public
- char subc3;
-}
-@end
-@implementation NoGCChangeSub @end
-
-@interface RunsOf15Sub : RunsOf15 {
- @public
- char sub;
-}
-@end
-@implementation RunsOf15Sub @end
-
-
-int main(int argc __attribute__((unused)), char **argv)
-{
- if (objc_collecting_enabled()) {
- objc_startCollectorThread();
- }
-
-#if __OBJC2__
-
- /*
- Bitfield ivars.
- rdar://5723893 anonymous bitfield ivars crash when slid
- rdar://5724385 bitfield ivar alignment incorrect
-
- Compile-time layout of Bitfields:
- [0 scan] isa
- [1 skip] uint8_ivar, uint8_bitfield
- [2 scan] id_ivar
- [3 skip] uintptr_ivar
- [4 skip] uintptr_bitfield
- [5 scan] id_ivar2
-
- Runtime layout of Bitfields:
- [0 scan] isa
- [1 skip] superIvar
- [2 skip] uint8_ivar, uint8_bitfield
- [3 scan] id_ivar
- [4 skip] uintptr_ivar
- [5 skip] uintptr_bitfield
- [6 scan] id_ivar2
- */
-
- [Bitfields class];
-
- testassert(class_getInstanceSize([Bitfields class]) == 7*sizeof(void*));
-
- if (objc_collecting_enabled()) {
- const char *bitfieldlayout;
- bitfieldlayout = class_getIvarLayout([Bitfields class]);
- testassert(0 == strcmp(bitfieldlayout, "\x01\x21\x21"));
-
- bitfieldlayout = class_getWeakIvarLayout([Bitfields class]);
- testassert(bitfieldlayout == NULL);
- }
-
- /*
- Compile-time layout of Sub:
- [0 scan] isa
- [1 skip] subIvar
- [2 scan] subIvar2
- [3 weak] subIvar3
- [6 skip] cxx
-
- Runtime layout of Sub:
- [0 scan] isa
- [1 skip] superIvar
- [2 skip] subIvar
- [3 scan] subIvar2
- [4 weak] subIvar3
- [6 skip] cxx
-
- Also, superIvar is only one byte, so subIvar's alignment must
- be handled correctly.
-
- fixme test more layouts
- */
-
- Ivar ivar;
- static Sub * volatile sub;
- sub = [Sub new];
- sub->subIvar = 10;
- testassert(((uintptr_t *)sub)[2] == 10);
-
-#ifdef __cplusplus
- uintptr_t i;
- for (i = 0; i < 10; i++) {
- if (i != 0) sub = [Sub new];
- testassert(((uintptr_t *)sub)[5] == 1);
- testassert(sub->cxx.magic == 1);
- sub->cxx.magic++;
- testassert(((uintptr_t *)sub)[5] == 2);
- testassert(sub->cxx.magic == 2);
- if (! objc_collecting_enabled()) {
- [sub dealloc];
- }
- }
-
- if (objc_collecting_enabled()) {
- // only require one of the objects to be reclaimed
- objc_collect(OBJC_EXHAUSTIVE_COLLECTION | OBJC_WAIT_UNTIL_DONE);
- objc_collect(OBJC_EXHAUSTIVE_COLLECTION | OBJC_WAIT_UNTIL_DONE);
- testassert(CXX::count > 0 && CXX::count <= i*2 && CXX::count % 2 == 0);
- }
- else {
- testassert(CXX::count == i*2);
- }
-#endif
-
-
- testassert(class_getInstanceSize([Sub class]) == 6*sizeof(void*));
-
- ivar = class_getInstanceVariable([Sub class], "subIvar");
- testassert(ivar);
- testassert(2*sizeof(void*) == ivar_getOffset(ivar));
- testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
- testassert(0 == strcmp(ivar_getTypeEncoding(ivar),
-#if __LP64__
- "Q"
-#else
- "I"
-#endif
- ));
-
-#ifdef __cplusplus
- ivar = class_getInstanceVariable([Sub class], "cxx");
- testassert(ivar);
-#endif
-
- ivar = class_getInstanceVariable([Super class], "superIvar");
- testassert(ivar);
- testassert(sizeof(void*) == ivar_getOffset(ivar));
- testassert(0 == strcmp(ivar_getName(ivar), "superIvar"));
- testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "c"));
-
- ivar = class_getInstanceVariable([Super class], "subIvar");
- testassert(!ivar);
-
- if (objc_collecting_enabled()) {
- const char *superlayout;
- const char *sublayout;
- superlayout = class_getIvarLayout([Super class]);
- sublayout = class_getIvarLayout([Sub class]);
- testassert(0 == strcmp(superlayout, "\x01\x10"));
- testassert(0 == strcmp(sublayout, "\x01\x21\x20"));
-
- superlayout = class_getWeakIvarLayout([Super class]);
- sublayout = class_getWeakIvarLayout([Sub class]);
- testassert(superlayout == NULL);
- testassert(0 == strcmp(sublayout, "\x41\x10"));
- }
-
- /*
- Shrinking superclass.
- Subclass ivars do not compact, but the GC layout needs to
- update, including the gap that the superclass no longer spans.
-
- Compile-time layout of Sub2:
- [0 scan] isa
- [1-5 scan] superIvar
- [6-10 weak] superIvar2
- [11 weak] subIvar
- [12 scan] subIvar2
-
- Runtime layout of Sub2:
- [0 scan] isa
- [1-10 skip] was superIvar
- [11 weak] subIvar
- [12 scan] subIvar2
- */
-
- Sub2 *sub2 = [Sub2 new];
- sub2->isa = [Sub2 class];
- sub2->subIvar = 10;
- testassert(((uintptr_t *)sub2)[11] == 10);
-
- testassert(class_getInstanceSize([Sub2 class]) == 13*sizeof(void*));
-
- ivar = class_getInstanceVariable([Sub2 class], "subIvar");
- testassert(ivar);
- testassert(11*sizeof(void*) == ivar_getOffset(ivar));
- testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
-
- ivar = class_getInstanceVariable([ShrinkingSuper class], "superIvar");
- testassert(!ivar);
-
- if (objc_collecting_enabled()) {
- const char *superlayout;
- const char *sublayout;
- superlayout = class_getIvarLayout([ShrinkingSuper class]);
- sublayout = class_getIvarLayout([Sub2 class]);
- testassert(superlayout == NULL);
- testassert(0 == strcmp(sublayout, "\x01\xb1"));
-
- superlayout = class_getWeakIvarLayout([ShrinkingSuper class]);
- sublayout = class_getWeakIvarLayout([Sub2 class]);
- testassert(superlayout == NULL);
- testassert(0 == strcmp(sublayout, "\xb1\x10"));
- }
-
- /*
- Ivars slide but GC layouts stay the same
- Here, the last word of the superclass is misaligned, but
- its GC layout includes a bit for that whole word.
- Additionally, all of the subclass ivars fit into that word too,
- both before and after sliding.
- The runtime will try to slide the GC layout and must not be
- confused (rdar://6851700). Note that the second skip-word may or may
- not actually be included, because it crosses the end of the object.
-
-
- Compile-time layout of NoGCChangeSub:
- [0 scan] isa
- [1 skip] d
- [2 skip] superc1, subc3
-
- Runtime layout of NoGCChangeSub:
- [0 scan] isa
- [1 skip] d
- [2 skip] superc1, superc2, subc3
- */
- if (objc_collecting_enabled()) {
- Ivar ivar1 = class_getInstanceVariable([NoGCChangeSub class], "superc1");
- testassert(ivar1);
- Ivar ivar2 = class_getInstanceVariable([NoGCChangeSub class], "superc2");
- testassert(ivar2);
- Ivar ivar3 = class_getInstanceVariable([NoGCChangeSub class], "subc3");
- testassert(ivar3);
- testassert(ivar_getOffset(ivar1) != ivar_getOffset(ivar2) &&
- ivar_getOffset(ivar1) != ivar_getOffset(ivar3) &&
- ivar_getOffset(ivar2) != ivar_getOffset(ivar3));
- }
-
- /* Ivar layout includes runs of 15 words.
- rdar://6859875 this would generate a truncated GC layout.
- */
- if (objc_collecting_enabled()) {
- const uint8_t *layout = (const uint8_t *)
- class_getIvarLayout(objc_getClass("RunsOf15Sub"));
- testassert(layout);
- int totalSkip = 0;
- int totalScan = 0;
- // should find 30+ each of skip and scan
- uint8_t c;
- while ((c = *layout++)) {
- totalSkip += c>>4;
- totalScan += c&0xf;
- }
- testassert(totalSkip >= 30);
- testassert(totalScan >= 30);
- }
-
-// __OBJC2__
-#endif
-
-
- /*
- Non-strong -> strong
- Classes do not change size, but GC layouts must be updated.
- Both new and old ABI detect this case (rdar://5774578)
-
- Compile-time layout of MoreStrongSub:
- [0 scan] isa
- [1 skip] superIvar
- [2 scan] subIvar
-
- Runtime layout of MoreStrongSub:
- [0 scan] isa
- [1 scan] superIvar
- [2 scan] subIvar
- */
- testassert(class_getInstanceSize([MoreStrongSub class]) == 3*sizeof(void*));
- if (objc_collecting_enabled()) {
- const char *layout;
- layout = class_getIvarLayout([MoreStrongSub class]);
- testassert(layout == NULL);
-
- layout = class_getWeakIvarLayout([MoreStrongSub class]);
- testassert(layout == NULL);
- }
-
-
- /*
- Strong -> weak
- Classes do not change size, but GC layouts must be updated.
- Old ABI intentionally does not detect this case (rdar://5774578)
-
- Compile-time layout of MoreWeakSub:
- [0 scan] isa
- [1 scan] superIvar
- [2 scan] subIvar
-
- Runtime layout of MoreWeakSub:
- [0 scan] isa
- [1 weak] superIvar
- [2 scan] subIvar
- */
- testassert(class_getInstanceSize([MoreWeakSub class]) == 3*sizeof(void*));
- if (objc_collecting_enabled()) {
- const char *layout;
- layout = class_getIvarLayout([MoreWeakSub class]);
-#if __OBJC2__
- testassert(0 == strcmp(layout, "\x01\x11"));
-#else
- testassert(layout == NULL);
-#endif
-
- layout = class_getWeakIvarLayout([MoreWeakSub class]);
-#if __OBJC2__
- testassert(0 == strcmp(layout, "\x11\x10"));
-#else
- testassert(layout == NULL);
-#endif
- }
-
-
- /*
- Non-strong -> weak
- Classes do not change size, but GC layouts must be updated.
- Old ABI intentionally does not detect this case (rdar://5774578)
-
- Compile-time layout of MoreWeak2Sub:
- [0 scan] isa
- [1 skip] superIvar
- [2 scan] subIvar
-
- Runtime layout of MoreWeak2Sub:
- [0 scan] isa
- [1 weak] superIvar
- [2 scan] subIvar
- */
- testassert(class_getInstanceSize([MoreWeak2Sub class]) == 3*sizeof(void*));
- if (objc_collecting_enabled()) {
- const char *layout;
- layout = class_getIvarLayout([MoreWeak2Sub class]);
- testassert(0 == strcmp(layout, "\x01\x11") ||
- 0 == strcmp(layout, "\x01\x10\x01"));
-
- layout = class_getWeakIvarLayout([MoreWeak2Sub class]);
-#if __OBJC2__
- testassert(0 == strcmp(layout, "\x11\x10"));
-#else
- testassert(layout == NULL);
-#endif
- }
-
-
- /*
- Strong -> non-strong
- Classes do not change size, but GC layouts must be updated.
- Old ABI intentionally does not detect this case (rdar://5774578)
-
- Compile-time layout of LessStrongSub:
- [0 scan] isa
- [1 scan] superIvar
- [2 scan] subIvar
-
- Runtime layout of LessStrongSub:
- [0 scan] isa
- [1 skip] superIvar
- [2 scan] subIvar
- */
- testassert(class_getInstanceSize([LessStrongSub class]) == 3*sizeof(void*));
- if (objc_collecting_enabled()) {
- const char *layout;
- layout = class_getIvarLayout([LessStrongSub class]);
-#if __OBJC2__
- testassert(0 == strcmp(layout, "\x01\x11"));
-#else
- testassert(layout == NULL);
-#endif
-
- layout = class_getWeakIvarLayout([LessStrongSub class]);
- testassert(layout == NULL);
- }
-
-
- /*
- Weak -> strong
- Classes do not change size, but GC layouts must be updated.
- Both new and old ABI detect this case (rdar://5774578 rdar://6924114)
-
- Compile-time layout of LessWeakSub:
- [0 scan] isa
- [1 weak] superIvar
- [2 scan] subIvar
-
- Runtime layout of LessWeakSub:
- [0 scan] isa
- [1 scan] superIvar
- [2 scan] subIvar
- */
- testassert(class_getInstanceSize([LessWeakSub class]) == 3*sizeof(void*));
- if (objc_collecting_enabled()) {
- const char *layout;
- layout = class_getIvarLayout([LessWeakSub class]);
- testassert(layout == NULL);
-
- layout = class_getWeakIvarLayout([LessWeakSub class]);
- testassert(layout == NULL);
- }
-
-
- /*
- Weak -> non-strong
- Classes do not change size, but GC layouts must be updated.
- Old ABI intentionally does not detect this case (rdar://5774578)
-
- Compile-time layout of LessWeak2Sub:
- [0 scan] isa
- [1 weak] superIvar
- [2 scan] subIvar
-
- Runtime layout of LessWeak2Sub:
- [0 scan] isa
- [1 skip] superIvar
- [2 scan] subIvar
- */
- testassert(class_getInstanceSize([LessWeak2Sub class]) == 3*sizeof(void*));
- if (objc_collecting_enabled()) {
- const char *layout;
- layout = class_getIvarLayout([LessWeak2Sub class]);
- testassert(0 == strcmp(layout, "\x01\x11") ||
- 0 == strcmp(layout, "\x01\x10\x01"));
-
- layout = class_getWeakIvarLayout([LessWeak2Sub class]);
-#if __OBJC2__
- testassert(layout == NULL);
-#else
- testassert(0 == strcmp(layout, "\x11\x10"));
-#endif
- }
-
-
- succeed(basename(argv[0]));
- return 0;
-}
+// TEST_CONFIG GC=1 SDK=macos
+
#include "test.h"
#include <string.h>
#include <objc/objc-runtime.h>
@class NSObject;
-void printlayout(const char *name, const char *layout)
+void printlayout(const char *name, const uint8_t *layout)
{
testprintf("%s: ", name);
return;
}
- const char *c;
+ const uint8_t *c;
for (c = layout; *c; c++) {
testprintf("%02x ", *c);
}
id id1;
NSObject *o1;
__strong void *v1;
- __strong intptr_t i1;
- __strong long l1;
+ __strong intptr_t *i1;
+ __strong long *l1;
/* fixme
struct {
id id1;
__weak id id1;
__weak NSObject *o1;
__weak void *v1;
- __weak intptr_t i1;
- __weak long l1;
+ __weak intptr_t *i1;
+ __weak long *l1;
/* fixme
struct {
__weak id id1;
int main()
{
- const char *layout;
+ const uint8_t *layout;
layout = class_getIvarLayout(objc_getClass("AllScanned"));
printlayout("AllScanned", layout);
+/*
+TEST_BUILD
+ $C{COMPILE} $DIR/load-order3.m -o load-order3.dylib -dynamiclib
+ $C{COMPILE} $DIR/load-order2.m -o load-order2.dylib -x none load-order3.dylib -dynamiclib
+ $C{COMPILE} $DIR/load-order1.m -o load-order1.dylib -x none load-order3.dylib load-order2.dylib -dynamiclib
+ $C{COMPILE} $DIR/load-order.m -o load-order.out -x none load-order3.dylib load-order2.dylib load-order1.dylib
+END
+*/
+
#include "test.h"
extern int state1, state2, state3;
+/*
+TEST_BUILD
+ $C{COMPILE} $DIR/load-parallel00.m -o load-parallel00.dylib -dynamiclib
+ $C{COMPILE} $DIR/load-parallel.m -x none load-parallel00.dylib -o load-parallel.out -DCOUNT=10
+
+ $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel0.dylib -dynamiclib -DN=0
+ $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel1.dylib -dynamiclib -DN=1
+ $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel2.dylib -dynamiclib -DN=2
+ $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel3.dylib -dynamiclib -DN=3
+ $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel4.dylib -dynamiclib -DN=4
+ $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel5.dylib -dynamiclib -DN=5
+ $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel6.dylib -dynamiclib -DN=6
+ $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel7.dylib -dynamiclib -DN=7
+ $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel8.dylib -dynamiclib -DN=8
+ $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel9.dylib -dynamiclib -DN=9
+END
+*/
+
#include "test.h"
#include <dlfcn.h>
objc_registerThreadWithCollector();
- asprintf(&buf, "load-parallel%lu.out", (unsigned long)num);
+ asprintf(&buf, "load-parallel%lu.dylib", (unsigned long)num);
testprintf("%s\n", buf);
void *dlh = dlopen(buf, RTLD_LAZY);
if (!dlh) {
fail("dlopen failed: %s", dlerror());
}
+ free(buf);
return NULL;
}
int main()
{
+#if TARGET_IPHONE_SIMULATOR
+ testwarn("simulator hangs calling dlopen() from +load");
+ succeed(__FILE__);
+#else
pthread_t t[COUNT];
uintptr_t i;
testassert(state == COUNT*26);
succeed(__FILE__);
+#endif
}
+/*
+TEST_BUILD
+ $C{COMPILE} $DIR/load-reentrant.m -o load-reentrant.out
+ $C{COMPILE} $DIR/load-reentrant2.m -o libload-reentrant2.dylib -bundle -bundle_loader load-reentrant.out
+END
+*/
+
#include "test.h"
#include <dlfcn.h>
+// TEST_CONFIG
+
#include "test.h"
int state = 0;
+++ /dev/null
-#include "test.h"
-
-@interface Main @end
-@implementation Main @end
-
-int main(int argc __attribute__((unused)), char **argv)
-{
- succeed(basename(argv[0]));
-}
+// TEST_CFLAGS -Wno-deprecated-declarations
+
#include "test.h"
#include <string.h>
#include <objc/objc-runtime.h>
-#include <objc/Protocol.h>
@interface Super { id isa; } @end
@implementation Super
+(void)initialize { }
+class { return self; }
-+(id)method:(int)arg { return (id)(intptr_t)arg; }
++(id)method:(int)arg :(void(^)(void))arg2 {
+ return (id)((intptr_t)arg+(intptr_t)arg2);
+}
@end
char buf[128];
char *arg;
struct objc_method_description *desc;
- Method m = class_getClassMethod([Super class], sel_registerName("method:"));
+ Method m = class_getClassMethod([Super class], sel_registerName("method::"));
testassert(m);
- testassert(method_getNumberOfArguments(m) == 3);
+ testassert(method_getNumberOfArguments(m) == 4);
#if !__OBJC2__
- testassert(method_getSizeOfArguments(m) == 12);
+ testassert(method_getSizeOfArguments(m) == 16);
#endif
arg = method_copyArgumentType(m, 0);
free(arg);
arg = method_copyArgumentType(m, 3);
+ testassert(arg);
+ testassert(0 == strcmp(arg, "@?"));
+ memset(buf, 1, 128);
+ method_getArgumentType(m, 3, buf, 1+strlen(arg));
+ testassert(0 == strcmp(arg, buf));
+ testassert(buf[1+strlen(arg)] == 1);
+ memset(buf, 1, 128);
+ method_getArgumentType(m, 3, buf, 2);
+ testassert(0 == strncmp(arg, buf, 2));
+ testassert(buf[2] == 1);
+ memset(buf, 1, 128);
+ method_getArgumentType(m, 3, buf, 3);
+ testassert(0 == strncmp(arg, buf, 3));
+ testassert(buf[3] == 1);
+ free(arg);
+
+ arg = method_copyArgumentType(m, 4);
testassert(!arg);
arg = method_copyArgumentType(m, -1);
testassert(!arg);
memset(buf, 1, 128);
- method_getArgumentType(m, 3, buf, 127);
+ method_getArgumentType(m, 4, buf, 127);
testassert(buf[0] == 0);
testassert(buf[1] == 0);
testassert(buf[127] == 1);
desc = method_getDescription(m);
testassert(desc);
- testassert(desc->name == sel_registerName("method:"));
+ testassert(desc->name == sel_registerName("method::"));
#if __LP64__
- testassert(0 == strcmp(desc->types, "@20@0:8i16"));
+ testassert(0 == strcmp(desc->types, "@28@0:8i16@?20"));
#else
- testassert(0 == strcmp(desc->types, "@12@0:4i8"));
+ testassert(0 == strcmp(desc->types, "@16@0:4i8@?12"));
#endif
testassert(0 == method_getNumberOfArguments(NULL));
--- /dev/null
+// TEST_CONFIG
+
+// rdar://8052003 rdar://8077031
+
+#include <Foundation/Foundation.h>
+#include <malloc/malloc.h>
+#include <objc/runtime.h>
+#include "test.h"
+
+// add SELCOUNT methods to each of CLASSCOUNT classes
+#define CLASSCOUNT 100
+#define SELCOUNT 200
+
+int main()
+{
+ int i, j;
+ malloc_statistics_t start, end;
+
+ Class root;
+ root = objc_allocateClassPair(NULL, "Root", 0);
+ objc_registerClassPair(root);
+
+ Class classes[CLASSCOUNT];
+ for (i = 0; i < CLASSCOUNT; i++) {
+ char *classname;
+ asprintf(&classname, "GrP_class_%d", i);
+ classes[i] = objc_allocateClassPair(root, classname, 0);
+ objc_registerClassPair(classes[i]);
+ free(classname);
+ }
+
+ SEL selectors[SELCOUNT];
+ for (i = 0; i < SELCOUNT; i++) {
+ char *selname;
+ asprintf(&selname, "GrP_sel_%d", i);
+ selectors[i] = sel_registerName(selname);
+ free(selname);
+ }
+
+ malloc_zone_statistics(NULL, &start);
+
+ for (i = 0; i < CLASSCOUNT; i++) {
+ for (j = 0; j < SELCOUNT; j++) {
+ class_addMethod(classes[i], selectors[j], (IMP)main, "");
+ }
+ }
+
+ malloc_zone_statistics(NULL, &end);
+
+ // expected: 3-word method struct plus two other words
+ ssize_t expected = (sizeof(void*) * (3+2)) * SELCOUNT * CLASSCOUNT;
+ ssize_t actual = end.size_in_use - start.size_in_use;
+ testassert(actual < expected * 3); // allow generous fudge factor
+
+ succeed(__FILE__);
+}
+// TEST_CFLAGS -framework Foundation
+
#include "test.h"
#include <Foundation/NSObject.h>
#include <objc/runtime.h>
-#include "../runtime/objc-rtp.h"
+#include "../runtime/objc-config.h"
int main() {
unsigned i;
for (i=0; i<numMethods; ++i) {
// <rdar://problem/6190950> method_getName crash on NSObject method when GC is enabled
- SEL aMethod = method_getName(methods[i]);
+ SEL aMethod;
+ aMethod = method_getName(methods[i]);
+#if defined(kIgnore)
if (aMethod == (SEL)kIgnore)
fail(__FILE__);
+#endif
}
succeed(__FILE__);
+// TEST_CONFIG
+
#include "test.h"
+
+#ifdef __cplusplus
+
+int main()
+{
+ testwarn("c++ rdar://8366474 @selector(foo::)");
+ succeed(__FILE__);
+}
+
+#else
+
#include <objc/objc.h>
#include <objc/objc-runtime.h>
+#include <objc/objc-abi.h>
+
+#if defined(__arm__)
+// rdar://8331406
+# define ALIGN_()
+#else
+# define ALIGN_() asm(".align 4");
+#endif
@interface Super { id isa; }
+class;
static int state = 0;
-#if defined(__ppc__) || defined(__ppc64__)
-// On ppc and ppc64, methods must be called with r12==IMP (i.e. indirect function call convention)
-#define CHECK_R12(cls) \
-do { \
- IMP val; \
- __asm__ volatile ("mr %[val], r12\n" : [val] "=r" (val)); \
- testassert(val == method_getImplementation(class_getClassMethod([cls class], _cmd))); \
-} while (0);
-#else
-#define CHECK_R12(cls) do {/* empty */} while (0)
-#endif
-
#define CHECK_ARGS(cls, sel) \
do { \
testassert(f15 == 15.0); \
} while (0)
+#define CHECK_ARGS_NOARG(cls, sel) \
+do { \
+ testassert(self == [cls class]); \
+ testassert(_cmd == sel_registerName(#sel "_noarg"));\
+} while (0)
+
struct stret {
int a;
int b;
int c;
int d;
int e;
- int pad[32]; // force stack return on ppc64
};
BOOL stret_equal(struct stret a, struct stret b)
id ID_RESULT = (id)0x12345678;
long long LL_RESULT = __LONG_LONG_MAX__ - 2LL*__INT_MAX__;
-struct stret STRET_RESULT = {1, 2, 3, 4, 5, {0}};
+struct stret STRET_RESULT = {1, 2, 3, 4, 5};
double FP_RESULT = __DBL_MIN__ + __DBL_EPSILON__;
long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
@implementation Super
+class { return self; }
++(struct stret)stret { return STRET_RESULT; }
+(void)initialize { }
+(id)idret:
(int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15
{
- CHECK_R12(Super);
- if (state == 10) CHECK_ARGS(Sub, idret);
+ if (state == 100) CHECK_ARGS(Sub, idret);
else CHECK_ARGS(Super, idret);
state = 1;
return ID_RESULT;
+(long long)llret:
(int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15
{
- CHECK_R12(Super);
- if (state == 10) CHECK_ARGS(Sub, llret);
+ if (state == 100) CHECK_ARGS(Sub, llret);
else CHECK_ARGS(Super, llret);
state = 2;
return LL_RESULT;
+(struct stret)stret:
(int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15
{
- CHECK_R12(Super);
- if (state == 10) CHECK_ARGS(Sub, stret);
+ if (state == 100) CHECK_ARGS(Sub, stret);
else CHECK_ARGS(Super, stret);
state = 3;
return STRET_RESULT;
+(double)fpret:
(int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15
{
- CHECK_R12(Super);
- if (state == 10) CHECK_ARGS(Sub, fpret);
+ if (state == 100) CHECK_ARGS(Sub, fpret);
else CHECK_ARGS(Super, fpret);
state = 4;
return FP_RESULT;
+(long double)lfpret:
(int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15
{
- CHECK_R12(Super);
- if (state == 10) CHECK_ARGS(Sub, lfpret);
+ if (state == 100) CHECK_ARGS(Sub, lfpret);
else CHECK_ARGS(Super, lfpret);
state = 5;
return LFP_RESULT;
}
++(id)idret_noarg
+{
+ if (state == 100) CHECK_ARGS_NOARG(Sub, idret);
+ else CHECK_ARGS_NOARG(Super, idret);
+ state = 11;
+ return ID_RESULT;
+}
+
++(long long)llret_noarg
+{
+ if (state == 100) CHECK_ARGS_NOARG(Sub, llret);
+ else CHECK_ARGS_NOARG(Super, llret);
+ state = 12;
+ return LL_RESULT;
+}
+
+/* no objc_msgSend_stret_noarg
++(struct stret)stret_noarg
+{
+ if (state == 100) CHECK_ARGS_NOARG(Sub, stret);
+ else CHECK_ARGS_NOARG(Super, stret);
+ state = 13;
+ return STRET_RESULT;
+}
+*/
+
++(double)fpret_noarg
+{
+ if (state == 100) CHECK_ARGS_NOARG(Sub, fpret);
+ else CHECK_ARGS_NOARG(Super, fpret);
+ state = 14;
+ return FP_RESULT;
+}
+
++(long double)lfpret_noarg
+{
+ if (state == 100) CHECK_ARGS_NOARG(Sub, lfpret);
+ else CHECK_ARGS_NOARG(Super, lfpret);
+ state = 15;
+ return LFP_RESULT;
+}
+
+
-(id)idret:
(int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15
{
CHECK_ARGS(Super, lfpret);
}
+-(id)idret_noarg
+{
+ fail("-idret_ called instead of +idret_noarg");
+ CHECK_ARGS_NOARG(Super, idret);
+}
+
+-(long long)llret_noarg
+{
+ fail("-llret_noarg called instead of +llret_noarg");
+ CHECK_ARGS_NOARG(Super, llret);
+}
+
+-(struct stret)stret_noarg
+{
+ fail("-stret_noarg called instead of +stret_noarg");
+ CHECK_ARGS_NOARG(Super, stret);
+}
+
+-(double)fpret_noarg
+{
+ fail("-fpret_noarg called instead of +fpret_noarg");
+ CHECK_ARGS_NOARG(Super, fpret);
+}
+
+-(long double)lfpret_noarg
+{
+ fail("-lfpret_noarg called instead of +lfpret_noarg");
+ CHECK_ARGS_NOARG(Super, lfpret);
+}
+
@end
(int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15
{
id result;
- CHECK_R12(Sub);
CHECK_ARGS(Sub, idret);
- state = 10;
+ state = 100;
result = [super idret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
testassert(state == 1);
testassert(result == ID_RESULT);
- state = 11;
+ state = 101;
return result;
}
(int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15
{
long long result;
- CHECK_R12(Sub);
CHECK_ARGS(Sub, llret);
- state = 10;
+ state = 100;
result = [super llret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
testassert(state == 2);
testassert(result == LL_RESULT);
- state = 12;
+ state = 102;
return result;
}
(int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15
{
struct stret result;
- CHECK_R12(Sub);
CHECK_ARGS(Sub, stret);
- state = 10;
+ state = 100;
result = [super stret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
testassert(state == 3);
testassert(stret_equal(result, STRET_RESULT));
- state = 13;
+ state = 103;
return result;
}
(int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15
{
double result;
- CHECK_R12(Sub);
CHECK_ARGS(Sub, fpret);
- state = 10;
+ state = 100;
result = [super fpret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
testassert(state == 4);
testassert(result == FP_RESULT);
- state = 14;
+ state = 104;
return result;
}
(int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15
{
long double result;
- CHECK_R12(Sub);
CHECK_ARGS(Sub, lfpret);
- state = 10;
+ state = 100;
result = [super lfpret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
testassert(state == 5);
testassert(result == LFP_RESULT);
- state = 15;
+ state = 105;
+ return result;
+}
+
+
++(id)idret_noarg
+{
+ id result;
+ CHECK_ARGS_NOARG(Sub, idret);
+ state = 100;
+ result = [super idret_noarg];
+ testassert(state == 11);
+ testassert(result == ID_RESULT);
+ state = 111;
+ return result;
+}
+
++(long long)llret_noarg
+{
+ long long result;
+ CHECK_ARGS_NOARG(Sub, llret);
+ state = 100;
+ result = [super llret_noarg];
+ testassert(state == 12);
+ testassert(result == LL_RESULT);
+ state = 112;
+ return result;
+}
+/*
++(struct stret)stret_noarg
+{
+ struct stret result;
+ CHECK_ARGS_NOARG(Sub, stret);
+ state = 100;
+ result = [super stret_noarg];
+ testassert(state == 13);
+ testassert(stret_equal(result, STRET_RESULT));
+ state = 113;
+ return result;
+}
+*/
++(double)fpret_noarg
+{
+ double result;
+ CHECK_ARGS_NOARG(Sub, fpret);
+ state = 100;
+ result = [super fpret_noarg];
+ testassert(state == 14);
+ testassert(result == FP_RESULT);
+ state = 114;
+ return result;
+}
+
++(long double)lfpret_noarg
+{
+ long double result;
+ CHECK_ARGS_NOARG(Sub, lfpret);
+ state = 100;
+ result = [super lfpret_noarg];
+ testassert(state == 15);
+ testassert(result == LFP_RESULT);
+ state = 115;
return result;
}
double (*fpfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
long double (*lfpfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
- struct stret zero = {0, 0, 0, 0, 0, {0}};
+ id (*idmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
+ long long (*llmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
+ struct stret (*stretmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
+ double (*fpmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
+ long double (*lfpmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
- // get +initialize out of the way
- [Sub class];
+ id (*idmsg0)(id, SEL) __attribute__((unused));
+ long long (*llmsg0)(id, SEL) __attribute__((unused));
+ // struct stret (*stretmsg0)(id, SEL) __attribute__((unused));
+ double (*fpmsg0)(id, SEL) __attribute__((unused));
+ long double (*lfpmsg0)(id, SEL) __attribute__((unused));
- idmethod = class_getClassMethod([Super class], @selector(idret::::::::::::::::::::::::::::));
- testassert(idmethod);
- llmethod = class_getClassMethod([Super class], @selector(llret::::::::::::::::::::::::::::));
- testassert(llmethod);
- stretmethod = class_getClassMethod([Super class], @selector(stret::::::::::::::::::::::::::::));
- testassert(stretmethod);
- fpmethod = class_getClassMethod([Super class], @selector(fpret::::::::::::::::::::::::::::));
- testassert(fpmethod);
- lfpmethod = class_getClassMethod([Super class], @selector(lfpret::::::::::::::::::::::::::::));
- testassert(lfpmethod);
+ struct stret zero = {0, 0, 0, 0, 0};
- idfn = (id (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
- llfn = (long long (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
- stretfn = (struct stret (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke_stret;
- fpfn = (double (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
- lfpfn = (long double (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
+ // get +initialize out of the way
+ [Sub class];
// message uncached
// message uncached long long
// message uncached stret
// message uncached fpret
// message uncached fpret long double
+ // message uncached noarg (as above)
// message cached
// message cached long long
// message cached stret
// message cached fpret
// message cached fpret long double
+ // message cached noarg (as above)
// fixme verify that uncached lookup didn't happen the 2nd time?
+ // Do this first before anything below caches stuff.
for (i = 0; i < 5; i++) {
state = 0;
idval = nil;
idval = [Sub idret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 11);
+ testassert(state == 101);
testassert(idval == ID_RESULT);
llval = 0;
llval = [Sub llret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 12);
+ testassert(state == 102);
testassert(llval == LL_RESULT);
stretval = zero;
stretval = [Sub stret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 13);
+ testassert(state == 103);
testassert(stret_equal(stretval, STRET_RESULT));
fpval = 0;
fpval = [Sub fpret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 14);
+ testassert(state == 104);
testassert(fpval == FP_RESULT);
lfpval = 0;
lfpval = [Sub lfpret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 15);
+ testassert(state == 105);
testassert(lfpval == LFP_RESULT);
+
+#if __OBJC2__
+ // explicitly call noarg messenger, even if compiler doesn't emit it
+ state = 0;
+ idval = nil;
+ idval = ((typeof(idmsg0))objc_msgSend_noarg)([Sub class], @selector(idret_noarg));
+ testassert(state == 111);
+ testassert(idval == ID_RESULT);
+
+ llval = 0;
+ llval = ((typeof(llmsg0))objc_msgSend_noarg)([Sub class], @selector(llret_noarg));
+ testassert(state == 112);
+ testassert(llval == LL_RESULT);
+ /*
+ stretval = zero;
+ stretval = ((typeof(stretmsg0))objc_msgSend_stret_noarg)([Sub class], @selector(stret_noarg));
+ stretval = [Sub stret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 113);
+ testassert(stret_equal(stretval, STRET_RESULT));
+ */
+# if !__i386__
+ fpval = 0;
+ fpval = ((typeof(fpmsg0))objc_msgSend_noarg)([Sub class], @selector(fpret_noarg));
+ testassert(state == 114);
+ testassert(fpval == FP_RESULT);
+# endif
+# if !__i386__ && !__x86_64__
+ lfpval = 0;
+ lfpval = ((typeof(lfpmsg0))objc_msgSend_noarg)([Sub class], @selector(lfpret_noarg));
+ testassert(state == 115);
+ testassert(lfpval == LFP_RESULT);
+# endif
+#endif
}
+ idmethod = class_getClassMethod([Super class], @selector(idret::::::::::::::::::::::::::::));
+ testassert(idmethod);
+ llmethod = class_getClassMethod([Super class], @selector(llret::::::::::::::::::::::::::::));
+ testassert(llmethod);
+ stretmethod = class_getClassMethod([Super class], @selector(stret::::::::::::::::::::::::::::));
+ testassert(stretmethod);
+ fpmethod = class_getClassMethod([Super class], @selector(fpret::::::::::::::::::::::::::::));
+ testassert(fpmethod);
+ lfpmethod = class_getClassMethod([Super class], @selector(lfpret::::::::::::::::::::::::::::));
+ testassert(lfpmethod);
+
+ idfn = (id (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
+ llfn = (long long (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
+ stretfn = (struct stret (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke_stret;
+ fpfn = (double (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
+ lfpfn = (long double (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
+
// cached message performance
// catches failure to cache or (abi=2) failure to fixup (#5584187)
// fixme unless they all fail
// `.align 4` matches loop alignment to make -O0 work
-#define COUNT 1000000
+ // fill cache first
[Sub idret_perf];
+ [Sub llret_perf];
+ [Sub stret_perf];
+ [Sub fpret_perf];
+ [Sub lfpret_perf];
+ [Sub idret_perf];
+ [Sub llret_perf];
+ [Sub stret_perf];
+ [Sub fpret_perf];
+ [Sub lfpret_perf];
+ [Sub idret_perf];
+ [Sub llret_perf];
+ [Sub stret_perf];
+ [Sub fpret_perf];
+ [Sub lfpret_perf];
+
+ // Some of these times have high variance on some compilers.
+ // The errors we're trying to catch should be catastrophically slow,
+ // so the margins here are generous to avoid false failures.
+
+#define COUNT 1000000
startTime = mach_absolute_time();
- asm(".align 4");
+ ALIGN_();
for (i = 0; i < COUNT; i++) {
[Sub idret_perf];
}
totalTime = mach_absolute_time() - startTime;
- testprintf("idret %llu\n", totalTime);
+ testprintf("time: idret %llu\n", totalTime);
targetTime = totalTime;
- [Sub llret_perf];
startTime = mach_absolute_time();
- asm(".align 4");
+ ALIGN_();
for (i = 0; i < COUNT; i++) {
[Sub llret_perf];
}
totalTime = mach_absolute_time() - startTime;
- testprintf("llret %llu\n", totalTime);
- timeassert(totalTime > targetTime * 0.8 && totalTime < targetTime * 2.0);
-
- [Sub stret_perf];
+ timecheck("llret ", totalTime, targetTime * 0.8, targetTime * 2.0);
+
startTime = mach_absolute_time();
- asm(".align 4");
+ ALIGN_();
for (i = 0; i < COUNT; i++) {
[Sub stret_perf];
}
totalTime = mach_absolute_time() - startTime;
- testprintf("stret %llu\n", totalTime);
- timeassert(totalTime > targetTime * 0.8 && totalTime < targetTime * 5.0);
+ timecheck("stret ", totalTime, targetTime * 0.8, targetTime * 5.0);
- [Sub fpret_perf];
startTime = mach_absolute_time();
- asm(".align 4");
+ ALIGN_();
for (i = 0; i < COUNT; i++) {
[Sub fpret_perf];
}
totalTime = mach_absolute_time() - startTime;
- testprintf("fpret %llu\n", totalTime);
- timeassert(totalTime > targetTime * 0.8 && totalTime < targetTime * 2.0);
+ timecheck("fpret ", totalTime, targetTime * 0.8, targetTime * 4.0);
- [Sub lfpret_perf];
startTime = mach_absolute_time();
- asm(".align 4");
+ ALIGN_();
for (i = 0; i < COUNT; i++) {
[Sub lfpret_perf];
}
totalTime = mach_absolute_time() - startTime;
- testprintf("lfpret %llu\n", totalTime);
- timeassert(totalTime > targetTime * 0.8 && totalTime < targetTime * 2.0);
+ timecheck("lfpret", totalTime, targetTime * 0.8, targetTime * 4.0);
#undef COUNT
// method_invoke
lfpval = [(id)nil lfpret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
testassert(state == 0);
testassert(lfpval == 0.0);
+
+#if __OBCJ2__
+ // message to nil noarg
+ // explicitly call noarg messenger, even if compiler doesn't emit it
+ state = 0;
+ idval = ID_RESULT;
+ idval = ((typeof(idmsg0))objc_msgSend_noarg)(nil, @selector(idret_noarg));
+ testassert(state == 0);
+ testassert(idval == nil);
+ state = 0;
+ llval = LL_RESULT;
+ llval = ((typeof(llmsg0))objc_msgSend_noarg)(nil, @selector(llret_noarg));
+ testassert(state == 0);
+ testassert(llval == 0LL);
+ /*
+ state = 0;
+ stretval = zero;
+ stretval = [(id)nil stret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
+ testassert(state == 0);
+ // no stret result guarantee
+ */
+# if !__i386__
+ state = 0;
+ fpval = FP_RESULT;
+ fpval = ((typeof(fpmsg0))objc_msgSend_noarg)(nil, @selector(fpret_noarg));
+ testassert(state == 0);
+ testassert(fpval == 0.0);
+# endif
+# if !__i386__ && !__x86_64__
+ state = 0;
+ lfpval = LFP_RESULT;
+ lfpval = ((typeof(lfpmsg0))objc_msgSend_noarg)(nil, @selector(lfpret_noarg));
+ testassert(state == 0);
+ testassert(lfpval == 0.0);
+# endif
+#endif
// message forwarded
// message forwarded long long
// message forwarded fpret long double
// fixme
+#if __OBJC2__
+ // rdar://8271364 objc_msgSendSuper2 must not change objc_super
+ struct objc_super sup = {
+ [Sub class],
+ object_getClass([Sub class]),
+ };
+
+ state = 100;
+ idval = nil;
+ idval = ((id(*)(struct objc_super *, SEL, int,int,int,int,int,int,int,int,int,int,int,int,int, double,double,double,double,double,double,double,double,double,double,double,double,double,double,double))objc_msgSendSuper2) (&sup, @selector(idret::::::::::::::::::::::::::::), 1,2,3,4,5,6,7,8,9,10,11,12,13, 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0);
+ testassert(state == 1);
+ testassert(idval == ID_RESULT);
+ testassert(sup.receiver == [Sub class]);
+ testassert(sup.super_class == object_getClass([Sub class]));
+
+ state = 100;
+ stretval = zero;
+ stretval = ((struct stret(*)(struct objc_super *, SEL, int,int,int,int,int,int,int,int,int,int,int,int,int, double,double,double,double,double,double,double,double,double,double,double,double,double,double,double))objc_msgSendSuper2_stret) (&sup, @selector(stret::::::::::::::::::::::::::::), 1,2,3,4,5,6,7,8,9,10,11,12,13, 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0);
+ testassert(state == 3);
+ testassert(stret_equal(stretval, STRET_RESULT));
+ testassert(sup.receiver == [Sub class]);
+ testassert(sup.super_class == object_getClass([Sub class]));
+#endif
+
+#if __OBJC2__
+ // Debug messengers.
+ state = 0;
+ idmsg = (typeof(idmsg))objc_msgSend_debug;
+ idval = nil;
+ idval = (*idmsg)([Sub class], @selector(idret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 101);
+ testassert(idval == ID_RESULT);
+
+ state = 0;
+ llmsg = (typeof(llmsg))objc_msgSend_debug;
+ llval = 0;
+ llval = (*llmsg)([Sub class], @selector(llret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 102);
+ testassert(llval == LL_RESULT);
+
+ state = 0;
+ stretmsg = (typeof(stretmsg))objc_msgSend_stret_debug;
+ stretval = zero;
+ stretval = (*stretmsg)([Sub class], @selector(stret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 103);
+ testassert(stret_equal(stretval, STRET_RESULT));
+
+ state = 100;
+ sup.receiver = [Sub class];
+ sup.super_class = object_getClass([Sub class]);
+ idmsg = (typeof(idmsg))objc_msgSendSuper2_debug;
+ idval = nil;
+ idval = (*idmsg)((id)&sup, @selector(idret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 1);
+ testassert(idval == ID_RESULT);
+
+ state = 100;
+ sup.receiver = [Sub class];
+ sup.super_class = object_getClass([Sub class]);
+ stretmsg = (typeof(stretmsg))objc_msgSendSuper2_stret_debug;
+ stretval = zero;
+ stretval = (*stretmsg)((id)&sup, @selector(stret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 3);
+ testassert(stret_equal(stretval, STRET_RESULT));
+
+#if __i386__
+ state = 0;
+ fpmsg = (typeof(fpmsg))objc_msgSend_fpret_debug;
+ fpval = 0;
+ fpval = (*fpmsg)([Sub class], @selector(fpret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 104);
+ testassert(fpval == FP_RESULT);
+#endif
+#if __x86_64__
+ state = 0;
+ lfpmsg = (typeof(lfpmsg))objc_msgSend_fpret_debug;
+ lfpval = 0;
+ lfpval = (*lfpmsg)([Sub class], @selector(lfpret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
+ testassert(state == 105);
+ testassert(lfpval == LFP_RESULT);
+
+ // fixme fp2ret
+#endif
+
+// debug messengers
+#endif
+
succeed(__FILE__);
}
+
+#endif
+// TEST_CONFIG
+
#include "test.h"
#import <objc/runtime.h>
--- /dev/null
+// TEST_CFLAGS -framework Foundation
+
+#define USE_FOUNDATION 1
+#include "exc.m"
+// TEST_CFLAGS -framework Foundation
+
#include "test.h"
#import <Foundation/Foundation.h>
+// TEST_CONFIG
+
#include "test.h"
#include <stdint.h>
#include <string.h>
--- /dev/null
+// TEST_CONFIG
+
+#include "test.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <objc/runtime.h>
+
+@interface Foo { id isa; } @end
+@implementation Foo
++class { return self; }
++(void)initialize { }
+@end
+
+struct objc_property {
+ const char *name;
+ const char *attr;
+};
+
+#define checkattrlist(attrs, attrcount, target) \
+ do { \
+ if (target > 0) { \
+ testassert(attrs); \
+ testassert(attrcount == target); \
+ testassert(malloc_size(attrs) >= \
+ (1+target) * sizeof(objc_property_attribute_t)); \
+ testassert(attrs[target].name == NULL); \
+ testassert(attrs[target].value == NULL); \
+ } else { \
+ testassert(!attrs); \
+ testassert(attrcount == 0); \
+ } \
+ } while (0)
+
+#define checkattr(attrs, i, n, v) \
+ do { \
+ char *attrsstart = (char *)attrs; \
+ char *attrsend = (char *)attrs + malloc_size(attrs); \
+ testassert((char*)(attrs+i+1) <= attrsend); \
+ testassert(attrs[i].name >= attrsstart); \
+ testassert(attrs[i].value >= attrsstart); \
+ testassert(attrs[i].name + strlen(attrs[i].name) + 1 <= attrsend); \
+ testassert(attrs[i].value + strlen(attrs[i].value) + 1 <= attrsend); \
+ if (n) testassert(0 == strcmp(attrs[i].name, n)); \
+ else testassert(attrs[i].name == NULL); \
+ if (v) testassert(0 == strcmp(attrs[i].value, v)); \
+ else testassert(attrs[i].value == NULL); \
+ } while (0)
+
+int main()
+{
+ char *value;
+ objc_property_attribute_t *attrs;
+ unsigned int attrcount;
+
+ // STRING TO ATTRIBUTE LIST (property_copyAttributeList)
+
+ struct objc_property prop;
+ prop.name = "test";
+
+ // null property
+ attrcount = 42;
+ attrs = property_copyAttributeList(NULL, &attrcount);
+ testassert(!attrs);
+ testassert(attrcount == 0);
+ attrs = property_copyAttributeList(NULL, NULL);
+ testassert(!attrs);
+
+ // null attributes
+ attrcount = 42;
+ prop.attr = NULL;
+ attrs = property_copyAttributeList(&prop, &attrcount);
+ checkattrlist(attrs, attrcount, 0);
+ attrs = property_copyAttributeList(&prop, NULL);
+ testassert(!attrs);
+
+ // empty attributes
+ attrcount = 42;
+ prop.attr = "";
+ attrs = property_copyAttributeList(&prop, &attrcount);
+ checkattrlist(attrs, attrcount, 0);
+ attrs = property_copyAttributeList(&prop, NULL);
+ testassert(!attrs);
+
+ // commas only
+ attrcount = 42;
+ prop.attr = ",,,";
+ attrs = property_copyAttributeList(&prop, &attrcount);
+ checkattrlist(attrs, attrcount, 0);
+ attrs = property_copyAttributeList(&prop, NULL);
+ testassert(!attrs);
+
+ // long and short names, with and without values
+ attrcount = 42;
+ prop.attr = "?XX,',\"?!?!\"YY,\"''''\"";
+ attrs = property_copyAttributeList(&prop, &attrcount);
+ checkattrlist(attrs, attrcount, 4);
+ checkattr(attrs, 0, "?", "XX");
+ checkattr(attrs, 1, "'", "");
+ checkattr(attrs, 2, "?!?!", "YY");
+ checkattr(attrs, 3, "''''", "");
+ free(attrs);
+
+ // all recognized attributes simultaneously, values with quotes
+ attrcount = 42;
+ prop.attr = "T11,V2222,S333333\",G\"44444444,W,P,D,R,N,C,&";
+ attrs = property_copyAttributeList(&prop, &attrcount);
+ checkattrlist(attrs, attrcount, 11);
+ checkattr(attrs, 0, "T", "11");
+ checkattr(attrs, 1, "V", "2222");
+ checkattr(attrs, 2, "S", "333333\"");
+ checkattr(attrs, 3, "G", "\"44444444");
+ checkattr(attrs, 4, "W", "");
+ checkattr(attrs, 5, "P", "");
+ checkattr(attrs, 6, "D", "");
+ checkattr(attrs, 7, "R", "");
+ checkattr(attrs, 8, "N", "");
+ checkattr(attrs, 9, "C", "");
+ checkattr(attrs,10, "&", "");
+ free(attrs);
+
+ // kitchen sink
+ attrcount = 42;
+ prop.attr = "W,T11,P,?XX,D,V2222,R,',N,S333333\",C,\"?!?!\"YY,&,G\"44444444,\"''''\"";
+ attrs = property_copyAttributeList(&prop, &attrcount);
+ checkattrlist(attrs, attrcount, 15);
+ checkattr(attrs, 0, "W", "");
+ checkattr(attrs, 1, "T", "11");
+ checkattr(attrs, 2, "P", "");
+ checkattr(attrs, 3, "?", "XX");
+ checkattr(attrs, 4, "D", "");
+ checkattr(attrs, 5, "V", "2222");
+ checkattr(attrs, 6, "R", "");
+ checkattr(attrs, 7, "'", "");
+ checkattr(attrs, 8, "N", "");
+ checkattr(attrs, 9, "S", "333333\"");
+ checkattr(attrs,10, "C", "");
+ checkattr(attrs,11, "?!?!", "YY");
+ checkattr(attrs,12, "&", "");
+ checkattr(attrs,13, "G", "\"44444444");
+ checkattr(attrs,14, "''''", "");
+ free(attrs);
+
+ // SEARCH ATTRIBUTE LIST (property_copyAttributeValue)
+
+ // null property, null name, empty name
+ value = property_copyAttributeValue(NULL, NULL);
+ testassert(!value);
+ value = property_copyAttributeValue(NULL, "foo");
+ testassert(!value);
+ value = property_copyAttributeValue(NULL, "");
+ testassert(!value);
+ value = property_copyAttributeValue(&prop, NULL);
+ testassert(!value);
+ value = property_copyAttributeValue(&prop, "");
+ testassert(!value);
+
+ // null attributes, empty attributes
+ prop.attr = NULL;
+ value = property_copyAttributeValue(&prop, "foo");
+ testassert(!value);
+ prop.attr = "";
+ value = property_copyAttributeValue(&prop, "foo");
+ testassert(!value);
+
+ // long and short names, with and without values
+ prop.attr = "?XX,',\"?!?!\"YY,\"''''\"";
+ value = property_copyAttributeValue(&prop, "missing");
+ testassert(!value);
+ value = property_copyAttributeValue(&prop, "X");
+ testassert(!value);
+ value = property_copyAttributeValue(&prop, "\"");
+ testassert(!value);
+ value = property_copyAttributeValue(&prop, "'''");
+ testassert(!value);
+ value = property_copyAttributeValue(&prop, "'''''");
+ testassert(!value);
+
+ value = property_copyAttributeValue(&prop, "?");
+ testassert(0 == strcmp(value, "XX"));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+ value = property_copyAttributeValue(&prop, "'");
+ testassert(0 == strcmp(value, ""));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+ value = property_copyAttributeValue(&prop, "?!?!");
+ testassert(0 == strcmp(value, "YY"));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+ value = property_copyAttributeValue(&prop, "''''");
+ testassert(0 == strcmp(value, ""));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+
+ // all recognized attributes simultaneously, values with quotes
+ prop.attr = "T11,V2222,S333333\",G\"44444444,W,P,D,R,N,C,&";
+ value = property_copyAttributeValue(&prop, "T");
+ testassert(0 == strcmp(value, "11"));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+ value = property_copyAttributeValue(&prop, "V");
+ testassert(0 == strcmp(value, "2222"));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+ value = property_copyAttributeValue(&prop, "S");
+ testassert(0 == strcmp(value, "333333\""));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+ value = property_copyAttributeValue(&prop, "G");
+ testassert(0 == strcmp(value, "\"44444444"));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+ value = property_copyAttributeValue(&prop, "W");
+ testassert(0 == strcmp(value, ""));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+ value = property_copyAttributeValue(&prop, "P");
+ testassert(0 == strcmp(value, ""));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+ value = property_copyAttributeValue(&prop, "D");
+ testassert(0 == strcmp(value, ""));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+ value = property_copyAttributeValue(&prop, "R");
+ testassert(0 == strcmp(value, ""));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+ value = property_copyAttributeValue(&prop, "N");
+ testassert(0 == strcmp(value, ""));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+ value = property_copyAttributeValue(&prop, "C");
+ testassert(0 == strcmp(value, ""));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+ value = property_copyAttributeValue(&prop, "&");
+ testassert(0 == strcmp(value, ""));
+ testassert(malloc_size(value) >= 1 + strlen(value));
+ free(value);
+
+ // ATTRIBUTE LIST TO STRING (class_addProperty)
+
+ BOOL ok;
+ objc_property_t prop2;
+
+ // null name
+ ok = class_addProperty([Foo class], NULL, (objc_property_attribute_t *)1, 1);
+ testassert(!ok);
+
+ // null description
+ ok = class_addProperty([Foo class], "test-null-desc", NULL, 0);
+ testassert(ok);
+ prop2 = class_getProperty([Foo class], "test-null-desc");
+ testassert(prop2);
+ testassert(0 == strcmp(property_getAttributes(prop2), ""));
+
+ // empty description
+ ok = class_addProperty([Foo class], "test-empty-desc", (objc_property_attribute_t*)1, 0);
+ testassert(ok);
+ prop2 = class_getProperty([Foo class], "test-empty-desc");
+ testassert(prop2);
+ testassert(0 == strcmp(property_getAttributes(prop2), ""));
+
+ // long and short names, with and without values
+ objc_property_attribute_t attrs2[] = {
+ { "!", NULL },
+ { "?", "XX" },
+ { "'", "" },
+ { "?!?!", "YY" },
+ { "''''", "" }
+ };
+ ok = class_addProperty([Foo class], "test-unrecognized", attrs2, 5);
+ testassert(ok);
+ prop2 = class_getProperty([Foo class], "test-unrecognized");
+ testassert(prop2);
+ testassert(0 == strcmp(property_getAttributes(prop2), "?XX,',\"?!?!\"YY,\"''''\""));
+
+ // all recognized attributes simultaneously, values with quotes
+ objc_property_attribute_t attrs3[] = {
+ { "&", "" },
+ { "C", "" },
+ { "N", "" },
+ { "R", "" },
+ { "D", "" },
+ { "P", "" },
+ { "W", "" },
+ { "G", "\"44444444" },
+ { "S", "333333\"" },
+ { "V", "2222" },
+ { "T", "11" },
+ };
+ ok = class_addProperty([Foo class], "test-recognized", attrs3, 11);
+ testassert(ok);
+ prop2 = class_getProperty([Foo class], "test-recognized");
+ testassert(prop2);
+ testassert(0 == strcmp(property_getAttributes(prop2),
+ "&,C,N,R,D,P,W,G\"44444444,S333333\",V2222,T11"));
+
+ // kitchen sink
+ objc_property_attribute_t attrs4[] = {
+ { "&", "" },
+ { "C", "" },
+ { "N", "" },
+ { "R", "" },
+ { "D", "" },
+ { "P", "" },
+ { "W", "" },
+ { "!", NULL },
+ { "G", "\"44444444" },
+ { "S", "333333\"" },
+ { "V", "2222" },
+ { "T", "11" },
+ { "?", "XX" },
+ { "'", "" },
+ { "?!?!", "YY" },
+ { "''''", "" }
+ };
+ ok = class_addProperty([Foo class], "test-sink", attrs4, 16);
+ testassert(ok);
+ prop2 = class_getProperty([Foo class], "test-sink");
+ testassert(prop2);
+ testassert(0 == strcmp(property_getAttributes(prop2),
+ "&,C,N,R,D,P,W,G\"44444444,S333333\",V2222,T11,"
+ "?XX,',\"?!?!\"YY,\"''''\""));
+
+ succeed(__FILE__);
+}
+// TEST_CFLAGS -Wno-deprecated-declarations
+
#include "test.h"
#include <string.h>
-#include <objc/Protocol.h>
#include <objc/objc-runtime.h>
+#if !__OBJC2__
+#include <objc/Protocol.h>
+#endif
+
@protocol Proto1
+proto1ClassMethod;
-proto1InstanceMethod;
int main()
{
Class cls;
- Protocol **list;
+ Protocol * const *list;
Protocol *protocol, *empty;
#if !__OBJC2__
struct objc_method_description *desc;
testassert(count == 1);
testassert(protocol_isEqual(list[0], @protocol(Proto2)));
testassert(!list[1]);
- free(list);
+ free((void*)list);
count = 100;
cls = objc_getClass("Super");
testassert(list[count] == NULL);
testassert(count == 1);
testassert(0 == strcmp(protocol_getName(list[0]), "Proto1"));
- free(list);
+ free((void*)list);
count = 100;
cls = objc_getClass("SuperNoProtocols");
testassert(cls);
list = class_copyProtocolList(cls, NULL);
testassert(list);
- free(list);
+ free((void*)list);
count = 100;
list = class_copyProtocolList(NULL, &count);
testassert(count == 1);
testassert(0 == strcmp(protocol_getName(list[0]), "Proto4"));
testassert(list[1] == NULL);
- free(list);
+ free((void*)list);
count = 100;
proplist = class_copyPropertyList(cls, &count);
testassert(count == 1);
testassert(0 == strcmp(property_getName(proplist[0]), "i"));
testassert(proplist[1] == NULL);
- free(proplist);
+ free(proplist);
succeed(__FILE__);
}
+// TEST_CONFIG
+
#include "test.h"
#include <malloc/malloc.h>
#include <objc/objc-runtime.h>
+// TEST_CONFIG
+
#include "test.h"
#include <string.h>
#include <malloc/malloc.h>
props = protocol_copyPropertyList(proto, &count);
testassert(props);
testassert(count == 2);
- testassert(isNamed(props[0], "prop4"));
- testassert(isNamed(props[1], "prop3"));
+ testassert((isNamed(props[0], "prop4") && isNamed(props[1], "prop3")) ||
+ (isNamed(props[0], "prop3") && isNamed(props[1], "prop4")));
// props[] should be null-terminated
testassert(props[2] == NULL);
free(props);
props = protocol_copyPropertyList(proto, &count);
testassert(props);
testassert(count == 2);
- testassert(isNamed(props[0], "prop2"));
- testassert(isNamed(props[1], "prop1"));
+ testassert((isNamed(props[0], "prop1") && isNamed(props[1], "prop2")) ||
+ (isNamed(props[0], "prop2") && isNamed(props[1], "prop1")));
// props[] should be null-terminated
testassert(props[2] == NULL);
free(props);
+// TEST_CFLAGS -Wno-deprecated-declarations
+
#include "test.h"
#if __OBJC2__
char Protocol_name[] __attribute__((section("__OBJC,__class_names"))) = "Protocol";
-struct {
+struct st {
void *isa;
- char *protocol_name;
+ const char *protocol_name;
void *protocol_list;
void *instance_methods;
void *class_methods;
-} Foo_protocol __attribute__((section("__OBJC,__protocol"))) = { Protocol_name, "Foo", 0, 0, 0 };
+};
+
+struct st Foo_protocol __attribute__((section("__OBJC,__protocol"))) = { Protocol_name, "Foo", 0, 0, 0 };
int main()
{
+++ /dev/null
-objc\[\d+\]: \+\[Sub resolveClassMethod:lyingClassMethod\] returned YES, but no new implementation of \+\[Sub lyingClassMethod\] was found
-objc\[\d+\]: \+\[Sub resolveInstanceMethod:lyingInstanceMethod\] returned YES, but no new implementation of -\[Sub lyingInstanceMethod\] was found
-OK: resolve\.m
/* resolve.m
* Test +resolveClassMethod: and +resolveInstanceMethod:
*/
+
+// TEST_CFLAGS -Wno-deprecated-declarations
+
+/*
+TEST_RUN_OUTPUT
+objc\[\d+\]: \+\[Sub resolveClassMethod:lyingClassMethod\] returned YES, but no new implementation of \+\[Sub lyingClassMethod\] was found
+objc\[\d+\]: \+\[Sub resolveInstanceMethod:lyingInstanceMethod\] returned YES, but no new implementation of -\[Sub lyingInstanceMethod\] was found
+OK: resolve\.m
+END
+*/
#include "test.h"
#include <objc/objc.h>
#include <objc/objc-runtime.h>
#include <unistd.h>
-// fixme new API?
-extern void _objc_flush_caches(Class cls, BOOL flushMeta);
-
static int state = 0;
@interface Super { id isa; } @end
return nil;
}
fail("forward:: shouldn't be called (sel %s)", sel_getName(sel));
- return args; // unused
+ return (id)args; // unused
}
@end
testassert(state == 11);
testassert(ret == [Super class]);
- _objc_flush_caches([Sub class]->isa, NO);
+ _objc_flush_caches([Sub class]->isa);
// Call a method that won't get resolved
state = 20;
testassert(state == 26);
testassert(ret == nil);
- _objc_flush_caches([Sub class]->isa, NO);
+ _objc_flush_caches([Sub class]->isa);
// Call a method that won't get resolved but the resolver lies about it
state = 30;
testassert(state == 36);
testassert(ret == nil);
- _objc_flush_caches([Sub class]->isa, NO);
+ _objc_flush_caches([Sub class]->isa);
// Resolve an instance method
testassert(state == 51);
testassert(ret == [Sub class]);
- _objc_flush_caches([Sub class], NO);
+ _objc_flush_caches([Sub class]);
// Call a method that won't get resolved
state = 60;
testassert(state == 66);
testassert(ret == nil);
- _objc_flush_caches([Sub class], NO);
+ _objc_flush_caches([Sub class]);
// Call a method that won't get resolved but the resolver lies about it
state = 70;
testassert(state == 76);
testassert(ret == nil);
- _objc_flush_caches([Sub class], NO);
+ _objc_flush_caches([Sub class]);
// Call a missing method on a class that doesn't support resolving
state = 80;
--- /dev/null
+// TEST_CFLAGS -framework Foundation
+// TEST_CONFIG GC=0
+
+#include "test.h"
+
+#if TARGET_OS_IPHONE
+
+int main()
+{
+ testwarn("iOS Foundation doesn't call _objc_root* yet");
+ succeed(__FILE__);
+}
+
+#else
+
+#define FOUNDATION 0
+#define NAME "rr-autorelease"
+
+#include "rr-autorelease2.m"
+
+#endif
--- /dev/null
+// Define FOUNDATION=1 for NSObject and NSAutoreleasePool
+// Define FOUNDATION=0 for _objc_root* and _objc_autoreleasePool*
+
+#if FOUNDATION
+# define PUSH() [[NSAutoreleasePool alloc] init]
+# define POP(p) [(id)p release]
+# define RETAIN(o) [o retain]
+# define RELEASE(o) [o release]
+# define AUTORELEASE(o) [o autorelease]
+#else
+# define PUSH() _objc_autoreleasePoolPush()
+# define POP(p) _objc_autoreleasePoolPop(p)
+# define RETAIN(o) _objc_rootRetain((id)o)
+# define RELEASE(o) _objc_rootRelease((id)o)
+# define AUTORELEASE(o) _objc_rootAutorelease((id)o)
+#endif
+
+#include "test.h"
+#include <objc/objc-internal.h>
+#include <Foundation/Foundation.h>
+
+static int state;
+
+#define NESTED_COUNT 8
+
+@interface Deallocator : NSObject @end
+@implementation Deallocator
+-(void) dealloc
+{
+ // testprintf("-[Deallocator %p dealloc]\n", self);
+ state++;
+ [super dealloc];
+}
+@end
+
+@interface AutoreleaseDuringDealloc : NSObject @end
+@implementation AutoreleaseDuringDealloc
+-(void) dealloc
+{
+ state++;
+ AUTORELEASE([[Deallocator alloc] init]);
+ [super dealloc];
+}
+@end
+
+@interface AutoreleasePoolDuringDealloc : NSObject @end
+@implementation AutoreleasePoolDuringDealloc
+-(void) dealloc
+{
+ // caller's pool
+ for (int i = 0; i < NESTED_COUNT; i++) {
+ AUTORELEASE([[Deallocator alloc] init]);
+ }
+
+ // local pool, popped
+ void *pool = PUSH();
+ for (int i = 0; i < NESTED_COUNT; i++) {
+ AUTORELEASE([[Deallocator alloc] init]);
+ }
+ POP(pool);
+
+ // caller's pool again
+ for (int i = 0; i < NESTED_COUNT; i++) {
+ AUTORELEASE([[Deallocator alloc] init]);
+ }
+
+#if FOUNDATION
+ {
+ static bool warned;
+ if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
+ warned = true;
+ }
+ state += NESTED_COUNT;
+#else
+ // local pool, not popped
+ PUSH();
+ for (int i = 0; i < NESTED_COUNT; i++) {
+ AUTORELEASE([[Deallocator alloc] init]);
+ }
+#endif
+
+ [super dealloc];
+}
+@end
+
+void *nopop_fn(void *arg __unused)
+{
+ PUSH();
+ AUTORELEASE([[Deallocator alloc] init]);
+ // pool not popped
+ return NULL;
+}
+
+void *autorelease_lots_fn(void *singlePool)
+{
+ // Enough to blow out the stack if AutoreleasePoolPage is recursive.
+ const int COUNT = 1024*1024;
+ state = 0;
+
+ int p = 0;
+ void **pools = (void**)malloc((COUNT+1) * sizeof(void*));
+ pools[p++] = PUSH();
+
+ id obj = AUTORELEASE([[Deallocator alloc] init]);
+
+ for (int i = 0; i < COUNT; i++) {
+ if (rand() % 1000 == 0 && !singlePool) {
+ pools[p++] = PUSH();
+ } else {
+ AUTORELEASE(RETAIN(obj));
+ }
+ }
+
+ testassert(state == 0);
+ while (--p) {
+ POP(pools[p]);
+ }
+ testassert(state == 0);
+ POP(pools[0]);
+ testassert(state == 1);
+ free(pools);
+
+ return NULL;
+}
+
+void *pop_fn(void *arg __unused)
+{
+ void *pool = PUSH();
+ AUTORELEASE([[Deallocator alloc] init]);
+ POP(pool);
+ return NULL;
+}
+
+void *nsthread_fn(void *arg)
+{
+ [NSThread currentThread];
+ return pop_fn(arg);
+}
+
+void cycle(void)
+{
+ // Normal autorelease.
+ testprintf("-- Normal autorelease.\n");
+ {
+ void *pool = PUSH();
+ state = 0;
+ AUTORELEASE([[Deallocator alloc] init]);
+ testassert(state == 0);
+ POP(pool);
+ testassert(state == 1);
+ }
+
+ // Autorelease during dealloc during autoreleasepool-pop.
+ // That autorelease is handled by the popping pool, not the one above it.
+ testprintf("-- Autorelease during dealloc during autoreleasepool-pop.\n");
+ {
+ void *pool = PUSH();
+ state = 0;
+ AUTORELEASE([[AutoreleaseDuringDealloc alloc] init]);
+ testassert(state == 0);
+ POP(pool);
+ testassert(state == 2);
+ }
+
+ // Autorelease pool during dealloc during autoreleasepool-pop.
+ testprintf("-- Autorelease pool during dealloc during autoreleasepool-pop.\n");
+ {
+ void *pool = PUSH();
+ state = 0;
+ AUTORELEASE([[AutoreleasePoolDuringDealloc alloc] init]);
+ testassert(state == 0);
+ POP(pool);
+ testassert(state == 4 * NESTED_COUNT);
+ }
+
+ // Top-level thread pool popped normally.
+ testprintf("-- Thread-level pool popped normally.\n");
+ {
+ state = 0;
+ pthread_t th;
+ pthread_create(&th, NULL, &pop_fn, NULL);
+ pthread_join(th, NULL);
+ testassert(state == 1);
+ }
+
+ // Top-level thread pool not popped.
+ // The runtime should clean it up.
+#if FOUNDATION
+ {
+ static bool warned;
+ if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
+ warned = true;
+ }
+#else
+ testprintf("-- Thread-level pool not popped.\n");
+ {
+ state = 0;
+ pthread_t th;
+ pthread_create(&th, NULL, &nopop_fn, NULL);
+ pthread_join(th, NULL);
+ testassert(state == 1);
+ }
+#endif
+
+ // Intermediate pool not popped.
+ // Popping the containing pool should clean up the skipped pool first.
+#if FOUNDATION
+ {
+ static bool warned;
+ if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
+ warned = true;
+ }
+#else
+ testprintf("-- Intermediate pool not popped.\n");
+ {
+ void *pool = PUSH();
+ void *pool2 = PUSH();
+ AUTORELEASE([[Deallocator alloc] init]);
+ state = 0;
+ (void)pool2; // pool2 not popped
+ POP(pool);
+ testassert(state == 1);
+ }
+#endif
+
+
+#if !FOUNDATION
+ // NSThread calls NSPopAutoreleasePool(0)
+ // rdar://9167170 but that currently breaks CF
+ {
+ static bool warned;
+ if (!warned) testwarn("rdar://9167170 ignore NSPopAutoreleasePool(0)");
+ warned = true;
+ }
+ /*
+ testprintf("-- pop(0).\n");
+ {
+ PUSH();
+ state = 0;
+ AUTORELEASE([[AutoreleaseDuringDealloc alloc] init]);
+ testassert(state == 0);
+ POP(0);
+ testassert(state == 2);
+ }
+ */
+#endif
+}
+
+int main()
+{
+ // inflate the refcount side table so it doesn't show up in leak checks
+ {
+ int count = 10000;
+ id *objs = (id *)malloc(count*sizeof(id));
+ for (int i = 0; i < count; i++) {
+ objs[i] = RETAIN([NSObject new]);
+ }
+ for (int i = 0; i < count; i++) {
+ RELEASE(objs[i]);
+ RELEASE(objs[i]);
+ }
+ free(objs);
+ }
+
+#if FOUNDATION
+ // inflate NSAutoreleasePool's instance cache
+ {
+ int count = 32;
+ id *objs = (id *)malloc(count * sizeof(id));
+ for (int i = 0; i < count; i++) {
+ objs[i] = [[NSAutoreleasePool alloc] init];
+ }
+ for (int i = 0; i < count; i++) {
+ [objs[count-i-1] release];
+ }
+
+ free(objs);
+ }
+#endif
+
+
+ pthread_attr_t smallstack;
+ pthread_attr_init(&smallstack);
+ pthread_attr_setstacksize(&smallstack, 4096*4);
+
+ for (int i = 0; i < 100; i++) {
+ cycle();
+ }
+
+ leak_mark();
+
+ for (int i = 0; i < 1000; i++) {
+ cycle();
+ }
+
+ leak_check(0);
+
+ // Large autorelease stack.
+ // Do this only once because it's slow.
+ testprintf("-- Large autorelease stack.\n");
+ {
+ // limit stack size: autorelease pop should not be recursive
+ pthread_t th;
+ pthread_create(&th, &smallstack, &autorelease_lots_fn, NULL);
+ pthread_join(th, NULL);
+ }
+
+ // Single large autorelease pool.
+ // Do this only once because it's slow.
+ testprintf("-- Large autorelease pool.\n");
+ {
+ // limit stack size: autorelease pop should not be recursive
+ pthread_t th;
+ pthread_create(&th, &smallstack, &autorelease_lots_fn, (void*)1);
+ pthread_join(th, NULL);
+ }
+
+ testwarn("rdar://9158789 leak slop due to false in-use from malloc");
+ leak_check(8192 /* should be 0 */);
+
+
+ // NSThread.
+ // Can't leak check this because it's too noisy.
+ testprintf("-- NSThread.\n");
+ {
+ pthread_t th;
+ pthread_create(&th, &smallstack, &nsthread_fn, 0);
+ pthread_join(th, NULL);
+ }
+
+ // NO LEAK CHECK HERE
+
+ succeed(NAME);
+}
--- /dev/null
+// TEST_CFLAGS -framework Foundation
+// TEST_CONFIG GC=0
+
+#define FOUNDATION 1
+#define NAME "rr-nsautorelease"
+
+#include "rr-autorelease2.m"
+++ /dev/null
-objc\[\d+\]: class `DoesNotExist\' not linked into application
-OK: runtime.m
+/*
+TEST_RUN_OUTPUT
+objc\[\d+\]: class `DoesNotExist\' not linked into application
+OK: runtime.m
+END
+*/
+
+
#include "test.h"
#include <string.h>
int main()
{
Class list[100];
- unsigned int count, count0;
+ Class *list2;
+ unsigned int count, count0, count2;
unsigned int i;
int foundSuper;
int foundSub;
testassert(objc_lookUpClass("DoesNotExist") == nil);
testassert(objc_lookUpClass(NULL) == nil);
+ list2 = objc_copyClassList(&count2);
+ testassert(count2 == count);
+ testassert(list2);
+ testassert(malloc_size(list2) >= (1+count2) * sizeof(Class));
+ for (i = 0; i < count; i++) {
+ testassert(list[i] == list2[i]);
+ }
+ testassert(list2[count] == NULL);
+ free(list2);
+ free(objc_copyClassList(NULL));
+
succeed(__FILE__);
}
+// TEST_CONFIG
+
#include "test.h"
#include <string.h>
#include <objc/objc-runtime.h>
testassert(0 == strcmp("<null selector>", sel_getName(0)));
// sel_getName recognizes GC-ignored SELs
- if (objc_collecting_enabled()) {
+#if defined(__i386__)
+ if (objc_collectingEnabled()) {
testassert(0 == strcmp("<ignored selector>",
sel_getName(@selector(retain))));
- } else {
+ } else
+#endif
+ {
testassert(0 == strcmp("retain",
sel_getName(@selector(retain))));
}
+// TEST_CFLAGS -Wno-deprecated-declarations
+
#include "test.h"
#include <objc/runtime.h>
@implementation Sub
+new { return class_createInstance(self, 0); }
+(int)classMethod { return [super classMethod] + 100; }
--(int)instanceMethod { return [super instanceMethod] + 1000000; }
+-(int)instanceMethod {
+ return [super instanceMethod] + 1000000;
+}
@end
int main()
+// TEST_CONFIG
+
#include "test.h"
#include <objc/objc-runtime.h>
+// TEST_CFLAGS -framework Foundation
+
#include "test.h"
#include <stdlib.h>
// synchronized stress test
// Single locked counter incremented by many threads.
+#if defined(__arm__)
+#define THREADS 16
+#define COUNT 1024*24
+#else
// 64 / 1024*24 test takes about 20s on 4x2.6GHz Mac Pro
#define THREADS 64
#define COUNT 1024*24
+#endif
static id lock;
static int count;
}
// Verify lack of objc pthread data (should have used sync fast cache)
- if (_pthread_has_direct_tsd()) {
- testassert(! pthread_getspecific(__PTK_FRAMEWORK_OBJC_KEY5));
- }
+#ifdef __PTK_FRAMEWORK_OBJC_KEY5
+ testassert(! pthread_getspecific(__PTK_FRAMEWORK_OBJC_KEY5));
+#endif
+
return NULL;
}
// Verify objc pthread data on this thread (from +initialize)
// Worker threads shouldn't have any because of sync fast cache.
- if (_pthread_has_direct_tsd()) {
- testassert(pthread_getspecific(__PTK_FRAMEWORK_OBJC_KEY5));
- }
+#ifdef __PTK_FRAMEWORK_OBJC_KEY5
+ testassert(pthread_getspecific(__PTK_FRAMEWORK_OBJC_KEY5));
+#endif
// Start the threads
for (t = 0; t < THREADS; t++) {
+// TEST_CFLAGS -framework Foundation
+
#include "test.h"
#include <stdlib.h>
// * thread increments counter [row][col]
// * thread unlocks all of the locks
+#if defined(__arm__)
+// 16 / 4 / 3 / 1024*8 test takes about 30s on 2nd gen iPod touch
+#define THREADS 16
+#define ROWS 4
+#define COLS 3
+#define COUNT 1024*8
+#else
// 64 / 4 / 3 / 1024*8 test takes about 20s on 4x2.6GHz Mac Pro
#define THREADS 64
#define ROWS 4
#define COLS 3
#define COUNT 1024*8
+#endif
static id locks[ROWS][COLS];
static int counts[ROWS][COLS];
+// TEST_CFLAGS -framework Foundation
+
#include "test.h"
#include <Foundation/Foundation.h>
--- /dev/null
+// TEST_CFLAGS -framework Foundation
+
+#include "test.h"
+#include <objc/runtime.h>
+#include <objc/objc-internal.h>
+#import <Foundation/Foundation.h>
+
+#if __OBJC2__ && __LP64__
+/*
+ gcc -o taggedPointers.out taggedPointers.m -L/tmp/bbum-products/Release/ -lobjc -undefined dynamic_lookup -framework Foundation -gdwarf-2
+ env DYLD_LIBRARY_PATH=/tmp/bbum-products/Release/ DYLD_FRAMEWORK_PATH=/tmp/bbum-products/Release gdb ./taggedPointers.out
+ env DYLD_LIBRARY_PATH=/tmp/bbum-products/Debug/ DYLD_FRAMEWORK_PATH=/tmp/bbum-products/Debug gdb ./taggedPointers.out
+ */
+
+static BOOL didIt;
+
+#define TAG_VALUE(tagSlot, value) ((id)(1UL | (((uintptr_t)(tagSlot)) << 1) | (((uintptr_t)(value)) << 4)))
+
+@interface TaggedBaseClass
+@end
+
+@implementation TaggedBaseClass
++ (void) initialize
+{
+ ;
+}
+
+- (void) instanceMethod
+{
+ didIt = YES;
+}
+
+- (uintptr_t) taggedValue
+{
+ return (uintptr_t) self >> 4;
+}
+
+- (NSRect) stret: (NSRect) aRect
+{
+ return aRect;
+}
+
+- (long double) fpret: (long double) aValue
+{
+ return aValue;
+}
+
+
+-(void) dealloc {
+ fail("TaggedBaseClass dealloc called!");
+}
+
+-(id) retain {
+ return _objc_rootRetain(self);
+}
+
+-(void) release {
+ return _objc_rootRelease(self);
+}
+
+-(id) autorelease {
+ return _objc_rootAutorelease(self);
+}
+
+-(uintptr_t) retainCount {
+ return _objc_rootRetainCount(self);
+}
+@end
+
+@interface TaggedSubclass: TaggedBaseClass
+@end
+
+@implementation TaggedSubclass
++ (void) initialize
+{
+ ;
+}
+
+- (void) instanceMethod
+{
+ return [super instanceMethod];
+}
+
+- (uintptr_t) taggedValue
+{
+ return [super taggedValue];
+}
+
+- (NSRect) stret: (NSRect) aRect
+{
+ return [super stret: aRect];
+}
+
+- (long double) fpret: (long double) aValue
+{
+ return [super fpret: aValue];
+}
+@end
+
+@interface TaggedNSObjectSubclass : NSObject
+@end
+
+@implementation TaggedNSObjectSubclass
++ autorelease {
+ abort();
+}
+- autorelease {
+ didIt = YES;
+ return self;
+}
+- retain {
+ didIt = YES;
+ return self;
+}
+- (oneway void) release {
+ didIt = YES;
+}
+
+- (void) instanceMethod {
+ didIt = YES;
+}
+
+- (uintptr_t) taggedValue
+{
+ return (uintptr_t) self >> 4;
+}
+
+- (NSRect) stret: (NSRect) aRect
+{
+ return aRect;
+}
+
+- (long double) fpret: (long double) aValue
+{
+ return aValue;
+}
+@end
+
+/*
+
+This class was used prior to integration of tagged numbers into CF.
+Now that CF has tagged numbers, the test assumes their presence.
+
+@interface TestTaggedNumber:NSNumber
+@end
+@implementation TestTaggedNumber
++(void) load
+{
+ _objc_insert_tagged_isa(4, self);
+}
+
++ taggedNumberWithInt: (int) arg
+{
+ uint64_t value = (uint64_t) arg;
+ id returnValue = (id) (((uint64_t) 0x9) | (value << 4));
+ return returnValue;
+}
+
+- (void)getValue:(void *)value
+{
+ *(uint64_t *)value = ((uint64_t)self) >> 4;
+}
+
+- (const char *)objCType
+{
+ return "i";
+}
+
+- (int)intValue
+{
+ return (int) (((uint64_t)self) >> 4);
+}
+@end
+*/
+
+void testTaggedNumber()
+{
+ NSNumber *taggedPointer = [NSNumber numberWithInt: 1234];
+ int result;
+
+ testassert( CFGetTypeID(taggedPointer) == CFNumberGetTypeID() );
+
+ CFNumberGetValue((CFNumberRef) taggedPointer, kCFNumberIntType, &result);
+ testassert(result == 1234);
+
+ testassert(((uintptr_t)taggedPointer) & 0x1); // make sure it is really tagged
+
+ // do some generic object-y things to the taggedPointer instance
+ CFRetain(taggedPointer);
+ CFRelease(taggedPointer);
+
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+ [dict setObject: taggedPointer forKey: @"fred"];
+ testassert(taggedPointer == [dict objectForKey: @"fred"]);
+ [dict setObject: @"bob" forKey: taggedPointer];
+ testassert([@"bob" isEqualToString: [dict objectForKey: taggedPointer]]);
+
+ NSNumber *i12345 = [NSNumber numberWithInt: 12345];
+ NSNumber *i12346 = [NSNumber numberWithInt: 12346];
+ NSNumber *i12347 = [NSNumber numberWithInt: 12347];
+
+ NSArray *anArray = [NSArray arrayWithObjects: i12345, i12346, i12347, nil];
+ testassert([anArray count] == 3);
+ testassert([anArray indexOfObject: i12346] == 1);
+
+ NSSet *aSet = [NSSet setWithObjects: i12345, i12346, i12347, nil];
+ testassert([aSet count] == 3);
+ testassert([aSet containsObject: i12346]);
+
+ [taggedPointer performSelector: @selector(intValue)];
+ testassert(![taggedPointer isProxy]);
+ testassert([taggedPointer isKindOfClass: [NSNumber class]]);
+ testassert([taggedPointer respondsToSelector: @selector(intValue)]);
+
+ [taggedPointer description];
+}
+
+void testGenericTaggedPointer(uint8_t tagSlot, const char *classname)
+{
+ Class cls = objc_getClass(classname);
+ testassert(cls);
+
+ id taggedPointer = TAG_VALUE(tagSlot, 1234);
+ testassert(object_getClass(taggedPointer) == cls);
+ testassert([taggedPointer taggedValue] == 1234);
+
+ didIt = NO;
+ [taggedPointer instanceMethod];
+ testassert(didIt);
+
+ NSRect originalRect = NSMakeRect(1.0, 2.0, 3.0, 4.0);
+ testassert(NSEqualRects(originalRect, [taggedPointer stret: originalRect]));
+
+ long double value = 3.14156789;
+ testassert(value == [taggedPointer fpret: value]);
+
+ if (!objc_collectingEnabled()) {
+ // Tagged pointers should bypass refcount tables and autorelease pools
+ leak_mark();
+ for (uintptr_t i = 0; i < 10000; i++) {
+ id o = TAG_VALUE(tagSlot, i);
+ testassert(object_getClass(o) == cls);
+
+ [o release]; testassert([o retainCount] != 0);
+ [o release]; testassert([o retainCount] != 0);
+ CFRelease(o); testassert([o retainCount] != 0);
+ CFRelease(o); testassert([o retainCount] != 0);
+ [o retain];
+ [o retain];
+ [o retain];
+ CFRetain(o);
+ CFRetain(o);
+ CFRetain(o);
+ [o autorelease];
+ }
+ leak_check(0);
+ }
+}
+
+int main()
+{
+ NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
+
+ _objc_insert_tagged_isa(5, objc_getClass("TaggedBaseClass"));
+ testGenericTaggedPointer(5, "TaggedBaseClass");
+
+ _objc_insert_tagged_isa(2, objc_getClass("TaggedSubclass"));
+ testGenericTaggedPointer(2, "TaggedSubclass");
+
+ _objc_insert_tagged_isa(3, objc_getClass("TaggedNSObjectSubclass"));
+ testGenericTaggedPointer(3, "TaggedNSObjectSubclass");
+
+ testTaggedNumber(); // should be tested by CF... our tests are wrong, wrong, wrong.
+ [p release];
+
+ succeed(__FILE__);
+}
+
+// OBJC2 && __LP64__
+#else
+// not (OBJC2 && __LP64__)
+
+ // Tagged pointers not supported. Crash if an NSNumber actually
+ // is a tagged pointer (which means this test is out of date).
+
+int main() {
+ NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
+ testassert(*(id *)[NSNumber numberWithInt:1234]);
+ [p release];
+
+ succeed(__FILE__);
+}
+
+#endif
#include <string.h>
#include <libgen.h>
#include <unistd.h>
+#include <sys/param.h>
#include <malloc/malloc.h>
+#include <mach/mach.h>
#include <mach/mach_time.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+#include <objc/message.h>
#include <objc/objc-auto.h>
+#include <TargetConditionals.h>
-static inline void succeed(const char *msg, ...) __attribute__((noreturn));
-static inline void succeed(const char *msg, ...)
+static inline void succeed(const char *name) __attribute__((noreturn));
+static inline void succeed(const char *name)
{
- va_list v;
- if (msg) {
- fprintf(stderr, "OK: ");
- va_start(v, msg);
- vfprintf(stderr, msg, v);
- va_end(v);
- fprintf(stderr, "\n");
+ if (name) {
+ char path[MAXPATHLEN+1];
+ strcpy(path, name);
+ fprintf(stderr, "OK: %s\n", basename(path));
} else {
fprintf(stderr, "OK\n");
}
exit(0);
}
-static inline int fail(const char *msg, ...) __attribute__((noreturn));
-static inline int fail(const char *msg, ...)
+static inline void fail(const char *msg, ...) __attribute__((noreturn));
+static inline void fail(const char *msg, ...)
{
va_list v;
if (msg) {
}
#define testassert(cond) \
- ((void) ((cond) ? 0 : __testassert(#cond, __FILE__, __LINE__)))
+ ((void) ((cond) ? (void)0 : __testassert(#cond, __FILE__, __LINE__)))
#define __testassert(cond, file, line) \
- fail("failed assertion '%s' at %s:%u", cond, __FILE__, __LINE__)
+ (fail("failed assertion '%s' at %s:%u", cond, __FILE__, __LINE__))
/* time-sensitive assertion, disabled under valgrind */
-#define timeassert(cond) \
- testassert((getenv("VALGRIND") && 0 != strcmp(getenv("VALGRIND"), "NO")) || (cond))
+#define timecheck(name, time, fast, slow) \
+ if (getenv("VALGRIND") && 0 != strcmp(getenv("VALGRIND"), "NO")) { \
+ /* valgrind; do nothing */ \
+ } else if (time > slow) { \
+ fprintf(stderr, "SLOW: %s %llu, expected %llu..%llu\n", \
+ name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
+ } else if (time < fast) { \
+ fprintf(stderr, "FAST: %s %llu, expected %llu..%llu\n", \
+ name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
+ } else { \
+ testprintf("time: %s %llu, expected %llu..%llu\n", \
+ name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
+ }
+
static inline void testprintf(const char *msg, ...)
{
- va_list v;
- va_start(v, msg);
- if (getenv("VERBOSE")) vfprintf(stderr, msg, v);
- va_end(v);
+ if (msg && getenv("VERBOSE")) {
+ va_list v;
+ va_start(v, msg);
+ fprintf(stderr, "VERBOSE: ");
+ vfprintf(stderr, msg, v);
+ va_end(v);
+ }
}
+// complain to output, but don't fail the test
+// Use when warning that some test is being temporarily skipped
+// because of something like a compiler bug.
+static inline void testwarn(const char *msg, ...)
+{
+ if (msg) {
+ va_list v;
+ va_start(v, msg);
+ fprintf(stderr, "WARN: ");
+ vfprintf(stderr, msg, v);
+ va_end(v);
+ fprintf(stderr, "\n");
+ }
+}
+
+static inline void testnoop() { }
+
+// Run GC. This is a macro to reach as high in the stack as possible.
+#ifndef OBJC_NO_GC
+
+# if __OBJC2__
+# define testexc()
+# else
+# include <objc/objc-exception.h>
+# define testexc() \
+ do { \
+ objc_exception_functions_t table = {0,0,0,0,0,0}; \
+ objc_exception_get_functions(&table); \
+ if (!table.throw_exc) { \
+ table.throw_exc = (typeof(table.throw_exc))abort; \
+ table.try_enter = (typeof(table.try_enter))testnoop; \
+ table.try_exit = (typeof(table.try_exit))testnoop; \
+ table.extract = (typeof(table.extract))abort; \
+ table.match = (typeof(table.match))abort; \
+ objc_exception_set_functions(&table); \
+ } \
+ } while (0)
+# endif
+
+# define testcollect() \
+ do { \
+ if (objc_collectingEnabled()) { \
+ testexc(); \
+ objc_clear_stack(0); \
+ objc_collect(OBJC_COLLECT_IF_NEEDED|OBJC_WAIT_UNTIL_DONE); \
+ objc_collect(OBJC_EXHAUSTIVE_COLLECTION|OBJC_WAIT_UNTIL_DONE);\
+ objc_collect(OBJC_EXHAUSTIVE_COLLECTION|OBJC_WAIT_UNTIL_DONE);\
+ } \
+ _objc_flush_caches(NULL); \
+ } while (0)
+
+#else
+
+# define testcollect() \
+ do { \
+ _objc_flush_caches(NULL); \
+ } while (0)
+
+#endif
+
+
+/* Make sure libobjc does not call global operator new.
+ Any test that DOES need to call global operator new must
+ `#define TEST_CALLS_OPERATOR_NEW` before including test.h.
+ */
+#if __cplusplus && !defined(TEST_CALLS_OPERATOR_NEW)
+#import <new>
+inline void* operator new(std::size_t) throw (std::bad_alloc) { fail("called global operator new"); }
+inline void* operator new[](std::size_t) throw (std::bad_alloc) { fail("called global operator new[]"); }
+inline void* operator new(std::size_t, const std::nothrow_t&) throw() { fail("called global operator new(nothrow)"); }
+inline void* operator new[](std::size_t, const std::nothrow_t&) throw() { fail("called global operator new[](nothrow)"); }
+inline void operator delete(void*) throw() { fail("called global operator delete"); }
+inline void operator delete[](void*) throw() { fail("called global operator delete[]"); }
+inline void operator delete(void*, const std::nothrow_t&) throw() { fail("called global operator delete(nothrow)"); }
+inline void operator delete[](void*, const std::nothrow_t&) throw() { fail("called global operator delete[](nothrow)"); }
+#endif
+
/* Leak checking
Fails if total malloc memory in use at leak_check(n)
is more than n bytes above that at leak_mark().
*/
+static inline void leak_recorder(task_t task __unused, void *ctx, unsigned type __unused, vm_range_t *ranges, unsigned count)
+{
+ size_t *inuse = (size_t *)ctx;
+ while (count--) {
+ *inuse += ranges[count].size;
+ }
+}
+
+static inline size_t leak_inuse(void)
+{
+ size_t total = 0;
+ vm_address_t *zones;
+ unsigned count;
+ malloc_get_all_zones(mach_task_self(), NULL, &zones, &count);
+ for (unsigned i = 0; i < count; i++) {
+ size_t inuse = 0;
+ malloc_zone_t *zone = (malloc_zone_t *)zones[i];
+ if (!zone->introspect || !zone->introspect->enumerator) continue;
+
+ zone->introspect->enumerator(mach_task_self(), &inuse, MALLOC_PTR_IN_USE_RANGE_TYPE, (vm_address_t)zone, NULL, leak_recorder);
+ total += inuse;
+ }
+
+ return total;
+}
+
+
+static inline void leak_dump_heap(const char *msg)
+{
+ fprintf(stderr, "%s\n", msg);
+
+ // Make `heap` write to stderr
+ int outfd = dup(STDOUT_FILENO);
+ dup2(STDERR_FILENO, STDOUT_FILENO);
+ pid_t pid = getpid();
+ char cmd[256];
+ // environment variables reset for iOS simulator use
+ sprintf(cmd, "DYLD_LIBRARY_PATH= DYLD_ROOT_PATH= /usr/bin/heap -addresses all %d", (int)pid);
+
+ system(cmd);
+
+ dup2(outfd, STDOUT_FILENO);
+ close(outfd);
+}
+
static size_t _leak_start;
static inline void leak_mark(void)
{
- malloc_statistics_t stats;
- if (objc_collecting_enabled()) {
- objc_startCollectorThread();
- objc_collect(OBJC_EXHAUSTIVE_COLLECTION|OBJC_WAIT_UNTIL_DONE);
+ testcollect();
+ if (getenv("LEAK_HEAP")) {
+ leak_dump_heap("HEAP AT leak_mark");
}
- malloc_zone_statistics(NULL, &stats);
- _leak_start = stats.size_in_use;
+ _leak_start = leak_inuse();
}
#define leak_check(n) \
do { \
const char *_check = getenv("LEAK_CHECK"); \
+ size_t inuse; \
if (_check && 0 == strcmp(_check, "NO")) break; \
- if (objc_collecting_enabled()) { \
- objc_collect(OBJC_EXHAUSTIVE_COLLECTION|OBJC_WAIT_UNTIL_DONE); \
+ testcollect(); \
+ if (getenv("LEAK_HEAP")) { \
+ leak_dump_heap("HEAP AT leak_check"); \
} \
- malloc_statistics_t stats; \
- malloc_zone_statistics(NULL, &stats); \
- if (stats.size_in_use > _leak_start + n) { \
+ inuse = leak_inuse(); \
+ if (inuse > _leak_start + n) { \
if (getenv("HANG_ON_LEAK")) { \
printf("leaks %d\n", getpid()); \
while (1) sleep(1); \
} \
- fail("%zu bytes leaked at %s:%u", \
- stats.size_in_use - _leak_start, __FILE__, __LINE__); \
+ fprintf(stderr, "BAD: %zu bytes leaked at %s:%u\n", \
+ inuse - _leak_start, __FILE__, __LINE__); \
} \
} while (0)
+static inline bool is_guardmalloc(void)
+{
+ const char *env = getenv("GUARDMALLOC");
+ return (env && 0 == strcmp(env, "YES"));
+}
+
#endif
--- /dev/null
+#!/usr/bin/perl
+
+# test.pl
+# Run unit tests.
+
+use strict;
+use File::Basename;
+
+chdir dirname $0;
+chomp (my $DIR = `pwd`);
+
+my $TESTLIBNAME = "libobjc.A.dylib";
+my $TESTLIBPATH = "/usr/lib/$TESTLIBNAME";
+
+my $BUILDDIR = "/tmp/test-$TESTLIBNAME-build";
+
+# xterm colors
+my $red = "\e[41;37m";
+my $yellow = "\e[43;37m";
+my $def = "\e[0m";
+
+# clean, help
+if (scalar(@ARGV) == 1) {
+ my $arg = $ARGV[0];
+ if ($arg eq "clean") {
+ my $cmd = "rm -rf $BUILDDIR *~";
+ print "$cmd\n";
+ `$cmd`;
+ exit 0;
+ }
+ elsif ($arg eq "-h" || $arg eq "-H" || $arg eq "-help" || $arg eq "help") {
+ print(<<END);
+usage: $0 [options] [testname ...]
+ $0 clean
+ $0 help
+
+testname:
+ `testname` runs a specific test. If no testnames are given, runs all tests.
+
+options:
+ ARCH=<arch>
+ GC=0|1
+ SDK=<sdk name>
+ ROOT=/path/to/project.roots/
+
+ CC=<compiler name>
+
+ GUARDMALLOC=0|1
+
+ BUILD=0|1
+ RUN=0|1
+ VERBOSE=0|1
+
+examples:
+
+ test installed library, x86_64, no gc
+ $0
+
+ test buildit-built root, i386 and x86_64, gc and no gc, clang compiler
+ $0 ARCH=i386,x86_64 ROOT=/tmp/libclosure.roots GC=1,0 CC=clang
+
+ test buildit-built root with iOS simulator
+ $0 ARCH=i386 ROOT=/tmp/libclosure.roots SDK=iphonesimulator
+
+ test buildit-built root on attached iOS device
+ $0 ARCH=armv7 ROOT=/tmp/libclosure.roots SDK=iphoneos
+END
+ exit 0;
+ }
+}
+
+#########################################################################
+## Tests
+
+my %ALL_TESTS;
+
+#########################################################################
+## Variables for use in complex build and run rules
+
+# variable # example value
+
+# things you can multiplex on the command line
+# ARCH=i386,x86_64,armv6,armv7
+# SDK=system,macosx,iphoneos,iphonesimulator
+# LANGUAGE=c,c++,objective-c,objective-c++
+# CC=clang,gcc-4.2,llvm-gcc-4.2
+# GC=0,1
+# GUARDMALLOC=0,1
+
+# things you can set once on the command line
+# ROOT=/path/to/project.roots
+# BUILD=0|1
+# RUN=0|1
+# VERBOSE=0|1
+
+
+
+my $BUILD;
+my $RUN;
+my $VERBOSE;
+
+my $crashcatch = <<'END';
+// interpose-able code to catch crashes, print, and exit cleanly
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <mach-o/dyld-interposing.h>
+
+static void catchcrash(int sig)
+{
+ const char *msg;
+ switch (sig) {
+ case SIGILL: msg = "CRASHED: SIGILL\\n"; break;
+ case SIGBUS: msg = "CRASHED: SIGBUS\\n"; break;
+ case SIGSYS: msg = "CRASHED: SIGSYS\\n"; break;
+ case SIGSEGV: msg = "CRASHED: SIGSEGV\\n"; break;
+ case SIGTRAP: msg = "CRASHED: SIGTRAP\\n"; break;
+ case SIGABRT: msg = "CRASHED: SIGABRT\\n"; break;
+ default: msg = "SIG\?\?\?\?\\n"; break;
+ }
+ write(STDERR_FILENO, msg, strlen(msg));
+ _exit(0);
+}
+
+static void setupcrash(void) __attribute__((constructor));
+static void setupcrash(void)
+{
+ signal(SIGILL, &catchcrash);
+ signal(SIGBUS, &catchcrash);
+ signal(SIGSYS, &catchcrash);
+ signal(SIGSEGV, &catchcrash);
+ signal(SIGTRAP, &catchcrash);
+ signal(SIGABRT, &catchcrash);
+}
+
+
+static int hacked = 0;
+ssize_t hacked_write(int fildes, const void *buf, size_t nbyte)
+{
+ if (!hacked) {
+ setupcrash();
+ hacked = 1;
+ }
+ return write(fildes, buf, nbyte);
+}
+
+DYLD_INTERPOSE(hacked_write, write);
+
+END
+
+
+#########################################################################
+## Harness
+
+
+# map language to buildable extensions for that language
+my %extensions_for_language = (
+ "c" => ["c"],
+ "objective-c" => ["c", "m"],
+ "c++" => ["c", "cc", "cp", "cpp", "cxx", "c++"],
+ "objective-c++" => ["c", "m", "cc", "cp", "cpp", "cxx", "c++", "mm"],
+
+ "any" => ["c", "m", "cc", "cp", "cpp", "cxx", "c++", "mm"],
+ );
+
+# map extension to languages
+my %languages_for_extension = (
+ "c" => ["c", "objective-c", "c++", "objective-c++"],
+ "m" => ["objective-c", "objective-c++"],
+ "mm" => ["objective-c++"],
+ "cc" => ["c++", "objective-c++"],
+ "cp" => ["c++", "objective-c++"],
+ "cpp" => ["c++", "objective-c++"],
+ "cxx" => ["c++", "objective-c++"],
+ "c++" => ["c++", "objective-c++"],
+ );
+
+# Run some newline-separated commands like `make` would, stopping if any fail
+# run("cmd1 \n cmd2 \n cmd3")
+sub make {
+ my $output = "";
+ my @cmds = split("\n", $_[0]);
+ die if scalar(@cmds) == 0;
+ $? = 0;
+ foreach my $cmd (@cmds) {
+ chomp $cmd;
+ next if $cmd =~ /^\s*$/;
+ $cmd .= " 2>&1";
+ print "$cmd\n" if $VERBOSE;
+ $output .= `$cmd`;
+ last if $?;
+ }
+ print "$output\n" if $VERBOSE;
+ return $output;
+}
+
+sub chdir_verbose {
+ my $dir = shift;
+ chdir $dir || die;
+ print "cd $dir\n" if $VERBOSE;
+}
+
+
+# Return test names from the command line.
+# Returns all tests if no tests were named.
+sub gettests {
+ my @tests;
+
+ foreach my $arg (@ARGV) {
+ push @tests, $arg if ($arg !~ /=/ && $arg !~ /^-/);
+ }
+
+ opendir(my $dir, $DIR) || die;
+ while (my $file = readdir($dir)) {
+ my ($name, $ext) = ($file =~ /^([^.]+)\.([^.]+)$/);
+ next if ! $languages_for_extension{$ext};
+
+ open(my $in, "< $file") || die "$file";
+ my $contents = join "", <$in>;
+ die if defined $ALL_TESTS{$name};
+ $ALL_TESTS{$name} = $ext if ($contents =~ m#^[/*\s]*TEST_#m);
+ close($in);
+ }
+ closedir($dir);
+
+ if (scalar(@tests) == 0) {
+ @tests = keys %ALL_TESTS;
+ }
+
+ @tests = sort @tests;
+
+ return @tests;
+}
+
+
+# Turn a C compiler name into a C++ compiler name.
+sub cplusplus {
+ my ($c) = @_;
+ if ($c =~ /cc/) {
+ $c =~ s/cc/\+\+/;
+ return $c;
+ }
+ return $c . "++"; # e.g. clang => clang++
+}
+
+# Returns an array of all sdks from `xcodebuild -showsdks`
+sub getsdks {
+ return ("system", `xcodebuild -showsdks` =~ /-sdk (.+)$/mg);
+}
+
+# Returns whether the given sdk supports GC
+sub supportsgc {
+ my ($sdk) = @_;
+ return 1 if $sdk eq "system";
+ return 1 if $sdk =~ /^macosx/;
+ return 0 if $sdk =~ /^iphone/;
+ die;
+}
+
+# print text with a colored prefix on each line
+sub colorprint {
+ my $color = shift;
+ while (my @lines = split("\n", shift)) {
+ for my $line (@lines) {
+ chomp $line;
+ print "$color $def$line\n";
+ }
+ }
+}
+
+sub rewind {
+ seek($_[0], 0, 0);
+}
+
+# parse name=value,value pairs
+sub readconditions {
+ my ($conditionstring) = @_;
+
+ my %results;
+ my @conditions = ($conditionstring =~ /\w+=(?:[^\s,]+,?)+/g);
+ for my $condition (@conditions) {
+ my ($name, $values) = ($condition =~ /(\w+)=(.+)/);
+ $results{$name} = [split ',', $values];
+ }
+
+ return %results;
+}
+
+# Get the name of the system SDK from sw_vers
+sub systemsdkname {
+ my @lines = `/usr/bin/sw_vers`;
+ my $name;
+ my $vers;
+ for my $line (@lines) {
+ ($name) = ($line =~ /^ProductName:\s+(.*)/) if !$name;
+ ($vers) = ($line =~ /^ProductVersion:\s+(.*)/) if !$vers;
+ }
+
+ $name =~ s/ //g;
+ $name = lc($name);
+ my $internal = "";
+ if (-d "/usr/local/include/objc") {
+ if ($name eq "macosx") {
+ $internal = "internal";
+ } else {
+ $internal = ".internal";
+ }
+ }
+ return $name . $vers . $internal;
+}
+
+sub check_output {
+ my %C = %{shift()};
+ my $name = shift;
+ my @output = @_;
+
+ my %T = %{$C{"TEST_$name"}};
+ my @original_output = @output;
+
+ # Run result-checking passes, reducing @output each time
+ my $xit = 1;
+ my $bad = "";
+ my $warn = "";
+ my $runerror = $T{TEST_RUN_OUTPUT};
+ filter_verbose(\@output);
+ $warn = filter_warn(\@output);
+ $bad |= filter_guardmalloc(\@output) if ($C{GUARDMALLOC});
+ $bad |= filter_valgrind(\@output) if ($C{VALGRIND});
+ $bad = filter_expected(\@output, \%C, $name) if ($bad eq "");
+ $bad = filter_bad(\@output) if ($bad eq "");
+
+ # OK line should be the only one left
+ $bad = "(output not 'OK: $name')" if ($bad eq "" && (scalar(@output) != 1 || $output[0] !~ /^OK: $name/));
+
+ if ($bad ne "") {
+ my $red = "\e[41;37m";
+ my $def = "\e[0m";
+ print "${red}FAIL: /// test '$name' \\\\\\$def\n";
+ colorprint($red, @original_output);
+ print "${red}FAIL: \\\\\\ test '$name' ///$def\n";
+ print "${red}FAIL: $name: $bad$def\n";
+ $xit = 0;
+ }
+ elsif ($warn ne "") {
+ my $yellow = "\e[43;37m";
+ my $def = "\e[0m";
+ print "${yellow}PASS: /// test '$name' \\\\\\$def\n";
+ colorprint($yellow, @original_output);
+ print "${yellow}PASS: \\\\\\ test '$name' ///$def\n";
+ print "PASS: $name (with warnings)\n";
+ }
+ else {
+ print "PASS: $name\n";
+ }
+ return $xit;
+}
+
+sub filter_expected
+{
+ my $outputref = shift;
+ my %C = %{shift()};
+ my $name = shift;
+
+ my %T = %{$C{"TEST_$name"}};
+ my $check = $T{TEST_RUN_OUTPUT} || return "";
+
+ my $bad = "";
+
+ my $output = join("\n", @$outputref) . "\n";
+ if ($output !~ /$check/s) {
+ $bad = "(run output does not match TEST_RUN_OUTPUT)";
+ @$outputref = ("FAIL: $name");
+ } else {
+ @$outputref = ("OK: $name"); # pacify later filter
+ }
+
+ return $bad;
+}
+
+sub filter_bad
+{
+ my $outputref = shift;
+ my $bad = "";
+
+ my @new_output;
+ for my $line (@$outputref) {
+ if ($line =~ /^BAD: (.*)/) {
+ $bad = "(failed)";
+ } else {
+ push @new_output, $line;
+ }
+ }
+
+ @$outputref = @new_output;
+ return $bad;
+}
+
+sub filter_warn
+{
+ my $outputref = shift;
+ my $warn = "";
+
+ my @new_output;
+ for my $line (@$outputref) {
+ if ($line !~ /^WARN: (.*)/) {
+ push @new_output, $line;
+ } else {
+ $warn = "(warned)";
+ }
+ }
+
+ @$outputref = @new_output;
+ return $warn;
+}
+
+sub filter_verbose
+{
+ my $outputref = shift;
+
+ my @new_output;
+ for my $line (@$outputref) {
+ if ($line !~ /^VERBOSE: (.*)/) {
+ push @new_output, $line;
+ }
+ }
+
+ @$outputref = @new_output;
+}
+
+sub filter_valgrind
+{
+ my $outputref = shift;
+ my $errors = 0;
+ my $leaks = 0;
+
+ my @new_output;
+ for my $line (@$outputref) {
+ if ($line =~ /^Approx: do_origins_Dirty\([RW]\): missed \d bytes$/) {
+ # --track-origins warning (harmless)
+ next;
+ }
+ if ($line =~ /^UNKNOWN __disable_threadsignal is unsupported. This warning will not be repeated.$/) {
+ # signals unsupported (harmless)
+ next;
+ }
+ if ($line =~ /^UNKNOWN __pthread_sigmask is unsupported. This warning will not be repeated.$/) {
+ # signals unsupported (harmless)
+ next;
+ }
+ if ($line !~ /^^\.*==\d+==/) {
+ # not valgrind output
+ push @new_output, $line;
+ next;
+ }
+
+ my ($errcount) = ($line =~ /==\d+== ERROR SUMMARY: (\d+) errors/);
+ if (defined $errcount && $errcount > 0) {
+ $errors = 1;
+ }
+
+ (my $leakcount) = ($line =~ /==\d+==\s+(?:definitely|possibly) lost:\s+([0-9,]+)/);
+ if (defined $leakcount && $leakcount > 0) {
+ $leaks = 1;
+ }
+ }
+
+ @$outputref = @new_output;
+
+ my $bad = "";
+ $bad .= "(valgrind errors)" if ($errors);
+ $bad .= "(valgrind leaks)" if ($leaks);
+ return $bad;
+}
+
+sub filter_guardmalloc
+{
+ my $outputref = shift;
+ my $errors = 0;
+
+ my @new_output;
+ for my $line (@$outputref) {
+ if ($line =~ /^GuardMalloc: /) {
+ # guardmalloc prologue
+ next;
+ }
+ if ($line !~ /^GuardMalloc\[[^\]]+\]: /) {
+ # not guardmalloc output
+ push @new_output, $line;
+ next;
+ }
+
+ $errors = 1;
+ }
+
+ @$outputref = @new_output;
+
+ my $bad = "";
+ $bad .= "(guardmalloc errors)" if ($errors);
+ return $bad;
+}
+
+sub gather_simple {
+ my $CREF = shift;
+ my %C = %{$CREF};
+ my $name = shift;
+ chdir_verbose $DIR;
+
+ my $ext = $ALL_TESTS{$name};
+ my $file = "$name.$ext";
+ return 0 if !$file;
+
+ # search file for 'TEST_CONFIG' or '#include "test.h"'
+ # also collect other values:
+ # TEST_CONFIG test conditions
+ # TEST_ENV environment prefix
+ # TEST_CFLAGS compile flags
+ # TEST_BUILD build instructions
+ # TEST_BUILD_OUTPUT expected build stdout/stderr
+ # TEST_RUN_OUTPUT expected run stdout/stderr
+ open(my $in, "< $file") || die;
+ my $contents = join "", <$in>;
+
+ my $test_h = ($contents =~ /^\s*#\s*(include|import)\s*"test\.h"/m);
+ my $disabled = ($contents =~ /\bTEST_DISABLED\b/m);
+ my $crashes = ($contents =~ /\bTEST_CRASHES\b/m);
+ my ($conditionstring) = ($contents =~ /\bTEST_CONFIG\b(.*)$/m);
+ my ($envstring) = ($contents =~ /\bTEST_ENV\b(.*)$/m);
+ my ($cflags) = ($contents =~ /\bTEST_CFLAGS\b(.*)$/m);
+ my ($buildcmd) = ($contents =~ /TEST_BUILD\n(.*?\n)END[ *\/]*\n/s);
+ my ($builderror) = ($contents =~ /TEST_BUILD_OUTPUT\n(.*?\n)END[ *\/]*\n/s);
+ my ($runerror) = ($contents =~ /TEST_RUN_OUTPUT\n(.*?\n)END[ *\/]*\n/s);
+
+ return 0 if !$test_h && !$disabled && !$crashes && !defined($conditionstring) && !defined($envstring) && !defined($cflags) && !defined($buildcmd) && !defined($builderror) && !defined($runerror);
+
+ if ($disabled) {
+ print "${yellow}SKIP: $name (disabled by TEST_DISABLED)$def\n";
+ return 0;
+ }
+
+ # check test conditions
+
+ my $run = 1;
+ my %conditions = readconditions($conditionstring);
+ if (! $conditions{LANGUAGE}) {
+ # implicit language restriction from file extension
+ $conditions{LANGUAGE} = $languages_for_extension{$ext};
+ }
+ for my $condkey (keys %conditions) {
+ my @condvalues = @{$conditions{$condkey}};
+
+ # special case: RUN=0 does not affect build
+ if ($condkey eq "RUN" && @condvalues == 1 && $condvalues[0] == 0) {
+ $run = 0;
+ next;
+ }
+
+ my $testvalue = $C{$condkey};
+ next if !defined($testvalue);
+ # testvalue is the configuration being run now
+ # condvalues are the allowed values for this test
+
+ # special case: look up the name of SDK "system"
+ if ($condkey eq "SDK" && $testvalue eq "system") {
+ $testvalue = systemsdkname();
+ }
+
+ my $ok = 0;
+ for my $condvalue (@condvalues) {
+
+ # special case: objc and objc++
+ if ($condkey eq "LANGUAGE") {
+ $condvalue = "objective-c" if $condvalue eq "objc";
+ $condvalue = "objective-c++" if $condvalue eq "objc++";
+ }
+
+ $ok = 1 if ($testvalue eq $condvalue);
+
+ # special case: SDK allows prefixes, and "system" is "macosx"
+ if ($condkey eq "SDK") {
+ $ok = 1 if ($testvalue =~ /^$condvalue/);
+ $ok = 1 if ($testvalue eq "system" && "macosx" =~ /^$condvalue/);
+ }
+
+ # special case: CC and CXX allow substring matches
+ if ($condkey eq "CC" || $condkey eq "CXX") {
+ $ok = 1 if ($testvalue =~ /$condvalue/);
+ }
+
+ last if $ok;
+ }
+
+ if (!$ok) {
+ my $plural = (@condvalues > 1) ? "one of: " : "";
+ print "SKIP: $name ($condkey=$testvalue, but test requires $plural", join(' ', @condvalues), ")\n";
+ return 0;
+ }
+ }
+
+ # builderror is multiple REs separated by OR
+ if (defined $builderror) {
+ $builderror =~ s/\nOR\n/\n|/sg;
+ $builderror = "^(" . $builderror . ")\$";
+ }
+ # runerror is multiple REs separated by OR
+ if (defined $runerror) {
+ $runerror =~ s/\nOR\n/\n|/sg;
+ $runerror = "^(" . $runerror . ")\$";
+ }
+
+ # save some results for build and run phases
+ $$CREF{"TEST_$name"} = {
+ TEST_BUILD => $buildcmd,
+ TEST_BUILD_OUTPUT => $builderror,
+ TEST_CRASHES => $crashes,
+ TEST_RUN_OUTPUT => $runerror,
+ TEST_CFLAGS => $cflags,
+ TEST_ENV => $envstring,
+ TEST_RUN => $run,
+ };
+
+ return 1;
+}
+
+# Builds a simple test
+sub build_simple {
+ my %C = %{shift()};
+ my $name = shift;
+ my %T = %{$C{"TEST_$name"}};
+ chdir_verbose "$C{DIR}/$name.build";
+
+ my $ext = $ALL_TESTS{$name};
+ my $file = "$DIR/$name.$ext";
+
+ if ($T{TEST_CRASHES}) {
+ `echo '$crashcatch' > crashcatch.c`;
+ make("$C{COMPILE_C} -dynamiclib -o libcrashcatch.dylib -x c crashcatch.c");
+ die "$?" if $?;
+ }
+
+ my $cmd = $T{TEST_BUILD} ? eval "return \"$T{TEST_BUILD}\"" : "$C{COMPILE} $T{TEST_CFLAGS} $file -o $name.out";
+
+ my $output = make($cmd);
+
+ my $ok;
+ if (my $builderror = $T{TEST_BUILD_OUTPUT}) {
+ # check for expected output and ignore $?
+ if ($output =~ /$builderror/s) {
+ $ok = 1;
+ } else {
+ print "${red}FAIL: /// test '$name' \\\\\\$def\n";
+ colorprint $red, $output;
+ print "${red}FAIL: \\\\\\ test '$name' ///$def\n";
+ print "${red}FAIL: $name (build output does not match TEST_BUILD_OUTPUT)$def\n";
+ $ok = 0;
+ }
+ } elsif ($?) {
+ print "${red}FAIL: /// test '$name' \\\\\\$def\n";
+ colorprint $red, $output;
+ print "${red}FAIL: \\\\\\ test '$name' ///$def\n";
+ print "${red}FAIL: $name (build failed)$def\n";
+ $ok = 0;
+ } elsif ($output ne "") {
+ print "${red}FAIL: /// test '$name' \\\\\\$def\n";
+ colorprint $red, $output;
+ print "${red}FAIL: \\\\\\ test '$name' ///$def\n";
+ print "${red}FAIL: $name (unexpected build output)$def\n";
+ $ok = 0;
+ } else {
+ $ok = 1;
+ }
+
+
+ if ($ok) {
+ foreach my $file (glob("*.out *.dylib *.bundle")) {
+ make("dsymutil $file");
+ }
+ }
+
+ return $ok;
+}
+
+# Run a simple test (testname.out, with error checking of stdout and stderr)
+sub run_simple {
+ my %C = %{shift()};
+ my $name = shift;
+ my %T = %{$C{"TEST_$name"}};
+
+ if (! $T{TEST_RUN}) {
+ print "PASS: $name (build only)\n";
+ return 1;
+ }
+ else {
+ chdir_verbose "$C{DIR}/$name.build";
+ }
+
+ my $env = "$C{ENV} $T{TEST_ENV}";
+ if ($T{TEST_CRASHES}) {
+ $env .= " DYLD_INSERT_LIBRARIES=libcrashcatch.dylib";
+ }
+
+ my $output;
+
+ if ($C{ARCH} =~ /^arm/ && `unamep -p` !~ /^arm/) {
+ # run on iOS device
+
+ my $remotedir = "/var/root/test/" . basename($C{DIR}) . "/$name.build";
+ my $remotedyld = " DYLD_LIBRARY_PATH=$remotedir";
+ $remotedyld .= ":/var/root/test/" if ($C{TESTLIB} ne $TESTLIBPATH);
+
+ # elide host-specific paths
+ $env =~ s/DYLD_LIBRARY_PATH=\S+//;
+ $env =~ s/DYLD_ROOT_PATH=\S+//;
+
+ my $cmd = "ssh iphone 'cd $remotedir && $remotedyld $env ./$name.out'";
+ $output = make("$cmd");
+ }
+ else {
+ # run locally
+
+ my $cmd = "$env ./$name.out";
+ $output = make("sh -c '$cmd 2>&1' 2>&1");
+ # need extra sh level to capture "sh: Illegal instruction" after crash
+ # fixme fail if $? except tests that expect to crash
+ }
+
+ return check_output(\%C, $name, split("\n", $output));
+}
+
+
+sub make_one_config {
+ my $configref = shift;
+ my $root = shift;
+ my %C = %{$configref};
+
+ $C{LANGUAGE} = "objective-c" if $C{LANGUAGE} eq "objc";
+ $C{LANGUAGE} = "objective-c++" if $C{LANGUAGE} eq "objc++";
+
+ # Look up SDK
+ # Try exact match first.
+ # Then try lexically-last prefix match (so "macosx" => "macosx10.7internal").
+ my @sdks = getsdks();
+ if ($VERBOSE) {
+ print "Installed SDKs: @sdks\n";
+ }
+ my $exactsdk = undef;
+ my $prefixsdk = undef;
+ foreach my $sdk (@sdks) {
+ my $SDK = $C{SDK};
+ $exactsdk = $sdk if ($sdk eq $SDK);
+ # check for digits to prevent e.g. "iphone" => "iphonesimulator4.2"
+ $prefixsdk = $sdk if ($sdk =~ /^$SDK[0-9]/ && $sdk gt $prefixsdk);
+ }
+ if ($exactsdk) {
+ $C{SDK} = $exactsdk;
+ } elsif ($prefixsdk) {
+ $C{SDK} = $prefixsdk;
+ } else {
+ die "unknown SDK '$C{SDK}'\nInstalled SDKs: @sdks\n";
+ }
+
+ # set the config name now, after massaging the language and sdk,
+ # but before adding other settings
+ my $configname = config_name(%C);
+ die if ($configname =~ /'/);
+ die if ($configname =~ /\//);
+ die if ($configname =~ / /);
+ $C{DIR} = "$BUILDDIR/$configname";
+ ($C{NAME} = $configname) =~ s/~/ /g;
+
+ $C{SDK_PATH} = "/";
+ if ($C{SDK} ne "system") {
+ ($C{SDK_PATH}) = (`xcodebuild -version -sdk $C{SDK} Path` =~ /^\s*(.+?)\s*$/);
+ }
+
+ # Look up test library (possible in root or SDK_PATH)
+
+ if (-e (glob "$root/*~dst")[0]) {
+ $root = (glob "$root/*~dst")[0];
+ }
+
+ if ($root ne "" && -e "$root$C{SDK_PATH}$TESTLIBPATH") {
+ $C{TESTLIB} = "$root$C{SDK_PATH}$TESTLIBPATH";
+ } elsif (-e "$root$TESTLIBPATH") {
+ $C{TESTLIB} = "$root$TESTLIBPATH";
+ } elsif (-e "$root/$TESTLIBNAME") {
+ $C{TESTLIB} = "$root/$TESTLIBNAME";
+ } else {
+ die "No $TESTLIBNAME in root '$root' and sdk '$C{SDK_PATH}'\n";
+ }
+
+ # Look up compilers
+ $C{CXX} = cplusplus($C{CC});
+ if ($BUILD) {
+ my $oldcc = $C{CC};
+ my $oldcxx = $C{CXX};
+
+ if (-e $C{CC}) {
+ # use it
+ } elsif (-e "$C{SDK_PATH}/$C{CC}") {
+ $C{CC} = "$C{SDK_PATH}/$C{CC}";
+ } elsif ($C{SDK} eq "system" && -e "/usr/bin/$C{CC}") {
+ $C{CC} = "/usr/bin/$C{CC}";
+ } elsif ($C{SDK} eq "system") {
+ $C{CC} = `xcrun -find $C{CC} 2>/dev/null`;
+ chomp $C{CC};
+ } else {
+ $C{CC} = `xcrun -sdk $C{SDK} -find $C{CC} 2>/dev/null`;
+ chomp $C{CC};
+ }
+
+ if (-e $C{CXX}) {
+ # use it
+ } elsif (-e "$C{SDK_PATH}/$C{CXX}") {
+ $C{CXX} = "$C{SDK_PATH}/$C{CXX}";
+ } elsif ($C{SDK} eq "system" && -e "/usr/bin/$C{CXX}") {
+ $C{CXX} = "/usr/bin/$C{CXX}";
+ } elsif ($C{SDK} eq "system") {
+ $C{CXX} = `xcrun -find $C{CXX} 2>/dev/null`;
+ chomp $C{CXX};
+ } else {
+ $C{CXX} = `xcrun -sdk $C{SDK} -find $C{CXX} 2>/dev/null`;
+ chomp $C{CXX};
+ }
+
+ die "No compiler '$oldcc' in SDK '$C{SDK}'\n" if ! -e $C{CC};
+ die "No compiler '$oldcxx' '$C{CXX}' in SDK '$C{SDK}'\n" if ! -e $C{CXX};
+ }
+
+
+ # Populate cflags
+
+ # save-temps so dsymutil works so debug info works
+ my $cflags = "-I$DIR -W -Wall -Wno-deprecated-declarations -Wshorten-64-to-32 -g -save-temps -Os -arch $C{ARCH} ";
+ my $objcflags = "";
+
+ if ($C{SDK} ne "system") {
+ $cflags .= " -isysroot '$C{SDK_PATH}'";
+ $cflags .= " '-Wl,-syslibroot,$C{SDK_PATH}'";
+ }
+
+ if ($C{SDK} =~ /^iphoneos[0-9]/ && $cflags !~ /-miphoneos-version-min/) {
+ my ($vers) = ($C{SDK} =~ /^iphoneos([0-9]+\.[0-9+])/);
+ $cflags .= " -miphoneos-version-min=$vers";
+ }
+ if ($C{SDK} =~ /^iphonesimulator[0-9]/ && $cflags !~ /-D__IPHONE_OS_VERSION_MIN_REQUIRED/) {
+ my ($vers) = ($C{SDK} =~ /^iphonesimulator([0-9]+\.[0-9+])/);
+ $vers = int($vers * 10000); # 4.2 => 42000
+ $cflags .= " -D__IPHONE_OS_VERSION_MIN_REQUIRED=$vers";
+ }
+ if ($C{SDK} =~ /^iphonesimulator/) {
+ $objcflags .= " -fobjc-abi-version=2 -fobjc-legacy-dispatch";
+ }
+
+ if ($root ne "") {
+ my $library_path = dirname($C{TESTLIB});
+ $cflags .= " -L$library_path";
+ $cflags .= " -isystem '$root/usr/include'";
+ $cflags .= " -isystem '$root/usr/local/include'";
+
+ if ($C{SDK_PATH} ne "/") {
+ $cflags .= " -isystem '$root$C{SDK_PATH}/usr/include'";
+ $cflags .= " -isystem '$root$C{SDK_PATH}/usr/local/include'";
+ }
+ }
+
+ if ($C{CC} =~ /clang/) {
+ $cflags .= " -Qunused-arguments -fno-caret-diagnostics";
+ }
+
+ # Populate objcflags
+
+ $objcflags .= " -lobjc";
+ if ($C{GC}) {
+ $objcflags .= " -fobjc-gc";
+ }
+ if (supportsgc($C{SDK})) {
+ $objcflags .= " -lauto";
+ }
+
+ # Populate ENV_PREFIX
+ $C{ENV} = "LANG=C";
+ $C{ENV} .= " VERBOSE=1" if $VERBOSE;
+ if ($root ne "") {
+ my $library_path = dirname($C{TESTLIB});
+ die "no spaces allowed in root" if $library_path =~ /\s+/;
+ $C{ENV} .= " DYLD_LIBRARY_PATH=$library_path" if ($library_path ne "/usr/lib");
+ }
+ if ($C{SDK_PATH} ne "/") {
+ die "no spaces allowed in sdk" if $C{SDK_PATH} =~ /\s+/;
+ $C{ENV} .= " DYLD_ROOT_PATH=$C{SDK_PATH}";
+ }
+ if ($C{GUARDMALLOC}) {
+ $ENV{GUARDMALLOC} = "1"; # checked by tests and errcheck.pl
+ $C{ENV} .= " DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib";
+ }
+ if ($C{SDK} =~ /^iphonesimulator[0-9]/) {
+ my ($vers) = ($C{SDK} =~ /^iphonesimulator([0-9]+\.[0-9+])/);
+ $C{ENV} .=
+ " CFFIXED_USER_HOME=$ENV{HOME}/Library/Application\\ Support/iPhone\\ Simulator/$vers" .
+ " IPHONE_SIMULATOR_ROOT=$C{SDK_PATH}" .
+ " IPHONE_SHARED_RESOURCES_DIRECTORY=$ENV{HOME}/Library/Application\\ Support/iPhone\\ Simulator/$vers";
+ }
+
+ # Populate compiler commands
+ $C{COMPILE_C} = "LANG=C '$C{CC}' $cflags -x c -std=gnu99";
+ $C{COMPILE_CXX} = "LANG=C '$C{CXX}' $cflags -x c++";
+ $C{COMPILE_M} = "LANG=C '$C{CC}' $cflags $objcflags -x objective-c -std=gnu99";
+ $C{COMPILE_MM} = "LANG=C '$C{CXX}' $cflags $objcflags -x objective-c++";
+
+ $C{COMPILE} = $C{COMPILE_C} if $C{LANGUAGE} eq "c";
+ $C{COMPILE} = $C{COMPILE_CXX} if $C{LANGUAGE} eq "c++";
+ $C{COMPILE} = $C{COMPILE_M} if $C{LANGUAGE} eq "objective-c";
+ $C{COMPILE} = $C{COMPILE_MM} if $C{LANGUAGE} eq "objective-c++";
+ die "unknown language '$C{LANGUAGE}'\n" if !defined $C{COMPILE};
+
+ ($C{COMPILE_NOGC} = $C{COMPILE}) =~ s/-fobjc-gc\S*//;
+
+ %$configref = %C;
+}
+
+sub make_configs {
+ my ($root, %args) = @_;
+
+ my @results = ({}); # start with one empty config
+
+ for my $key (keys %args) {
+ my @newresults;
+ my @values = @{$args{$key}};
+ for my $configref (@results) {
+ my %config = %{$configref};
+ for my $value (@values) {
+ my %newconfig = %config;
+ $newconfig{$key} = $value;
+ push @newresults, \%newconfig;
+ }
+ }
+ @results = @newresults;
+ }
+
+ for my $configref(@results) {
+ make_one_config($configref, $root);
+ }
+
+ return @results;
+}
+
+sub config_name {
+ my %config = @_;
+ my $name = "";
+ for my $key (sort keys %config) {
+ $name .= '~' if $name ne "";
+ $name .= "$key=$config{$key}";
+ }
+ return $name;
+}
+
+sub run_one_config {
+ my %C = %{shift()};
+ my @tests = @_;
+
+ # Build and run
+ my $testcount = 0;
+ my $failcount = 0;
+
+ my @gathertests;
+ foreach my $test (@tests) {
+ if ($VERBOSE) {
+ print "\nGATHER $test\n";
+ }
+
+ if ($ALL_TESTS{$test}) {
+ gather_simple(\%C, $test) || next; # not pass, not fail
+ push @gathertests, $test;
+ } else {
+ die "No test named '$test'\n";
+ }
+ }
+
+ my @builttests;
+ if (!$BUILD) {
+ @builttests = @gathertests;
+ $testcount = scalar(@gathertests);
+ } else {
+ my $configdir = $C{DIR};
+ print $configdir, "\n" if $VERBOSE;
+ mkdir $configdir || die;
+
+ foreach my $test (@gathertests) {
+ if ($VERBOSE) {
+ print "\nBUILD $test\n";
+ }
+ mkdir "$configdir/$test.build" || die;
+
+ if ($ALL_TESTS{$test}) {
+ $testcount++;
+ if (!build_simple(\%C, $test)) {
+ $failcount++;
+ } else {
+ push @builttests, $test;
+ }
+ } else {
+ die "No test named '$test'\n";
+ }
+ }
+ }
+
+ if (!$RUN || !scalar(@builttests)) {
+ # nothing to do
+ }
+ else {
+ if ($C{ARCH} =~ /^arm/ && `unamep -p` !~ /^arm/) {
+ # upload all tests to iOS device
+ make("RSYNC_PASSWORD=alpine rsync -av $C{DIR} rsync://root\@localhost:10873/root/var/root/test/");
+ die "Couldn't rsync tests to device\n" if ($?);
+
+ # upload library to iOS device
+ if ($C{TESTLIB} ne $TESTLIBPATH) {
+ # hack - send thin library because device may use lib=armv7
+ # even though app=armv6, and we want to set the lib's arch
+ make("lipo -output /tmp/$TESTLIBNAME -thin $C{ARCH} $C{TESTLIB} || cp $C{TESTLIB} /tmp/$TESTLIBNAME");
+ die "Couldn't thin $C{TESTLIB} to $C{ARCH}\n" if ($?);
+ make("RSYNC_PASSWORD=alpine rsync -av /tmp/$TESTLIBNAME rsync://root\@localhost:10873/root/var/root/test/");
+ die "Couldn't rsync $C{TESTLIB} to device\n" if ($?);
+ }
+ }
+
+ foreach my $test (@builttests) {
+ print "\nRUN $test\n" if ($VERBOSE);
+
+ if ($ALL_TESTS{$test})
+ {
+ if (!run_simple(\%C, $test)) {
+ $failcount++;
+ }
+ } else {
+ die "No test named '$test'\n";
+ }
+ }
+ }
+
+ return ($testcount, $failcount);
+}
+
+
+
+# Return value if set by "$argname=value" on the command line
+# Return $default if not set.
+sub getargs {
+ my ($argname, $default) = @_;
+
+ foreach my $arg (@ARGV) {
+ my ($value) = ($arg =~ /^$argname=(.+)$/);
+ return [split ',', $value] if defined $value;
+ }
+
+ return [$default];
+}
+
+# Return 1 or 0 if set by "$argname=1" or "$argname=0" on the
+# command line. Return $default if not set.
+sub getbools {
+ my ($argname, $default) = @_;
+
+ my @values = @{getargs($argname, $default)};
+ return [( map { ($_ eq "0") ? 0 : 1 } @values )];
+}
+
+sub getarg {
+ my ($argname, $default) = @_;
+ my @values = @{getargs($argname, $default)};
+ die "Only one value allowed for $argname\n" if @values > 1;
+ return $values[0];
+}
+
+sub getbool {
+ my ($argname, $default) = @_;
+ my @values = @{getbools($argname, $default)};
+ die "Only one value allowed for $argname\n" if @values > 1;
+ return $values[0];
+}
+
+
+# main
+my %args;
+
+
+my $default_arch = (`/usr/sbin/sysctl hw.optional.x86_64` eq "hw.optional.x86_64: 1\n") ? "x86_64" : "i386";
+$args{ARCH} = getargs("ARCH", 0);
+$args{ARCH} = getargs("ARCHS", $default_arch) if !@{$args{ARCH}}[0];
+
+$args{SDK} = getargs("SDK", "system");
+
+$args{GC} = getbools("GC", 0);
+$args{LANGUAGE} = [ map { lc($_) } @{getargs("LANGUAGE", "objective-c")} ];
+
+$args{CC} = getargs("CC", "llvm-gcc-4.2");
+
+$args{GUARDMALLOC} = getbools("GUARDMALLOC", 0);
+
+$BUILD = getbool("BUILD", 1);
+$RUN = getbool("RUN", 1);
+$VERBOSE = getbool("VERBOSE", 0);
+
+my $root = getarg("ROOT", "");
+$root =~ s#/*$##;
+
+my @tests = gettests();
+
+print "note: -----\n";
+print "note: testing root '$root'\n";
+
+my @configs = make_configs($root, %args);
+
+print "note: -----\n";
+print "note: testing ", scalar(@configs), " configurations:\n";
+for my $configref (@configs) {
+ my $configname = $$configref{NAME};
+ print "note: configuration $configname\n";
+}
+
+if ($BUILD) {
+ `rm -rf '$BUILDDIR'`;
+ mkdir "$BUILDDIR" || die;
+}
+
+my $failed = 0;
+
+my $testconfigs = @configs;
+my $failconfigs = 0;
+my $testcount = 0;
+my $failcount = 0;
+for my $configref (@configs) {
+ my $configname = $$configref{NAME};
+ print "note: -----\n";
+ print "note: \nnote: $configname\nnote: \n";
+
+ (my $t, my $f) = eval { run_one_config($configref, @tests); };
+ if ($@) {
+ chomp $@;
+ print "${red}FAIL: $configname${def}\n";
+ print "${red}FAIL: $@${def}\n";
+ $failconfigs++;
+ } else {
+ my $color = ($f ? $red : "");
+ print "note:\n";
+ print "${color}note: $configname$def\n";
+ print "${color}note: $t tests, $f failures$def\n";
+ $testcount += $t;
+ $failcount += $f;
+ $failconfigs++ if ($f);
+ }
+}
+
+print "note: -----\n";
+my $color = ($failconfigs ? $red : "");
+print "${color}note: $testconfigs configurations, $failconfigs with failures$def\n";
+print "${color}note: $testcount tests, $failcount failures$def\n";
+
+$failed = ($failconfigs ? 1 : 0);
+
+exit ($failed ? 1 : 0);
+++ /dev/null
-// !$*UTF8*$!
-{
- archiveVersion = 1;
- classes = {
- };
- objectVersion = 42;
- objects = {
-
-/* Begin PBXBuildFile section */
- 3986128C0A534D3700C30D67 /* accessors.m in Sources */ = {isa = PBXBuildFile; fileRef = 3986128B0A534D3700C30D67 /* accessors.m */; };
- 3986128F0A534D6300C30D67 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3986128D0A534D6300C30D67 /* CoreFoundation.framework */; };
- 398612900A534D6300C30D67 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3986128E0A534D6300C30D67 /* Foundation.framework */; };
- 39B32C010A54A81100A8062E /* libobjc.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 39B32C000A54A81100A8062E /* libobjc.dylib */; };
- 39B32C020A54A81100A8062E /* libobjc.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 39B32C000A54A81100A8062E /* libobjc.dylib */; };
- 39B32C030A54A81100A8062E /* libobjc.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 39B32C000A54A81100A8062E /* libobjc.dylib */; };
- 39C8683B0A549A6300B32D32 /* layout.m in Sources */ = {isa = PBXBuildFile; fileRef = 39C8683A0A549A6300B32D32 /* layout.m */; };
- 39C868400A549A7E00B32D32 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3986128E0A534D6300C30D67 /* Foundation.framework */; };
- 39C868410A549A7F00B32D32 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3986128D0A534D6300C30D67 /* CoreFoundation.framework */; };
- 39C868820A549B8C00B32D32 /* category.m in Sources */ = {isa = PBXBuildFile; fileRef = 39C8687C0A549B6700B32D32 /* category.m */; };
- 39C868830A549B8D00B32D32 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3986128D0A534D6300C30D67 /* CoreFoundation.framework */; };
- 39C868840A549B8D00B32D32 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3986128E0A534D6300C30D67 /* Foundation.framework */; };
-/* End PBXBuildFile section */
-
-/* Begin PBXFileReference section */
- 39481F560A54AA2A007DE311 /* testaccessors */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testaccessors; sourceTree = BUILT_PRODUCTS_DIR; };
- 3986128B0A534D3700C30D67 /* accessors.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = accessors.m; sourceTree = "<group>"; };
- 3986128D0A534D6300C30D67 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; };
- 3986128E0A534D6300C30D67 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
- 39B32C000A54A81100A8062E /* libobjc.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libobjc.dylib; path = /usr/lib/libobjc.dylib; sourceTree = "<absolute>"; };
- 39C868380A549A4F00B32D32 /* layout */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = layout; sourceTree = BUILT_PRODUCTS_DIR; };
- 39C8683A0A549A6300B32D32 /* layout.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = layout.m; sourceTree = "<group>"; };
- 39C8687C0A549B6700B32D32 /* category.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = category.m; sourceTree = "<group>"; };
- 39C868800A549B8400B32D32 /* category */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = category; sourceTree = BUILT_PRODUCTS_DIR; };
-/* End PBXFileReference section */
-
-/* Begin PBXFrameworksBuildPhase section */
- 39C868360A549A4F00B32D32 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 39C868400A549A7E00B32D32 /* Foundation.framework in Frameworks */,
- 39C868410A549A7F00B32D32 /* CoreFoundation.framework in Frameworks */,
- 39B32C020A54A81100A8062E /* libobjc.dylib in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- 39C8687E0A549B8400B32D32 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 39C868830A549B8D00B32D32 /* CoreFoundation.framework in Frameworks */,
- 39C868840A549B8D00B32D32 /* Foundation.framework in Frameworks */,
- 39B32C030A54A81100A8062E /* libobjc.dylib in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- 39D7E9B70A40E5EB007A7A47 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 3986128F0A534D6300C30D67 /* CoreFoundation.framework in Frameworks */,
- 398612900A534D6300C30D67 /* Foundation.framework in Frameworks */,
- 39B32C010A54A81100A8062E /* libobjc.dylib in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXFrameworksBuildPhase section */
-
-/* Begin PBXGroup section */
- 08FB7794FE84155DC02AAC07 /* libobjc */ = {
- isa = PBXGroup;
- children = (
- 3986128B0A534D3700C30D67 /* accessors.m */,
- 39C8687C0A549B6700B32D32 /* category.m */,
- 39C8683A0A549A6300B32D32 /* layout.m */,
- 39B32C000A54A81100A8062E /* libobjc.dylib */,
- 3986128D0A534D6300C30D67 /* CoreFoundation.framework */,
- 3986128E0A534D6300C30D67 /* Foundation.framework */,
- 39C868380A549A4F00B32D32 /* layout */,
- 39C868800A549B8400B32D32 /* category */,
- 39481F560A54AA2A007DE311 /* testaccessors */,
- );
- name = libobjc;
- sourceTree = "<group>";
- };
-/* End PBXGroup section */
-
-/* Begin PBXNativeTarget section */
- 39C868370A549A4F00B32D32 /* layout */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = 39C8683C0A549A6300B32D32 /* Build configuration list for PBXNativeTarget "layout" */;
- buildPhases = (
- 39C868350A549A4F00B32D32 /* Sources */,
- 39C868360A549A4F00B32D32 /* Frameworks */,
- );
- buildRules = (
- );
- dependencies = (
- );
- name = layout;
- productName = layout;
- productReference = 39C868380A549A4F00B32D32 /* layout */;
- productType = "com.apple.product-type.tool";
- };
- 39C8687F0A549B8400B32D32 /* category */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = 39C868860A549BAB00B32D32 /* Build configuration list for PBXNativeTarget "category" */;
- buildPhases = (
- 39C8687D0A549B8400B32D32 /* Sources */,
- 39C8687E0A549B8400B32D32 /* Frameworks */,
- );
- buildRules = (
- );
- dependencies = (
- );
- name = category;
- productName = category;
- productReference = 39C868800A549B8400B32D32 /* category */;
- productType = "com.apple.product-type.tool";
- };
- 39D7E9B80A40E5EB007A7A47 /* accessors */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = 39D7E9C70A40E687007A7A47 /* Build configuration list for PBXNativeTarget "accessors" */;
- buildPhases = (
- 39D7E9B60A40E5EB007A7A47 /* Sources */,
- 39D7E9B70A40E5EB007A7A47 /* Frameworks */,
- );
- buildRules = (
- );
- dependencies = (
- );
- name = accessors;
- productName = testaccessors;
- productReference = 39481F560A54AA2A007DE311 /* testaccessors */;
- productType = "com.apple.product-type.tool";
- };
-/* End PBXNativeTarget section */
-
-/* Begin PBXProject section */
- 08FB7793FE84155DC02AAC07 /* Project object */ = {
- isa = PBXProject;
- buildConfigurationList = 39DBC2AE09D8B0CD000A1C12 /* Build configuration list for PBXProject "test" */;
- hasScannedForEncodings = 1;
- mainGroup = 08FB7794FE84155DC02AAC07 /* libobjc */;
- productRefGroup = 08FB7794FE84155DC02AAC07 /* libobjc */;
- projectDirPath = "";
- targets = (
- 39D7E9B80A40E5EB007A7A47 /* accessors */,
- 39C868370A549A4F00B32D32 /* layout */,
- 39C8687F0A549B8400B32D32 /* category */,
- );
- };
-/* End PBXProject section */
-
-/* Begin PBXSourcesBuildPhase section */
- 39C868350A549A4F00B32D32 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 39C8683B0A549A6300B32D32 /* layout.m in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- 39C8687D0A549B8400B32D32 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 39C868820A549B8C00B32D32 /* category.m in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- 39D7E9B60A40E5EB007A7A47 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 3986128C0A534D3700C30D67 /* accessors.m in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXSourcesBuildPhase section */
-
-/* Begin XCBuildConfiguration section */
- 39C8683D0A549A6300B32D32 /* Development */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- COPY_PHASE_STRIP = NO;
- GCC_DYNAMIC_NO_PIC = YES;
- GCC_ENABLE_OBJC_GC = YES;
- GCC_ENABLE_SYMBOL_SEPARATION = NO;
- GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
- GCC_MODEL_TUNING = G5;
- GCC_OPTIMIZATION_LEVEL = 0;
- GCC_PRECOMPILE_PREFIX_HEADER = NO;
- INSTALL_PATH = "$(HOME)/bin";
- OTHER_LDFLAGS = (
- "-framework",
- Foundation,
- "-framework",
- AppKit,
- );
- PREBINDING = NO;
- PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
- PRODUCT_NAME = layout;
- };
- name = Development;
- };
- 39C8683E0A549A6300B32D32 /* Deployment */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- COPY_PHASE_STRIP = YES;
- GCC_DYNAMIC_NO_PIC = YES;
- GCC_ENABLE_OBJC_GC = YES;
- GCC_ENABLE_SYMBOL_SEPARATION = NO;
- GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
- GCC_MODEL_TUNING = G5;
- GCC_PRECOMPILE_PREFIX_HEADER = NO;
- INSTALL_PATH = "$(HOME)/bin";
- OTHER_LDFLAGS = (
- "-framework",
- Foundation,
- "-framework",
- AppKit,
- );
- PREBINDING = NO;
- PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
- PRODUCT_NAME = layout;
- };
- name = Deployment;
- };
- 39C8683F0A549A6300B32D32 /* Default */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- GCC_DYNAMIC_NO_PIC = YES;
- GCC_ENABLE_OBJC_GC = YES;
- GCC_ENABLE_SYMBOL_SEPARATION = NO;
- GCC_MODEL_TUNING = G5;
- GCC_PRECOMPILE_PREFIX_HEADER = NO;
- INSTALL_PATH = "$(HOME)/bin";
- OTHER_LDFLAGS = (
- "-framework",
- Foundation,
- "-framework",
- AppKit,
- );
- PREBINDING = NO;
- PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
- PRODUCT_NAME = layout;
- };
- name = Default;
- };
- 39C868870A549BAB00B32D32 /* Development */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- COPY_PHASE_STRIP = NO;
- GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = YES;
- GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
- GCC_MODEL_TUNING = G5;
- GCC_OPTIMIZATION_LEVEL = 0;
- GCC_PRECOMPILE_PREFIX_HEADER = YES;
- GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h";
- INSTALL_PATH = "$(HOME)/bin";
- OTHER_LDFLAGS = (
- "-framework",
- Foundation,
- "-framework",
- AppKit,
- );
- PREBINDING = NO;
- PRODUCT_NAME = category;
- ZERO_LINK = YES;
- };
- name = Development;
- };
- 39C868880A549BAB00B32D32 /* Deployment */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- COPY_PHASE_STRIP = YES;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
- GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
- GCC_MODEL_TUNING = G5;
- GCC_PRECOMPILE_PREFIX_HEADER = YES;
- GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h";
- INSTALL_PATH = "$(HOME)/bin";
- OTHER_LDFLAGS = (
- "-framework",
- Foundation,
- "-framework",
- AppKit,
- );
- PREBINDING = NO;
- PRODUCT_NAME = category;
- ZERO_LINK = NO;
- };
- name = Deployment;
- };
- 39C868890A549BAB00B32D32 /* Default */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- GCC_ENABLE_FIX_AND_CONTINUE = YES;
- GCC_MODEL_TUNING = G5;
- GCC_PRECOMPILE_PREFIX_HEADER = YES;
- GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h";
- INSTALL_PATH = "$(HOME)/bin";
- OTHER_LDFLAGS = (
- "-framework",
- Foundation,
- "-framework",
- AppKit,
- );
- PREBINDING = NO;
- PRODUCT_NAME = category;
- ZERO_LINK = YES;
- };
- name = Default;
- };
- 39D7E9C80A40E687007A7A47 /* Development */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- COPY_PHASE_STRIP = NO;
- GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = YES;
- GCC_ENABLE_OBJC_GC = YES;
- GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
- GCC_MODEL_TUNING = G5;
- GCC_OPTIMIZATION_LEVEL = 0;
- GCC_PRECOMPILE_PREFIX_HEADER = NO;
- GCC_PREPROCESSOR_DEFINITIONS = TEST_ACCESSORS;
- INSTALL_PATH = "$(HOME)/bin";
- OTHER_LDFLAGS = (
- "-framework",
- Foundation,
- "-framework",
- AppKit,
- );
- PREBINDING = NO;
- PRODUCT_NAME = testaccessors;
- ZERO_LINK = YES;
- };
- name = Development;
- };
- 39D7E9C90A40E687007A7A47 /* Deployment */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- COPY_PHASE_STRIP = YES;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
- GCC_ENABLE_OBJC_GC = YES;
- GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
- GCC_MODEL_TUNING = G5;
- GCC_PRECOMPILE_PREFIX_HEADER = NO;
- GCC_PREPROCESSOR_DEFINITIONS = TEST_ACCESSORS;
- INSTALL_PATH = "$(HOME)/bin";
- OTHER_LDFLAGS = (
- "-framework",
- Foundation,
- "-framework",
- AppKit,
- );
- PREBINDING = NO;
- PRODUCT_NAME = testaccessors;
- ZERO_LINK = NO;
- };
- name = Deployment;
- };
- 39D7E9CA0A40E687007A7A47 /* Default */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- GCC_ENABLE_FIX_AND_CONTINUE = YES;
- GCC_ENABLE_OBJC_GC = YES;
- GCC_MODEL_TUNING = G5;
- GCC_PRECOMPILE_PREFIX_HEADER = NO;
- GCC_PREPROCESSOR_DEFINITIONS = TEST_ACCESSORS;
- INSTALL_PATH = "$(HOME)/bin";
- OTHER_LDFLAGS = (
- "-framework",
- Foundation,
- "-framework",
- AppKit,
- );
- PREBINDING = NO;
- PRODUCT_NAME = testaccessors;
- ZERO_LINK = YES;
- };
- name = Default;
- };
- 39DBC2AF09D8B0CD000A1C12 /* Development */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- GCC_ENABLE_OBJC_GC = YES;
- };
- name = Development;
- };
- 39DBC2B009D8B0CD000A1C12 /* Deployment */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- GCC_ENABLE_OBJC_GC = YES;
- };
- name = Deployment;
- };
- 39DBC2B109D8B0CD000A1C12 /* Default */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- GCC_ENABLE_OBJC_GC = YES;
- };
- name = Default;
- };
-/* End XCBuildConfiguration section */
-
-/* Begin XCConfigurationList section */
- 39C8683C0A549A6300B32D32 /* Build configuration list for PBXNativeTarget "layout" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 39C8683D0A549A6300B32D32 /* Development */,
- 39C8683E0A549A6300B32D32 /* Deployment */,
- 39C8683F0A549A6300B32D32 /* Default */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Default;
- };
- 39C868860A549BAB00B32D32 /* Build configuration list for PBXNativeTarget "category" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 39C868870A549BAB00B32D32 /* Development */,
- 39C868880A549BAB00B32D32 /* Deployment */,
- 39C868890A549BAB00B32D32 /* Default */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Default;
- };
- 39D7E9C70A40E687007A7A47 /* Build configuration list for PBXNativeTarget "accessors" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 39D7E9C80A40E687007A7A47 /* Development */,
- 39D7E9C90A40E687007A7A47 /* Deployment */,
- 39D7E9CA0A40E687007A7A47 /* Default */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Default;
- };
- 39DBC2AE09D8B0CD000A1C12 /* Build configuration list for PBXProject "test" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 39DBC2AF09D8B0CD000A1C12 /* Development */,
- 39DBC2B009D8B0CD000A1C12 /* Deployment */,
- 39DBC2B109D8B0CD000A1C12 /* Default */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Default;
- };
-/* End XCConfigurationList section */
- };
- rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
-}
+/*
+TEST_BUILD
+ $C{COMPILE} $DIR/unload4.m -o unload4.dylib -dynamiclib
+ $C{COMPILE_C} $DIR/unload3.c -o unload3.dylib -dynamiclib
+ $C{COMPILE} $DIR/unload2.m -o unload2.bundle -bundle
+ $C{COMPILE} $DIR/unload.m -o unload.out
+END
+ */
+
#include "test.h"
#include <objc/runtime.h>
#include <dlfcn.h>
testassert(names);
free(names);
- void *bundle = dlopen("unload2.out", RTLD_LAZY);
+ void *bundle = dlopen("unload2.bundle", RTLD_LAZY);
testassert(bundle);
names = objc_copyImageNames(&imageCount);
testassert(names);
testassert(imageCount == imageCount0 + 1);
- testassert(hasName(names, "unload2.out"));
+ testassert(hasName(names, "unload2.bundle"));
free(names);
Class small = objc_getClass("SmallClass");
name = class_getImageName(small);
testassert(name);
- testassert(strstr(name, "unload2.out"));
+ testassert(strstr(name, "unload2.bundle"));
name = class_getImageName(big);
testassert(name);
- testassert(strstr(name, "unload2.out"));
+ testassert(strstr(name, "unload2.bundle"));
id o1 = [small new];
id o2 = [big new];
[o1 free];
[o2 free];
- if (objc_collecting_enabled()) objc_collect(OBJC_EXHAUSTIVE_COLLECTION | OBJC_WAIT_UNTIL_DONE);
+ testcollect();
int err = dlclose(bundle);
testassert(err == 0);
names = objc_copyImageNames(&imageCount);
testassert(names);
testassert(imageCount == imageCount0);
- testassert(! hasName(names, "unload2.out"));
+ testassert(! hasName(names, "unload2.bundle"));
free(names);
// these selectors came from the bundle
int main()
{
// fixme object_dispose() not aggressive enough?
- if (objc_collecting_enabled()) succeed(__FILE__);
+ if (objc_collectingEnabled()) succeed(__FILE__);
- int count = 100;
+#if defined(__arm__)
+ int count = 10;
+#else
+ int count = is_guardmalloc() ? 10 : 100;
+#endif
cycle();
#if __LP64__
leak_check(0);
// 5359412 Make sure dylibs with nothing other than image_info can close
- void *dylib = dlopen("unload3.out", RTLD_LAZY);
+ void *dylib = dlopen("unload3.dylib", RTLD_LAZY);
testassert(dylib);
int err = dlclose(dylib);
testassert(err == 0);
testassert(err == -1); // already closed
// Make sure dylibs with real objc content cannot close
- dylib = dlopen("unload4.out", RTLD_LAZY);
+ dylib = dlopen("unload4.dylib", RTLD_LAZY);
testassert(dylib);
err = dlclose(dylib);
testassert(err == 0);
--- /dev/null
+// unload3: contains imageinfo but no other objc metadata
+// libobjc must not keep it open
+// DO NOT USE __OBJC2__; this is a C file.
+
+#include <TargetConditionals.h>
+
+#if TARGET_OS_WIN32 || (TARGET_OS_MAC && TARGET_CPU_X86 && !TARGET_IPHONE_SIMULATOR)
+// old ABI
+int fake[2] __attribute__((section("__OBJC,__image_info"))) = {0, 0};
+#else
+// new ABI
+int fake[2] __attribute__((section("__DATA,__objc_imageinfo"))) = {0, 0};
+#endif
+
+// silence "no debug symbols in executable" warning
+void fn(void) { }
+++ /dev/null
-// unload3: contains imageinfo but no other objc metadata
-// libobjc must not keep it open
-
-#if __OBJC2__
-int fake __attribute__((section("__DATA,__objc_imageinfo"))) = 0;
-#else
-int fake __attribute__((section("__OBJC,__image_info"))) = 0;
-#endif
#else
int fake2 __attribute__((section("__OBJC,__foo"))) = 0;
#endif
+
+// getsectiondata() falls over if __TEXT has no contents
+const char *unload4 = "unload4";
+// TEST_CFLAGS -framework Foundation
+
#include "test.h"
#include <objc/objc-exception.h>
#include <Foundation/Foundation.h>
static id exc;
+static void handler(id unused, void *ctx) __attribute__((used));
static void handler(id unused __unused, void *ctx __unused)
{
testassert(state == 3); state++;
+(BOOL) resolveClassMethod:(SEL)__unused name
{
testassert(state == 1); state++;
+#if !TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
objc_addExceptionHandler(&handler, 0);
- testassert(state == 2); state++;
+ testassert(state == 2);
+#else
+ state++; // handler would have done this
+#endif
+ state++;
exc = [Foo new];
@throw exc;
}
int main()
{
+#if TARGET_IPHONE_SIMULATOR
+ testwarn("<rdar://problem/7965763> Simulator: cannot throw exceptions across objc_msgSend");
+ succeed(__FILE__);
+#else
int i;
// unwind exception and alt handler through objc_msgSend()
[pool drain];
succeed(__FILE__);
+#endif
}
#endif
--- /dev/null
+#!/usr/bin/perl
+
+# verify-exports.pl
+# Check exports in a library vs. declarations in header files.
+# usage: verify-exports.pl /path/to/dylib /glob/path/to/headers decl-prefix [-arch <arch>] [/path/to/project~dst]
+# example: verify-exports.pl /usr/lib/libobjc.A.dylib '/usr/{local/,}include/objc/*' OBJC_EXPORT -arch x86_64 /tmp/objc-test.roots/objc-test~dst
+
+# requirements:
+# - every export must have an @interface or specially-marked declaration
+# - every @interface or specially-marked declaration must have an availability macro
+# - no C++ exports allowed
+
+use strict;
+use File::Basename;
+use File::Glob ':glob';
+
+my $bad = 0;
+
+$0 = basename($0, ".pl");
+my $usage = "/path/to/dylib /glob/path/to/headers decl-prefix [-arch <arch>] [-sdk sdkname] [/path/to/project~dst]";
+
+my $lib_arg = shift || die "$usage";
+die "$usage" unless ($lib_arg =~ /^\//);
+my $headers_arg = shift || die "$usage";
+my $export_arg = shift || die "$usage";
+
+my $arch = "x86_64";
+if ($ARGV[0] eq "-arch") {
+ shift;
+ $arch = shift || die "$0: -arch requires an architecture";
+}
+my $sdk = "system";
+if ($ARGV[0] eq "-sdk") {
+ shift;
+ $sdk = shift || die "$0: -sdk requires an SDK name";
+}
+
+my $root = shift || "";
+
+
+# Collect symbols from dylib.
+my $lib_path = "$root$lib_arg";
+die "$0: file not found: $lib_path\n" unless -e $lib_path;
+
+my %symbols;
+my @symbollines = `nm -arch $arch '$lib_path'`;
+die "$0: nm failed: (arch $arch) $lib_path\n" if ($?);
+for my $line (@symbollines) {
+ chomp $line;
+ (my $type, my $name) = ($line =~ /^[[:xdigit:]]*\s+(.) (.*)$/);
+ if ($type =~ /^[A-TV-Z]$/) {
+ $symbols{$name} = 1;
+ } else {
+ # undefined (U) or non-external - ignore
+ }
+}
+
+# Complain about C++ exports
+for my $symbol (keys %symbols) {
+ if ($symbol =~ /^__Z/) {
+ print "BAD: C++ export '$symbol'\n"; $bad++;
+ }
+}
+
+
+# Translate arch to unifdef(1) parameters: archnames, __LP64__, __OBJC2__
+my @archnames = ("x86_64", "i386", "arm", "armv6", "armv7");
+my %archOBJC1 = (i386 => 1);
+my %archLP64 = (x86_64 => 1);
+my @archparams;
+
+my $OBJC1 = ($archOBJC1{$arch} && $sdk !~ /^iphonesimulator/);
+
+if ($OBJC1) {
+ push @archparams, "-U__OBJC2__";
+} else {
+ push @archparams, "-D__OBJC2__=1";
+}
+
+if ($archLP64{$arch}) { push @archparams, "-D__LP64__=1"; }
+else { push @archparams, "-U__LP64__"; }
+
+for my $archname (@archnames) {
+ if ($archname eq $arch) {
+ push @archparams, "-D__${archname}__=1";
+ push @archparams, "-D__$archname=1";
+ } else {
+ push @archparams, "-U__${archname}__";
+ push @archparams, "-U__$archname";
+ }
+}
+
+# TargetConditionals.h
+# fixme iphone and simulator
+push @archparams, "-DTARGET_OS_WIN32=0";
+push @archparams, "-DTARGET_OS_EMBEDDED=0";
+push @archparams, "-DTARGET_OS_IPHONE=0";
+push @archparams, "-DTARGET_OS_MAC=1";
+
+# Gather declarations from header files
+# A C declaration starts with $export_arg and ends with ';'
+# A class declaration is @interface plus the line before it.
+my $unifdef_cmd = "/usr/bin/unifdef " . join(" ", @archparams);
+my @cdecls;
+my @classdecls;
+for my $header_path(bsd_glob("$root$headers_arg",GLOB_BRACE)) {
+ my $header;
+ # feed through unifdef(1) first to strip decls from other archs
+ # fixme strip other SDKs as well
+ open($header, "$unifdef_cmd < '$header_path' |");
+ my $header_contents = join("", <$header>);
+
+ # C decls
+ push @cdecls, ($header_contents =~ /^\s*$export_arg\s+([^;]*)/msg);
+
+ # ObjC classes, but not categories.
+ # fixme ivars
+ push @classdecls, ($header_contents =~ /^([^\n]*\n\s*\@interface\s+[^(\n]+\n)/mg);
+}
+
+# Find name and availability of C declarations
+my %declarations;
+for my $cdecl (@cdecls) {
+ $cdecl =~ s/\n/ /mg; # strip newlines
+
+ # Pull availability macro off the end:
+ # __OSX_AVAILABLE_*(*)
+ # AVAILABLE_MAC_OS_X_VERSION_*
+ # OBJC2_UNAVAILABLE
+ # OBJC_HASH_AVAILABILITY
+ # OBJC_MAP_AVAILABILITY
+ # UNAVAILABLE_ATTRIBUTE
+ # (DEPRECATED_ATTRIBUTE is not good enough. Be specific.)
+ my $avail = undef;
+ my $cdecl2;
+ ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(__OSX_AVAILABLE_\w+\([a-zA-Z0-9_, ]+\))\s*$/) if (!defined $avail);
+ ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(AVAILABLE_MAC_OS_X_VERSION_\w+)\s*$/) if (!defined $avail);
+ ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(OBJC2_UNAVAILABLE)\s*$/) if (!defined $avail);
+ ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(OBJC_HASH_AVAILABILITY)\s*$/) if (!defined $avail);
+ ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(OBJC_MAP_AVAILABILITY)\s*$/) if (!defined $avail);
+ ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(UNAVAILABLE_ATTRIBUTE)\s*$/) if (!defined $avail);
+ # ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(DEPRECATED_\w+)\s*$/) if (!defined $avail);
+ $cdecl2 = $cdecl if (!defined $cdecl2);
+
+ # Extract declaration name (assumes availability macro is already gone):
+ # `(*xxx)` (function pointer)
+ # `xxx(` (function)
+ # `xxx`$` or `xxx[nnn]$` (variable or array variable)
+ my $name = undef;
+ ($name) = ($cdecl2 =~ /^[^(]*\(\s*\*\s*(\w+)\s*\)/) if (!defined $name);
+ ($name) = ($cdecl2 =~ /(\w+)\s*\(/) if (!defined $name);
+ ($name) = ($cdecl2 =~ /(\w+)\s*(?:\[\d*\]\s*)*$/) if (!defined $name);
+
+ if (!defined $name) {
+ print "BAD: unintellible declaration:\n $cdecl\n"; $bad++;
+ } elsif (!defined $avail) {
+ print "BAD: no availability on declaration of '$name':\n $cdecl\n"; $bad++;
+ }
+
+ if ($avail eq "UNAVAILABLE_ATTRIBUTE")
+ {
+ $declarations{$name} = "unavailable";
+ } elsif ($avail eq "OBJC2_UNAVAILABLE" && ! $OBJC1) {
+ # fixme OBJC2_UNAVAILABLE may or may not have an exported symbol
+ # $declarations{$name} = "unavailable";
+ } else {
+ $declarations{"_$name"} = "available";
+ }
+}
+
+# Find name and availability of Objective-C classes
+for my $classdecl (@classdecls) {
+ $classdecl =~ s/\n/ /mg; # strip newlines
+
+ # Pull availability macro off the front:
+ # __OSX_AVAILABLE_*(*)
+ # AVAILABLE_MAC_OS_X_VERSION_*
+ # OBJC2_UNAVAILABLE
+ # OBJC_HASH_AVAILABILITY
+ # OBJC_MAP_AVAILABILITY
+ # UNAVAILABLE_ATTRIBUTE
+ # (DEPRECATED_ATTRIBUTE is not good enough. Be specific.)
+ my $avail = undef;
+ my $classdecl2;
+ ($avail, $classdecl2) = ($classdecl =~ /^\s*(__OSX_AVAILABLE_\w+\([a-zA-Z0-9_, ]+\))\s*(.*)$/) if (!defined $avail);
+ ($avail, $classdecl2) = ($classdecl =~ /^\s*(AVAILABLE_MAC_OS_X_VERSION_\w+)\s*(.*)$/) if (!defined $avail);
+ ($avail, $classdecl2) = ($classdecl =~ /^\s*(OBJC2_UNAVAILABLE)\s*(.*)$/) if (!defined $avail);
+ ($avail, $classdecl2) = ($classdecl =~ /^\s*(OBJC_HASH_AVAILABILITY)\s*(.*)$/) if (!defined $avail);
+ ($avail, $classdecl2) = ($classdecl =~ /^\s*(OBJC_MAP_AVAILABILITY)\s*(.*)$/) if (!defined $avail);
+ ($avail, $classdecl2) = ($classdecl =~ /^\s*(UNAVAILABLE_ATTRIBUTE)\s*(.*)$/) if (!defined $avail);
+ # ($avail, $classdecl2) = ($classdecl =~ /^\s*(DEPRECATED_\w+)\s*(.*)$/) if (!defined $avail);
+ $classdecl2 = $classdecl if (!defined $classdecl2);
+
+ # Extract class name.
+ my $name = undef;
+ ($name) = ($classdecl2 =~ /\@interface\s+(\w+)/);
+
+ if (!defined $name) {
+ print "BAD: unintellible declaration:\n $classdecl\n"; $bad++;
+ } elsif (!defined $avail) {
+ print "BAD: no availability on declaration of '$name':\n $classdecl\n"; $bad++;
+ }
+
+ my $availability;
+ if ($avail eq "UNAVAILABLE_ATTRIBUTE") {
+ $availability = "unavailable";
+ } elsif ($avail eq "OBJC2_UNAVAILABLE" && ! $OBJC1) {
+ # fixme OBJC2_UNAVAILABLE may or may not have an exported symbol
+ # $declarations{$name} = "unavailable";
+ $availability = undef;
+ } else {
+ $availability = "available";
+ }
+
+ if (! $OBJC1) {
+ $declarations{"_OBJC_CLASS_\$_$name"} = $availability;
+ $declarations{"_OBJC_METACLASS_\$_$name"} = $availability;
+ # fixme ivars
+ $declarations{"_OBJC_IVAR_\$_$name.isa"} = $availability if ($name eq "Object");
+ } else {
+ $declarations{".objc_class_name_$name"} = $availability;
+ }
+}
+
+# All exported symbols must have an export declaration
+my @missing_symbols;
+for my $name (keys %symbols) {
+ my $avail = $declarations{$name};
+ if ($avail eq "unavailable" || !defined $avail) {
+ push @missing_symbols, $name;
+ }
+}
+for my $symbol (sort @missing_symbols) {
+ print "BAD: symbol $symbol has no export declaration\n"; $bad++;
+}
+
+
+# All export declarations must have an exported symbol
+my @missing_decls;
+for my $name (keys %declarations) {
+ my $avail = $declarations{$name};
+ my $hasSymbol = exists $symbols{$name};
+ if ($avail ne "unavailable" && !$hasSymbol) {
+ push @missing_decls, $name;
+ }
+}
+for my $decl (sort @missing_decls) {
+ print "BAD: declaration $decl has no exported symbol\n"; $bad++;
+}
+
+print "OK: verify-exports\n" unless $bad;
+exit ($bad ? 1 : 0);
+/*
+ To test -weak-l or -weak-framework:
+ * -DWEAK_IMPORT=
+ * -DWEAK_FRAMEWORK=1
+ * -UEMPTY when building the weak-not-missing library
+ * -DEMPTY= when building the weak-missing library
+
+ To test attribute((weak_import)):
+ * -DWEAK_IMPORT=__attribute__((weak_import))
+ * -UWEAK_FRAMEWORK
+ * -UEMPTY when building the weak-not-missing library
+ * -DEMPTY= when building the weak-missing library
+
+*/
+
#include "test.h"
#include <objc/runtime.h>
extern int state;
+WEAK_IMPORT
@interface MissingRoot {
id isa;
}
+(Class) class;
+(id) alloc;
-(id) init;
+-(void) dealloc;
+(int) method;
@end
+WEAK_IMPORT
@interface MissingSuper : MissingRoot {
@public
int ivar;
+(Class) class;
+(id) alloc;
-(id) init;
+-(void) dealloc;
+(int) method;
@end
+// See instructions in weak.h
+
#include "test.h"
#include "weak.h"
@end
+#if WEAK_FRAMEWORK
+# define TESTIVAR(cond) testassert(cond)
+#else
+# define TESTIVAR(cond) /* rdar */
+#endif
+
static BOOL classInList(Class *classes, const char *name)
{
Class *cp;
return NO;
}
-int main()
+int main(int argc __unused, char **argv)
{
- // DYLD_IMAGE_SUFFIX=_empty loads the weak-missing version
- BOOL weakMissing = NO;
- if (getenv("DYLD_IMAGE_SUFFIX")) weakMissing = YES;
+ BOOL weakMissing;
+ if (strstr(argv[0], "-not-missing.out")) {
+ weakMissing = NO;
+ } else if (strstr(argv[0], "-missing.out")) {
+ weakMissing = YES;
+ } else {
+ fail("executable name must be weak*-missing.out or weak*-not-missing.out");
+ }
// class and category +load methods
if (weakMissing) testassert(state == 8);
}
// class list
- Class classes[100];
- int count = objc_getClassList(classes, 99);
- classes[count] = NULL;
+ Class *classes = objc_copyClassList(NULL);
testassert(classInList(classes, "NotMissingRoot"));
testassert(classInList(classes, "NotMissingSuper"));
testassert(classInList(classes, "MyNotMissingSuper"));
testassert(classInList(classes, "MyMissingSuper"));
testassert(classInList(classes, "MyMissingSub"));
}
+ free(classes);
// class name list
const char *image = class_getImageName(objc_getClass("NotMissingRoot"));
NotMissingSuper *obj2;
MissingSuper *obj3;
testassert((obj = [[NotMissingRoot alloc] init]));
- free(obj);
+ [obj dealloc];
testassert((obj2 = [[NotMissingSuper alloc] init]));
- testassert(obj2->ivar == 200); free(obj2);
+ TESTIVAR(obj2->ivar == 200);
+ [obj2 dealloc];
testassert((obj2 = [[MyNotMissingSuper alloc] init]));
- testassert(obj2->ivar == 200); free(obj2);
+ TESTIVAR(obj2->ivar == 200);
+ [obj2 dealloc];
testassert((obj2 = [[MyNotMissingSub alloc] init]));
- testassert(obj2->ivar == 200); free(obj2);
+ TESTIVAR(obj2->ivar == 200);
+ [obj2 dealloc];
if (weakMissing) {
testassert(! [[MissingRoot alloc] init]);
testassert(! [[MissingSuper alloc] init]);
testassert(! [[MyMissingSub alloc] init]);
} else {
testassert((obj = [[MissingRoot alloc] init]));
- free(obj);
+ [obj dealloc];
testassert((obj3 = [[MissingSuper alloc] init]));
- testassert(obj3->ivar == 100); free(obj3);
+ TESTIVAR(obj3->ivar == 100);
+ [obj3 dealloc];
testassert((obj3 = [[MyMissingSuper alloc] init]));
- testassert(obj3->ivar == 100); free(obj3);
+ TESTIVAR(obj3->ivar == 100);
+ [obj3 dealloc];
testassert((obj3 = [[MyMissingSub alloc] init]));
- testassert(obj3->ivar == 100); free(obj3);
+ TESTIVAR(obj3->ivar == 100);
+ [obj3 dealloc];
}
- if (weakMissing) succeed("weak-missing");
- else succeed("weak-not-missing");
+ *strrchr(argv[0], '.') = 0;
+ succeed(basename(argv[0]));
return 0;
}
+
+// See instructions in weak.h
+
#include "test.h"
#include "weak.h"
+(Class) class { return self; }
+(id) alloc { return class_createInstance(self, 0); }
-(id) init { return self; }
+-(void) dealloc { object_dispose(self); }
+(int) method { return 10; }
+(void) load { state++; }
@end
+(Class) class { return self; }
+(id) alloc { return class_createInstance(self, 0); }
-(id) init { return self; }
+-(void) dealloc { object_dispose(self); }
+(int) method { return 20; }
+(void) load { state++; }
@end
+// TEST_CONFIG
+
#include "test.h"
#include <stdint.h>
#include <string.h>
--- /dev/null
+/*
+TEST_BUILD
+ $C{COMPILE} $DIR/weak2.m -DWEAK_FRAMEWORK=1 -DWEAK_IMPORT= -UEMPTY -dynamiclib -o libweakframework.dylib
+
+ $C{COMPILE} $DIR/weakframework-missing.m -L. -weak-lweakframework -o weakframework-missing.out
+
+ $C{COMPILE} $DIR/weak2.m -DWEAK_FRAMEWORK=1 -DWEAK_IMPORT= -DEMPTY= -dynamiclib -o libweakframework.dylib
+
+END
+*/
+
+#define WEAK_FRAMEWORK 1
+#define WEAK_IMPORT
+#include "weak.m"
--- /dev/null
+/*
+TEST_BUILD
+ $C{COMPILE} $DIR/weak2.m -DWEAK_FRAMEWORK=1 -DWEAK_IMPORT= -UEMPTY -dynamiclib -o libweakframework.dylib
+
+ $C{COMPILE} $DIR/weakframework-not-missing.m -L. -weak-lweakframework -o weakframework-not-missing.out
+END
+*/
+
+#define WEAK_FRAMEWORK 1
+#define WEAK_IMPORT
+#include "weak.m"
--- /dev/null
+/*
+TEST_BUILD
+ $C{COMPILE} $DIR/weak2.m -UWEAK_FRAMEWORK -DWEAK_IMPORT=__attribute__\\(\\(weak_import\\)\\) -UEMPTY -dynamiclib -o libweakimport.dylib
+
+ $C{COMPILE} $DIR/weakimport-missing.m -L. -weak-lweakimport -o weakimport-missing.out
+
+ $C{COMPILE} $DIR/weak2.m -UWEAK_FRAMEWORK -DWEAK_IMPORT=__attribute__\\(\\(weak_import\\)\\) -DEMPTY= -dynamiclib -o libweakimport.dylib
+END
+*/
+
+// #define WEAK_FRAMEWORK
+#define WEAK_IMPORT __attribute__((weak_import))
+#include "weak.m"
--- /dev/null
+/*
+TEST_BUILD
+ $C{COMPILE} $DIR/weak2.m -UWEAK_FRAMEWORK -DWEAK_IMPORT=__attribute__\\(\\(weak_import\\)\\) -UEMPTY -dynamiclib -o libweakimport.dylib
+
+ $C{COMPILE} $DIR/weakimport-not-missing.m -L. -weak-lweakimport -o weakimport-not-missing.out
+END
+*/
+
+// #define WEAK_FRAMEWORK
+#define WEAK_IMPORT __attribute__((weak_import))
+#include "weak.m"
--- /dev/null
+// TEST_CFLAGS -framework Foundation
+
+#include <Foundation/Foundation.h>
+#include <objc/runtime.h>
+#include <objc/objc-internal.h>
+
+#include "test.h"
+
+int main()
+{
+ // rdar://8350188 External references (handles)
+
+ id object = [NSObject new];
+ testassert(object);
+
+ // STRONG
+ objc_xref_t xref = _object_addExternalReference(object, OBJC_XREF_STRONG);
+ testassert(xref);
+ testassert(_object_readExternalReference(xref) == object);
+ _object_removeExternalReference(xref);
+ // TODO: expect a crash if a stale xref is used.
+
+ // WEAK
+ xref = _object_addExternalReference(object, OBJC_XREF_WEAK);
+ testassert(xref);
+ testassert(_object_readExternalReference(xref) == object);
+ _object_removeExternalReference(xref);
+
+ [object release];
+
+ succeed(__FILE__);
+}
+// TEST_CONFIG
+
#include "test.h"
#include <mach/mach.h>
#include <malloc/malloc.h>
int main()
{
+ if (is_guardmalloc()) {
+ // guard malloc confuses this test
+ succeed(__FILE__);
+ }
+
kern_return_t kr;
vm_address_t *zones;
unsigned int count, i;
-__ZSt11lower_boundIPKmmET_S2_S2_RKT0_
+.objc_class_name___IncompleteProtocol
+__Znam
+__ZnamRKSt9nothrow_t
+__Znwm
+__ZnwmRKSt9nothrow_t
+__ZdaPv
+__ZdaPvRKSt9nothrow_t
+__ZdlPv
+__ZdlPvRKSt9nothrow_t
--- /dev/null
+:: version.bat
+:: Writes version numbers from B&I into version.h for use by version.rc.
+
+@ECHO OFF
+
+:: Set default values for environment variables if not set by B&I
+IF "%OBJROOT%"=="" SET OBJROOT=.
+IF "%RC_PROJECTSOURCEVERSION%"=="" SET RC_PROJECTSOURCEVERSION=0.0
+IF "%RC_PROJECTBUILDVERSION%"=="" SET RC_PROJECTBUILDVERSION=0
+
+:: Get version numbers from environment variables
+SET major=1
+SET patch=0
+FOR /F "tokens=1* eol= delims=." %%i IN ("%RC_PROJECTSOURCEVERSION%") DO (
+ SET minor=%%i
+ IF NOT "%%j"=="" SET patch=%%j
+)
+SET build=%RC_PROJECTBUILDVERSION%
+
+ECHO version %major% . %minor% . %patch% . %build%
+
+:: Write version.h
+ECHO // This file is automatically generated by version.bat. > "%OBJROOT%\version.h"
+ECHO // DO NOT EDIT >> "%OBJROOT%\version.h"
+ECHO #define major %major% >> "%OBJROOT%\version.h"
+ECHO #define minor %minor% >> "%OBJROOT%\version.h"
+ECHO #define patch %patch% >> "%OBJROOT%\version.h"
+ECHO #define build %build% >> "%OBJROOT%\version.h"
+ECHO #define string "%major%,%minor%,%patch%,%build%" >> "%OBJROOT%\version.h"
--- /dev/null
+#include "Winver.h"
+
+// built by version.bat; sets variables major, minor, patch, build, string
+#include "version.h"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION major,minor,patch,build
+ PRODUCTVERSION major,minor,patch,build
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Apple Inc."
+ VALUE "FileDescription", "Objective-C Runtime Library"
+ VALUE "FileVersion", string
+ VALUE "ProductVersion", string
+ VALUE "ProductName", "objc4"
+ VALUE "InternalName", "objc4"
+ VALUE "LegalCopyright", "Copyright (C) 2007-2009, Apple Inc."
+ VALUE "OriginalFilename", "objc.dll"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+