]> git.saurik.com Git - apple/objc4.git/commitdiff
objc4-493.9.tar.gz mac-os-x-107 mac-os-x-1071 mac-os-x-1072 v493.9
authorApple <opensource@apple.com>
Wed, 13 Jul 2011 00:08:44 +0000 (00:08 +0000)
committerApple <opensource@apple.com>
Wed, 13 Jul 2011 00:08:44 +0000 (00:08 +0000)
231 files changed:
libobjc.order
markgc.c
objc.suo
objc.vcproj
objc.xcodeproj/project.pbxproj
prebuild.bat [new file with mode: 0755]
runtests.sh
runtime/Accessors.subproj/objc-accessors.h
runtime/Accessors.subproj/objc-accessors.m
runtime/Auto.subproj/objc-auto-i386.s
runtime/Auto.subproj/objc-auto-ppc.s [deleted file]
runtime/Auto.subproj/objc-auto-ppc64.s [deleted file]
runtime/Auto.subproj/objc-auto-x86_64.s
runtime/Messengers.subproj/objc-msg-arm.s
runtime/Messengers.subproj/objc-msg-i386.s
runtime/Messengers.subproj/objc-msg-ppc.s [deleted file]
runtime/Messengers.subproj/objc-msg-simulator-i386.s [new file with mode: 0644]
runtime/Messengers.subproj/objc-msg-win32.m
runtime/Messengers.subproj/objc-msg-x86_64.s
runtime/Object.h
runtime/Object.m
runtime/OldClasses.subproj/List.h
runtime/OldClasses.subproj/List.m
runtime/Protocol.h
runtime/Protocol.m
runtime/a1a2-blocktramps-arm.s [new file with mode: 0644]
runtime/a1a2-blocktramps-i386.s [new file with mode: 0755]
runtime/a1a2-blocktramps-x86_64.s [new file with mode: 0755]
runtime/a2a3-blocktramps-arm.s [new file with mode: 0644]
runtime/a2a3-blocktramps-i386.s [new file with mode: 0755]
runtime/a2a3-blocktramps-x86_64.s [new file with mode: 0755]
runtime/error.h [deleted file]
runtime/hashtable2.h
runtime/hashtable2.m
runtime/llvm-DenseMap.h [new file with mode: 0644]
runtime/llvm-type_traits.h [new file with mode: 0644]
runtime/maptable.h
runtime/maptable.m
runtime/message.h
runtime/objc-abi.h [new file with mode: 0644]
runtime/objc-api.h
runtime/objc-arr.mm [new file with mode: 0644]
runtime/objc-auto-dump.m
runtime/objc-auto.h
runtime/objc-auto.m
runtime/objc-block-trampolines.m [new file with mode: 0644]
runtime/objc-cache.m
runtime/objc-class-old.m
runtime/objc-class.m
runtime/objc-config.h
runtime/objc-errors.m
runtime/objc-exception.h
runtime/objc-exception.m
runtime/objc-externalref.m [new file with mode: 0644]
runtime/objc-file-old.h [new file with mode: 0644]
runtime/objc-file-old.m [new file with mode: 0644]
runtime/objc-file.h [new file with mode: 0644]
runtime/objc-file.m [deleted file]
runtime/objc-file.mm [new file with mode: 0644]
runtime/objc-gdb.h
runtime/objc-initialize.m
runtime/objc-internal.h
runtime/objc-layout.m
runtime/objc-load.m
runtime/objc-loadmethod.h
runtime/objc-loadmethod.m
runtime/objc-lockdebug.m
runtime/objc-os.h
runtime/objc-os.m
runtime/objc-private.h
runtime/objc-references.mm
runtime/objc-rtp.h [deleted file]
runtime/objc-rtp.m
runtime/objc-runtime-new.h
runtime/objc-runtime-new.m [deleted file]
runtime/objc-runtime-new.mm [new file with mode: 0644]
runtime/objc-runtime-old.h [new file with mode: 0644]
runtime/objc-runtime-old.m
runtime/objc-runtime.m
runtime/objc-sel-set.m
runtime/objc-sel-table.s
runtime/objc-sel.mm
runtime/objc-selopt.h
runtime/objc-sync.h
runtime/objc-sync.m
runtime/objc-typeencoding.m
runtime/objc-weak.h [new file with mode: 0644]
runtime/objc-weak.mm [new file with mode: 0644]
runtime/objc.h
runtime/objcrt.c
runtime/runtime.h
test/ARRBase.h [new file with mode: 0644]
test/ARRBase.m [new file with mode: 0644]
test/ARRLayouts.m [new file with mode: 0644]
test/ARRMRR.h [new file with mode: 0644]
test/ARRMRR.m [new file with mode: 0644]
test/MRRARR.h [new file with mode: 0644]
test/MRRARR.m [new file with mode: 0644]
test/MRRBase.h [new file with mode: 0644]
test/MRRBase.m [new file with mode: 0644]
test/Makefile
test/README [deleted file]
test/accessors.m
test/addMethod.m
test/addProtocol.m [new file with mode: 0644]
test/arr-weak.m [new file with mode: 0644]
test/association-cf.m
test/association.m [new file with mode: 0644]
test/badAltHandler.m [new file with mode: 0644]
test/blocksAsImps.m [new file with mode: 0644]
test/cacheflush.m
test/category.m
test/cdtors.mm [new file with mode: 0644]
test/classgetclass.m
test/classname.m
test/classpair.m
test/classversion.m
test/concurrentcat.m
test/concurrentcat_category.m
test/copyIvarList.m
test/copyMethodList.m
test/copyPropertyList.m
test/createInstance.m
test/debuggerMode.m
test/definitions.m
test/duplicateClass.m
test/errcheck.pl [deleted file]
test/evil-category-0.m [new file with mode: 0644]
test/evil-category-00.m [new file with mode: 0644]
test/evil-category-000.m [new file with mode: 0644]
test/evil-category-1.m [new file with mode: 0644]
test/evil-category-2.m [new file with mode: 0644]
test/evil-category-3.m [new file with mode: 0644]
test/evil-category-4.m [new file with mode: 0644]
test/evil-category-def.m [new file with mode: 0644]
test/evil-class-0.m [new file with mode: 0644]
test/evil-class-00.m [new file with mode: 0644]
test/evil-class-000.m [new file with mode: 0644]
test/evil-class-1.m [new file with mode: 0644]
test/evil-class-2.m [new file with mode: 0644]
test/evil-class-3.m [new file with mode: 0644]
test/evil-class-4.m [new file with mode: 0644]
test/evil-class-5.m [new file with mode: 0644]
test/evil-class-def.m [new file with mode: 0644]
test/evil-main.m [new file with mode: 0644]
test/exc.m
test/exchangeImp.m
test/fail.m [deleted file]
test/foreach.m
test/forward.m
test/future.m [new file with mode: 0644]
test/future1.m [deleted file]
test/gc-main.m [new file with mode: 0644]
test/gc.m
test/gcenforcer-nogc-1.m [new file with mode: 0644]
test/gcenforcer-nogc-2.m [new file with mode: 0644]
test/gcenforcer-noobjc.m [new file with mode: 0644]
test/gcenforcer-requiresgc-1.m [new file with mode: 0644]
test/gcenforcer-requiresgc-2.m [new file with mode: 0644]
test/gcenforcer-supportsgc.m [new file with mode: 0644]
test/gcenforcer.m
test/gcenforcer_nogc.gc.expected-stderr [deleted file]
test/gcenforcer_nogc.nogc.expected-stderr [deleted file]
test/gcenforcer_requiresgc.gc.expected-stderr [deleted file]
test/gcenforcer_requiresgc.nogc.expected-stderr [deleted file]
test/gdb-lock.m
test/gdb.m
test/getMethod.m
test/ignoredSelector.m
test/imageorder.m
test/initialize.m
test/instanceSize.m
test/ismeta.m
test/ivar.m
test/ivarSlide.m [new file with mode: 0644]
test/ivarSlide2.m [deleted file]
test/layout.m
test/load-order.m
test/load-parallel.m
test/load-reentrant.m
test/load.m
test/main.m [deleted file]
test/methodArgs.m
test/methodListSize.m [new file with mode: 0644]
test/method_getName.m
test/msgSend.m
test/nilAPIArgs.m
test/nsexc.m [new file with mode: 0644]
test/nsobject.m
test/property.m
test/propertyDesc.m [new file with mode: 0644]
test/protocol.m
test/protocol_copyMethodList.m
test/protocol_copyPropertyList.m
test/protocol_cw.m
test/resolve.expected-stderr [deleted file]
test/resolve.m
test/rr-autorelease.m [new file with mode: 0644]
test/rr-autorelease2.m [new file with mode: 0644]
test/rr-nsautorelease.m [new file with mode: 0644]
test/runtime.expected-stderr [deleted file]
test/runtime.m
test/sel.m
test/setSuper.m
test/super.m
test/synchronized-counter.m
test/synchronized-grid.m
test/synchronized.m
test/taggedPointers.m [new file with mode: 0644]
test/test.h
test/test.pl [new file with mode: 0755]
test/test.xcodeproj/project.pbxproj [deleted file]
test/unload.m
test/unload3.c [new file with mode: 0644]
test/unload3.m [deleted file]
test/unload4.m
test/unwind.m
test/verify-exports.pl [new file with mode: 0755]
test/weak.h
test/weak.m
test/weak2.m
test/weakcopy.m
test/weakframework-missing.m [new file with mode: 0644]
test/weakframework-not-missing.m [new file with mode: 0644]
test/weakimport-missing.m [new file with mode: 0644]
test/weakimport-not-missing.m [new file with mode: 0644]
test/xref.m [new file with mode: 0644]
test/zone.m
unexported_symbols
version.bat [new file with mode: 0755]
version.rc [new file with mode: 0644]

index c7e85ca361d771edab972d0f0d79e0033e4dbf2f..211cfb6b8bac1de854f9ad17ac4667a1ec74b850 100644 (file)
 __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_
index e2d26e86c2d8da1bf3166c62df408c3528f4bbe1..146fd8c6239ade7af06a69ed8dba606f6f341f88 100644 (file)
--- a/markgc.c
+++ b/markgc.c
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -40,6 +48,7 @@ bool verbose;
 bool quiet;
 bool rrOnly;
 bool patch = true;
+bool unpatch = false;
 
 struct gcinfo {
         bool hasObjC;
@@ -60,14 +69,10 @@ int main(int argc, char *argv[]) {
     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);
     }
@@ -84,22 +89,13 @@ int main(int argc, char *argv[]) {
             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]);
@@ -116,30 +112,36 @@ void patchFile(uint32_t value, size_t offset) {
     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);
     }
@@ -194,7 +196,7 @@ void dosect64(void *start, struct section_64 *sect, bool needsFlip, struct gcinf
         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) {
@@ -207,9 +209,8 @@ void doseg32(void *start, struct segment_command *seg, bool needsFlip, struct gc
     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);
@@ -224,9 +225,8 @@ void doseg64(void *start, struct segment_command_64 *seg, bool needsFlip, struct
         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);
@@ -274,11 +274,11 @@ void dodylib(void *start, struct dylib_command *dylibCmd, bool needsFlip) {
     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) {
@@ -309,7 +309,7 @@ struct load_command *doloadcommand(void *start, struct load_command *lc, bool ne
     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) {
@@ -411,16 +411,16 @@ bool openFile(const char *filename) {
         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;
     }
index 83257504cbd7f9983625e58e8abdfa8fb71a5dcf..7f8180c631918658823f234ec88fd2017eac6078 100755 (executable)
Binary files a/objc.suo and b/objc.suo differ
index cda033029f480b5a041f2face80f2782a7a81872..2c8466253f56c1c4424863348670ff87b9ed3c31 100644 (file)
@@ -24,8 +24,8 @@
                        >\r
                        <Tool\r
                                Name="VCPreBuildEventTool"\r
-                               Description="Install"\r
-                               CommandLine="xcopy /Y &quot;$(ProjectDir)runtime\objc.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\objc-api.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\objc-auto.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\objc-exception.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\message.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\runtime.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\hashtable.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\hashtable2.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\maptable.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;"\r
+                               Description="prebuild.bat..."\r
+                               CommandLine="prebuild"\r
                        />\r
                        <Tool\r
                                Name="VCCustomBuildTool"\r
@@ -67,6 +67,7 @@
                                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
@@ -96,7 +97,7 @@
                        <Tool\r
                                Name="VCPostBuildEventTool"\r
                                Description="Install"\r
-                               CommandLine="xcopy /Y &quot;$(TargetDir)$(TargetName).dll&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).pdb&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).exp&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).lib&quot; &quot;%DSTROOT%\AppleInternal\lib\&quot;&#x0D;&#x0A;"\r
+                               CommandLine="xcopy /Y &quot;$(TargetDir)$(TargetName).dll&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).exp&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).lib&quot; &quot;%DSTROOT%\AppleInternal\lib\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).pdb&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).pdbstripped&quot; &quot;$(TargetDir)$(TargetName).pdb&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).pdb&quot; &quot;%DSTROOT%\AppleInternal\public\sym\&quot;&#x0D;&#x0A;"\r
                        />\r
                </Configuration>\r
                <Configuration\r
                        >\r
                        <Tool\r
                                Name="VCPreBuildEventTool"\r
-                               Description="Install Headers"\r
-                               CommandLine="xcopy /Y &quot;$(ProjectDir)runtime\objc.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\objc-api.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\objc-auto.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\objc-exception.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\message.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\runtime.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\hashtable.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\hashtable2.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\maptable.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;"\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 &quot;$(TargetDir)$(TargetName).dll&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).pdb&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).exp&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).lib&quot; &quot;%DSTROOT%\AppleInternal\lib\&quot;&#x0D;&#x0A;"\r
+                               CommandLine="xcopy /Y &quot;$(TargetDir)$(TargetName).dll&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).exp&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).lib&quot; &quot;%DSTROOT%\AppleInternal\lib\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).pdb&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).pdbstripped&quot; &quot;$(TargetDir)$(TargetName).pdb&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).pdb&quot; &quot;%DSTROOT%\AppleInternal\public\sym\&quot;&#x0D;&#x0A;"\r
                        />\r
                </Configuration>\r
                <Configuration\r
                        >\r
                        <Tool\r
                                Name="VCPreBuildEventTool"\r
-                               Description="Install"\r
-                               CommandLine="xcopy /Y &quot;$(ProjectDir)runtime\objc.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\objc-api.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\objc-auto.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\objc-exception.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\message.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\runtime.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\hashtable.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\hashtable2.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(ProjectDir)runtime\maptable.h&quot; &quot;%DSTROOT%\AppleInternal\include\objc\&quot;&#x0D;&#x0A;"\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 &quot;$(TargetDir)$(TargetName).dll&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).pdb&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).exp&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).lib&quot; &quot;%DSTROOT%\AppleInternal\lib\&quot;&#x0D;&#x0A;"\r
+                               CommandLine="xcopy /Y &quot;$(TargetDir)$(TargetName).dll&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).exp&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).lib&quot; &quot;%DSTROOT%\AppleInternal\lib\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).pdb&quot; &quot;%DSTROOT%\AppleInternal\bin\&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).pdbstripped&quot; &quot;$(TargetDir)$(TargetName).pdb&quot;&#x0D;&#x0A;xcopy /Y &quot;$(TargetDir)$(TargetName).pdb&quot; &quot;%DSTROOT%\AppleInternal\public\sym\&quot;&#x0D;&#x0A;"\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
index d7aa9389d608e03f3a1b676107d57cd7207b6d72..6a4c65a2aba69f222d85dbd4f28d7b82fe3b70cc 100644 (file)
        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 */;
diff --git a/prebuild.bat b/prebuild.bat
new file mode 100755 (executable)
index 0000000..70c55b0
--- /dev/null
@@ -0,0 +1,15 @@
+@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
index f10f7661ffe1011c8641b14c1b396f286d52844b..dc0f6f88f719cd698ebe35067e503b3cc96b6dac 100755 (executable)
@@ -9,16 +9,24 @@ ObjcDir="`dirname $0`"
 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
index 5342bd2b09a64ea93e3587d5d3e9d97966871555..059461aa13c7a9f28d1b6744956ea86b1946a235 100644 (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 
@@ -48,4 +58,6 @@ void object_setProperty_bycopy(id object, SEL _cmd, id <NSCopying> value, ptrdif
 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
index d1e5617d8b3ed55f1d32e65f8af75c3ab914dc26..d324dbe3c9de796e18703a6c2118ad3cea399201 100644 (file)
 - (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);
@@ -60,11 +54,11 @@ extern void _spin_unlock(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;
@@ -72,24 +66,35 @@ id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
     // 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);
 
@@ -99,7 +104,7 @@ void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL ato
     if (shouldCopy) {
         newValue = (shouldCopy == OBJC_PROPERTY_MUTABLECOPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]);
     } else {
-        newValue = [newValue retain];
+        newValue = objc_retain(newValue);
     }
 
     if (!atomic) {
@@ -113,7 +118,16 @@ void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL ato
         _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);
 }
 
 
@@ -139,10 +153,12 @@ void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, B
         _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) {
index 4b4584b9e7bd196fb0c1906b3ebcb37af52eac31..1e7345f21b72aef2ad36578d2880ccbace64880a 100644 (file)
@@ -21,7 +21,9 @@
  * @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
@@ -39,8 +41,6 @@
 
 .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:
@@ -64,6 +64,17 @@ _objc_assign_global:
     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.
 
@@ -78,10 +89,7 @@ _objc_assign_strongCast:
     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
diff --git a/runtime/Auto.subproj/objc-auto-ppc.s b/runtime/Auto.subproj/objc-auto-ppc.s
deleted file mode 100644 (file)
index c3f7332..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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
diff --git a/runtime/Auto.subproj/objc-auto-ppc64.s b/runtime/Auto.subproj/objc-auto-ppc64.s
deleted file mode 100644 (file)
index 18bb0ac..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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
index 12f096204364ef69245d5955a758f49b69ba7e4c..ac2d144151b6fcb89e1439a8f04845f8b350c358 100644 (file)
@@ -21,6 +21,8 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#error not currently used
+
 #ifdef __x86_64__
 
 /*
@@ -38,8 +40,6 @@
 .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:
@@ -60,6 +60,16 @@ _objc_assign_global:
     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.
 
@@ -73,10 +83,7 @@ _objc_assign_strongCast:
     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
index a644620e5a68e50e1eafc812703e3cb4eef8a6b6..c6b165dfbd0cfe84209fa9edaa5120302ab7664a 100644 (file)
  * 
  * @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
@@ -60,14 +57,23 @@ L ## var ## __non_lazy_ptr:                             ;\
        .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      ;\
@@ -75,22 +81,27 @@ L ## var ## __non_lazy_ptr:                             ;\
 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
 
@@ -120,25 +131,31 @@ L__objc_notify_images:
 # 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
 
 
@@ -148,11 +165,8 @@ _objc_exitPoints:
 
 /* 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
@@ -160,6 +174,7 @@ _objc_exitPoints:
 
 /* Cache header */
 .set MASK,             0
+.set NEGMASK,         -8
 .set OCCUPIED,         4
 .set BUCKETS,          8     /* variable length array */
 
@@ -176,11 +191,31 @@ _objc_exitPoints:
 
 .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
@@ -197,46 +232,46 @@ _$0:
 
 #####################################################################
 #
-# 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
 
@@ -260,25 +295,21 @@ _$0:
  * 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
@@ -294,23 +325,20 @@ LGetMethodExit:
  * 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
@@ -328,38 +356,35 @@ LGetImpExit:
        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  */
@@ -371,10 +396,39 @@ _objc_msgSend_uncached:
 
 # 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,
@@ -393,37 +447,35 @@ _objc_msgSend_uncached:
        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 */
@@ -436,7 +488,6 @@ _objc_msgSend_stret_uncached:
 # 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
 
@@ -452,46 +503,34 @@ _objc_msgSend_stret_uncached:
  * }
  ********************************************************************/
 
-       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   */
@@ -503,7 +542,56 @@ _objc_msgSendSuper_uncached:
 
 # 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
@@ -529,46 +617,35 @@ _objc_msgSendSuper_uncached:
  *             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 */
@@ -580,8 +657,59 @@ _objc_msgSendSuper_stret_uncached:
 
 # 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
@@ -617,9 +745,6 @@ _objc_msgSendSuper_stret_uncached:
  * stack args...
  * 
  * typedef struct objc_sendv_margs {
- * #ifdef VFP_ARGS
- *     double          vfp[8];
- * #endif
  *     int             a[4];
  *     int             stackArgs[...];
  * };
@@ -640,8 +765,7 @@ __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
@@ -660,13 +784,11 @@ __objc_forward_stret_handler:
        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
@@ -687,11 +809,7 @@ __objc_forward_stret_handler:
 
 # 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
@@ -699,18 +817,16 @@ __objc_forward_stret_handler:
 
        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
@@ -731,11 +847,7 @@ __objc_forward_stret_handler:
 
 # 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
@@ -748,152 +860,21 @@ LMsgForwardError:
        .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
@@ -911,4 +892,12 @@ LMsgSendvStretCall:
        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
index 3840542fb0d87428c9e337257913169b3aa44fc7..929bcba13e226ead67758bc541dcec50cfd14276 100644 (file)
@@ -21,7 +21,8 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
-#ifdef __i386__
+#include <TargetConditionals.h>
+#if defined(__i386__)  &&  !TARGET_IPHONE_SIMULATOR
 
 /********************************************************************
  ********************************************************************
@@ -31,9 +32,8 @@
  ********************************************************************
  ********************************************************************/
 
-#undef  OBJC_ASM
-#define OBJC_ASM
-#include "objc-rtp.h"
+// for kIgnore
+#include "objc-config.h"
 
 
 /********************************************************************
@@ -76,58 +76,6 @@ _objc_exitPoints:
        .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.
@@ -273,6 +221,13 @@ EXTERNAL_SYMBOL    = 1
 $0:
 .endmacro
 
+.macro STATIC_ENTRY
+       .text
+       .private_extern $0
+       .align  4, 0x90
+$0:
+.endmacro
+
 //////////////////////////////////////////////////////////////////////
 //
 // END_ENTRY   functionName
@@ -302,7 +257,7 @@ $0:
        movl    %esp,%ebp
        subl    $$8,%esp
        // Current stack contents: ret, ebp, pad, pad
-       CALL_EXTERN(mcount)
+       call    mcount
        movl    %ebp,%esp
        popl    %ebp
 #endif
@@ -554,7 +509,7 @@ LMsgSendHitInstrumentDone_$0_$1_$2:
        // push args (class, selector)
        pushl   %ecx
        pushl   %eax
-       CALL_EXTERN(__class_lookupMethodAndLoadCache)
+       call    __class_lookupMethodAndLoadCache
        addl    $$12, %esp              // pop parameters and alignment
 .endmacro
 
@@ -574,8 +529,7 @@ LMsgSendHitInstrumentDone_$0_$1_$2:
  * 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
@@ -608,8 +562,7 @@ LGetMethodExit:
  * 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
@@ -637,14 +590,6 @@ LGetImpExit:
  *
  ********************************************************************/
 
-       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
 
@@ -707,19 +652,6 @@ LMsgSendExit:
  * };
  ********************************************************************/
 
-       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
 
@@ -810,14 +742,6 @@ LMsgSendvArgsOK:
  *
  ********************************************************************/
 
-       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
 
@@ -929,14 +853,6 @@ LMsgSendvFpretArgsOK:
  *             (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
 
@@ -1002,19 +918,6 @@ LMsgSendStretExit:
  *
  ********************************************************************/
 
-       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
 
@@ -1124,8 +1027,7 @@ __objc_forward_handler:   .long 0
        .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
@@ -1187,7 +1089,7 @@ LMsgForwardError:
        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
 
@@ -1240,7 +1142,7 @@ LMsgForwardStretError:
        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
 
@@ -1266,4 +1168,12 @@ LMsgForwardStretError:
        
        END_ENTRY _method_invoke_stret
 
+       
+       STATIC_ENTRY __objc_ignored_method
+       
+       movl    self(%esp), %eax
+       ret
+       
+       END_ENTRY __objc_ignored_method
+       
 #endif
diff --git a/runtime/Messengers.subproj/objc-msg-ppc.s b/runtime/Messengers.subproj/objc-msg-ppc.s
deleted file mode 100644 (file)
index 17da0a0..0000000
+++ /dev/null
@@ -1,1493 +0,0 @@
-/*
- * 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
diff --git a/runtime/Messengers.subproj/objc-msg-simulator-i386.s b/runtime/Messengers.subproj/objc-msg-simulator-i386.s
new file mode 100644 (file)
index 0000000..7e65212
--- /dev/null
@@ -0,0 +1,902 @@
+/*
+ * 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
index 7fcc38eacb0dff8d0d5ea3b2efde3dd432929422..1d3724caa905086fafcf139ec8d1d010a15962ce 100644 (file)
@@ -5,16 +5,16 @@
 #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
@@ -39,8 +39,11 @@ void *_objc_forward_stret_handler = NULL;
 __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
@@ -64,14 +67,16 @@ MISS:
         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
@@ -79,8 +84,11 @@ HIT:
 __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
@@ -104,14 +112,15 @@ MISS:
         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
@@ -119,11 +128,14 @@ HIT:
 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
@@ -157,29 +169,30 @@ HIT:
         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
@@ -188,11 +201,14 @@ NIL:
 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
@@ -226,28 +242,29 @@ HIT:
         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
@@ -256,12 +273,15 @@ NIL:
 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
@@ -288,10 +308,11 @@ HIT:
         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
@@ -299,19 +320,18 @@ MISS:
 \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
@@ -320,11 +340,14 @@ MISS:
 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
@@ -358,29 +381,30 @@ HIT:
         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
@@ -389,12 +413,15 @@ NIL:
 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
@@ -421,10 +448,11 @@ HIT:
         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
@@ -432,19 +460,18 @@ MISS:
 \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
@@ -484,10 +511,15 @@ STRET:
 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
@@ -496,10 +528,21 @@ OBJC_EXPORT __declspec(naked) void method_invoke(void)
 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
index da3c2f293d8cb2ba4cf38890a20113b9d94aa459..823b86be21b57f75891c39c4d6d82a86d642c057 100644 (file)
 
 #define __OBJC2__ 1
        
-#undef  OBJC_ASM
-#define OBJC_ASM
-#include "objc-rtp.h"
-
-
 /********************************************************************
 * Data used by the ObjC runtime.
 *
@@ -47,7 +42,7 @@
 // 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
 
@@ -55,7 +50,7 @@ __objc_nilReceiver:
 // 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
@@ -65,36 +60,91 @@ _objc_entryPoints:
        .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.
@@ -102,25 +152,21 @@ _objc_exitPoints:
  ********************************************************************/
 
 // 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
@@ -146,6 +192,13 @@ _objc_exitPoints:
 .macro ENTRY
        .text
        .globl  $0
+       .align  6, 0x90
+$0:
+.endmacro
+
+.macro STATIC_ENTRY
+       .text
+       .private_extern $0
        .align  2, 0x90
 $0:
 .endmacro
@@ -161,6 +214,7 @@ $0:
 //////////////////////////////////////////////////////////////////////
 
 .macro END_ENTRY
+LExit$0:       
 .endmacro
 
 
@@ -307,50 +361,37 @@ LF1$0:
 
 /////////////////////////////////////////////////////////////////////
 //
-// 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
 
 /////////////////////////////////////////////////////////////////////
@@ -359,93 +400,89 @@ LF1$0:
 //
 // 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
 
@@ -454,23 +491,28 @@ LMsgSendProbeCache_$1:
 //
 // 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
@@ -479,6 +521,126 @@ LMsgSendProbeCache_$1:
 
 .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)
@@ -498,25 +660,26 @@ LMsgSendProbeCache_$1:
  * _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:
@@ -534,20 +697,21 @@ 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:
@@ -561,51 +725,32 @@ 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
 
@@ -613,21 +758,20 @@ LMsgSendExit:
        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
@@ -640,19 +784,13 @@ LMsgSendExit:
        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
@@ -673,40 +811,34 @@ LMsgSendFixupNilSelf:
        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
@@ -729,25 +861,35 @@ LMsgSendSuperExit:
        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
 
 
@@ -761,47 +903,23 @@ LMsgSendSuperExit:
        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
        
@@ -809,21 +927,20 @@ LMsgSendFpretExit:
        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
@@ -836,21 +953,13 @@ LMsgSendFpretExit:
        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
@@ -868,48 +977,23 @@ LMsgSendFpretFixupNilSelf:
        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
 
@@ -917,21 +1001,20 @@ LMsgSendFp2retExit:
        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
@@ -944,22 +1027,13 @@ LMsgSendFp2retExit:
        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
@@ -983,34 +1057,23 @@ LMsgSendFp2retFixupNilSelf:
        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
 
@@ -1018,21 +1081,20 @@ LMsgSendStretExit:
        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
@@ -1045,14 +1107,13 @@ LMsgSendStretExit:
        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
@@ -1083,31 +1144,33 @@ LMsgSendStretFixupNilSelf:
        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
@@ -1128,25 +1191,34 @@ LMsgSendSuperStretExit:
        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
 
 
@@ -1178,8 +1250,7 @@ __objc_forward_handler:   .quad 0
 __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
@@ -1226,7 +1297,7 @@ __objc_forward_stret_handler:     .quad 0
        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
 
@@ -1251,7 +1322,7 @@ __objc_forward_stret_handler:     .quad 0
        // 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
@@ -1300,7 +1371,7 @@ LMsgForwardError:
        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
 
@@ -1325,7 +1396,7 @@ LMsgForwardError:
        // 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
@@ -1340,6 +1411,36 @@ LMsgForwardStretError:
        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
@@ -1357,6 +1458,14 @@ LMsgForwardStretError:
        
        END_ENTRY _method_invoke_stret
 
+
+       STATIC_ENTRY __objc_ignored_method
+
+       movq    %a1, %rax
+       ret
+       
+       END_ENTRY __objc_ignored_method
+       
        
 /********************************************************************
  *
@@ -1365,33 +1474,63 @@ LMsgForwardStretError:
  * 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
 
@@ -1408,6 +1547,17 @@ _vtable_prototype_size:
 _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
 
 /********************************************************************
  *
@@ -1417,10 +1567,7 @@ _vtable_prototype_index_offset:
  *
  ********************************************************************/ 
 
-       .text
-       .align 2
-       .private_extern _vtable_ignored
-_vtable_ignored:
+       STATIC_ENTRY _vtable_ignored
        movq    %a1, %rax
        ret
 
index 3a2580b3c7dcd6224020267dea18e2f56ebddc59..5c3707825e9679b6113c800ddaa8bc13f5881df0 100644 (file)
@@ -37,6 +37,7 @@
 
 #if __OBJC2__
 
+__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA)
 @interface Object
 {
        Class isa;      /* A pointer to the instance's class structure */
@@ -49,6 +50,7 @@
 
 #else
 
+__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA)
 @interface Object
 {
        Class isa;      /* A pointer to the instance's class structure */
index 1990ccf5d34e7fb1fa7c1e5cb0a3fd59f5cc8574..bea2cb9ddd8171c3dc5c0aae4db5f1da48e1cbea 100644 (file)
@@ -50,7 +50,6 @@
 #import <string.h> 
 #import <malloc/malloc.h>
 
-#define OLD 1
 #import "Object.h"
 #import "Protocol.h"
 #import "objc-runtime.h"
index ace45a7ea2031ea8e6ff30f41d413d8a0be54b12..a67cdad06213d3311fb3ef48de14b60fcd843be0 100644 (file)
 #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 
@@ -106,14 +100,14 @@ DEPRECATED_IN_MAC_OS_X_VERSION_10_5_AND_LATER
  * 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)
 
index 96690550845ab1dd211e1d5c6f502230f9e40b99..b9217fc5982bd64b9ae0582c62ccbd51c37cd589 100644 (file)
@@ -33,7 +33,6 @@
 #import <stdio.h>
 #import <string.h>
 
-#define OLD 1
 #import <objc/List.h>
 
 #define DATASIZE(count) ((count) * sizeof(id))
index 1b133b8af1d893e94f95ff72193d4515c8f2bf6a..698b922fb1732446af353c4e8846ff27c472546a 100644 (file)
@@ -32,6 +32,7 @@
 
 /* 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
 
index 43d556720e1f8da87c5f62907e542730f0d8885e..a3c9f095047c6fd307946790533b709b6ea535ed 100644 (file)
 #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 
 
@@ -66,7 +62,8 @@ typedef struct {
 - (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
@@ -75,7 +72,8 @@ typedef struct {
 - (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
diff --git a/runtime/a1a2-blocktramps-arm.s b/runtime/a1a2-blocktramps-arm.s
new file mode 100644 (file)
index 0000000..ca4ad44
--- /dev/null
@@ -0,0 +1,585 @@
+#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
diff --git a/runtime/a1a2-blocktramps-i386.s b/runtime/a1a2-blocktramps-i386.s
new file mode 100755 (executable)
index 0000000..641160e
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * 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
diff --git a/runtime/a1a2-blocktramps-x86_64.s b/runtime/a1a2-blocktramps-x86_64.s
new file mode 100755 (executable)
index 0000000..93f4477
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ * 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
diff --git a/runtime/a2a3-blocktramps-arm.s b/runtime/a2a3-blocktramps-arm.s
new file mode 100644 (file)
index 0000000..7904bb3
--- /dev/null
@@ -0,0 +1,582 @@
+#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
diff --git a/runtime/a2a3-blocktramps-i386.s b/runtime/a2a3-blocktramps-i386.s
new file mode 100755 (executable)
index 0000000..a9e2637
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * 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
diff --git a/runtime/a2a3-blocktramps-x86_64.s b/runtime/a2a3-blocktramps-x86_64.s
new file mode 100755 (executable)
index 0000000..5dc70a1
--- /dev/null
@@ -0,0 +1,563 @@
+/*
+ * 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
diff --git a/runtime/error.h b/runtime/error.h
deleted file mode 100644 (file)
index 49d4ae1..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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_ */
index de52196967d15195c8252f46383adb40bc195b3c..d87c564976dcf6a690600a9891b135cbcb392987 100644 (file)
 #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
  *************************************************************************/
@@ -61,41 +65,41 @@ typedef struct {
  */
 
 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:
@@ -104,7 +108,7 @@ OBJC_EXPORT int NXHashMember (NXHashTable *table, const void *data);
        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:
@@ -114,14 +118,14 @@ OBJC_EXPORT void *NXHashGet (NXHashTable *table, const void *data);
        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:
@@ -133,12 +137,12 @@ OBJC_EXPORT void *NXHashRemove (NXHashTable *table, const void *data);
     }
 */
 
-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 */
 
 /*************************************************************************
@@ -146,23 +150,23 @@ OBJC_EXPORT int NXNextHashState(NXHashTable *table, NXHashState *state, void **d
  *     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.
@@ -175,8 +179,8 @@ For example NXStrStructKeyPrototype can be used to hash pointers to Example, whe
     
 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
@@ -188,34 +192,36 @@ OBJC_EXPORT const NXHashTablePrototype NXStrStructKeyPrototype;
 /* 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_ */
index 89a31ab0497ab3f3ebff3821efa7f2bd4cbb4312..bbaee1fe0b076153f107c414c882f1fdb92d762e 100644 (file)
@@ -52,7 +52,7 @@ static unsigned log2u (unsigned x) { return (x<2) ? 0 : log2u (x>>1)+1; };
 
 #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)))
@@ -66,7 +66,7 @@ static unsigned log2u (unsigned x) { return (x<2) ? 0 : log2u (x>>1)+1; };
 #   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))
@@ -107,7 +107,7 @@ static NXHashTablePrototype protoPrototype = {
     hashPrototype, isEqualPrototype, NXNoEffectFree, 0
     };
 
-static NXHashTable *prototypes NOBSS = NULL;
+static NXHashTable *prototypes = NULL;
        /* table of all prototypes */
 
 static void bootstrap (void) {
@@ -283,11 +283,11 @@ void *NXHashGet (NXHashTable *table, const void *data) {
     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;
@@ -632,7 +632,7 @@ NXAtom NXUniqueStringWithLength (const char *buffer, int length) {
     };
 
 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);
diff --git a/runtime/llvm-DenseMap.h b/runtime/llvm-DenseMap.h
new file mode 100644 (file)
index 0000000..0948e00
--- /dev/null
@@ -0,0 +1,862 @@
+//===- 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
diff --git a/runtime/llvm-type_traits.h b/runtime/llvm-type_traits.h
new file mode 100644 (file)
index 0000000..2fef4b6
--- /dev/null
@@ -0,0 +1,126 @@
+//===- 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
index c289f4ab4d53d553b7649704d1c173e99d46bd3f..bc2a6355517ea27b83d887dccd3cb227e83b7a39 100644 (file)
 #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.
@@ -45,16 +49,16 @@ typedef struct _NXMapTable {
     /* 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
@@ -68,32 +72,32 @@ typedef struct _NXMapTablePrototype {
 
 /***************       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:
@@ -106,25 +110,27 @@ OBJC_EXPORT void *NXMapRemove(NXMapTable *table, const void *key);
     }
 */
 
-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_ */
index 2816a859636b4910e73a2217bd7baea104a33c06..d60126c041aab1a6960957ddcf114876843a36f9 100644 (file)
@@ -50,13 +50,16 @@ typedef struct _MapPair {
 
 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) {
@@ -64,7 +67,7 @@ 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) {
@@ -113,8 +116,8 @@ NXMapTable *NXCreateMapTableFromZone(NXMapTablePrototype prototype, unsigned cap
        (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;
 }
 
@@ -131,7 +134,7 @@ void NXFreeMapTable(NXMapTable *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);
@@ -158,24 +161,17 @@ BOOL NXCompareMapTables(NXMapTable *table1, NXMapTable *table2) {
 
 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)) {
@@ -196,19 +192,16 @@ void *NXMapGet(NXMapTable *table, const void *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);
@@ -220,10 +213,6 @@ static void _NXMapRehash(NXMapTable *table) {
     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);
@@ -232,44 +221,32 @@ void *NXMapInsert(NXMapTable *table, const void *key, const void *value) {
        _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)) {
@@ -331,7 +308,7 @@ void *NXMapRemove(NXMapTable *table, const void *key) {
 
 NXMapState NXInitMapState(NXMapTable *table) {
     NXMapState state;
-    state.index = table->nbBuckets;
+    state.index = table->nbBucketsMinusOne + 1;
     return state;
 }
     
@@ -353,7 +330,7 @@ int NXNextMapState(NXMapTable *table, NXMapState *state, const void **key, const
 * 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;
@@ -373,7 +350,7 @@ __private_extern__ void *NXMapKeyCopyingInsert(NXMapTable *table, const void *ke
 * 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;
@@ -393,7 +370,11 @@ __private_extern__ void *NXMapKeyFreeingRemove(NXMapTable *table, const void *ke
 /****          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) {
@@ -411,7 +392,7 @@ 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) {
index bca49416333de620d6952d72bcf28b8802f06aad..7f842b3509b216c63b6f8234da61c9fd0e7e2963 100644 (file)
@@ -46,12 +46,15 @@ struct objc_super {
  *
  * 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
@@ -64,16 +67,22 @@ OBJC_EXPORT id objc_msgSendSuper(struct objc_super *super, SEL op, ...);
  * 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
 
 
@@ -83,22 +92,41 @@ OBJC_EXPORT void objc_msgSendSuper_stret(void * stretAddr, struct objc_super *su
  * 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
 
 
@@ -112,9 +140,9 @@ OBJC_EXPORT long double objc_msgSend_fpret(id self, SEL op, ...);
  * 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
@@ -133,9 +161,9 @@ OBJC_EXPORT void method_invoke_stret(id receiver, Method m, ...)
  * 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
@@ -169,11 +197,7 @@ OBJC_EXPORT double objc_msgSendv_fpret(id self, SEL op, unsigned arg_size, marg_
 
 #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 { \
diff --git a/runtime/objc-abi.h b/runtime/objc-abi.h
new file mode 100644 (file)
index 0000000..53f64f0
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * 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
index 876150914aa7412d5cd1521140bf90fd37ccb30a..2fcb92f80e0ea1cfb5f84940bc7562baea27f081 100644 (file)
 #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
diff --git a/runtime/objc-arr.mm b/runtime/objc-arr.mm
new file mode 100644 (file)
index 0000000..0bcd995
--- /dev/null
@@ -0,0 +1,1296 @@
+/*
+ * 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();
+}
index 43b0adaeb627bb0c978b2fe9139b7ac663088b68..bb4e8b2965745d9f606cd1ee564a279353d310e7 100644 (file)
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#import "objc-auto.h"
+
+#ifndef OBJC_NO_GC
+
 #import <auto_zone.h>
 #import <objc/objc.h>
 #import <objc/runtime.h>
@@ -102,7 +106,7 @@ static void pointer_set_dispose(pointer_set_t *set) {
 /*
    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;
@@ -118,7 +122,7 @@ __private_extern__ BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename)
     // 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);
@@ -127,7 +131,7 @@ __private_extern__ BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename)
     };
     
     // 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);
@@ -147,7 +151,7 @@ __private_extern__ BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename)
     
     
     // 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
@@ -158,26 +162,25 @@ __private_extern__ BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename)
     
     // 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);
@@ -196,13 +199,13 @@ __private_extern__ BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename)
         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
     });
@@ -216,3 +219,5 @@ __private_extern__ BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename)
     }
     return YES;
 }
+
+#endif
index dbd846530ef1993b0ad0f5e7e07983a6d3e6fcba..cd8962799e5594f8b57a2b2824d8ce0851e368c7 100644 (file)
@@ -25,6 +25,7 @@
 #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 {
@@ -76,90 +71,147 @@ 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
@@ -207,17 +259,17 @@ static OBJC_INLINE id objc_read_weak(id *location)
 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); }
 
@@ -225,5 +277,16 @@ static OBJC_INLINE void objc_registerThreadWithCollector() { }
 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
index f702091f7f7796d9efba672c89ceaafb0b2fa205..bc87e613468fcd79abc37401d75f502fbaa7579b 100644 (file)
@@ -21,7 +21,9 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#import "objc-config.h"
 #import "objc-auto.h"
+#import "objc-accessors.h"
 
 #ifndef OBJC_NO_GC
 
@@ -31,6 +33,7 @@
 #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 */
@@ -71,6 +75,22 @@ __private_extern__ id (*objc_assign_ivar_internal)(id, id, ptrdiff_t) = objc_ass
 @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.
@@ -127,23 +147,9 @@ static struct {
 
 
 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);
@@ -151,24 +157,49 @@ 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);
     }
 }
 
@@ -190,6 +221,10 @@ BOOL objc_collecting_enabled(void) // Old naming
     return UseGC;
 }
 
+malloc_zone_t *objc_collectableZone(void) {
+    return gc_zone;
+}
+
 BOOL objc_dumpHeap(char *filenamebuffer, unsigned long length) {
     static int counter = 0;
     ++counter;
@@ -227,20 +262,17 @@ id objc_allocate_object(Class cls, int extra)
 * 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);
         }
@@ -252,15 +284,24 @@ __private_extern__ id objc_assign_global_gc(id value, id *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);
         }
@@ -271,20 +312,39 @@ __private_extern__ id objc_assign_ivar_gc(id value, id base, ptrdiff_t 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);
@@ -293,8 +353,7 @@ id objc_assign_strongCast_generic(id value, id *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);
@@ -303,8 +362,16 @@ id objc_assign_global_generic(id value, id *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);
@@ -314,25 +381,112 @@ id objc_assign_ivar_generic(id value, id dest, ptrdiff_t 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 = &sectionsStart[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)
 {
@@ -397,51 +551,20 @@ BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, id replaceme
 
 
 /***********************************************************************
-* 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;
@@ -451,59 +574,42 @@ id objc_read_weak(id *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;
 }
 
 /***********************************************************************
@@ -519,38 +625,15 @@ BOOL objc_is_finalized(void *ptr) {
 
 
 /***********************************************************************
-* 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
 **********************************************************************/
@@ -559,8 +642,6 @@ static IMP _NSObject_finalize = NULL;
 
 // 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
@@ -568,22 +649,28 @@ static const char *__crashreporter_info__;
 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);
     }
@@ -597,7 +684,7 @@ static void finalizeOneMainThreadOnlyObject(void *obj, void *arg) {
 // 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);
@@ -622,17 +709,29 @@ static bool batchFinalize(auto_zone_t *zone,
                           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;
@@ -712,7 +811,11 @@ static void batchFinalizeOnTwoThreads(auto_zone_t *zone,
     
     // 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");
 
@@ -784,7 +887,7 @@ static BOOL _NSResurrectedObject_resolveInstanceMethod(id self, SEL _cmd, SEL na
 }
 
 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;
 }
 
@@ -792,7 +895,7 @@ static void _NSResurrectedObject_initialize() {
     _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);
@@ -800,13 +903,13 @@ static void _NSResurrectedObject_initialize() {
 
 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);
     }
 }
 
@@ -823,60 +926,125 @@ static char* objc_name_for_address(auto_zone_t *zone, vm_address_t base, vm_addr
     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
@@ -886,6 +1054,10 @@ static auto_zone_t *gc_zone_init(void)
     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;
 }
@@ -895,6 +1067,11 @@ static auto_zone_t *gc_zone_init(void)
 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);
@@ -911,20 +1088,23 @@ void objc_assertRegisteredThreadWithCollector()
 }
 
 // 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;
@@ -934,14 +1114,11 @@ __private_extern__ void gc_init(BOOL on)
 
         // 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");
 }
 
 
@@ -989,16 +1166,13 @@ static void block_gc_memmove(void *dst, void *src, unsigned long size) {
     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
     );
 }
 
@@ -1025,19 +1199,21 @@ static volatile Class *AllClasses = nil;
 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.
@@ -1080,7 +1256,7 @@ static void addClassHelper(uintptr_t *table, uintptr_t candidate) {
 }
 
 // lock held by callers
-__private_extern__
+PRIVATE_EXTERN
 void objc_addRegisteredClass(Class candidate) {
     if (!UseGC) return;
     uintptr_t *table = (uintptr_t *)AllClasses;
@@ -1100,7 +1276,7 @@ void objc_addRegisteredClass(Class candidate) {
             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;
@@ -1108,8 +1284,9 @@ void objc_addRegisteredClass(Class candidate) {
                     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;
         }
@@ -1120,7 +1297,7 @@ void objc_addRegisteredClass(Class candidate) {
 }
 
 // lock held by callers
-__private_extern__
+PRIVATE_EXTERN
 void objc_removeRegisteredClass(Class candidate) {
     if (!UseGC) return;
     uintptr_t *table = (uintptr_t *)AllClasses;
@@ -1176,14 +1353,6 @@ static void strlcati(char *str, uintptr_t value, size_t bufSize)
     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)
 {
@@ -1317,7 +1486,8 @@ static char *name_for_address(auto_zone_t *zone, vm_address_t base, vm_address_t
 
     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));
@@ -1325,7 +1495,7 @@ static char *name_for_address(auto_zone_t *zone, vm_address_t base, vm_address_t
             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;
@@ -1338,8 +1508,19 @@ static char *name_for_address(auto_zone_t *zone, vm_address_t base, vm_address_t
         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) {
diff --git a/runtime/objc-block-trampolines.m b/runtime/objc-block-trampolines.m
new file mode 100644 (file)
index 0000000..8e87a57
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * 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, &currentProtection, &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;
+}
index 0da21a391701e1ca81686ce1d1b180dbac6d5d7a..34728fb763fbb671f3b93982a08ae8cab29d4c7a 100644 (file)
@@ -103,9 +103,9 @@ typedef struct {
 #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 {
@@ -190,18 +190,18 @@ typedef struct
 
 /* 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
 
 
@@ -213,21 +213,13 @@ __private_extern__ unsigned int MaxIdealFlushCachesCount            = 0;
 * 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
 
@@ -244,7 +236,7 @@ static void _cache_flush(Class cls);
 
 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);
@@ -306,7 +298,7 @@ static Cache _cache_malloc(uintptr_t slotCount)
 #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);
@@ -373,7 +365,7 @@ static void _cache_free_block(void *block)
 * 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;
 
@@ -471,7 +463,7 @@ static Cache _cache_expand(Class cls)
 
                 // Deallocate "forward::" entry
                 if (oldEntry->imp == &_objc_msgForward_internal) {
-                    _cache_collect_free (oldEntry, sizeof(cache_entry), NO);
+                    _cache_collect_free (oldEntry, sizeof(cache_entry));
                 }
             }
 
@@ -501,7 +493,7 @@ static Cache _cache_expand(Class cls)
     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));
         }
     }
 
@@ -509,7 +501,9 @@ static Cache _cache_expand(Class cls)
     _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;
 }
 
@@ -524,7 +518,7 @@ static Cache _cache_expand(Class cls)
 *
 * 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;
@@ -561,7 +555,7 @@ __private_extern__ BOOL _cache_fill(Class cls, Method smt, SEL sel)
     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);
@@ -570,41 +564,17 @@ __private_extern__ BOOL _cache_fill(Class cls, Method smt, SEL sel)
         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);
 
@@ -619,7 +589,7 @@ __private_extern__ BOOL _cache_fill(Class cls, Method smt, SEL sel)
 * 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;
   
@@ -633,6 +603,58 @@ __private_extern__ void _cache_addForwardEntry(Class cls, SEL sel)
 }
 
 
+/***********************************************************************
+* _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.
 *
@@ -642,7 +664,7 @@ __private_extern__ void _cache_addForwardEntry(Class cls, SEL sel)
 #if __OBJC2__
 static 
 #else
-__private_extern__
+PRIVATE_EXTERN
 #endif
 void _cache_flush(Class cls)
 {
@@ -680,7 +702,7 @@ 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
@@ -692,7 +714,7 @@ void _cache_flush(Class cls)
 * 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);
@@ -708,8 +730,10 @@ __private_extern__ void flush_cache(Class cls)
 
 #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
@@ -725,20 +749,6 @@ static uintptr_t _get_pc_for_thread(thread_t thread)
     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;
@@ -894,53 +904,64 @@ static void _garbage_make_room(void)
 * 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;
@@ -1051,8 +1072,7 @@ static cache_allocator_region *cache_allocator_add_region(size_t size)
     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);
 
@@ -1289,7 +1309,7 @@ static void _cache_print(Cache cache)
 /***********************************************************************
 * _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));
index 65a85af156785adaf4a9be66a488f0714b0826d3..3dcea04924ef0a561895ed0e5e5e38f12734421b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999-2007 Apple Inc.  All Rights Reserved.
+ * Copyright (c) 1999-2009 Apple Inc.  All Rights Reserved.
  * 
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -28,8 +28,8 @@
 
 #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.
@@ -89,10 +89,10 @@ static void allocateExt(struct old_class *cls)
 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;
         }
     }
@@ -116,7 +116,7 @@ static inline struct old_method *_findNamedMethodInList(struct old_method_list *
 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;
 }
@@ -161,11 +161,9 @@ static struct old_method_list *fixupSelectorsInMethodList(struct old_class *cls,
             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;
@@ -306,7 +304,7 @@ static inline struct old_method * _getMethod(struct old_class *cls, SEL sel) {
 
 
 // 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;
@@ -327,15 +325,15 @@ static void _freedHandler(id obj, SEL sel)
 /***********************************************************************
 * 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);
 
@@ -358,10 +356,10 @@ __private_extern__ IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init)
 /***********************************************************************
 * 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;
@@ -375,6 +373,7 @@ Ivar _class_getVariable(Class cls_gen, const char *name)
             // (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;
             }
         }
@@ -385,14 +384,41 @@ Ivar _class_getVariable(Class cls_gen, const char *name)
 }
 
 
-/***********************************************************************
-* 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)) {
@@ -404,7 +430,7 @@ nextPropertyList(struct old_class *cls, uintptr_t *indexp)
     } 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;
         }
@@ -427,10 +453,10 @@ nextPropertyList(struct old_class *cls, uintptr_t *indexp)
 * 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 {
@@ -443,10 +469,10 @@ class_getIvarLayout(Class cls_gen)
 * 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 {
@@ -459,9 +485,9 @@ class_getWeakIvarLayout(Class cls_gen)
 * 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)) {
@@ -470,17 +496,46 @@ void class_setIvarLayout(Class cls_gen, const char *layout)
     } 
 
     // 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);
@@ -488,7 +543,7 @@ void class_setWeakIvarLayout(Class cls_gen, const char *layout)
     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);
 }
@@ -499,9 +554,9 @@ void class_setWeakIvarLayout(Class cls_gen, const char *layout)
 * 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 {
@@ -515,9 +570,9 @@ __private_extern__ void _class_changeInfo(Class cls, long set, long clear)
 * _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;
 }
 
@@ -526,7 +581,7 @@ __private_extern__ BOOL _class_getInfo(Class cls, int get)
 * _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);
 }
@@ -536,7 +591,7 @@ __private_extern__ void _class_setInfo(Class cls, long set)
 * _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);
 }
@@ -547,7 +602,7 @@ __private_extern__ void _class_clearInfo(Class cls, long 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);
 }
@@ -558,7 +613,7 @@ __private_extern__ BOOL _class_isInitializing(Class cls)
 * 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);
 }
@@ -568,7 +623,7 @@ __private_extern__ BOOL _class_isInitialized(Class cls)
 * 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);
 }
@@ -578,7 +633,7 @@ __private_extern__ void _class_setInitializing(Class cls)
 * 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);
 }
@@ -603,13 +658,13 @@ int class_getVersion(Class cls)
 }
 
 
-__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);
@@ -621,7 +676,7 @@ __private_extern__ BOOL _class_isMetaClass(Class cls)
 * 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)) {
@@ -640,30 +695,30 @@ __private_extern__ Class _class_getNonMetaClass(Class 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;
@@ -671,24 +726,24 @@ __private_extern__ const char * _class_getName(Class cls)
 
 
 
-__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 {
@@ -718,7 +773,7 @@ OBJC_EXPORT struct objc_method_list *class_nextMethodList(Class cls, void **it)
     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;
 }
@@ -735,7 +790,7 @@ OBJC_EXPORT void class_addMethods(Class cls, struct objc_method_list *meths)
 
     // 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
@@ -754,7 +809,7 @@ OBJC_EXPORT void class_removeMethods(Class cls, struct objc_method_list *meths)
 
     // 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
@@ -769,39 +824,39 @@ OBJC_EXPORT void class_removeMethods(Class cls, struct objc_method_list *meths)
 * 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);
 }
 
 
@@ -833,7 +888,7 @@ static NXMapTable * posed_class_hash = NULL;
 /***********************************************************************
 * objc_getOrigClass.
 **********************************************************************/
-__private_extern__ Class _objc_getOrigClass(const char *name)
+PRIVATE_EXTERN Class _objc_getOrigClass(const char *name)
 {
     Class ret;
 
@@ -894,7 +949,7 @@ static void _objc_addOrigClass         (struct old_class *origClass)
 * 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, 
@@ -951,8 +1006,8 @@ void change_class_references(struct old_class *imposter,
 **********************************************************************/
 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;
 
@@ -1050,10 +1105,10 @@ Class class_poseAs(Class imposter_gen, Class original_gen)
 *
 * 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;
@@ -1221,7 +1276,7 @@ __private_extern__ void flush_caches(Class target_gen, BOOL flush_meta)
 * 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;
@@ -1295,7 +1350,7 @@ static IMP _class_getLoadMethod_nocheck(struct old_class *cls)
 }
 
 
-__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);
@@ -1306,9 +1361,9 @@ __private_extern__ BOOL _class_hasLoadMethod(Class cls)
 * _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);
     }
@@ -1316,87 +1371,112 @@ __private_extern__ IMP _class_getLoadMethod(Class cls_gen)
 }
 
 
-__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;
     }
@@ -1412,11 +1492,11 @@ IMP method_setImplementation(Method m_gen, IMP 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;
@@ -1438,6 +1518,43 @@ struct objc_method_description * method_getDescription(Method m)
 }
 
 
+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
 **********************************************************************/
@@ -1467,7 +1584,7 @@ static IMP _class_addMethod(Class cls_gen, SEL name, IMP imp,
         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;
@@ -1617,15 +1734,18 @@ BOOL class_addProtocol(Class cls_gen, Protocol *protocol_gen)
 
 
 /***********************************************************************
-* 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) 
@@ -1636,22 +1756,22 @@ _class_addProperties(struct old_class *cls,
     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));
@@ -1664,6 +1784,63 @@ _class_addProperties(struct old_class *cls,
     }
 
     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);
 }
 
 
@@ -1673,7 +1850,8 @@ _class_addProperties(struct old_class *cls,
 * 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;
@@ -1717,21 +1895,21 @@ Protocol **class_copyProtocolList(Class cls_gen, unsigned int *outCount)
 /***********************************************************************
 * 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;
@@ -1743,7 +1921,7 @@ Property class_getProperty(Class cls_gen, const char *name)
  done:
     mutex_unlock(&classLock);
 
-    return result;
+    return (objc_property_t)result;
 }
 
 
@@ -1753,12 +1931,12 @@ Property class_getProperty(Class cls_gen, const char *name)
 * 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;
 
@@ -1775,7 +1953,7 @@ Property *class_copyPropertyList(Class cls_gen, unsigned int *outCount)
     }
 
     if (count > 0) {
-        result = malloc((count+1) * sizeof(Property));
+        result = malloc((count+1) * sizeof(struct old_property *));
         
         p = 0;
         iterator = 0;
@@ -1790,7 +1968,7 @@ Property *class_copyPropertyList(Class cls_gen, unsigned int *outCount)
     mutex_unlock(&classLock);
 
     if (outCount) *outCount = count;
-    return result;
+    return (objc_property_t *)result;
 }
 
 
@@ -1830,7 +2008,7 @@ Method *class_copyMethodList(Class cls_gen, unsigned int *outCount)
             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;
                 }
@@ -1886,8 +2064,9 @@ Ivar *class_copyIvarList(Class cls_gen, unsigned int *outCount)
 /***********************************************************************
 * 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;
 
@@ -1896,6 +2075,12 @@ void set_superclass(struct old_class *cls, struct old_class *supercls)
         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);
@@ -1914,7 +2099,10 @@ void set_superclass(struct old_class *cls, struct old_class *supercls)
     }    
 }
 
-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);
@@ -1922,7 +2110,7 @@ void objc_initializeClassPair(Class superclass_gen, const char *name, Class cls_
     
     // 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);
@@ -1941,18 +2129,23 @@ void objc_initializeClassPair(Class superclass_gen, const char *name, Class cls_
         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, 
@@ -1971,8 +2164,8 @@ 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);
@@ -2015,23 +2208,23 @@ void objc_registerClassPair(Class cls_gen)
 
     // 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);
@@ -2040,34 +2233,36 @@ void objc_registerClassPair(Class cls_gen)
                 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;
             }
         }
     }
@@ -2096,7 +2291,7 @@ Class objc_duplicateClass(Class orig_gen, const char *name, size_t extraBytes)
     // 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;
@@ -2174,18 +2369,63 @@ void objc_disposeClassPair(Class cls_gen)
 }
 
 
+
 /***********************************************************************
-* _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;
@@ -2198,21 +2438,70 @@ static id _internal_object_copyFromZone(id oldObj, size_t extraBytes, void *zone
     // 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; 
@@ -2244,28 +2533,28 @@ static id _internal_object_reallocFromZone(id anObject, size_t nBytes,
 }
 
 
-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);
     }
@@ -2275,42 +2564,54 @@ id class_createInstanceFromZone(Class cls, size_t extraBytes, void *z)
 {
     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); 
 }
 
@@ -2319,7 +2620,7 @@ id object_reallocFromZone(id obj, size_t nBytes, void *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;
 }
index 8debcdd2d6550dae20d67b8f2154dc53329ee92b..bf638389b175bf348c6d4d6a732817da7bc357c5 100644 (file)
 **********************************************************************/
 
 #include "objc-private.h"
+#include "objc-abi.h"
+#include "objc-auto.h"
 #include <objc/message.h>
 
 
@@ -197,6 +199,7 @@ static ObjCLogProc  objcMsgLogProc          = &LogObjCMessageSend;
 static int                     objcMsgLogEnabled       = 0;
 #endif
 
+
 /***********************************************************************
 * Information about multi-thread support:
 *
@@ -211,11 +214,11 @@ static int                        objcMsgLogEnabled       = 0;
 
 /***********************************************************************
 * 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);
 }
 
 
@@ -229,6 +232,11 @@ Class object_setClass(id obj, Class cls)
         do {
             old = obj->isa;
         } while (! OSAtomicCompareAndSwapPtrBarrier(old, cls, (void*)&obj->isa));
+
+        if (old  &&  _class_instancesHaveAssociatedObjects(old)) {
+            _class_setInstancesHaveAssociatedObjects(cls);
+        }
+
         return old;
     }
     else return Nil;
@@ -240,7 +248,8 @@ Class object_setClass(id obj, Class cls)
 **********************************************************************/
 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";
 }
 
@@ -250,7 +259,7 @@ const char *object_getClassName(id obj)
 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;
 }
 
@@ -260,11 +269,8 @@ Ivar object_setInstanceVariable(id obj, const char *name, void *value)
     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;
@@ -274,10 +280,8 @@ Ivar object_getInstanceVariable(id obj, const char *name, void **value)
 {
     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;
         }
     }
@@ -285,11 +289,70 @@ Ivar object_getInstanceVariable(id obj, const char *name, void **value)
     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);
     }
 }
 
@@ -297,7 +360,27 @@ void object_setIvar(id obj, Ivar ivar, id value)
 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;
@@ -318,7 +401,7 @@ static void object_cxxDestructFromClass(id obj, Class cls)
     // 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) {
@@ -337,10 +420,11 @@ static void object_cxxDestructFromClass(id obj, Class cls)
 * 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
 }
 
 
@@ -361,7 +445,12 @@ __private_extern__ void object_cxxDestruct(id obj)
 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) {
@@ -370,7 +459,6 @@ static BOOL object_cxxConstructFromClass(id obj, Class cls)
     }
 
     // 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
     
@@ -395,10 +483,11 @@ static BOOL object_cxxConstructFromClass(id obj, Class cls)
 *   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
 }
 
 
@@ -498,7 +587,7 @@ static Method _class_resolveInstanceMethod(Class cls, SEL sel)
 * 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;
 
@@ -579,7 +668,7 @@ Ivar class_getInstanceVariable(Class cls, const char *name)
 {
     if (!cls  ||  !name) return NULL;
 
-    return _class_getVariable(cls, name);
+    return _class_getVariable(cls, name, NULL);
 }
 
 
@@ -594,67 +683,14 @@ Ivar class_getClassVariable(Class cls, const char *name)
 }
 
 
-__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)
+);
 
 
 /***********************************************************************
@@ -666,6 +702,13 @@ void gdb_objc_class_changed(Class cls, unsigned long changes, const char *classn
 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);
+    }
 }
 
 
@@ -739,14 +782,14 @@ IMP class_getMethodImplementation_stret(Class cls, SEL sel)
 }
 
 
-// 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,
@@ -772,7 +815,7 @@ static int  LogObjCMessageSend (BOOL                        isClassMethod,
             isClassMethod ? '+' : '-',
             objectsClass,
             implementingClass,
-            (char *) selector);
+            sel_getName(selector));
 
     static OSSpinLock lock = OS_SPINLOCK_INIT;
     OSSpinLockLock(&lock);
@@ -802,7 +845,7 @@ void        instrumentObjcMessageSends       (BOOL          flag)
     objcMsgLogEnabled = enabledValue;
 }
 
-__private_extern__ void        logObjcMessageSends      (ObjCLogProc   logProc)
+PRIVATE_EXTERN void    logObjcMessageSends      (ObjCLogProc   logProc)
 {
     if (logProc)
     {
@@ -826,7 +869,7 @@ __private_extern__ void     logObjcMessageSends      (ObjCLogProc   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)
@@ -850,8 +893,12 @@ log_and_fill_cache(Class cls, Class implementer, Method meth, SEL sel)
 * 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*/);
 }
 
@@ -865,8 +912,8 @@ __private_extern__ IMP _class_lookupMethodAndLoadCache(Class cls, SEL sel)
 * 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;
@@ -891,6 +938,12 @@ __private_extern__ IMP lookUpMethod(Class cls, SEL sel,
  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);
@@ -956,7 +1009,7 @@ __private_extern__ IMP lookUpMethod(Class cls, SEL 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;
 }
@@ -997,16 +1050,6 @@ static IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel)
 }
 
 
-/***********************************************************************
-* _objc_create_zone.
-**********************************************************************/
-
-void *_objc_create_zone(void)
-{
-    return malloc_default_zone();
-}
-
-
 /***********************************************************************
 * _malloc_internal
 * _calloc_internal
@@ -1017,22 +1060,22 @@ void *_objc_create_zone(void)
 * _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;
@@ -1043,8 +1086,13 @@ __private_extern__ char *_strdup_internal(const char *str)
     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);
@@ -1054,22 +1102,27 @@ __private_extern__ char *_strdupcat_internal(const char *s1, const char *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);
@@ -1108,24 +1161,6 @@ unsigned int method_getNumberOfArguments(Method m)
 }
 
 
-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);
@@ -1160,16 +1195,15 @@ char * method_copyArgumentType(Method m, unsigned int index)
 * 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)) {
@@ -1181,21 +1215,25 @@ id objc_constructInstance(Class cls, void *bytes)
 }
 
 
-/***********************************************************************
-* 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;
@@ -1203,83 +1241,65 @@ void *objc_destructInstance(id 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
@@ -1296,3 +1316,259 @@ inform_duplicate(const char *name, Class oldCls, Class cls)
                   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;
+}
index 219214f088aae2aedc6bfbf1a905bde0d50dbe31..9f81b9aa03236a83e1bc09debe014b7ab4efcf22 100644 (file)
 
 #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
index c0b3f9cd9e43f4982ff81ed40a96117fcc3ec484..68b6e2fc21cdc4f504b21bb4cb3e8073f4e719dd 100644 (file)
 
 #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);
@@ -44,7 +44,7 @@ __private_extern__ void _objc_inform(const char *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);
@@ -55,22 +55,25 @@ __private_extern__ void _objc_fatal(const char *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);
 
@@ -93,10 +96,15 @@ static void _objc_crashlog(const char *message)
     }
 #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) {
@@ -104,9 +112,11 @@ static void _objc_crashlog(const char *message)
         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.
@@ -122,26 +132,14 @@ static void _objc_syslog(const char *message)
         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) 
 { 
@@ -157,11 +155,26 @@ 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;
@@ -182,7 +195,7 @@ __private_extern__ void _objc_fatal(const char *fmt, ...)
  * 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;
@@ -204,7 +217,7 @@ __private_extern__ void _objc_inform(const char *fmt, ...)
  * 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;
@@ -225,7 +238,7 @@ __private_extern__ void _objc_inform_on_crash(const char *fmt, ...)
 /* 
  * 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;
@@ -262,7 +275,11 @@ static void _objc_trap2(void)
 #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) {
@@ -271,18 +288,5 @@ __private_extern__ void _objc_warn_deprecated(const char *oldf, const char *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) {
-}
-
index 17c0b612a34ebb2d90daa32b32e05738ccd48741..153f50c80c9f9c134efd4209715230ca841497fb 100644 (file)
 
 // 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 {
@@ -45,14 +50,15 @@ 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__
@@ -64,19 +70,27 @@ typedef int (*objc_exception_matcher)(Class catch_type, id exception);
 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
index 4cc4a3e70827b3cb464543d3dedd127e65c82ec4..1aa58818fe0d2e5892d422d60c95c5a8d15d5857 100644 (file)
@@ -30,6 +30,7 @@
 #include "objc-private.h"
 #include <stdlib.h>
 #include <setjmp.h>
+#include <execinfo.h>
 
 #include "objc-exception.h"
 
@@ -66,6 +67,14 @@ void objc_exception_throw(id exception) {
     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);
@@ -194,7 +203,7 @@ static id default_extract(void *localExceptionData) {
 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;
 }
@@ -209,12 +218,12 @@ static void set_default_handlers() {
 }
 
 
-__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
 }
@@ -230,7 +239,7 @@ __private_extern__ void _destroyAltHandlerList(struct alt_handler_list *list)
 
 #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
@@ -245,17 +254,16 @@ static const _Unwind_Action _UA_CLEANUP_PHASE = 2;
 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
 {
@@ -270,13 +278,15 @@ extern uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context *);
 
 
 // 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);
@@ -284,13 +294,10 @@ extern void __cxa_end_catch(void);
 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 
@@ -303,8 +310,6 @@ CXX_PERSONALITY(int version,
 
 // 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
@@ -368,7 +373,7 @@ static objc_exception_preprocessor exception_preprocessor = _objc_default_except
 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))
     {
@@ -439,11 +444,11 @@ objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
 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));
@@ -493,12 +498,21 @@ void objc_exception_throw(id obj)
 
     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);
@@ -583,7 +597,7 @@ static char _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo,
 * 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) {
@@ -614,9 +628,9 @@ static void _objc_terminate(void)
 * 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)
 {
 }
 
@@ -628,6 +642,8 @@ static void call_alt_handlers(struct _Unwind_Context *ctx)
 #else
 
 #include <libunwind.h>
+#include <execinfo.h>
+#include <dispatch/dispatch.h>
 
 // Dwarf eh data encodings
 #define DW_EH_PE_omit      0xff  // no data follows
@@ -906,20 +922,39 @@ static struct frame_range findHandler(void)
 
 // 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)
@@ -932,15 +967,32 @@ 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);
         }
@@ -991,11 +1043,37 @@ uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context)
     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) {
@@ -1007,7 +1085,7 @@ uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context)
         }
     }
 
-    return i+1;
+    return token;
 }
 
 
@@ -1017,37 +1095,122 @@ void objc_removeExceptionHandler(uintptr_t 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
@@ -1080,7 +1243,7 @@ static void call_alt_handlers(struct _Unwind_Context *ctx)
     }
 }
 
-// ! NO_ZEROCOST_EXCEPTIONS
+// SUPPORT_ALT_HANDLERS
 #endif
 
 
@@ -1089,10 +1252,14 @@ static void call_alt_handlers(struct _Unwind_Context *ctx)
 * 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
 }
 
 
diff --git a/runtime/objc-externalref.m b/runtime/objc-externalref.m
new file mode 100644 (file)
index 0000000..121faad
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * 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;
+}
diff --git a/runtime/objc-file-old.h b/runtime/objc-file-old.h
new file mode 100644 (file)
index 0000000..c9127d0
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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
diff --git a/runtime/objc-file-old.m b/runtime/objc-file-old.m
new file mode 100644 (file)
index 0000000..3eaa6b1
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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
diff --git a/runtime/objc-file.h b/runtime/objc-file.h
new file mode 100644 (file)
index 0000000..b27ddeb
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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
diff --git a/runtime/objc-file.m b/runtime/objc-file.m
deleted file mode 100644 (file)
index ce4866a..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * 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
diff --git a/runtime/objc-file.mm b/runtime/objc-file.mm
new file mode 100644 (file)
index 0000000..e334764
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * 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
index 231619bb4abb1da96b865a91a5ec5b320acea5e0..4ec1f29ff8a328750bac01aab05be9e8804f9ba0 100644 (file)
 
 #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;
@@ -58,12 +62,16 @@ typedef struct objc_trampoline_header {
     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.
@@ -76,15 +84,34 @@ extern void gdb_objc_trampolines_changed(objc_trampoline_header *thdr);
 // 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);
 
 
 /***********************************************************************
@@ -94,24 +121,34 @@ extern void gdb_objc_debuggerModeFailure(void);
 #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
index a911c40f9bfa24993999249d0d3319794684b35f..9d750919a9665d50f1214a46938055595e580cc7 100644 (file)
@@ -129,7 +129,7 @@ static _objc_initializing_classes *_fetchInitializingClassList(BOOL create)
     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) {
@@ -160,7 +160,7 @@ static _objc_initializing_classes *_fetchInitializingClassList(BOOL create)
 * 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) {
@@ -348,7 +348,7 @@ static void _finishInitializingAfter(Class cls, Class supercls)
 *
 * 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;
index 97e0028bcc565cc489c689c4d7082640ce5e4957..c960d3eae09285b3cfd2070b20c6db83d5e6d632 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
index 9fe6bd87f8c10e353ed04288a1268b1ff4bf78ea..35541682594b5184fd7bab552fca0975d8db16b1 100644 (file)
@@ -74,13 +74,13 @@ compress_layout(const uint8_t *bits, size_t bitmap_bits, BOOL weak)
 
         // 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++;
@@ -95,7 +95,7 @@ compress_layout(const uint8_t *bits, size_t bitmap_bits, BOOL weak)
             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;
@@ -151,15 +151,34 @@ static void move_bits(layout_bitmap bits, size_t src, size_t dst,
                       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));
+            }
         }
     }
 }
@@ -192,7 +211,7 @@ static void decompress_layout(const unsigned char *layout_string, layout_bitmap
 *   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)
@@ -220,20 +239,60 @@ layout_bitmap_create(const unsigned char *layout_string,
     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
@@ -243,6 +302,7 @@ layout_bitmap_set_ivar(layout_bitmap bits, const char *type, size_t offset)
     if (type[0] == '@'  ||  0 == strcmp(type, "^@")) {
         // id
         // id *
+        // Block ("@?")
         set_bits(bits, bit, 1);
     } 
     else if (type[0] == '[') {
@@ -265,7 +325,7 @@ layout_bitmap_set_ivar(layout_bitmap bits, const char *type, size_t offset)
 * 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;
@@ -289,7 +349,7 @@ layout_bitmap_grow(layout_bitmap *bits, size_t newCount)
 * 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;
@@ -306,6 +366,32 @@ layout_bitmap_slide(layout_bitmap *bits, size_t oldPos, size_t newPos)
 }
 
 
+/***********************************************************************
+* 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.
@@ -313,7 +399,7 @@ layout_bitmap_slide(layout_bitmap *bits, size_t oldPos, size_t newPos)
 * 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)
 {
@@ -352,7 +438,7 @@ layout_bitmap_splat(layout_bitmap dst, layout_bitmap src,
 * 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;
@@ -383,7 +469,7 @@ layout_bitmap_or(layout_bitmap dst, layout_bitmap src, const char *msg)
 * 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;
@@ -408,19 +494,18 @@ layout_bitmap_clear(layout_bitmap dst, layout_bitmap src, const char *msg)
 }
 
 
-__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.
 
index 5488555485bb1c3f97268a1b1c62e91b063bdd65..3e00a038a71d9549239a56157a77df74604fd56d 100644 (file)
  */
 
 #include "objc-private.h"
+#include "objc-load.h"
 
 #if !__OBJC2__  &&  !TARGET_OS_WIN32
 
-extern void (*callbackFunction)( Class, const char * );
+extern void (*callbackFunction)( Class, Category );
 
 
 /**********************************************************************************
@@ -52,7 +53,7 @@ extern void (*callbackFunction)( Class, const char * );
 * 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;
@@ -107,7 +108,7 @@ int objc_loadModule(const char *moduleName, void (*class_callback) (Class, const
 
 long   objc_loadModules   (char *                      modlist[],
                          void *                        errStream,
-                         void                  (*class_callback) (Class, const char *),
+                         void                  (*class_callback) (Class, Category),
                          headerType ** hdr_addr,
                          char *                        debug_file)
 {
index d6ab45d567b43a9d7f461c3f02a3f944982ee3f0..f1f2b9c1b99daf41f01b76f788c2650abd482190 100644 (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);
@@ -35,3 +40,6 @@ extern void remove_category_from_loadable_list(Category cat);
 
 extern void call_load_methods(void);
 
+__END_DECLS
+
+#endif
index 68fc2abdd9fd9da5386b4d1443f9a6fea7f57c40..e62e35c310ecb8bd17545144f62b343e5ea96760 100644 (file)
@@ -42,14 +42,14 @@ struct loadable_category {
 
 // 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;
 
 
 /***********************************************************************
@@ -57,7 +57,7 @@ static int loadable_categories_allocated NOBSS = 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;
 
@@ -90,7 +90,7 @@ __private_extern__ void add_class_to_loadable_list(Class cls)
 * 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;
 
@@ -125,7 +125,7 @@ __private_extern__ void add_category_to_loadable_list(Category cat)
 * 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);
 
@@ -149,7 +149,7 @@ __private_extern__ void remove_class_from_loadable_list(Class cls)
 * 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);
 
@@ -329,7 +329,7 @@ static BOOL call_category_loads(void)
 * 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;
index ef407928f91953c476fced4243cafcde87fa44f3..e6cae1746505f7373a77cdc5aadef7c2387ffdc8 100644 (file)
@@ -52,16 +52,26 @@ typedef struct _objc_lock_list {
     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;
@@ -69,7 +79,7 @@ getLocks(BOOL create)
             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);
         }
     }
 
@@ -77,12 +87,13 @@ getLocks(BOOL create)
         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);
         }
     }
 
@@ -136,19 +147,12 @@ clearLock(_objc_lock_list *locks, void *lock, int kind)
     _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);
@@ -163,7 +167,7 @@ _mutex_lock_debug(mutex_t *lock, const char *name)
     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);
@@ -179,7 +183,7 @@ _mutex_try_lock_debug(mutex_t *lock, const char *name)
     return result;
 }
 
-__private_extern__ int 
+PRIVATE_EXTERN int 
 _mutex_unlock_debug(mutex_t *lock, const char *name)
 {
     _objc_lock_list *locks = getLocks(NO);
@@ -194,7 +198,7 @@ _mutex_unlock_debug(mutex_t *lock, const char *name)
     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);
@@ -207,7 +211,7 @@ _mutex_assert_locked_debug(mutex_t *lock, const char *name)
 }
 
 
-__private_extern__ void 
+PRIVATE_EXTERN void 
 _mutex_assert_unlocked_debug(mutex_t *lock, const char *name)
 {
     _objc_lock_list *locks = getLocks(NO);
@@ -224,7 +228,7 @@ _mutex_assert_unlocked_debug(mutex_t *lock, const char *name)
 * 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);
@@ -236,7 +240,7 @@ _recursive_mutex_lock_debug(recursive_mutex_t *lock, const char *name)
     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);
@@ -251,7 +255,7 @@ _recursive_mutex_try_lock_debug(recursive_mutex_t *lock, const char *name)
     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);
@@ -266,7 +270,7 @@ _recursive_mutex_unlock_debug(recursive_mutex_t *lock, const char *name)
     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);
@@ -279,7 +283,7 @@ _recursive_mutex_assert_locked_debug(recursive_mutex_t *lock, const char *name)
 }
 
 
-__private_extern__ void 
+PRIVATE_EXTERN void 
 _recursive_mutex_assert_unlocked_debug(recursive_mutex_t *lock, const char *name)
 {
     _objc_lock_list *locks = getLocks(NO);
@@ -296,7 +300,7 @@ _recursive_mutex_assert_unlocked_debug(recursive_mutex_t *lock, const char *name
 * Monitor checking
 **********************************************************************/
 
-__private_extern__ int 
+PRIVATE_EXTERN int 
 _monitor_enter_debug(monitor_t *lock, const char *name)
 {
     _objc_lock_list *locks = getLocks(YES);
@@ -311,7 +315,7 @@ _monitor_enter_debug(monitor_t *lock, const char *name)
     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);
@@ -326,7 +330,7 @@ _monitor_exit_debug(monitor_t *lock, const char *name)
     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);
@@ -340,7 +344,7 @@ _monitor_wait_debug(monitor_t *lock, const char *name)
     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);
@@ -352,7 +356,7 @@ _monitor_assert_locked_debug(monitor_t *lock, const char *name)
     }
 }
 
-__private_extern__ void 
+PRIVATE_EXTERN void 
 _monitor_assert_unlocked_debug(monitor_t *lock, const char *name)
 {
     _objc_lock_list *locks = getLocks(NO);
@@ -369,7 +373,7 @@ _monitor_assert_unlocked_debug(monitor_t *lock, const char *name)
 * rwlock checking
 **********************************************************************/
 
-__private_extern__ void
+PRIVATE_EXTERN void
 _rwlock_read_debug(rwlock_t *lock, const char *name)
 {
     _objc_lock_list *locks = getLocks(YES);
@@ -388,7 +392,7 @@ _rwlock_read_debug(rwlock_t *lock, const char *name)
     _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);
@@ -405,7 +409,7 @@ _rwlock_try_read_debug(rwlock_t *lock, const char *name)
     return result;
 }
 
-__private_extern__ void 
+PRIVATE_EXTERN void 
 _rwlock_unlock_read_debug(rwlock_t *lock, const char *name)
 {
     _objc_lock_list *locks = getLocks(NO);
@@ -420,7 +424,7 @@ _rwlock_unlock_read_debug(rwlock_t *lock, const char *name)
     _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);
@@ -440,7 +444,7 @@ _rwlock_write_debug(rwlock_t *lock, const char *name)
 }
 
 
-__private_extern__ int 
+PRIVATE_EXTERN int 
 _rwlock_try_write_debug(rwlock_t *lock, const char *name)
 {
     _objc_lock_list *locks = getLocks(YES);
@@ -457,7 +461,7 @@ _rwlock_try_write_debug(rwlock_t *lock, const char *name)
     return result;
 }
 
-__private_extern__ void 
+PRIVATE_EXTERN void 
 _rwlock_unlock_write_debug(rwlock_t *lock, const char *name)
 {
     _objc_lock_list *locks = getLocks(NO);
@@ -473,7 +477,7 @@ _rwlock_unlock_write_debug(rwlock_t *lock, const char *name)
 }
 
 
-__private_extern__ void 
+PRIVATE_EXTERN void 
 _rwlock_assert_reading_debug(rwlock_t *lock, const char *name)
 {
     _objc_lock_list *locks = getLocks(NO);
@@ -485,7 +489,7 @@ _rwlock_assert_reading_debug(rwlock_t *lock, const char *name)
     }
 }
 
-__private_extern__ void 
+PRIVATE_EXTERN void 
 _rwlock_assert_writing_debug(rwlock_t *lock, const char *name)
 {
     _objc_lock_list *locks = getLocks(NO);
@@ -497,7 +501,7 @@ _rwlock_assert_writing_debug(rwlock_t *lock, const char *name)
     }
 }
 
-__private_extern__ void 
+PRIVATE_EXTERN void 
 _rwlock_assert_locked_debug(rwlock_t *lock, const char *name)
 {
     _objc_lock_list *locks = getLocks(NO);
@@ -510,7 +514,7 @@ _rwlock_assert_locked_debug(rwlock_t *lock, const char *name)
     }
 }
 
-__private_extern__ void 
+PRIVATE_EXTERN void 
 _rwlock_assert_unlocked_debug(rwlock_t *lock, const char *name)
 {
     _objc_lock_list *locks = getLocks(NO);
index e2f5613442de6693eacbd2b9de5ed737fbbffa1d..f6b2bf64e9f2c49bfcf475b4455f8abe3384e1bc 100644 (file)
 
 #if TARGET_OS_MAC
 
+#   ifndef __STDC_LIMIT_MACROS
+#       define __STDC_LIMIT_MACROS
+#   endif
+
 #   include <stdio.h>
 #   include <stdlib.h>
 #   include <stdint.h>
@@ -49,7 +53,7 @@
 #   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)
@@ -133,7 +210,7 @@ extern void _objc_fatal(const char *fmt, ...) __attribute__((noreturn, format (p
         if (var) break;                                                 \
         typeof(var) v = create;                                         \
         while (!var) {                                                  \
-            if (OSAtomicCompareAndSwapPtrBarrier(0, v, (void**)&var)) { \
+            if (OSAtomicCompareAndSwapPtrBarrier(0, (void*)v, (void**)&var)){ \
                 goto done;                                              \
             }                                                           \
         }                                                               \
@@ -158,13 +235,16 @@ extern void _objc_fatal(const char *fmt, ...) __attribute__((noreturn, format (p
 // 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
 
 
@@ -249,10 +329,12 @@ typedef struct {
     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); 
@@ -427,6 +509,7 @@ typedef struct {
     size_t selrefCount;
     struct objc_class **clsrefs;
     size_t clsrefCount;    
+    TCHAR *moduleName;
 } os_header_info;
 
 typedef IMAGE_DOS_HEADER headerType;
@@ -442,6 +525,16 @@ OBJC_EXTERN IMAGE_DOS_HEADER __ImageBase;
 
 
 // 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
@@ -462,8 +555,10 @@ static __inline objc_thread_t thread_self(void) {
 
 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); 
@@ -472,7 +567,7 @@ static inline void tls_set(tls_key_t k, void *value) {
     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  ||
@@ -816,9 +911,6 @@ typedef struct section_64 sectionType;
 
 typedef struct {
     Dl_info             dl_info;
-    const segmentType * objcSegmentHeader;
-    const segmentType * dataSegmentHeader;
-    ptrdiff_t           image_slide;
 #if !__OBJC2__
     struct old_protocol **proto_refs;
 #endif
index 5993e9d23efc7178101623a46e2648e539bb2328..4a8aa4dd5ae6fb6cd340c4c1f111dc542327c677 100644 (file)
 * 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) 
@@ -154,6 +154,9 @@ OBJC_EXPORT void *_objc_init_image(HMODULE image, const objc_sections *sects)
             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);
 
@@ -181,16 +184,26 @@ OBJC_EXPORT void _objc_unload_image(HMODULE image, header_info *hinfo)
 }
 
 
+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;
@@ -232,46 +245,20 @@ __private_extern__ void recursive_mutex_init(recursive_mutex_t *m)
 * 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;
 
@@ -281,10 +268,8 @@ static header_info * _objc_addHeader(const headerType *mhdr)
     }
 
     // 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.
@@ -292,11 +277,8 @@ static header_info * _objc_addHeader(const headerType *mhdr)
 
     // 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
@@ -338,22 +320,52 @@ static header_info * _objc_addHeader(const headerType *mhdr)
 }
 
 
-#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 "";
 }
 
 
@@ -363,25 +375,27 @@ __private_extern__ const char *_gcForHInfo2(const header_info *hinfo)
 * 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));
@@ -389,7 +403,6 @@ static BOOL check_wants_gc(void)
             }
         }
     }
-    return appWantsGC;
 }
 
 
@@ -398,8 +411,8 @@ static BOOL check_wants_gc(void)
 * 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;
@@ -430,6 +443,18 @@ static void verify_gc_readiness(BOOL wantsGC, header_info **hList,
                  _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", 
@@ -469,6 +494,8 @@ static const char *gc_enforcer(enum dyld_image_states state,
     }
 
     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;
 
@@ -481,12 +508,13 @@ static const char *gc_enforcer(enum dyld_image_states state,
         }
 
 #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) {
@@ -497,12 +525,12 @@ static const char *gc_enforcer(enum dyld_image_states state,
                 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;
@@ -514,21 +542,26 @@ static const char *gc_enforcer(enum dyld_image_states state,
             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
 
 
@@ -543,12 +576,13 @@ static const char *gc_enforcer(enum dyld_image_states state,
 *
 * 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];
@@ -558,7 +592,7 @@ map_images_nolock(enum dyld_image_states state, uint32_t 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;
@@ -602,40 +636,47 @@ map_images_nolock(enum dyld_image_states state, uint32_t infoCount,
     // 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);
@@ -653,7 +694,7 @@ map_images_nolock(enum dyld_image_states state, uint32_t infoCount,
 *
 * 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[])
 {
@@ -684,8 +725,8 @@ load_images_nolock(enum dyld_image_states state,uint32_t infoCount,
 * 
 * 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");
@@ -710,16 +751,16 @@ unmap_image_nolock(const struct mach_header *mh, intptr_t vmaddr_slide)
                      _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
 
@@ -743,7 +784,7 @@ void _objc_init(void)
     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,
@@ -758,27 +799,24 @@ void _objc_init(void)
 **********************************************************************/
 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;
     }
 
@@ -792,7 +830,7 @@ static const header_info *_headerForAddress(void *addr)
 * 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);
 }
@@ -809,7 +847,7 @@ __private_extern__ const header_info *_headerForClass(Class 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;
@@ -888,7 +926,7 @@ __private_extern__ int secure_open(const char *filename, int flags, uid_t euid)
 * 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) {
@@ -903,6 +941,54 @@ __private_extern__ malloc_zone_t *_objc_internal_zone(void)
 }
 
 
+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
 
index fd63f76c318b2c8eb4ed4eaf251083e0324835f5..837d4e1f4c0ffea1f2797ed7f16fc88a0c4d18b0 100644 (file)
 #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)
@@ -80,127 +92,13 @@ __BEGIN_DECLS
 #   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;
@@ -211,6 +109,7 @@ typedef struct {
 #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))
@@ -230,8 +129,10 @@ typedef struct {
 
 #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
@@ -264,33 +165,9 @@ extern void _objc_appendHeader(header_info *hi);
 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 */
@@ -299,7 +176,7 @@ extern SEL sel_registerNameNoLock(const char *str, BOOL copy);
 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;
@@ -310,8 +187,12 @@ extern SEL SEL_cxx_destruct;
 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 */
@@ -321,23 +202,13 @@ extern void *_calloc_internal(size_t count, size_t size);
 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);
@@ -347,34 +218,33 @@ extern IMP _cache_getImp(Class cls, SEL sel);
 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. */
@@ -398,8 +268,6 @@ extern void endDebuggerMode(void);
 
 #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)
@@ -431,9 +299,6 @@ extern void endDebuggerMode(void);
 
 #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);
@@ -514,8 +379,71 @@ extern id _objc_getNilReceiver(void);
 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 */
@@ -534,12 +462,19 @@ extern void gdb_objc_class_changed(Class cls, unsigned long changes, const char
 /* 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);
@@ -547,9 +482,13 @@ 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.
 
@@ -565,30 +504,14 @@ extern size_t objc_branch_size(void *entry, void *target);
 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
@@ -608,35 +531,31 @@ ENV(PrintGC);                   // env OBJC_PRINT_GC
 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 (;;) {
@@ -651,7 +570,6 @@ static __inline uint32_t _objc_strhash(const char *s) {
 // 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
 
@@ -664,21 +582,6 @@ extern _objc_pthread_data *_objc_fetch_pthread_data(BOOL create);
 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
 
@@ -689,31 +592,12 @@ extern void flush_caches(Class cls, BOOL flush_meta);
 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);
@@ -726,6 +610,12 @@ extern char *encoding_copyArgumentType(const char *t, unsigned int index);
 // 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;
@@ -734,11 +624,13 @@ typedef struct {
     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);
@@ -753,7 +645,7 @@ extern const char *map_images_nolock(enum dyld_image_states state, uint32_t info
 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);
@@ -762,15 +654,8 @@ extern const char ** _objc_copyClassNamesForImage(header_info *hi, unsigned int
 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);
@@ -790,100 +675,85 @@ extern Method _class_getMethodNoSuper_nolock(Class cls, SEL sel);
 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_ */
 
index 1e59310f7251ef7cfe5a9f74f142c5f421080ebc..cf259a61b2f4d2891763abd2f4ec7d0255c718a5 100644 (file)
@@ -191,7 +191,7 @@ enum {
     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;
     {
@@ -237,7 +237,7 @@ struct ReleaseValue {
     }
 };
 
-__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;
@@ -265,7 +265,7 @@ __private_extern__ void _object_set_associative_reference(id object, void *key,
                 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.
@@ -286,8 +286,8 @@ __private_extern__ void _object_set_associative_reference(id object, void *key,
     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());
diff --git a/runtime/objc-rtp.h b/runtime/objc-rtp.h
deleted file mode 100644 (file)
index f433993..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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
index c903eb5a3ee97e3d9757bed39fc939ee0f700606..2fa1b6c6e3056cf7967c436486fe5b5889d1a9dd 100644 (file)
  * 
  * @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__)
 
 
 /**********************************************************************
@@ -317,17 +51,19 @@ static void rtp_swap_imp(unsigned *address, void *code, const char *name)
 }
 
 
-__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");
     }
@@ -344,7 +80,7 @@ __private_extern__ void rtp_init(void)
 #else
 
 
-__private_extern__ void rtp_init(void)
+PRIVATE_EXTERN void rtp_init(void)
 {
     if (PrintRTP) {
         _objc_inform("RTP: no rtp implementation for this platform");
index 1f84e36dfc79a07287b401620483d3fee13e1725..64e8736f6a27c1bf728c58b46806c789f17b8414 100644 (file)
  * @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 {
@@ -94,9 +206,20 @@ 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 {
@@ -107,7 +230,7 @@ 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 {
@@ -132,7 +255,7 @@ typedef struct class_ro_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 {
@@ -141,20 +264,63 @@ 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 {
@@ -163,10 +329,20 @@ 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
diff --git a/runtime/objc-runtime-new.m b/runtime/objc-runtime-new.m
deleted file mode 100644 (file)
index de8d4fc..0000000
+++ /dev/null
@@ -1,5393 +0,0 @@
-/*
- * 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
diff --git a/runtime/objc-runtime-new.mm b/runtime/objc-runtime-new.mm
new file mode 100644 (file)
index 0000000..296df91
--- /dev/null
@@ -0,0 +1,6401 @@
+/*
+ * 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
diff --git a/runtime/objc-runtime-old.h b/runtime/objc-runtime-old.h
new file mode 100644 (file)
index 0000000..1523b3e
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * 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
index 537456145b90b5e37885b70b6253e8cfa2ea4e63..f0ac76d4ab9a1aff6084f72a097542d86585ae70 100644 (file)
 
 #if !__OBJC2__
 
-#define OLD 1
 #include "objc-private.h"
+#include "objc-runtime-old.h"
 #include "objc-loadmethod.h"
 
 /* NXHashTable SPI */
@@ -193,10 +193,10 @@ static BOOL _objc_register_category(struct old_category *cat, int version);
 
 
 // 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,
@@ -205,7 +205,7 @@ static NXHashTablePrototype classHashPrototype =
 };
 
 // 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;
@@ -235,7 +235,7 @@ static BOOL (*_objc_classLoader)(const char *) = 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;
@@ -254,7 +254,7 @@ __private_extern__ void objc_dump_class_hash(void)
 * _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)
@@ -306,12 +306,49 @@ int objc_getClassList(Class *buffer, int bufferLen)
 }
 
 
+/***********************************************************************
+* 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;
@@ -385,9 +422,7 @@ static uintptr_t classHash(void *info, Class data)
 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;
 }
 
 
@@ -481,7 +516,7 @@ static void makeFutureClass(struct old_class *cls, const char *name)
 * 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;
 
@@ -511,7 +546,7 @@ void objc_setFutureClass(Class cls, const char *name)
     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));
@@ -593,7 +628,7 @@ Protocol *objc_getProtocol(const char *name)
 * 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;
@@ -661,9 +696,9 @@ static BOOL class_is_connected(struct old_class *cls)
 * 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));
 }
 
 
@@ -853,7 +888,7 @@ static void really_connect_class(struct old_class *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: 
@@ -1026,7 +1061,7 @@ static BOOL connect_class(struct old_class *cls)
         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);
@@ -1275,7 +1310,7 @@ static void _objc_connect_classes_from_image(header_info *hi)
                     // 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);
@@ -1287,7 +1322,7 @@ static void _objc_connect_classes_from_image(header_info *hi)
                 // 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 {
@@ -1315,7 +1350,7 @@ static void fix_class_ref(struct old_class **ref, const char *name, BOOL isMeta)
     // 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;
@@ -1499,7 +1534,7 @@ lookup_method(struct objc_method_description_list *mlist, SEL aSel)
 * 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)
 {
@@ -1620,7 +1655,7 @@ protocol_copyMethodDescriptionList(Protocol *p,
 }
 
 
-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);
@@ -1635,13 +1670,13 @@ Property protocol_getProperty(Protocol *p, const char *name,
     }
 
     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;
                 }
             }
         }
@@ -1650,7 +1685,7 @@ Property protocol_getProperty(Protocol *p, const char *name,
     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;
@@ -1661,11 +1696,11 @@ Property protocol_getProperty(Protocol *p, const char *name,
 }
 
 
-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))) {
@@ -1676,7 +1711,7 @@ Property *protocol_copyPropertyList(Protocol *p, unsigned int *outCount)
     plist = ext->instance_properties;
     result = copyPropertyList(plist, outCount);
     
-    return result;
+    return (objc_property_t *)result;
 }
 
 
@@ -1685,7 +1720,8 @@ Property *protocol_copyPropertyList(Protocol *p, unsigned int *outCount)
 * 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;
@@ -1756,6 +1792,253 @@ BOOL protocol_isEqual(Protocol *self, Protocol *other)
 }
 
 
+/***********************************************************************
+* 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.
@@ -1890,17 +2173,18 @@ static inline BOOL _is_threaded() {
 #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);
 }
 
@@ -1910,7 +2194,7 @@ unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide)
 * 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[])
 {
@@ -1931,7 +2215,7 @@ map_images(enum dyld_image_states state, uint32_t infoCount,
 *
 * 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[])
 {
@@ -1951,13 +2235,14 @@ load_images(enum dyld_image_states state, uint32_t infoCount,
 
     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;
@@ -2025,7 +2310,7 @@ static void schedule_class_load(struct old_class *cls)
     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;
@@ -2090,7 +2375,7 @@ __private_extern__ void prepare_load_methods(header_info *hi)
 
 #if TARGET_OS_WIN32
 
-__private_extern__ void unload_class(struct old_class *cls)
+PRIVATE_EXTERN void unload_class(struct old_class *cls)
 {
 }
 
@@ -2127,7 +2412,7 @@ static void rependClassReferences(struct old_class **refs, size_t count,
 }
 
 
-static void try_free(const void *p)
+PRIVATE_EXTERN void try_free(const void *p)
 {
     if (p  &&  malloc_size(p)) free((void *)p);
 }
@@ -2142,8 +2427,23 @@ static void unload_mlist(struct old_method_list *mlist)
     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
@@ -2196,15 +2496,17 @@ __private_extern__ void unload_class(struct old_class *cls)
                 // 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);
                 }
@@ -2288,8 +2590,9 @@ static void _objc_remove_classes_in_image(header_info *hi)
     // 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) {
@@ -2366,8 +2669,9 @@ static void _objc_remove_categories_in_image(header_info *hi)
 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));
@@ -2439,7 +2743,7 @@ static void unload_paranoia(header_info *hi)
 * 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);
 
@@ -2464,7 +2768,7 @@ __private_extern__ void _unload_image(header_info *hi)
 **********************************************************************/
 void           objc_addClass           (Class cls_gen)
 {
-    struct old_class *cls = _class_asOld(cls_gen);
+    struct old_class *cls = oldcls(cls_gen);
 
     OBJC_WARN_DEPRECATED;
 
@@ -2546,7 +2850,7 @@ static void _objcTweakMethodListPointerForClass(struct old_class *cls)
 * 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)
 {
@@ -2626,7 +2930,7 @@ __private_extern__ void _objc_insertMethods(struct old_class *cls,
 * 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;
@@ -2819,7 +3123,7 @@ static void resolve_categories_for_class(struct old_class *cls)
 **********************************************************************/
 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.
@@ -2828,9 +3132,9 @@ void _objc_resolve_categories_for_class(Class cls_gen)
             // 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));
         }
     }
 
@@ -2855,7 +3159,7 @@ static BOOL _objc_register_category(struct old_category *cat, int version)
     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);
     }
     
@@ -2863,7 +3167,7 @@ static BOOL _objc_register_category(struct old_category *cat, int 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;
     }
@@ -2898,7 +3202,7 @@ static BOOL _objc_register_category(struct old_category *cat, int version)
 }
 
 
-__private_extern__ const char **
+PRIVATE_EXTERN const char **
 _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount)
 {
     Module mods;
@@ -2945,6 +3249,21 @@ _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount)
     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)) {
@@ -2971,24 +3290,24 @@ BOOL gdb_objc_isRuntimeLocked()
 * 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
@@ -3000,7 +3319,7 @@ __private_extern__ void lock_init(void)
 *   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;
 
@@ -3047,7 +3366,7 @@ __private_extern__ int startDebuggerMode(void)
 * endDebuggerMode
 * Relinquish locks acquired in startDebuggerMode().
 **********************************************************************/
-__private_extern__ void endDebuggerMode(void)
+PRIVATE_EXTERN void endDebuggerMode(void)
 {
     if (debugger_loadMethodLock) {
         recursive_mutex_unlock(&loadMethodLock);
@@ -3065,7 +3384,7 @@ __private_extern__ void endDebuggerMode(void)
 * 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;
@@ -3081,7 +3400,7 @@ __private_extern__ BOOL isManagedDuringDebugger(void *lock)
 * 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);
 
@@ -3099,7 +3418,7 @@ __private_extern__ BOOL isLockedDuringDebugger(mutex_t *lock)
 * 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);
     
@@ -3115,7 +3434,7 @@ __private_extern__ BOOL isReadingDuringDebugger(rwlock_t *lock)
 * 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);
     
@@ -3124,7 +3443,7 @@ __private_extern__ BOOL isWritingDuringDebugger(rwlock_t *lock)
     return NO;
 }
 
-// !defined(NO_DEBUGGER_MODE)
+// SUPPORT_DEBUGGER_MODE
 #endif
 
 #endif
index 46c785d75e91376f47eabba15bfa043bf487b644..d933e86ed1ee3d5f11eda924b43d9b2668441f01 100644 (file)
@@ -35,6 +35,7 @@
 
 #include "objc-private.h"
 #include "objc-loadmethod.h"
+#include "message.h"
 
 OBJC_EXPORT Class getOriginalClassForPosingClass(Class);
 
@@ -44,37 +45,41 @@ 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
 
 
@@ -82,21 +87,25 @@ __private_extern__ int DebugFinalizers = -1; // env OBJC_DEBUG_FINALIZERS
 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;
 
 
 
@@ -196,7 +205,7 @@ id objc_getMetaClass(const char *aClassName)
 /***********************************************************************
 * _nameForHeader.
 **********************************************************************/
-__private_extern__ const char *_nameForHeader(const headerType *header)
+PRIVATE_EXTERN const char *_nameForHeader(const headerType *header)
 {
     return _getObjcHeaderName ((headerType *) header);
 }
@@ -205,7 +214,7 @@ __private_extern__ const char *_nameForHeader(const 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.
@@ -234,7 +243,7 @@ __private_extern__ void _objc_appendHeader(header_info *hi)
 * 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;
 
@@ -262,9 +271,9 @@ __private_extern__ void _objc_removeHeader(header_info *hi)
 * 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();
@@ -332,12 +341,16 @@ __private_extern__ void environ_init(void)
            "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");
@@ -347,6 +360,10 @@ __private_extern__ void environ_init(void)
            "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");
@@ -367,7 +384,7 @@ __private_extern__ void environ_init(void)
 * 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)
@@ -412,7 +429,7 @@ void objc_setMultithreaded (BOOL flag)
 * 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;
 
@@ -432,12 +449,11 @@ __private_extern__ _objc_pthread_data *_objc_fetch_pthread_data(BOOL create)
 * 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);
 
@@ -448,13 +464,13 @@ __private_extern__ void _objc_pthread_destroyspecific(void *arg)
 }
 
 
-__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
 }
 
@@ -471,7 +487,7 @@ void _objcInit(void)
 }
 
 
-#if !TARGET_OS_WIN32
+#if !(TARGET_OS_WIN32  ||  TARGET_OS_EMBEDDED  ||  TARGET_OS_IPHONE)
 /***********************************************************************
 * _objc_setNilReceiver
 **********************************************************************/
@@ -505,167 +521,6 @@ void objc_setForwardHandler(void *fwd, void *fwd_stret)
 }
 
 
-#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__)
 
 /**********************************************************************
@@ -673,12 +528,12 @@ objc_write_cond_branch(void *entry, void *target, unsigned cond)
 * 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.
@@ -692,12 +547,12 @@ objc_cond_branch_size(void *entry, void *target, unsigned cond)
 * 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
@@ -743,13 +598,13 @@ const char *class_getImageName(Class cls)
 #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) {
@@ -768,20 +623,17 @@ const char **objc_copyImageNames(unsigned int *outCount)
     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;
@@ -817,7 +669,7 @@ objc_copyClassNamesForImage(const char *image, unsigned int *outCount)
     // 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
@@ -859,6 +711,75 @@ void objc_setEnumerationMutationHandler(void (*handler)(id)) {
 }
 
 
+/**********************************************************************
+* 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
 *
@@ -878,10 +799,10 @@ void objc_setEnumerationMutationHandler(void (*handler)(id)) {
 * 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;
 
 /**********************************************************************
@@ -958,5 +879,5 @@ void gdb_objc_debuggerModeFailure(void)
     _objc_fatal("DEBUGGER MODE: failed");
 }
 
-// !defined(NO_DEBUGGER_MODE)
+// SUPPORT_DEBUGGER_MODE
 #endif
index 38ef37026dd92a3e0f316e12ec79f5f5a872e203..d24b30639ab7ef09e01a00cbfe1c9052e1a5b2c2 100644 (file)
@@ -34,7 +34,7 @@
 #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))
@@ -95,7 +95,7 @@ static struct __objc_sel_set_finds __objc_sel_set_findBuckets(struct __objc_sel_
         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++;
@@ -106,7 +106,7 @@ static struct __objc_sel_set_finds __objc_sel_set_findBuckets(struct __objc_sel_
 }
 
 // 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));
@@ -123,12 +123,12 @@ __private_extern__ struct __objc_sel_set *__objc_sel_set_create(uint32_t capacit
 }
 
 // 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;
index 997745d8ae579a655b34234fb89181827ed37b5c..38524055d11f980b13caf48358e20936d40e64de 100644 (file)
@@ -1,21 +1,21 @@
 /* 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
index 4b5e207c3166457f539191c0f9ced05652fe5378..8e010c30801ecc2adb3cb131f793ed4d3cc06518 100644 (file)
 #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
 
 
@@ -58,64 +62,35 @@ static const char *_objc_empty_selector = "";
 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
 
@@ -127,13 +102,13 @@ static SEL _objc_search_builtins(const char *key)
 #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
 
@@ -142,7 +117,7 @@ static SEL _objc_search_builtins(const char *key)
 
 
 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>";
@@ -154,7 +129,9 @@ BOOL sel_isMapped(SEL name)
     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;
@@ -213,16 +190,16 @@ SEL sel_registerName(const char *name) {
     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);
 }
@@ -248,25 +225,25 @@ BOOL sel_isEqual(SEL lhs, SEL rhs)
 * 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
@@ -277,55 +254,65 @@ __private_extern__ BOOL sel_preoptimizationValid(const header_info *hi)
 * 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
@@ -350,8 +337,12 @@ __private_extern__ void sel_init(BOOL wantsGC)
     s(retain);
     s(release);
     s(autorelease);
+    s(retainCount);
+    s(alloc);
     s(copy);
+    s(new);
     s(finalize);
+    t(forwardInvocation:, forwardInvocation);
 
     sel_unlock();
 
index ca8ad34942e6918c8cec97c44eac0a39a98a02f0..9bb56374c0ca236d2b23daf93d06e3fd96c5f92b 100644 (file)
@@ -87,11 +87,21 @@ Source is http://burtleburtle.net/bob/c/perfect.c
   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
 
@@ -126,26 +136,27 @@ static perfect_hash make_perfect(const string_map& strings);
 
 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);
@@ -153,27 +164,82 @@ struct objc_selopt_t {
         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       \
+}
 
 
 /*
@@ -316,7 +382,10 @@ write_selopt(void *dst, uint64_t base, size_t dstSize,
         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)";
     }
@@ -325,14 +394,13 @@ write_selopt(void *dst, uint64_t base, size_t dstSize,
     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++) {
@@ -347,8 +415,12 @@ write_selopt(void *dst, uint64_t base, size_t dstSize,
         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) {
@@ -372,14 +444,12 @@ write_selopt(void *dst, uint64_t base, size_t dstSize,
         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
 
@@ -774,7 +844,7 @@ static int perfect(bstuff *tabb, hstuff *tabh, qstuff *tabq, ub4 blen, ub4 smax,
   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
 
index 5afd2f3b7b309b9f448a629312b1792033a46856..169c468298c55eb252502f087097c28c3fef19e3 100644 (file)
 // 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,
index 2c0456dfc67365d847931db6c20111c2cffef11d..5b8bf056b26f0a44fb773a601758f49fa180a1df 100644 (file)
@@ -79,7 +79,7 @@ static SyncCache *fetch_cache(BOOL create)
     _objc_pthread_data *data;
     
     data = _objc_fetch_pthread_data(create);
-    if (!data  &&  !create) return NULL;
+    if (!data) return NULL;
 
     if (!data->syncCache) {
         if (!create) {
@@ -104,7 +104,7 @@ static SyncCache *fetch_cache(BOOL create)
 }
 
 
-__private_extern__ void _destroySyncCache(struct SyncCache *cache)
+PRIVATE_EXTERN void _destroySyncCache(struct SyncCache *cache)
 {
     if (cache) free(cache);
 }
@@ -116,7 +116,7 @@ static SyncData* id2data(id object, enum usage why)
     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);
@@ -259,7 +259,7 @@ static SyncData* id2data(id object, enum usage why)
         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);
@@ -280,11 +280,9 @@ static SyncData* id2data(id object, enum usage why)
 }
 
 
-__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'. 
@@ -305,7 +303,7 @@ int objc_sync_enter(id 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: 
@@ -336,22 +334,3 @@ 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
index fdd1bd6a280b4a16203831c944143aeff50b678d..4ba8eedfc6f9996b8b83221bd173cacb48d0e772 100644 (file)
@@ -77,6 +77,10 @@ static const char *  SkipFirstType      (const char *        type)
             case '^':  /* pointers */
                 break;
 
+            case '@':   /* objects */
+                if (type[0] == '?') type++;  /* Blocks */
+                return type;
+
                 /* arrays */
             case '[':
                 while ((*type >= '0') && (*type <= '9'))
@@ -102,7 +106,7 @@ static const char * SkipFirstType      (const char *        type)
 /***********************************************************************
 * encoding_getNumberOfArguments.
 **********************************************************************/
-__private_extern__ unsigned int 
+PRIVATE_EXTERN unsigned int 
 encoding_getNumberOfArguments(const char *typedesc)
 {
     unsigned nargs;
@@ -140,61 +144,20 @@ encoding_getNumberOfArguments(const char *typedesc)
 /***********************************************************************
 * 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;
 }
@@ -203,7 +166,7 @@ encoding_getSizeOfArguments(const char *typedesc)
 /***********************************************************************
 * encoding_getArgumentInfo.
 **********************************************************************/
-__private_extern__ unsigned int 
+PRIVATE_EXTERN unsigned int 
 encoding_getArgumentInfo(const char *typedesc, int arg,
                          const char **type, int *offset)
 {
@@ -307,7 +270,7 @@ encoding_getArgumentInfo(const char *typedesc, int arg,
 }
 
 
-__private_extern__ void 
+PRIVATE_EXTERN void 
 encoding_getReturnType(const char *t, char *dst, size_t dst_len)
 {
     size_t len;
@@ -329,7 +292,7 @@ encoding_getReturnType(const char *t, char *dst, size_t dst_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;
@@ -347,7 +310,7 @@ encoding_copyReturnType(const char *t)
 }
 
 
-__private_extern__ void 
+PRIVATE_EXTERN void 
 encoding_getArgumentType(const char *t, unsigned int index, 
                          char *dst, size_t dst_len)
 {
@@ -379,7 +342,7 @@ encoding_getArgumentType(const char *t, unsigned int index,
 * 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;
diff --git a/runtime/objc-weak.h b/runtime/objc-weak.h
new file mode 100644 (file)
index 0000000..e40da16
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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
diff --git a/runtime/objc-weak.mm b/runtime/objc-weak.mm
new file mode 100644 (file)
index 0000000..7744330
--- /dev/null
@@ -0,0 +1,535 @@
+/*
+ * 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;
+}
+
index b48f5e3581a430edc7984d7a72f6044d2618746c..455858d97dd1a8608fd66cf27859f567fe378b76 100644 (file)
@@ -29,6 +29,7 @@
 #define _OBJC_OBJC_H_
 
 #include <sys/types.h>      // for __DARWIN_NULL
+#include <Availability.h>
 #include <objc/objc-api.h>
 
 
@@ -57,14 +58,69 @@ typedef signed char         BOOL;
 #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__
@@ -81,9 +137,6 @@ OBJC_EXPORT void *object_getIndexedIvars(id obj);
 #   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)
index af2830174561fa3a8d4a19996190992a8fa0f850..7570e2d54709cf39904543f7c2b47ae647fed17a 100644 (file)
@@ -87,9 +87,10 @@ static int __objc_load(void)
 #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
index c6047bbdb38ed2cd9d2bb7fee2d653d063874070..4ad172865e3ef099846c5fb0c173f43cf20d6de6 100644 (file)
@@ -28,6 +28,7 @@
 #include <stdarg.h>
 #include <stdint.h>
 #include <stddef.h>
+#include <Availability.h>
 #include <AvailabilityMacros.h>
 #include <TargetConditionals.h>
 
@@ -72,195 +73,261 @@ struct objc_method_description {
        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. */
@@ -275,12 +342,21 @@ enum {
 };
 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       '@'
@@ -443,7 +519,7 @@ struct objc_cache {
     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;
@@ -464,9 +540,16 @@ struct objc_method_list;
 
 /* 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;
 
@@ -476,10 +559,13 @@ OBJC_EXPORT void objc_addClass(Class myClass)                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;
 
@@ -497,7 +583,7 @@ OBJC_EXPORT struct objc_method_list *class_nextMethodList(Class, void **) OBJC2_
 // 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;
diff --git a/test/ARRBase.h b/test/ARRBase.h
new file mode 100644 (file)
index 0000000..be8bdf4
--- /dev/null
@@ -0,0 +1,16 @@
+//
+//  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
diff --git a/test/ARRBase.m b/test/ARRBase.m
new file mode 100644 (file)
index 0000000..5fbe357
--- /dev/null
@@ -0,0 +1,24 @@
+//
+//  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
diff --git a/test/ARRLayouts.m b/test/ARRLayouts.m
new file mode 100644 (file)
index 0000000..45c6ac7
--- /dev/null
@@ -0,0 +1,100 @@
+// 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;
+}
diff --git a/test/ARRMRR.h b/test/ARRMRR.h
new file mode 100644 (file)
index 0000000..8029eed
--- /dev/null
@@ -0,0 +1,13 @@
+//
+//  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
diff --git a/test/ARRMRR.m b/test/ARRMRR.m
new file mode 100644 (file)
index 0000000..ce0b7a6
--- /dev/null
@@ -0,0 +1,11 @@
+//
+//  ARRMRR.m
+//
+
+#import "ARRMRR.h"
+
+@implementation ARRMRR
+
+@synthesize dataSource;
+
+@end
diff --git a/test/MRRARR.h b/test/MRRARR.h
new file mode 100644 (file)
index 0000000..275ae2f
--- /dev/null
@@ -0,0 +1,13 @@
+//
+//  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
diff --git a/test/MRRARR.m b/test/MRRARR.m
new file mode 100644 (file)
index 0000000..fa32a34
--- /dev/null
@@ -0,0 +1,11 @@
+//
+//  MRRARR.m
+//
+
+#import "MRRARR.h"
+
+@implementation MRRARR
+
+@synthesize dataSource;
+
+@end
diff --git a/test/MRRBase.h b/test/MRRBase.h
new file mode 100644 (file)
index 0000000..c339f61
--- /dev/null
@@ -0,0 +1,16 @@
+//
+//  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
diff --git a/test/MRRBase.m b/test/MRRBase.m
new file mode 100644 (file)
index 0000000..5720dcc
--- /dev/null
@@ -0,0 +1,24 @@
+//
+//  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
index f2fa04a2c56f4d837fd617561b3085b973db40f7..acf0082768382db793f0d2fbe882f85be4c9adcd 100644 (file)
-# 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
diff --git a/test/README b/test/README
deleted file mode 100644 (file)
index 6d19edc..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-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
index 4d935ee815d30d093dd566845fcaf5ef68a94f5b..3acd7c714a1928158c272d28565cac1478a1422d 100644 (file)
@@ -1,16 +1,16 @@
+// 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
@@ -32,7 +32,7 @@ typedef struct {
     [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); }
@@ -58,9 +58,9 @@ int main() {
     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;
index 2608b51ac66bbf25f1f100a657ff3930a520d66e..3c4f6f033f0ba910756345dea80ff870a5798627 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <objc/runtime.h>
 
diff --git a/test/addProtocol.m b/test/addProtocol.m
new file mode 100644 (file)
index 0000000..c483a49
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+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__);
+}
diff --git a/test/arr-weak.m b/test/arr-weak.m
new file mode 100644 (file)
index 0000000..aeb94e6
--- /dev/null
@@ -0,0 +1,74 @@
+// 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__);
+}
index ffd995d909c0cdc8b846423efb710e43804c138d..bed3b40978f168185b29ff54b7bb00ccfec18391 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CFLAGS -framework CoreFoundation
+
 #include <CoreFoundation/CoreFoundation.h>
 #include <objc/runtime.h>
 
diff --git a/test/association.m b/test/association.m
new file mode 100644 (file)
index 0000000..5d1536a
--- /dev/null
@@ -0,0 +1,70 @@
+// 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__);
+}
diff --git a/test/badAltHandler.m b/test/badAltHandler.m
new file mode 100644 (file)
index 0000000..5325442
--- /dev/null
@@ -0,0 +1,89 @@
+// 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__);
+}
diff --git a/test/blocksAsImps.m b/test/blocksAsImps.m
new file mode 100644 (file)
index 0000000..3574898
--- /dev/null
@@ -0,0 +1,245 @@
+// 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__);
+}
+
index 0de609e97759bf9f8e2a7d9f3e4577b42897f7b4..2624a8c2d5557e9469fb1210e65758902eaa5ec5 100644 (file)
@@ -1,3 +1,12 @@
+/*
+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>
@@ -27,7 +36,7 @@ int main()
     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]);
@@ -37,7 +46,7 @@ int main()
     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]);
index 4c946b4c4af34e0959c3c3f4f3aafb1b15daa95f..6b4a208480da139c0d14d7c2f6946e3c65f3f363 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <string.h>
 #include <objc/objc-runtime.h>
diff --git a/test/cdtors.mm b/test/cdtors.mm
new file mode 100644 (file)
index 0000000..d993265
--- /dev/null
@@ -0,0 +1,282 @@
+// 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__);
+}
index b70001933725105e98bec0132dfe2f15c89de9f3..64f7dd46c3c9205ceeb0d4ef2691072245ca135d 100644 (file)
@@ -1,5 +1,8 @@
+// TEST_CFLAGS -framework Foundation
+
 #include "test.h"
 #include <objc/objc-runtime.h>
+#include <objc/objc-gdb.h>
 #import <Foundation/Foundation.h>
 
 @interface Foo:NSObject
@@ -7,8 +10,6 @@
 @implementation Foo
 @end
 
-extern Class gdb_class_getClass(Class cls);
-
 int main()
 {
 #if __OBJC2__
index 7fc9804754241e9a3afdc5e0ff2f25b42bb25699..1e9eb7ef8028edc8ae71f0586d96e548512c544f 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <string.h>
 #include <objc/objc-runtime.h>
@@ -25,6 +27,9 @@ int main()
     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);
index 38976744480959227239cba84459afa5865d181e..0fb9758f0d762682c0ef50981169b0ffce9992a0 100644 (file)
@@ -1,3 +1,5 @@
+// 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); }
@@ -60,10 +67,17 @@ static void class_fn(id self, SEL _cmd __attribute__((unused)))
     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]);
@@ -73,9 +87,9 @@ static void cycle(void)
     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
     
@@ -83,12 +97,42 @@ static void cycle(void)
                     (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
@@ -97,12 +141,23 @@ static void cycle(void)
 # 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");
@@ -110,7 +165,13 @@ static void cycle(void)
 
     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"));
 
@@ -120,11 +181,11 @@ static void cycle(void)
     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));
     }
 
@@ -145,6 +206,34 @@ static void cycle(void)
     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];
@@ -158,25 +247,46 @@ static void cycle(void)
     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);
@@ -189,13 +299,13 @@ static void cycle(void)
     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);
@@ -203,42 +313,42 @@ static void cycle(void)
     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--) {
index 65dd89a247084ec4baa347e3d11703db10034b15..ab0e6ac2c64218f84ddf9286bc4da3a31f9ed655 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <objc/objc-runtime.h>
 
index 633cbc4c49d07aa1a65885a7f0f488672075939a..4f5205feaf5cb678e3f6763bd425cb155ee3a100 100644 (file)
@@ -1,5 +1,28 @@
+/*
+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();
 
@@ -97,7 +120,7 @@ int main()
     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);
index 74cb14453e155fa49ce11a224b3bc92805bd1477..c59f6639eb826c9b3813da6f4da4ff509fcafc40 100644 (file)
@@ -28,8 +28,8 @@
 @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
index d7535a10920e0260ffea7de53360633e24851dc4..d820a2204bacd325b4e37dcad222c116c231ae67 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <string.h>
 #include <malloc/malloc.h>
index b050f40a6cf515b8ad0c0d9964487f2587205c0f..584d8b5fe1347510c464f77be71f4e08c4ed0c8c 100644 (file)
@@ -1,37 +1,49 @@
+// 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()
@@ -67,21 +99,14 @@ 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");
@@ -89,21 +114,14 @@ int main()
     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
@@ -112,17 +130,17 @@ int main()
     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
@@ -139,8 +157,15 @@ int main()
     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__);
 }
index 58210165f48da7c6122734859d15e52c0cc16536..f74e4b4800007411c892570604220c175fb2301b 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <string.h>
 #include <malloc/malloc.h>
index 5be912fa68fe764d6e25024a86be48f943d8bee4..61cb2a07a3c603aa36b2dc3c17dd4e8f215ef1db 100644 (file)
@@ -1,7 +1,12 @@
+// 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"
 
@@ -22,7 +27,7 @@ int main()
     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);
 
@@ -30,7 +35,7 @@ int main()
     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);
 
@@ -38,7 +43,7 @@ int main()
     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);
 
@@ -46,7 +51,7 @@ int main()
     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);
 
index 2836a4d53bf80951050f5959cb0dd9301e762832..b7607060727a63e75f97c3b9e1edd9abae9b4bc9 100644 (file)
@@ -1,3 +1,6 @@
+// rdar://6401639, waiting for rdar://5648998
+// TEST_DISABLED
+
 #include "test.h"
 #include <objc/objc.h>
 #include <mach/mach.h>
@@ -5,9 +8,6 @@
 #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
@@ -30,6 +30,8 @@ void *thread(void *arg __unused)
         [Super method];
         _objc_flush_caches(0, YES);
     }
+
+    return NULL;
 }
 
 
index 5519933374c2a2a38cce29a774aa455bd1bb8046..78bb8d18d213a5b2e6ec7caff6604d792821d0ff 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 // DO NOT include anything else here
 #include <objc/objc.h>
 // DO NOT include anything else here
index 794a46aaaaa8bf32d91a07cf83670d060bfed8b2..e712dac2f1ba1f0c360d833c26258d62ec7a6c77 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CFLAGS -Wno-deprecated-declarations
+
 #include "test.h"
 #include <objc/objc-runtime.h>
 #ifndef OBJC_NO_GC
@@ -60,10 +62,10 @@ int main()
     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
 
@@ -107,16 +109,16 @@ int main()
     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);
diff --git a/test/errcheck.pl b/test/errcheck.pl
deleted file mode 100644 (file)
index dd49a18..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/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;
-}
diff --git a/test/evil-category-0.m b/test/evil-category-0.m
new file mode 100644 (file)
index 0000000..8dfa8d7
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+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"
diff --git a/test/evil-category-00.m b/test/evil-category-00.m
new file mode 100644 (file)
index 0000000..e3030dd
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+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"
diff --git a/test/evil-category-000.m b/test/evil-category-000.m
new file mode 100644 (file)
index 0000000..67b5c6b
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+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"
diff --git a/test/evil-category-1.m b/test/evil-category-1.m
new file mode 100644 (file)
index 0000000..7e6d07f
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+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"
diff --git a/test/evil-category-2.m b/test/evil-category-2.m
new file mode 100644 (file)
index 0000000..21d1d66
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+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"
diff --git a/test/evil-category-3.m b/test/evil-category-3.m
new file mode 100644 (file)
index 0000000..9c097ae
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+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"
diff --git a/test/evil-category-4.m b/test/evil-category-4.m
new file mode 100644 (file)
index 0000000..d024b8f
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+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"
diff --git a/test/evil-category-def.m b/test/evil-category-def.m
new file mode 100644 (file)
index 0000000..5917e1a
--- /dev/null
@@ -0,0 +1,72 @@
+
+#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) { }
diff --git a/test/evil-class-0.m b/test/evil-class-0.m
new file mode 100644 (file)
index 0000000..f468315
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+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"
diff --git a/test/evil-class-00.m b/test/evil-class-00.m
new file mode 100644 (file)
index 0000000..6c8d4f3
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+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"
diff --git a/test/evil-class-000.m b/test/evil-class-000.m
new file mode 100644 (file)
index 0000000..7987ef6
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+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"
diff --git a/test/evil-class-1.m b/test/evil-class-1.m
new file mode 100644 (file)
index 0000000..ce4abbc
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+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"
diff --git a/test/evil-class-2.m b/test/evil-class-2.m
new file mode 100644 (file)
index 0000000..0e2a6f8
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+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"
diff --git a/test/evil-class-3.m b/test/evil-class-3.m
new file mode 100644 (file)
index 0000000..3f8e81e
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+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"
diff --git a/test/evil-class-4.m b/test/evil-class-4.m
new file mode 100644 (file)
index 0000000..3a5bdee
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+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"
diff --git a/test/evil-class-5.m b/test/evil-class-5.m
new file mode 100644 (file)
index 0000000..2f47285
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+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"
diff --git a/test/evil-class-def.m b/test/evil-class-def.m
new file mode 100644 (file)
index 0000000..4e418f8
--- /dev/null
@@ -0,0 +1,169 @@
+#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) { }
diff --git a/test/evil-main.m b/test/evil-main.m
new file mode 100644 (file)
index 0000000..aa6a124
--- /dev/null
@@ -0,0 +1,15 @@
+#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
+}
index 292135174db54b370917cc18cefe90f79531027f..e811aae1e9436a9cdd7ab859d69e902bc62443e8 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <objc/runtime.h>
 #include <objc/objc-exception.h>
@@ -39,7 +41,7 @@ void pool(void) {  }
 @end
 
 
-#if __OBJC2__
+#if __OBJC2__  &&  !TARGET_OS_EMBEDDED  &&  !TARGET_OS_IPHONE  &&  !TARGET_IPHONE_SIMULATOR
 
 void altHandlerFail(id unused __unused, void *context __unused)
 {
@@ -67,7 +69,7 @@ static void throwWithAltHandler(void)
 {
     @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;
@@ -85,7 +87,7 @@ static void throwWithAltHandlerAndRethrow(void)
 {
     @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;
@@ -447,9 +449,11 @@ int main()
                 state++;
                 @throw;
                 state = BAD;
-            } @catch (Sub *e) {
+            } 
+            @catch (Sub *e) {
                 state = BAD;
-            } @finally {
+            } 
+            @finally {
                 state++;
             }
 
@@ -464,7 +468,8 @@ int main()
     }
     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
@@ -503,7 +508,7 @@ int main()
             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;
@@ -535,9 +540,9 @@ int main()
             @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;
@@ -570,9 +575,9 @@ int main()
             @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;
@@ -633,3 +638,4 @@ int main()
     succeed("exc.m");
 #endif
 }
+
index 8649e738011117ee813e7f3225c2bfe5bb91a068..b8e3357c4a32d4db1bb28ba110956d186608299e 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <objc/objc-runtime.h>
 
diff --git a/test/fail.m b/test/fail.m
deleted file mode 100644 (file)
index a0f6755..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "test.h"
-
-int main()
-{
-    fail("always fails");
-}
index c30717eeca07bfae393bd3dba5dee43a22631047..8a65a19231ae13e5d46ae9c95c0e5d678f5e79b4 100644 (file)
@@ -1,17 +1,16 @@
+// 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;
@@ -37,9 +36,7 @@ bool testHandwritten(char *style, char *test, char *message, id collection, NSSe
  
  
     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;
@@ -49,18 +46,14 @@ bool testHandwritten(char *style, char *test, char *message, id collection, NSSe
     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;
@@ -72,10 +65,8 @@ bool testCompiler(char *style, char *test, char *message, id collection, NSSet *
 
 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;
@@ -92,7 +83,7 @@ bool testBreak(unsigned int where, NSArray *array) {
     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;
     }
@@ -101,7 +92,7 @@ bool testBreak(unsigned int where, NSArray *array) {
         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;
@@ -114,7 +105,7 @@ bool testBreak(unsigned int where, NSArray *array) {
     
 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;
@@ -122,14 +113,14 @@ bool testBreaks(NSArray *array) {
     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);
@@ -153,7 +144,7 @@ void makeReferences(int n) {
     }
 }
     
-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];
@@ -171,9 +162,9 @@ void testCollections(char *test, NSArray *array, NSSet *set) {
     [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);
@@ -182,7 +173,7 @@ void testInnerDecl(char *test, char *message, id collection) {
 }
 
 
-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)
@@ -192,16 +183,16 @@ void testOuterDecl(char *test, char *message, id 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])
@@ -212,7 +203,7 @@ void testOuterExpression(char *test, char *message, id collection) {
     }
 }
 
-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);
@@ -221,7 +212,6 @@ void testExpressions(char *message, id collection) {
     
 
 int main() {
-    Verbosity = (getenv("VERBOSE") != NULL);
     NSAutoreleasePool *pool = [NSAutoreleasePool new];
     testCollections("nil", nil, nil);
     testCollections("empty", [NSArray array], [NSSet set]);
@@ -230,8 +220,8 @@ int main() {
     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);
 }
index 741dca8077fae27ecd13617201022ba3ac74836c..9a48b173f3cc188332f4dcfbf1085724135dd33e 100644 (file)
@@ -1,16 +1,26 @@
+// 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)
@@ -24,7 +34,7 @@ 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__;
 
@@ -133,9 +143,7 @@ long long forward_handler(id self, SEL _cmd, long i1, long i2, long i3, long i4,
     {
         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));
@@ -232,10 +240,6 @@ struct stret forward_stret_handler(id self, SEL _cmd, long i1, long i2, long i3,
     
 #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__)
@@ -248,15 +252,7 @@ struct stret forward_stret_handler(id self, SEL _cmd, long i1, long i2, long i3,
     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::::::::::::::::::::::::::::)  ||  
@@ -293,15 +289,9 @@ struct stret forward_stret_handler(id self, SEL _cmd, long i1, long i2, long i3,
     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);
@@ -315,9 +305,6 @@ struct stret forward_stret_handler(id self, SEL _cmd, long i1, long i2, long i3,
     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);
 
@@ -370,9 +357,7 @@ struct stret forward_stret_handler(id self, SEL _cmd, long i1, long i2, long i3,
     {
         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));
@@ -411,34 +396,61 @@ typedef double (*fp_fn_t)(id self, SEL _cmd, long i1, long i2, long i3, long i4,
 
 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));
 
@@ -446,47 +458,71 @@ int main()
     // 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));
 
@@ -494,22 +530,34 @@ int main()
     // 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));
 
@@ -517,47 +565,71 @@ int main()
     // 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));
 
@@ -567,22 +639,34 @@ int main()
     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));
 
@@ -590,50 +674,76 @@ int main()
     // 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
diff --git a/test/future.m b/test/future.m
new file mode 100644 (file)
index 0000000..2734e30
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+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__);
+}
diff --git a/test/future1.m b/test/future1.m
deleted file mode 100644 (file)
index 1423a5f..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-#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__);
-}
diff --git a/test/gc-main.m b/test/gc-main.m
new file mode 100644 (file)
index 0000000..54bd976
--- /dev/null
@@ -0,0 +1,9 @@
+#include "test.h"
+
+@interface Main @end
+@implementation Main @end
+
+int main(int argc __attribute__((unused)), char **argv)
+{
+    succeed(basename(argv[0]));
+}
index fb9497d5d9942bbe850678ebd075cca52f69e83d..e5b84bbfef027ee3f819214f6c4f97cbc1ca3bda 100644 (file)
--- a/test/gc.m
+++ b/test/gc.m
@@ -1,2 +1,5 @@
 @interface GC @end
 @implementation GC @end
+
+// silence "no debug symbols in executable" warning
+void foo(void) { }
diff --git a/test/gcenforcer-nogc-1.m b/test/gcenforcer-nogc-1.m
new file mode 100644 (file)
index 0000000..64b2b9c
--- /dev/null
@@ -0,0 +1,15 @@
+// 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
+*/
diff --git a/test/gcenforcer-nogc-2.m b/test/gcenforcer-nogc-2.m
new file mode 100644 (file)
index 0000000..9058ca1
--- /dev/null
@@ -0,0 +1,22 @@
+// 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
+*/
diff --git a/test/gcenforcer-noobjc.m b/test/gcenforcer-noobjc.m
new file mode 100644 (file)
index 0000000..46de0c6
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+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
+*/
diff --git a/test/gcenforcer-requiresgc-1.m b/test/gcenforcer-requiresgc-1.m
new file mode 100644 (file)
index 0000000..0cb971a
--- /dev/null
@@ -0,0 +1,23 @@
+// 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
+*/
diff --git a/test/gcenforcer-requiresgc-2.m b/test/gcenforcer-requiresgc-2.m
new file mode 100644 (file)
index 0000000..3493e52
--- /dev/null
@@ -0,0 +1,16 @@
+// 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
+*/
diff --git a/test/gcenforcer-supportsgc.m b/test/gcenforcer-supportsgc.m
new file mode 100644 (file)
index 0000000..92ff952
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+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
+*/
index f3241b06eae38138ac8a529d6cd70f1814992f81..f170933840c4cbe029a3650ca18e237bc3fba83d 100644 (file)
@@ -1,3 +1,17 @@
+/*
+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>
@@ -9,7 +23,7 @@ int main()
         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 {
diff --git a/test/gcenforcer_nogc.gc.expected-stderr b/test/gcenforcer_nogc.gc.expected-stderr
deleted file mode 100644 (file)
index 2bc2de8..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-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
diff --git a/test/gcenforcer_nogc.nogc.expected-stderr b/test/gcenforcer_nogc.nogc.expected-stderr
deleted file mode 100644 (file)
index 5ec78df..0000000
+++ /dev/null
@@ -1 +0,0 @@
-OK: gcenforcer_nogc.out
diff --git a/test/gcenforcer_requiresgc.gc.expected-stderr b/test/gcenforcer_requiresgc.gc.expected-stderr
deleted file mode 100644 (file)
index 6a0db82..0000000
+++ /dev/null
@@ -1 +0,0 @@
-OK: gcenforcer_requiresgc.out
diff --git a/test/gcenforcer_requiresgc.nogc.expected-stderr b/test/gcenforcer_requiresgc.nogc.expected-stderr
deleted file mode 100644 (file)
index 8b91e01..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-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
index ee426d6cfc91500e5f81817b8dfdb5147d0e63cd..322e45d827da20a91c2db08f5854bc77730069a3 100644 (file)
@@ -1,20 +1,15 @@
+// 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
 {
 }
 
@@ -56,4 +51,4 @@ int main() {
     succeed(__FILE__);
     
     return 0;
-}
\ No newline at end of file
+}
index 9507ec58bec7c7ef752cfeeaebd3d33ea98895c4..4305aa0da31812f87389566a2ba20a8375e504a5 100644 (file)
@@ -1,4 +1,16 @@
+// 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>
 
@@ -24,28 +36,30 @@ int main()
     [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
index c6babd80b55e7d3d99c631f59396c4e4a92b3b85..88da706d8d81e94df20399e7771bfe09940c8066 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <objc/runtime.h>
 #include <objc/message.h>
index f0f87b7d788a555235fcca8f789c562092336948..251bae8400f7afda06376b9223e579b894320b5f 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CFLAGS -Wno-deprecated-declarations
+
 #include "test.h"
 #include <objc/runtime.h>
 #include <objc/message.h>
@@ -63,15 +65,18 @@ void cycle(Class cls)
     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)      &&  
@@ -93,7 +98,7 @@ void cycle(Class cls)
     getImp(dealloc);
     getImp(retainCount);
 
-    if (objc_collecting_enabled()) {
+    if (objc_collectingEnabled()) {
         // GC: all ignored selector IMPs are identical
         testassert(retain == release      &&  
                    retain == autorelease  &&  
@@ -123,26 +128,26 @@ void cycle(Class cls)
 
     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];
@@ -151,23 +156,50 @@ void cycle(Class cls)
 
     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()
@@ -178,9 +210,13 @@ 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__
@@ -195,7 +231,7 @@ int main()
     testassert(cls);
     cycle(cls);
 
-    if (objc_collecting_enabled()) {
+    if (objc_collectingEnabled()) {
         // rdar://6200570 Method manipulation shouldn't affect ignored methods.
 
         cls = [Super class];
@@ -251,7 +287,7 @@ int main()
 
     // 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;
@@ -281,6 +317,31 @@ int main()
         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__);
index 8ae12aa60c864963aec8a8fd57877271e592e264..f417eb5244dfbd07e93abbe32ffcd6cdb4a34756 100644 (file)
@@ -1,3 +1,12 @@
+/*
+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>
@@ -23,7 +32,7 @@ int main()
     [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);
index 4f3f6ef421581e9bf4bfa2e3fb90c34ba5baea5a..849d2e0582573be98a0c94ce728eb5ad2869b995 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 // initialize.m
 // Test basic +initialize behavior
 // * +initialize before class method
index 14e32e10393901deeb2e5bfa91b822cb41b59bbf..ef46cf492a6ea3aaafc32132e85c23cda4d6542e 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 
 #include <objc/runtime.h>
index e3b16c72ce30aec881479a8bfa81062b82fb39ac..050778a573535255ddaf414ea3436809f95eb38b 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <objc/objc-runtime.h>
 
index 56b097fa7283e40310d49808e66efe08c6f5bfba..a45b18b5fffdb2bf53aeaace48ff80f04c706c31 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <stdint.h>
 #include <string.h>
@@ -43,17 +45,19 @@ int main()
 
     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"));
diff --git a/test/ivarSlide.m b/test/ivarSlide.m
new file mode 100644 (file)
index 0000000..056f6f8
--- /dev/null
@@ -0,0 +1,517 @@
+/*
+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;
+}
diff --git a/test/ivarSlide2.m b/test/ivarSlide2.m
deleted file mode 100644 (file)
index 22d8493..0000000
+++ /dev/null
@@ -1,519 +0,0 @@
-#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;
-}
index 181e47da0d0734c9668616dc738327b63416fd7b..a2f7ac521c0fa5cfb6cc8103a625456409c3f6c6 100644 (file)
@@ -1,10 +1,12 @@
+// 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);
 
@@ -13,7 +15,7 @@ void printlayout(const char *name, const char *layout)
         return;
     }
 
-    const char *c;
+    const uint8_t *c;
     for (c = layout; *c; c++) {
         testprintf("%02x ", *c);
     }
@@ -32,8 +34,8 @@ void printlayout(const char *name, const char *layout)
     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;
@@ -51,8 +53,8 @@ void printlayout(const char *name, const char *layout)
     __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;
@@ -71,7 +73,7 @@ void printlayout(const char *name, const char *layout)
 
 int main() 
 {
-    const char *layout;
+    const uint8_t *layout;
 
     layout = class_getIvarLayout(objc_getClass("AllScanned"));
     printlayout("AllScanned", layout);
index 79d68e4cc7dd85ff55c8f7845c41faf24d224a42..fd093704a5c92f7d4ac554c814ec6de3c6169f86 100644 (file)
@@ -1,3 +1,12 @@
+/*
+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;
index db31191ed419d86d93d9f086381a77cb4ba62af8..54b0f6b4f50906e76e7a0cba245a327b24942de6 100644 (file)
@@ -1,3 +1,21 @@
+/*
+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>
@@ -16,18 +34,23 @@ void *thread(void *arg)
 
     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;
 
@@ -43,4 +66,5 @@ int main()
     testassert(state == COUNT*26);
 
     succeed(__FILE__);
+#endif
 }
index d1740e4e057f487b15fde844e71d6fafb9f4bb4b..cb7d63598f2ab47f960370a226368ce094b6757b 100644 (file)
@@ -1,3 +1,10 @@
+/*
+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>
 
index 6158d9a943875ee4f00d535e4cd58a12e687815e..7d97815f9b3bb14acf0b1748cc93c395c127359e 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 
 int state = 0;
diff --git a/test/main.m b/test/main.m
deleted file mode 100644 (file)
index 54bd976..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "test.h"
-
-@interface Main @end
-@implementation Main @end
-
-int main(int argc __attribute__((unused)), char **argv)
-{
-    succeed(basename(argv[0]));
-}
index ec13df2bec374aa1973861acd034dc3bb0a5acdb..3e3635de18418045f694be460728b7cd0f126085 100644 (file)
@@ -1,13 +1,16 @@
+// 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
 
 
@@ -16,12 +19,12 @@ int main()
     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);
@@ -64,13 +67,30 @@ int main()
     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);
@@ -96,11 +116,11 @@ int main()
 
     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));
diff --git a/test/methodListSize.m b/test/methodListSize.m
new file mode 100644 (file)
index 0000000..05216ad
--- /dev/null
@@ -0,0 +1,56 @@
+// 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__);
+}
index fe6793d7c10b7f87bdf58ac0ad6f253058c11f76..3d12119e040738734a19da3aaa2388ca6548f126 100644 (file)
@@ -1,7 +1,9 @@
+// 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;
@@ -11,9 +13,12 @@ int main() {
 
   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__);
index 6383c62fd3d81c592764d3364e5c8891cabd4b74..583a6227b547f11276b79b3bb5b07c9f297cf8f7 100644 (file)
@@ -1,6 +1,27 @@
+// 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 { \
@@ -57,13 +66,18 @@ 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)
@@ -77,20 +91,20 @@ 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;
@@ -99,8 +113,7 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
 +(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;
@@ -109,8 +122,7 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
 +(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;
@@ -119,8 +131,7 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
 +(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;
@@ -129,14 +140,56 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
 +(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
 {
@@ -172,6 +225,36 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
     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
 
 
@@ -181,13 +264,12 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
    (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;
 }
 
@@ -195,13 +277,12 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
    (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;
 }
 
@@ -209,13 +290,12 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
    (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;
 }
 
@@ -223,13 +303,12 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
    (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;
 }
 
@@ -237,13 +316,73 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
    (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;
 }
 
@@ -304,121 +443,181 @@ int main()
     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 
@@ -488,7 +687,43 @@ int main()
     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
@@ -497,5 +732,94 @@ int main()
     // 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
index e26ad12b3de057b80b3b1f5bcbe1088c23d1007d..a133451a58deec9538c24849420f0933c71e90c2 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 
 #import <objc/runtime.h>
diff --git a/test/nsexc.m b/test/nsexc.m
new file mode 100644 (file)
index 0000000..61b04f7
--- /dev/null
@@ -0,0 +1,4 @@
+// TEST_CFLAGS -framework Foundation
+
+#define USE_FOUNDATION 1
+#include "exc.m"
index 7e831c23c2f67a83e63545d5fc658644bec6af60..a1d515aae8724834fa500121371a0faae8d5a716 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CFLAGS -framework Foundation
+
 #include "test.h"
 
 #import <Foundation/Foundation.h>
index d62f45ff6690cab31f1d5d8c2f8b1f5dbb70d0b6..bfa217c60d4b655e97f8a11ac8ea33057ef815f5 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <stdint.h>
 #include <string.h>
diff --git a/test/propertyDesc.m b/test/propertyDesc.m
new file mode 100644 (file)
index 0000000..7db8e04
--- /dev/null
@@ -0,0 +1,329 @@
+// 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__);
+}
index 33214b877eb8fac831955ae4e4dbc0124e016b54..dc8345bbb00b3b33b918f362f7643840aa5415a5 100644 (file)
@@ -1,9 +1,14 @@
+// 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;
@@ -52,7 +57,7 @@
 int main()
 {
     Class cls;
-    Protocol **list;
+    Protocol * const *list;
     Protocol *protocol, *empty;
 #if !__OBJC2__
     struct objc_method_description *desc;
@@ -134,7 +139,7 @@ int main()
     testassert(count == 1);
     testassert(protocol_isEqual(list[0], @protocol(Proto2)));
     testassert(!list[1]);
-    free(list);    
+    free((void*)list);    
 
     count = 100;
     cls = objc_getClass("Super");
@@ -144,7 +149,7 @@ int main()
     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");
@@ -170,7 +175,7 @@ int main()
     testassert(cls);
     list = class_copyProtocolList(cls, NULL);
     testassert(list);
-    free(list);
+    free((void*)list);
 
     count = 100;
     list = class_copyProtocolList(NULL, &count);
@@ -188,7 +193,7 @@ int main()
     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);
@@ -196,7 +201,7 @@ int main()
     testassert(count == 1);
     testassert(0 == strcmp(property_getName(proplist[0]), "i"));
     testassert(proplist[1] == NULL);
-    free(proplist);    
+    free(proplist);
 
     succeed(__FILE__);
 }
index 8847099198165e400afac80753ec7a6460132adf..9e6acd3c2abdddc33db7cbb1cf9ab7f126daba64 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <malloc/malloc.h>
 #include <objc/objc-runtime.h>
index af03d9a087e096ce73d6cc9876ef9f48391a8a38..8c8b3d1365378e4b711ddb00b5549811e3b5f3ea 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <string.h>
 #include <malloc/malloc.h>
@@ -41,8 +43,8 @@ int main()
     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);
@@ -54,8 +56,8 @@ int main()
     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);
index a35ee7c3587cdecebac672c4263e62f42952028b..a82f9912ac781162b89b4721375065753d415315 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CFLAGS -Wno-deprecated-declarations
+
 #include "test.h"
 
 #if __OBJC2__
@@ -16,13 +18,15 @@ int main()
 
 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()
 {
diff --git a/test/resolve.expected-stderr b/test/resolve.expected-stderr
deleted file mode 100644 (file)
index 1eca94b..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-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
index a590b19c4ba7fd134e7ecf5ab3c6cf52f7dd2315..d03cc876a7f0aaaf140ff430d2633e47ac260262 100644 (file)
@@ -1,15 +1,22 @@
 /* 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
@@ -51,7 +58,7 @@ static int state = 0;
         return nil;
     }
     fail("forward:: shouldn't be called (sel %s)", sel_getName(sel));
-    return args;  // unused
+    return (id)args;  // unused
 }
 @end
 
@@ -169,7 +176,7 @@ int main()
     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;
@@ -184,7 +191,7 @@ int main()
     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;
@@ -199,7 +206,7 @@ int main()
     testassert(state == 36);
     testassert(ret == nil);
 
-    _objc_flush_caches([Sub class]->isa, NO);
+    _objc_flush_caches([Sub class]->isa);
 
 
     // Resolve an instance method
@@ -216,7 +223,7 @@ int main()
     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;
@@ -231,7 +238,7 @@ int main()
     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;
@@ -246,7 +253,7 @@ int main()
     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;
diff --git a/test/rr-autorelease.m b/test/rr-autorelease.m
new file mode 100644 (file)
index 0000000..b09c9d5
--- /dev/null
@@ -0,0 +1,21 @@
+// 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
diff --git a/test/rr-autorelease2.m b/test/rr-autorelease2.m
new file mode 100644 (file)
index 0000000..b3a8aaa
--- /dev/null
@@ -0,0 +1,334 @@
+// 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);
+}
diff --git a/test/rr-nsautorelease.m b/test/rr-nsautorelease.m
new file mode 100644 (file)
index 0000000..38bbe57
--- /dev/null
@@ -0,0 +1,7 @@
+// TEST_CFLAGS -framework Foundation
+// TEST_CONFIG GC=0
+
+#define FOUNDATION 1
+#define NAME "rr-nsautorelease"
+
+#include "rr-autorelease2.m"
diff --git a/test/runtime.expected-stderr b/test/runtime.expected-stderr
deleted file mode 100644 (file)
index ad64db4..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-objc\[\d+\]: class `DoesNotExist\' not linked into application
-OK: runtime.m
index ea4493686e92a5a314d26726c16b7e6938486ae0..31f9875d1c2e0e66452501b2dc7e0ab36c261af4 100644 (file)
@@ -1,3 +1,11 @@
+/* 
+TEST_RUN_OUTPUT
+objc\[\d+\]: class `DoesNotExist\' not linked into application
+OK: runtime.m
+END 
+*/
+
+
 #include "test.h"
 
 #include <string.h>
@@ -18,7 +26,8 @@
 int main()
 {
     Class list[100];
-    unsigned int count, count0;
+    Class *list2;
+    unsigned int count, count0, count2;
     unsigned int i;
     int foundSuper;
     int foundSub;
@@ -79,5 +88,16 @@ int main()
     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__);
 }
index f272f63dc336c7b9c1076dea8e527efd5c8bf27a..ec4fc0d31881fb74a944f46ada8252cd1b468ba5 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <string.h>
 #include <objc/objc-runtime.h>
@@ -12,10 +14,13 @@ int main()
     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))));
     }
index 9a859bdf03b8f33f14bb457e2cd4f9662688ef27..5f14f7051675894fc697b2a3ff8e8f428986b233 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CFLAGS -Wno-deprecated-declarations
+
 #include "test.h"
 #include <objc/runtime.h>
 
@@ -21,7 +23,9 @@
 @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()
index 24c1c6234ec65a98a2632fc379ae75697c638991..16688c412b5e93ce6a47929d405d4aa4696eeec4 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <objc/objc-runtime.h>
 
index b60c569b4762d8f3145f788cc8a90a2a7284c6a9..96654cfa5fb0394dda21a797c9838dcb49517f16 100644 (file)
@@ -1,3 +1,5 @@
+// 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;
@@ -42,9 +49,10 @@ static void *threadfn(void *arg)
     }
 
     // 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;
 }
 
@@ -58,9 +66,9 @@ int main()
 
     // 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++) {
index 3988306407d81ef0160b7dd0ddcc55d9eda4bfd4..fdf31d8979cd2d7a4ee2704f0c9e05f99aa13269 100644 (file)
@@ -1,3 +1,5 @@
+// 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];
index 128df3da814c97bf61865104ddfebbf9e8ad4db2..6840a3ac75c1bfc5c417253817dfb894d3b58497 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CFLAGS -framework Foundation
+
 #include "test.h"
 
 #include <Foundation/Foundation.h>
diff --git a/test/taggedPointers.m b/test/taggedPointers.m
new file mode 100644 (file)
index 0000000..72129b1
--- /dev/null
@@ -0,0 +1,294 @@
+// 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
index a9fa26669d321a0849347e5f18d4d6884644f83c..ed378aa574528749bfee9e30ef2d37edac2c53bf 100644 (file)
 #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) {
@@ -48,57 +51,199 @@ static inline int fail(const char *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
diff --git a/test/test.pl b/test/test.pl
new file mode 100755 (executable)
index 0000000..9635eff
--- /dev/null
@@ -0,0 +1,1161 @@
+#!/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);
diff --git a/test/test.xcodeproj/project.pbxproj b/test/test.xcodeproj/project.pbxproj
deleted file mode 100644 (file)
index d27b516..0000000
+++ /dev/null
@@ -1,453 +0,0 @@
-// !$*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 */;
-}
index 801f634531973f367ef1e1051fe527e90c3055fc..cf3c97a4b62fbb1ed381e642ddeebf29d460de30 100644 (file)
@@ -1,3 +1,12 @@
+/*
+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>
@@ -27,13 +36,13 @@ void cycle(void)
     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");
@@ -43,10 +52,10 @@ void cycle(void)
 
     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];
@@ -64,7 +73,7 @@ void cycle(void)
     [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);
@@ -77,7 +86,7 @@ void cycle(void)
     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
@@ -88,9 +97,13 @@ void cycle(void)
 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__
@@ -105,7 +118,7 @@ int main()
     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);
@@ -113,7 +126,7 @@ int main()
     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);
diff --git a/test/unload3.c b/test/unload3.c
new file mode 100644 (file)
index 0000000..0927524
--- /dev/null
@@ -0,0 +1,16 @@
+// 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) { }
diff --git a/test/unload3.m b/test/unload3.m
deleted file mode 100644 (file)
index 9f83a9b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-// 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
index d3361b136fb0f6d3c2b1531a45e3c5739d0330c5..94dd034d15a12c9c2d68c948424ae87f489262ff 100644 (file)
@@ -6,3 +6,6 @@ int fake2 __attribute__((section("__DATA,__objc_foo"))) = 0;
 #else
 int fake2 __attribute__((section("__OBJC,__foo"))) = 0;
 #endif
+
+// getsectiondata() falls over if __TEXT has no contents
+const char *unload4 = "unload4";
index 85559ae4d276eace2e867cc4bfe4b5e7dd323414..30db745fa8835b58f8e3b254cb5d2781ef167a90 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CFLAGS -framework Foundation
+
 #include "test.h"
 #include <objc/objc-exception.h>
 #include <Foundation/Foundation.h>
@@ -27,6 +29,7 @@ static int state;
 
 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++;
@@ -35,8 +38,13 @@ static void handler(id unused __unused, void *ctx __unused)
 +(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;
 }
@@ -46,6 +54,10 @@ static void handler(id unused __unused, void *ctx __unused)
 
 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()
@@ -78,6 +90,7 @@ int main()
     [pool drain];
 
     succeed(__FILE__);
+#endif
 }
 
 #endif
diff --git a/test/verify-exports.pl b/test/verify-exports.pl
new file mode 100755 (executable)
index 0000000..b242a27
--- /dev/null
@@ -0,0 +1,252 @@
+#!/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);
index a7bb2e500fca6b96df32931c45936c9ad51897cc..bacc39e1f47b0430570e9207b422ecc1b0393ffd 100644 (file)
@@ -1,8 +1,24 @@
+/*
+  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;
 }
@@ -10,9 +26,11 @@ extern int state;
 +(Class) class;
 +(id) alloc;
 -(id) init;
+-(void) dealloc;
 +(int) method;
 @end
 
+WEAK_IMPORT
 @interface MissingSuper : MissingRoot {
   @public
     int ivar;
@@ -27,6 +45,7 @@ extern int state;
 +(Class) class;
 +(id) alloc;
 -(id) init;
+-(void) dealloc;
 +(int) method;
 @end
 
index 379f36464dbce5448168e69e4fa9cfbf4700754a..fd694850592608355455462f26544511c6a2a768 100644 (file)
@@ -1,3 +1,5 @@
+// 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;
@@ -130,11 +138,16 @@ static BOOL classInNameList(const char **names, const char *name)
     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);
@@ -176,9 +189,7 @@ int main()
     }
 
     // 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"));
@@ -194,6 +205,7 @@ int main()
         testassert(classInList(classes, "MyMissingSuper"));
         testassert(classInList(classes, "MyMissingSub"));
     }
+    free(classes);
 
     // class name list
     const char *image = class_getImageName(objc_getClass("NotMissingRoot"));
@@ -265,13 +277,16 @@ int main()
     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]);
@@ -279,16 +294,20 @@ int main()
         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;
 }
+
index ab17752730f88644832a2a0b718717a78a560e2b..260b662538e5641cb99d21188d30682bb8083bd1 100644 (file)
@@ -1,3 +1,5 @@
+// See instructions in weak.h
+
 #include "test.h"
 #include "weak.h"
 
@@ -10,6 +12,7 @@ int state = 0;
 +(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
@@ -27,6 +30,7 @@ int state = 0;
 +(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
index 7c550b9a41225d8f965201feb95d904335ef8a6b..bbac80609f7a8c706e94ad4dcff948e31b459e6e 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <stdint.h>
 #include <string.h>
diff --git a/test/weakframework-missing.m b/test/weakframework-missing.m
new file mode 100644 (file)
index 0000000..43de10b
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+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"
diff --git a/test/weakframework-not-missing.m b/test/weakframework-not-missing.m
new file mode 100644 (file)
index 0000000..2a11104
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+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"
diff --git a/test/weakimport-missing.m b/test/weakimport-missing.m
new file mode 100644 (file)
index 0000000..bd86f43
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+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"
diff --git a/test/weakimport-not-missing.m b/test/weakimport-not-missing.m
new file mode 100644 (file)
index 0000000..440f79e
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+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"
diff --git a/test/xref.m b/test/xref.m
new file mode 100644 (file)
index 0000000..3843064
--- /dev/null
@@ -0,0 +1,32 @@
+// 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__);
+}
index 902330831208c2ebb665f0edda510106232c7656..c15127657c806ccb3a1619f7d235ab530d736b86 100644 (file)
@@ -1,3 +1,5 @@
+// TEST_CONFIG
+
 #include "test.h"
 #include <mach/mach.h>
 #include <malloc/malloc.h>
@@ -8,6 +10,11 @@
 
 int main()
 {
+    if (is_guardmalloc()) {
+        // guard malloc confuses this test
+        succeed(__FILE__);
+    }
+
     kern_return_t kr;
     vm_address_t *zones;
     unsigned int count, i;
index 239bb8fd9d4015fccce28830e428f228a15293f8..94edbfd262c35917772bf3002a5657eded90dc6b 100644 (file)
@@ -1 +1,9 @@
-__ZSt11lower_boundIPKmmET_S2_S2_RKT0_
+.objc_class_name___IncompleteProtocol
+__Znam
+__ZnamRKSt9nothrow_t
+__Znwm
+__ZnwmRKSt9nothrow_t
+__ZdaPv
+__ZdaPvRKSt9nothrow_t
+__ZdlPv
+__ZdlPvRKSt9nothrow_t
diff --git a/version.bat b/version.bat
new file mode 100755 (executable)
index 0000000..7df1c3a
--- /dev/null
@@ -0,0 +1,29 @@
+:: 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"
diff --git a/version.rc b/version.rc
new file mode 100644 (file)
index 0000000..709d0a4
--- /dev/null
@@ -0,0 +1,38 @@
+#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
+