diff --git a/README.md b/README.md index 96b61212..9ab5ba73 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # objc-runtime -objc runtime 750 +objc runtime 756.2 diff --git a/debug-objc/main.m b/debug-objc/main.m index ff408f2c..c59a3ef6 100644 --- a/debug-objc/main.m +++ b/debug-objc/main.m @@ -10,7 +10,7 @@ int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... - NSLog(@"Hello, World!"); + NSLog(@"Hello, World! %@", [NSString class]); } return 0; } diff --git a/include/System/pthread_machdep.h b/include/System/pthread_machdep.h index aee0c5ed..dfad1dc0 100755 --- a/include/System/pthread_machdep.h +++ b/include/System/pthread_machdep.h @@ -71,14 +71,14 @@ #define _PTHREAD_TSD_SLOT_DYLD_1 1 #define _PTHREAD_TSD_SLOT_DYLD_2 2 #define _PTHREAD_TSD_SLOT_DYLD_3 3 -#define _PTHREAD_TSD_RESERVED_SLOT_COUNT 4 +//#define _PTHREAD_TSD_RESERVED_SLOT_COUNT 4 /* To mirror the usage by dyld for Unwind_SjLj */ #define _PTHREAD_TSD_SLOT_DYLD_8 8 /* Keys 10 - 29 are for Libc/Libsystem internal ussage */ /* used as __pthread_tsd_first + Num */ #define __PTK_LIBC_LOCALE_KEY 10 -#define __PTK_LIBC_TTYNAME_KEY 11 +//#define __PTK_LIBC_TTYNAME_KEY 11 #define __PTK_LIBC_LOCALTIME_KEY 12 #define __PTK_LIBC_GMTIME_KEY 13 #define __PTK_LIBC_GDTOA_BIGINT_KEY 14 @@ -297,8 +297,8 @@ extern "C" { #endif -#define LOCK_INIT(l) ((l) = 0) -#define LOCK_INITIALIZER 0 +//#define LOCK_INIT(l) ((l) = 0) +//#define LOCK_INITIALIZER 0 #endif /* ! __ASSEMBLER__ */ #endif /* _POSIX_PTHREAD_MACHDEP_H */ diff --git a/objc.xcodeproj/project.pbxproj b/objc.xcodeproj/project.pbxproj index 4c83539e..3e2c1249 100644 --- a/objc.xcodeproj/project.pbxproj +++ b/objc.xcodeproj/project.pbxproj @@ -7,6 +7,17 @@ objects = { /* Begin PBXAggregateTarget section */ + 834F9B01212E560100F95A54 /* objc4_tests */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 834F9B04212E560200F95A54 /* Build configuration list for PBXAggregateTarget "objc4_tests" */; + buildPhases = ( + 834F9B05212E561400F95A54 /* Run Script (build tests) */, + ); + dependencies = ( + ); + name = objc4_tests; + productName = objc4_tests; + }; 837F67A81A771F63004D34FA /* objc-simulator */ = { isa = PBXAggregateTarget; buildConfigurationList = 837F67A91A771F63004D34FA /* Build configuration list for PBXAggregateTarget "objc-simulator" */; @@ -21,73 +32,73 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 301498BE21B658D100F165E9 /* CrashReporterClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014987A21B658D100F165E9 /* CrashReporterClient.h */; }; - 301498BF21B658D100F165E9 /* Block_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014987B21B658D100F165E9 /* Block_private.h */; }; - 301498C021B658D100F165E9 /* _simple.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014987C21B658D100F165E9 /* _simple.h */; }; - 301498C121B658D100F165E9 /* queue_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014987E21B658D100F165E9 /* queue_private.h */; }; - 301498C221B658D100F165E9 /* private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014987F21B658D100F165E9 /* private.h */; }; - 301498C321B658D100F165E9 /* source_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014988021B658D100F165E9 /* source_private.h */; }; - 301498C421B658D100F165E9 /* benchmark.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014988121B658D100F165E9 /* benchmark.h */; }; - 301498C521B658D100F165E9 /* spinlock_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014988321B658D100F165E9 /* spinlock_private.h */; }; - 301498C621B658D100F165E9 /* tsd_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014988421B658D100F165E9 /* tsd_private.h */; }; - 301498C721B658D100F165E9 /* qos_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014988521B658D100F165E9 /* qos_private.h */; }; - 301498C821B658D100F165E9 /* workqueue_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014988621B658D100F165E9 /* workqueue_private.h */; }; - 301498C921B658D100F165E9 /* pthread_machdep.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014988821B658D100F165E9 /* pthread_machdep.h */; }; - 301498CA21B658D100F165E9 /* cpu_capabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014988A21B658D100F165E9 /* cpu_capabilities.h */; }; - 301498CB21B658D100F165E9 /* cpu_capabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014988C21B658D100F165E9 /* cpu_capabilities.h */; }; - 301498CC21B658D100F165E9 /* cpu_capabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014988E21B658D100F165E9 /* cpu_capabilities.h */; }; - 301498CD21B658D100F165E9 /* auto_zone.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014988F21B658D100F165E9 /* auto_zone.h */; }; - 301498CE21B658D100F165E9 /* reason.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014989121B658D100F165E9 /* reason.h */; }; - 301498CF21B658D100F165E9 /* qos_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014989221B658D100F165E9 /* qos_private.h */; }; - 301498D021B658D100F165E9 /* ast_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014989421B658D100F165E9 /* ast_types.h */; }; - 301498D121B658D100F165E9 /* trap.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014989521B658D100F165E9 /* trap.h */; }; - 301498D221B658D100F165E9 /* machparam.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014989621B658D100F165E9 /* machparam.h */; }; - 301498D321B658D100F165E9 /* xpr.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014989721B658D100F165E9 /* xpr.h */; }; - 301498D421B658D100F165E9 /* task.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014989821B658D100F165E9 /* task.h */; }; - 301498D521B658D100F165E9 /* vm_tuning.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014989921B658D100F165E9 /* vm_tuning.h */; }; - 301498D621B658D100F165E9 /* endian.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014989A21B658D100F165E9 /* endian.h */; }; - 301498D721B658D100F165E9 /* Makefile in Sources */ = {isa = PBXBuildFile; fileRef = 3014989B21B658D100F165E9 /* Makefile */; }; - 301498D821B658D100F165E9 /* lock.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014989C21B658D100F165E9 /* lock.h */; }; - 301498D921B658D100F165E9 /* db_machdep.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014989D21B658D100F165E9 /* db_machdep.h */; }; - 301498DA21B658D100F165E9 /* cpu_data.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014989E21B658D100F165E9 /* cpu_data.h */; }; - 301498DB21B658D100F165E9 /* pmap.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014989F21B658D100F165E9 /* pmap.h */; }; - 301498DC21B658D100F165E9 /* simple_lock.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498A021B658D100F165E9 /* simple_lock.h */; }; - 301498DD21B658D100F165E9 /* io_map_entries.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498A121B658D100F165E9 /* io_map_entries.h */; }; - 301498DE21B658D100F165E9 /* cpu_number.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498A221B658D100F165E9 /* cpu_number.h */; }; - 301498DF21B658D100F165E9 /* setjmp.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498A321B658D100F165E9 /* setjmp.h */; }; - 301498E021B658D100F165E9 /* asm.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498A421B658D100F165E9 /* asm.h */; }; - 301498E121B658D100F165E9 /* timer.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498A521B658D100F165E9 /* timer.h */; }; - 301498E221B658D100F165E9 /* sched_param.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498A621B658D100F165E9 /* sched_param.h */; }; - 301498E321B658D100F165E9 /* thread.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498A721B658D100F165E9 /* thread.h */; }; - 301498E421B658D100F165E9 /* ast.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498A821B658D100F165E9 /* ast.h */; }; - 301498E521B658D100F165E9 /* machine_rpc.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498A921B658D100F165E9 /* machine_rpc.h */; }; - 301498E621B658D100F165E9 /* machine_cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498AA21B658D100F165E9 /* machine_cpu.h */; }; - 301498E721B658D100F165E9 /* commpage.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498AB21B658D100F165E9 /* commpage.h */; }; - 301498E821B658D100F165E9 /* machine_routines.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498AC21B658D100F165E9 /* machine_routines.h */; }; - 301498E921B658D100F165E9 /* cpu_capabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498AD21B658D100F165E9 /* cpu_capabilities.h */; }; - 301498EA21B658D100F165E9 /* cpu_affinity.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498AE21B658D100F165E9 /* cpu_affinity.h */; }; - 301498EB21B658D100F165E9 /* machlimits.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498AF21B658D100F165E9 /* machlimits.h */; }; - 301498EC21B658D100F165E9 /* locks.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498B021B658D100F165E9 /* locks.h */; }; - 301498ED21B658D100F165E9 /* vproc_priv.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498B121B658D100F165E9 /* vproc_priv.h */; }; - 301498EE21B658D100F165E9 /* notify_server.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498B321B658D100F165E9 /* notify_server.h */; }; - 301498EF21B658D100F165E9 /* clock_reply_server.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498B421B658D100F165E9 /* clock_reply_server.h */; }; - 301498F021B658D100F165E9 /* mach_exc_server.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498B521B658D100F165E9 /* mach_exc_server.h */; }; - 301498F121B658D100F165E9 /* exc_server.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498B621B658D100F165E9 /* exc_server.h */; }; - 301498F221B658D100F165E9 /* tsd.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498B821B658D100F165E9 /* tsd.h */; }; - 301498F321B658D100F165E9 /* objc-shared-cache.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498B921B658D100F165E9 /* objc-shared-cache.h */; }; - 301498F421B658D100F165E9 /* dyld_priv.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498BB21B658D100F165E9 /* dyld_priv.h */; }; - 301498F521B658D100F165E9 /* OSCrossEndian.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498BD21B658D100F165E9 /* OSCrossEndian.h */; }; - 301498F721B65D0A00F165E9 /* isa.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498F621B65D0A00F165E9 /* isa.h */; }; - 3014990221B6617600F165E9 /* lock_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498F821B6617600F165E9 /* lock_private.h */; }; - 3014990321B6617600F165E9 /* internal_shared.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498FA21B6617600F165E9 /* internal_shared.h */; }; - 3014990421B6617600F165E9 /* crashlog.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498FB21B6617600F165E9 /* crashlog.h */; }; - 3014990521B6617600F165E9 /* atomic.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498FC21B6617600F165E9 /* atomic.h */; }; - 3014990621B6617600F165E9 /* lock.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498FD21B6617600F165E9 /* lock.h */; }; - 3014990721B6617600F165E9 /* alloc_once_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498FE21B6617600F165E9 /* alloc_once_impl.h */; }; - 3014990821B6617600F165E9 /* semaphore_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 301498FF21B6617600F165E9 /* semaphore_private.h */; }; - 3014990921B6617600F165E9 /* base_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014990021B6617600F165E9 /* base_private.h */; }; - 3014990A21B6617600F165E9 /* once_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3014990121B6617600F165E9 /* once_private.h */; }; - 30466D8721B665A500DA7B9D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 30466D8621B665A500DA7B9D /* main.m */; }; + 3044E22323D4626F006763E3 /* _simple 3.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1D423D4626F006763E3 /* _simple 3.h */; }; + 3044E22423D4626F006763E3 /* CrashReporterClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1D523D4626F006763E3 /* CrashReporterClient.h */; }; + 3044E22523D4626F006763E3 /* Block_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1D623D4626F006763E3 /* Block_private.h */; }; + 3044E22623D4626F006763E3 /* _simple.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1D723D4626F006763E3 /* _simple.h */; }; + 3044E22723D4626F006763E3 /* queue_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1D923D4626F006763E3 /* queue_private.h */; }; + 3044E22823D4626F006763E3 /* private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1DA23D4626F006763E3 /* private.h */; }; + 3044E22923D4626F006763E3 /* source_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1DB23D4626F006763E3 /* source_private.h */; }; + 3044E22A23D4626F006763E3 /* benchmark.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1DC23D4626F006763E3 /* benchmark.h */; }; + 3044E22B23D4626F006763E3 /* spinlock_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1DE23D4626F006763E3 /* spinlock_private.h */; }; + 3044E22C23D4626F006763E3 /* tsd_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1DF23D4626F006763E3 /* tsd_private.h */; }; + 3044E22D23D4626F006763E3 /* qos_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1E023D4626F006763E3 /* qos_private.h */; }; + 3044E22E23D4626F006763E3 /* workqueue_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1E123D4626F006763E3 /* workqueue_private.h */; }; + 3044E22F23D4626F006763E3 /* pthread_machdep.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1E323D4626F006763E3 /* pthread_machdep.h */; }; + 3044E23023D4626F006763E3 /* cpu_capabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1E523D4626F006763E3 /* cpu_capabilities.h */; }; + 3044E23123D4626F006763E3 /* cpu_capabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1E723D4626F006763E3 /* cpu_capabilities.h */; }; + 3044E23223D4626F006763E3 /* cpu_capabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1E923D4626F006763E3 /* cpu_capabilities.h */; }; + 3044E23323D4626F006763E3 /* auto_zone.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1EA23D4626F006763E3 /* auto_zone.h */; }; + 3044E23423D4626F006763E3 /* reason.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1EC23D4626F006763E3 /* reason.h */; }; + 3044E23523D4626F006763E3 /* qos_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1ED23D4626F006763E3 /* qos_private.h */; }; + 3044E23623D4626F006763E3 /* ast_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1EF23D4626F006763E3 /* ast_types.h */; }; + 3044E23723D4626F006763E3 /* trap.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1F023D4626F006763E3 /* trap.h */; }; + 3044E23823D4626F006763E3 /* machparam.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1F123D4626F006763E3 /* machparam.h */; }; + 3044E23923D4626F006763E3 /* xpr.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1F223D4626F006763E3 /* xpr.h */; }; + 3044E23A23D4626F006763E3 /* task.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1F323D4626F006763E3 /* task.h */; }; + 3044E23B23D4626F006763E3 /* vm_tuning.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1F423D4626F006763E3 /* vm_tuning.h */; }; + 3044E23C23D4626F006763E3 /* endian.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1F523D4626F006763E3 /* endian.h */; }; + 3044E23E23D4626F006763E3 /* lock.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1F723D4626F006763E3 /* lock.h */; }; + 3044E23F23D4626F006763E3 /* db_machdep.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1F823D4626F006763E3 /* db_machdep.h */; }; + 3044E24023D4626F006763E3 /* cpu_data.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1F923D4626F006763E3 /* cpu_data.h */; }; + 3044E24123D4626F006763E3 /* pmap.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1FA23D4626F006763E3 /* pmap.h */; }; + 3044E24223D4626F006763E3 /* simple_lock.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1FB23D4626F006763E3 /* simple_lock.h */; }; + 3044E24323D4626F006763E3 /* io_map_entries.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1FC23D4626F006763E3 /* io_map_entries.h */; }; + 3044E24423D4626F006763E3 /* cpu_number.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1FD23D4626F006763E3 /* cpu_number.h */; }; + 3044E24523D4626F006763E3 /* setjmp.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1FE23D4626F006763E3 /* setjmp.h */; }; + 3044E24623D4626F006763E3 /* asm.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E1FF23D4626F006763E3 /* asm.h */; }; + 3044E24723D4626F006763E3 /* timer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20023D4626F006763E3 /* timer.h */; }; + 3044E24823D4626F006763E3 /* sched_param.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20123D4626F006763E3 /* sched_param.h */; }; + 3044E24923D4626F006763E3 /* thread.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20223D4626F006763E3 /* thread.h */; }; + 3044E24A23D4626F006763E3 /* ast.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20323D4626F006763E3 /* ast.h */; }; + 3044E24B23D4626F006763E3 /* machine_rpc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20423D4626F006763E3 /* machine_rpc.h */; }; + 3044E24C23D4626F006763E3 /* machine_cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20523D4626F006763E3 /* machine_cpu.h */; }; + 3044E24D23D4626F006763E3 /* commpage.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20623D4626F006763E3 /* commpage.h */; }; + 3044E24E23D4626F006763E3 /* machine_routines.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20723D4626F006763E3 /* machine_routines.h */; }; + 3044E24F23D4626F006763E3 /* cpu_capabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20823D4626F006763E3 /* cpu_capabilities.h */; }; + 3044E25023D4626F006763E3 /* cpu_affinity.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20923D4626F006763E3 /* cpu_affinity.h */; }; + 3044E25123D4626F006763E3 /* machlimits.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20A23D4626F006763E3 /* machlimits.h */; }; + 3044E25223D4626F006763E3 /* locks.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20B23D4626F006763E3 /* locks.h */; }; + 3044E25323D4626F006763E3 /* vproc_priv.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20C23D4626F006763E3 /* vproc_priv.h */; }; + 3044E25423D4626F006763E3 /* notify_server.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20E23D4626F006763E3 /* notify_server.h */; }; + 3044E25523D4626F006763E3 /* clock_reply_server.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E20F23D4626F006763E3 /* clock_reply_server.h */; }; + 3044E25623D4626F006763E3 /* mach_exc_server.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E21023D4626F006763E3 /* mach_exc_server.h */; }; + 3044E25723D4626F006763E3 /* exc_server.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E21123D4626F006763E3 /* exc_server.h */; }; + 3044E25823D4626F006763E3 /* lock_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E21323D4626F006763E3 /* lock_private.h */; }; + 3044E25923D4626F006763E3 /* tsd.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E21423D4626F006763E3 /* tsd.h */; }; + 3044E25A23D4626F006763E3 /* lock.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E21523D4626F006763E3 /* lock.h */; }; + 3044E25B23D4626F006763E3 /* internal_shared.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E21723D4626F006763E3 /* internal_shared.h */; }; + 3044E25C23D4626F006763E3 /* crashlog.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E21823D4626F006763E3 /* crashlog.h */; }; + 3044E25D23D4626F006763E3 /* atomic.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E21923D4626F006763E3 /* atomic.h */; }; + 3044E25E23D4626F006763E3 /* base_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E21A23D4626F006763E3 /* base_private.h */; }; + 3044E25F23D4626F006763E3 /* semaphore_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E21B23D4626F006763E3 /* semaphore_private.h */; }; + 3044E26023D4626F006763E3 /* alloc_once_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E21C23D4626F006763E3 /* alloc_once_impl.h */; }; + 3044E26123D4626F006763E3 /* once_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E21D23D4626F006763E3 /* once_private.h */; }; + 3044E26223D4626F006763E3 /* objc-shared-cache.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E21E23D4626F006763E3 /* objc-shared-cache.h */; }; + 3044E26323D4626F006763E3 /* dyld_priv.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E22023D4626F006763E3 /* dyld_priv.h */; }; + 3044E26423D4626F006763E3 /* OSCrossEndian.h in Headers */ = {isa = PBXBuildFile; fileRef = 3044E22223D4626F006763E3 /* OSCrossEndian.h */; }; + 3044E26C23D47C0E006763E3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3044E26B23D47C0E006763E3 /* main.m */; }; + 3044E27223D47C4F006763E3 /* libobjc.A.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D2AAC0630554660B00DB518D /* libobjc.A.dylib */; }; 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 */; }; 39ABD72312F0B61800D1054C /* objc-weak.h in Headers */ = {isa = PBXBuildFile; fileRef = 39ABD71F12F0B61800D1054C /* objc-weak.h */; }; @@ -155,6 +166,7 @@ 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, ); }; }; 83A4AEDC1EA0840800ACADDE /* module.modulemap in Headers */ = {isa = PBXBuildFile; fileRef = 83A4AED71EA06D9D00ACADDE /* module.modulemap */; settings = {ATTRIBUTES = (Public, ); }; }; + 83A4AEDE1EA08C7200ACADDE /* ObjectiveC.apinotes in Headers */ = {isa = PBXBuildFile; fileRef = 83A4AEDD1EA08C5700ACADDE /* ObjectiveC.apinotes */; settings = {ATTRIBUTES = (Public, ); }; }; 83B1A8BE0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 83B1A8BC0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s */; }; 83BE02E40FCCB23400661494 /* objc-file-old.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83BE02E30FCCB23400661494 /* objc-file-old.mm */; }; 83BE02E80FCCB24D00661494 /* objc-file-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E50FCCB24D00661494 /* objc-file-old.h */; }; @@ -162,6 +174,8 @@ 83BE02EA0FCCB24D00661494 /* objc-runtime-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E70FCCB24D00661494 /* objc-runtime-old.h */; }; 83C9C3391668B50E00F4E544 /* objc-msg-simulator-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = 83C9C3381668B50E00F4E544 /* objc-msg-simulator-x86_64.s */; }; 83D49E4F13C7C84F0057F1DD /* objc-msg-arm64.s in Sources */ = {isa = PBXBuildFile; fileRef = 83D49E4E13C7C84F0057F1DD /* objc-msg-arm64.s */; }; + 83D92696212254CF00299F69 /* isa.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D92695212254CF00299F69 /* isa.h */; }; + 83D9269821225A7400299F69 /* arm64-asm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D9269721225A7400299F69 /* arm64-asm.h */; }; 83EB007B121C9EC200B92C16 /* objc-sel-table.s in Sources */ = {isa = PBXBuildFile; fileRef = 83EB007A121C9EC200B92C16 /* objc-sel-table.s */; }; 83EF5E9820D2298400F486A4 /* objc-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9C116AB2820071B552 /* objc-blocktramps-i386.s */; }; 83EF5E9920D2298400F486A4 /* objc-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9D116AB2820071B552 /* objc-blocktramps-x86_64.s */; }; @@ -176,6 +190,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 3044E27023D47C4A006763E3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D2AAC0620554660B00DB518D; + remoteInfo = objc; + }; 837F67AC1A771F6E004D34FA /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; @@ -193,7 +214,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 30466D8221B665A500DA7B9D /* CopyFiles */ = { + 3044E26723D47C0E006763E3 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; @@ -205,74 +226,74 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 3014987A21B658D100F165E9 /* CrashReporterClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CrashReporterClient.h; sourceTree = ""; }; - 3014987B21B658D100F165E9 /* Block_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Block_private.h; sourceTree = ""; }; - 3014987C21B658D100F165E9 /* _simple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _simple.h; sourceTree = ""; }; - 3014987E21B658D100F165E9 /* queue_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = queue_private.h; sourceTree = ""; }; - 3014987F21B658D100F165E9 /* private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = private.h; sourceTree = ""; }; - 3014988021B658D100F165E9 /* source_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = source_private.h; sourceTree = ""; }; - 3014988121B658D100F165E9 /* benchmark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = benchmark.h; sourceTree = ""; }; - 3014988321B658D100F165E9 /* spinlock_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = spinlock_private.h; sourceTree = ""; }; - 3014988421B658D100F165E9 /* tsd_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tsd_private.h; sourceTree = ""; }; - 3014988521B658D100F165E9 /* qos_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qos_private.h; sourceTree = ""; }; - 3014988621B658D100F165E9 /* workqueue_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = workqueue_private.h; sourceTree = ""; }; - 3014988821B658D100F165E9 /* pthread_machdep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pthread_machdep.h; sourceTree = ""; }; - 3014988A21B658D100F165E9 /* cpu_capabilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_capabilities.h; sourceTree = ""; }; - 3014988C21B658D100F165E9 /* cpu_capabilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_capabilities.h; sourceTree = ""; }; - 3014988E21B658D100F165E9 /* cpu_capabilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_capabilities.h; sourceTree = ""; }; - 3014988F21B658D100F165E9 /* auto_zone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = auto_zone.h; sourceTree = ""; }; - 3014989121B658D100F165E9 /* reason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = reason.h; sourceTree = ""; }; - 3014989221B658D100F165E9 /* qos_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qos_private.h; sourceTree = ""; }; - 3014989421B658D100F165E9 /* ast_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ast_types.h; sourceTree = ""; }; - 3014989521B658D100F165E9 /* trap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = trap.h; sourceTree = ""; }; - 3014989621B658D100F165E9 /* machparam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machparam.h; sourceTree = ""; }; - 3014989721B658D100F165E9 /* xpr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xpr.h; sourceTree = ""; }; - 3014989821B658D100F165E9 /* task.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = task.h; sourceTree = ""; }; - 3014989921B658D100F165E9 /* vm_tuning.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vm_tuning.h; sourceTree = ""; }; - 3014989A21B658D100F165E9 /* endian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = endian.h; sourceTree = ""; }; - 3014989B21B658D100F165E9 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; - 3014989C21B658D100F165E9 /* lock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lock.h; sourceTree = ""; }; - 3014989D21B658D100F165E9 /* db_machdep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = db_machdep.h; sourceTree = ""; }; - 3014989E21B658D100F165E9 /* cpu_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_data.h; sourceTree = ""; }; - 3014989F21B658D100F165E9 /* pmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pmap.h; sourceTree = ""; }; - 301498A021B658D100F165E9 /* simple_lock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simple_lock.h; sourceTree = ""; }; - 301498A121B658D100F165E9 /* io_map_entries.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = io_map_entries.h; sourceTree = ""; }; - 301498A221B658D100F165E9 /* cpu_number.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_number.h; sourceTree = ""; }; - 301498A321B658D100F165E9 /* setjmp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = setjmp.h; sourceTree = ""; }; - 301498A421B658D100F165E9 /* asm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asm.h; sourceTree = ""; }; - 301498A521B658D100F165E9 /* timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = timer.h; sourceTree = ""; }; - 301498A621B658D100F165E9 /* sched_param.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sched_param.h; sourceTree = ""; }; - 301498A721B658D100F165E9 /* thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = thread.h; sourceTree = ""; }; - 301498A821B658D100F165E9 /* ast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ast.h; sourceTree = ""; }; - 301498A921B658D100F165E9 /* machine_rpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machine_rpc.h; sourceTree = ""; }; - 301498AA21B658D100F165E9 /* machine_cpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machine_cpu.h; sourceTree = ""; }; - 301498AB21B658D100F165E9 /* commpage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = commpage.h; sourceTree = ""; }; - 301498AC21B658D100F165E9 /* machine_routines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machine_routines.h; sourceTree = ""; }; - 301498AD21B658D100F165E9 /* cpu_capabilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_capabilities.h; sourceTree = ""; }; - 301498AE21B658D100F165E9 /* cpu_affinity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_affinity.h; sourceTree = ""; }; - 301498AF21B658D100F165E9 /* machlimits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machlimits.h; sourceTree = ""; }; - 301498B021B658D100F165E9 /* locks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = locks.h; sourceTree = ""; }; - 301498B121B658D100F165E9 /* vproc_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vproc_priv.h; sourceTree = ""; }; - 301498B321B658D100F165E9 /* notify_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = notify_server.h; sourceTree = ""; }; - 301498B421B658D100F165E9 /* clock_reply_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = clock_reply_server.h; sourceTree = ""; }; - 301498B521B658D100F165E9 /* mach_exc_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mach_exc_server.h; sourceTree = ""; }; - 301498B621B658D100F165E9 /* exc_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exc_server.h; sourceTree = ""; }; - 301498B821B658D100F165E9 /* tsd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tsd.h; sourceTree = ""; }; - 301498B921B658D100F165E9 /* objc-shared-cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "objc-shared-cache.h"; sourceTree = ""; }; - 301498BB21B658D100F165E9 /* dyld_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dyld_priv.h; sourceTree = ""; }; - 301498BD21B658D100F165E9 /* OSCrossEndian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSCrossEndian.h; sourceTree = ""; }; - 301498F621B65D0A00F165E9 /* isa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = isa.h; path = runtime/isa.h; sourceTree = ""; }; - 301498F821B6617600F165E9 /* lock_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lock_private.h; sourceTree = ""; }; - 301498FA21B6617600F165E9 /* internal_shared.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = internal_shared.h; sourceTree = ""; }; - 301498FB21B6617600F165E9 /* crashlog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crashlog.h; sourceTree = ""; }; - 301498FC21B6617600F165E9 /* atomic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = atomic.h; sourceTree = ""; }; - 301498FD21B6617600F165E9 /* lock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lock.h; sourceTree = ""; }; - 301498FE21B6617600F165E9 /* alloc_once_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = alloc_once_impl.h; sourceTree = ""; }; - 301498FF21B6617600F165E9 /* semaphore_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = semaphore_private.h; sourceTree = ""; }; - 3014990021B6617600F165E9 /* base_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base_private.h; sourceTree = ""; }; - 3014990121B6617600F165E9 /* once_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = once_private.h; sourceTree = ""; }; - 30466D8421B665A500DA7B9D /* debug-objc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "debug-objc"; sourceTree = BUILT_PRODUCTS_DIR; }; - 30466D8621B665A500DA7B9D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 3044E1D423D4626F006763E3 /* _simple 3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "_simple 3.h"; sourceTree = ""; }; + 3044E1D523D4626F006763E3 /* CrashReporterClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CrashReporterClient.h; sourceTree = ""; }; + 3044E1D623D4626F006763E3 /* Block_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Block_private.h; sourceTree = ""; }; + 3044E1D723D4626F006763E3 /* _simple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _simple.h; sourceTree = ""; }; + 3044E1D923D4626F006763E3 /* queue_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = queue_private.h; sourceTree = ""; }; + 3044E1DA23D4626F006763E3 /* private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = private.h; sourceTree = ""; }; + 3044E1DB23D4626F006763E3 /* source_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = source_private.h; sourceTree = ""; }; + 3044E1DC23D4626F006763E3 /* benchmark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = benchmark.h; sourceTree = ""; }; + 3044E1DE23D4626F006763E3 /* spinlock_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = spinlock_private.h; sourceTree = ""; }; + 3044E1DF23D4626F006763E3 /* tsd_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tsd_private.h; sourceTree = ""; }; + 3044E1E023D4626F006763E3 /* qos_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qos_private.h; sourceTree = ""; }; + 3044E1E123D4626F006763E3 /* workqueue_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = workqueue_private.h; sourceTree = ""; }; + 3044E1E323D4626F006763E3 /* pthread_machdep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pthread_machdep.h; sourceTree = ""; }; + 3044E1E523D4626F006763E3 /* cpu_capabilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_capabilities.h; sourceTree = ""; }; + 3044E1E723D4626F006763E3 /* cpu_capabilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_capabilities.h; sourceTree = ""; }; + 3044E1E923D4626F006763E3 /* cpu_capabilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_capabilities.h; sourceTree = ""; }; + 3044E1EA23D4626F006763E3 /* auto_zone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = auto_zone.h; sourceTree = ""; }; + 3044E1EC23D4626F006763E3 /* reason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = reason.h; sourceTree = ""; }; + 3044E1ED23D4626F006763E3 /* qos_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qos_private.h; sourceTree = ""; }; + 3044E1EF23D4626F006763E3 /* ast_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ast_types.h; sourceTree = ""; }; + 3044E1F023D4626F006763E3 /* trap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = trap.h; sourceTree = ""; }; + 3044E1F123D4626F006763E3 /* machparam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machparam.h; sourceTree = ""; }; + 3044E1F223D4626F006763E3 /* xpr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xpr.h; sourceTree = ""; }; + 3044E1F323D4626F006763E3 /* task.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = task.h; sourceTree = ""; }; + 3044E1F423D4626F006763E3 /* vm_tuning.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vm_tuning.h; sourceTree = ""; }; + 3044E1F523D4626F006763E3 /* endian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = endian.h; sourceTree = ""; }; + 3044E1F623D4626F006763E3 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; + 3044E1F723D4626F006763E3 /* lock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lock.h; sourceTree = ""; }; + 3044E1F823D4626F006763E3 /* db_machdep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = db_machdep.h; sourceTree = ""; }; + 3044E1F923D4626F006763E3 /* cpu_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_data.h; sourceTree = ""; }; + 3044E1FA23D4626F006763E3 /* pmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pmap.h; sourceTree = ""; }; + 3044E1FB23D4626F006763E3 /* simple_lock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simple_lock.h; sourceTree = ""; }; + 3044E1FC23D4626F006763E3 /* io_map_entries.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = io_map_entries.h; sourceTree = ""; }; + 3044E1FD23D4626F006763E3 /* cpu_number.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_number.h; sourceTree = ""; }; + 3044E1FE23D4626F006763E3 /* setjmp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = setjmp.h; sourceTree = ""; }; + 3044E1FF23D4626F006763E3 /* asm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asm.h; sourceTree = ""; }; + 3044E20023D4626F006763E3 /* timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = timer.h; sourceTree = ""; }; + 3044E20123D4626F006763E3 /* sched_param.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sched_param.h; sourceTree = ""; }; + 3044E20223D4626F006763E3 /* thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = thread.h; sourceTree = ""; }; + 3044E20323D4626F006763E3 /* ast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ast.h; sourceTree = ""; }; + 3044E20423D4626F006763E3 /* machine_rpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machine_rpc.h; sourceTree = ""; }; + 3044E20523D4626F006763E3 /* machine_cpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machine_cpu.h; sourceTree = ""; }; + 3044E20623D4626F006763E3 /* commpage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = commpage.h; sourceTree = ""; }; + 3044E20723D4626F006763E3 /* machine_routines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machine_routines.h; sourceTree = ""; }; + 3044E20823D4626F006763E3 /* cpu_capabilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_capabilities.h; sourceTree = ""; }; + 3044E20923D4626F006763E3 /* cpu_affinity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu_affinity.h; sourceTree = ""; }; + 3044E20A23D4626F006763E3 /* machlimits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machlimits.h; sourceTree = ""; }; + 3044E20B23D4626F006763E3 /* locks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = locks.h; sourceTree = ""; }; + 3044E20C23D4626F006763E3 /* vproc_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vproc_priv.h; sourceTree = ""; }; + 3044E20E23D4626F006763E3 /* notify_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = notify_server.h; sourceTree = ""; }; + 3044E20F23D4626F006763E3 /* clock_reply_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = clock_reply_server.h; sourceTree = ""; }; + 3044E21023D4626F006763E3 /* mach_exc_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mach_exc_server.h; sourceTree = ""; }; + 3044E21123D4626F006763E3 /* exc_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exc_server.h; sourceTree = ""; }; + 3044E21323D4626F006763E3 /* lock_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lock_private.h; sourceTree = ""; }; + 3044E21423D4626F006763E3 /* tsd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tsd.h; sourceTree = ""; }; + 3044E21523D4626F006763E3 /* lock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lock.h; sourceTree = ""; }; + 3044E21723D4626F006763E3 /* internal_shared.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = internal_shared.h; sourceTree = ""; }; + 3044E21823D4626F006763E3 /* crashlog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crashlog.h; sourceTree = ""; }; + 3044E21923D4626F006763E3 /* atomic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = atomic.h; sourceTree = ""; }; + 3044E21A23D4626F006763E3 /* base_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base_private.h; sourceTree = ""; }; + 3044E21B23D4626F006763E3 /* semaphore_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = semaphore_private.h; sourceTree = ""; }; + 3044E21C23D4626F006763E3 /* alloc_once_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = alloc_once_impl.h; sourceTree = ""; }; + 3044E21D23D4626F006763E3 /* once_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = once_private.h; sourceTree = ""; }; + 3044E21E23D4626F006763E3 /* objc-shared-cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "objc-shared-cache.h"; sourceTree = ""; }; + 3044E22023D4626F006763E3 /* dyld_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dyld_priv.h; sourceTree = ""; }; + 3044E22223D4626F006763E3 /* OSCrossEndian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSCrossEndian.h; sourceTree = ""; }; + 3044E26923D47C0E006763E3 /* debug-objc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "debug-objc"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3044E26B23D47C0E006763E3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 393CEABF0DC69E3E000B69DE /* objc-references.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-references.mm"; path = "runtime/objc-references.mm"; sourceTree = ""; }; 393CEAC50DC69E67000B69DE /* objc-references.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-references.h"; path = "runtime/objc-references.h"; sourceTree = ""; }; 39ABD71F12F0B61800D1054C /* objc-weak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-weak.h"; path = "runtime/objc-weak.h"; sourceTree = ""; }; @@ -355,6 +376,8 @@ 83C9C3381668B50E00F4E544 /* objc-msg-simulator-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-simulator-x86_64.s"; path = "runtime/Messengers.subproj/objc-msg-simulator-x86_64.s"; sourceTree = ""; }; 83CE671D1E6E76B60095A33E /* interposable.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = interposable.txt; sourceTree = ""; }; 83D49E4E13C7C84F0057F1DD /* objc-msg-arm64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-arm64.s"; path = "runtime/Messengers.subproj/objc-msg-arm64.s"; sourceTree = ""; }; + 83D92695212254CF00299F69 /* isa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = isa.h; path = runtime/isa.h; sourceTree = ""; }; + 83D9269721225A7400299F69 /* arm64-asm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "arm64-asm.h"; path = "runtime/arm64-asm.h"; sourceTree = ""; }; 83EB007A121C9EC200B92C16 /* objc-sel-table.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-sel-table.s"; path = "runtime/objc-sel-table.s"; sourceTree = ""; }; 83F4B52615E843B100E0926F /* NSObjCRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSObjCRuntime.h; path = runtime/NSObjCRuntime.h; sourceTree = ""; }; 83F4B52715E843B100E0926F /* NSObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSObject.h; path = runtime/NSObject.h; sourceTree = ""; }; @@ -370,10 +393,11 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 30466D8121B665A500DA7B9D /* Frameworks */ = { + 3044E26623D47C0E006763E3 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 3044E27223D47C4F006763E3 /* libobjc.A.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -397,8 +421,8 @@ 08FB7794FE84155DC02AAC07 /* objc */ = { isa = PBXGroup; children = ( - 3014987921B658D100F165E9 /* include */, BC8B5D1212D3D48100C78A5B /* libauto.dylib */, + 3044E1D323D4626F006763E3 /* include */, 838485C60D6D687700CEA253 /* Public Headers */, 838485C70D6D688200CEA253 /* Private Headers */, 8384862A0D6D6ABC00CEA253 /* Project Headers */, @@ -406,7 +430,7 @@ 838486270D6D690F00CEA253 /* Obsolete Source */, 08FB7795FE84155DC02AAC07 /* Source */, 838485B20D6D67F900CEA253 /* Other */, - 30466D8521B665A500DA7B9D /* debug-objc */, + 3044E26A23D47C0E006763E3 /* debug-objc */, 1AB674ADFE9D54B511CA2CBB /* Products */, F9BCC72A205C6A1600DD9AFC /* Frameworks */, ); @@ -469,191 +493,192 @@ children = ( D2AAC0630554660B00DB518D /* libobjc.A.dylib */, F9BCC727205C68E800DD9AFC /* libobjc-trampolines.dylib */, - 30466D8421B665A500DA7B9D /* debug-objc */, + 3044E26923D47C0E006763E3 /* debug-objc */, ); name = Products; sourceTree = ""; }; - 3014987921B658D100F165E9 /* include */ = { + 3044E1D323D4626F006763E3 /* include */ = { isa = PBXGroup; children = ( - 3014987A21B658D100F165E9 /* CrashReporterClient.h */, - 3014987B21B658D100F165E9 /* Block_private.h */, - 3014987C21B658D100F165E9 /* _simple.h */, - 3014987D21B658D100F165E9 /* dispatch */, - 3014988221B658D100F165E9 /* pthread */, - 3014988721B658D100F165E9 /* System */, - 3014988F21B658D100F165E9 /* auto_zone.h */, - 3014989021B658D100F165E9 /* sys */, - 3014989321B658D100F165E9 /* machine */, - 301498B121B658D100F165E9 /* vproc_priv.h */, - 301498B221B658D100F165E9 /* mach */, - 301498B721B658D100F165E9 /* os */, - 301498B921B658D100F165E9 /* objc-shared-cache.h */, - 301498BA21B658D100F165E9 /* mach-o */, - 301498BC21B658D100F165E9 /* libkern */, + 3044E1D423D4626F006763E3 /* _simple 3.h */, + 3044E1D523D4626F006763E3 /* CrashReporterClient.h */, + 3044E1D623D4626F006763E3 /* Block_private.h */, + 3044E1D723D4626F006763E3 /* _simple.h */, + 3044E1D823D4626F006763E3 /* dispatch */, + 3044E1DD23D4626F006763E3 /* pthread */, + 3044E1E223D4626F006763E3 /* System */, + 3044E1EA23D4626F006763E3 /* auto_zone.h */, + 3044E1EB23D4626F006763E3 /* sys */, + 3044E1EE23D4626F006763E3 /* machine */, + 3044E20C23D4626F006763E3 /* vproc_priv.h */, + 3044E20D23D4626F006763E3 /* mach */, + 3044E21223D4626F006763E3 /* os */, + 3044E21E23D4626F006763E3 /* objc-shared-cache.h */, + 3044E21F23D4626F006763E3 /* mach-o */, + 3044E22123D4626F006763E3 /* libkern */, ); path = include; sourceTree = ""; }; - 3014987D21B658D100F165E9 /* dispatch */ = { + 3044E1D823D4626F006763E3 /* dispatch */ = { isa = PBXGroup; children = ( - 3014987E21B658D100F165E9 /* queue_private.h */, - 3014987F21B658D100F165E9 /* private.h */, - 3014988021B658D100F165E9 /* source_private.h */, - 3014988121B658D100F165E9 /* benchmark.h */, + 3044E1D923D4626F006763E3 /* queue_private.h */, + 3044E1DA23D4626F006763E3 /* private.h */, + 3044E1DB23D4626F006763E3 /* source_private.h */, + 3044E1DC23D4626F006763E3 /* benchmark.h */, ); path = dispatch; sourceTree = ""; }; - 3014988221B658D100F165E9 /* pthread */ = { + 3044E1DD23D4626F006763E3 /* pthread */ = { isa = PBXGroup; children = ( - 3014988321B658D100F165E9 /* spinlock_private.h */, - 3014988421B658D100F165E9 /* tsd_private.h */, - 3014988521B658D100F165E9 /* qos_private.h */, - 3014988621B658D100F165E9 /* workqueue_private.h */, + 3044E1DE23D4626F006763E3 /* spinlock_private.h */, + 3044E1DF23D4626F006763E3 /* tsd_private.h */, + 3044E1E023D4626F006763E3 /* qos_private.h */, + 3044E1E123D4626F006763E3 /* workqueue_private.h */, ); path = pthread; sourceTree = ""; }; - 3014988721B658D100F165E9 /* System */ = { + 3044E1E223D4626F006763E3 /* System */ = { isa = PBXGroup; children = ( - 3014988821B658D100F165E9 /* pthread_machdep.h */, - 3014988921B658D100F165E9 /* i386 */, - 3014988B21B658D100F165E9 /* ppc */, - 3014988D21B658D100F165E9 /* machine */, + 3044E1E323D4626F006763E3 /* pthread_machdep.h */, + 3044E1E423D4626F006763E3 /* i386 */, + 3044E1E623D4626F006763E3 /* ppc */, + 3044E1E823D4626F006763E3 /* machine */, ); path = System; sourceTree = ""; }; - 3014988921B658D100F165E9 /* i386 */ = { + 3044E1E423D4626F006763E3 /* i386 */ = { isa = PBXGroup; children = ( - 3014988A21B658D100F165E9 /* cpu_capabilities.h */, + 3044E1E523D4626F006763E3 /* cpu_capabilities.h */, ); path = i386; sourceTree = ""; }; - 3014988B21B658D100F165E9 /* ppc */ = { + 3044E1E623D4626F006763E3 /* ppc */ = { isa = PBXGroup; children = ( - 3014988C21B658D100F165E9 /* cpu_capabilities.h */, + 3044E1E723D4626F006763E3 /* cpu_capabilities.h */, ); path = ppc; sourceTree = ""; }; - 3014988D21B658D100F165E9 /* machine */ = { + 3044E1E823D4626F006763E3 /* machine */ = { isa = PBXGroup; children = ( - 3014988E21B658D100F165E9 /* cpu_capabilities.h */, + 3044E1E923D4626F006763E3 /* cpu_capabilities.h */, ); path = machine; sourceTree = ""; }; - 3014989021B658D100F165E9 /* sys */ = { + 3044E1EB23D4626F006763E3 /* sys */ = { isa = PBXGroup; children = ( - 3014989121B658D100F165E9 /* reason.h */, - 3014989221B658D100F165E9 /* qos_private.h */, + 3044E1EC23D4626F006763E3 /* reason.h */, + 3044E1ED23D4626F006763E3 /* qos_private.h */, ); path = sys; sourceTree = ""; }; - 3014989321B658D100F165E9 /* machine */ = { + 3044E1EE23D4626F006763E3 /* machine */ = { isa = PBXGroup; children = ( - 3014989421B658D100F165E9 /* ast_types.h */, - 3014989521B658D100F165E9 /* trap.h */, - 3014989621B658D100F165E9 /* machparam.h */, - 3014989721B658D100F165E9 /* xpr.h */, - 3014989821B658D100F165E9 /* task.h */, - 3014989921B658D100F165E9 /* vm_tuning.h */, - 3014989A21B658D100F165E9 /* endian.h */, - 3014989B21B658D100F165E9 /* Makefile */, - 3014989C21B658D100F165E9 /* lock.h */, - 3014989D21B658D100F165E9 /* db_machdep.h */, - 3014989E21B658D100F165E9 /* cpu_data.h */, - 3014989F21B658D100F165E9 /* pmap.h */, - 301498A021B658D100F165E9 /* simple_lock.h */, - 301498A121B658D100F165E9 /* io_map_entries.h */, - 301498A221B658D100F165E9 /* cpu_number.h */, - 301498A321B658D100F165E9 /* setjmp.h */, - 301498A421B658D100F165E9 /* asm.h */, - 301498A521B658D100F165E9 /* timer.h */, - 301498A621B658D100F165E9 /* sched_param.h */, - 301498A721B658D100F165E9 /* thread.h */, - 301498A821B658D100F165E9 /* ast.h */, - 301498A921B658D100F165E9 /* machine_rpc.h */, - 301498AA21B658D100F165E9 /* machine_cpu.h */, - 301498AB21B658D100F165E9 /* commpage.h */, - 301498AC21B658D100F165E9 /* machine_routines.h */, - 301498AD21B658D100F165E9 /* cpu_capabilities.h */, - 301498AE21B658D100F165E9 /* cpu_affinity.h */, - 301498AF21B658D100F165E9 /* machlimits.h */, - 301498B021B658D100F165E9 /* locks.h */, + 3044E1EF23D4626F006763E3 /* ast_types.h */, + 3044E1F023D4626F006763E3 /* trap.h */, + 3044E1F123D4626F006763E3 /* machparam.h */, + 3044E1F223D4626F006763E3 /* xpr.h */, + 3044E1F323D4626F006763E3 /* task.h */, + 3044E1F423D4626F006763E3 /* vm_tuning.h */, + 3044E1F523D4626F006763E3 /* endian.h */, + 3044E1F623D4626F006763E3 /* Makefile */, + 3044E1F723D4626F006763E3 /* lock.h */, + 3044E1F823D4626F006763E3 /* db_machdep.h */, + 3044E1F923D4626F006763E3 /* cpu_data.h */, + 3044E1FA23D4626F006763E3 /* pmap.h */, + 3044E1FB23D4626F006763E3 /* simple_lock.h */, + 3044E1FC23D4626F006763E3 /* io_map_entries.h */, + 3044E1FD23D4626F006763E3 /* cpu_number.h */, + 3044E1FE23D4626F006763E3 /* setjmp.h */, + 3044E1FF23D4626F006763E3 /* asm.h */, + 3044E20023D4626F006763E3 /* timer.h */, + 3044E20123D4626F006763E3 /* sched_param.h */, + 3044E20223D4626F006763E3 /* thread.h */, + 3044E20323D4626F006763E3 /* ast.h */, + 3044E20423D4626F006763E3 /* machine_rpc.h */, + 3044E20523D4626F006763E3 /* machine_cpu.h */, + 3044E20623D4626F006763E3 /* commpage.h */, + 3044E20723D4626F006763E3 /* machine_routines.h */, + 3044E20823D4626F006763E3 /* cpu_capabilities.h */, + 3044E20923D4626F006763E3 /* cpu_affinity.h */, + 3044E20A23D4626F006763E3 /* machlimits.h */, + 3044E20B23D4626F006763E3 /* locks.h */, ); path = machine; sourceTree = ""; }; - 301498B221B658D100F165E9 /* mach */ = { + 3044E20D23D4626F006763E3 /* mach */ = { isa = PBXGroup; children = ( - 301498B321B658D100F165E9 /* notify_server.h */, - 301498B421B658D100F165E9 /* clock_reply_server.h */, - 301498B521B658D100F165E9 /* mach_exc_server.h */, - 301498B621B658D100F165E9 /* exc_server.h */, + 3044E20E23D4626F006763E3 /* notify_server.h */, + 3044E20F23D4626F006763E3 /* clock_reply_server.h */, + 3044E21023D4626F006763E3 /* mach_exc_server.h */, + 3044E21123D4626F006763E3 /* exc_server.h */, ); path = mach; sourceTree = ""; }; - 301498B721B658D100F165E9 /* os */ = { + 3044E21223D4626F006763E3 /* os */ = { isa = PBXGroup; children = ( - 301498FE21B6617600F165E9 /* alloc_once_impl.h */, - 3014990021B6617600F165E9 /* base_private.h */, - 301498F921B6617600F165E9 /* internal */, - 301498F821B6617600F165E9 /* lock_private.h */, - 301498FD21B6617600F165E9 /* lock.h */, - 3014990121B6617600F165E9 /* once_private.h */, - 301498FF21B6617600F165E9 /* semaphore_private.h */, - 301498B821B658D100F165E9 /* tsd.h */, + 3044E21323D4626F006763E3 /* lock_private.h */, + 3044E21423D4626F006763E3 /* tsd.h */, + 3044E21523D4626F006763E3 /* lock.h */, + 3044E21623D4626F006763E3 /* internal */, + 3044E21A23D4626F006763E3 /* base_private.h */, + 3044E21B23D4626F006763E3 /* semaphore_private.h */, + 3044E21C23D4626F006763E3 /* alloc_once_impl.h */, + 3044E21D23D4626F006763E3 /* once_private.h */, ); path = os; sourceTree = ""; }; - 301498BA21B658D100F165E9 /* mach-o */ = { + 3044E21623D4626F006763E3 /* internal */ = { isa = PBXGroup; children = ( - 301498BB21B658D100F165E9 /* dyld_priv.h */, + 3044E21723D4626F006763E3 /* internal_shared.h */, + 3044E21823D4626F006763E3 /* crashlog.h */, + 3044E21923D4626F006763E3 /* atomic.h */, ); - path = "mach-o"; + path = internal; sourceTree = ""; }; - 301498BC21B658D100F165E9 /* libkern */ = { + 3044E21F23D4626F006763E3 /* mach-o */ = { isa = PBXGroup; children = ( - 301498BD21B658D100F165E9 /* OSCrossEndian.h */, + 3044E22023D4626F006763E3 /* dyld_priv.h */, ); - path = libkern; + path = "mach-o"; sourceTree = ""; }; - 301498F921B6617600F165E9 /* internal */ = { + 3044E22123D4626F006763E3 /* libkern */ = { isa = PBXGroup; children = ( - 301498FA21B6617600F165E9 /* internal_shared.h */, - 301498FB21B6617600F165E9 /* crashlog.h */, - 301498FC21B6617600F165E9 /* atomic.h */, + 3044E22223D4626F006763E3 /* OSCrossEndian.h */, ); - path = internal; + path = libkern; sourceTree = ""; }; - 30466D8521B665A500DA7B9D /* debug-objc */ = { + 3044E26A23D47C0E006763E3 /* debug-objc */ = { isa = PBXGroup; children = ( - 30466D8621B665A500DA7B9D /* main.m */, + 3044E26B23D47C0E006763E3 /* main.m */, ); path = "debug-objc"; sourceTree = ""; @@ -703,7 +728,6 @@ 838486220D6D68E300CEA253 /* Obsolete Headers */ = { isa = PBXGroup; children = ( - 301498F621B65D0A00F165E9 /* isa.h */, 830F2A970D738DC200392440 /* hashtable.h */, 838485B70D6D687300CEA253 /* hashtable2.h */, 838485CD0D6D68A200CEA253 /* objc-class.h */, @@ -728,6 +752,8 @@ 8384862A0D6D6ABC00CEA253 /* Project Headers */ = { isa = PBXGroup; children = ( + 83D9269721225A7400299F69 /* arm64-asm.h */, + 83D92695212254CF00299F69 /* isa.h */, 838485CF0D6D68A200CEA253 /* objc-config.h */, 83BE02E50FCCB24D00661494 /* objc-file-old.h */, 83BE02E60FCCB24D00661494 /* objc-file.h */, @@ -770,130 +796,134 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 301498D821B658D100F165E9 /* lock.h in Headers */, - 301498DA21B658D100F165E9 /* cpu_data.h in Headers */, - 301498E021B658D100F165E9 /* asm.h in Headers */, - 301498E621B658D100F165E9 /* machine_cpu.h in Headers */, + 3044E23A23D4626F006763E3 /* task.h in Headers */, + 3044E25223D4626F006763E3 /* locks.h in Headers */, + 3044E23823D4626F006763E3 /* machparam.h in Headers */, + 83A4AEDE1EA08C7200ACADDE /* ObjectiveC.apinotes in Headers */, + 3044E22923D4626F006763E3 /* source_private.h in Headers */, 75A95051202BAA9A00D7D56F /* objc-locks.h in Headers */, + 3044E23E23D4626F006763E3 /* lock.h in Headers */, + 3044E26323D4626F006763E3 /* dyld_priv.h in Headers */, 83A4AEDC1EA0840800ACADDE /* module.modulemap in Headers */, - 301498EA21B658D100F165E9 /* cpu_affinity.h in Headers */, 830F2A980D738DC200392440 /* hashtable.h in Headers */, - 301498C921B658D100F165E9 /* pthread_machdep.h in Headers */, - 3014990721B6617600F165E9 /* alloc_once_impl.h in Headers */, - 3014990A21B6617600F165E9 /* once_private.h in Headers */, - 301498EC21B658D100F165E9 /* locks.h in Headers */, - 301498E721B658D100F165E9 /* commpage.h in Headers */, - 301498F521B658D100F165E9 /* OSCrossEndian.h in Headers */, - 3014990421B6617600F165E9 /* crashlog.h in Headers */, + 3044E22C23D4626F006763E3 /* tsd_private.h in Headers */, + 3044E24C23D4626F006763E3 /* machine_cpu.h in Headers */, 838485BF0D6D687300CEA253 /* hashtable2.h in Headers */, + 3044E24723D4626F006763E3 /* timer.h in Headers */, + 3044E25C23D4626F006763E3 /* crashlog.h in Headers */, + 3044E26123D4626F006763E3 /* once_private.h in Headers */, + 3044E24423D4626F006763E3 /* cpu_number.h in Headers */, + 3044E23623D4626F006763E3 /* ast_types.h in Headers */, 838486260D6D68F000CEA253 /* List.h in Headers */, - 301498F121B658D100F165E9 /* exc_server.h in Headers */, - 301498DF21B658D100F165E9 /* setjmp.h in Headers */, - 301498C221B658D100F165E9 /* private.h in Headers */, - 301498DC21B658D100F165E9 /* simple_lock.h in Headers */, - 301498CD21B658D100F165E9 /* auto_zone.h in Headers */, 838485C30D6D687300CEA253 /* maptable.h in Headers */, - 301498C621B658D100F165E9 /* tsd_private.h in Headers */, 838486280D6D6A2400CEA253 /* message.h in Headers */, 834EC0A411614167009B2563 /* objc-abi.h in Headers */, - 301498D621B658D100F165E9 /* endian.h in Headers */, - 301498F321B658D100F165E9 /* objc-shared-cache.h in Headers */, - 301498D921B658D100F165E9 /* db_machdep.h in Headers */, - 301498DE21B658D100F165E9 /* cpu_number.h in Headers */, + 3044E24623D4626F006763E3 /* asm.h in Headers */, 838485EF0D6D68A200CEA253 /* objc-api.h in Headers */, - 301498CA21B658D100F165E9 /* cpu_capabilities.h in Headers */, + 3044E22D23D4626F006763E3 /* qos_private.h in Headers */, + 3044E23323D4626F006763E3 /* auto_zone.h in Headers */, 838485F00D6D68A200CEA253 /* objc-auto.h in Headers */, - 301498F221B658D100F165E9 /* tsd.h in Headers */, - 3014990321B6617600F165E9 /* internal_shared.h in Headers */, 838485F40D6D68A200CEA253 /* objc-class.h in Headers */, 838485F60D6D68A200CEA253 /* objc-config.h in Headers */, - 301498ED21B658D100F165E9 /* vproc_priv.h in Headers */, + 3044E24E23D4626F006763E3 /* machine_routines.h in Headers */, + 3044E22F23D4626F006763E3 /* pthread_machdep.h in Headers */, + 3044E23223D4626F006763E3 /* cpu_capabilities.h in Headers */, + 3044E23923D4626F006763E3 /* xpr.h in Headers */, + 3044E23123D4626F006763E3 /* cpu_capabilities.h in Headers */, + 3044E23423D4626F006763E3 /* reason.h in Headers */, + 3044E25B23D4626F006763E3 /* internal_shared.h in Headers */, + 3044E25423D4626F006763E3 /* notify_server.h in Headers */, 838485F80D6D68A200CEA253 /* objc-exception.h in Headers */, + 3044E26423D4626F006763E3 /* OSCrossEndian.h in Headers */, + 3044E22523D4626F006763E3 /* Block_private.h in Headers */, + 3044E25123D4626F006763E3 /* machlimits.h in Headers */, 83BE02E80FCCB24D00661494 /* objc-file-old.h in Headers */, + 3044E24D23D4626F006763E3 /* commpage.h in Headers */, 83BE02E90FCCB24D00661494 /* objc-file.h in Headers */, - 301498C721B658D100F165E9 /* qos_private.h in Headers */, - 3014990221B6617600F165E9 /* lock_private.h in Headers */, + 3044E24123D4626F006763E3 /* pmap.h in Headers */, 75A9504F202BAA0600D7D56F /* objc-locks-new.h in Headers */, + 3044E25723D4626F006763E3 /* exc_server.h in Headers */, + 3044E22323D4626F006763E3 /* _simple 3.h in Headers */, 834266D80E665A8B002E4DA2 /* objc-gdb.h in Headers */, + 3044E23C23D4626F006763E3 /* endian.h in Headers */, 838485FB0D6D68A200CEA253 /* objc-initialize.h in Headers */, - 3014990621B6617600F165E9 /* lock.h in Headers */, - 301498E421B658D100F165E9 /* ast.h in Headers */, + 3044E25323D4626F006763E3 /* vproc_priv.h in Headers */, 7593EC58202248E50046AB96 /* objc-object.h in Headers */, - 301498E821B658D100F165E9 /* machine_routines.h in Headers */, - 301498E121B658D100F165E9 /* timer.h in Headers */, + 3044E24223D4626F006763E3 /* simple_lock.h in Headers */, 83112ED40F00599600A5FBAF /* objc-internal.h in Headers */, 838485FE0D6D68A200CEA253 /* objc-load.h in Headers */, - 301498EE21B658D100F165E9 /* notify_server.h in Headers */, - 301498BE21B658D100F165E9 /* CrashReporterClient.h in Headers */, - 301498F021B658D100F165E9 /* mach_exc_server.h in Headers */, + 3044E25923D4626F006763E3 /* tsd.h in Headers */, + 3044E23723D4626F006763E3 /* trap.h in Headers */, + 3044E25023D4626F006763E3 /* cpu_affinity.h in Headers */, + 3044E23F23D4626F006763E3 /* db_machdep.h in Headers */, + 3044E24923D4626F006763E3 /* thread.h in Headers */, + 3044E22623D4626F006763E3 /* _simple.h in Headers */, + 3044E26223D4626F006763E3 /* objc-shared-cache.h in Headers */, 838486000D6D68A200CEA253 /* objc-loadmethod.h in Headers */, - 301498E521B658D100F165E9 /* machine_rpc.h in Headers */, - 301498F421B658D100F165E9 /* dyld_priv.h in Headers */, - 301498C521B658D100F165E9 /* spinlock_private.h in Headers */, - 301498E221B658D100F165E9 /* sched_param.h in Headers */, - 301498D021B658D100F165E9 /* ast_types.h in Headers */, - 301498F721B65D0A00F165E9 /* isa.h in Headers */, - 301498C321B658D100F165E9 /* source_private.h in Headers */, - 301498C821B658D100F165E9 /* workqueue_private.h in Headers */, + 3044E25823D4626F006763E3 /* lock_private.h in Headers */, + 3044E24523D4626F006763E3 /* setjmp.h in Headers */, + 3044E22B23D4626F006763E3 /* spinlock_private.h in Headers */, + 3044E24023D4626F006763E3 /* cpu_data.h in Headers */, + 3044E24A23D4626F006763E3 /* ast.h in Headers */, 75A95053202BAC4100D7D56F /* objc-lockdebug.h in Headers */, - 301498D121B658D100F165E9 /* trap.h in Headers */, 831C85D50E10CF850066E64C /* objc-os.h in Headers */, + 3044E24823D4626F006763E3 /* sched_param.h in Headers */, + 3044E25F23D4626F006763E3 /* semaphore_private.h in Headers */, 838486030D6D68A200CEA253 /* objc-private.h in Headers */, - 301498E921B658D100F165E9 /* cpu_capabilities.h in Headers */, - 301498BF21B658D100F165E9 /* Block_private.h in Headers */, - 301498D221B658D100F165E9 /* machparam.h in Headers */, - 301498EB21B658D100F165E9 /* machlimits.h in Headers */, - 301498C421B658D100F165E9 /* benchmark.h in Headers */, - 301498D521B658D100F165E9 /* vm_tuning.h in Headers */, - 301498EF21B658D100F165E9 /* clock_reply_server.h in Headers */, - 301498D321B658D100F165E9 /* xpr.h in Headers */, - 301498CE21B658D100F165E9 /* reason.h in Headers */, - 301498E321B658D100F165E9 /* thread.h in Headers */, - 3014990821B6617600F165E9 /* semaphore_private.h in Headers */, 393CEAC60DC69E67000B69DE /* objc-references.h in Headers */, 838486070D6D68A200CEA253 /* objc-runtime-new.h in Headers */, - 301498CF21B658D100F165E9 /* qos_private.h in Headers */, - 301498C021B658D100F165E9 /* _simple.h in Headers */, + 3044E25A23D4626F006763E3 /* lock.h in Headers */, 83BE02EA0FCCB24D00661494 /* objc-runtime-old.h in Headers */, - 301498CB21B658D100F165E9 /* cpu_capabilities.h in Headers */, + 3044E25E23D4626F006763E3 /* base_private.h in Headers */, 8384860A0D6D68A200CEA253 /* objc-runtime.h in Headers */, - 301498DD21B658D100F165E9 /* io_map_entries.h in Headers */, - 301498C121B658D100F165E9 /* queue_private.h in Headers */, 8384860C0D6D68A200CEA253 /* objc-sel-set.h in Headers */, - 3014990521B6617600F165E9 /* atomic.h in Headers */, + 3044E25623D4626F006763E3 /* mach_exc_server.h in Headers */, + 3044E23023D4626F006763E3 /* cpu_capabilities.h in Headers */, + 3044E22823D4626F006763E3 /* private.h in Headers */, + 3044E23B23D4626F006763E3 /* vm_tuning.h in Headers */, + 3044E22723D4626F006763E3 /* queue_private.h in Headers */, 838486100D6D68A200CEA253 /* objc-sync.h in Headers */, + 3044E25523D4626F006763E3 /* clock_reply_server.h in Headers */, + 83D92696212254CF00299F69 /* isa.h in Headers */, 838486130D6D68A200CEA253 /* objc.h in Headers */, + 83D9269821225A7400299F69 /* arm64-asm.h in Headers */, 838486140D6D68A200CEA253 /* Object.h in Headers */, - 3014990921B6617600F165E9 /* base_private.h in Headers */, + 3044E24B23D4626F006763E3 /* machine_rpc.h in Headers */, + 3044E25D23D4626F006763E3 /* atomic.h in Headers */, 8384861E0D6D68A800CEA253 /* Protocol.h in Headers */, + 3044E26023D4626F006763E3 /* alloc_once_impl.h in Headers */, 838486200D6D68A800CEA253 /* runtime.h in Headers */, - 301498DB21B658D100F165E9 /* pmap.h in Headers */, - 301498D421B658D100F165E9 /* task.h in Headers */, 39ABD72312F0B61800D1054C /* objc-weak.h in Headers */, + 3044E22423D4626F006763E3 /* CrashReporterClient.h in Headers */, + 3044E24323D4626F006763E3 /* io_map_entries.h in Headers */, + 3044E24F23D4626F006763E3 /* cpu_capabilities.h in Headers */, + 3044E22E23D4626F006763E3 /* workqueue_private.h in Headers */, + 3044E23523D4626F006763E3 /* qos_private.h in Headers */, + 3044E22A23D4626F006763E3 /* benchmark.h in Headers */, 83F4B52815E843B100E0926F /* NSObjCRuntime.h in Headers */, 83F4B52915E843B100E0926F /* NSObject.h in Headers */, - 301498CC21B658D100F165E9 /* cpu_capabilities.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 30466D8321B665A500DA7B9D /* debug-objc */ = { + 3044E26823D47C0E006763E3 /* debug-objc */ = { isa = PBXNativeTarget; - buildConfigurationList = 30466D8A21B665A500DA7B9D /* Build configuration list for PBXNativeTarget "debug-objc" */; + buildConfigurationList = 3044E26D23D47C0E006763E3 /* Build configuration list for PBXNativeTarget "debug-objc" */; buildPhases = ( - 30466D8021B665A500DA7B9D /* Sources */, - 30466D8121B665A500DA7B9D /* Frameworks */, - 30466D8221B665A500DA7B9D /* CopyFiles */, + 3044E26523D47C0E006763E3 /* Sources */, + 3044E26623D47C0E006763E3 /* Frameworks */, + 3044E26723D47C0E006763E3 /* CopyFiles */, ); buildRules = ( ); dependencies = ( + 3044E27123D47C4A006763E3 /* PBXTargetDependency */, ); name = "debug-objc"; productName = "debug-objc"; - productReference = 30466D8421B665A500DA7B9D /* debug-objc */; + productReference = 3044E26923D47C0E006763E3 /* debug-objc */; productType = "com.apple.product-type.tool"; }; D2AAC0620554660B00DB518D /* objc */ = { @@ -940,13 +970,18 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = NO; - LastUpgradeCheck = 0440; + LastUpgradeCheck = 1130; TargetAttributes = { - 30466D8321B665A500DA7B9D = { - CreatedOnToolsVersion = 10.1; + 3044E26823D47C0E006763E3 = { + CreatedOnToolsVersion = 11.3; DevelopmentTeam = LY6BSMT7G9; ProvisioningStyle = Automatic; }; + 834F9B01212E560100F95A54 = { + CreatedOnToolsVersion = 10.0; + DevelopmentTeam = 59GAB85EFG; + ProvisioningStyle = Automatic; + }; 837F67A81A771F63004D34FA = { CreatedOnToolsVersion = 6.3; }; @@ -954,13 +989,14 @@ }; buildConfigurationList = 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "objc" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 1; knownRegions = ( - English, - Japanese, - French, - German, + fr, + de, + en, + ja, + Base, ); mainGroup = 08FB7794FE84155DC02AAC07 /* objc */; projectDirPath = ""; @@ -969,7 +1005,8 @@ D2AAC0620554660B00DB518D /* objc */, 837F67A81A771F63004D34FA /* objc-simulator */, F9BCC6CA205C68E800DD9AFC /* objc-trampolines */, - 30466D8321B665A500DA7B9D /* debug-objc */, + 834F9B01212E560100F95A54 /* objc4_tests */, + 3044E26823D47C0E006763E3 /* debug-objc */, ); }; /* End PBXProject section */ @@ -1004,14 +1041,32 @@ shellPath = /bin/sh; shellScript = "cd \"${INSTALL_DIR}\"\n/bin/ln -s libobjc.A.dylib libobjc.dylib\n\nTBD_UPPER=`echo ${GENERATE_TEXT_BASED_STUBS} | tr a-z A-Z`\n\nif [ ${TBD_UPPER} = \"YES\" ] || [ ${TBD_UPPER} = \"TRUE\" ] || [ ${TBD_UPPER} = \"1\" ]; then\nGENERATE_TBD=1\nfi\n\nif [ ${GENERATE_TBD} ]; then\n /bin/ln -s libobjc.A.tbd libobjc.tbd\nfi\n"; }; + 834F9B05212E561400F95A54 /* Run Script (build tests) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Run Script (build tests)"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -x\nset -e\n\n# Set this to empty, or a space-separated list of tests to run.\ntestfiles=\"\"\n\n# Location inside DSTROOT of our test files and our BATS config file.\ntestdir=/AppleInternal/CoreOS/tests/objc4\nconfigdir=/AppleInternal/CoreOS/BATS/unit_tests\n\nmkdir -p ${DSTROOT}${testdir}\nmkdir -p ${DSTROOT}${configdir}\n\n# Common test.pl args for building and running.\ntestargs=\"ARCH=`echo ${ARCHS} | tr ' ' ','` OS=${PLATFORM_NAME} MEM=mrc,arc LANGUAGE=c,c++,objc,objc++ RUN=0 VERBOSE=1 BATS=1 ${testfiles}\"\n\n# Build the tests and BATS plist into DSTROOT.\nperl ${SRCROOT}/test/test.pl $testargs BUILD=1 RUN=0\n\n# Move the BATS plist where BATS expects it, and convert it to binary format.\nmv ${DSTROOT}${testdir}/objc4.plist ${DSTROOT}${configdir}\nplutil -convert binary1 ${DSTROOT}${configdir}/objc4.plist\n\n# Copy test sources to DSTROOT; running the test requires reading them again.\ncp -R ${SRCROOT}/test ${DSTROOT}${testdir}/test\n\n# Don't copy gcfiles because XBS chokes on them.\n# Don't copy other cruft because verifiers dislike them. (This doesn't matter for submissions but does affect local buildit builds.)\nrm -rf ${DSTROOT}${testdir}/test/gcfiles\nrm -rf ${DSTROOT}${testdir}/test/*~\nrm -rf ${DSTROOT}${testdir}/test/\\#*\\#\nrm -rf ${DSTROOT}${testdir}/test/.\\#*\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 30466D8021B665A500DA7B9D /* Sources */ = { + 3044E26523D47C0E006763E3 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 30466D8721B665A500DA7B9D /* main.m in Sources */, + 3044E26C23D47C0E006763E3 /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1026,7 +1081,6 @@ 838485F30D6D68A200CEA253 /* objc-class-old.mm in Sources */, 838485F50D6D68A200CEA253 /* objc-class.mm in Sources */, 838485F70D6D68A200CEA253 /* objc-errors.mm in Sources */, - 301498D721B658D100F165E9 /* Makefile in Sources */, 838485F90D6D68A200CEA253 /* objc-exception.mm in Sources */, 838485FA0D6D68A200CEA253 /* objc-file.mm in Sources */, 838485FC0D6D68A200CEA253 /* objc-initialize.mm in Sources */, @@ -1079,6 +1133,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 3044E27123D47C4A006763E3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D2AAC0620554660B00DB518D /* objc */; + targetProxy = 3044E27023D47C4A006763E3 /* PBXContainerItemProxy */; + }; 837F67AD1A771F6E004D34FA /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D2AAC0620554660B00DB518D /* objc */; @@ -1095,7 +1154,7 @@ 1DEB914B08733D8E0010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD)"; + CLANG_ENABLE_OBJC_WEAK = YES; COPY_HEADERS_RUN_UNIFDEF = YES; COPY_HEADERS_UNIFDEF_FLAGS = "-UBUILD_FOR_OSX"; "COPY_HEADERS_UNIFDEF_FLAGS[sdk=macosx*]" = "-DBUILD_FOR_OSX"; @@ -1104,18 +1163,18 @@ EXECUTABLE_PREFIX = lib; GCC_CW_ASM_SYNTAX = NO; GCC_OPTIMIZATION_LEVEL = 0; - GCC_VERSION = ""; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; HEADER_SEARCH_PATHS = ( "$(DSTROOT)/usr/include/**", "$(DSTROOT)/usr/local/include/**", + "\"$(SRCROOT)/include\"", "$(CONFIGURATION_BUILD_DIR)/usr/include/**", "$(CONFIGURATION_BUILD_DIR)/usr/local/include/**", - "$(SRCROOT)/include", + /System/Library/Frameworks/System.framework/PrivateHeaders, ); INSTALL_PATH = /usr/lib; IS_ZIPPERED = YES; - ORDER_FILE = "$(SRCROOT)/libobjc.order"; + ORDER_FILE = $SRCROOT/libobjc.order; "ORDER_FILE[sdk=iphonesimulator*]" = ""; OTHER_CFLAGS = ( "-fdollars-in-identifiers", @@ -1159,13 +1218,13 @@ "-Xlinker", interposable.txt, ); - OTHER_TAPI_FLAGS = "-exclude-public-header $(SRCROOT)/runtime/module/ObjectiveC.apinotes -exclude-public-header $(SRCROOT)/runtime/module/module.modulemap -Xparser -Wno-deprecated-declarations -Xparser -Wno-unavailable-declarations -Xparser -D_OBJC_PRIVATE_H_=1 -DOBJC_DECLARE_SYMBOLS=1"; + OTHER_TAPI_FLAGS = "-exclude-public-header \"$(SRCROOT)/libobjc.order\" -exclude-public-header \"$(SRCROOT)/runtime/Module/module.modulemap\" -Xparser -Wno-deprecated-declarations -Xparser -Wno-unavailable-declarations -Xparser -D_OBJC_PRIVATE_H_=1 -DOBJC_DECLARE_SYMBOLS=1"; PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc; PRODUCT_NAME = objc.A; PUBLIC_HEADERS_FOLDER_PATH = /usr/include/objc; SDKROOT = macosx; SUPPORTS_TEXT_BASED_API = YES; - TAPI_VERIFY_MODE = Pedantic; + TAPI_VERIFY_MODE = ErrorsOnly; UNEXPORTED_SYMBOLS_FILE = unexported_symbols; }; name = Debug; @@ -1173,24 +1232,25 @@ 1DEB914C08733D8E0010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; COPY_HEADERS_RUN_UNIFDEF = YES; COPY_HEADERS_UNIFDEF_FLAGS = "-UBUILD_FOR_OSX"; "COPY_HEADERS_UNIFDEF_FLAGS[sdk=macosx*]" = "-DBUILD_FOR_OSX"; DYLIB_CURRENT_VERSION = 228; EXECUTABLE_PREFIX = lib; GCC_CW_ASM_SYNTAX = NO; - GCC_VERSION = ""; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; HEADER_SEARCH_PATHS = ( "$(DSTROOT)/usr/include/**", "$(DSTROOT)/usr/local/include/**", + "\"$(SRCROOT)/include\"", "$(CONFIGURATION_BUILD_DIR)/usr/include/**", "$(CONFIGURATION_BUILD_DIR)/usr/local/include/**", - "$(SRCROOT)/include/**", + /System/Library/Frameworks/System.framework/PrivateHeaders, ); INSTALL_PATH = /usr/lib; IS_ZIPPERED = YES; - ORDER_FILE = "$(SRCROOT)/libobjc.order"; + ORDER_FILE = $SRCROOT/libobjc.order; "ORDER_FILE[sdk=iphonesimulator*]" = ""; OTHER_CFLAGS = ( "-fdollars-in-identifiers", @@ -1234,13 +1294,13 @@ "-Xlinker", interposable.txt, ); - OTHER_TAPI_FLAGS = "-exclude-public-header $(SRCROOT)/runtime/module/ObjectiveC.apinotes -exclude-public-header $(SRCROOT)/runtime/module/module.modulemap -Xparser -Wno-deprecated-declarations -Xparser -Wno-unavailable-declarations -Xparser -D_OBJC_PRIVATE_H_=1 -DOBJC_DECLARE_SYMBOLS=1"; + OTHER_TAPI_FLAGS = "-exclude-public-header \"$(SRCROOT)/libobjc.order\" -exclude-public-header \"$(SRCROOT)/runtime/Module/module.modulemap\" -Xparser -Wno-deprecated-declarations -Xparser -Wno-unavailable-declarations -Xparser -D_OBJC_PRIVATE_H_=1 -DOBJC_DECLARE_SYMBOLS=1"; PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc; PRODUCT_NAME = objc.A; PUBLIC_HEADERS_FOLDER_PATH = /usr/include/objc; SDKROOT = macosx; SUPPORTS_TEXT_BASED_API = YES; - TAPI_VERIFY_MODE = Pedantic; + TAPI_VERIFY_MODE = ErrorsOnly; UNEXPORTED_SYMBOLS_FILE = unexported_symbols; WARNING_CFLAGS = ( "$(inherited)", @@ -1252,34 +1312,59 @@ 1DEB914F08733D8E0010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_LINK_OBJC_RUNTIME = NO; CLANG_OBJC_RUNTIME = NO; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; DEBUG_INFORMATION_FORMAT = dwarf; - EXCLUDED_INSTALLSRC_SUBDIRECTORY_PATTERNS = "$(inherited) test"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_ENABLE_CPP_EXCEPTIONS = NO; GCC_ENABLE_CPP_RTTI = NO; GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = "OS_OBJECT_USE_OBJC=0"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "OS_OBJECT_USE_OBJC=0", + "OBJC_IS_DEBUG_BUILD=1", + ); GCC_STRICT_ALIASING = YES; GCC_SYMBOLS_PRIVATE_EXTERN = YES; - GCC_VERSION = ""; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_SHADOW = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ""; "OTHER_CFLAGS[arch=x86_64]" = "-fobjc-legacy-dispatch"; OTHER_CPLUSPLUSFLAGS = ( "$(OTHER_CFLAGS)", "-D_LIBCPP_VISIBLE=\"\"", ); - SDKROOT = macosx; - VALID_ARCHS = x86_64; WARNING_CFLAGS = ( "-Wall", "-Wextra", @@ -1295,27 +1380,49 @@ 1DEB915008733D8E0010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_LINK_OBJC_RUNTIME = NO; CLANG_OBJC_RUNTIME = NO; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - EXCLUDED_INSTALLSRC_SUBDIRECTORY_PATTERNS = "$(inherited) test"; + ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_ENABLE_CPP_EXCEPTIONS = NO; GCC_ENABLE_CPP_RTTI = NO; GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( "OS_OBJECT_USE_OBJC=0", "NDEBUG=1", ); GCC_STRICT_ALIASING = YES; GCC_SYMBOLS_PRIVATE_EXTERN = YES; - GCC_VERSION = ""; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_SHADOW = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; "OTHER_CFLAGS[arch=i386]" = "-momit-leaf-frame-pointer"; "OTHER_CFLAGS[arch=x86_64]" = ( @@ -1326,8 +1433,6 @@ "$(OTHER_CFLAGS)", "-D_LIBCPP_VISIBLE=\"\"", ); - SDKROOT = macosx; - VALID_ARCHS = x86_64; WARNING_CFLAGS = ( "-Wall", "-Wextra", @@ -1340,7 +1445,7 @@ }; name = Release; }; - 30466D8821B665A500DA7B9D /* Debug */ = { + 3044E26E23D47C0E006763E3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; @@ -1349,53 +1454,31 @@ CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEVELOPMENT_TEAM = LY6BSMT7G9; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; + ENABLE_HARDENED_RUNTIME = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; }; name = Debug; }; - 30466D8921B665A500DA7B9D /* Release */ = { + 3044E26F23D47C0E006763E3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; @@ -1404,49 +1487,50 @@ CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEVELOPMENT_TEAM = LY6BSMT7G9; + ENABLE_HARDENED_RUNTIME = YES; ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; + 834F9B02212E560200F95A54 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 59GAB85EFG; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 834F9B03212E560200F95A54 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 59GAB85EFG; + PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; 837F67AA1A771F63004D34FA /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -1454,6 +1538,7 @@ 837F67AB1A771F63004D34FA /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -1461,7 +1546,7 @@ F9BCC725205C68E800DD9AFC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD)"; + CLANG_ENABLE_OBJC_WEAK = YES; COPY_HEADERS_RUN_UNIFDEF = YES; COPY_HEADERS_UNIFDEF_FLAGS = "-UBUILD_FOR_OSX"; "COPY_HEADERS_UNIFDEF_FLAGS[sdk=macosx*]" = "-DBUILD_FOR_OSX"; @@ -1470,7 +1555,6 @@ EXECUTABLE_PREFIX = lib; GCC_CW_ASM_SYNTAX = NO; GCC_OPTIMIZATION_LEVEL = 0; - GCC_VERSION = ""; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; HEADER_SEARCH_PATHS = ( "$(DSTROOT)/usr/include/**", @@ -1500,13 +1584,13 @@ F9BCC726205C68E800DD9AFC /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; COPY_HEADERS_RUN_UNIFDEF = YES; COPY_HEADERS_UNIFDEF_FLAGS = "-UBUILD_FOR_OSX"; "COPY_HEADERS_UNIFDEF_FLAGS[sdk=macosx*]" = "-DBUILD_FOR_OSX"; DYLIB_CURRENT_VERSION = 228; EXECUTABLE_PREFIX = lib; GCC_CW_ASM_SYNTAX = NO; - GCC_VERSION = ""; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; HEADER_SEARCH_PATHS = ( "$(DSTROOT)/usr/include/**", @@ -1554,11 +1638,20 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 30466D8A21B665A500DA7B9D /* Build configuration list for PBXNativeTarget "debug-objc" */ = { + 3044E26D23D47C0E006763E3 /* Build configuration list for PBXNativeTarget "debug-objc" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3044E26E23D47C0E006763E3 /* Debug */, + 3044E26F23D47C0E006763E3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 834F9B04212E560200F95A54 /* Build configuration list for PBXAggregateTarget "objc4_tests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 30466D8821B665A500DA7B9D /* Debug */, - 30466D8921B665A500DA7B9D /* Release */, + 834F9B02212E560200F95A54 /* Debug */, + 834F9B03212E560200F95A54 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/runtime/Messengers.subproj/objc-msg-arm64.s b/runtime/Messengers.subproj/objc-msg-arm64.s index 8cef3e1f..89975d0f 100755 --- a/runtime/Messengers.subproj/objc-msg-arm64.s +++ b/runtime/Messengers.subproj/objc-msg-arm64.s @@ -189,16 +189,19 @@ LExit$0: #define GETIMP 1 #define LOOKUP 2 -// CacheHit: x17 = cached IMP, x12 = address of cached IMP +// CacheHit: x17 = cached IMP, x12 = address of cached IMP, x1 = SEL .macro CacheHit .if $0 == NORMAL - TailCallCachedImp x17, x12 // authenticate and call imp + TailCallCachedImp x17, x12, x1 // authenticate and call imp .elseif $0 == GETIMP mov p0, p17 - AuthAndResignAsIMP x0, x12 // authenticate imp and re-sign as IMP - ret // return IMP + cbz p0, 9f // don't ptrauth a nil imp + AuthAndResignAsIMP x0, x12, x1 // authenticate imp and re-sign as IMP +9: ret // return IMP .elseif $0 == LOOKUP - AuthAndResignAsIMP x17, x12 // authenticate imp and re-sign as IMP + // No nil check for ptrauth: the caller would crash anyway when they + // jump to a nil IMP. We don't care if that jump also fails ptrauth. + AuthAndResignAsIMP x17, x12, x1 // authenticate imp and re-sign as IMP ret // return imp via x17 .else .abort oops diff --git a/runtime/NSObject.mm b/runtime/NSObject.mm index 33d72679..c49b44ea 100644 --- a/runtime/NSObject.mm +++ b/runtime/NSObject.mm @@ -308,7 +308,7 @@ BOOL objc_should_deallocate(id object) { !((objc_class *)cls)->isInitialized()) { SideTable::unlockTwo(oldTable, newTable); - _class_initialize(_class_getNonMetaClass(cls, (id)newObj)); + class_initialize(cls, (id)newObj); // If this class is finished with +initialize then we're good. // If this class is still running +initialize on this thread @@ -506,7 +506,7 @@ BOOL objc_should_deallocate(id object) { } else { table->unlock(); - _class_initialize(cls); + class_initialize(cls, obj); goto retry; } } @@ -614,7 +614,12 @@ BOOL objc_should_deallocate(id object) { } ~magic_t() { - m[0] = m[1] = m[2] = m[3] = 0; + // Clear magic before deallocation. + // This prevents some false positives in memory debugging tools. + // fixme semantically this should be memset_s(), but the + // compiler doesn't optimize that at all (rdar://44856676). + volatile uint64_t *p = (volatile uint64_t *)m; + p[0] = 0; p[1] = 0; } bool check() const { @@ -1782,6 +1787,12 @@ void objc_overrelease_during_dealloc_error(void) return callAlloc(cls, true/*checkNil*/, true/*allocWithZone*/); } +// Calls [[cls alloc] init]. +id +objc_alloc_init(Class cls) +{ + return [callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/) init]; +} void _objc_rootDealloc(id obj) diff --git a/runtime/arm64-asm.h b/runtime/arm64-asm.h index 0bbed2fc..281bb7fd 100644 --- a/runtime/arm64-asm.h +++ b/runtime/arm64-asm.h @@ -108,7 +108,8 @@ .endmacro .macro TailCallCachedImp - // $0 = cached imp, $1 = address of cached imp + // $0 = cached imp, $1 = address of cached imp, $2 = SEL + eor $1, $1, $2 // mix SEL into ptrauth modifier brab $0, $1 .endmacro @@ -123,8 +124,11 @@ .endmacro .macro AuthAndResignAsIMP - // $0 = cached imp, $1 = address of cached imp + // $0 = cached imp, $1 = address of cached imp, $2 = SEL + // note: assumes the imp is not nil + eor $1, $1, $2 // mix SEL into ptrauth modifier autib $0, $1 // authenticate cached imp + ldr xzr, [$0] // crash if authentication failed paciza $0 // resign cached imp as IMP .endmacro @@ -138,7 +142,7 @@ .endmacro .macro TailCallCachedImp - // $0 = cached imp, $1 = address of cached imp + // $0 = cached imp, $1 = address of cached imp, $2 = SEL br $0 .endmacro @@ -153,6 +157,7 @@ .endmacro .macro AuthAndResignAsIMP + // $0 = cached imp, $1 = address of cached imp, $2 = SEL // empty .endmacro diff --git a/runtime/message.h b/runtime/message.h index a53b4304..20f698f3 100644 --- a/runtime/message.h +++ b/runtime/message.h @@ -198,9 +198,12 @@ objc_msgSend_fp2ret(void /* id self, SEL op, ... */ ) * you must use \c objc_msgSend_fpret for functions returning non-integral type. For \c float or * \c long \c double return types, cast the function to an appropriate function pointer type first. */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincompatible-library-redeclaration" OBJC_EXPORT double objc_msgSend_fpret(id _Nullable self, SEL _Nonnull op, ...) OBJC_AVAILABLE(10.4, 2.0, 9.0, 1.0, 2.0); +#pragma clang diagnostic pop /* Use objc_msgSendSuper() for fp-returning messages to super. */ /* See also objc_msgSendv_fpret() below. */ diff --git a/runtime/objc-abi.h b/runtime/objc-abi.h index ddab3b86..bacf2e4a 100644 --- a/runtime/objc-abi.h +++ b/runtime/objc-abi.h @@ -100,6 +100,7 @@ typedef struct objc_image_info { #if __cplusplus >= 201103L private: enum : uint32_t { + // 1 byte assorted flags IsReplacement = 1<<0, // used for Fix&Continue, now ignored SupportsGC = 1<<1, // image supports GC RequiresGC = 1<<2, // image requires GC @@ -107,17 +108,28 @@ typedef struct objc_image_info { CorrectedSynthesize = 1<<4, // used for an old workaround, now ignored IsSimulated = 1<<5, // image compiled for a simulator platform HasCategoryClassProperties = 1<<6, // class properties in category_t + // not yet used = 1<<7 - SwiftVersionMaskShift = 8, - SwiftVersionMask = 0xff << SwiftVersionMaskShift // Swift ABI version + // 1 byte Swift unstable ABI version number + SwiftUnstableVersionMaskShift = 8, + SwiftUnstableVersionMask = 0xff << SwiftUnstableVersionMaskShift, + // 2 byte Swift stable ABI version number + SwiftStableVersionMaskShift = 16, + SwiftStableVersionMask = 0xffffUL << SwiftStableVersionMaskShift }; - public: + public: enum : uint32_t { + // Values for SwiftUnstableVersion + // All stable ABIs store SwiftVersion5 here. SwiftVersion1 = 1, SwiftVersion1_2 = 2, SwiftVersion2 = 3, - SwiftVersion3 = 4 + SwiftVersion3 = 4, + SwiftVersion4 = 5, + SwiftVersion4_1 = 6, + SwiftVersion4_2 = 6, // [sic] + SwiftVersion5 = 7 }; public: @@ -126,8 +138,8 @@ typedef struct objc_image_info { bool requiresGC() const { return flags & RequiresGC; } bool optimizedByDyld() const { return flags & OptimizedByDyld; } bool hasCategoryClassProperties() const { return flags & HasCategoryClassProperties; } - bool containsSwift() const { return (flags & SwiftVersionMask) != 0; } - uint32_t swiftVersion() const { return (flags & SwiftVersionMask) >> SwiftVersionMaskShift; } + bool containsSwift() const { return (flags & SwiftUnstableVersionMask) != 0; } + uint32_t swiftUnstableVersion() const { return (flags & SwiftUnstableVersionMask) >> SwiftUnstableVersionMaskShift; } #endif } objc_image_info; diff --git a/runtime/objc-block-trampolines.h b/runtime/objc-block-trampolines.h index f5ec10a5..c65eeefb 100644 --- a/runtime/objc-block-trampolines.h +++ b/runtime/objc-block-trampolines.h @@ -37,6 +37,13 @@ * objc-block-trampolines.h: Symbols for IMP block trampolines */ +// WARNING: remapped code and dtrace do not play well together. Dtrace +// will place trap instructions to instrument the code, which then get +// remapped along with everything else. The remapped traps are not +// recognized by dtrace and the process crashes. To avoid this, dtrace +// blacklists this library by name. Do not change the name of this +// library. rdar://problem/42627391 + #include OBJC_EXPORT const char _objc_blockTrampolineImpl diff --git a/runtime/objc-block-trampolines.mm b/runtime/objc-block-trampolines.mm index c11423bb..21879b70 100644 --- a/runtime/objc-block-trampolines.mm +++ b/runtime/objc-block-trampolines.mm @@ -163,7 +163,8 @@ void Initialize() { void *dylib = dlopen("/usr/lib/libobjc-trampolines.dylib", RTLD_NOW | RTLD_LOCAL | RTLD_FIRST); if (!dylib) { - _objc_fatal("couldn't dlopen libobjc-trampolines.dylib"); + _objc_fatal("couldn't dlopen libobjc-trampolines.dylib: %s", + dlerror()); } auto t = new TrampolinePointers(dylib); @@ -177,6 +178,9 @@ void Initialize() { uintptr_t textSegment() { return get()->textSegment; } uintptr_t textSegmentSize() { return get()->textSegmentSize; } + // See comments below about PAGE_SIZE and PAGE_MAX_SIZE. + uintptr_t dataSize() { return PAGE_MAX_SIZE; } + uintptr_t impl() { return get()->impl.address(); } uintptr_t start() { return get()->start.address(); } }; @@ -184,6 +188,8 @@ void Initialize() { static TrampolinePointerWrapper Trampolines; // argument mode identifier +// Some calculations assume that these modes are sequential starting from 0. +// This order must match the order of the trampoline's assembly code. typedef enum { ReturnValueInRegisterArgumentMode, #if SUPPORT_STRET @@ -211,8 +217,17 @@ void Initialize() { { TrampolineBlockPageGroup *nextPageGroup; // linked list of all pages TrampolineBlockPageGroup *nextAvailablePage; // linked list of pages with available slots - + uintptr_t nextAvailable; // index of next available slot, endIndex() if no more available + + const void * TrampolinePtrauth const text; // text VM region; stored only for the benefit of the leaks tool + + TrampolineBlockPageGroup() + : nextPageGroup(nil) + , nextAvailablePage(nil) + , nextAvailable(startIndex()) + , text((const void *)((uintptr_t)this + Trampolines.dataSize())) + { } // Payload data: block pointers and free list. // Bytes parallel with trampoline header code are the fields above or unused @@ -249,7 +264,7 @@ static uintptr_t startIndex() { } static uintptr_t endIndex() { - return (uintptr_t)PAGE_MAX_SIZE / slotSize(); + return (uintptr_t)Trampolines.dataSize() / slotSize(); } static bool validIndex(uintptr_t index) { @@ -262,8 +277,10 @@ static bool validIndex(uintptr_t index) { } uintptr_t trampolinesForMode(int aMode) { - // Skip over data page and Mach-O page. - return (uintptr_t)this + PAGE_MAX_SIZE * (2 + aMode); + // Skip over the data area, one page of Mach-O headers, + // and one text page for each mode before this one. + return (uintptr_t)this + Trampolines.dataSize() + + PAGE_MAX_SIZE * (1 + aMode); } IMP trampoline(int aMode, uintptr_t index) { @@ -334,7 +351,7 @@ static void check() { auto textSource = Trampolines.textSegment(); auto textSourceSize = Trampolines.textSegmentSize(); - auto dataSize = PAGE_MAX_SIZE; + auto dataSize = Trampolines.dataSize(); // Allocate a single contiguous region big enough to hold data+text. kern_return_t result; @@ -358,10 +375,7 @@ static void check() { _objc_fatal("vm_remap trampolines failed (%d)", result); } - TrampolineBlockPageGroup *pageGroup = (TrampolineBlockPageGroup *) dataAddress; - pageGroup->nextAvailable = pageGroup->startIndex(); - pageGroup->nextPageGroup = nil; - pageGroup->nextAvailablePage = nil; + auto *pageGroup = new ((void*)dataAddress) TrampolineBlockPageGroup; if (HeadPageGroup) { TrampolineBlockPageGroup *lastPageGroup = HeadPageGroup; diff --git a/runtime/objc-blocktramps-arm.s b/runtime/objc-blocktramps-arm.s index bbbe1cfe..de80a43a 100644 --- a/runtime/objc-blocktramps-arm.s +++ b/runtime/objc-blocktramps-arm.s @@ -32,16 +32,13 @@ L__objc_blockTrampolineImpl_func: mov r1, r0 // _cmd = self - // Trampoline's data is one page before the trampoline text. + // Trampoline's data is two pages before the trampoline text. // Also correct PC bias of 4 bytes. sub r12, # 2*PAGE_MAX_SIZE ldr r0, [r12, #-4] // self = block object ldr pc, [r0, #12] // tail call block->invoke // not reached - // Align trampolines to 8 bytes -.align 3 - .macro TrampolineEntry mov r12, pc b L__objc_blockTrampolineImpl_func @@ -92,8 +89,9 @@ L__objc_blockTrampolineImpl_func: TrampolineEntryX16 .endmacro +.align 5 __objc_blockTrampolineStart: - // 2048-2 trampolines to fill 16K page + // 2048-4 trampolines to fill 16K page TrampolineEntryX256 TrampolineEntryX256 TrampolineEntryX256 @@ -135,11 +133,11 @@ __objc_blockTrampolineStart: TrampolineEntry TrampolineEntry TrampolineEntry - TrampolineEntry - - TrampolineEntry __objc_blockTrampolineLast: TrampolineEntry + + // TrampolineEntry + // TrampolineEntry // TrampolineEntry // TrampolineEntry @@ -172,15 +170,12 @@ L__objc_blockTrampolineImpl_stret_func: mov r2, r1 // _cmd = self - // Trampoline's data is one page before the trampoline text. + // Trampoline's data is three pages before the trampoline text. // Also correct PC bias of 4 bytes. sub r12, # 3*PAGE_MAX_SIZE ldr r1, [r12, #-4] // self = block object ldr pc, [r1, #12] // tail call block->invoke // not reached - - // Align trampolines to 8 bytes -.align 3 .macro TrampolineEntry_stret mov r12, pc @@ -232,8 +227,9 @@ L__objc_blockTrampolineImpl_stret_func: TrampolineEntryX16_stret .endmacro +.align 5 __objc_blockTrampolineStart_stret: - // 2048-2 trampolines to fill 16K page + // 2048-4 trampolines to fill 16K page TrampolineEntryX256_stret TrampolineEntryX256_stret TrampolineEntryX256_stret @@ -275,11 +271,11 @@ __objc_blockTrampolineStart_stret: TrampolineEntry_stret TrampolineEntry_stret TrampolineEntry_stret - TrampolineEntry_stret - - TrampolineEntry_stret __objc_blockTrampolineLast_stret: TrampolineEntry_stret + + // TrampolineEntry_stret + // TrampolineEntry_stret // TrampolineEntry_stret // TrampolineEntry_stret diff --git a/runtime/objc-blocktramps-arm64.s b/runtime/objc-blocktramps-arm64.s index a79a0311..d7e32a55 100644 --- a/runtime/objc-blocktramps-arm64.s +++ b/runtime/objc-blocktramps-arm64.s @@ -34,6 +34,8 @@ L_objc_blockTrampolineImpl: // pad up to TrampolineBlockPagePair header size nop + nop + nop .macro TrampolineEntry // load address of trampoline data (two pages before this instruction) @@ -87,7 +89,7 @@ L_objc_blockTrampolineImpl: .align 3 __objc_blockTrampolineStart: - // 2048-3 trampolines to fill 16K page + // 2048-4 trampolines to fill 16K page TrampolineEntryX256 TrampolineEntryX256 TrampolineEntryX256 @@ -129,10 +131,10 @@ __objc_blockTrampolineStart: TrampolineEntry TrampolineEntry TrampolineEntry - TrampolineEntry - __objc_blockTrampolineLast: TrampolineEntry + + // TrampolineEntry // TrampolineEntry // TrampolineEntry // TrampolineEntry diff --git a/runtime/objc-blocktramps-i386.s b/runtime/objc-blocktramps-i386.s index f2a1aceb..d4f1eb8e 100755 --- a/runtime/objc-blocktramps-i386.s +++ b/runtime/objc-blocktramps-i386.s @@ -32,20 +32,21 @@ .align PAGE_SHIFT __objc_blockTrampolineImpl: - popl %eax - andl $0xFFFFFFF8, %eax - subl $ 2*PAGE_SIZE, %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 + movl (%esp), %eax // return address pushed by trampoline + // 4(%esp) is return address pushed by the call site + movl 8(%esp), %ecx // self -> ecx + movl %ecx, 12(%esp) // ecx -> _cmd + movl -2*PAGE_SIZE-5(%eax), %ecx // block object pointer -> ecx + // trampoline is -5 bytes from the return address + // data is -2 pages from the trampoline + movl %ecx, 8(%esp) // ecx -> self + ret // back to TrampolineEntry to preserve CPU's return stack .macro TrampolineEntry - call __objc_blockTrampolineImpl - nop - nop - nop + // This trampoline is 8 bytes long. + // This callq is 5 bytes long. + calll __objc_blockTrampolineImpl + jmp *12(%ecx) // tail call block->invoke .endmacro .align 5 @@ -568,20 +569,22 @@ __objc_blockTrampolineLast: .align PAGE_SHIFT __objc_blockTrampolineImpl_stret: - popl %eax - andl $0xFFFFFFF8, %eax - subl $ 3*PAGE_SIZE, %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 + movl (%esp), %eax // return address pushed by trampoline + // 4(%esp) is return address pushed by the call site + // 8(%esp) is struct-return address + movl 12(%esp), %ecx // self -> ecx + movl %ecx, 16(%esp) // ecx -> _cmd + movl -3*PAGE_SIZE-5(%eax), %ecx // block object pointer -> ecx + // trampoline is -5 bytes from the return address + // data is -3 pages from the trampoline + movl %ecx, 12(%esp) // ecx -> self + ret .macro TrampolineEntry_stret + // This trampoline is 8 bytes long. + // This callq is 5 bytes long. call __objc_blockTrampolineImpl_stret - nop - nop - nop + jmp *12(%ecx) // tail to block->invoke .endmacro .align 5 diff --git a/runtime/objc-blocktramps-x86_64.s b/runtime/objc-blocktramps-x86_64.s index 4423859c..5f377f06 100755 --- a/runtime/objc-blocktramps-x86_64.s +++ b/runtime/objc-blocktramps-x86_64.s @@ -32,18 +32,18 @@ .align PAGE_SHIFT __objc_blockTrampolineImpl: - popq %r10 - andq $0xFFFFFFFFFFFFFFF8, %r10 - subq $ 2*PAGE_SIZE, %r10 - movq %rdi, %rsi // arg1 -> arg2 - movq (%r10), %rdi // block -> arg1 - jmp *16(%rdi) + movq (%rsp), %r10 // read return address pushed by TrampolineEntry's callq + movq %rdi, %rsi // arg1 -> arg2 + movq -2*PAGE_SIZE-5(%r10), %rdi // block object pointer -> arg1 + // trampoline is -5 bytes from the return address + // data is -2 pages from the trampoline + ret // back to TrampolineEntry to preserve CPU's return stack .macro TrampolineEntry + // This trampoline is 8 bytes long. + // This callq is 5 bytes long. callq __objc_blockTrampolineImpl - nop - nop - nop + jmp *16(%rdi) .endmacro .align 5 @@ -566,19 +566,20 @@ __objc_blockTrampolineLast: .align PAGE_SHIFT __objc_blockTrampolineImpl_stret: - popq %r10 - andq $0xFFFFFFFFFFFFFFF8, %r10 - subq $ 3*PAGE_SIZE, %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) + + // %rdi -- arg1 -- is address of return value's space. Don't mess with it. + movq (%rsp), %r10 // read return address pushed by TrampolineEntry's callq + movq %rsi, %rdx // arg2 -> arg3 + movq -3*PAGE_SIZE-5(%r10), %rsi // block object pointer -> arg2 + // trampoline is -5 bytes from the return address + // data is -3 pages from the trampoline + ret // back to TrampolineEntry to preserve CPU's return stack .macro TrampolineEntry_stret + // This trampoline is 8 bytes long. + // This callq is 5 bytes long. callq __objc_blockTrampolineImpl_stret - nop - nop - nop + jmp *16(%rsi) .endmacro .align 5 diff --git a/runtime/objc-cache.mm b/runtime/objc-cache.mm index 73b172c3..7602fe07 100644 --- a/runtime/objc-cache.mm +++ b/runtime/objc-cache.mm @@ -243,9 +243,9 @@ __asm__ __volatile__( \ // Class points to cache. SEL is key. Cache buckets store SEL+IMP. // Caches are never built in the dyld shared cache. -static inline mask_t cache_hash(cache_key_t key, mask_t mask) +static inline mask_t cache_hash(SEL sel, mask_t mask) { - return (mask_t)(key & mask); + return (mask_t)(uintptr_t)sel & mask; } cache_t *getCache(Class cls) @@ -254,52 +254,49 @@ static inline mask_t cache_hash(cache_key_t key, mask_t mask) return &cls->cache; } -cache_key_t getKey(SEL sel) -{ - assert(sel); - return (cache_key_t)sel; -} - #if __arm64__ -void bucket_t::set(cache_key_t newKey, IMP newImp) +template +void bucket_t::set(SEL newSel, IMP newImp) { - assert(_key == 0 || _key == newKey); + assert(_sel == 0 || _sel == newSel); - static_assert(offsetof(bucket_t,_imp) == 0 && offsetof(bucket_t,_key) == sizeof(void *), - "bucket_t doesn't match arm64 bucket_t::set()"); + static_assert(offsetof(bucket_t,_imp) == 0 && + offsetof(bucket_t,_sel) == sizeof(void *), + "bucket_t layout doesn't match arm64 bucket_t::set()"); -#if __has_feature(ptrauth_calls) - // Authenticate as a C function pointer and re-sign for the cache bucket. - uintptr_t signedImp = _imp.prepareWrite(newImp); -#else - // No function pointer signing. - uintptr_t signedImp = (uintptr_t)newImp; -#endif + uintptr_t signedImp = signIMP(newImp, newSel); - // Write to the bucket. - // LDP/STP guarantees that all observers get - // either imp/key or newImp/newKey - stp(signedImp, newKey, this); + if (atomicity == Atomic) { + // LDP/STP guarantees that all observers get + // either imp/sel or newImp/newSel + stp(signedImp, (uintptr_t)newSel, this); + } else { + _sel = newSel; + _imp = signedImp; + } } #else -void bucket_t::set(cache_key_t newKey, IMP newImp) +template +void bucket_t::set(SEL newSel, IMP newImp) { - assert(_key == 0 || _key == newKey); + assert(_sel == 0 || _sel == newSel); - // objc_msgSend uses key and imp with no locks. - // It is safe for objc_msgSend to see new imp but NULL key + // objc_msgSend uses sel and imp with no locks. + // It is safe for objc_msgSend to see new imp but NULL sel // (It will get a cache miss but not dispatch to the wrong place.) - // It is unsafe for objc_msgSend to see old imp and new key. - // Therefore we write new imp, wait a lot, then write new key. + // It is unsafe for objc_msgSend to see old imp and new sel. + // Therefore we write new imp, wait a lot, then write new sel. - _imp = newImp; + _imp = (uintptr_t)newImp; - if (_key != newKey) { - mega_barrier(); - _key = newKey; + if (_sel != newSel) { + if (atomicity == Atomic) { + mega_barrier(); + } + _sel = newSel; } } @@ -385,14 +382,12 @@ cache_key_t getKey(SEL sel) bucket_t *end = cache_t::endMarker(newBuckets, newCapacity); #if __arm__ - // End marker's key is 1 and imp points BEFORE the first bucket. + // End marker's sel is 1 and imp points BEFORE the first bucket. // This saves an instruction in objc_msgSend. - end->setKey((cache_key_t)(uintptr_t)1); - end->setImp((IMP)(newBuckets - 1)); + end->set((SEL)(uintptr_t)1, (IMP)(newBuckets - 1)); #else - // End marker's key is 1 and imp points to the first bucket. - end->setKey((cache_key_t)(uintptr_t)1); - end->setImp((IMP)newBuckets); + // End marker's sel is 1 and imp points to the first bucket. + end->set((SEL)(uintptr_t)1, (IMP)newBuckets); #endif if (PrintCaches) recordNewCache(newCapacity); @@ -521,23 +516,23 @@ cache_key_t getKey(SEL sel) } -bucket_t * cache_t::find(cache_key_t k, id receiver) +bucket_t * cache_t::find(SEL s, id receiver) { - assert(k != 0); + assert(s != 0); bucket_t *b = buckets(); mask_t m = mask(); - mask_t begin = cache_hash(k, m); + mask_t begin = cache_hash(s, m); mask_t i = begin; do { - if (b[i].key() == 0 || b[i].key() == k) { + if (b[i].sel() == 0 || b[i].sel() == s) { return &b[i]; } } while ((i = cache_next(i, m)) != begin); // hack Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache)); - cache_t::bad_cache(receiver, (SEL)k, cls); + cache_t::bad_cache(receiver, (SEL)s, cls); } @@ -570,7 +565,6 @@ static void cache_fill_nolock(Class cls, SEL sel, IMP imp, id receiver) if (cache_getImp(cls, sel)) return; cache_t *cache = getCache(cls); - cache_key_t key = getKey(sel); // Use the cache as-is if it is less than 3/4 full mask_t newOccupied = cache->occupied() + 1; @@ -590,9 +584,9 @@ static void cache_fill_nolock(Class cls, SEL sel, IMP imp, id receiver) // 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. - bucket_t *bucket = cache->find(key, receiver); - if (bucket->key() == 0) cache->incrementOccupied(); - bucket->set(key, imp); + bucket_t *bucket = cache->find(sel, receiver); + if (bucket->sel() == 0) cache->incrementOccupied(); + bucket->set(sel, imp); } void cache_fill(Class cls, SEL sel, IMP imp, id receiver) diff --git a/runtime/objc-class-old.mm b/runtime/objc-class-old.mm index dd13e22f..e23718fa 100644 --- a/runtime/objc-class-old.mm +++ b/runtime/objc-class-old.mm @@ -36,6 +36,7 @@ static Method _class_getMethod(Class cls, SEL sel); static Method _class_getMethodNoSuper(Class cls, SEL sel); static Method _class_getMethodNoSuper_nolock(Class cls, SEL sel); +static Class _class_getNonMetaClass(Class cls, id obj); static void flush_caches(Class cls, bool flush_meta); @@ -324,6 +325,118 @@ static void _freedHandler(id obj, SEL sel) } +/*********************************************************************** +* _class_resolveClassMethod +* Call +resolveClassMethod, looking for a method to be added to class cls. +* cls should be a metaclass. +* Does not check if the method already exists. +**********************************************************************/ +static void _class_resolveClassMethod(Class cls, SEL sel, id inst) +{ + assert(cls->isMetaClass()); + + if (! lookUpImpOrNil(cls, SEL_resolveClassMethod, inst, + NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) + { + // Resolver not implemented. + return; + } + + BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend; + bool resolved = msg(_class_getNonMetaClass(cls, inst), + SEL_resolveClassMethod, sel); + + // Cache the result (good or bad) so the resolver doesn't fire next time. + // +resolveClassMethod adds to self->ISA() a.k.a. cls + IMP imp = lookUpImpOrNil(cls, sel, inst, + NO/*initialize*/, YES/*cache*/, NO/*resolver*/); + + if (resolved && PrintResolving) { + if (imp) { + _objc_inform("RESOLVE: method %c[%s %s] " + "dynamically resolved to %p", + cls->isMetaClass() ? '+' : '-', + cls->nameForLogging(), sel_getName(sel), imp); + } + else { + // Method resolver didn't add anything? + _objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES" + ", but no new implementation of %c[%s %s] was found", + cls->nameForLogging(), sel_getName(sel), + cls->isMetaClass() ? '+' : '-', + cls->nameForLogging(), sel_getName(sel)); + } + } +} + + +/*********************************************************************** +* _class_resolveInstanceMethod +* Call +resolveInstanceMethod, looking for a method to be added to class cls. +* cls may be a metaclass or a non-meta class. +* Does not check if the method already exists. +**********************************************************************/ +static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst) +{ + if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, + NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) + { + // Resolver not implemented. + return; + } + + BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend; + bool resolved = msg(cls, SEL_resolveInstanceMethod, sel); + + // Cache the result (good or bad) so the resolver doesn't fire next time. + // +resolveInstanceMethod adds to self a.k.a. cls + IMP imp = lookUpImpOrNil(cls, sel, inst, + NO/*initialize*/, YES/*cache*/, NO/*resolver*/); + + if (resolved && PrintResolving) { + if (imp) { + _objc_inform("RESOLVE: method %c[%s %s] " + "dynamically resolved to %p", + cls->isMetaClass() ? '+' : '-', + cls->nameForLogging(), sel_getName(sel), imp); + } + else { + // Method resolver didn't add anything? + _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES" + ", but no new implementation of %c[%s %s] was found", + cls->nameForLogging(), sel_getName(sel), + cls->isMetaClass() ? '+' : '-', + cls->nameForLogging(), sel_getName(sel)); + } + } +} + + +/*********************************************************************** +* _class_resolveMethod +* Call +resolveClassMethod or +resolveInstanceMethod. +* Returns nothing; any result would be potentially out-of-date already. +* Does not check if the method already exists. +**********************************************************************/ +void _class_resolveMethod(Class cls, SEL sel, id inst) +{ + if (! cls->isMetaClass()) { + // try [cls resolveInstanceMethod:sel] + _class_resolveInstanceMethod(cls, sel, inst); + } + else { + // try [nonMetaClass resolveClassMethod:sel] + // and [cls resolveInstanceMethod:sel] + _class_resolveClassMethod(cls, sel, inst); + if (!lookUpImpOrNil(cls, sel, inst, + NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) + { + _class_resolveInstanceMethod(cls, sel, inst); + } + } +} + + /*********************************************************************** * log_and_fill_cache * Log this method call. If the logger permits it, fill the method cache. @@ -393,9 +506,9 @@ IMP lookUpImpOrForward(Class cls, SEL sel, id inst, // Check for +initialize if (initialize && !cls->isInitialized()) { - _class_initialize (_class_getNonMetaClass(cls, inst)); - // If sel == initialize, _class_initialize will send +initialize and - // then the messenger will send +initialize again after this + initializeNonMetaClass (_class_getNonMetaClass(cls, inst)); + // If sel == initialize, initializeNonMetaClass 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 } @@ -734,7 +847,7 @@ int class_getVersion(Class cls) * Return the ordinary class for this class or metaclass. * Used by +initialize. **********************************************************************/ -Class _class_getNonMetaClass(Class cls, id obj) +static Class _class_getNonMetaClass(Class cls, id obj) { // fixme ick if (cls->isMetaClass()) { @@ -759,6 +872,14 @@ Class _class_getNonMetaClass(Class cls, id obj) } +Class class_initialize(Class cls, id inst) { + if (!cls->isInitialized()) { + initializeNonMetaClass (_class_getNonMetaClass(cls, inst)); + } + return cls; +} + + Cache _class_getCache(Class cls) { return cls->cache; diff --git a/runtime/objc-class.mm b/runtime/objc-class.mm index bb9ceae2..7a719944 100644 --- a/runtime/objc-class.mm +++ b/runtime/objc-class.mm @@ -195,13 +195,35 @@ Class object_setClass(id obj, Class cls) // weakly-referenced object has an un-+initialized isa. // Unresolved future classes are not so protected. if (!cls->isFuture() && !cls->isInitialized()) { - _class_initialize(_class_getNonMetaClass(cls, nil)); + // use lookUpImpOrForward to indirectly provoke +initialize + // to avoid duplicating the code to actually send +initialize + lookUpImpOrForward(cls, SEL_initialize, nil, + YES/*initialize*/, YES/*cache*/, NO/*resolver*/); } return obj->changeIsa(cls); } +Class _Nullable +objc_opt_class(id _Nullable obj) { + if (obj == nil) { + return nil; + } + Class cls = nil; +#if OBJC_HAVE_TAGGED_POINTERS + const bool isTaggedPtr = _objc_isTaggedPointer(obj); + if (isTaggedPtr) { + cls = _objc_getClassForTag(_objc_getTaggedPointerTag(obj)); + } else { +#endif + cls = obj->ISA(); +#if OBJC_HAVE_TAGGED_POINTERS + } +#endif + return cls; +} + /*********************************************************************** * object_isClass. **********************************************************************/ @@ -552,10 +574,12 @@ void fixupCopiedIvars(id newObject, id oldObject) while ((byte = *weakLayout++)) { unsigned skips = (byte >> 4); unsigned weaks = (byte & 0x0F); - newPtr += skips, oldPtr += skips; + newPtr += skips; + oldPtr += skips; while (weaks--) { objc_copyWeak(newPtr, oldPtr); - ++newPtr, ++oldPtr; + ++newPtr; + ++oldPtr; } } } @@ -564,117 +588,6 @@ void fixupCopiedIvars(id newObject, id oldObject) } -/*********************************************************************** -* _class_resolveClassMethod -* Call +resolveClassMethod, looking for a method to be added to class cls. -* cls should be a metaclass. -* Does not check if the method already exists. -**********************************************************************/ -static void _class_resolveClassMethod(Class cls, SEL sel, id inst) -{ - assert(cls->isMetaClass()); - - if (! lookUpImpOrNil(cls, SEL_resolveClassMethod, inst, - NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) - { - // Resolver not implemented. - return; - } - - BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend; - bool resolved = msg(_class_getNonMetaClass(cls, inst), - SEL_resolveClassMethod, sel); - - // Cache the result (good or bad) so the resolver doesn't fire next time. - // +resolveClassMethod adds to self->ISA() a.k.a. cls - IMP imp = lookUpImpOrNil(cls, sel, inst, - NO/*initialize*/, YES/*cache*/, NO/*resolver*/); - - if (resolved && PrintResolving) { - if (imp) { - _objc_inform("RESOLVE: method %c[%s %s] " - "dynamically resolved to %p", - cls->isMetaClass() ? '+' : '-', - cls->nameForLogging(), sel_getName(sel), imp); - } - else { - // Method resolver didn't add anything? - _objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES" - ", but no new implementation of %c[%s %s] was found", - cls->nameForLogging(), sel_getName(sel), - cls->isMetaClass() ? '+' : '-', - cls->nameForLogging(), sel_getName(sel)); - } - } -} - - -/*********************************************************************** -* _class_resolveInstanceMethod -* Call +resolveInstanceMethod, looking for a method to be added to class cls. -* cls may be a metaclass or a non-meta class. -* Does not check if the method already exists. -**********************************************************************/ -static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst) -{ - if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, - NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) - { - // Resolver not implemented. - return; - } - - BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend; - bool resolved = msg(cls, SEL_resolveInstanceMethod, sel); - - // Cache the result (good or bad) so the resolver doesn't fire next time. - // +resolveInstanceMethod adds to self a.k.a. cls - IMP imp = lookUpImpOrNil(cls, sel, inst, - NO/*initialize*/, YES/*cache*/, NO/*resolver*/); - - if (resolved && PrintResolving) { - if (imp) { - _objc_inform("RESOLVE: method %c[%s %s] " - "dynamically resolved to %p", - cls->isMetaClass() ? '+' : '-', - cls->nameForLogging(), sel_getName(sel), imp); - } - else { - // Method resolver didn't add anything? - _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES" - ", but no new implementation of %c[%s %s] was found", - cls->nameForLogging(), sel_getName(sel), - cls->isMetaClass() ? '+' : '-', - cls->nameForLogging(), sel_getName(sel)); - } - } -} - - -/*********************************************************************** -* _class_resolveMethod -* Call +resolveClassMethod or +resolveInstanceMethod. -* Returns nothing; any result would be potentially out-of-date already. -* Does not check if the method already exists. -**********************************************************************/ -void _class_resolveMethod(Class cls, SEL sel, id inst) -{ - if (! cls->isMetaClass()) { - // try [cls resolveInstanceMethod:sel] - _class_resolveInstanceMethod(cls, sel, inst); - } - else { - // try [nonMetaClass resolveClassMethod:sel] - // and [cls resolveInstanceMethod:sel] - _class_resolveClassMethod(cls, sel, inst); - if (!lookUpImpOrNil(cls, sel, inst, - NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) - { - _class_resolveInstanceMethod(cls, sel, inst); - } - } -} - /*********************************************************************** * class_getClassMethod. Return the class method for the specified diff --git a/runtime/objc-gdb.h b/runtime/objc-gdb.h index 88dc2a8b..67ce2cb8 100644 --- a/runtime/objc-gdb.h +++ b/runtime/objc-gdb.h @@ -204,6 +204,15 @@ OBJC_EXPORT unsigned int objc_debug_taggedpointer_ext_payload_rshift #endif + +/*********************************************************************** +* Swift marker bits +**********************************************************************/ +#if __OBJC2__ +OBJC_EXPORT const uintptr_t objc_debug_swift_stable_abi_bit +OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0); +#endif + __END_DECLS // APPLE_API_PRIVATE diff --git a/runtime/objc-initialize.h b/runtime/objc-initialize.h index 9ec99b59..c4695d59 100644 --- a/runtime/objc-initialize.h +++ b/runtime/objc-initialize.h @@ -30,7 +30,7 @@ __BEGIN_DECLS struct _objc_initializing_classes; -extern void _class_initialize(Class cls); +extern void initializeNonMetaClass(Class cls); extern void _destroyInitializingClassList(struct _objc_initializing_classes *list); diff --git a/runtime/objc-initialize.mm b/runtime/objc-initialize.mm index 80491bb6..8f962aa8 100644 --- a/runtime/objc-initialize.mm +++ b/runtime/objc-initialize.mm @@ -481,7 +481,7 @@ void performForkChildInitialize(Class cls, Class supercls) * class_initialize. Send the '+initialize' message on demand to any * uninitialized class. Force initialization of superclasses first. **********************************************************************/ -void _class_initialize(Class cls) +void initializeNonMetaClass(Class cls) { assert(!cls->isMetaClass()); @@ -492,7 +492,7 @@ void _class_initialize(Class cls) // See note about deadlock above. supercls = cls->superclass; if (supercls && !supercls->isInitialized()) { - _class_initialize(supercls); + initializeNonMetaClass(supercls); } // Try to atomically set CLS_INITIALIZING. diff --git a/runtime/objc-internal.h b/runtime/objc-internal.h index 44e89ff2..6c4e9360 100644 --- a/runtime/objc-internal.h +++ b/runtime/objc-internal.h @@ -57,6 +57,11 @@ __BEGIN_DECLS +// This symbol is exported only from debug builds of libobjc itself. +#if defined(OBJC_IS_DEBUG_BUILD) +OBJC_EXPORT void _objc_isDebugBuild(void); +#endif + // In-place construction of an Objective-C class. // cls and metacls must each be OBJC_MAX_CLASS_SIZE bytes. // Returns nil if a class with the same name already exists. @@ -659,6 +664,14 @@ OBJC_EXPORT id _Nullable objc_allocWithZone(Class _Nullable cls) OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0); +OBJC_EXPORT id _Nullable +objc_alloc_init(Class _Nullable cls) + OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0); +// rdar://44986431 fixme correct availability for objc_alloc_init() + +OBJC_EXPORT Class _Nullable +objc_opt_class(id _Nullable obj) OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0); + OBJC_EXPORT id _Nullable objc_retain(id _Nullable obj) __asm__("_objc_retain") diff --git a/runtime/objc-object.h b/runtime/objc-object.h index 177ea10e..4c68e24b 100644 --- a/runtime/objc-object.h +++ b/runtime/objc-object.h @@ -43,7 +43,7 @@ bool prepareOptimizedReturn(ReturnDisposition disposition); #if SUPPORT_TAGGED_POINTERS extern "C" { - extern Class objc_debug_taggedpointer_classes[_OBJC_TAG_SLOT_COUNT*2]; + extern Class objc_debug_taggedpointer_classes[_OBJC_TAG_SLOT_COUNT]; extern Class objc_debug_taggedpointer_ext_classes[_OBJC_TAG_EXT_SLOT_COUNT]; } #define objc_tag_classes objc_debug_taggedpointer_classes diff --git a/runtime/objc-os.h b/runtime/objc-os.h index 662e1b20..ae290203 100644 --- a/runtime/objc-os.h +++ b/runtime/objc-os.h @@ -102,7 +102,6 @@ class nocopy_t { # include # include # include -//# include # include # include # include @@ -120,20 +119,6 @@ void vsyslog(int, const char *, va_list) UNAVAILABLE_ATTRIBUTE; #define fastpath(x) (__builtin_expect(bool(x), 1)) #define slowpath(x) (__builtin_expect(bool(x), 0)) -//typedef OSSpinLock os_lock_handoff_s; -//#define OS_LOCK_HANDOFF_INIT OS_SPINLOCK_INIT -// -//ALWAYS_INLINE void os_lock_lock(volatile os_lock_handoff_s *lock) { -// return OSSpinLockLock(lock); -//} -// -//ALWAYS_INLINE void os_lock_unlock(volatile os_lock_handoff_s *lock) { -// return OSSpinLockUnlock(lock); -//} -// -//ALWAYS_INLINE bool os_lock_trylock(volatile os_lock_handoff_s *lock) { -// return OSSpinLockTry(lock); -//} static ALWAYS_INLINE uintptr_t addc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout) @@ -147,125 +132,67 @@ subc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout) return __builtin_subcl(lhs, rhs, carryin, carryout); } - -#if __arm64__ - -// Pointer-size register prefix for inline asm -# if __LP64__ -# define p "x" // true arm64 -# else -# define p "w" // arm64_32 -# endif +#if __arm64__ && !__arm64e__ static ALWAYS_INLINE -uintptr_t +uintptr_t LoadExclusive(uintptr_t *src) { - uintptr_t result; - asm("ldxr %" p "0, [%x1]" - : "=r" (result) - : "r" (src), "m" (*src)); - return result; + return __builtin_arm_ldrex(src); } static ALWAYS_INLINE -bool +bool StoreExclusive(uintptr_t *dst, uintptr_t oldvalue __unused, uintptr_t value) { - uint32_t result; - asm("stxr %w0, %" p "2, [%x3]" - : "=&r" (result), "=m" (*dst) - : "r" (value), "r" (dst)); - return !result; + return !__builtin_arm_strex(value, dst); } static ALWAYS_INLINE -bool +bool StoreReleaseExclusive(uintptr_t *dst, uintptr_t oldvalue __unused, uintptr_t value) { - uint32_t result; - asm("stlxr %w0, %" p "2, [%x3]" - : "=&r" (result), "=m" (*dst) - : "r" (value), "r" (dst)); - return !result; -} - -static ALWAYS_INLINE -void -ClearExclusive(uintptr_t *dst) -{ - // pretend it writes to *dst for instruction ordering purposes - asm("clrex" : "=m" (*dst)); -} - -#undef p - -#elif __arm__ - -static ALWAYS_INLINE -uintptr_t -LoadExclusive(uintptr_t *src) -{ - return *src; -} - -static ALWAYS_INLINE -bool -StoreExclusive(uintptr_t *dst, uintptr_t oldvalue, uintptr_t value) -{ - return OSAtomicCompareAndSwapPtr((void *)oldvalue, (void *)value, - (void **)dst); -} - -static ALWAYS_INLINE -bool -StoreReleaseExclusive(uintptr_t *dst, uintptr_t oldvalue, uintptr_t value) -{ - return OSAtomicCompareAndSwapPtrBarrier((void *)oldvalue, (void *)value, - (void **)dst); + return !__builtin_arm_stlex(value, dst); } static ALWAYS_INLINE -void +void ClearExclusive(uintptr_t *dst __unused) { + __builtin_arm_clrex(); } - -#elif __x86_64__ || __i386__ +#else static ALWAYS_INLINE -uintptr_t +uintptr_t LoadExclusive(uintptr_t *src) { - return *src; + return __c11_atomic_load((_Atomic(uintptr_t) *)src, __ATOMIC_RELAXED); } static ALWAYS_INLINE -bool +bool StoreExclusive(uintptr_t *dst, uintptr_t oldvalue, uintptr_t value) { - - return __sync_bool_compare_and_swap((void **)dst, (void *)oldvalue, (void *)value); + return __c11_atomic_compare_exchange_weak((_Atomic(uintptr_t) *)dst, &oldvalue, value, __ATOMIC_RELAXED, __ATOMIC_RELAXED); } + static ALWAYS_INLINE -bool +bool StoreReleaseExclusive(uintptr_t *dst, uintptr_t oldvalue, uintptr_t value) { - return StoreExclusive(dst, oldvalue, value); + return __c11_atomic_compare_exchange_weak((_Atomic(uintptr_t) *)dst, &oldvalue, value, __ATOMIC_RELEASE, __ATOMIC_RELAXED); } static ALWAYS_INLINE -void +void ClearExclusive(uintptr_t *dst __unused) { } - -#else -# error unknown architecture #endif @@ -661,7 +588,7 @@ OBJC_EXTERN IMAGE_DOS_HEADER __ImageBase; // OS compatibility static inline uint64_t nanoseconds() { - return mach_absolute_time(); + return clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW); } // Internal data types @@ -757,9 +684,6 @@ using mutex_t = mutex_tt; using monitor_t = monitor_tt; using recursive_mutex_t = recursive_mutex_tt; -//extern "C" void os_unfair_lock_assert_owner(os_unfair_lock *); -//extern "C" void os_unfair_lock_assert_not_owner(os_unfair_lock *); - // Use fork_unsafe_lock to get a lock that isn't // acquired and released around fork(). // All fork-safe locks are checked in debug builds. diff --git a/runtime/objc-private.h b/runtime/objc-private.h index 66994f64..f28cd381 100644 --- a/runtime/objc-private.h +++ b/runtime/objc-private.h @@ -462,6 +462,7 @@ extern IMP lookUpImpOrForward(Class, SEL, id obj, bool initialize, bool cache, b extern IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel); extern bool class_respondsToSelector_inst(Class cls, SEL sel, id inst); +extern Class class_initialize(Class cls, id inst); extern bool objcMsgLogEnabled; extern bool logMessageSend(bool isClassMethod, @@ -551,6 +552,9 @@ typedef struct { struct SyncCache *syncCache; // for @synchronize struct alt_handler_list *handlerList; // for exception alt handlers char *printableNames[4]; // temporary demangled names for logging + const char **classNameLookups; // for objc_getClass() hooks + unsigned classNameLookupsAllocated; + unsigned classNameLookupsUsed; // If you add new fields here, don't forget to update // _objc_pthread_destroyspecific() @@ -618,7 +622,6 @@ extern void _unload_image(header_info *hi); extern const header_info *_headerForClass(Class cls); extern Class _class_remap(Class cls); -extern Class _class_getNonMetaClass(Class cls, id obj); extern Ivar _class_getVariable(Class cls, const char *name); extern unsigned _class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone, id *results, unsigned num_requested); @@ -632,8 +635,6 @@ extern IMP _category_getLoadMethod(Category cat); extern id object_cxxConstructFromClass(id obj, Class cls); extern void object_cxxDestruct(id obj); -extern void _class_resolveMethod(Class cls, SEL sel, id inst); - extern void fixupCopiedIvars(id newObject, id oldObject); extern Class _class_getClassForIvar(Class cls, Ivar ivar); diff --git a/runtime/objc-ptrauth.h b/runtime/objc-ptrauth.h index f766d26a..e275dcad 100644 --- a/runtime/objc-ptrauth.h +++ b/runtime/objc-ptrauth.h @@ -28,9 +28,6 @@ // On some architectures, method lists and method caches store signed IMPs. -// StorageSignedFunctionPointer is declared by libclosure. -#include - // fixme simply include ptrauth.h once all build trains have it #if __has_include () #include @@ -66,17 +63,16 @@ #if __has_feature(ptrauth_calls) +#if !__arm64__ +#error ptrauth other than arm64e is unimplemented +#endif + // Method lists use process-independent signature for compatibility. -// Method caches use process-dependent signature for extra protection. -// (fixme not yet __ptrauth(...) because of `stp` inline asm in objc-cache.mm) using MethodListIMP = IMP __ptrauth_objc_method_list_imp; -using MethodCacheIMP = - StorageSignedFunctionPointer; #else using MethodListIMP = IMP; -using MethodCacheIMP = IMP; #endif diff --git a/runtime/objc-references.mm b/runtime/objc-references.mm index c1119f04..7de81877 100644 --- a/runtime/objc-references.mm +++ b/runtime/objc-references.mm @@ -269,6 +269,16 @@ void operator() (ObjcAssociation &association) { }; void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) { + // This code used to work when nil was passed for object and key. Some code + // probably relies on that to not crash. Check and handle it explicitly. + // rdar://problem/44094390 + if (!object && !value) return; + + assert(object); + + if (object->getIsa()->forbidsAssociatedObjects()) + _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object)); + // retain the new value (if any) outside the lock. ObjcAssociation old_association(0, nil); id new_value = value ? acquireValue(value, policy) : nil; diff --git a/runtime/objc-runtime-new.h b/runtime/objc-runtime-new.h index 04a4c659..19258f69 100644 --- a/runtime/objc-runtime-new.h +++ b/runtime/objc-runtime-new.h @@ -29,30 +29,53 @@ typedef uint32_t mask_t; // x86_64 & arm64 asm are less efficient with 16-bits #else typedef uint16_t mask_t; #endif -typedef uintptr_t cache_key_t; +typedef uintptr_t SEL; struct swift_class_t; +enum Atomicity { Atomic = true, NotAtomic = false }; struct bucket_t { private: // IMP-first is better for arm64e ptrauth and no worse for arm64. // SEL-first is better for armv7* and i386 and x86_64. #if __arm64__ - MethodCacheIMP _imp; - cache_key_t _key; + uintptr_t _imp; + SEL _sel; #else - cache_key_t _key; - MethodCacheIMP _imp; + SEL _sel; + uintptr_t _imp; #endif + // Compute the ptrauth signing modifier from &_imp and newSel + uintptr_t modifierForSEL(SEL newSel) const { + return (uintptr_t)&_imp ^ (uintptr_t)newSel; + } + + // Sign newImp, with &_imp and newSel as modifiers. + uintptr_t signIMP(IMP newImp, SEL newSel) const { + if (!newImp) return 0; + return (uintptr_t) + ptrauth_auth_and_resign(newImp, + ptrauth_key_function_pointer, 0, + ptrauth_key_process_dependent_code, + modifierForSEL(newSel)); + } + public: - inline cache_key_t key() const { return _key; } - inline IMP imp() const { return (IMP)_imp; } - inline void setKey(cache_key_t newKey) { _key = newKey; } - inline void setImp(IMP newImp) { _imp = newImp; } + inline SEL sel() const { return _sel; } + + inline IMP imp() const { + if (!_imp) return nil; + return (IMP) + ptrauth_auth_and_resign((const void *)_imp, + ptrauth_key_process_dependent_code, + modifierForSEL(_sel), + ptrauth_key_function_pointer, 0); + } - void set(cache_key_t newKey, IMP newImp); + template + void set(SEL newSel, IMP newImp); }; @@ -78,7 +101,7 @@ struct cache_t { void expand(); void reallocate(mask_t oldCapacity, mask_t newCapacity); - struct bucket_t * find(cache_key_t key, id receiver); + struct bucket_t * find(SEL sel, id receiver); static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn)); }; @@ -402,14 +425,16 @@ struct locstamped_category_list_t { #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 has ro field for Swift metadata initializer callback +#define RO_HAS_SWIFT_INITIALIZER (1<<6) // class compiled with ARC #define RO_IS_ARC (1<<7) // class has .cxx_destruct but no .cxx_construct (with RO_HAS_CXX_STRUCTORS) #define RO_HAS_CXX_DTOR_ONLY (1<<8) // class is not ARC but has ARC-style weak ivar layout #define RO_HAS_WEAK_WITHOUT_ARC (1<<9) +// class does not allow associated objects on instances +#define RO_FORBIDS_ASSOCIATED_OBJECTS (1<<10) // class is in an unloadable bundle - must never be set by compiler #define RO_FROM_BUNDLE (1<<29) @@ -445,8 +470,8 @@ struct locstamped_category_list_t { #endif // class has instance-specific GC layout #define RW_HAS_INSTANCE_SPECIFIC_LAYOUT (1 << 21) -// available for use -// #define RW_20 (1<<20) +// class does not allow associated objects on its instances +#define RW_FORBIDS_ASSOCIATED_OBJECTS (1<<20) // class has started realizing but not yet completed it #define RW_REALIZING (1<<19) @@ -568,9 +593,33 @@ struct class_ro_t { const uint8_t * weakIvarLayout; property_list_t *baseProperties; + // This field exists only when RO_HAS_SWIFT_INITIALIZER is set. + _objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0]; + + _objc_swiftMetadataInitializer swiftMetadataInitializer() const { + if (flags & RO_HAS_SWIFT_INITIALIZER) { + return _swiftMetadataInitializer_NEVER_USE[0]; + } else { + return nil; + } + } + method_list_t *baseMethods() const { return baseMethodList; } + + class_ro_t *duplicate() const { + if (flags & RO_HAS_SWIFT_INITIALIZER) { + size_t size = sizeof(*this) + sizeof(_swiftMetadataInitializer_NEVER_USE[0]); + class_ro_t *ro = (class_ro_t *)memdup(this, size); + ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0]; + return ro; + } else { + size_t size = sizeof(*this); + class_ro_t *ro = (class_ro_t *)memdup(this, size); + return ro; + } + } }; @@ -878,43 +927,47 @@ struct class_data_bits_t { } #if FAST_ALLOC - static uintptr_t updateFastAlloc(uintptr_t oldBits, uintptr_t change) + // On entry, `newBits` is a bits value after setting and/or clearing + // the bits in `change`. Fix the fast-alloc parts of newBits if necessary + // and return the updated value. + static uintptr_t updateFastAlloc(uintptr_t newBits, uintptr_t change) { if (change & FAST_ALLOC_MASK) { - if (((oldBits & FAST_ALLOC_MASK) == FAST_ALLOC_VALUE) && - ((oldBits >> FAST_SHIFTED_SIZE_SHIFT) != 0)) + if (((newBits & FAST_ALLOC_MASK) == FAST_ALLOC_VALUE) && + ((newBits >> FAST_SHIFTED_SIZE_SHIFT) != 0)) { - oldBits |= FAST_ALLOC; + newBits |= FAST_ALLOC; } else { - oldBits &= ~FAST_ALLOC; + newBits &= ~FAST_ALLOC; } } - return oldBits; + return newBits; } #else - static uintptr_t updateFastAlloc(uintptr_t oldBits, uintptr_t change) { - return oldBits; + static uintptr_t updateFastAlloc(uintptr_t newBits, uintptr_t change) { + return newBits; } #endif - void setBits(uintptr_t set) + // Atomically set the bits in `set` and clear the bits in `clear`. + // set and clear must not overlap. + void setAndClearBits(uintptr_t set, uintptr_t clear) { + assert((set & clear) == 0); uintptr_t oldBits; uintptr_t newBits; do { oldBits = LoadExclusive(&bits); - newBits = updateFastAlloc(oldBits | set, set); + newBits = updateFastAlloc((oldBits | set) & ~clear, set | clear); } while (!StoreReleaseExclusive(&bits, oldBits, newBits)); } - void clearBits(uintptr_t clear) - { - uintptr_t oldBits; - uintptr_t newBits; - do { - oldBits = LoadExclusive(&bits); - newBits = updateFastAlloc(oldBits & ~clear, clear); - } while (!StoreReleaseExclusive(&bits, oldBits, newBits)); + void setBits(uintptr_t set) { + setAndClearBits(set, 0); + } + + void clearBits(uintptr_t clear) { + setAndClearBits(0, clear); } public: @@ -933,6 +986,20 @@ struct class_data_bits_t { bits = newBits; } + // Get the class's ro data, even in the presence of concurrent realization. + // fixme this isn't really safe without a compiler barrier at least + // and probably a memory barrier when realizeClass changes the data field + const class_ro_t *safe_ro() { + class_rw_t *maybe_rw = data(); + if (maybe_rw->flags & RW_REALIZED) { + // maybe_rw is rw + return maybe_rw->ro; + } else { + // maybe_rw is actually ro + return (class_ro_t *)maybe_rw; + } + } + #if FAST_HAS_DEFAULT_RR bool hasDefaultRR() { return getBit(FAST_HAS_DEFAULT_RR); @@ -1096,14 +1163,26 @@ struct class_data_bits_t { return getBit(FAST_IS_SWIFT_STABLE); } void setIsSwiftStable() { - setBits(FAST_IS_SWIFT_STABLE); + setAndClearBits(FAST_IS_SWIFT_STABLE, FAST_IS_SWIFT_LEGACY); } bool isSwiftLegacy() { return getBit(FAST_IS_SWIFT_LEGACY); } void setIsSwiftLegacy() { - setBits(FAST_IS_SWIFT_LEGACY); + setAndClearBits(FAST_IS_SWIFT_LEGACY, FAST_IS_SWIFT_STABLE); + } + + // fixme remove this once the Swift runtime uses the stable bits + bool isSwiftStable_ButAllowLegacyForNow() { + return isAnySwift(); + } + + _objc_swiftMetadataInitializer swiftMetadataInitializer() { + // This function is called on un-realized classes without + // holding any locks. + // Beware of races with other realizers. + return safe_ro()->swiftMetadataInitializer(); } }; @@ -1205,6 +1284,40 @@ struct objc_class : objc_object { return bits.isAnySwift(); } + bool isSwiftStable_ButAllowLegacyForNow() { + return bits.isSwiftStable_ButAllowLegacyForNow(); + } + + // Swift stable ABI built for old deployment targets looks weird. + // The is-legacy bit is set for compatibility with old libobjc. + // We are on a "new" deployment target so we need to rewrite that bit. + // These stable-with-legacy-bit classes are distinguished from real + // legacy classes using another bit in the Swift data + // (ClassFlags::IsSwiftPreStableABI) + + bool isUnfixedBackwardDeployingStableSwift() { + // Only classes marked as Swift legacy need apply. + if (!bits.isSwiftLegacy()) return false; + + // Check the true legacy vs stable distinguisher. + // The low bit of Swift's ClassFlags is SET for true legacy + // and UNSET for stable pretending to be legacy. + uint32_t swiftClassFlags = *(uint32_t *)(&bits + 1); + bool isActuallySwiftLegacy = bool(swiftClassFlags & 1); + return !isActuallySwiftLegacy; + } + + void fixupBackwardDeployingStableSwift() { + if (isUnfixedBackwardDeployingStableSwift()) { + // Class really is stable Swift, pretending to be pre-stable. + // Fix its lie. + bits.setIsSwiftStable(); + } + } + + _objc_swiftMetadataInitializer swiftMetadataInitializer() { + return bits.swiftMetadataInitializer(); + } // Return YES if the class's ivars are managed by ARC, // or the class is MRC but has ARC-style weak ivars. @@ -1218,6 +1331,10 @@ struct objc_class : objc_object { } + bool forbidsAssociatedObjects() { + return (data()->flags & RW_FORBIDS_ASSOCIATED_OBJECTS); + } + #if SUPPORT_NONPOINTER_ISA // Tracked in non-pointer isas; not tracked otherwise #else @@ -1281,6 +1398,11 @@ struct objc_class : objc_object { return data()->ro->flags & RO_META; } + // Like isMetaClass, but also valid on un-realized classes + bool isMetaClassMaybeUnrealized() { + return bits.safe_ro()->flags & RO_META; + } + // NOT identical to this->ISA when this is a metaclass Class getMeta() { if (isMetaClass()) return (Class)this; @@ -1305,7 +1427,7 @@ struct objc_class : objc_object { } } - const char *demangledName(bool realize = false); + const char *demangledName(); const char *nameForLogging(); // May be unaligned depending on class's ivars. diff --git a/runtime/objc-runtime-new.mm b/runtime/objc-runtime-new.mm index b52aaa04..f7d09c9c 100644 --- a/runtime/objc-runtime-new.mm +++ b/runtime/objc-runtime-new.mm @@ -42,7 +42,6 @@ static void detach_class(Class cls, bool isMeta); static void free_class(Class cls); static Class setSuperclass(Class cls, Class newSuper); -static Class realizeClass(Class cls); static method_t *getMethodNoSuper_nolock(Class cls, SEL sel); static method_t *getMethod_nolock(Class cls, SEL sel); static IMP addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace); @@ -57,6 +56,8 @@ #if SUPPORT_FIXUP static void fixupMessageRef(message_ref_t *msg); #endif +static Class realizeClassMaybeSwiftAndUnlock(Class cls, mutex_t& lock); +static Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized); static bool MetaclassNSObjectAWZSwizzled; static bool ClassNSObjectRRSwizzled; @@ -174,6 +175,12 @@ void lock_init(void) #endif +/*********************************************************************** +* Swift marker bits +**********************************************************************/ +const uintptr_t objc_debug_swift_stable_abi_bit = FAST_IS_SWIFT_STABLE; + + /*********************************************************************** * allocatedClasses * A table of all classes (and metaclasses) which have been allocated @@ -354,9 +361,7 @@ void _objc_setClassCopyFixupHandler(void (* _Nonnull newFixupHandler) if (rw->flags & RW_COPIED_RO) { // already writeable, do nothing } else { - class_ro_t *ro = (class_ro_t *) - memdup(rw->ro, sizeof(*rw->ro)); - rw->ro = ro; + rw->ro = rw->ro->duplicate(); rw->flags |= RW_COPIED_RO; } return (class_ro_t *)rw->ro; @@ -930,7 +935,8 @@ static void remethodizeClass(Class cls) * Classes with no duplicates are not included. * Classes in the preoptimized named-class table are not included. * Classes whose duplicates are in the preoptimized table are not included. -* Most code should use getNonMetaClass() instead of reading this table. +* Most code should use getMaybeUnrealizedNonMetaClass() +* instead of reading this table. * Locking: runtimeLock must be read- or write-locked by the caller **********************************************************************/ static NXMapTable *nonmeta_class_map = nil; @@ -960,8 +966,8 @@ static void addNonMetaClass(Class cls) void *old; old = NXMapInsert(nonMetaClasses(), cls->ISA(), cls); - assert(!cls->isMetaClass()); - assert(cls->ISA()->isMetaClass()); + assert(!cls->isMetaClassMaybeUnrealized()); + assert(cls->ISA()->isMetaClassMaybeUnrealized()); assert(!old); } @@ -1090,9 +1096,12 @@ static bool scanMangledField(const char *&string, const char *end, /*********************************************************************** -* getClass +* getClassExceptSomeSwift * Looks up a class by name. The class MIGHT NOT be realized. * Demangled Swift names are recognized. +* Classes known to the Swift runtime but not yet used are NOT recognized. +* (such as subclasses of un-instantiated generics) +* Use look_up_class() to find them as well. * Locking: runtimeLock must be read- or write-locked by the caller. **********************************************************************/ @@ -1115,7 +1124,7 @@ static Class getClass_impl(const char *name) return getPreoptimizedClass(name); } -static Class getClass(const char *name) +static Class getClassExceptSomeSwift(const char *name) { runtimeLock.assertLocked(); @@ -1144,11 +1153,12 @@ static void addNamedClass(Class cls, const char *name, Class replacing = nil) { runtimeLock.assertLocked(); Class old; - if ((old = getClass(name)) && old != replacing) { + if ((old = getClassExceptSomeSwift(name)) && old != replacing) { inform_duplicate(name, old, cls); - // getNonMetaClass uses name lookups. Classes not found by name - // lookup must be in the secondary meta->nonmeta table. + // getMaybeUnrealizedNonMetaClass uses name lookups. + // Classes not found by name lookup must be in the + // secondary meta->nonmeta table. addNonMetaClass(cls); } else { NXMapInsert(gdb_objc_realized_classes, name, cls); @@ -1389,19 +1399,19 @@ static void remapClassRef(Class *clsref) /*********************************************************************** -* getNonMetaClass +* getMaybeUnrealizedNonMetaClass * Return the ordinary class for this class or metaclass. * `inst` is an instance of `cls` or a subclass thereof, or nil. * Non-nil inst is faster. +* The result may be unrealized. * Used by +initialize. * Locking: runtimeLock must be read- or write-locked by the caller **********************************************************************/ -static Class getNonMetaClass(Class metacls, id inst) +static Class getMaybeUnrealizedNonMetaClass(Class metacls, id inst) { static int total, named, secondary, sharedcache; runtimeLock.assertLocked(); - - realizeClass(metacls); + assert(metacls->isRealized()); total++; @@ -1409,6 +1419,7 @@ static Class getNonMetaClass(Class metacls, id inst) if (!metacls->isMetaClass()) return metacls; // metacls really is a metaclass + // which means inst (if any) is a class // special case for root metaclass // where inst == inst->ISA() == metacls is possible @@ -1422,17 +1433,16 @@ static Class getNonMetaClass(Class metacls, id inst) // use inst if available if (inst) { - Class cls = (Class)inst; - realizeClass(cls); + Class cls = remapClass((Class)inst); // cls may be a subclass - find the real class for metacls - while (cls && cls->ISA() != metacls) { + // fixme this probably stops working once Swift starts + // reallocating classes if cls is unrealized. + while (cls) { + if (cls->ISA() == metacls) { + assert(!cls->isMetaClassMaybeUnrealized()); + return cls; + } cls = cls->superclass; - realizeClass(cls); - } - if (cls) { - assert(!cls->isMetaClass()); - assert(cls->ISA() == metacls); - return cls; } #if DEBUG _objc_fatal("cls is not an instance of metacls"); @@ -1443,7 +1453,7 @@ static Class getNonMetaClass(Class metacls, id inst) // try name lookup { - Class cls = getClass(metacls->mangledName()); + Class cls = getClassExceptSomeSwift(metacls->mangledName()); if (cls->ISA() == metacls) { named++; if (PrintInitializing) { @@ -1451,8 +1461,6 @@ static Class getNonMetaClass(Class metacls, id inst) "successful by-name metaclass lookups", named, total, named*100.0/total); } - - realizeClass(cls); return cls; } } @@ -1469,7 +1477,6 @@ static Class getNonMetaClass(Class metacls, id inst) } assert(cls->ISA() == metacls); - realizeClass(cls); return cls; } } @@ -1498,7 +1505,6 @@ static Class getNonMetaClass(Class metacls, id inst) sharedcache, total, sharedcache*100.0/total); } - realizeClass(cls); return cls; } } @@ -1508,19 +1514,66 @@ static Class getNonMetaClass(Class metacls, id inst) /*********************************************************************** -* _class_getNonMetaClass -* Return the ordinary class for this class or metaclass. -* Used by +initialize. -* Locking: acquires runtimeLock +* class_initialize. Send the '+initialize' message on demand to any +* uninitialized class. Force initialization of superclasses first. +* inst is an instance of cls, or nil. Non-nil is better for performance. +* Returns the class pointer. If the class was unrealized then +* it may be reallocated. +* Locking: +* runtimeLock must be held by the caller +* This function may drop the lock. +* On exit the lock is re-acquired or dropped as requested by leaveLocked. **********************************************************************/ -Class _class_getNonMetaClass(Class cls, id obj) +static Class initializeAndMaybeRelock(Class cls, id inst, + mutex_t& lock, bool leaveLocked) { - mutex_locker_t lock(runtimeLock); - cls = getNonMetaClass(cls, obj); + lock.assertLocked(); assert(cls->isRealized()); + + if (cls->isInitialized()) { + if (!leaveLocked) lock.unlock(); + return cls; + } + + // Find the non-meta class for cls, if it is not already one. + // The +initialize message is sent to the non-meta class object. + Class nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst); + + // Realize the non-meta class if necessary. + if (nonmeta->isRealized()) { + // nonmeta is cls, which was already realized + // OR nonmeta is distinct, but is already realized + // - nothing else to do + lock.unlock(); + } else { + nonmeta = realizeClassMaybeSwiftAndUnlock(nonmeta, lock); + // runtimeLock is now unlocked + // fixme Swift can't relocate the class today, + // but someday it will: + cls = object_getClass(nonmeta); + } + + // runtimeLock is now unlocked, for +initialize dispatch + assert(nonmeta->isRealized()); + initializeNonMetaClass(nonmeta); + + if (leaveLocked) runtimeLock.lock(); return cls; } +// Locking: acquires runtimeLock +Class class_initialize(Class cls, id obj) +{ + runtimeLock.lock(); + return initializeAndMaybeRelock(cls, obj, runtimeLock, false); +} + +// Locking: caller must hold runtimeLock; this may drop and re-acquire it +static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock) +{ + return initializeAndMaybeRelock(cls, obj, lock, true); +} + /*********************************************************************** * addRootClass @@ -1849,13 +1902,14 @@ static void reconcileInstanceVariables(Class cls, Class supercls, const class_ro /*********************************************************************** -* realizeClass +* realizeClassWithoutSwift * Performs first-time initialization on class cls, * including allocating its read-write data. +* Does not perform any Swift-side initialization. * Returns the real class structure for the class. * Locking: runtimeLock must be write-locked by the caller **********************************************************************/ -static Class realizeClass(Class cls) +static Class realizeClassWithoutSwift(Class cls) { runtimeLock.assertLocked(); @@ -1895,16 +1949,22 @@ static Class realizeClass(Class cls) cls->chooseClassArrayIndex(); if (PrintConnecting) { - _objc_inform("CLASS: realizing class '%s'%s %p %p #%u", + _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s", cls->nameForLogging(), isMeta ? " (meta)" : "", - (void*)cls, ro, cls->classArrayIndex()); + (void*)cls, ro, cls->classArrayIndex(), + cls->isSwiftStable() ? "(swift)" : "", + cls->isSwiftLegacy() ? "(pre-stable swift)" : ""); } // Realize superclass and metaclass, if they aren't already. // This needs to be done after RW_REALIZED is set above, for root classes. // This needs to be done after class index is chosen, for root metaclasses. - supercls = realizeClass(remapClass(cls->superclass)); - metacls = realizeClass(remapClass(cls->ISA())); + // This assumes that none of those classes have Swift contents, + // or that Swift's initializers have already been called. + // fixme that assumption will be wrong if we add support + // for ObjC subclasses of Swift classes. + supercls = realizeClassWithoutSwift(remapClass(cls->superclass)); + metacls = realizeClassWithoutSwift(remapClass(cls->ISA())); #if SUPPORT_NONPOINTER_ISA // Disable non-pointer isa for some classes and/or platforms. @@ -1959,6 +2019,14 @@ static Class realizeClass(Class cls) cls->setHasCxxCtor(); } } + + // Propagate the associated objects forbidden flag from ro or from + // the superclass. + if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) || + (supercls && supercls->forbidsAssociatedObjects())) + { + rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS; + } // Connect this class to its superclass's subclass lists if (supercls) { @@ -1974,6 +2042,172 @@ static Class realizeClass(Class cls) } +/*********************************************************************** +* _objc_realizeClassFromSwift +* Called by Swift when it needs the ObjC part of a class to be realized. +* There are four cases: +* 1. cls != nil; previously == cls +* Class cls is being realized in place +* 2. cls != nil; previously == nil +* Class cls is being constructed at runtime +* 3. cls != nil; previously != cls +* The class that was at previously has been reallocated to cls +* 4. cls == nil, previously != nil +* The class at previously is hereby disavowed +* +* Only variants #1 and #2 are supported today. +* +* Locking: acquires runtimeLock +**********************************************************************/ +Class _objc_realizeClassFromSwift(Class cls, void *previously) +{ + if (cls) { + if (previously && previously != (void*)cls) { + // #3: relocation + // In the future this will mean remapping the old address + // to the new class, and installing dispatch forwarding + // machinery at the old address + _objc_fatal("Swift requested that class %p be reallocated, " + "but libobjc does not support that.", previously); + } else { + // #1 and #2: realization in place, or new class + mutex_locker_t lock(runtimeLock); + + if (!previously) { + // #2: new class + cls = readClass(cls, false/*bundle*/, false/*shared cache*/); + } + + // #1 and #2: realization in place, or new class + // We ignore the Swift metadata initializer callback. + // We assume that's all handled since we're being called from Swift. + return realizeClassWithoutSwift(cls); + } + } + else { + // #4: disavowal + // In the future this will mean remapping the old address to nil + // and if necessary removing the old address from any other tables. + _objc_fatal("Swift requested that class %p be ignored, " + "but libobjc does not support that.", previously); + } +} + +/*********************************************************************** +* realizeSwiftClass +* Performs first-time initialization on class cls, +* including allocating its read-write data, +* and any Swift-side initialization. +* Returns the real class structure for the class. +* Locking: acquires runtimeLock indirectly +**********************************************************************/ +static Class realizeSwiftClass(Class cls) +{ + runtimeLock.assertUnlocked(); + + // Some assumptions: + // * Metaclasses never have a Swift initializer. + // * Root classes never have a Swift initializer. + // (These two together avoid initialization order problems at the root.) + // * Unrealized non-Swift classes have no Swift ancestry. + // * Unrealized Swift classes with no initializer have no ancestry that + // does have the initializer. + // (These two together mean we don't need to scan superclasses here + // and we don't need to worry about Swift superclasses inside + // realizeClassWithoutSwift()). + + // fixme some of these assumptions will be wrong + // if we add support for ObjC sublasses of Swift classes. + +#if DEBUG + runtimeLock.lock(); + assert(remapClass(cls) == cls); + assert(cls->isSwiftStable_ButAllowLegacyForNow()); + assert(!cls->isMetaClassMaybeUnrealized()); + assert(cls->superclass); + runtimeLock.unlock(); +#endif + + // Look for a Swift metadata initialization function + // installed on the class. If it is present we call it. + // That function in turn initializes the Swift metadata, + // prepares the "compiler-generated" ObjC metadata if not + // already present, and calls _objc_realizeSwiftClass() to finish + // our own initialization. + + if (auto init = cls->swiftMetadataInitializer()) { + if (PrintConnecting) { + _objc_inform("CLASS: calling Swift metadata initializer " + "for class '%s' (%p)", cls->nameForLogging(), cls); + } + + Class newcls = init(cls, nil); + + // fixme someday Swift will need to relocate classes at this point, + // but we don't accept that yet. + if (cls != newcls) { + _objc_fatal("Swift metadata initializer moved a class " + "from %p to %p, but libobjc does not yet allow that.", + cls, newcls); + } + + return newcls; + } + else { + // No Swift-side initialization callback. + // Perform our own realization directly. + mutex_locker_t lock(runtimeLock); + return realizeClassWithoutSwift(cls); + } +} + + +/*********************************************************************** +* realizeClassMaybeSwift (MaybeRelock / AndUnlock / AndLeaveLocked) +* Realize a class that might be a Swift class. +* Returns the real class structure for the class. +* Locking: +* runtimeLock must be held on entry +* runtimeLock may be dropped during execution +* ...AndUnlock function leaves runtimeLock unlocked on exit +* ...AndLeaveLocked re-acquires runtimeLock if it was dropped +* This complication avoids repeated lock transitions in some cases. +**********************************************************************/ +static Class +realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked) +{ + lock.assertLocked(); + + if (!cls->isSwiftStable_ButAllowLegacyForNow()) { + // Non-Swift class. Realize it now with the lock still held. + // fixme wrong in the future for objc subclasses of swift classes + realizeClassWithoutSwift(cls); + if (!leaveLocked) lock.unlock(); + } else { + // Swift class. We need to drop locks and call the Swift + // runtime to initialize it. + lock.unlock(); + cls = realizeSwiftClass(cls); + assert(cls->isRealized()); // callback must have provoked realization + if (leaveLocked) lock.lock(); + } + + return cls; +} + +static Class +realizeClassMaybeSwiftAndUnlock(Class cls, mutex_t& lock) +{ + return realizeClassMaybeSwiftMaybeRelock(cls, lock, false); +} + +static Class +realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock) +{ + return realizeClassMaybeSwiftMaybeRelock(cls, lock, true); +} + + /*********************************************************************** * missingWeakSuperclass * Return YES if some superclass of cls was weak-linked and is missing. @@ -2002,6 +2236,7 @@ static Class realizeClass(Class cls) * realizeAllClassesInImage * Non-lazily realizes all unrealized classes in the given image. * Locking: runtimeLock must be held by the caller. +* Locking: this function may drop and re-acquire the lock. **********************************************************************/ static void realizeAllClassesInImage(header_info *hi) { @@ -2015,7 +2250,10 @@ static void realizeAllClassesInImage(header_info *hi) classlist = _getObjc2ClassList(hi, &count); for (i = 0; i < count; i++) { - realizeClass(remapClass(classlist[i])); + Class cls = remapClass(classlist[i]); + if (cls) { + realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock); + } } hi->setAllClassesRealized(YES); @@ -2026,6 +2264,11 @@ static void realizeAllClassesInImage(header_info *hi) * realizeAllClasses * Non-lazily realizes all unrealized classes in all known images. * Locking: runtimeLock must be held by the caller. +* Locking: this function may drop and re-acquire the lock. +* Dropping the lock makes this function thread-unsafe with respect +* to concurrent image unload, but the callers of this function +* already ultimately do something that is also thread-unsafe with +* respect to image unload (such as using the list of all classes). **********************************************************************/ static void realizeAllClasses(void) { @@ -2033,7 +2276,7 @@ static void realizeAllClasses(void) header_info *hi; for (hi = FirstHeader; hi; hi = hi->getNext()) { - realizeAllClassesInImage(hi); + realizeAllClassesInImage(hi); // may drop and re-acquire runtimeLock } } @@ -2242,6 +2485,20 @@ bool mustReadClasses(header_info *hi) goto readthem; } + // readClass() rewrites bits in backward-deploying Swift stable ABI code. + // The assumption here is there there are no such classes + // in the dyld shared cache. +#if DEBUG + { + size_t count; + classref_t *classlist = _getObjc2ClassList(hi, &count); + for (size_t i = 0; i < count; i++) { + Class cls = remapClass(classlist[i]); + assert(!cls->isUnfixedBackwardDeployingStableSwift()); + } + } +#endif + // readClass() does not need to do anything. return NO; @@ -2300,6 +2557,8 @@ Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized) if (cls->ISA()->cache._occupied) cls->ISA()->cache._occupied = 0; #endif + cls->fixupBackwardDeployingStableSwift(); + Class replacing = nil; if (Class newCls = popFutureNamedClass(mangledName)) { // This name was previously allocated as a future class. @@ -2330,7 +2589,7 @@ Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized) // class list built in shared cache // fixme strict assert doesn't work because of duplicates // assert(cls == getClass(name)); - assert(getClass(mangledName)); + assert(getClassExceptSomeSwift(mangledName)); } else { addNamedClass(cls, mangledName, replacing); addClassTableEntry(cls); @@ -2473,7 +2732,7 @@ void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int un // Disable nonpointer isa if any image contains old Swift code for (EACH_HEADER) { if (hi->info()->containsSwift() && - hi->info()->swiftVersion() < objc_image_info::SwiftVersion3) + hi->info()->swiftUnstableVersion() < objc_image_info::SwiftVersion3) { DisableNonpointerIsa = true; if (PrintRawIsa) { @@ -2683,7 +2942,18 @@ void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int un #endif addClassTableEntry(cls); - realizeClass(cls); + + if (cls->isSwiftStable()) { + if (cls->swiftMetadataInitializer()) { + _objc_fatal("Swift class %s with a metadata initializer " + "is not allowed to be non-lazy", + cls->nameForLogging()); + } + // fixme also disallow relocatable classes + // We can't disallow all Swift classes because of + // classes like Swift.__EmptyArrayStorage + } + realizeClassWithoutSwift(cls); } } @@ -2692,8 +2962,12 @@ void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int un // Realize newly-resolved future classes, in case CF manipulates them if (resolvedFutureClasses) { for (i = 0; i < resolvedFutureClassCount; i++) { - realizeClass(resolvedFutureClasses[i]); - resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/); + Class cls = resolvedFutureClasses[i]; + if (cls->isSwiftStable()) { + _objc_fatal("Swift class is not allowed to be future"); + } + realizeClassWithoutSwift(cls); + cls->setInstancesRequireRawIsa(false/*inherited*/); } free(resolvedFutureClasses); } @@ -2880,7 +3154,11 @@ void prepare_load_methods(const headerType *mhdr) category_t *cat = categorylist[i]; Class cls = remapClass(cat->cls); if (!cls) continue; // category for ignored weak-linked class - realizeClass(cls); + if (cls->isSwiftStable()) { + _objc_fatal("Swift class extensions and categories on Swift " + "classes are not allowed to have +load methods"); + } + realizeClassWithoutSwift(cls); assert(cls->ISA()->isRealized()); add_category_to_loadable_list(cat); } @@ -4418,7 +4696,7 @@ void objc_registerProtocol(Protocol *proto_gen) for (size_t i = 0; i < count; i++) { Class cls = remapClass(classlist[i]); if (cls) { - names[i-shift] = cls->demangledName(true/*realize*/); + names[i-shift] = cls->demangledName(); } else { shift++; // ignored weak-linked class } @@ -4554,12 +4832,12 @@ void objc_registerProtocol(Protocol *proto_gen) /*********************************************************************** * objc_class::demangledName * If realize=false, the class must already be realized or future. -* Locking: If realize=true, runtimeLock must be held by the caller. +* Locking: runtimeLock may or may not be held by the caller. **********************************************************************/ mutex_t DemangleCacheLock; static NXHashTable *DemangleCache; const char * -objc_class::demangledName(bool realize) +objc_class::demangledName() { // Return previously demangled name if available. if (isRealized() || isFuture()) { @@ -4587,33 +4865,30 @@ void objc_registerProtocol(Protocol *proto_gen) return mangled; } - // Class is not yet realized and name is mangled. Realize the class. + // Class is not yet realized and name is mangled. + // Allocate the name but don't save it in the class. + // Save the name in a side cache instead to prevent leaks. + // When the class is actually realized we may allocate a second + // copy of the name, but we don't care. + // (Previously we would try to realize the class now and save the + // name there, but realization is more complicated for Swift classes.) + // Only objc_copyClassNamesForImage() should get here. - // fixme lldb's calls to class_getName() can also get here when // interrogating the dyld shared cache. (rdar://27258517) // fixme runtimeLock.assertLocked(); // fixme assert(realize); - - if (realize) { - runtimeLock.assertLocked(); - realizeClass((Class)this); - data()->demangledName = de; - return de; - } - else { - // Save the string to avoid leaks. - char *cached; - { - mutex_locker_t lock(DemangleCacheLock); - if (!DemangleCache) { - DemangleCache = NXCreateHashTable(NXStrPrototype, 0, nil); - } - cached = (char *)NXHashInsertIfAbsent(DemangleCache, de); + + char *cached; + { + mutex_locker_t lock(DemangleCacheLock); + if (!DemangleCache) { + DemangleCache = NXCreateHashTable(NXStrPrototype, 0, nil); } - if (cached != de) free(de); - return cached; + cached = (char *)NXHashInsertIfAbsent(DemangleCache, de); } + if (cached != de) free(de); + return cached; } @@ -4810,6 +5085,135 @@ Method class_getInstanceMethod(Class cls, SEL sel) } +/*********************************************************************** +* resolveClassMethod +* Call +resolveClassMethod, looking for a method to be added to class cls. +* cls should be a metaclass. +* Does not check if the method already exists. +**********************************************************************/ +static void resolveClassMethod(Class cls, SEL sel, id inst) +{ + runtimeLock.assertUnlocked(); + assert(cls->isRealized()); + assert(cls->isMetaClass()); + + if (! lookUpImpOrNil(cls, SEL_resolveClassMethod, inst, + NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) + { + // Resolver not implemented. + return; + } + + Class nonmeta; + { + mutex_locker_t lock(runtimeLock); + nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst); + // +initialize path should have realized nonmeta already + if (!nonmeta->isRealized()) { + _objc_fatal("nonmeta class %s (%p) unexpectedly not realized", + nonmeta->nameForLogging(), nonmeta); + } + } + BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend; + bool resolved = msg(nonmeta, SEL_resolveClassMethod, sel); + + // Cache the result (good or bad) so the resolver doesn't fire next time. + // +resolveClassMethod adds to self->ISA() a.k.a. cls + IMP imp = lookUpImpOrNil(cls, sel, inst, + NO/*initialize*/, YES/*cache*/, NO/*resolver*/); + + if (resolved && PrintResolving) { + if (imp) { + _objc_inform("RESOLVE: method %c[%s %s] " + "dynamically resolved to %p", + cls->isMetaClass() ? '+' : '-', + cls->nameForLogging(), sel_getName(sel), imp); + } + else { + // Method resolver didn't add anything? + _objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES" + ", but no new implementation of %c[%s %s] was found", + cls->nameForLogging(), sel_getName(sel), + cls->isMetaClass() ? '+' : '-', + cls->nameForLogging(), sel_getName(sel)); + } + } +} + + +/*********************************************************************** +* resolveInstanceMethod +* Call +resolveInstanceMethod, looking for a method to be added to class cls. +* cls may be a metaclass or a non-meta class. +* Does not check if the method already exists. +**********************************************************************/ +static void resolveInstanceMethod(Class cls, SEL sel, id inst) +{ + runtimeLock.assertUnlocked(); + assert(cls->isRealized()); + + if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, + NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) + { + // Resolver not implemented. + return; + } + + BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend; + bool resolved = msg(cls, SEL_resolveInstanceMethod, sel); + + // Cache the result (good or bad) so the resolver doesn't fire next time. + // +resolveInstanceMethod adds to self a.k.a. cls + IMP imp = lookUpImpOrNil(cls, sel, inst, + NO/*initialize*/, YES/*cache*/, NO/*resolver*/); + + if (resolved && PrintResolving) { + if (imp) { + _objc_inform("RESOLVE: method %c[%s %s] " + "dynamically resolved to %p", + cls->isMetaClass() ? '+' : '-', + cls->nameForLogging(), sel_getName(sel), imp); + } + else { + // Method resolver didn't add anything? + _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES" + ", but no new implementation of %c[%s %s] was found", + cls->nameForLogging(), sel_getName(sel), + cls->isMetaClass() ? '+' : '-', + cls->nameForLogging(), sel_getName(sel)); + } + } +} + + +/*********************************************************************** +* resolveMethod +* Call +resolveClassMethod or +resolveInstanceMethod. +* Returns nothing; any result would be potentially out-of-date already. +* Does not check if the method already exists. +**********************************************************************/ +static void resolveMethod(Class cls, SEL sel, id inst) +{ + runtimeLock.assertUnlocked(); + assert(cls->isRealized()); + + if (! cls->isMetaClass()) { + // try [cls resolveInstanceMethod:sel] + resolveInstanceMethod(cls, sel, inst); + } + else { + // try [nonMetaClass resolveClassMethod:sel] + // and [cls resolveInstanceMethod:sel] + resolveClassMethod(cls, sel, inst); + if (!lookUpImpOrNil(cls, sel, inst, + NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) + { + resolveInstanceMethod(cls, sel, inst); + } + } +} + + /*********************************************************************** * log_and_fill_cache * Log this method call. If the logger permits it, fill the method cache. @@ -4884,20 +5288,21 @@ IMP lookUpImpOrForward(Class cls, SEL sel, id inst, checkIsKnownClass(cls); if (!cls->isRealized()) { - realizeClass(cls); + cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock); + // runtimeLock may have been dropped but is now locked again } - if (initialize && !cls->isInitialized()) { - runtimeLock.unlock(); - _class_initialize (_class_getNonMetaClass(cls, inst)); - runtimeLock.lock(); - // If sel == initialize, _class_initialize will send +initialize and + if (initialize && !cls->isInitialized()) { + cls = initializeAndLeaveLocked(cls, inst, runtimeLock); + // runtimeLock may have been dropped but is now locked again + + // 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 } - + retry: runtimeLock.assertLocked(); @@ -4958,7 +5363,7 @@ IMP lookUpImpOrForward(Class cls, SEL sel, id inst, if (resolver && !triedResolver) { runtimeLock.unlock(); - _class_resolveMethod(cls, sel, inst); + resolveMethod(cls, sel, inst); runtimeLock.lock(); // Don't cache the result; we don't hold the lock so it may have // changed already. Re-do the search from scratch instead. @@ -6016,6 +6421,20 @@ BOOL class_addProtocol(Class cls, Protocol *protocol_gen) * Look up a class by name, and realize it. * Locking: acquires runtimeLock **********************************************************************/ +static BOOL empty_getClass(const char *name, Class *outClass) +{ + *outClass = nil; + return NO; +} + +static ChainedHookFunction GetClassHook{empty_getClass}; + +void objc_setHook_getClass(objc_hook_getClass newValue, + objc_hook_getClass *outOldValue) +{ + GetClassHook.set(newValue, outOldValue); +} + Class look_up_class(const char *name, bool includeUnconnected __attribute__((unused)), @@ -6026,14 +6445,58 @@ bool includeClassHandler __attribute__((unused))) Class result; bool unrealized; { - mutex_locker_t lock(runtimeLock); - result = getClass(name); + runtimeLock.lock(); + result = getClassExceptSomeSwift(name); unrealized = result && !result->isRealized(); + if (unrealized) { + result = realizeClassMaybeSwiftAndUnlock(result, runtimeLock); + // runtimeLock is now unlocked + } else { + runtimeLock.unlock(); + } } - if (unrealized) { - mutex_locker_t lock(runtimeLock); - realizeClass(result); + + if (!result) { + // Ask Swift about its un-instantiated classes. + + // We use thread-local storage to prevent infinite recursion + // if the hook function provokes another lookup of the same name + // (for example, if the hook calls objc_allocateClassPair) + + auto *tls = _objc_fetch_pthread_data(true); + + // Stop if this thread is already looking up this name. + for (unsigned i = 0; i < tls->classNameLookupsUsed; i++) { + if (0 == strcmp(name, tls->classNameLookups[i])) { + return nil; + } + } + + // Save this lookup in tls. + if (tls->classNameLookupsUsed == tls->classNameLookupsAllocated) { + tls->classNameLookupsAllocated = + (tls->classNameLookupsAllocated * 2 ?: 1); + size_t size = tls->classNameLookupsAllocated * + sizeof(tls->classNameLookups[0]); + tls->classNameLookups = (const char **) + realloc(tls->classNameLookups, size); + } + tls->classNameLookups[tls->classNameLookupsUsed++] = name; + + // Call the hook. + Class swiftcls = nil; + if (GetClassHook.get()(name, &swiftcls)) { + assert(swiftcls->isRealized()); + result = swiftcls; + } + + // Erase the name from tls. + unsigned slot = --tls->classNameLookupsUsed; + assert(slot >= 0 && slot < tls->classNameLookupsAllocated); + assert(name == tls->classNameLookups[slot]); + tls->classNameLookups[slot] = nil; } + return result; } @@ -6072,8 +6535,7 @@ bool includeClassHandler __attribute__((unused))) duplicate->bits = original->bits; duplicate->setData(rw); - rw->ro = (class_ro_t *) - memdup(original->data()->ro, sizeof(*original->data()->ro)); + rw->ro = original->data()->ro->duplicate(); *(char **)&rw->ro->name = strdupIfMutable(name); rw->methods = original->data()->methods.duplicate(); @@ -6142,6 +6604,8 @@ static void objc_initializeClassPair_internal(Class superclass, const char *name meta_ro_w->flags |= RO_ROOT; } if (superclass) { + uint32_t flagsToCopy = RW_FORBIDS_ASSOCIATED_OBJECTS; + cls->data()->flags |= superclass->data()->flags & flagsToCopy; cls_ro_w->instanceStart = superclass->unalignedInstanceSize(); meta_ro_w->instanceStart = superclass->ISA()->unalignedInstanceSize(); cls->setInstanceSize(cls_ro_w->instanceStart); @@ -6215,11 +6679,16 @@ static void objc_initializeClassPair_internal(Class superclass, const char *name **********************************************************************/ Class objc_initializeClassPair(Class superclass, const char *name, Class cls, Class meta) { + // Fail if the class name is in use. + if (look_up_class(name, NO, NO)) return nil; + mutex_locker_t lock(runtimeLock); // Fail if the class name is in use. // Fail if the superclass isn't kosher. - if (getClass(name) || !verifySuperclass(superclass, true/*rootOK*/)) { + if (getClassExceptSomeSwift(name) || + !verifySuperclass(superclass, true/*rootOK*/)) + { return nil; } @@ -6239,11 +6708,16 @@ Class objc_allocateClassPair(Class superclass, const char *name, { Class cls, meta; + // Fail if the class name is in use. + if (look_up_class(name, NO, NO)) return nil; + mutex_locker_t lock(runtimeLock); // Fail if the class name is in use. // Fail if the superclass isn't kosher. - if (getClass(name) || !verifySuperclass(superclass, true/*rootOK*/)) { + if (getClassExceptSomeSwift(name) || + !verifySuperclass(superclass, true/*rootOK*/)) + { return nil; } @@ -6327,7 +6801,11 @@ Class objc_readClassPair(Class bits, const struct objc_image_info *info) _objc_fatal("objc_readClassPair for class %s changed %p to %p", cls->nameForLogging(), bits, cls); } - realizeClass(cls); + + // The only client of this function is old Swift. + // Stable Swift won't use it. + // fixme once Swift in the OS settles we can assert(!cls->isSwiftStable()). + cls = realizeClassWithoutSwift(cls); return cls; } diff --git a/runtime/objc-runtime-old.h b/runtime/objc-runtime-old.h index 664cf4ba..b367c099 100644 --- a/runtime/objc-runtime-old.h +++ b/runtime/objc-runtime-old.h @@ -248,6 +248,11 @@ struct objc_class : objc_object { void setHasDefaultAWZ() { } void printCustomAWZ(bool) { } + bool forbidsAssociatedObjects() { + // Old runtime doesn't support forbidding associated objects. + return false; + } + bool instancesHaveAssociatedObjects() { return info & CLS_INSTANCES_HAVE_ASSOCIATED_OBJECTS; } diff --git a/runtime/objc-runtime.mm b/runtime/objc-runtime.mm index 4d94b649..176d3414 100644 --- a/runtime/objc-runtime.mm +++ b/runtime/objc-runtime.mm @@ -131,6 +131,22 @@ id objc_noop_imp(id self, SEL _cmd __unused) { } +/*********************************************************************** +* _objc_isDebugBuild. Defined in debug builds only. +* Some test code looks for the presence of this symbol. +**********************************************************************/ +#if DEBUG != OBJC_IS_DEBUG_BUILD +#error mismatch in debug-ness macros +// DEBUG is used in our code. OBJC_IS_DEBUG_BUILD is used in the +// header declaration of _objc_isDebugBuild() because that header +// is visible to other clients who might have their own DEBUG macro. +#endif + +#if OBJC_IS_DEBUG_BUILD +void _objc_isDebugBuild(void) { } +#endif + + /*********************************************************************** * objc_getClass. Return the id of the named class. If the class does * not exist, call _objc_classLoader and then objc_classHandler, either of @@ -420,6 +436,7 @@ void _objc_pthread_destroyspecific(void *arg) free(data->printableNames[i]); } } + free(data->classNameLookups); // add further cleanup here... diff --git a/runtime/runtime.h b/runtime/runtime.h index 2e99cf0a..1542d034 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -1728,6 +1728,65 @@ OBJC_EXPORT void objc_setHook_getImageName(objc_hook_getImageName _Nonnull newVa objc_hook_getImageName _Nullable * _Nonnull outOldValue) OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0); +/** + * Function type for a hook that assists objc_getClass() and related functions. + * + * @param name The class name to look up. + * @param outClass On return, the result of the class lookup. + * @return YES if a class with this name was found, NO otherwise. + * + * @see objc_getClass + * @see objc_setHook_getClass + */ +typedef BOOL (*objc_hook_getClass)(const char * _Nonnull name, Class _Nullable * _Nonnull outClass); + +/** + * Install a hook for objc_getClass() and related functions. + * + * @param newValue The hook function to install. + * @param outOldValue The address of a function pointer variable. On return, + * the old hook function is stored in the variable. + * + * @note The store to *outOldValue is thread-safe: the variable will be + * updated before objc_getClass() calls your new hook to read it, + * even if your new hook is called from another thread before this + * setter completes. + * @note Your hook should call the previous hook for class names + * that you do not recognize. + * + * @see objc_getClass + * @see objc_hook_getClass + */ +#if !(TARGET_OS_OSX && __i386__) +#define OBJC_GETCLASSHOOK_DEFINED 1 +OBJC_EXPORT void objc_setHook_getClass(objc_hook_getClass _Nonnull newValue, + objc_hook_getClass _Nullable * _Nonnull outOldValue) + OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0); +// rdar://44986431 fixme correct availability for _objc_realizeClassFromSwift +#endif + +/** + * Callback from Objective-C to Swift to perform Swift class initialization. + */ +#if !(TARGET_OS_OSX && __i386__) +typedef Class _Nullable +(*_objc_swiftMetadataInitializer)(Class _Nonnull cls, void * _Nullable arg); +#endif + + +/** + * Perform Objective-C initialization of a Swift class. + * Do not call this function. It is provided for the Swift runtime's use only + * and will change without notice or mercy. + */ +#if !(TARGET_OS_OSX && __i386__) +#define OBJC_REALIZECLASSFROMSWIFT_DEFINED 1 +OBJC_EXPORT Class _Nullable +_objc_realizeClassFromSwift(Class _Nullable cls, void * _Nullable previously) + OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0); +// rdar://44986431 fixme correct availability for _objc_realizeClassFromSwift +#endif + #define _C_ID '@' #define _C_CLASS '#' diff --git a/test/ARRBase.h b/test/ARCBase.h similarity index 66% rename from test/ARRBase.h rename to test/ARCBase.h index 81b8fed4..55722cf0 100644 --- a/test/ARRBase.h +++ b/test/ARCBase.h @@ -1,6 +1,6 @@ // -// ARRBase.h -// TestARRLayouts +// ARCBase.h +// TestARCLayouts // // Created by Patrick Beard on 3/8/11. // Copyright 2011 __MyCompanyName__. All rights reserved. @@ -8,7 +8,12 @@ #import -@interface ARRBase : NSObject +@interface ARCMisalign : NSObject { + char misalign1; +} +@end + +@interface ARCBase : ARCMisalign @property long number; @property(retain) id object; @property void *pointer; diff --git a/test/ARCBase.m b/test/ARCBase.m new file mode 100644 index 00000000..37f91c51 --- /dev/null +++ b/test/ARCBase.m @@ -0,0 +1,30 @@ +// +// ARCBase.m +// TestARCLayouts +// +// Created by Patrick Beard on 3/8/11. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// + +#import "ARCBase.h" + +// ARCMisalign->misalign1 and ARCBase->misalign2 together cause +// ARCBase's instanceStart to be misaligned, which exercises handling +// of storage that is not represented in the class's ivar layout bitmaps. + +@implementation ARCMisalign +@end + +@interface ARCBase () { +@private + char misalign2; + long number; + id object; + void *pointer; + __weak id delegate; +} +@end + +@implementation ARCBase +@synthesize number, object, pointer, delegate; +@end diff --git a/test/ARCLayouts.m b/test/ARCLayouts.m new file mode 100644 index 00000000..516c8376 --- /dev/null +++ b/test/ARCLayouts.m @@ -0,0 +1,217 @@ +// Note that test ARCLayoutsWithoutWeak uses the same files +// with different build options. +/* +TEST_CONFIG MEM=arc +TEST_BUILD + mkdir -p $T{OBJDIR} + $C{COMPILE_NOLINK_NOMEM} -c $DIR/MRCBase.m -o $T{OBJDIR}/MRCBase.o + $C{COMPILE_NOLINK_NOMEM} -c $DIR/MRCARC.m -o $T{OBJDIR}/MRCARC.o + $C{COMPILE_NOLINK} -c $DIR/ARCBase.m -o $T{OBJDIR}/ARCBase.o + $C{COMPILE_NOLINK} -c $DIR/ARCMRC.m -o $T{OBJDIR}/ARCMRC.o + $C{COMPILE} '-DNAME=\"ARCLayouts.m\"' -fobjc-arc $DIR/ARCLayouts.m -x none $T{OBJDIR}/MRCBase.o $T{OBJDIR}/MRCARC.o $T{OBJDIR}/ARCBase.o $T{OBJDIR}/ARCMRC.o -framework Foundation -o ARCLayouts.exe +END +*/ + +#include "test.h" +#import +#import +#import + +#import "ARCMRC.h" +#import "MRCARC.h" + +@interface NSObject (Layouts) ++ (const char *)strongLayout; ++ (const char *)weakLayout; +@end + +void printlayout(const char *name, const uint8_t *layout) +{ + if (!testverbose()) return; + + testprintf("%s: ", name); + + // these use fprintf() to avoid repeated VERBOSE: in the middle of the line + if (!layout) { + fprintf(stderr, "NULL\n"); + return; + } + + const uint8_t *c; + for (c = layout; *c; c++) { + fprintf(stderr, "%02x ", *c); + } + + fprintf(stderr, "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 + +void checkMM(Class cls, const char *ivarName, + objc_ivar_memory_management_t mmExpected) +{ + Ivar ivar = [cls instanceVariable:ivarName]; + objc_ivar_memory_management_t mm = _class_getIvarMemoryManagement(cls,ivar); + testprintf("%s->%s want %d, got %d\n", + class_getName(cls), ivarName, mmExpected, mm); + testassert(mmExpected == mm); +} + +int main (int argc __unused, const char * argv[] __unused) { + + testprintf("ARCMRC\n"); + testassert(strcmp([ARCMRC strongLayout], "\x01") == 0); + testassert([ARCMRC weakLayout] == NULL); + + + // Verify that ARCBase->misalign and MRCBase->alignment did their thing. + testassert(ivar_getOffset(class_getInstanceVariable([ARCBase class], "misalign2")) & 1); + testassert(ivar_getOffset(class_getInstanceVariable([MRCBase class], "alignment")) > (ptrdiff_t)sizeof(void*)); + + testprintf("ARCMisalign\n"); + testassert([ARCMisalign strongLayout] == NULL); + testassert([ARCMisalign weakLayout] == NULL); + + testprintf("ARCBase\n"); + if (strcmp([ARCBase strongLayout], "\x11\x30") == 0) { + testwarn("1130 layout is a compiler flaw but doesn't fail"); + } else { + testassert(strcmp([ARCBase strongLayout], "\x11") == 0); + } + testassert(strcmp([ARCBase weakLayout], "\x31") == 0); + + testprintf("MRCARC\n"); + testassert([MRCARC strongLayout] == NULL); + testassert([MRCARC weakLayout] == NULL); + + testprintf("MRCBase\n"); + // MRC marks __weak only. + testassert([MRCBase strongLayout] == NULL); + if (supportsMRCWeak) { + testassert(strcmp([MRCBase weakLayout], "\x71") == 0); + } else { + testassert([MRCBase weakLayout] == nil); + } + + // now check consistency between dynamic accessors and KVC, etc. + ARCMRC *am = [ARCMRC new]; + MRCARC *ma = [MRCARC new]; + + NSString *amValue = [[NSString alloc] initWithFormat:@"%s %p", "ARCMRC", am]; + NSString *amValue2 = [[NSString alloc] initWithFormat:@"%s %p", "ARCMRC", am]; + NSString *maValue = [[NSString alloc] initWithFormat:@"%s %p", "MRCARC", ma]; + NSString *maValue2 = [[NSString alloc] initWithFormat:@"%s %p", "MRCARC", ma]; + + am.number = M_PI; + + object_setIvar(am, [ARCMRC instanceVariable:"object"], amValue); + testassert(CFGetRetainCount((__bridge CFTypeRef)amValue) == 1); + testassert(am.object == amValue); + + object_setIvarWithStrongDefault(am, [ARCMRC instanceVariable:"object"], amValue2); + testassert(CFGetRetainCount((__bridge CFTypeRef)amValue2) == 2); + testassert(am.object == amValue2); + + am.pointer = @selector(ARCMRC); + + object_setIvar(am, [ARCMRC instanceVariable:"delegate"], ma); + testassert(CFGetRetainCount((__bridge CFTypeRef)ma) == 1); + testassert(am.delegate == ma); + + object_setIvarWithStrongDefault(am, [ARCMRC instanceVariable:"delegate"], ma); + testassert(CFGetRetainCount((__bridge CFTypeRef)ma) == 1); + testassert(am.delegate == ma); + + + ma.number = M_E; + + object_setIvar(ma, [MRCARC instanceVariable:"object"], maValue); + testassert(CFGetRetainCount((__bridge CFTypeRef)maValue) == 2); + @autoreleasepool { + testassert(ma.object == maValue); + } + + object_setIvarWithStrongDefault(ma, [MRCARC instanceVariable:"object"], maValue2); + testassert(CFGetRetainCount((__bridge CFTypeRef)maValue2) == 2); + @autoreleasepool { + testassert(ma.object == maValue2); + } + + ma.pointer = @selector(MRCARC); + + ma.delegate = am; + object_setIvar(ma, [MRCARC instanceVariable:"delegate"], am); + testassert(CFGetRetainCount((__bridge CFTypeRef)am) == 1); + @autoreleasepool { + testassert(ma.delegate == am); + } + + object_setIvarWithStrongDefault(ma, [MRCARC instanceVariable:"delegate"], am); + testassert(CFGetRetainCount((__bridge CFTypeRef)am) == 1); + @autoreleasepool { + testassert(ma.delegate == am); + } + + + // Verify that object_copy() handles ARC variables correctly. + + MRCARC *ma2 = docopy(ma); + testassert(ma2); + testassert(ma2 != ma); + testassert(CFGetRetainCount((__bridge CFTypeRef)maValue2) == 3); + testassert(CFGetRetainCount((__bridge CFTypeRef)am) == 1); + testassert(ma2.number == ma.number); + testassert(ma2.object == ma.object); + @autoreleasepool { + testassert(ma2.delegate == ma.delegate); + } + testassert(ma2.pointer == ma.pointer); + + + // Test _class_getIvarMemoryManagement() SPI + + objc_ivar_memory_management_t memoryMRCWeak = + supportsMRCWeak ? objc_ivar_memoryWeak : objc_ivar_memoryUnknown; + checkMM([ARCMRC class], "number", objc_ivar_memoryUnknown); + checkMM([ARCMRC class], "object", objc_ivar_memoryUnknown); + checkMM([ARCMRC class], "pointer", objc_ivar_memoryUnknown); + checkMM([ARCMRC class], "delegate", memoryMRCWeak); + checkMM([ARCMRC class], "dataSource", objc_ivar_memoryStrong); + + checkMM([MRCARC class], "number", objc_ivar_memoryUnretained); + checkMM([MRCARC class], "object", objc_ivar_memoryStrong); + checkMM([MRCARC class], "pointer", objc_ivar_memoryUnretained); + checkMM([MRCARC class], "delegate", objc_ivar_memoryWeak); + checkMM([MRCARC class], "dataSource", objc_ivar_memoryUnknown); + + checkMM([ARCBase class], "number", objc_ivar_memoryUnretained); + checkMM([ARCBase class], "object", objc_ivar_memoryStrong); + checkMM([ARCBase class], "pointer", objc_ivar_memoryUnretained); + checkMM([ARCBase class], "delegate", objc_ivar_memoryWeak); + + checkMM([MRCBase class], "number", objc_ivar_memoryUnknown); + checkMM([MRCBase class], "object", objc_ivar_memoryUnknown); + checkMM([MRCBase class], "pointer", objc_ivar_memoryUnknown); + checkMM([MRCBase class], "delegate", memoryMRCWeak); + + succeed(NAME); + return 0; +} diff --git a/test/ARCLayoutsWithoutWeak.m b/test/ARCLayoutsWithoutWeak.m new file mode 100644 index 00000000..6e81ac65 --- /dev/null +++ b/test/ARCLayoutsWithoutWeak.m @@ -0,0 +1,12 @@ +// Same as test ARCLayouts but with MRC __weak support disabled. +/* +TEST_CONFIG MEM=arc +TEST_BUILD + mkdir -p $T{OBJDIR} + $C{COMPILE_NOLINK_NOMEM} -c $DIR/MRCBase.m -o $T{OBJDIR}/MRCBase.o -fno-objc-weak + $C{COMPILE_NOLINK_NOMEM} -c $DIR/MRCARC.m -o $T{OBJDIR}/MRCARC.o -fno-objc-weak + $C{COMPILE_NOLINK} -c $DIR/ARCBase.m -o $T{OBJDIR}/ARCBase.o + $C{COMPILE_NOLINK} -c $DIR/ARCMRC.m -o $T{OBJDIR}/ARCMRC.o + $C{COMPILE} '-DNAME=\"ARCLayoutsWithoutWeak.m\"' -fobjc-arc $DIR/ARCLayouts.m -x none $T{OBJDIR}/MRCBase.o $T{OBJDIR}/MRCARC.o $T{OBJDIR}/ARCBase.o $T{OBJDIR}/ARCMRC.o -framework Foundation -o ARCLayoutsWithoutWeak.exe +END +*/ diff --git a/test/MRRARR.h b/test/ARCMRC.h similarity index 64% rename from test/MRRARR.h rename to test/ARCMRC.h index 275ae2f0..a37422ef 100644 --- a/test/MRRARR.h +++ b/test/ARCMRC.h @@ -1,13 +1,13 @@ // -// MRRARR.h -// TestARRLayouts +// ARCMRC.h +// TestARCLayouts // // Created by Patrick Beard on 3/8/11. // Copyright 2011 __MyCompanyName__. All rights reserved. // -#import "ARRBase.h" +#import "MRCBase.h" -@interface MRRARR : ARRBase +@interface ARCMRC : MRCBase @property(retain) id dataSource; @end diff --git a/test/ARCMRC.m b/test/ARCMRC.m new file mode 100644 index 00000000..eed9fce1 --- /dev/null +++ b/test/ARCMRC.m @@ -0,0 +1,11 @@ +// +// ARCMRC.m +// + +#import "ARCMRC.h" + +@implementation ARCMRC + +@synthesize dataSource; + +@end diff --git a/test/ARRBase.m b/test/ARRBase.m deleted file mode 100644 index 737f1c6d..00000000 --- a/test/ARRBase.m +++ /dev/null @@ -1,24 +0,0 @@ -// -// 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 - long 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 deleted file mode 100644 index 5db5213e..00000000 --- a/test/ARRLayouts.m +++ /dev/null @@ -1,99 +0,0 @@ -/* -TEST_CONFIG MEM=arc CC=clang -TEST_BUILD - $C{COMPILE_NOLINK_NOMEM} -c $DIR/MRRBase.m - $C{COMPILE_NOLINK_NOMEM} -c $DIR/MRRARR.m - $C{COMPILE_NOLINK} -c $DIR/ARRBase.m - $C{COMPILE_NOLINK} -c $DIR/ARRMRR.m - $C{COMPILE} -fobjc-arc $DIR/ARRLayouts.m -x none MRRBase.o MRRARR.o ARRBase.o ARRMRR.o -framework Foundation -o ARRLayouts.out -END -*/ - -#include "test.h" -#import -#import -#import - -#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) -{ - if (! getenv("VERBOSE")) return; - - testprintf("%s: ", name); - - if (!layout) { - fprintf(stderr, "NULL\n"); - return; - } - - const uint8_t *c; - for (c = layout; *c; c++) { - fprintf(stderr, "%02x ", *c); - } - - fprintf(stderr, "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. - testassert(strcmp([ARRBase strongLayout], "\x11\x20") == 0); - testassert(strcmp([ARRBase weakLayout], "\x31") == 0); - testassert([MRRBase strongLayout] == NULL); - testassert([MRRBase weakLayout] == NULL); - testassert(strcmp([ARRMRR strongLayout], "\x01") == 0); - testassert([ARRMRR weakLayout] == NULL); - testassert([MRRARR strongLayout] == NULL); - testassert([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); - testassert(CFGetRetainCount(objc_unretainedPointer(am_description)) == 1); - am.pointer = @selector(ARRMRR); - object_setIvar(am, [ARRMRR instanceVariable:"delegate"], ma); - testassert(CFGetRetainCount(objc_unretainedPointer(ma)) == 1); - - ma.number = M_E; - object_setIvar(ma, [MRRARR instanceVariable:"object"], ma_description); - testassert(CFGetRetainCount(objc_unretainedPointer(ma_description)) == 2); - ma.pointer = @selector(MRRARR); - ma.delegate = am; - object_setIvar(ma, [MRRARR instanceVariable:"delegate"], am); - testassert(CFGetRetainCount(objc_unretainedPointer(am)) == 1); - - succeed(__FILE__); - return 0; -} diff --git a/test/ARRMRR.m b/test/ARRMRR.m deleted file mode 100644 index ce0b7a66..00000000 --- a/test/ARRMRR.m +++ /dev/null @@ -1,11 +0,0 @@ -// -// ARRMRR.m -// - -#import "ARRMRR.h" - -@implementation ARRMRR - -@synthesize dataSource; - -@end diff --git a/test/ARRMRR.h b/test/MRCARC.h similarity index 64% rename from test/ARRMRR.h rename to test/MRCARC.h index 8029eed4..9443ea98 100644 --- a/test/ARRMRR.h +++ b/test/MRCARC.h @@ -1,13 +1,13 @@ // -// ARRMRR.h -// TestARRLayouts +// MRCARC.h +// TestARCLayouts // // Created by Patrick Beard on 3/8/11. // Copyright 2011 __MyCompanyName__. All rights reserved. // -#import "MRRBase.h" +#import "ARCBase.h" -@interface ARRMRR : MRRBase +@interface MRCARC : ARCBase @property(retain) id dataSource; @end diff --git a/test/MRCARC.m b/test/MRCARC.m new file mode 100644 index 00000000..c2882f33 --- /dev/null +++ b/test/MRCARC.m @@ -0,0 +1,11 @@ +// +// MRCARC.m +// + +#import "MRCARC.h" + +@implementation MRCARC + +@synthesize dataSource; + +@end diff --git a/test/MRCBase.h b/test/MRCBase.h new file mode 100644 index 00000000..050f43f2 --- /dev/null +++ b/test/MRCBase.h @@ -0,0 +1,28 @@ +// +// MRCBase.h +// TestARCLayouts +// +// Created by Patrick Beard on 3/8/11. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// + +#import + +// YES if MRC compiler supports ARC-style weak +extern bool supportsMRCWeak; + +#if __LP64__ +#define DOUBLEWORD_ALIGNED __attribute__((aligned(16))) +#else +#define DOUBLEWORD_ALIGNED __attribute__((aligned(8))) +#endif + +@interface MRCBase : NSObject +@property double number; +@property(retain) id object; +@property void *pointer; +@property(weak) __weak id delegate; +@end + +// Call object_copy from MRC. +extern id __attribute__((ns_returns_retained)) docopy(id obj); diff --git a/test/MRCBase.m b/test/MRCBase.m new file mode 100644 index 00000000..e95cc2ea --- /dev/null +++ b/test/MRCBase.m @@ -0,0 +1,46 @@ +// +// MRCBase.m +// TestARCLayouts +// +// Created by Patrick Beard on 3/8/11. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// + +#include "MRCBase.h" +#include "test.h" + +// MRCBase->alignment ensures that there is a gap between the end of +// NSObject's ivars and the start of MRCBase's ivars, which exercises +// handling of storage that is not represented in any class's ivar +// layout bitmaps. + +#if __has_feature(objc_arc_weak) +bool supportsMRCWeak = true; +#else +bool supportsMRCWeak = false; +#endif + +@interface MRCBase () { +@private + double DOUBLEWORD_ALIGNED alignment; + uintptr_t pad[3]; // historically this made OBJC2 layout bitmaps match OBJC1 + double number; + id object; + void *pointer; +#if __has_feature(objc_arc_weak) + __weak +#endif + id delegate; +} +@end + +@implementation MRCBase +@synthesize number, object, pointer, delegate; +@end + +// Call object_copy from MRC. +extern id __attribute__((ns_returns_retained)) +docopy(id obj) +{ + return object_copy(obj, 0); +} diff --git a/test/MRRARR.m b/test/MRRARR.m deleted file mode 100644 index fa32a34d..00000000 --- a/test/MRRARR.m +++ /dev/null @@ -1,11 +0,0 @@ -// -// MRRARR.m -// - -#import "MRRARR.h" - -@implementation MRRARR - -@synthesize dataSource; - -@end diff --git a/test/MRRBase.h b/test/MRRBase.h deleted file mode 100644 index c339f612..00000000 --- a/test/MRRBase.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// MRRBase.h -// TestARRLayouts -// -// Created by Patrick Beard on 3/8/11. -// Copyright 2011 __MyCompanyName__. All rights reserved. -// - -#import - -@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 deleted file mode 100644 index 5720dcc5..00000000 --- a/test/MRRBase.m +++ /dev/null @@ -1,24 +0,0 @@ -// -// 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 diff --git a/test/Makefile b/test/Makefile deleted file mode 100644 index e5852e8d..00000000 --- a/test/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# quick test -all: - perl test.pl $(MAKEFLAGS) - -# default-arch but otherwise comprehensive test for buildbot -buildbot: - perl test.pl $(MAKEFLAGS) MEM=mrc,arc,gc CC=clang LANGUAGE=objc,objc++ - -# comprehensive tests -mac macos macosx: - perl test.pl $(MAKEFLAGS) ARCH=x86_64,i386 MEM=mrc,arc,gc CC=clang LANGUAGE=objc,objc++ - -iphonesimulator: - perl test.pl $(MAKEFLAGS) ARCH=i386 SDK=iphonesimulator MEM=mrc,arc CC=clang LANGUAGE=objc,objc++ - -iphoneos: - perl test.pl $(MAKEFLAGS) ARCH=armv6,armv7 SDK=iphoneos MEM=mrc,arc CC=clang LANGUAGE=objc,objc++ - -clean: - @ perl test.pl clean diff --git a/test/accessors2.m b/test/accessors2.m index 3c21b9f1..4b3db6d4 100644 --- a/test/accessors2.m +++ b/test/accessors2.m @@ -1,4 +1,4 @@ -// TEST_CONFIG MEM=mrc,arc +// TEST_CONFIG #include "test.h" #include diff --git a/test/addMethod.m b/test/addMethod.m index 0b1f96c6..8e693d9b 100644 --- a/test/addMethod.m +++ b/test/addMethod.m @@ -6,7 +6,8 @@ @interface Super : TestRoot @end @implementation Super --(int)superMethod { return 0; } +-(int)superMethod { return 0; } +-(int)superMethod2 { return 0; } -(int)bothMethod { return 0; } @end @@ -25,9 +26,12 @@ -(int)bothMethod { return 0; } id fn(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) { return nil; } +#define TWICE(x) x; x + int main() { IMP superMethodFromSuper = class_getMethodImplementation([Super class], @selector(superMethod)); + IMP superMethod2FromSuper = class_getMethodImplementation([Super class], @selector(superMethod2)); IMP bothMethodFromSuper = class_getMethodImplementation([Super class], @selector(bothMethod)); IMP subMethodFromSub = class_getMethodImplementation([Sub class], @selector(subMethod)); IMP bothMethodFromSub = class_getMethodImplementation([Sub class], @selector(bothMethod)); @@ -35,6 +39,7 @@ int main() IMP bothMethodFromSub2 = class_getMethodImplementation([Sub2 class], @selector(bothMethod)); testassert(superMethodFromSuper); + testassert(superMethod2FromSuper); testassert(bothMethodFromSuper); testassert(subMethodFromSub); testassert(bothMethodFromSub); @@ -44,59 +49,71 @@ int main() BOOL ok; IMP imp; + // Each method lookup below is performed twice, with the intent + // that at least one of them will be a cache lookup. + // class_addMethod doesn't replace existing implementations ok = class_addMethod([Super class], @selector(superMethod), (IMP)fn, NULL); testassert(!ok); - testassert(class_getMethodImplementation([Super class], @selector(superMethod)) == superMethodFromSuper); + TWICE(testassert(class_getMethodImplementation([Super class], @selector(superMethod)) == superMethodFromSuper)); // class_addMethod does override superclass implementations ok = class_addMethod([Sub class], @selector(superMethod), (IMP)fn, NULL); testassert(ok); - testassert(class_getMethodImplementation([Sub class], @selector(superMethod)) == (IMP)fn); + TWICE(testassert(class_getMethodImplementation([Sub class], @selector(superMethod)) == (IMP)fn)); // class_addMethod does add root implementations ok = class_addMethod([Super class], @selector(superMethodNew2), (IMP)fn, NULL); testassert(ok); - testassert(class_getMethodImplementation([Super class], @selector(superMethodNew2)) == (IMP)fn); - testassert(class_getMethodImplementation([Sub class], @selector(superMethodNew2)) == (IMP)fn); - + TWICE(testassert(class_getMethodImplementation([Super class], @selector(superMethodNew2)) == (IMP)fn)); + TWICE(testassert(class_getMethodImplementation([Sub class], @selector(superMethodNew2)) == (IMP)fn)); + + // bincompat: some apps call class_addMethod() and class_replaceMethod() with a nil IMP + // This has the effect of covering the superclass implementation. + // fixme change this, possibly behind a linked-on-or-after check + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wnonnull" + ok = class_addMethod([Sub class], @selector(superMethod2), nil, NULL); + #pragma clang diagnostic pop + testassert(ok); + TWICE(testassert(class_getMethodImplementation([Sub class], @selector(superMethod2)) == (IMP)_objc_msgForward)); // class_replaceMethod does add new implementations, // returning NULL if super has an implementation imp = class_replaceMethod([Sub2 class], @selector(superMethod), (IMP)fn, NULL); testassert(imp == NULL); - testassert(class_getMethodImplementation([Sub2 class], @selector(superMethod)) == (IMP)fn); + TWICE(testassert(class_getMethodImplementation([Sub2 class], @selector(superMethod)) == (IMP)fn)); // class_replaceMethod does add new implementations, // returning NULL if super has no implementation imp = class_replaceMethod([Sub2 class], @selector(subMethodNew), (IMP)fn, NULL); testassert(imp == NULL); - testassert(class_getMethodImplementation([Sub2 class], @selector(subMethodNew)) == (IMP)fn); + TWICE(testassert(class_getMethodImplementation([Sub2 class], @selector(subMethodNew)) == (IMP)fn)); // class_replaceMethod does add new implemetations // returning NULL if there is no super class imp = class_replaceMethod([Super class], @selector(superMethodNew), (IMP)fn, NULL); testassert(imp == NULL); - testassert(class_getMethodImplementation([Super class], @selector(superMethodNew)) == (IMP)fn); + TWICE(testassert(class_getMethodImplementation([Super class], @selector(superMethodNew)) == (IMP)fn)); // class_replaceMethod does replace existing implementations, // returning existing implementation (regardless of super) imp = class_replaceMethod([Sub2 class], @selector(subMethod), (IMP)fn, NULL); testassert(imp == subMethodFromSub2); - testassert(class_getMethodImplementation([Sub2 class], @selector(subMethod)) == (IMP)fn); + TWICE(testassert(class_getMethodImplementation([Sub2 class], @selector(subMethod)) == (IMP)fn)); // class_replaceMethod does replace existing implemetations, // returning existing implementation (regardless of super) imp = class_replaceMethod([Sub2 class], @selector(bothMethod), (IMP)fn, NULL); testassert(imp == bothMethodFromSub2); - testassert(class_getMethodImplementation([Sub2 class], @selector(bothMethod)) == (IMP)fn); + TWICE(testassert(class_getMethodImplementation([Sub2 class], @selector(bothMethod)) == (IMP)fn)); // class_replaceMethod does replace existing implemetations, // returning existing implementation (regardless of super) imp = class_replaceMethod([Super class], @selector(superMethod), (IMP)fn, NULL); testassert(imp == superMethodFromSuper); - testassert(class_getMethodImplementation([Super class], @selector(superMethod)) == (IMP)fn); + TWICE(testassert(class_getMethodImplementation([Super class], @selector(superMethod)) == (IMP)fn)); // fixme actually try calling them diff --git a/test/addMethods.m b/test/addMethods.m new file mode 100644 index 00000000..953ada41 --- /dev/null +++ b/test/addMethods.m @@ -0,0 +1,323 @@ +// TEST_CONFIG + +#include "test.h" +#include "testroot.i" +#include +#include + +// Macros for array construction. +// ten IMPs +#define IMPS10 fn0, fn1, fn2, fn3, fn4, fn5, fn6, fn7, fn8, fn9 +// ten method types +#define TYPES10 "", "", "", "", "", "", "", "", "", "" +// ten selectors of the form name0..name9 +#define SELS10(name) \ + @selector(name##0), @selector(name##1), @selector(name##2), \ + @selector(name##3), @selector(name##4), @selector(name##5), \ + @selector(name##6), @selector(name##7), @selector(name##8), \ + @selector(name##9) + + +@interface Super : TestRoot @end +@implementation Super +-(int)superMethod0 { return 0; } +-(int)superMethod1 { return 0; } +-(int)superMethod2 { return 0; } +-(int)superMethod3 { return 0; } +-(int)superMethod4 { return 0; } +-(int)superMethod5 { return 0; } +-(int)superMethod6 { return 0; } +-(int)superMethod7 { return 0; } +-(int)superMethod8 { return 0; } +-(int)superMethod9 { return 0; } +-(int)bothMethod0 { return 0; } +-(int)bothMethod1 { return 0; } +-(int)bothMethod2 { return 0; } +-(int)bothMethod3 { return 0; } +-(int)bothMethod4 { return 0; } +-(int)bothMethod5 { return 0; } +-(int)bothMethod6 { return 0; } +-(int)bothMethod7 { return 0; } +-(int)bothMethod8 { return 0; } +-(int)bothMethod9 { return 0; } +@end + +@interface Sub : Super @end +@implementation Sub +-(int)subMethod0 { return 0; } +-(int)subMethod1 { return 0; } +-(int)subMethod2 { return 0; } +-(int)subMethod3 { return 0; } +-(int)subMethod4 { return 0; } +-(int)subMethod5 { return 0; } +-(int)subMethod6 { return 0; } +-(int)subMethod7 { return 0; } +-(int)subMethod8 { return 0; } +-(int)subMethod9 { return 0; } +-(int)bothMethod0 { return 0; } +-(int)bothMethod1 { return 0; } +-(int)bothMethod2 { return 0; } +-(int)bothMethod3 { return 0; } +-(int)bothMethod4 { return 0; } +-(int)bothMethod5 { return 0; } +-(int)bothMethod6 { return 0; } +-(int)bothMethod7 { return 0; } +-(int)bothMethod8 { return 0; } +-(int)bothMethod9 { return 0; } +@end + +@interface Sub2 : Super @end +@implementation Sub2 +-(int)subMethod0 { return 0; } +-(int)subMethod1 { return 0; } +-(int)subMethod2 { return 0; } +-(int)subMethod3 { return 0; } +-(int)subMethod4 { return 0; } +-(int)subMethod5 { return 0; } +-(int)subMethod6 { return 0; } +-(int)subMethod7 { return 0; } +-(int)subMethod8 { return 0; } +-(int)subMethod9 { return 0; } +-(int)bothMethod0 { return 0; } +-(int)bothMethod1 { return 0; } +-(int)bothMethod2 { return 0; } +-(int)bothMethod3 { return 0; } +-(int)bothMethod4 { return 0; } +-(int)bothMethod5 { return 0; } +-(int)bothMethod6 { return 0; } +-(int)bothMethod7 { return 0; } +-(int)bothMethod8 { return 0; } +-(int)bothMethod9 { return 0; } +@end + + +id fn0(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) { + return nil; +} +id fn1(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) { + return nil; +} +id fn2(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) { + return nil; +} +id fn3(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) { + return nil; +} +id fn4(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) { + return nil; +} +id fn5(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) { + return nil; +} +id fn6(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) { + return nil; +} +id fn7(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) { + return nil; +} +id fn8(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) { + return nil; +} +id fn9(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) { + return nil; +} + +void testBulkMemoryOnce(void) +{ + Class c = objc_allocateClassPair([TestRoot class], "c", 0); + objc_registerClassPair(c); + + SEL sels[10] = { + SELS10(method) + }; + IMP imps[10] = { + IMPS10 + }; + const char *types[10] = { + TYPES10 + }; + + uint32_t failureCount = 0; + SEL *failed; + + // Test all successes. + failed = class_addMethodsBulk(c, sels, imps, types, 4, &failureCount); + testassert(failed == NULL); + testassert(failureCount == 0); + + // Test mixed success and failure (this overlaps the previous one, so there + // will be one of each). + failed = class_addMethodsBulk(c, sels + 3, imps + 3, types + 3, 2, + &failureCount); + testassert(failed != NULL); + testassert(failureCount == 1); + testassert(failed[0] == sels[3]); + free(failed); + + // Test total failure. + failed = class_addMethodsBulk(c, sels, imps, types, 5, &failureCount); + testassert(failed != NULL); + testassert(failureCount == 5); + for(int i = 0; i < 5; i++) { + testassert(failed[i] == sels[i]); + } + free(failed); + + class_replaceMethodsBulk(c, sels, imps, types, 10); + + for(int i = 0; i < 10; i++) { + testassert(class_getMethodImplementation(c, sels[i]) == imps[i]); + } + + objc_disposeClassPair(c); +} + +int main() +{ + IMP dummyIMPs[130] = { + IMPS10, IMPS10, IMPS10, IMPS10, IMPS10, + IMPS10, IMPS10, IMPS10, IMPS10, IMPS10, + IMPS10, IMPS10, IMPS10, + }; + + // similar to dummyIMPs but with different values in each slot + IMP dummyIMPs2[130] = { + fn5, fn6, fn7, fn8, fn9, + IMPS10, IMPS10, IMPS10, IMPS10, IMPS10, + IMPS10, IMPS10, IMPS10, IMPS10, IMPS10, + IMPS10, IMPS10, + fn0, fn1, fn2, fn3, fn4, + }; + + const char *dummyTypes[130] = { + TYPES10, TYPES10, TYPES10, TYPES10, TYPES10, + TYPES10, TYPES10, TYPES10, TYPES10, TYPES10, + TYPES10, TYPES10, TYPES10, + }; + + SEL addSELs[20] = { + SELS10(superMethod), + SELS10(superMethodAddNew) + }; + + uint32_t failedCount = 0; + SEL *failed; + + failed = class_addMethodsBulk([Super class], addSELs, dummyIMPs, dummyTypes, + 20, &failedCount); + + // class_addMethodsBulk reports failures for all methods that already exist + testassert(failed != NULL); + testassert(failedCount == 10); + + // class_addMethodsBulk failed for existing implementations + for(int i = 0; i < 10; i++) { + testassert(failed[i] == addSELs[i]); + testassert(class_getMethodImplementation([Super class], addSELs[i]) + != dummyIMPs[i]); + } + + free(failed); + + // class_addMethodsBulk does add root implementations + for(int i = 10; i < 20; i++) { + testassert(class_getMethodImplementation([Super class], addSELs[i]) + == dummyIMPs[i]); + } + + // class_addMethod does override superclass implementations + failed = class_addMethodsBulk([Sub class], addSELs, dummyIMPs, dummyTypes, + 10, &failedCount); + testassert(failedCount == 0); + testassert(failed == NULL); + for(int i = 0; i < 10; i++) { + testassert(class_getMethodImplementation([Sub class], addSELs[i]) + == dummyIMPs[i]); + } + + SEL subReplaceSELs[40] = { + SELS10(superMethod), + SELS10(subMethodNew), + SELS10(subMethod), + SELS10(bothMethod), + }; + + // class_replaceMethodsBulk adds new implementations or replaces existing + // ones for methods that exist on the superclass, the subclass, both, or + // neither + class_replaceMethodsBulk([Sub2 class], subReplaceSELs, dummyIMPs, + dummyTypes, 40); + for(int i = 0; i < 40; i++) { + IMP newIMP = class_getMethodImplementation([Sub2 class], + subReplaceSELs[i]); + testassert(newIMP == dummyIMPs[i]); + } + + SEL superReplaceSELs[20] = { + SELS10(superMethod), + SELS10(superMethodNew), + }; + + // class_replaceMethodsBulk adds new implementations or replaces existing + // ones in the superclass + class_replaceMethodsBulk([Super class], superReplaceSELs, dummyIMPs, + dummyTypes, 20); + for(int i = 0; i < 20; i++) { + IMP newIMP = class_getMethodImplementation([Super class], + superReplaceSELs[i]); + testassert(newIMP == dummyIMPs[i]); + } + + + // class_addMethodsBulk, where almost all of the requested additions + // already exist and thus can't be added. (They were already added + // above by class_replaceMethodsBulk([Sub2 class], subReplaceSELs, ...).) + + // This list is large in the hope of provoking any realloc() of the + // new method list inside addMethods(). + // The runtime doesn't care that the list contains lots of duplicates. + SEL subAddMostlyExistingSELs[130] = { + SELS10(superMethod), SELS10(subMethodNew), SELS10(subMethod), + SELS10(superMethod), SELS10(subMethodNew), SELS10(subMethod), + SELS10(superMethod), SELS10(subMethodNew), SELS10(subMethod), + SELS10(superMethod), SELS10(subMethodNew), SELS10(subMethod), + SELS10(bothMethod), + }; + subAddMostlyExistingSELs[16] = @selector(INDEX_16_IS_DIFFERENT); + + failed = class_addMethodsBulk([Sub2 class], subAddMostlyExistingSELs, + dummyIMPs2, dummyTypes, 130, &failedCount); + testassert(failedCount == 129); + testassert(failed != NULL); + + for(int i = 0; i < 130; i++) { + IMP newIMP = class_getMethodImplementation([Sub2 class], + subAddMostlyExistingSELs[i]); + if (i == 16) { + // the only one that was actually added + testassert(newIMP != dummyIMPs[i]); + testassert(newIMP == dummyIMPs2[i]); + } else { + // the others should all have failed + testassert(newIMP == dummyIMPs[i]); + testassert(newIMP != dummyIMPs2[i]); + } + } + for (uint32_t i = 0; i < failedCount; i++) { + testassert(failed[i] != NULL); + testassert(failed[i] != subAddMostlyExistingSELs[16]); + } + + + // fixme actually try calling them + + // make sure the Bulk functions aren't leaking + testBulkMemoryOnce(); + leak_mark(); + for(int i = 0; i < 10; i++) { + testBulkMemoryOnce(); + } + leak_check(0); + + succeed(__FILE__); +} diff --git a/test/addProtocol.m b/test/addProtocol.m index e95bc9a2..7f639b15 100644 --- a/test/addProtocol.m +++ b/test/addProtocol.m @@ -1,4 +1,12 @@ /* +TEST_CFLAGS -framework Foundation +TEST_BUILD_OUTPUT +.*addProtocol.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*addProtocol.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*addProtocol.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*addProtocol.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*addProtocol.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +END TEST_RUN_OUTPUT objc\[\d+\]: protocol_addProtocol: added protocol 'EmptyProto' is still under construction! objc\[\d+\]: objc_registerProtocol: protocol 'Proto1' was already registered! @@ -14,6 +22,7 @@ #include "test.h" #include +#include @protocol SuperProto @end @protocol SuperProto2 @end @@ -30,6 +39,10 @@ void Crash(id self, SEL _cmd) int main() { + // old-ABI implementation of [Protocol retain] + // is added by +[NSObject(NSObject) load] in CF. + [NSObject class]; + Protocol *proto, *proto2; Protocol * __unsafe_unretained *protolist; struct objc_method_description *desclist; @@ -87,6 +100,10 @@ int main() char *name1 = strdup("ReqInst1"); char *name2 = strdup("ReqInst2"); char *name3 = strdup("ReqInst3"); + char *name4 = strdup("ReqClass0"); + char *name5 = strdup("ReqClass1"); + char *name6 = strdup("ReqClass2"); + char *name7 = strdup("ReqClass3"); char *attrname = strdup("T"); char *attrvalue = strdup("i"); objc_property_attribute_t attrs[] = {{attrname, attrvalue}}; @@ -95,6 +112,10 @@ int main() protocol_addProperty(proto, name1, attrs, attrcount, YES, YES); protocol_addProperty(proto, name2, attrs, attrcount, YES, YES); protocol_addProperty(proto, name3, attrs, attrcount, YES, YES); + protocol_addProperty(proto, name4, attrs, attrcount, YES, NO); + protocol_addProperty(proto, name5, attrs, attrcount, YES, NO); + protocol_addProperty(proto, name6, attrs, attrcount, YES, NO); + protocol_addProperty(proto, name7, attrs, attrcount, YES, NO); objc_registerProtocol(proto); testassert(0 == strcmp(protocol_getName(proto), "Proto1")); @@ -145,9 +166,16 @@ int main() strcpy(name1, "XXXXXXXX"); // name is copied strcpy(name2, "XXXXXXXX"); // name is copied strcpy(name3, "XXXXXXXX"); // name is copied + strcpy(name4, "XXXXXXXXX"); // name is copied + strcpy(name5, "XXXXXXXXX"); // name is copied + strcpy(name6, "XXXXXXXXX"); // name is copied + strcpy(name7, "XXXXXXXXX"); // name is copied strcpy(attrname, "X"); // description is copied strcpy(attrvalue, "X"); // description is copied memset(attrs, 'X', sizeof(attrs)); // description is copied + + // instance properties + count = 100; proplist = protocol_copyPropertyList(proto, &count); testassert(proplist); testassert(count == 4); @@ -162,6 +190,22 @@ int main() testassert(0 == strcmp(property_getAttributes(proplist[3]), "Ti")); free(proplist); + // class properties + count = 100; + proplist = protocol_copyPropertyList2(proto, &count, YES, NO); + testassert(proplist); + testassert(count == 4); + // note this order is not required + testassert(0 == strcmp(property_getName(proplist[0]), "ReqClass0")); + testassert(0 == strcmp(property_getName(proplist[1]), "ReqClass1")); + testassert(0 == strcmp(property_getName(proplist[2]), "ReqClass2")); + testassert(0 == strcmp(property_getName(proplist[3]), "ReqClass3")); + 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")); @@ -202,8 +246,8 @@ int main() // NULL protocols ignored - protocol_addProtocol((Protocol *)objc_unretainedObject((void*)1), NULL); - protocol_addProtocol(NULL, (Protocol *)objc_unretainedObject((void*)1)); + protocol_addProtocol((__bridge Protocol *)((void*)1), NULL); + protocol_addProtocol(NULL, (__bridge Protocol *)((void*)1)); protocol_addProtocol(NULL, NULL); protocol_addMethodDescription(NULL, @selector(foo), "", YES, YES); diff --git a/test/applescriptobjc.m b/test/applescriptobjc.m index 158c3016..dd4370e3 100644 --- a/test/applescriptobjc.m +++ b/test/applescriptobjc.m @@ -9,6 +9,5 @@ int main() { [NSBundle class]; - testassert(!objc_collectingEnabled()); succeed(__FILE__); } diff --git a/test/applescriptobjc2.m b/test/applescriptobjc2.m deleted file mode 100644 index 832b3cd5..00000000 --- a/test/applescriptobjc2.m +++ /dev/null @@ -1,17 +0,0 @@ -// TEST_CFLAGS -framework AppleScriptObjC -framework Foundation -// TEST_CONFIG MEM=gc - -// Verify that non-trivial AppleScriptObjC apps run with GC ON. - -#include -#include "test.h" - -@interface NonTrivial : NSObject @end -@implementation NonTrivial @end - -int main() -{ - [NSBundle class]; - testassert(objc_collectingEnabled()); - succeed(__FILE__); -} diff --git a/test/arr-weak.m b/test/arr-weak.m index 3ea58a3b..a38077f7 100644 --- a/test/arr-weak.m +++ b/test/arr-weak.m @@ -3,7 +3,7 @@ /* TEST_RUN_OUTPUT objc\[\d+\]: Cannot form weak reference to instance \(0x[0-9a-f]+\) of class Crash. It is possible that this object was over-released, or is in the process of deallocation. -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: HALTED END */ diff --git a/test/asm-placeholder.s b/test/asm-placeholder.s new file mode 100644 index 00000000..f222644b --- /dev/null +++ b/test/asm-placeholder.s @@ -0,0 +1,47 @@ +.macro NOP16 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop +.endmacro + +.macro NOP256 + NOP16 + NOP16 + NOP16 + NOP16 + NOP16 + NOP16 + NOP16 + NOP16 + NOP16 + NOP16 + NOP16 + NOP16 + NOP16 + NOP16 + NOP16 + NOP16 +.endmacro + +.text + .globl _main + .align 14 +_main: + // at least 1024 instruction bytes on all architectures + NOP256 + NOP256 + NOP256 + NOP256 diff --git a/test/association-cf.m b/test/association-cf.m index 5930c5c8..4ffa2f4c 100644 --- a/test/association-cf.m +++ b/test/association-cf.m @@ -1,4 +1,7 @@ // TEST_CFLAGS -framework CoreFoundation +// TEST_CONFIG MEM=mrc +// not for ARC because ARC memory management doesn't +// work on CF types whose ObjC side is not yet loaded #include #include @@ -17,20 +20,19 @@ int main() int main() { - // rdar://6164781 setAssociatedObject on pure-CF object crashes LP64 + // rdar://6164781 setAssociatedObject on unresolved future class crashes - id obj; - id array = objc_retainedObject(CFArrayCreate(0, 0, 0, 0)); - testassert(array); + id mp = (id)CFMachPortCreate(0, 0, 0, 0); + testassert(mp); - testassert(! objc_getClass("NSCFArray")); + testassert(! objc_getClass("NSMachPort")); - objc_setAssociatedObject(array, (void*)1, array, OBJC_ASSOCIATION_ASSIGN); + objc_setAssociatedObject(mp, (void*)1, mp, OBJC_ASSOCIATION_ASSIGN); - obj = objc_getAssociatedObject(array, (void*)1); - testassert(obj == array); + id obj = objc_getAssociatedObject(mp, (void*)1); + testassert(obj == mp); - RELEASE_VAR(array); + CFRelease((CFTypeRef)mp); succeed(__FILE__); } diff --git a/test/association.m b/test/association.m index 429a1c02..c2974215 100644 --- a/test/association.m +++ b/test/association.m @@ -37,11 +37,6 @@ -(void) dealloc supers++; SUPER_DEALLOC(); } --(void) finalize -{ - supers++; - [super finalize]; -} @end @@ -51,11 +46,6 @@ -(void) dealloc subs++; SUPER_DEALLOC(); } --(void) finalize -{ - subs++; - [super finalize]; -} @end @implementation Super2 @@ -77,11 +67,6 @@ -(void) dealloc supers++; SUPER_DEALLOC(); } --(void) finalize -{ - supers++; - [super finalize]; -} @end @@ -91,11 +76,6 @@ -(void) dealloc subs++; SUPER_DEALLOC(); } --(void) finalize -{ - subs++; - [super finalize]; -} @end @implementation Value @@ -103,10 +83,6 @@ -(void) dealloc { values++; SUPER_DEALLOC(); } --(void) finalize { - values++; - [super finalize]; -} @end @@ -140,6 +116,12 @@ int main() testassert(supers > 0); testassert(subs == 0); testassert(supers == values); + + // rdar://44094390 tolerate nil object and nil value +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + objc_setAssociatedObject(nil, &key, nil, 0); +#pragma clang diagnostic pop succeed(__FILE__); } diff --git a/test/associationForbidden.h b/test/associationForbidden.h new file mode 100644 index 00000000..2ec4de0e --- /dev/null +++ b/test/associationForbidden.h @@ -0,0 +1,50 @@ +#include "testroot.i" + +@interface Normal : TestRoot +@end +@implementation Normal +@end + +@interface Forbidden : TestRoot +@end +@implementation Forbidden +@end + +struct minimal_unrealized_class { + void *isa; + void *superclass; + void *cachePtr; + uintptr_t maskAndOccupied; + struct minimal_class_ro *ro; +}; + +struct minimal_class_ro { + uint32_t flags; +}; + +extern struct minimal_unrealized_class OBJC_CLASS_$_Forbidden; + +#define RO_FORBIDS_ASSOCIATED_OBJECTS (1<<10) + +static void *key = &key; + +static void test(void); + +int main() +{ + struct minimal_unrealized_class *localForbidden = &OBJC_CLASS_$_Forbidden; + localForbidden->ro->flags |= RO_FORBIDS_ASSOCIATED_OBJECTS; + test(); +} + +static inline void ShouldSucceed(id obj) { + objc_setAssociatedObject(obj, key, obj, OBJC_ASSOCIATION_ASSIGN); + id assoc = objc_getAssociatedObject(obj, key); + fprintf(stderr, "Associated object is %p\n", assoc); + testassert(obj == assoc); +} + +static inline void ShouldFail(id obj) { + objc_setAssociatedObject(obj, key, obj, OBJC_ASSOCIATION_ASSIGN); + fail("should have crashed trying to set the associated object"); +} diff --git a/test/associationForbidden.m b/test/associationForbidden.m new file mode 100644 index 00000000..1afac35c --- /dev/null +++ b/test/associationForbidden.m @@ -0,0 +1,16 @@ +// TEST_CRASHES +/* +TEST_RUN_OUTPUT +Associated object is 0x[0-9a-fA-F]+ +objc\[\d+\]: objc_setAssociatedObject called on instance \(0x[0-9a-fA-F]+\) of class Forbidden which does not allow associated objects +objc\[\d+\]: HALTED +END +*/ + +#include "associationForbidden.h" + +void test(void) +{ + ShouldSucceed([Normal alloc]); + ShouldFail([Forbidden alloc]); +} diff --git a/test/associationForbidden2.m b/test/associationForbidden2.m new file mode 100644 index 00000000..9d71ee01 --- /dev/null +++ b/test/associationForbidden2.m @@ -0,0 +1,19 @@ +// TEST_CRASHES +/* +TEST_RUN_OUTPUT +Associated object is 0x[0-9a-fA-F]+ +objc\[\d+\]: objc_setAssociatedObject called on instance \(0x[0-9a-fA-F]+\) of class ForbiddenSubclass which does not allow associated objects +objc\[\d+\]: HALTED +END +*/ + +#include "associationForbidden.h" + +void test(void) +{ + ShouldSucceed([Normal alloc]); + Class ForbiddenSubclass = objc_allocateClassPair([Forbidden class], + "ForbiddenSubclass", 0); + objc_registerClassPair(ForbiddenSubclass); + ShouldFail([ForbiddenSubclass alloc]); +} diff --git a/test/associationForbidden3.m b/test/associationForbidden3.m new file mode 100644 index 00000000..f96ba905 --- /dev/null +++ b/test/associationForbidden3.m @@ -0,0 +1,21 @@ +// TEST_CRASHES +/* +TEST_RUN_OUTPUT +Associated object is 0x[0-9a-fA-F]+ +objc\[\d+\]: objc_setAssociatedObject called on instance \(0x[0-9a-fA-F]+\) of class ForbiddenSubclass which does not allow associated objects +objc\[\d+\]: HALTED +END +*/ + +#include "associationForbidden.h" + +@interface ForbiddenSubclass : Forbidden +@end +@implementation ForbiddenSubclass +@end + +void test(void) +{ + ShouldSucceed([Normal alloc]); + ShouldSucceed([ForbiddenSubclass alloc]); +} diff --git a/test/associationForbidden4.m b/test/associationForbidden4.m new file mode 100644 index 00000000..05370fed --- /dev/null +++ b/test/associationForbidden4.m @@ -0,0 +1,18 @@ +// TEST_CRASHES +/* +TEST_RUN_OUTPUT +Associated object is 0x[0-9a-fA-F]+ +objc\[\d+\]: objc_setAssociatedObject called on instance \(0x[0-9a-fA-F]+\) of class ForbiddenDuplicate which does not allow associated objects +objc\[\d+\]: HALTED +END +*/ + +#include "associationForbidden.h" + +void test(void) +{ + ShouldSucceed([Normal alloc]); + Class ForbiddenDuplicate = objc_duplicateClass([Forbidden class], + "ForbiddenDuplicate", 0); + ShouldFail([ForbiddenDuplicate alloc]); +} diff --git a/test/atomicProperty.mm b/test/atomicProperty.mm index ea8bade1..2625e76b 100644 --- a/test/atomicProperty.mm +++ b/test/atomicProperty.mm @@ -1,4 +1,4 @@ -// TEST_CONFIG CC=clang +// TEST_CONFIG #include "test.h" #include diff --git a/test/badAltHandler.m b/test/badAltHandler.m index efee8525..6ea4c760 100644 --- a/test/badAltHandler.m +++ b/test/badAltHandler.m @@ -1,10 +1,10 @@ -// for OBJC2 mac only -/* TEST_CONFIG OS=macosx ARCH=x86_64 +/* TEST_CONFIG OS=macosx 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 +objc\[\d+\]: objc_removeExceptionHandler\(\) called with unknown alt handler; this is probably a bug in multithreaded AppKit use. +objc\[\d+\]: HALTED END */ diff --git a/test/badCache.m b/test/badCache.m index 96f455ba..08daf776 100644 --- a/test/badCache.m +++ b/test/badCache.m @@ -1,7 +1,7 @@ /* TEST_CRASHES TEST_RUN_OUTPUT -objc1 +arm OK: badCache.m OR crash now @@ -10,19 +10,22 @@ objc\[\d+\]: .* objc\[\d+\]: .* objc\[\d+\]: .* -objc\[\d+\]: Method cache corrupted\. -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: Method cache corrupted.* +objc\[\d+\]: HALTED END */ #include "test.h" -#if !__OBJC2__ || __arm__ +// Test objc_msgSend's detection of infinite loops during cache scan. + +#if __arm__ int main() { - fprintf(stderr, "objc1\n"); + testwarn("objc_msgSend on arm doesn't detect infinite loops"); + fprintf(stderr, "arm\n"); succeed(__FILE__); } @@ -65,7 +68,7 @@ int main() struct cache_t *cache = &((__bridge struct class_t *)cls)->cache; # define COUNT 4 - struct bucket_t *buckets = calloc(sizeof(struct bucket_t), COUNT+1); + struct bucket_t *buckets = (struct bucket_t *)calloc(sizeof(struct bucket_t), COUNT+1); for (int i = 0; i < COUNT; i++) { buckets[i].sel = ~0; buckets[i].imp = ~0; diff --git a/test/badPool.m b/test/badPool.m new file mode 100644 index 00000000..fa47e8e8 --- /dev/null +++ b/test/badPool.m @@ -0,0 +1,34 @@ +// TEST_CONFIG MEM=mrc +// TEST_CRASHES + +// Test badPoolCompat also uses this file. + +/* +TEST_RUN_OUTPUT +objc\[\d+\]: [Ii]nvalid or prematurely-freed autorelease pool 0x[0-9a-fA-F]+\.? +objc\[\d+\]: HALTED +END +*/ + +#include "test.h" + +int main() +{ + void *outer = objc_autoreleasePoolPush(); + void *inner = objc_autoreleasePoolPush(); + objc_autoreleasePoolPop(outer); + objc_autoreleasePoolPop(inner); + +#if !OLD + fail("should have crashed already with new SDK"); +#else + // should only warn once + outer = objc_autoreleasePoolPush(); + inner = objc_autoreleasePoolPush(); + objc_autoreleasePoolPop(outer); + objc_autoreleasePoolPop(inner); + + succeed(__FILE__); +#endif +} + diff --git a/test/badPoolCompat-ios-tvos.m b/test/badPoolCompat-ios-tvos.m new file mode 100644 index 00000000..5f1b92ca --- /dev/null +++ b/test/badPoolCompat-ios-tvos.m @@ -0,0 +1,14 @@ +// Run test badPool as if it were built with an old SDK. + +// TEST_CONFIG MEM=mrc OS=iphoneos,iphonesimulator,appletvos,appletvsimulator +// TEST_CRASHES +// TEST_CFLAGS -DOLD=1 -Xlinker -sdk_version -Xlinker 9.0 + +/* +TEST_RUN_OUTPUT +objc\[\d+\]: Invalid or prematurely-freed autorelease pool 0x[0-9a-fA-f]+\. Set a breakpoint .* Proceeding anyway .* +OK: badPool.m +END +*/ + +#include "badPool.m" diff --git a/test/badPoolCompat-macos.m b/test/badPoolCompat-macos.m new file mode 100644 index 00000000..afd21176 --- /dev/null +++ b/test/badPoolCompat-macos.m @@ -0,0 +1,14 @@ +// Run test badPool as if it were built with an old SDK. + +// TEST_CONFIG MEM=mrc OS=macosx +// TEST_CRASHES +// TEST_CFLAGS -DOLD=1 -Xlinker -sdk_version -Xlinker 10.11 + +/* +TEST_RUN_OUTPUT +objc\[\d+\]: Invalid or prematurely-freed autorelease pool 0x[0-9a-fA-f]+\. Set a breakpoint .* Proceeding anyway .* +OK: badPool.m +END +*/ + +#include "badPool.m" diff --git a/test/badPoolCompat-watchos.m b/test/badPoolCompat-watchos.m new file mode 100644 index 00000000..6e89e44b --- /dev/null +++ b/test/badPoolCompat-watchos.m @@ -0,0 +1,14 @@ +// Run test badPool as if it were built with an old SDK. + +// TEST_CONFIG MEM=mrc OS=watchos,watchsimulator +// TEST_CRASHES +// TEST_CFLAGS -DOLD=1 -Xlinker -sdk_version -Xlinker 2.0 + +/* +TEST_RUN_OUTPUT +objc\[\d+\]: Invalid or prematurely-freed autorelease pool 0x[0-9a-fA-f]+\. Set a breakpoint .* Proceeding anyway .* +OK: badPool.m +END +*/ + +#include "badPool.m" diff --git a/test/badSuperclass.m b/test/badSuperclass.m new file mode 100644 index 00000000..2fa0bc75 --- /dev/null +++ b/test/badSuperclass.m @@ -0,0 +1,37 @@ +// TEST_CRASHES +/* +TEST_RUN_OUTPUT +objc\[\d+\]: Memory corruption in class list\. +objc\[\d+\]: HALTED +END +*/ + +#include "test.h" +#include "testroot.i" + +@interface Super : TestRoot @end +@implementation Super @end + +@interface Sub : Super @end +@implementation Sub @end + +int main() +{ + alarm(10); + + Class supercls = [Super class]; + Class subcls = [Sub class]; + id subobj __unused = [Sub alloc]; + + // Create a cycle in a superclass chain (Sub->supercls == Sub) + // then attempt to walk that chain. Runtime should halt eventually. + _objc_flush_caches(supercls); + ((Class *)(__bridge void *)subcls)[1] = subcls; +#ifdef CACHE_FLUSH + _objc_flush_caches(supercls); +#else + [subobj class]; +#endif + + fail("should have crashed"); +} diff --git a/test/badSuperclass2.m b/test/badSuperclass2.m new file mode 100644 index 00000000..a75f053d --- /dev/null +++ b/test/badSuperclass2.m @@ -0,0 +1,13 @@ +// TEST_CRASHES +/* +TEST_RUN_OUTPUT +objc\[\d+\]: Memory corruption in class list\. +objc\[\d+\]: HALTED +OR +old abi +OK: badSuperclass\.m +END +*/ + +#define CACHE_FLUSH +#include "badSuperclass.m" diff --git a/test/badTagClass.m b/test/badTagClass.m index d7d10be4..2a08ebd8 100644 --- a/test/badTagClass.m +++ b/test/badTagClass.m @@ -1,8 +1,11 @@ /* TEST_CRASHES +TEST_BUILD_OUTPUT +.*badTagClass.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +END TEST_RUN_OUTPUT -objc\[\d+\]: tag index 7 used for two different classes \(was 0x[0-9a-fA-F]+ NSObject, now 0x[0-9a-fA-F]+ TestRoot\) -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: tag index 1 used for two different classes \(was 0x[0-9a-fA-F]+ NSObject, now 0x[0-9a-fA-F]+ TestRoot\) +objc\[\d+\]: HALTED OR no tagged pointers OK: badTagClass.m @@ -20,13 +23,13 @@ int main() { // re-registration and nil registration allowed - _objc_registerTaggedPointerClass(OBJC_TAG_7, [NSObject class]); - _objc_registerTaggedPointerClass(OBJC_TAG_7, [NSObject class]); - _objc_registerTaggedPointerClass(OBJC_TAG_7, nil); - _objc_registerTaggedPointerClass(OBJC_TAG_7, [NSObject class]); + _objc_registerTaggedPointerClass(OBJC_TAG_1, [NSObject class]); + _objc_registerTaggedPointerClass(OBJC_TAG_1, [NSObject class]); + _objc_registerTaggedPointerClass(OBJC_TAG_1, nil); + _objc_registerTaggedPointerClass(OBJC_TAG_1, [NSObject class]); // colliding registration disallowed - _objc_registerTaggedPointerClass(OBJC_TAG_7, [TestRoot class]); + _objc_registerTaggedPointerClass(OBJC_TAG_1, [TestRoot class]); fail(__FILE__); } @@ -35,6 +38,9 @@ int main() int main() { + // provoke the same nullability warning as the real test + objc_getClass(nil); + fprintf(stderr, "no tagged pointers\n"); succeed(__FILE__); } diff --git a/test/badTagIndex.m b/test/badTagIndex.m index 7a84b66a..c4bfd33a 100644 --- a/test/badTagIndex.m +++ b/test/badTagIndex.m @@ -1,8 +1,8 @@ /* TEST_CRASHES TEST_RUN_OUTPUT -objc\[\d+\]: tag index 8 is too large. -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: tag index 264 is invalid +objc\[\d+\]: HALTED OR no tagged pointers OK: badTagIndex.m @@ -18,7 +18,7 @@ int main() { - _objc_registerTaggedPointerClass((objc_tag_index_t)8, [NSObject class]); + _objc_registerTaggedPointerClass((objc_tag_index_t)(OBJC_TAG_Last52BitPayload+1), [NSObject class]); fail(__FILE__); } diff --git a/test/bigrc.m b/test/bigrc.m index f171c6e6..419bbb60 100644 --- a/test/bigrc.m +++ b/test/bigrc.m @@ -57,6 +57,12 @@ -(void)dealloc @end +size_t clz(uintptr_t isa) { + if (sizeof(uintptr_t) == 4) + return __builtin_clzl(isa); + testassert(sizeof(uintptr_t) == 8); + return __builtin_clzll(isa); +} int main() { @@ -68,7 +74,7 @@ int main() uintptr_t isa = *(uintptr_t *)o; if (isa & 1) { // Assume refcount in high bits. - LOTS = 1 << (4 + __builtin_clzll(isa)); + LOTS = 1 << (4 + clz(isa)); testprintf("LOTS %zu via cntlzw\n", LOTS); } else { LOTS = 0x1000000; diff --git a/test/blocksAsImps.m b/test/blocksAsImps.m index 360321af..2ba25808 100644 --- a/test/blocksAsImps.m +++ b/test/blocksAsImps.m @@ -10,17 +10,11 @@ # define __bridge #endif -#if !__clang__ - // gcc and llvm-gcc will never support struct-return marking -# define STRET_OK 0 -# define STRET_SPECIAL 0 -#elif __arm64__ +#if __arm64__ // stret supported, but is identical to non-stret -# define STRET_OK 1 # define STRET_SPECIAL 0 #else // stret supported and distinct from non-stret -# define STRET_OK 1 # define STRET_SPECIAL 1 #endif @@ -106,21 +100,18 @@ int main () { aMode = _argumentModeForBlock(registerReturn); testassert(aMode == ReturnValueInRegisterArgumentMode); -#if STRET_OK - BigStruct (^stackReturn)() = ^() { BigStruct k; return k; }; + BigStruct (^stackReturn)() = ^() { BigStruct k = {{0}}; return k; }; aMode = _argumentModeForBlock(stackReturn); -# if STRET_SPECIAL +#if STRET_SPECIAL testassert(aMode == ReturnValueOnStackArgumentMode); -# else +#else testassert(aMode == ReturnValueInRegisterArgumentMode); -# endif #endif -#define TEST_QUANTITY 100000 - static FuncPtr funcArray[TEST_QUANTITY]; + uintptr_t TEST_QUANTITY = is_guardmalloc() ? 1000 : 100000; + FuncPtr funcArray[TEST_QUANTITY]; - uintptr_t i; - for(i = 0; i + +#if TARGET_OS_OSX +# define RealBool 0 +#elif TARGET_OS_IOS +# if (__arm__ && !__armv7k__) || __i386__ +# define RealBool 0 +# else +# define RealBool 1 +# endif +#else +# define RealBool 1 +#endif + +#if __OBJC__ && !defined(__OBJC_BOOL_IS_BOOL) +# error no __OBJC_BOOL_IS_BOOL +#endif + +#if RealBool != OBJC_BOOL_IS_BOOL +# error wrong OBJC_BOOL_IS_BOOL +#endif + +#if RealBool == OBJC_BOOL_IS_CHAR +# error wrong OBJC_BOOL_IS_CHAR +#endif + +int main() +{ + const char *expected __unused = +#if RealBool + "B" +#else + "c" +#endif + ; +#if __OBJC__ + const char *enc = @encode(BOOL); + testassert(0 == strcmp(enc, expected)); +#endif + succeed(__FILE__); +} diff --git a/test/cacheflush.m b/test/cacheflush.m index 85136d7f..2bc21ae7 100644 --- a/test/cacheflush.m +++ b/test/cacheflush.m @@ -3,7 +3,7 @@ $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 + $C{COMPILE} $DIR/cacheflush.m -x none cacheflush0.dylib -o cacheflush.exe END */ diff --git a/test/category.m b/test/category.m index 3e55a150..f8cd3a9b 100644 --- a/test/category.m +++ b/test/category.m @@ -31,10 +31,13 @@ -(void)instancemethod { @interface Super (PropertyCategory) @property int i; +@property(class) int i; @end @implementation Super (PropertyCategory) - (int)i { return 0; } - (void)setI:(int)value { (void)value; } ++ (int)i { return 0; } ++ (void)setI:(int)value { (void)value; } @end // rdar://5086110 memory smasher in category with class method and property @@ -49,6 +52,46 @@ - (int)property5086110 { fail("property5086110 called!"); return 0; } - (void)setProperty5086110:(int)value { fail("setProperty5086110 called!"); (void)value; } @end +// rdar://25605427 incorrect handling of class properties in 10.11 and earlier +@interface Super25605427 : TestRoot +@property(class, readonly) int i; +@end +@implementation Super25605427 ++(int)i { return 0; } +@end + +@interface Super25605427 (r25605427a) +@property(readonly) int r25605427a1; +@end +@implementation Super25605427 (r25605427a) +-(int)r25605427a1 { return 0; } ++(int)r25605427a2 { return 0; } +@end + +@interface Super25605427 (r25605427b) +@property(readonly) int r25605427b1; +@end +@implementation Super25605427 (r25605427b) +-(int)r25605427b1 { return 0; } ++(int)r25605427b2 { return 0; } +@end + +@interface Super25605427 (r25605427c) +@property(readonly) int r25605427c1; +@end +@implementation Super25605427 (r25605427c) +-(int)r25605427c1 { return 0; } ++(int)r25605427c2 { return 0; } +@end + +@interface Super25605427 (r25605427d) +@property(readonly) int r25605427d1; +@end +@implementation Super25605427 (r25605427d) +-(int)r25605427d1 { return 0; } ++(int)r25605427d2 { return 0; } +@end + @interface PropertyClass : Super { int q; @@ -69,6 +112,16 @@ @implementation PropertyClass (PropertyCategory) int main() { + { + // rdar://25605427 bugs in 10.11 and earlier when metaclass + // has a property and category has metaclass additions. + // Memory smasher in buildPropertyList (caught by guard malloc) + Class cls = [Super25605427 class]; + // Incorrect attachment of instance properties from category to metacls + testassert(class_getProperty(cls, "r25605427d1")); + testassert(! class_getProperty(object_getClass(cls), "r25605427d1")); + } + // methods introduced by category state = 0; [Super method]; @@ -81,6 +134,12 @@ int main() testassert(0 == strcmp(property_getName(p), "i")); testassert(property_getAttributes(p)); + objc_property_t p2 = class_getProperty(object_getClass([Super class]), "i"); + testassert(p2); + testassert(p != p2); + testassert(0 == strcmp(property_getName(p2), "i")); + testassert(property_getAttributes(p2)); + // methods introduced by category's property Method m; m = class_getInstanceMethod([Super class], @selector(i)); @@ -88,6 +147,11 @@ int main() m = class_getInstanceMethod([Super class], @selector(setI:)); testassert(m); + m = class_getClassMethod([Super class], @selector(i)); + testassert(m); + m = class_getClassMethod([Super class], @selector(setI:)); + testassert(m); + // class's property shadowed by category's property objc_property_t *plist = class_copyPropertyList([PropertyClass class], NULL); testassert(plist); diff --git a/test/cdtors.mm b/test/cdtors.mm index c91e738a..be2a7d17 100644 --- a/test/cdtors.mm +++ b/test/cdtors.mm @@ -276,11 +276,6 @@ void test_batch(void) int main() { - if (objc_collectingEnabled()) { - testwarn("rdar://19042235 test disabled in GC because it is slow"); - succeed(FILENAME); - } - for (int i = 0; i < 1000; i++) { testonthread(^{ test_single(); }); testonthread(^{ test_inplace(); }); diff --git a/test/classgetclass.m b/test/classgetclass.m index 70686ebc..b73243b2 100644 --- a/test/classgetclass.m +++ b/test/classgetclass.m @@ -12,9 +12,6 @@ @implementation Foo int main() { -#if __OBJC2__ testassert(gdb_class_getClass([Foo class]) == [Foo class]); -#endif - succeed(__FILE__); } diff --git a/test/classname.m b/test/classname.m index 7234641c..8d4b993c 100644 --- a/test/classname.m +++ b/test/classname.m @@ -11,7 +11,7 @@ @implementation Fake @end int main() { TestRoot *obj = [TestRoot new]; - Class __unsafe_unretained * buf = (Class *)objc_unretainedPointer(obj); + Class __unsafe_unretained * buf = (Class *)(__bridge void *)(obj); *buf = [Fake class]; testassert(object_getClass(obj) == [Fake class]); diff --git a/test/classpair.m b/test/classpair.m index c30733be..d1346f18 100644 --- a/test/classpair.m +++ b/test/classpair.m @@ -1,14 +1,10 @@ -// TEST_CFLAGS -Wno-deprecated-declarations +// TEST_CONFIG #include "test.h" #include "testroot.i" #include #include -#ifndef OBJC_NO_GC -#include -#include -#endif @protocol Proto -(void) instanceMethod; @@ -49,9 +45,6 @@ -(void) instanceMethod { fail("-[Super instanceMethod] called"); } -(void) instanceMethod2 { fail("-[Super instanceMethod2] called"); } @end -@interface WeakSuper : Super { __weak id weakIvar; } @end -@implementation WeakSuper @end - static int state; static void instance_fn(id self, SEL _cmd __attribute__((unused))) @@ -86,12 +79,6 @@ static void cycle(void) cls = objc_allocateClassPair([Super class], "Sub", 0); testassert(cls); -#ifndef OBJC_NO_GC - if (objc_collectingEnabled()) { - testassert(auto_zone_size(objc_collectableZone(), objc_unretainedPointer(cls))); - testassert(auto_zone_size(objc_collectableZone(), objc_unretainedPointer(object_getClass(cls)))); - } -#endif class_addMethod(cls, @selector(instanceMethod), (IMP)&instance_fn, "v@:"); @@ -186,11 +173,6 @@ static void cycle(void) testassert(class_getInstanceSize(cls) >= sizeof(Class) + 4 + 3*size); testassert(class_conformsToProtocol(cls, @protocol(Proto))); - if (objc_collectingEnabled()) { - testassert(0 == strcmp((char *)class_getIvarLayout(cls), "\x01\x13")); - testassert(NULL == class_getWeakIvarLayout(cls)); - } - class_addMethod(cls, @selector(instanceMethod2), (IMP)&instance_fn, "v@:"); class_addMethod(object_getClass(cls), @selector(classMethod2), @@ -243,7 +225,7 @@ static void cycle(void) testassert(state == 2); // put instance tests on a separate thread so they - // are reliably GC'd before class destruction + // are reliably deallocated before class destruction testonthread(^{ id obj = [cls new]; state = 0; @@ -282,94 +264,35 @@ static void cycle(void) objc_registerClassPair(cls2); - 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); - testcollect(); // GC: finalize "obj" above before disposing its class objc_disposeClassPair(cls2); objc_disposeClassPair(cls); testassert(!objc_getClass("Sub")); - - // Test unmodified ivar layouts - - cls = objc_allocateClassPair([Super class], "Sub2", 0); - testassert(cls); - objc_registerClassPair(cls); - if (objc_collectingEnabled()) { - const char *l1, *l2; - l1 = (char *)class_getIvarLayout([Super class]); - l2 = (char *)class_getIvarLayout(cls); - testassert(l1 == l2 || 0 == strcmp(l1, l2)); - l1 = (char *)class_getWeakIvarLayout([Super class]); - l2 = (char *)class_getWeakIvarLayout(cls); - testassert(l1 == l2 || 0 == strcmp(l1, l2)); - } - objc_disposeClassPair(cls); - - cls = objc_allocateClassPair([WeakSuper class], "Sub3", 0); - testassert(cls); - objc_registerClassPair(cls); - if (objc_collectingEnabled()) { - const char *l1, *l2; - l1 = (char *)class_getIvarLayout([WeakSuper class]); - l2 = (char *)class_getIvarLayout(cls); - testassert(l1 == l2 || 0 == strcmp(l1, l2)); - 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_collectingEnabled()) { - cls = objc_allocateClassPair([Super class], "Sub4", 0); - testassert(cls); - class_setIvarLayout(cls, (uint8_t *)"foo"); - class_setWeakIvarLayout(cls, NULL); - objc_registerClassPair(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, (uint8_t *)"bar"); - objc_registerClassPair(cls); - testassert(NULL == class_getIvarLayout(cls)); - testassert(0 == strcmp("bar", (char *)class_getWeakIvarLayout(cls))); - objc_disposeClassPair(cls); - } + // fixme test layout setters } int main() { - int count = 1000; + int count = 5000; - testonthread(^{ cycle(); }); - testonthread(^{ cycle(); }); - testonthread(^{ cycle(); }); + // fixme even with this long warmup we still + // suffer false 4096-byte leaks occasionally. + for (int i = 0; i < 500; i++) { + testonthread(^{ cycle(); }); + } leak_mark(); while (count--) { testonthread(^{ cycle(); }); } -#if __OBJC_GC__ - testwarn("rdar://19042235 possible leaks suppressed under GC"); - leak_check(16000); -#else - leak_check(0); -#endif + leak_check(4096); succeed(__FILE__); } diff --git a/test/concurrentcat.m b/test/concurrentcat.m index 0f6ef699..f4bb1450 100644 --- a/test/concurrentcat.m +++ b/test/concurrentcat.m @@ -1,22 +1,22 @@ /* TEST_BUILD - $C{COMPILE} $DIR/concurrentcat.m -o concurrentcat.out -framework Foundation + $C{COMPILE} $DIR/concurrentcat.m -o concurrentcat.exe -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 + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc1 -o cc1.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc2 -o cc2.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc3 -o cc3.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc4 -o cc4.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc5 -o cc5.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc6 -o cc6.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc7 -o cc7.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc8 -o cc8.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc9 -o cc9.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc10 -o cc10.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc11 -o cc11.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc12 -o cc12.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc13 -o cc13.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc14 -o cc14.bundle + $C{COMPILE} -bundle -bundle_loader concurrentcat.exe -framework Foundation $DIR/concurrentcat_category.m -DTN=cc15 -o cc15.bundle END */ @@ -63,8 +63,6 @@ - (void) m6 { fail("shoulda been loaded!"); } void *threadFun(void *aTargetClassName) { const char *className = (const char *)aTargetClassName; - objc_registerThreadWithCollector(); - PUSH_POOL { Class targetSubclass = objc_getClass(className); @@ -116,7 +114,7 @@ int main() for(i=1; i<16; i++) { pthread_t t; char dlName[100]; - sprintf(dlName, "cc%d.dylib", i); + sprintf(dlName, "cc%d.bundle", i); dylib = dlopen(dlName, RTLD_LAZY); char className[100]; sprintf(className, "cc%d", i); diff --git a/test/copyPropertyList.m b/test/copyPropertyList.m index 40dbec24..e3b26325 100644 --- a/test/copyPropertyList.m +++ b/test/copyPropertyList.m @@ -42,6 +42,20 @@ @implementation FourProps @interface NoProps @end @implementation NoProps @end +OBJC_ROOT_CLASS +@interface ClassProps +@property(readonly,class) int prop1; +@property(readonly,class) int prop2; +@property(readonly) int prop2; +@property(readonly) int prop3; +@end +@implementation ClassProps ++(int)prop1 { return 0; } ++(int)prop2 { return 0; } +-(int)prop2 { return 0; } +-(int)prop3 { return 0; } +@end + static int isNamed(objc_property_t p, const char *name) { return (0 == strcmp(name, property_getName(p))); @@ -119,6 +133,35 @@ int main() testassert(!props); testassert(count == 0); + // Check class properties + + cls = objc_getClass("ClassProps"); + testassert(cls); + + count = 100; + props = class_copyPropertyList(cls, &count); + testassert(props); + testassert(count == 2); + testassert((isNamed(props[0], "prop2") && isNamed(props[1], "prop3")) || + (isNamed(props[1], "prop2") && isNamed(props[0], "prop3"))); + // props[] should be null-terminated + testassert(props[2] == NULL); + free(props); + + cls = object_getClass(objc_getClass("ClassProps")); + testassert(cls); + + count = 100; + props = class_copyPropertyList(cls, &count); + testassert(props); + testassert(count == 2); + testassert((isNamed(props[0], "prop1") && isNamed(props[1], "prop2")) || + (isNamed(props[1], "prop1") && isNamed(props[0], "prop2"))); + // props[] should be null-terminated + testassert(props[2] == NULL); + free(props); + + succeed(__FILE__); return 0; } diff --git a/test/createInstance.m b/test/createInstance.m index e53e20aa..c1c156c9 100644 --- a/test/createInstance.m +++ b/test/createInstance.m @@ -1,25 +1,20 @@ -// TEST_CONFIG MEM=mrc,gc -// TEST_CFLAGS -Wno-deprecated-declarations +// TEST_CONFIG #import #import -#ifndef OBJC_NO_GC -#include -#else -static BOOL auto_zone_is_valid_pointer(void *a, void *b) { return a||b; } -#endif #include "test.h" +#include "testroot.i" -OBJC_ROOT_CLASS -@interface Super { @public id isa; } @end -@implementation Super -+(void) initialize { } -+(Class) class { return self; } -@end +@interface Super : TestRoot @end +@implementation Super @end @interface Sub : Super { int array[128]; } @end @implementation Sub @end +#if __has_feature(objc_arc) +#define object_dispose(x) do {} while (0) +#endif + int main() { Super *s; @@ -27,37 +22,45 @@ int main() s = class_createInstance([Super class], 0); testassert(s); testassert(object_getClass(s) == [Super class]); - testassert(malloc_size(s) >= class_getInstanceSize([Super class])); - if (objc_collectingEnabled()) testassert(auto_zone_is_valid_pointer(objc_collectableZone(), s)); + testassert(malloc_size((__bridge const void *)s) >= class_getInstanceSize([Super class])); object_dispose(s); s = class_createInstance([Sub class], 0); testassert(s); testassert(object_getClass(s) == [Sub class]); - testassert(malloc_size(s) >= class_getInstanceSize([Sub class])); - if (objc_collectingEnabled()) testassert(auto_zone_is_valid_pointer(objc_collectableZone(), s)); + testassert(malloc_size((__bridge const void *)s) >= class_getInstanceSize([Sub class])); object_dispose(s); s = class_createInstance([Super class], 100); testassert(s); testassert(object_getClass(s) == [Super class]); - testassert(malloc_size(s) >= class_getInstanceSize([Super class]) + 100); - if (objc_collectingEnabled()) testassert(auto_zone_is_valid_pointer(objc_collectableZone(), s)); + testassert(malloc_size((__bridge const void *)s) >= class_getInstanceSize([Super class]) + 100); object_dispose(s); s = class_createInstance([Sub class], 100); testassert(s); testassert(object_getClass(s) == [Sub class]); - testassert(malloc_size(s) >= class_getInstanceSize([Sub class]) + 100); - if (objc_collectingEnabled()) testassert(auto_zone_is_valid_pointer(objc_collectableZone(), s)); + testassert(malloc_size((__bridge const void *)s) >= class_getInstanceSize([Sub class]) + 100); object_dispose(s); s = class_createInstance(Nil, 0); testassert(!s); + testassert(TestRootAlloc == 0); + +#if __has_feature(objc_arc) + // ARC version didn't use object_dispose() + // and should have called -dealloc on 4 objects + testassert(TestRootDealloc == 4); +#else + // MRC version used object_dispose() + // which doesn't call -dealloc + testassert(TestRootDealloc == 0); +#endif + succeed(__FILE__); } diff --git a/test/customrr-nsobject-awz.m b/test/customrr-nsobject-awz.m index b7887292..bda03165 100644 --- a/test/customrr-nsobject-awz.m +++ b/test/customrr-nsobject-awz.m @@ -4,12 +4,12 @@ TEST_ENV OBJC_PRINT_CUSTOM_RR=YES OBJC_PRINT_CUSTOM_AWZ=YES TEST_BUILD - $C{COMPILE} $DIR/customrr-nsobject.m -o customrr-nsobject-awz.out -DSWIZZLE_AWZ=1 + $C{COMPILE} $DIR/customrr-nsobject.m -o customrr-nsobject-awz.exe -DSWIZZLE_AWZ=1 END TEST_RUN_OUTPUT objc\[\d+\]: CUSTOM AWZ: NSObject \(meta\) -OK: customrr-nsobject-awz.out +OK: customrr-nsobject-awz.exe END */ diff --git a/test/customrr-nsobject-none.m b/test/customrr-nsobject-none.m index fb20f240..60bb6cee 100644 --- a/test/customrr-nsobject-none.m +++ b/test/customrr-nsobject-none.m @@ -4,11 +4,11 @@ TEST_ENV OBJC_PRINT_CUSTOM_RR=YES OBJC_PRINT_CUSTOM_AWZ=YES TEST_BUILD - $C{COMPILE} $DIR/customrr-nsobject.m -o customrr-nsobject-none.out + $C{COMPILE} $DIR/customrr-nsobject.m -o customrr-nsobject-none.exe END TEST_RUN_OUTPUT -OK: customrr-nsobject-none.out +OK: customrr-nsobject-none.exe END */ diff --git a/test/customrr-nsobject-rr.m b/test/customrr-nsobject-rr.m index 859d155a..94418f89 100644 --- a/test/customrr-nsobject-rr.m +++ b/test/customrr-nsobject-rr.m @@ -4,12 +4,12 @@ TEST_ENV OBJC_PRINT_CUSTOM_RR=YES OBJC_PRINT_CUSTOM_AWZ=YES TEST_BUILD - $C{COMPILE} $DIR/customrr-nsobject.m -o customrr-nsobject-rr.out -DSWIZZLE_RELEASE=1 + $C{COMPILE} $DIR/customrr-nsobject.m -o customrr-nsobject-rr.exe -DSWIZZLE_RELEASE=1 END TEST_RUN_OUTPUT objc\[\d+\]: CUSTOM RR: NSObject -OK: customrr-nsobject-rr.out +OK: customrr-nsobject-rr.exe END */ diff --git a/test/customrr-nsobject-rrawz.m b/test/customrr-nsobject-rrawz.m index b0e546c1..413d9739 100644 --- a/test/customrr-nsobject-rrawz.m +++ b/test/customrr-nsobject-rrawz.m @@ -4,13 +4,13 @@ TEST_ENV OBJC_PRINT_CUSTOM_RR=YES OBJC_PRINT_CUSTOM_AWZ=YES TEST_BUILD - $C{COMPILE} $DIR/customrr-nsobject.m -o customrr-nsobject-rrawz.out -DSWIZZLE_RELEASE=1 -DSWIZZLE_AWZ=1 + $C{COMPILE} $DIR/customrr-nsobject.m -o customrr-nsobject-rrawz.exe -DSWIZZLE_RELEASE=1 -DSWIZZLE_AWZ=1 END TEST_RUN_OUTPUT objc\[\d+\]: CUSTOM AWZ: NSObject \(meta\) objc\[\d+\]: CUSTOM RR: NSObject -OK: customrr-nsobject-rrawz.out +OK: customrr-nsobject-rrawz.exe END */ diff --git a/test/customrr-nsobject.m b/test/customrr-nsobject.m index d055f705..57e5b5a0 100644 --- a/test/customrr-nsobject.m +++ b/test/customrr-nsobject.m @@ -3,11 +3,10 @@ #include "test.h" #include -#if __OBJC2__ -# define BYPASS 1 +#if __has_feature(ptrauth_calls) +typedef IMP __ptrauth_objc_method_list_imp MethodListIMP; #else -// old ABI does not implement the optimization -# define BYPASS 0 +typedef IMP MethodListIMP; #endif static int Retains; @@ -16,6 +15,7 @@ static int PlusInitializes; static int Allocs; static int AllocWithZones; +static int Inits; id (*RealRetain)(id self, SEL _cmd); void (*RealRelease)(id self, SEL _cmd); @@ -32,6 +32,8 @@ void HackPlusInitialize(id self __unused, SEL _cmd __unused) { PlusInitializes++; } +id HackInit(id self, SEL _cmd __unused) { Inits++; return self; } + int main(int argc __unused, char **argv) { @@ -51,7 +53,7 @@ int main(int argc __unused, char **argv) #if SWIZZLE_AWZ method_setImplementation(meth, (IMP)HackAllocWithZone); #else - ((IMP *)meth)[2] = (IMP)HackAllocWithZone; + ((MethodListIMP *)meth)[2] = (IMP)HackAllocWithZone; #endif meth = class_getInstanceMethod(cls, @selector(release)); @@ -59,90 +61,111 @@ int main(int argc __unused, char **argv) #if SWIZZLE_RELEASE method_setImplementation(meth, (IMP)HackRelease); #else - ((IMP *)meth)[2] = (IMP)HackRelease; + ((MethodListIMP *)meth)[2] = (IMP)HackRelease; #endif // These other methods get hacked for counting purposes only meth = class_getInstanceMethod(cls, @selector(retain)); RealRetain = (typeof(RealRetain))method_getImplementation(meth); - ((IMP *)meth)[2] = (IMP)HackRetain; + ((MethodListIMP *)meth)[2] = (IMP)HackRetain; meth = class_getInstanceMethod(cls, @selector(autorelease)); RealAutorelease = (typeof(RealAutorelease))method_getImplementation(meth); - ((IMP *)meth)[2] = (IMP)HackAutorelease; + ((MethodListIMP *)meth)[2] = (IMP)HackAutorelease; meth = class_getClassMethod(cls, @selector(alloc)); RealAlloc = (typeof(RealAlloc))method_getImplementation(meth); - ((IMP *)meth)[2] = (IMP)HackAlloc; + ((MethodListIMP *)meth)[2] = (IMP)HackAlloc; + + meth = class_getInstanceMethod(cls, @selector(init)); + ((MethodListIMP *)meth)[2] = (IMP)HackInit; // Verify that the swizzles occurred before +initialize by provoking it now testassert(PlusInitializes == 0); [NSObject self]; testassert(PlusInitializes == 1); -#if !__OBJC2__ - // hack: fool the expected output because old ABI doesn't optimize this -# if SWIZZLE_AWZ - fprintf(stderr, "objc[1234]: CUSTOM AWZ: NSObject (meta)\n"); -# endif -# if SWIZZLE_RELEASE - fprintf(stderr, "objc[1234]: CUSTOM RR: NSObject\n"); -# endif -#endif - id obj; + id result; Allocs = 0; AllocWithZones = 0; + Inits = 0; obj = objc_alloc(cls); -#if SWIZZLE_AWZ || !BYPASS +#if SWIZZLE_AWZ testprintf("swizzled AWZ should be called\n"); testassert(Allocs == 1); testassert(AllocWithZones == 1); + testassert(Inits == 0); #else testprintf("unswizzled AWZ should be bypassed\n"); testassert(Allocs == 0); testassert(AllocWithZones == 0); + testassert(Inits == 0); #endif + testassert([obj isKindOfClass:[NSObject class]]); Allocs = 0; AllocWithZones = 0; + Inits = 0; obj = [NSObject alloc]; -#if SWIZZLE_AWZ || !BYPASS +#if SWIZZLE_AWZ testprintf("swizzled AWZ should be called\n"); testassert(Allocs == 1); testassert(AllocWithZones == 1); + testassert(Inits == 0); #else testprintf("unswizzled AWZ should be bypassed\n"); testassert(Allocs == 1); testassert(AllocWithZones == 0); + testassert(Inits == 0); #endif + testassert([obj isKindOfClass:[NSObject class]]); + + Allocs = 0; + AllocWithZones = 0; + Inits = 0; + obj = objc_alloc_init(cls); +#if SWIZZLE_AWZ + testprintf("swizzled AWZ should be called\n"); + testassert(Allocs == 1); + testassert(AllocWithZones == 1); + testassert(Inits == 1); +#else + testprintf("unswizzled AWZ should be bypassed\n"); + testassert(Allocs == 0); + testassert(AllocWithZones == 0); + testassert(Inits == 1); // swizzled init is still called +#endif + testassert([obj isKindOfClass:[NSObject class]]); Retains = 0; - objc_retain(obj); -#if SWIZZLE_RELEASE || !BYPASS + result = objc_retain(obj); +#if SWIZZLE_RELEASE testprintf("swizzled release should force retain\n"); testassert(Retains == 1); #else testprintf("unswizzled release should bypass retain\n"); testassert(Retains == 0); #endif + testassert(result == obj); Releases = 0; Autoreleases = 0; PUSH_POOL { - objc_autorelease(obj); -#if SWIZZLE_RELEASE || !BYPASS + result = objc_autorelease(obj); +#if SWIZZLE_RELEASE testprintf("swizzled release should force autorelease\n"); testassert(Autoreleases == 1); #else testprintf("unswizzled release should bypass autorelease\n"); testassert(Autoreleases == 0); #endif + testassert(result == obj); } POP_POOL -#if SWIZZLE_RELEASE || !BYPASS +#if SWIZZLE_RELEASE testprintf("swizzled release should be called\n"); testassert(Releases == 1); #else diff --git a/test/customrr.m b/test/customrr.m index 7fa21ae1..8ad8a475 100644 --- a/test/customrr.m +++ b/test/customrr.m @@ -2,9 +2,9 @@ // TEST_CONFIG MEM=mrc /* TEST_BUILD - $C{COMPILE} $DIR/customrr.m -fvisibility=default -o customrr.out - $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/customrr-cat1.m -o customrr-cat1.dylib - $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/customrr-cat2.m -o customrr-cat2.dylib + $C{COMPILE} $DIR/customrr.m -fvisibility=default -o customrr.exe + $C{COMPILE} -bundle -bundle_loader customrr.exe $DIR/customrr-cat1.m -o customrr-cat1.bundle + $C{COMPILE} -bundle -bundle_loader customrr.exe $DIR/customrr-cat2.m -o customrr-cat2.bundle END */ @@ -13,20 +13,6 @@ #include #include -#if !__OBJC2__ - -// pacify exported symbols list -OBJC_ROOT_CLASS -@interface InheritingSubCat @end -@implementation InheritingSubCat @end - -int main(int argc __unused, char **argv) -{ - succeed(basename(argv[0])); -} - -#else - static int Retains; static int Releases; static int Autoreleases; @@ -205,32 +191,37 @@ int main(int argc __unused, char **argv) // Don't use runtime functions to do this - // we want the runtime to think that these are NSObject's real code { +#if __has_feature(ptrauth_calls) + typedef IMP __ptrauth_objc_method_list_imp MethodListIMP; +#else + typedef IMP MethodListIMP; +#endif + Class cls = [NSObject class]; - IMP *m; - IMP imp; - imp = class_getMethodImplementation(cls, @selector(retain)); - m = (IMP *)class_getInstanceMethod(cls, @selector(retain)); + IMP imp = class_getMethodImplementation(cls, @selector(retain)); + MethodListIMP *m = (MethodListIMP *) + class_getInstanceMethod(cls, @selector(retain)); testassert(m[2] == imp); // verify Method struct is as we expect - m = (IMP *)class_getInstanceMethod(cls, @selector(retain)); + m = (MethodListIMP *)class_getInstanceMethod(cls, @selector(retain)); m[2] = (IMP)HackRetain; - m = (IMP *)class_getInstanceMethod(cls, @selector(release)); + m = (MethodListIMP *)class_getInstanceMethod(cls, @selector(release)); m[2] = (IMP)HackRelease; - m = (IMP *)class_getInstanceMethod(cls, @selector(autorelease)); + m = (MethodListIMP *)class_getInstanceMethod(cls, @selector(autorelease)); m[2] = (IMP)HackAutorelease; - m = (IMP *)class_getInstanceMethod(cls, @selector(retainCount)); + m = (MethodListIMP *)class_getInstanceMethod(cls, @selector(retainCount)); m[2] = (IMP)HackRetainCount; - m = (IMP *)class_getClassMethod(cls, @selector(retain)); + m = (MethodListIMP *)class_getClassMethod(cls, @selector(retain)); m[2] = (IMP)HackPlusRetain; - m = (IMP *)class_getClassMethod(cls, @selector(release)); + m = (MethodListIMP *)class_getClassMethod(cls, @selector(release)); m[2] = (IMP)HackPlusRelease; - m = (IMP *)class_getClassMethod(cls, @selector(autorelease)); + m = (MethodListIMP *)class_getClassMethod(cls, @selector(autorelease)); m[2] = (IMP)HackPlusAutorelease; - m = (IMP *)class_getClassMethod(cls, @selector(retainCount)); + m = (MethodListIMP *)class_getClassMethod(cls, @selector(retainCount)); m[2] = (IMP)HackPlusRetainCount; - m = (IMP *)class_getClassMethod(cls, @selector(alloc)); + m = (MethodListIMP *)class_getClassMethod(cls, @selector(alloc)); m[2] = (IMP)HackAlloc; - m = (IMP *)class_getClassMethod(cls, @selector(allocWithZone:)); + m = (MethodListIMP *)class_getClassMethod(cls, @selector(allocWithZone:)); m[2] = (IMP)HackAllocWithZone; _objc_flush_caches(cls); @@ -365,14 +356,12 @@ int main(int argc __unused, char **argv) autorelease_fn(ocl, @selector(autorelease)); testassert(SubPlusAutoreleases == 1); -#if __OBJC2__ retain_fn((Class)&OBJC_CLASS_$_UnrealizedSubB1, @selector(retain)); testassert(PlusRetains == 3); release_fn((Class)&OBJC_CLASS_$_UnrealizedSubB2, @selector(release)); testassert(PlusReleases == 3); autorelease_fn((Class)&OBJC_CLASS_$_UnrealizedSubB3, @selector(autorelease)); testassert(PlusAutoreleases == 3); -#endif testprintf("arc function bypasses instance but not class or override\n"); @@ -420,14 +409,12 @@ int main(int argc __unused, char **argv) objc_autorelease(ocl); testassert(SubPlusAutoreleases == 1); -#if __OBJC2__ objc_retain((Class)&OBJC_CLASS_$_UnrealizedSubC1); testassert(PlusRetains == 3); objc_release((Class)&OBJC_CLASS_$_UnrealizedSubC2); testassert(PlusReleases == 3); objc_autorelease((Class)&OBJC_CLASS_$_UnrealizedSubC3); testassert(PlusAutoreleases == 3); -#endif testprintf("unrelated addMethod does not clobber\n"); @@ -727,7 +714,7 @@ int main(int argc __unused, char **argv) objc_autorelease(oo2); testassert(Autoreleases == 0); - dlh = dlopen("customrr-cat1.dylib", RTLD_LAZY); + dlh = dlopen("customrr-cat1.bundle", RTLD_LAZY); testassert(dlh); objc_retain(ooo); @@ -767,7 +754,7 @@ int main(int argc __unused, char **argv) objc_autorelease(oo2); testassert(Autoreleases == 0); - dlh = dlopen("customrr-cat2.dylib", RTLD_LAZY); + dlh = dlopen("customrr-cat2.bundle", RTLD_LAZY); testassert(dlh); objc_retain(ooo); @@ -900,5 +887,3 @@ int main(int argc __unused, char **argv) succeed(basename(argv[0])); } - -#endif diff --git a/test/customrr2.m b/test/customrr2.m index cd49f38d..935aae63 100644 --- a/test/customrr2.m +++ b/test/customrr2.m @@ -2,8 +2,8 @@ // TEST_CONFIG MEM=mrc /* TEST_BUILD - $C{COMPILE} $DIR/customrr.m -fvisibility=default -o customrr2.out -DTEST_EXCHANGEIMPLEMENTATIONS=1 - $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/customrr-cat1.m -o customrr-cat1.dylib - $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/customrr-cat2.m -o customrr-cat2.dylib + $C{COMPILE} $DIR/customrr.m -fvisibility=default -o customrr2.exe -DTEST_EXCHANGEIMPLEMENTATIONS=1 + $C{COMPILE} -bundle -bundle_loader customrr2.exe $DIR/customrr-cat1.m -o customrr-cat1.bundle + $C{COMPILE} -bundle -bundle_loader customrr2.exe $DIR/customrr-cat2.m -o customrr-cat2.bundle END */ diff --git a/test/definitions.c b/test/definitions.c index c3512343..570abb07 100644 --- a/test/definitions.c +++ b/test/definitions.c @@ -13,20 +13,18 @@ BOOL b2 = NO; __strong void *p; #endif id __unsafe_unretained u; +#if __has_feature(objc_arc_weak) id __weak w; +#endif void fn(void) __unused; void fn(void) { id __autoreleasing a __unused; } -#if __llvm__ && !__clang__ -// llvm-gcc defines _NSConcreteGlobalBlock wrong -#else -// rdar://10118972 wrong type inference for blocks returning YES and NO +// check type inference for blocks returning YES and NO (rdar://10118972) BOOL (^block1)(void) = ^{ return YES; }; BOOL (^block2)(void) = ^{ return NO; }; -#endif #include "test.h" diff --git a/test/duplicateClass.m b/test/duplicateClass.m index 0ac2671d..7c44f072 100644 --- a/test/duplicateClass.m +++ b/test/duplicateClass.m @@ -3,10 +3,6 @@ #include "test.h" #include "testroot.i" #include -#ifndef OBJC_NO_GC -#include -#include -#endif static int state; @@ -66,13 +62,6 @@ int main() cls = [Super class]; clone = objc_duplicateClass(cls, "Super_copy", 0); -#ifndef OBJC_NO_GC - if (objc_collectingEnabled()) { - testassert(auto_zone_size(objc_collectableZone(), objc_unretainedPointer(clone))); - // objc_duplicateClass() doesn't duplicate the metaclass - // no: testassert(auto_zone_size(objc_collectableZone(), clone->isa)); - } -#endif testassert(clone != cls); testassert(object_getClass(clone) == object_getClass(cls)); @@ -81,9 +70,6 @@ int main() testassert(class_isMetaClass(clone) == class_isMetaClass(cls)); testassert(class_getIvarLayout(clone) == class_getIvarLayout(cls)); testassert(class_getWeakIvarLayout(clone) == class_getWeakIvarLayout(cls)); -#if !__OBJC2__ - testassert((clone->info & (CLS_CLASS|CLS_META)) == (cls->info & (CLS_CLASS|CLS_META))); -#endif // Check method list diff --git a/test/duplicatedClasses.m b/test/duplicatedClasses.m index 991b1338..f5acb89f 100644 --- a/test/duplicatedClasses.m +++ b/test/duplicatedClasses.m @@ -1,9 +1,12 @@ -// TEST_ENV OBJC_DEBUG_DUPLICATE_CLASSES=YES +// fixme rdar://24624435 duplicate class warning fails with the shared cache +// OBJC_DISABLE_PREOPTIMIZATION=YES works around that problem. + +// TEST_ENV OBJC_DEBUG_DUPLICATE_CLASSES=YES OBJC_DISABLE_PREOPTIMIZATION=YES // TEST_CRASHES /* TEST_RUN_OUTPUT -objc\[\d+\]: Class GKScore is implemented in both [^\s]+ and [^\s]+ One of the two will be used. Which one is undefined. -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: Class [^\s]+ is implemented in both .+ \(0x[0-9a-f]+\) and .+ \(0x[0-9a-f]+\)\. One of the two will be used\. Which one is undefined\. +objc\[\d+\]: HALTED OR OK: duplicatedClasses.m END @@ -12,16 +15,12 @@ #include "test.h" #include "testroot.i" -@interface GKScore : TestRoot @end -@implementation GKScore @end +@interface WKWebView : TestRoot @end +@implementation WKWebView @end int main() { - if (objc_collectingEnabled()) { - testwarn("rdar://19042235 test disabled because GameKit is not GC"); - succeed(__FILE__); - } - void *dl = dlopen("/System/Library/Frameworks/GameKit.framework/GameKit", RTLD_LAZY); - if (!dl) fail("couldn't open GameKit"); + void *dl = dlopen("/System/Library/Frameworks/WebKit.framework/WebKit", RTLD_LAZY); + if (!dl) fail("couldn't open WebKit"); fail("should have crashed already"); } diff --git a/test/evil-category-0.m b/test/evil-category-0.m index a0d6060e..a7cc36b7 100644 --- a/test/evil-category-0.m +++ b/test/evil-category-0.m @@ -3,7 +3,7 @@ TEST_BUILD $C{COMPILE} $DIR/evil-category-0.m -dynamiclib -o libevil.dylib - $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-category-0.out + $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-category-0.exe END */ diff --git a/test/evil-category-00.m b/test/evil-category-00.m index 4bc050d5..0b1e8423 100644 --- a/test/evil-category-00.m +++ b/test/evil-category-00.m @@ -5,11 +5,11 @@ TEST_CRASHES TEST_BUILD - $C{COMPILE} $DIR/evil-category-00.m $DIR/evil-main.m -o evil-category-00.out + $C{COMPILE} $DIR/evil-category-00.m $DIR/evil-main.m -o evil-category-00.exe END TEST_RUN_OUTPUT -CRASHED: SIGSEGV +CRASHED: SIGABRT END */ diff --git a/test/evil-category-000.m b/test/evil-category-000.m index e451706b..9e599f99 100644 --- a/test/evil-category-000.m +++ b/test/evil-category-000.m @@ -3,7 +3,7 @@ TEST_BUILD $C{COMPILE} $DIR/evil-category-000.m -dynamiclib -o libevil.dylib - $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-category-000.out + $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-category-000.exe END */ diff --git a/test/evil-category-1.m b/test/evil-category-1.m index 7a84f840..7907a888 100644 --- a/test/evil-category-1.m +++ b/test/evil-category-1.m @@ -6,12 +6,12 @@ TEST_BUILD $C{COMPILE} $DIR/evil-category-1.m -dynamiclib -o libevil.dylib - $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-1.out + $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-1.exe END TEST_RUN_OUTPUT objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\) -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: HALTED END */ diff --git a/test/evil-category-2.m b/test/evil-category-2.m index 65f7e3b0..c7194024 100644 --- a/test/evil-category-2.m +++ b/test/evil-category-2.m @@ -6,12 +6,12 @@ TEST_BUILD $C{COMPILE} $DIR/evil-category-2.m -dynamiclib -o libevil.dylib - $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-2.out + $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-2.exe END TEST_RUN_OUTPUT objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\) -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: HALTED END */ diff --git a/test/evil-category-3.m b/test/evil-category-3.m index 6be6d0b0..3a7b5104 100644 --- a/test/evil-category-3.m +++ b/test/evil-category-3.m @@ -6,12 +6,12 @@ TEST_BUILD $C{COMPILE} $DIR/evil-category-3.m -dynamiclib -o libevil.dylib - $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-3.out + $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-3.exe END TEST_RUN_OUTPUT objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\) -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: HALTED END */ diff --git a/test/evil-category-4.m b/test/evil-category-4.m index 4f0a9eb2..12c10fa7 100644 --- a/test/evil-category-4.m +++ b/test/evil-category-4.m @@ -6,12 +6,12 @@ TEST_BUILD $C{COMPILE} $DIR/evil-category-4.m -dynamiclib -o libevil.dylib - $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-4.out + $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-4.exe END TEST_RUN_OUTPUT objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\) -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: HALTED END */ diff --git a/test/evil-category-def.m b/test/evil-category-def.m index a4bd1dcc..6d0f1e00 100644 --- a/test/evil-category-def.m +++ b/test/evil-category-def.m @@ -1,7 +1,4 @@ - -#if __OBJC2__ - -#include +#include #if __LP64__ # define PTR " .quad " @@ -9,6 +6,12 @@ # define PTR " .long " #endif +#if __has_feature(ptrauth_calls) +# define SIGNED_METHOD_LIST_IMP "@AUTH(ia,0,addr) " +#else +# define SIGNED_METHOD_LIST_IMP +#endif + #define str(x) #x #define str2(x) str(x) @@ -40,14 +43,15 @@ void nop(void) { } ".long 1 \n" PTR "L_load \n" PTR "L_load \n" - PTR str2(SHARED_REGION_BASE+SHARED_REGION_SIZE-PAGE_MAX_SIZE) " \n" + PTR "_abort" SIGNED_METHOD_LIST_IMP "\n" + // assumes that abort is inside the dyld shared cache "L_good_methods: \n" ".long 24 \n" ".long 1 \n" PTR "L_load \n" PTR "L_load \n" - PTR "_nop \n" + PTR "_nop" SIGNED_METHOD_LIST_IMP "\n" ".cstring \n" "L_cat_name: .ascii \"Evil\\0\" \n" @@ -66,7 +70,4 @@ PTR str2(SHARED_REGION_BASE+SHARED_REGION_SIZE-PAGE_MAX_SIZE) " \n" ".text \n" ); -// __OBJC2__ -#endif - void fn(void) { } diff --git a/test/evil-class-0.m b/test/evil-class-0.m index 7d35ad80..cd71806c 100644 --- a/test/evil-class-0.m +++ b/test/evil-class-0.m @@ -3,7 +3,7 @@ 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 + $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-class-0.exe END */ diff --git a/test/evil-class-00.m b/test/evil-class-00.m index 335675e9..1b394071 100644 --- a/test/evil-class-00.m +++ b/test/evil-class-00.m @@ -5,11 +5,11 @@ TEST_CRASHES TEST_BUILD - $C{COMPILE} $DIR/evil-class-00.m $DIR/evil-main.m -o evil-class-00.out + $C{COMPILE} $DIR/evil-class-00.m $DIR/evil-main.m -o evil-class-00.exe END TEST_RUN_OUTPUT -CRASHED: SIGSEGV +CRASHED: SIGABRT END */ diff --git a/test/evil-class-000.m b/test/evil-class-000.m index 003d4d47..fbf1ce48 100644 --- a/test/evil-class-000.m +++ b/test/evil-class-000.m @@ -3,7 +3,7 @@ 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 + $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-class-000.exe END */ diff --git a/test/evil-class-1.m b/test/evil-class-1.m index 9266a79e..f8785ee2 100644 --- a/test/evil-class-1.m +++ b/test/evil-class-1.m @@ -6,12 +6,12 @@ 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 + $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-1.exe END TEST_RUN_OUTPUT objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\) -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: HALTED END */ diff --git a/test/evil-class-2.m b/test/evil-class-2.m index 47aa3e29..9175eb3e 100644 --- a/test/evil-class-2.m +++ b/test/evil-class-2.m @@ -6,12 +6,12 @@ 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 + $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-2.exe END TEST_RUN_OUTPUT objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\) -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: HALTED END */ diff --git a/test/evil-class-3.m b/test/evil-class-3.m index 1d2e2137..c068b342 100644 --- a/test/evil-class-3.m +++ b/test/evil-class-3.m @@ -6,12 +6,12 @@ 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 + $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-3.exe END TEST_RUN_OUTPUT objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\) -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: HALTED END */ diff --git a/test/evil-class-4.m b/test/evil-class-4.m index 8aef1f59..5189da80 100644 --- a/test/evil-class-4.m +++ b/test/evil-class-4.m @@ -6,12 +6,12 @@ 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 + $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-4.exe END TEST_RUN_OUTPUT objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\) -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: HALTED END */ diff --git a/test/evil-class-5.m b/test/evil-class-5.m index d8146f6e..9a78ab4d 100644 --- a/test/evil-class-5.m +++ b/test/evil-class-5.m @@ -8,12 +8,12 @@ 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 + $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-5.exe END TEST_RUN_OUTPUT objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\) -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: HALTED END */ diff --git a/test/evil-class-def.m b/test/evil-class-def.m index 25ec45a2..6c9f25c8 100644 --- a/test/evil-class-def.m +++ b/test/evil-class-def.m @@ -1,6 +1,4 @@ -#if __OBJC2__ - -#include +#include #if __LP64__ # define PTR " .quad " @@ -12,6 +10,12 @@ # define LOGPTRSIZE "2" #endif +#if __has_feature(ptrauth_calls) +# define SIGNED_METHOD_LIST_IMP "@AUTH(ia,0,addr) " +#else +# define SIGNED_METHOD_LIST_IMP +#endif + #define str(x) #x #define str2(x) str(x) @@ -248,17 +252,18 @@ ".long 1 \n" PTR "L_load \n" PTR "L_load \n" - PTR str2(SHARED_REGION_BASE+SHARED_REGION_SIZE-PAGE_MAX_SIZE) " \n" + PTR "_abort" SIGNED_METHOD_LIST_IMP "\n" + // assumes that abort is inside the dyld shared cache "L_good_methods: \n" ".long 3*"PTRSIZE" \n" ".long 2 \n" PTR "L_load \n" PTR "L_load \n" - PTR "_nop \n" + PTR "_nop" SIGNED_METHOD_LIST_IMP "\n" PTR "L_self \n" PTR "L_self \n" - PTR "_nop \n" + PTR "_nop" SIGNED_METHOD_LIST_IMP "\n" "L_super_ivars: \n" ".long 4*"PTRSIZE" \n" @@ -313,7 +318,4 @@ PTR str2(SHARED_REGION_BASE+SHARED_REGION_SIZE-PAGE_MAX_SIZE) " \n" ".text \n" ); -// __OBJC2__ -#endif - void fn(void) { } diff --git a/test/evil-main.m b/test/evil-main.m index aa6a124c..1e17fb86 100644 --- a/test/evil-main.m +++ b/test/evil-main.m @@ -6,7 +6,7 @@ int main(int argc __unused, char **argv) { fn(); -#if TARGET_OS_EMBEDDED && !defined(NOT_EVIL) +#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR && !defined(NOT_EVIL) #pragma unused (argv) fail("All that is necessary for the triumph of evil is that good men do nothing."); #else diff --git a/test/exc.m b/test/exc.m index 4f25b75b..8c7435aa 100644 --- a/test/exc.m +++ b/test/exc.m @@ -1,20 +1,6 @@ /* need exception-safe ARC for exception deallocation tests -need F/CF for testonthread() in GC mode TEST_CFLAGS -fobjc-arc-exceptions -framework Foundation - -llvm-gcc unavoidably warns about our deliberately out-of-order handlers - -TEST_BUILD_OUTPUT -.*exc.m: In function .* -.*exc.m:\d+: warning: exception of type .* will be caught -.*exc.m:\d+: warning: by earlier handler for .* -.*exc.m:\d+: warning: exception of type .* will be caught -.*exc.m:\d+: warning: by earlier handler for .* -.*exc.m:\d+: warning: exception of type .* will be caught -.*exc.m:\d+: warning: by earlier handler for .* -OR -END */ #include "test.h" @@ -36,7 +22,6 @@ +(id)exception { return AUTORELEASE([[self alloc] initWithName:@"Super" reason:@ -(void)check { state++; } +(void)check { testassert(!"caught class object, not instance"); } -(void)dealloc { dealloced++; SUPER_DEALLOC(); } --(void)finalize { dealloced++; [super finalize]; } @end #define FILENAME "nsexc.m" @@ -49,7 +34,6 @@ +(id)exception { return AUTORELEASE([self new]); } -(void)check { state++; } +(void)check { testassert(!"caught class object, not instance"); } -(void)dealloc { dealloced++; SUPER_DEALLOC(); } --(void)finalize { dealloced++; [super finalize]; } @end #define FILENAME "exc.m" @@ -61,7 +45,7 @@ @implementation Sub @end -#if __OBJC2__ && !TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE +#if TARGET_OS_OSX void altHandlerFail(id unused __unused, void *context __unused) { fail("altHandlerFail called"); @@ -84,7 +68,7 @@ void altHandlerFail(id unused __unused, void *context __unused) ALT_HANDLER(7) -static void throwWithAltHandler(void) __attribute__((noinline)); +static void throwWithAltHandler(void) __attribute__((noinline, used)); static void throwWithAltHandler(void) { @try { @@ -102,7 +86,7 @@ static void throwWithAltHandler(void) } -static void throwWithAltHandlerAndRethrow(void) __attribute__((noinline)); +static void throwWithAltHandlerAndRethrow(void) __attribute__((noinline, used)); static void throwWithAltHandlerAndRethrow(void) { @try { @@ -123,7 +107,7 @@ static void throwWithAltHandlerAndRethrow(void) #endif -#if __cplusplus && __OBJC2__ +#if __cplusplus #include void terminator() { succeed(FILENAME); @@ -219,7 +203,6 @@ int main() testassert(dealloced == 1); -#if __OBJC2__ testprintf("try-finally, with autorelease pool pop during unwind\n"); // Popping an autorelease pool during unwind used to deallocate the // exception object, but now we retain them while in flight. @@ -256,7 +239,6 @@ int main() }); testassert(state == 5); testassert(dealloced == 1); -#endif testprintf("try-catch-finally, no exception\n"); @@ -587,7 +569,7 @@ int main() testassert(dealloced == 1); -#if __cplusplus && __OBJC2__ +#if __cplusplus testprintf("C++ try/catch, Objective-C exception superclass\n"); TEST({ @@ -672,9 +654,8 @@ int main() #endif -#if !__OBJC2__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE - // alt handlers for modern Mac OS only - +#if !TARGET_OS_OSX + // alt handlers are for macOS only #else { // alt handlers @@ -740,7 +721,14 @@ int main() testprintf("alt handler, nested\n"); - +#if 1 + testwarn("fixme compiler no longer cooperative for local nested?"); + // Nested alt handlers inside the same function require that each + // catch group have its own landing pad descriptor. The compiler is + // probably not doing that anymore. For now we assume that the + // combination of nested exception handlers and alt handlers is + // rare enough that nobody cares. +#else TEST({ dealloced = 0; for (int i = 0; i < ALT_HANDLER_REPEAT; i++) { @@ -773,10 +761,13 @@ int main() } }); testassert(dealloced == ALT_HANDLER_REPEAT); - +#endif testprintf("alt handler, nested, rethrows in between\n"); - +#if 1 + testwarn("fixme compiler no longer cooperative for local nested?"); + // See above. +#else TEST({ dealloced = 0; for (int i = 0; i < ALT_HANDLER_REPEAT; i++) { @@ -811,7 +802,7 @@ int main() } }); testassert(dealloced == ALT_HANDLER_REPEAT); - +#endif testprintf("alt handler, exception thrown and caught inside\n"); @@ -877,7 +868,7 @@ int main() // alt handlers #endif -#if __cplusplus && __OBJC2__ +#if __cplusplus std::set_terminate(terminator); objc_terminate(); fail("should not have returned from objc_terminate()"); diff --git a/test/exchangeImp.m b/test/exchangeImp.m index 12f81e4d..da84f94b 100644 --- a/test/exchangeImp.m +++ b/test/exchangeImp.m @@ -1,4 +1,23 @@ -// TEST_CONFIG +/* +TEST_BUILD_OUTPUT +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +.*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')? +END +*/ #include "test.h" #include "testroot.i" diff --git a/test/fork.m b/test/fork.m new file mode 100644 index 00000000..f3677df7 --- /dev/null +++ b/test/fork.m @@ -0,0 +1,54 @@ +// TEST_CONFIG + +#include "test.h" + +void *flushthread(void *arg __unused) +{ + while (1) { + _objc_flush_caches(nil); + } +} + +int main() +{ + pthread_t th; + pthread_create(&th, nil, &flushthread, nil); + + alarm(120); + + [NSObject self]; + [NSObject self]; + + int max = is_guardmalloc() ? 10: 100; + + for (int i = 0; i < max; i++) { + pid_t child; + switch ((child = fork())) { + case -1: + abort(); + case 0: + // child + alarm(10); + [NSObject self]; + _exit(0); + default: { + // parent + int result = 0; + while (waitpid(child, &result, 0) < 0) { + if (errno != EINTR) { + fail("waitpid failed (errno %d %s)", + errno, strerror(errno)); + } + } + if (!WIFEXITED(result)) { + fail("child crashed (waitpid result %d)", result); + } + + [NSObject self]; + break; + } + } + } + + succeed(__FILE__ " parent"); +} diff --git a/test/forkInitialize.m b/test/forkInitialize.m new file mode 100644 index 00000000..b9e9a3aa --- /dev/null +++ b/test/forkInitialize.m @@ -0,0 +1,159 @@ +/* +TEST_CRASHES +TEST_RUN_OUTPUT +objc\[\d+\]: \+\[BlockingSub initialize\] may have been in progress in another thread when fork\(\) was called\. +objc\[\d+\]: \+\[BlockingSub initialize\] may have been in progress in another thread when fork\(\) was called\. We cannot safely call it or ignore it in the fork\(\) child process\. Crashing instead\. Set a breakpoint on objc_initializeAfterForkError to debug\. +objc\[\d+\]: HALTED +OK: forkInitialize\.m +END +*/ + +#include "test.h" + +static void *retain_fn(void *self, SEL _cmd __unused) { return self; } +static void release_fn(void *self __unused, SEL _cmd __unused) { } + +OBJC_ROOT_CLASS +@interface BlockingRootClass @end +@implementation BlockingRootClass ++(id)self { return self; } ++(void)initialize { + class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, ""); + class_addMethod(self, sel_registerName("release"), (IMP)release_fn, ""); + + if (self == [BlockingRootClass self]) { + while (1) sleep(1); + } +} +@end + +@interface BlockingRootSub : BlockingRootClass @end +@implementation BlockingRootSub +@end + +OBJC_ROOT_CLASS +@interface BlockingSubRoot @end +@implementation BlockingSubRoot ++(id)self { return self; } ++(void)initialize { + class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, ""); + class_addMethod(self, sel_registerName("release"), (IMP)release_fn, ""); +} +@end + +@interface BlockingSub : BlockingSubRoot @end +@implementation BlockingSub ++(void)initialize { + class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, ""); + class_addMethod(self, sel_registerName("release"), (IMP)release_fn, ""); + + while (1) sleep(1); +} +@end + +OBJC_ROOT_CLASS +@interface AnotherRootClass @end + +@interface BoringSub : AnotherRootClass @end +@implementation BoringSub +// can't implement +initialize here +@end + +@implementation AnotherRootClass + +void doFork() +{ + testprintf("FORK\n"); + + pid_t child; + switch((child = fork())) { + case -1: + fail("fork failed"); + case 0: + // child + // This one succeeds even though we're nested inside it's + // superclass's +initialize, because ordinary +initialize nesting + // still works across fork(). + // This falls in the isInitializing() case in _class_initialize. + [BoringSub self]; + +#if !SINGLETHREADED + // This one succeeds even though another thread is in its + // superclass's +initialize, because that superclass is a root class + // so we assume that +initialize is empty and therefore this one + // is safe to call. + // This falls in the reallyInitialize case in _class_initialize. + [BlockingRootSub self]; + + // This one aborts without deadlocking because it was in progress + // when fork() was called. + // This falls in the isInitializing() case in _class_initialize. + [BlockingSub self]; + + fail("should have crashed"); +#endif + break; + default: { + // parent + int result = 0; + while (waitpid(child, &result, 0) < 0) { + if (errno != EINTR) { + fail("waitpid failed (errno %d %s)", + errno, strerror(errno)); + } + } + if (!WIFEXITED(result)) { + fail("child crashed (waitpid result %d)", result); + } + break; + } + } +} + ++(void)initialize { + class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, ""); + class_addMethod(self, sel_registerName("release"), (IMP)release_fn, ""); + + if (self == [AnotherRootClass self]) { + static bool called = false; + if (!called) { + doFork(); + called = true; + } else { + fail("+[AnotherRootClass initialize] called again"); + } + } +} + ++(id)self { + return self; +} +@end + + +void *blocker(void *arg __unused) +{ + [BlockingSub self]; + return nil; +} + +void *blocker2(void *arg __unused) +{ + [BlockingRootClass self]; + return nil; +} + +int main() +{ +#if !SINGLETHREADED + pthread_t th; + pthread_create(&th, nil, blocker, nil); + pthread_detach(th); + pthread_create(&th, nil, blocker2, nil); + pthread_detach(th); + sleep(1); +#endif + + [AnotherRootClass self]; + succeed(__FILE__); +} diff --git a/test/forkInitializeDisabled.m b/test/forkInitializeDisabled.m new file mode 100644 index 00000000..747d1b89 --- /dev/null +++ b/test/forkInitializeDisabled.m @@ -0,0 +1,21 @@ +/* +TEST_CONFIG OS=macosx MEM=mrc ARCH=x86_64 +(confused by ARC which loads Foundation which provokes more +initialize logs) +(also confused by i386 OS_object +load workaround) + +TEST_ENV OBJC_PRINT_INITIALIZE_METHODS=YES + +TEST_RUN_OUTPUT +objc\[\d+\]: INITIALIZE: disabling \+initialize fork safety enforcement because the app has a __DATA,__objc_fork_ok section +OK: forkInitializeDisabled\.m +END +*/ + +#include "test.h" + +asm(".section __DATA, __objc_fork_ok\n.long 0\n"); + +int main() +{ + succeed(__FILE__); +} diff --git a/test/forkInitializeSingleThreaded.m b/test/forkInitializeSingleThreaded.m new file mode 100644 index 00000000..575c6f31 --- /dev/null +++ b/test/forkInitializeSingleThreaded.m @@ -0,0 +1,8 @@ +/* +TEST_RUN_OUTPUT +OK: forkInitialize\.m +OK: forkInitialize\.m +END +*/ +#define SINGLETHREADED 1 +#include "forkInitialize.m" diff --git a/test/forward.m b/test/forward.m index 5f3ba1a0..517f5e23 100644 --- a/test/forward.m +++ b/test/forward.m @@ -1,18 +1,7 @@ -// TEST_CONFIG MEM=mrc,gc -// TEST_CFLAGS -Wno-deprecated-declarations +// TEST_CONFIG MEM=mrc #include "test.h" -#if __cplusplus && !__clang__ - -int main() -{ - // llvm-g++ is confused by @selector(foo::) and will never be fixed - succeed(__FILE__); -} - -#else - #include #include @@ -72,8 +61,13 @@ +(double)fpre3: long long forward_handler(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) { #if __arm64__ +# if __LP64__ +# define p "x" // true arm64 +# else +# define p "w" // arm64_32 +# endif void *struct_addr; - __asm__ volatile("mov %0, x8" : "=r" (struct_addr) : : "x8"); + __asm__ volatile("mov %"p"0, "p"8" : "=r" (struct_addr) : : p"8"); #endif testassert(self == receiver); @@ -139,6 +133,10 @@ long long forward_handler(id self, SEL _cmd, long i1, long i2, long i3, long i4, __asm__ volatile("fldl %0" : : "m" (FP_RESULT)); #elif defined(__x86_64__) __asm__ volatile("movsd %0, %%xmm0" : : "m" (FP_RESULT)); +#elif defined(__arm64__) + __asm__ volatile("ldr d0, %0" : : "m" (FP_RESULT)); +#elif defined(__arm__) && __ARM_ARCH_7K__ + __asm__ volatile("vld1.64 {d0}, %0" : : "m" (FP_RESULT)); #elif defined(__arm__) union { double fpval; @@ -146,8 +144,6 @@ long long forward_handler(id self, SEL _cmd, long i1, long i2, long i3, long i4, } result; result.fpval = FP_RESULT; return result.llval; -#elif defined(__arm64__) - __asm__ volatile("ldr d0, %0" : : "m" (FP_RESULT)); #else # error unknown architecture #endif @@ -159,7 +155,7 @@ long long forward_handler(id self, SEL _cmd, long i1, long i2, long i3, long i4, { #if __i386__ || __x86_64__ || __arm__ fail("stret message sent to non-stret forward_handler"); -#elif __arm64__ +#elif __arm64_32__ || __arm64__ testassert(state == 17); state = 18; memcpy(struct_addr, &STRET_RESULT, sizeof(STRET_RESULT)); @@ -238,167 +234,6 @@ struct stret forward_stret_handler(id self, SEL _cmd, long i1, long i2, long i3, @implementation Super +(void)initialize { } +(id)class { return self; } - -#if __OBJC2__ -// forward:: not supported -#else --(long long) forward:(SEL)sel :(marg_list)args -{ - char *p; - uintptr_t *gp; - double *fp; - struct stret *struct_addr; - -#if defined(__i386__) - struct_addr = ((struct stret **)args)[-1]; -#elif defined(__x86_64__) - struct_addr = *(struct stret **)((char *)args + 8*16+4*8); -#elif defined(__arm__) - struct_addr = *(struct stret **)((char *)args + 0); -#else -# error unknown architecture -#endif - - testassert(self == receiver); - testassert(_cmd == sel_registerName("forward::")); - - p = (char *)args; -#if defined(__x86_64__) - p += 8*16 + 4*8; // skip over xmm and linkage - if (sel == @selector(stret::::::::::::::::::::::::::::) || - sel == @selector(stre2::::::::::::::::::::::::::::) || - sel == @selector(stre3::::::::::::::::::::::::::::)) - { - p += sizeof(void *); // struct return - } -#elif defined(__i386__) - // nothing to do -#elif defined(__arm__) - if (sel == @selector(stret::::::::::::::::::::::::::::) || - sel == @selector(stre2::::::::::::::::::::::::::::) || - sel == @selector(stre3::::::::::::::::::::::::::::)) - { - p += sizeof(void *); // struct return; - } -#else -# error unknown architecture -#endif - gp = (uintptr_t *)p; - testassert(*gp++ == (uintptr_t)self); - testassert(*gp++ == (uintptr_t)(void *)sel); - testassert(*gp++ == 1); - testassert(*gp++ == 2); - testassert(*gp++ == 3); - testassert(*gp++ == 4); - testassert(*gp++ == 5); - testassert(*gp++ == 6); - testassert(*gp++ == 7); - testassert(*gp++ == 8); - testassert(*gp++ == 9); - testassert(*gp++ == 10); - testassert(*gp++ == 11); - testassert(*gp++ == 12); - testassert(*gp++ == 13); - -#if defined(__i386__) || defined(__arm__) - - fp = (double *)gp; - testassert(*fp++ == 1.0); - testassert(*fp++ == 2.0); - testassert(*fp++ == 3.0); - testassert(*fp++ == 4.0); - testassert(*fp++ == 5.0); - testassert(*fp++ == 6.0); - testassert(*fp++ == 7.0); - testassert(*fp++ == 8.0); - testassert(*fp++ == 9.0); - testassert(*fp++ == 10.0); - testassert(*fp++ == 11.0); - testassert(*fp++ == 12.0); - testassert(*fp++ == 13.0); - testassert(*fp++ == 14.0); - testassert(*fp++ == 15.0); - -#elif defined(__x86_64__) - - fp = (double *)args; // xmm, double-wide - testassert(*fp++ == 1.0); fp++; - testassert(*fp++ == 2.0); fp++; - testassert(*fp++ == 3.0); fp++; - testassert(*fp++ == 4.0); fp++; - testassert(*fp++ == 5.0); fp++; - testassert(*fp++ == 6.0); fp++; - testassert(*fp++ == 7.0); fp++; - testassert(*fp++ == 8.0); fp++; - fp = (double *)gp; - testassert(*fp++ == 9.0); - testassert(*fp++ == 10.0); - testassert(*fp++ == 11.0); - testassert(*fp++ == 12.0); - testassert(*fp++ == 13.0); - testassert(*fp++ == 14.0); - testassert(*fp++ == 15.0); - -#else -# error unknown architecture -#endif - - if (sel == @selector(idret::::::::::::::::::::::::::::) || - sel == @selector(idre2::::::::::::::::::::::::::::) || - sel == @selector(idre3::::::::::::::::::::::::::::)) - { - union { - id idval; - long long llval; - } result; - testassert(state == 1); - state = 2; - result.idval = ID_RESULT; - return result.llval; - } else if (sel == @selector(llret::::::::::::::::::::::::::::) || - sel == @selector(llre2::::::::::::::::::::::::::::) || - sel == @selector(llre3::::::::::::::::::::::::::::)) - { - testassert(state == 3); - state = 4; - return LL_RESULT; - } else if (sel == @selector(fpret::::::::::::::::::::::::::::) || - sel == @selector(fpre2::::::::::::::::::::::::::::) || - sel == @selector(fpre3::::::::::::::::::::::::::::)) - { - testassert(state == 5); - state = 6; -#if defined(__i386__) - __asm__ volatile("fldl %0" : : "m" (FP_RESULT)); -#elif defined(__x86_64__) - __asm__ volatile("movsd %0, %%xmm0" : : "m" (FP_RESULT)); -#elif defined(__arm__) - union { - double fpval; - long long llval; - } result; - result.fpval = FP_RESULT; - return result.llval; -#else -# error unknown architecture -#endif - return 0; - } else if (sel == @selector(stret::::::::::::::::::::::::::::) || - sel == @selector(stre2::::::::::::::::::::::::::::) || - sel == @selector(stre3::::::::::::::::::::::::::::)) - { - testassert(state == 7); - state = 8; - *struct_addr = STRET_RESULT; - return 0; - } else { - fail("unknown selector %s in forward::", sel_getName(sel)); - } - return 0; -} - -#endif - @end typedef id (*id_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); @@ -451,298 +286,6 @@ int main() receiver = [Super class]; -#if __OBJC2__ - // forward:: not supported -#else - // 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)); - -#if __x86_64__ - // check stret return register - state = 7; - sp1 = getSP(); - stptr = ((fake_st_fn_t)objc_msgSend_stret)(&stval, [Super 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); - sp2 = getSP(); - testassert(sp1 == sp2); - testassert(state == 8); - testassert(stret_equal(stval, STRET_RESULT)); - testassert(stptr == &stval); -#endif - - - // 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)); - -#if __x86_64__ - // check stret return register - state = 7; - sp1 = getSP(); - stptr = ((fake_st_fn_t)objc_msgSend_stret)(&stval, [Super 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); - sp2 = getSP(); - testassert(sp1 == sp2); - testassert(state == 8); - testassert(stret_equal(stval, STRET_RESULT)); - testassert(stptr == &stval); -#endif - - - // Test default forward handler, uncached but fixed-up - - _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)); - -#if __x86_64__ - // check stret return register - state = 7; - sp1 = getSP(); - stptr = ((fake_st_fn_t)objc_msgSend_stret)(&stval, [Super 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); - sp2 = getSP(); - testassert(sp1 == sp2); - testassert(state == 8); - testassert(stret_equal(stval, STRET_RESULT)); - testassert(stptr == &stval); -#endif - - - // 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 = stret_fwd(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)); - -#if __x86_64__ - // check stret return register - state = 7; - sp1 = getSP(); - stptr = ((fake_st_fn_t)_objc_msgForward_stret)(&stval, 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)); - testassert(stptr == &stval); -#endif - - - // 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 = stret_fwd(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)); - -#if __x86_64__ - // check stret return register - state = 7; - sp1 = getSP(); - stptr = ((fake_st_fn_t)_objc_msgForward_stret)(&stval, 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)); - testassert(stptr == &stval); -#endif - - - // Test manual forwarding, uncached but fixed-up - - _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 = stret_fwd(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)); - -#if __x86_64__ - // check stret return register - state = 7; - sp1 = getSP(); - stptr = ((fake_st_fn_t)_objc_msgForward_stret)(&stval, 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)); - testassert(stptr == &stval); -#endif - -// !__OBJC2__ -#endif - - // Test user-defined forward handler objc_setForwardHandler((void*)&forward_handler, (void*)&forward_stret_handler); @@ -998,5 +541,3 @@ int main() succeed(__FILE__); } - -#endif diff --git a/test/forwardDefault.m b/test/forwardDefault.m index 2d8b968f..0ba1d6c3 100644 --- a/test/forwardDefault.m +++ b/test/forwardDefault.m @@ -1,14 +1,10 @@ /* no arc, rdar://11368528 confused by Foundation -TEST_CONFIG MEM=mrc,gc +TEST_CONFIG MEM=mrc TEST_CRASHES TEST_RUN_OUTPUT objc\[\d+\]: \+\[NSObject fakeorama\]: unrecognized selector sent to instance 0x[0-9a-fA-F]+ \(no message forward handler is installed\) -CRASHED: SIG(ILL|TRAP) -OR -not OBJC2 -objc\[\d+\]: NSObject: Does not recognize selector forward:: \(while forwarding fakeorama\) -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: HALTED END */ @@ -22,9 +18,6 @@ -(void)fakeorama; int main() { -#if !__OBJC2__ - fprintf(stderr, "not OBJC2\n"); -#endif [NSObject fakeorama]; fail("should have crashed"); } diff --git a/test/forwardDefaultStret.m b/test/forwardDefaultStret.m index 6c6229d0..979f5355 100644 --- a/test/forwardDefaultStret.m +++ b/test/forwardDefaultStret.m @@ -1,14 +1,10 @@ /* no arc, rdar://11368528 confused by Foundation -TEST_CONFIG MEM=mrc,gc +TEST_CONFIG MEM=mrc TEST_CRASHES TEST_RUN_OUTPUT objc\[\d+\]: \+\[NSObject fakeorama\]: unrecognized selector sent to instance 0x[0-9a-fA-F]+ \(no message forward handler is installed\) -CRASHED: SIG(ILL|TRAP) -OR -not OBJC2 -objc\[\d+\]: NSObject: Does not recognize selector forward:: \(while forwarding fakeorama\) -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: HALTED END */ @@ -22,9 +18,6 @@ -(struct stret)fakeorama; int main() { -#if !__OBJC2__ - fprintf(stderr, "not OBJC2\n"); -#endif [NSObject fakeorama]; fail("should have crashed"); } diff --git a/test/future.m b/test/future.m index e36f2f60..9714f662 100644 --- a/test/future.m +++ b/test/future.m @@ -2,7 +2,7 @@ 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 + $C{COMPILE} $DIR/future.m -x none future0.dylib -o future.exe END */ @@ -54,7 +54,7 @@ int main() // objc_getFutureClass with missing class oldSub1 = objc_getFutureClass("Sub1"); testassert(oldSub1); - testassert(malloc_size(objc_unretainedPointer(oldSub1)) > 0); + testassert(malloc_size((__bridge void*)oldSub1) > 0); testassert(objc_getClass("Sub1") == Nil); testassert(_class_isFutureClass(oldSub1)); testassert(0 == strcmp(class_getName(oldSub1), "Sub1")); diff --git a/test/gcenforcer-app-aso.m b/test/gcenforcer-app-aso.m new file mode 100644 index 00000000..8507a628 --- /dev/null +++ b/test/gcenforcer-app-aso.m @@ -0,0 +1,12 @@ +/* +fixme disabled in BATS because of gcfiles +TEST_CONFIG OS=macosx BATS=0 + +TEST_BUILD + cp $DIR/gcfiles/$C{ARCH}-aso gcenforcer-app-aso.exe +END + +TEST_RUN_OUTPUT +.*No Info\.plist file in application bundle or no NSPrincipalClass in the Info\.plist file, exiting +END +*/ diff --git a/test/gcenforcer-app-gc.m b/test/gcenforcer-app-gc.m new file mode 100644 index 00000000..a8ff65be --- /dev/null +++ b/test/gcenforcer-app-gc.m @@ -0,0 +1,14 @@ +/* +fixme disabled in BATS because of gcfiles +TEST_CONFIG OS=macosx BATS=0 + +TEST_BUILD + cp $DIR/gcfiles/$C{ARCH}-gc gcenforcer-app-gc.exe +END + +TEST_CRASHES +TEST_RUN_OUTPUT +objc\[\d+\]: Objective-C garbage collection is no longer supported\. +objc\[\d+\]: HALTED +END +*/ diff --git a/test/gcenforcer-app-gcaso.m b/test/gcenforcer-app-gcaso.m new file mode 100644 index 00000000..2094937b --- /dev/null +++ b/test/gcenforcer-app-gcaso.m @@ -0,0 +1,14 @@ +/* +fixme disabled in BATS because of gcfiles +TEST_CONFIG OS=macosx BATS=0 + +TEST_BUILD + cp $DIR/gcfiles/$C{ARCH}-gcaso gcenforcer-app-gcaso.exe +END + +TEST_CRASHES +TEST_RUN_OUTPUT +objc\[\d+\]: Objective-C garbage collection is no longer supported\. +objc\[\d+\]: HALTED +END +*/ diff --git a/test/gcenforcer-app-gcaso2.m b/test/gcenforcer-app-gcaso2.m new file mode 100644 index 00000000..8231993e --- /dev/null +++ b/test/gcenforcer-app-gcaso2.m @@ -0,0 +1,14 @@ +/* +fixme disabled in BATS because of gcfiles +TEST_CONFIG OS=macosx BATS=0 + +TEST_BUILD + cp $DIR/gcfiles/$C{ARCH}-gcaso2 gcenforcer-app-gcaso2.exe +END + +TEST_CRASHES +TEST_RUN_OUTPUT +objc\[\d+\]: Objective-C garbage collection is no longer supported\. +objc\[\d+\]: HALTED +END +*/ diff --git a/test/gcenforcer-app-gconly.m b/test/gcenforcer-app-gconly.m new file mode 100644 index 00000000..1b8e6a65 --- /dev/null +++ b/test/gcenforcer-app-gconly.m @@ -0,0 +1,14 @@ +/* +fixme disabled in BATS because of gcfiles +TEST_CONFIG OS=macosx BATS=0 + +TEST_BUILD + cp $DIR/gcfiles/$C{ARCH}-gconly gcenforcer-app-gconly.exe +END + +TEST_CRASHES +TEST_RUN_OUTPUT +objc\[\d+\]: Objective-C garbage collection is no longer supported\. +objc\[\d+\]: HALTED +END +*/ diff --git a/test/gcenforcer-app-nogc.m b/test/gcenforcer-app-nogc.m new file mode 100644 index 00000000..d99db0f1 --- /dev/null +++ b/test/gcenforcer-app-nogc.m @@ -0,0 +1,12 @@ +/* +fixme disabled in BATS because of gcfiles +TEST_CONFIG OS=macosx BATS=0 + +TEST_BUILD + cp $DIR/gcfiles/$C{ARCH}-nogc gcenforcer-app-nogc.exe +END + +TEST_RUN_OUTPUT +running +END +*/ diff --git a/test/gcenforcer-app-noobjc.m b/test/gcenforcer-app-noobjc.m new file mode 100644 index 00000000..ad746c3c --- /dev/null +++ b/test/gcenforcer-app-noobjc.m @@ -0,0 +1,12 @@ +/* +fixme disabled in BATS because of gcfiles +TEST_CONFIG OS=macosx BATS=0 + +TEST_BUILD + cp $DIR/gcfiles/$C{ARCH}-noobjc gcenforcer-app-noobjc.exe +END + +TEST_RUN_OUTPUT + +END +*/ diff --git a/test/gcenforcer-dylib-nogc.m b/test/gcenforcer-dylib-nogc.m new file mode 100644 index 00000000..b10fbe1f --- /dev/null +++ b/test/gcenforcer-dylib-nogc.m @@ -0,0 +1,11 @@ +// gc-off app loading gc-off dylib: should work + +/* +fixme disabled in BATS because of gcfiles +TEST_CONFIG OS=macosx BATS=0 + +TEST_BUILD + cp $DIR/gcfiles/libnogc.dylib . + $C{COMPILE} $DIR/gc-main.m -x none libnogc.dylib -o gcenforcer-dylib-nogc.exe +END +*/ diff --git a/test/gcenforcer-dylib-noobjc.m b/test/gcenforcer-dylib-noobjc.m new file mode 100644 index 00000000..a06fa54f --- /dev/null +++ b/test/gcenforcer-dylib-noobjc.m @@ -0,0 +1,9 @@ +/* +fixme disabled in BATS because of gcfiles +TEST_CONFIG OS=macosx BATS=0 + +TEST_BUILD + cp $DIR/gcfiles/libnoobjc.dylib . + $C{COMPILE} $DIR/gc-main.m -x none libnoobjc.dylib -o gcenforcer-dylib-noobjc.exe +END +*/ diff --git a/test/gcenforcer-dylib-requiresgc.m b/test/gcenforcer-dylib-requiresgc.m new file mode 100644 index 00000000..67ef3ce2 --- /dev/null +++ b/test/gcenforcer-dylib-requiresgc.m @@ -0,0 +1,21 @@ +// gc-off app loading gc-required dylib: should crash +// linker sees librequiresgc.fake.dylib, runtime uses librequiresgc.dylib + +/* +fixme disabled in BATS because of gcfiles +TEST_CONFIG OS=macosx BATS=0 +TEST_CRASHES + +TEST_RUN_OUTPUT +dyld: Library not loaded: librequiresgc\.dylib + Referenced from: .*gcenforcer-dylib-requiresgc.exe + Reason: no suitable image found\. Did find: + .*librequiresgc\.dylib: cannot load '.*librequiresgc\.dylib' because Objective-C garbage collection is not supported + librequiresgc.dylib: cannot load 'librequiresgc\.dylib' because Objective-C garbage collection is not supported +END + +TEST_BUILD + cp $DIR/gcfiles/librequiresgc.dylib . + $C{COMPILE} $DIR/gc-main.m -x none $DIR/gcfiles/librequiresgc.fake.dylib -o gcenforcer-dylib-requiresgc.exe +END +*/ diff --git a/test/gcenforcer-dylib-supportsgc.m b/test/gcenforcer-dylib-supportsgc.m new file mode 100644 index 00000000..d8ce9e38 --- /dev/null +++ b/test/gcenforcer-dylib-supportsgc.m @@ -0,0 +1,9 @@ +/* +fixme disabled in BATS because of gcfiles +TEST_CONFIG OS=macosx BATS=0 + +TEST_BUILD + cp $DIR/gcfiles/libsupportsgc.dylib . + $C{COMPILE} $DIR/gc-main.m -x none libsupportsgc.dylib -o gcenforcer-dylib-supportsgc.exe +END +*/ diff --git a/test/gcenforcer-nogc-1.m b/test/gcenforcer-nogc-1.m deleted file mode 100644 index d72212bf..00000000 --- a/test/gcenforcer-nogc-1.m +++ /dev/null @@ -1,15 +0,0 @@ -// gc-off app loading gc-off dylib: should work - -/* -TEST_CONFIG MEM=mrc,arc OS=macosx - -TEST_BUILD - $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libnogc.dylib - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only - $C{COMPILE_NOMEM} $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 deleted file mode 100644 index 75146fe7..00000000 --- a/test/gcenforcer-nogc-2.m +++ /dev/null @@ -1,22 +0,0 @@ -// gc-on app loading gc-off dylib: should crash - -/* -TEST_CONFIG MEM=gc OS=macosx -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_NOMEM} $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 deleted file mode 100644 index f37c9bae..00000000 --- a/test/gcenforcer-noobjc.m +++ /dev/null @@ -1,13 +0,0 @@ -/* -TEST_CONFIG OS=macosx - -TEST_BUILD - $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libnogc.dylib - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only - $C{COMPILE_NOMEM} $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-preflight.m b/test/gcenforcer-preflight.m new file mode 100644 index 00000000..828cc337 --- /dev/null +++ b/test/gcenforcer-preflight.m @@ -0,0 +1,88 @@ +#pragma clang diagnostic ignored "-Wcomment" +/* +fixme disabled in BATS because of gcfiles +TEST_CONFIG OS=macosx BATS=0 + +TEST_BUILD + cp $DIR/gcfiles/* . + $C{COMPILE} $DIR/gcenforcer-preflight.m -o gcenforcer-preflight.exe +END +*/ + +#include "test.h" +#include + +void check(int expected, const char *name) +{ + int fd = open(name, O_RDONLY); + testassert(fd >= 0); + + int result = objc_appRequiresGC(fd); + + close(fd); + testprintf("want %2d got %2d for %s\n", expected, result, name); + if (result != expected) { + fail("want %2d got %2d for %s\n", expected, result, name); + } + testassert(result == expected); +} + +int main() +{ + int i; + for (i = 0; i < 1000; i++) { + // dlopen_preflight + + testassert(dlopen_preflight("libsupportsgc.dylib")); + testassert(dlopen_preflight("libnoobjc.dylib")); + testassert(! dlopen_preflight("librequiresgc.dylib")); + testassert(dlopen_preflight("libnogc.dylib")); + + // objc_appRequiresGC + + // noobjc: no ObjC content + // nogc: ordinary not GC + // aso: trivial AppleScriptObjC wrapper that can run without GC + // gc: -fobjc-gc + // gconly: -fobjc-gc-only + // gcaso: non-trivial AppleScriptObjC with too many classrefs + // gcaso2: non-trivial AppleScriptObjC with too many class impls + + check(0, "x86_64-noobjc"); + check(0, "x86_64-nogc"); + check(0, "x86_64-aso"); + check(1, "x86_64-gc"); + check(1, "x86_64-gconly"); + check(1, "x86_64-gcaso"); + check(1, "x86_64-gcaso2"); + + check(0, "i386-noobjc"); + check(0, "i386-nogc"); + check(0, "i386-aso"); + check(1, "i386-gc"); + check(1, "i386-gconly"); + check(1, "i386-gcaso"); + check(1, "i386-gcaso2"); + + // fat files + check(0, "i386-aso--x86_64-aso"); + check(0, "i386-nogc--x86_64-nogc"); + check(1, "i386-gc--x86_64-gc"); + check(1, "i386-gc--x86_64-nogc"); + check(1, "i386-nogc--x86_64-gc"); + + // broken files + check(-1, "x86_64-broken"); + check(-1, "i386-broken"); + check(-1, "i386-broken--x86_64-gc"); + check(-1, "i386-broken--x86_64-nogc"); + check(-1, "i386-gc--x86_64-broken"); + check(-1, "i386-nogc--x86_64-broken"); + + // evil files + // evil1: claims to have 4 billion load commands of size 0 + check(-1, "evil1"); + } + + succeed(__FILE__); +} diff --git a/test/gcenforcer-requiresgc-1.m b/test/gcenforcer-requiresgc-1.m deleted file mode 100644 index 132e6724..00000000 --- a/test/gcenforcer-requiresgc-1.m +++ /dev/null @@ -1,23 +0,0 @@ -// gc-off app loading gc-required dylib: should crash -// linker sees librequiresgc.fake.dylib, runtime uses librequiresgc.dylib - -/* -TEST_CONFIG MEM=mrc,arc OS=macosx -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_NOMEM} $DIR/gc.m -dynamiclib -o libnogc.dylib - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only - $C{COMPILE_NOMEM} $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 deleted file mode 100644 index 530891a9..00000000 --- a/test/gcenforcer-requiresgc-2.m +++ /dev/null @@ -1,16 +0,0 @@ -// gc-off app loading gc-required dylib: should crash -// linker sees librequiresgc.fake.dylib, runtime uses librequiresgc.dylib - -/* -TEST_CONFIG MEM=gc OS=macosx - -TEST_BUILD - $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib - $C{COMPILE_NOMEM} $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 deleted file mode 100644 index 9551483b..00000000 --- a/test/gcenforcer-supportsgc.m +++ /dev/null @@ -1,13 +0,0 @@ -/* -TEST_CONFIG OS=macosx - -TEST_BUILD - $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libnogc.dylib - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only - $C{COMPILE_NOMEM} $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 -*/ diff --git a/test/gcenforcer.m b/test/gcenforcer.m deleted file mode 100644 index 66d66e94..00000000 --- a/test/gcenforcer.m +++ /dev/null @@ -1,36 +0,0 @@ -/* -TEST_CONFIG OS=macosx - -TEST_BUILD - $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libnogc.dylib - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc - $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only - $C{COMPILE_NOMEM} $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 -#include - -int main() -{ - int i; - for (i = 0; i < 1000; i++) { - testassert(dlopen_preflight("libsupportsgc.dylib")); - testassert(dlopen_preflight("libnoobjc.dylib")); - - if (objc_collectingEnabled()) { - testassert(dlopen_preflight("librequiresgc.dylib")); - testassert(! dlopen_preflight("libnogc.dylib")); - } else { - testassert(! dlopen_preflight("librequiresgc.dylib")); - testassert(dlopen_preflight("libnogc.dylib")); - } - } - - succeed(__FILE__); -} diff --git a/test/gdb.m b/test/gdb.m index a5f571a2..dc746352 100644 --- a/test/gdb.m +++ b/test/gdb.m @@ -1,16 +1,6 @@ // TEST_CFLAGS -Wno-deprecated-declarations #include "test.h" - -#if TARGET_OS_IPHONE - -int main() -{ - succeed(__FILE__); -} - -#else - #include "testroot.i" #include #include @@ -18,8 +8,6 @@ int main() int main() { // Class hashes -#if __OBJC2__ - Class result; // Class should not be realized yet @@ -30,30 +18,17 @@ int main() [TestRoot class]; // Now class should be realized - result = (Class)objc_unretainedObject(NXMapGet(gdb_objc_realized_classes, "TestRoot")); + result = (__bridge Class)(NXMapGet(gdb_objc_realized_classes, "TestRoot")); testassert(result); testassert(result == [TestRoot class]); - result = (Class)objc_unretainedObject(NXMapGet(gdb_objc_realized_classes, "DoesNotExist")); + result = (__bridge Class)(NXMapGet(gdb_objc_realized_classes, "DoesNotExist")); testassert(!result); -#else - - struct objc_class query; - Class result; - - query.name = "TestRoot"; - result = (Class)NXHashGet(_objc_debug_class_hash, &query); - testassert(result); - testassert((id)result == [TestRoot class]); - - query.name = "DoesNotExist"; - result = (Class)NXHashGet(_objc_debug_class_hash, &query); - testassert(!result); - -#endif + // Class structure decoding + + uintptr_t *maskp = (uintptr_t *)dlsym(RTLD_DEFAULT, "objc_debug_class_rw_data_mask"); + testassert(maskp); succeed(__FILE__); } - -#endif diff --git a/test/getClassHook.m b/test/getClassHook.m new file mode 100644 index 00000000..1d671d9e --- /dev/null +++ b/test/getClassHook.m @@ -0,0 +1,128 @@ +// TEST_CONFIG + +#include "test.h" +#include "testroot.i" + +@interface OrdinaryClass : TestRoot @end +@implementation OrdinaryClass @end + +objc_hook_getClass OnePreviousHook; +static int HookOneCalls = 0; +BOOL GetClassHookOne(const char *name, Class *outClass) +{ + HookOneCalls++; + if (0 == strcmp(name, "TwoClass")) { + fail("other hook should have handled this already"); + } else if (0 == strcmp(name, "OrdinaryClass")) { + fail("runtime should have handled this already"); + } else if (0 == strcmp(name, "OneClass")) { + Class cls = objc_allocateClassPair([OrdinaryClass class], "OneClass", 0); + objc_registerClassPair(cls); + *outClass = cls; + return YES; + } else { + return OnePreviousHook(name, outClass); + } +} + +objc_hook_getClass TwoPreviousHook; +static int HookTwoCalls = 0; +BOOL GetClassHookTwo(const char *name, Class *outClass) +{ + HookTwoCalls++; + if (0 == strcmp(name, "OrdinaryClass")) { + fail("runtime should have handled this already"); + } else if (0 == strcmp(name, "TwoClass")) { + Class cls = objc_allocateClassPair([OrdinaryClass class], "TwoClass", 0); + objc_registerClassPair(cls); + *outClass = cls; + return YES; + } else { + return TwoPreviousHook(name, outClass); + } +} + + +objc_hook_getClass ThreePreviousHook; +static int HookThreeCalls = 0; +#define MAXDEPTH 100 +BOOL GetClassHookThree(const char *name, Class *outClass) +{ + // Re-entrant hook test. + // libobjc must prevent re-entrancy when a getClass + // hook provokes another getClass call. + + static int depth = 0; + static char *names[MAXDEPTH] = {0}; + + if (depth < MAXDEPTH) { + // Re-entrantly call objc_getClass() with a new class name. + if (!names[depth]) asprintf(&names[depth], "Reentrant%d", depth); + const char *reentrantName = names[depth]; + depth++; + (void)objc_getClass(reentrantName); + depth--; + } else if (depth == MAXDEPTH) { + // We now have maxdepth getClass hooks stacked up. + // Call objc_getClass() on all of those names a second time. + // None of those lookups should call this hook again. + HookThreeCalls++; + depth = -1; + for (int i = 0; i < MAXDEPTH; i++) { + testassert(!objc_getClass(names[i])); + } + depth = MAXDEPTH; + } else { + fail("reentrancy protection failed"); + } + + // Chain to the previous hook after all of the reentrancy is unwound. + if (depth == 0) { + return ThreePreviousHook(name, outClass); + } else { + return NO; + } +} + + +void testLookup(const char *name, int expectedHookOneCalls, + int expectedHookTwoCalls, int expectedHookThreeCalls) +{ + HookOneCalls = HookTwoCalls = HookThreeCalls = 0; + Class cls = objc_getClass(name); + testassert(HookOneCalls == expectedHookOneCalls && + HookTwoCalls == expectedHookTwoCalls && + HookThreeCalls == expectedHookThreeCalls); + testassert(cls); + testassert(0 == strcmp(class_getName(cls), name)); + testassert(cls == [cls self]); +} + +int main() +{ + testassert(objc_getClass("OrdinaryClass")); + testassert(!objc_getClass("OneClass")); + testassert(!objc_getClass("TwoClass")); + testassert(!objc_getClass("NoSuchClass")); + + objc_setHook_getClass(GetClassHookOne, &OnePreviousHook); + objc_setHook_getClass(GetClassHookTwo, &TwoPreviousHook); + objc_setHook_getClass(GetClassHookThree, &ThreePreviousHook); + // invocation order: HookThree -> Hook Two -> Hook One + + HookOneCalls = HookTwoCalls = HookThreeCalls = 0; + testassert(!objc_getClass("NoSuchClass")); + testassert(HookOneCalls == 1 && HookTwoCalls == 1 && HookThreeCalls == 1); + + testLookup("OneClass", 1, 1, 1); + testLookup("TwoClass", 0, 1, 1); + testLookup("OrdinaryClass", 0, 0, 0); + + // Check again. No hooks should be needed this time. + + testLookup("OneClass", 0, 0, 0); + testLookup("TwoClass", 0, 0, 0); + testLookup("OrdinaryClass", 0, 0, 0); + + succeed(__FILE__); +} diff --git a/test/getImageNameHook.m b/test/getImageNameHook.m new file mode 100644 index 00000000..87c72fec --- /dev/null +++ b/test/getImageNameHook.m @@ -0,0 +1,78 @@ +// TEST_CONFIG + +#include "test.h" +#include "testroot.i" + +@interface One : TestRoot @end +@implementation One @end + +@interface Two : TestRoot @end +@implementation Two @end + +@interface Both : TestRoot @end +@implementation Both @end + +@interface None : TestRoot @end +@implementation None @end + + +objc_hook_getImageName OnePreviousHook; +BOOL GetImageNameHookOne(Class cls, const char **outName) +{ + if (0 == strcmp(class_getName(cls), "One")) { + *outName = "Image One"; + return YES; + } else if (0 == strcmp(class_getName(cls), "Both")) { + *outName = "Image Both via One"; + return YES; + } else { + return OnePreviousHook(cls, outName); + } +} + +objc_hook_getImageName TwoPreviousHook; +BOOL GetImageNameHookTwo(Class cls, const char **outName) +{ + if (0 == strcmp(class_getName(cls), "Two")) { + *outName = "Image Two"; + return YES; + } else if (0 == strcmp(class_getName(cls), "Both")) { + *outName = "Image Both via Two"; + return YES; + } else { + return TwoPreviousHook(cls, outName); + } +} + +int main() +{ + + // before hooks: main executable is the image name for four classes + testassert(strstr(class_getImageName([One class]), "getImageNameHook")); + testassert(strstr(class_getImageName([Two class]), "getImageNameHook")); + testassert(strstr(class_getImageName([Both class]), "getImageNameHook")); + testassert(strstr(class_getImageName([None class]), "getImageNameHook")); + testassert(strstr(class_getImageName([NSObject class]), "libobjc")); + + // install hook One + objc_setHook_getImageName(GetImageNameHookOne, &OnePreviousHook); + + // two classes are in Image One with hook One in place + testassert(strstr(class_getImageName([One class]), "Image One")); + testassert(strstr(class_getImageName([Two class]), "getImageNameHook")); + testassert(strstr(class_getImageName([Both class]), "Image Both via One")); + testassert(strstr(class_getImageName([None class]), "getImageNameHook")); + testassert(strstr(class_getImageName([NSObject class]), "libobjc")); + + // install hook Two which chains to One + objc_setHook_getImageName(GetImageNameHookTwo, &TwoPreviousHook); + + // two classes are in Image Two and one in One with both hooks in place + testassert(strstr(class_getImageName([One class]), "Image One")); + testassert(strstr(class_getImageName([Two class]), "Image Two")); + testassert(strstr(class_getImageName([Both class]), "Image Both via Two")); + testassert(strstr(class_getImageName([None class]), "getImageNameHook")); + testassert(strstr(class_getImageName([NSObject class]), "libobjc")); + + succeed(__FILE__); +} diff --git a/test/getMethod.m b/test/getMethod.m index 84408a8a..37773297 100644 --- a/test/getMethod.m +++ b/test/getMethod.m @@ -1,4 +1,11 @@ -// TEST_CONFIG +/* +TEST_BUILD_OUTPUT +.*getMethod.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*getMethod.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*getMethod.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*getMethod.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +END +*/ #include "test.h" #include "testroot.i" @@ -31,6 +38,8 @@ int main() SEL sel; IMP imp; + id bufobj = (__bridge_transfer id)(void*)buf; + // don't use [Super class] to check laziness handing Super_cls = objc_getClass("Super"); Sub_cls = objc_getClass("Sub"); @@ -75,9 +84,9 @@ int main() imp = method_getImplementation(m); testassert(imp == class_getMethodImplementation(Super_cls, sel)); buf[0] = Super_cls; - testassert(imp == object_getMethodImplementation(objc_unretainedObject(buf), sel)); + testassert(imp == object_getMethodImplementation(bufobj, sel)); state = 0; - (*(imp_t)imp)(objc_unretainedObject(buf), sel); + (*(imp_t)imp)(bufobj, sel); testassert(state == 4); sel = sel_registerName("instanceMethod"); @@ -87,9 +96,9 @@ int main() imp = method_getImplementation(m); testassert(imp == class_getMethodImplementation(Sub_cls, sel)); buf[0] = Sub_cls; - testassert(imp == object_getMethodImplementation(objc_unretainedObject(buf), sel)); + testassert(imp == object_getMethodImplementation(bufobj, sel)); state = 0; - (*(imp_t)imp)(objc_unretainedObject(buf), sel); + (*(imp_t)imp)(bufobj, sel); testassert(state == 5); sel = sel_registerName("instanceMethodSuperOnly"); @@ -99,9 +108,9 @@ int main() imp = method_getImplementation(m); testassert(imp == class_getMethodImplementation(Sub_cls, sel)); buf[0] = Sub_cls; - testassert(imp == object_getMethodImplementation(objc_unretainedObject(buf), sel)); + testassert(imp == object_getMethodImplementation(bufobj, sel)); state = 0; - (*(imp_t)imp)(objc_unretainedObject(buf), sel); + (*(imp_t)imp)(bufobj, sel); testassert(state == 6); // check class_getClassMethod(cls) == class_getInstanceMethod(cls->isa) @@ -113,10 +122,10 @@ int main() testassert(! class_getClassMethod(Sub_cls, sel)); testassert(class_getMethodImplementation(Sub_cls, sel) == (IMP)&_objc_msgForward); buf[0] = Sub_cls; - testassert(object_getMethodImplementation(objc_unretainedObject(buf), sel) == (IMP)&_objc_msgForward); + testassert(object_getMethodImplementation(bufobj, sel) == (IMP)&_objc_msgForward); #if !__arm64__ testassert(class_getMethodImplementation_stret(Sub_cls, sel) == (IMP)&_objc_msgForward_stret); - testassert(object_getMethodImplementation_stret(objc_unretainedObject(buf), sel) == (IMP)&_objc_msgForward_stret); + testassert(object_getMethodImplementation_stret(bufobj, sel) == (IMP)&_objc_msgForward_stret); #endif testassert(! class_getInstanceMethod(NULL, NULL)); diff --git a/test/get_task_allow_entitlement.plist b/test/get_task_allow_entitlement.plist new file mode 100644 index 00000000..bd100851 --- /dev/null +++ b/test/get_task_allow_entitlement.plist @@ -0,0 +1,8 @@ + + + + + get-task-allow + + + diff --git a/test/headers.c b/test/headers.c new file mode 100644 index 00000000..9fbc1ac2 --- /dev/null +++ b/test/headers.c @@ -0,0 +1,19 @@ +/* +TEST_BUILD +$DIR/headers.sh '$C{TESTINCLUDEDIR}' '$C{TESTLOCALINCLUDEDIR}' '$C{COMPILE_C}' '$C{COMPILE_CXX}' '$C{COMPILE_M}' '$C{COMPILE_MM}' '$VERBOSE' +$C{COMPILE_C} $DIR/headers.c -o headers.exe +END + +allow `sh -x` output from headers.sh +TEST_BUILD_OUTPUT +(\+ .*\n)*(\+ .*)?done +END + */ + + +#include "test.h" + +int main() +{ + succeed(__FILE__); +} diff --git a/test/headers.sh b/test/headers.sh new file mode 100755 index 00000000..b6306a43 --- /dev/null +++ b/test/headers.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# Compile every exported ObjC header as if it were a file in every language. +# This script is executed by test headers.c's TEST_BUILD command. + +TESTINCLUDEDIR=$1; shift +TESTLOCALINCLUDEDIR=$1; shift +COMPILE_C=$1; shift +COMPILE_CXX=$1; shift +COMPILE_M=$1; shift +COMPILE_MM=$1; shift +VERBOSE=$1; shift + +# stop after any command error +set -e + +# echo commands when verbose +if [ "$VERBOSE" != "0" ]; then + set -x +fi + +FILES="$TESTINCLUDEDIR/objc/*.h $TESTLOCALINCLUDEDIR/objc/*.h" +CFLAGS='-fsyntax-only -Wno-unused-function -D_OBJC_PRIVATE_H_' + +$COMPILE_C $CFLAGS $FILES +$COMPILE_CXX $CFLAGS $FILES +$COMPILE_M $CFLAGS $FILES +$COMPILE_MM $CFLAGS $FILES +for STDC in '99' '11' ; do + $COMPILE_C $CFLAGS $FILES -std=c$STDC + $COMPILE_M $CFLAGS $FILES -std=c$STDC +done +for STDCXX in '98' '03' '11' '14' '17' ; do + $COMPILE_CXX $CFLAGS $FILES -std=c++$STDCXX + $COMPILE_MM $CFLAGS $FILES -std=c++$STDCXX +done + +echo done diff --git a/test/ignoredSelector.m b/test/ignoredSelector.m deleted file mode 100644 index 733da6a8..00000000 --- a/test/ignoredSelector.m +++ /dev/null @@ -1,350 +0,0 @@ -// TEST_CONFIG MEM=mrc,gc -// TEST_CFLAGS -Wno-deprecated-declarations - -#include "test.h" -#include -#include -#include - -static int state = 0; - -OBJC_ROOT_CLASS -@interface Super { id isa; } @end -@implementation Super -+(id)class { return self; } -+(void)initialize { } - -+(id)ordinary { state = 1; return self; } -+(id)ordinary2 { testassert(0); } -+(id)retain { state = 2; return self; } -+(void)release { state = 3; } -+(id)autorelease { state = 4; return self; } -+(void)dealloc { state = 5; } -+(uintptr_t)retainCount { state = 6; return 6; } -@end - -@interface Sub : Super @end -@implementation Sub @end - -@interface Sub2 : Super @end -@implementation Sub2 @end - -OBJC_ROOT_CLASS -@interface Empty { id isa; } @end -@implementation Empty -+(id)class { return self; } -+(void)initialize { } -@end - -void *forward_handler(id obj, SEL _cmd) { - testassert(obj == [Empty class]); - testassert(_cmd == @selector(ordinary)); - state = 1; - return nil; -} - -@interface Empty (Unimplemented) -+(id)ordinary; -+(id)retain; -+(void)release; -+(id)autorelease; -+(void)dealloc; -+(uintptr_t)retainCount; -@end - - -#define getImp(sel) \ - do { \ - sel##Method = class_getClassMethod(cls, @selector(sel)); \ - testassert(sel##Method); \ - testassert(@selector(sel) == method_getName(sel##Method)); \ - sel = method_getImplementation(sel##Method); \ - } while (0) - - -static IMP ordinary, ordinary2, retain, release, autorelease, dealloc, retainCount; -static Method ordinaryMethod, ordinary2Method, retainMethod, releaseMethod, autoreleaseMethod, deallocMethod, retainCountMethod; - -void cycle(Class cls) -{ - id idVal; - uintptr_t intVal; - -#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 -#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) && - @selector(retain) != @selector(retainCount) ); - } - - // no ignored selector matches a real selector - testassert(@selector(ordinary) != @selector(retain) && - @selector(ordinary) != @selector(release) && - @selector(ordinary) != @selector(autorelease) && - @selector(ordinary) != @selector(dealloc) && - @selector(ordinary) != @selector(retainCount) ); - - getImp(ordinary); - getImp(ordinary2); - getImp(retain); - getImp(release); - getImp(autorelease); - getImp(dealloc); - getImp(retainCount); - - if (objc_collectingEnabled()) { - // GC: all ignored selector IMPs are identical - testassert(retain == release && - retain == autorelease && - retain == dealloc && - retain == retainCount ); - } - else { - // no GC: all ignored selector IMPs are distinct - testassert(retain != release && - retain != autorelease && - retain != dealloc && - retain != retainCount ); - } - - // no ignored selector IMP matches a real selector IMP - testassert(ordinary != retain && - ordinary != release && - ordinary != autorelease && - ordinary != dealloc && - ordinary != retainCount ); - - // Test calls via method_invoke - - idVal = ((id(*)(id, Method))method_invoke)(cls, ordinaryMethod); - testassert(state == 1); - testassert(idVal == cls); - - state = 0; - idVal = ((id(*)(id, Method))method_invoke)(cls, retainMethod); - testassert(state == (objc_collectingEnabled() ? 0 : 2)); - testassert(idVal == cls); - - (void) ((void(*)(id, Method))method_invoke)(cls, releaseMethod); - testassert(state == (objc_collectingEnabled() ? 0 : 3)); - - idVal = ((id(*)(id, Method))method_invoke)(cls, autoreleaseMethod); - testassert(state == (objc_collectingEnabled() ? 0 : 4)); - testassert(idVal == cls); - - (void) ((void(*)(id, Method))method_invoke)(cls, deallocMethod); - testassert(state == (objc_collectingEnabled() ? 0 : 5)); - - intVal = ((uintptr_t(*)(id, Method))method_invoke)(cls, retainCountMethod); - testassert(state == (objc_collectingEnabled() ? 0 : 6)); - testassert(intVal == (objc_collectingEnabled() ? (uintptr_t)cls : 6)); - - - // Test calls via compiled objc_msgSend - - state = 0; - idVal = [cls ordinary]; - testassert(state == 1); - testassert(idVal == cls); - - state = 0; - idVal = [cls retain]; - testassert(state == (objc_collectingEnabled() ? 0 : 2)); - testassert(idVal == cls); - - (void) [cls release]; - testassert(state == (objc_collectingEnabled() ? 0 : 3)); - - idVal = [cls autorelease]; - testassert(state == (objc_collectingEnabled() ? 0 : 4)); - testassert(idVal == cls); - - (void) [cls dealloc]; - testassert(state == (objc_collectingEnabled() ? 0 : 5)); - - intVal = [cls retainCount]; - 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(ordinary)); - 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); - - (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(release)); - testassert(state == (objc_collectingEnabled() ? 0 : 3)); - - 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() -{ - Class cls; - - objc_setForwardHandler((void*)&forward_handler, nil); - - // Test selector API - - testassert(sel_registerName("retain") == @selector(retain)); - testassert(sel_getUid("retain") == @selector(retain)); -#if defined(__i386__) - if (objc_collectingEnabled()) { - // only i386's GC currently remaps these - testassert(0 == strcmp(sel_getName(@selector(retain)), "")); - } else -#endif - { - testassert(0 == strcmp(sel_getName(@selector(retain)), "retain")); - } -#if !__OBJC2__ - testassert(sel_isMapped(@selector(retain))); -#endif - - cls = [Sub class]; - testassert(cls); - cycle(cls); - - cls = [Super class]; - testassert(cls); - cycle(cls); - - if (objc_collectingEnabled()) { - // rdar://6200570 Method manipulation shouldn't affect ignored methods. - - cls = [Super class]; - testassert(cls); - cycle(cls); - - method_setImplementation(retainMethod, (IMP)1); - method_setImplementation(releaseMethod, (IMP)1); - method_setImplementation(autoreleaseMethod, (IMP)1); - method_setImplementation(deallocMethod, (IMP)1); - method_setImplementation(retainCountMethod, (IMP)1); - cycle(cls); - - testassert(ordinary2 != retainCount); - method_exchangeImplementations(retainMethod, autoreleaseMethod); - method_exchangeImplementations(deallocMethod, releaseMethod); - method_exchangeImplementations(retainCountMethod, ordinary2Method); - cycle(cls); - // ordinary2 exchanged with ignored method is now ignored too - testassert(ordinary2 == retainCount); - - // replace == replace existing - class_replaceMethod(cls, @selector(retain), (IMP)1, ""); - class_replaceMethod(cls, @selector(release), (IMP)1, ""); - class_replaceMethod(cls, @selector(autorelease), (IMP)1, ""); - class_replaceMethod(cls, @selector(dealloc), (IMP)1, ""); - class_replaceMethod(cls, @selector(retainCount), (IMP)1, ""); - cycle(cls); - - cls = [Sub class]; - testassert(cls); - cycle(cls); - - // replace == add override - class_replaceMethod(cls, @selector(retain), (IMP)1, ""); - class_replaceMethod(cls, @selector(release), (IMP)1, ""); - class_replaceMethod(cls, @selector(autorelease), (IMP)1, ""); - class_replaceMethod(cls, @selector(dealloc), (IMP)1, ""); - class_replaceMethod(cls, @selector(retainCount), (IMP)1, ""); - cycle(cls); - - cls = [Sub2 class]; - testassert(cls); - cycle(cls); - - class_addMethod(cls, @selector(retain), (IMP)1, ""); - class_addMethod(cls, @selector(release), (IMP)1, ""); - class_addMethod(cls, @selector(autorelease), (IMP)1, ""); - class_addMethod(cls, @selector(dealloc), (IMP)1, ""); - class_addMethod(cls, @selector(retainCount), (IMP)1, ""); - cycle(cls); - } - - // Test calls via objc_msgSend - ignored selectors are ignored - // under GC even if the class provides no implementation for them - if (objc_collectingEnabled()) { - Class cls; - id idVal; - uintptr_t intVal; - - cls = [Empty class]; - state = 0; - - idVal = [Empty retain]; - testassert(state == 0); - testassert(idVal == cls); - - (void) [Empty release]; - testassert(state == 0); - - idVal = [Empty autorelease]; - testassert(state == 0); - testassert(idVal == cls); - - (void) [Empty dealloc]; - testassert(state == 0); - - intVal = [Empty retainCount]; - testassert(state == 0); - testassert(intVal == (uintptr_t)cls); - - idVal = [Empty ordinary]; - testassert(state == 1); - testassert(idVal == nil); - - state = 0; - - idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(retain)); - testassert(state == 0); - testassert(idVal == cls); - - (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(release)); - testassert(state == 0); - - 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(ordinary)); - testassert(state == 1); - testassert(idVal == nil); - } - - succeed(__FILE__); -} diff --git a/test/ignoredSelector2.m b/test/ignoredSelector2.m deleted file mode 100644 index d0a24c8b..00000000 --- a/test/ignoredSelector2.m +++ /dev/null @@ -1,37 +0,0 @@ -// TEST_CONFIG MEM=gc -// TEST_CFLAGS -framework Foundation - -// This test must use CF and test ignoredSelector must not use CF. - -#include "test.h" -#include - -int main() -{ - if (objc_collectingEnabled()) { - // ARC RR functions don't retain and don't hit the side table. - __block int count; - testblock_t testblock = ^{ - for (int i = 0; i < count; i++) { - id obj = [NSObject new]; - objc_retain(obj); - objc_retain(obj); - objc_release(obj); - } - }; - count = 100; - testonthread(testblock); - testonthread(testblock); - leak_mark(); - count = 10000000; - testonthread(testblock); -#if __OBJC_GC__ - testwarn("rdar://19042235 possible leaks suppressed under GC"); - leak_check(2000); -#else - leak_check(0); -#endif - } - - succeed(__FILE__); -} diff --git a/test/imageorder.m b/test/imageorder.m index f417eb52..ec7b6299 100644 --- a/test/imageorder.m +++ b/test/imageorder.m @@ -3,7 +3,7 @@ $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 + $C{COMPILE} $DIR/imageorder.m -x none imageorder3.dylib imageorder2.dylib imageorder1.dylib -o imageorder.exe END */ diff --git a/test/imports.c b/test/imports.c new file mode 100644 index 00000000..99e46e74 --- /dev/null +++ b/test/imports.c @@ -0,0 +1,37 @@ +/* +Disallow some imports into and exports from libobjc.A.dylib. + +To debug, re-run libobjc's link command with + -Xlinker -dead_strip -Xlinker -why_live -Xlinker SYMBOL_NAME_HERE + +Disallowed imports (nm -u): +___cxa_guard_acquire (C++ function-scope static initializer) +___cxa_guard_release (C++ function-scope static initializer) +___cxa_atexit (C++ static destructor) +weak external (any weak externals, including operators new and delete) + +Disallowed exports (nm -U): +__Z* (any C++-mangled export) +weak external (any weak externals, including operators new and delete) + +fixme rdar://13354718 should disallow anything from libc++ (i.e. not libc++abi) +*/ + +/* +TEST_BUILD +echo $C{XCRUN} nm -m -arch $C{ARCH} $C{TESTLIB} +$C{XCRUN} nm -u -m -arch $C{ARCH} $C{TESTLIB} | egrep '(weak external| external (___cxa_atexit|___cxa_guard_acquire|___cxa_guard_release))' || true +$C{XCRUN} nm -U -m -arch $C{ARCH} $C{TESTLIB} | egrep '(weak external| external __Z)' || true +$C{COMPILE_C} $DIR/imports.c -o imports.exe +END + +TEST_BUILD_OUTPUT +.*libobjc.A.dylib +END + */ + +#include "test.h" +int main() +{ + succeed(__FILE__); +} diff --git a/test/include-warnings.c b/test/include-warnings.c new file mode 100644 index 00000000..72990e23 --- /dev/null +++ b/test/include-warnings.c @@ -0,0 +1,19 @@ +/* +TEST_BUILD + $C{COMPILE} $DIR/include-warnings.c -o include-warnings.exe -Wsystem-headers -Weverything -Wno-undef -Wno-old-style-cast -Wno-nullability-extension 2>&1 | grep -v 'In file' | grep objc || true +END + +TEST_RUN_OUTPUT +OK: includes.c +END +*/ + +// Detect warnings inside any header. +// The build command above filters out warnings inside non-objc headers +// (which are noisy with -Weverything). +// -Wno-undef suppresses warnings about `#if __cplusplus` and the like. +// -Wno-old-style-cast is tough to avoid in mixed C/C++ code. +// -Wno-nullability-extension disables a warning about non-portable +// _Nullable etc which we already handle correctly in objc-abi.h. + +#include "includes.c" diff --git a/test/includes-objc2.c b/test/includes-objc2.c new file mode 100644 index 00000000..6fb80ab5 --- /dev/null +++ b/test/includes-objc2.c @@ -0,0 +1,13 @@ +// TEST_CFLAGS -D__OBJC2__ + +// Verify that all headers can be included in any language, even if +// the client is C code that defined __OBJC2__. + +// This is the definition that Instruments uses in its build. +#if defined(__OBJC2__) +#undef __OBJC2__ +#endif +#define __OBJC2__ 1 + +#define NAME "includes-objc2.c" +#include "includes.c" diff --git a/test/includes.c b/test/includes.c index 01c16864..1abab68e 100644 --- a/test/includes.c +++ b/test/includes.c @@ -1,6 +1,13 @@ // TEST_CONFIG // Verify that all headers can be included in any language. +// See also test/include-warnings.c which checks for warnings in these headers. +// See also test/includes-objc2.c which checks for safety even if +// the client is C code that defined __OBJC2__. + +#ifndef NAME +#define NAME "includes.c" +#endif #include @@ -20,19 +27,21 @@ #include #include -#include #include #include -#if !TARGET_OS_IPHONE +#if TARGET_OS_OSX #include #include #include #endif +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Weverything" #include "test.h" +#pragma clang diagnostic pop int main() { - succeed(__FILE__); + succeed(NAME); } diff --git a/test/initialize.m b/test/initialize.m index c3bced1a..d32b4df4 100644 --- a/test/initialize.m +++ b/test/initialize.m @@ -220,6 +220,27 @@ +(void)initialize { @end + +@interface SuperThrower : TestRoot @end +@implementation SuperThrower ++(void)initialize { + testprintf("in [SuperThrower initialize]\n"); + testassert(state == 0); + state = 10; + @throw AUTORELEASE([TestRoot new]); + fail("@throw didn't throw"); +} +@end + +@interface SubThrower : SuperThrower @end +@implementation SubThrower ++(void)initialize { + testprintf("in [SubThrower initialize]\n"); + testassert(state == 0); + state = 20; +} +@end + int main() { Class cls; @@ -271,6 +292,31 @@ int main() [Sub7 class]; testassert(state == 6); + // exception from +initialize must be handled cleanly + PUSH_POOL { + alarm(3); + testonthread( ^{ + @try { + state = 0; + [SuperThrower class]; + fail("where's the beef^Wexception?"); + } @catch (...) { + testassert(state == 10); + state = 11; + } + testassert(state == 11); + }); + @try { + state = 0; + [SuperThrower class]; + testassert(state == 0); + [SubThrower class]; + testassert(state == 20); + } @catch (...) { + fail("+initialize called again after exception"); + } + } POP_POOL; + succeed(__FILE__); return 0; diff --git a/test/initializeVersusWeak.m b/test/initializeVersusWeak.m index c7a0e368..e9c1580d 100644 --- a/test/initializeVersusWeak.m +++ b/test/initializeVersusWeak.m @@ -12,36 +12,40 @@ #include #include "test.h" -#pragma clang diagnostic ignored "-Wdeprecated-declarations" #pragma clang diagnostic ignored "-Warc-unsafe-retained-assign" // This is StripedMap's pointer hash -uintptr_t hash(id obj) { +#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR + enum { StripeCount = 8 }; +#else + enum { StripeCount = 64 }; +#endif +uintptr_t stripehash(id obj) { uintptr_t addr = (uintptr_t)obj; - return ((addr >> 4) ^ (addr >> 9)) % 64; + return ((addr >> 4) ^ (addr >> 9)) % StripeCount; } bool sameAlignment(id o1, id o2) { - return hash(o1) == hash(o2); + return stripehash(o1) == stripehash(o2); } -// Return a new string object that uses the same striped weak locks as `obj`. -NSMutableString *newAlignedString(id obj) +// Return a new non-tagged object that uses the same striped weak locks as `obj` +NSObject *newAlignedObject(id obj) { - NSMutableArray *strings = [NSMutableArray new]; - NSMutableString *result; - do { - result = [NSMutableString new]; - [strings addObject:result]; - } while (!sameAlignment(obj, result)); + // Use immutable arrays because their contents are stored inline, + // which prevents Guard Malloc from using the same alignment for all of them + NSArray *result = [NSArray new]; + while (!sameAlignment(obj, result)) { + result = [result arrayByAddingObject:result]; + } return result; } __weak NSObject *weak1; -__weak NSMutableString *weak2; -NSMutableString *strong2; +__weak NSObject *weak2; +NSObject *strong2; @interface A : NSObject @end @implementation A @@ -58,7 +62,7 @@ void testA() // without holding locks. @autoreleasepool { A *obj = [A new]; - strong2 = newAlignedString(obj); + strong2 = newAlignedObject(obj); [obj addObserver:obj forKeyPath:@"foo" options:0 context:0]; weak1 = obj; // weak store #1 [obj removeObserver:obj forKeyPath:@"foo"]; @@ -68,8 +72,8 @@ void testA() __weak NSObject *weak3; -__weak NSMutableString *weak4; -NSMutableString *strong4; +__weak NSObject *weak4; +NSObject *strong4; @interface B : NSObject @end @implementation B @@ -87,7 +91,7 @@ void testB() // without holding locks. @autoreleasepool { B *obj = [B new]; - strong4 = newAlignedString(obj); + strong4 = newAlignedObject(obj); weak3 = obj; [obj addObserver:obj forKeyPath:@"foo" options:0 context:0]; [weak3 self]; // weak load #3 @@ -116,13 +120,67 @@ void testC() } -int main() +__weak id weak6; +NSObject *strong6; +semaphore_t Dgo; +semaphore_t Ddone; + +void *Dthread(void *arg __unused) +{ + @autoreleasepool { + semaphore_wait(Dgo); + for (int i = 0; i < 1000; i++) { + id x = weak6; + testassert(x == strong6); + } + return nil; + } +} + +@interface D : NSObject @end +@implementation D ++(void)initialize { + strong6 = [self new]; + weak6 = strong6; + semaphore_signal(Dgo); + for (int i = 0; i < 1000; i++) { + id x = weak6; + testassert(x == strong6); + } +} +@end + +void testD() { - alarm(10); // replace hangs with crashes + // +initialize performs a weak store of itself, then another thread + // tries to load that weak variable before +initialize completes. + // Deadlock occurs if the +initialize thread tries to acquire the + // sidetable lock for another operation and the second thread holds + // the sidetable lock while waiting for +initialize. + + @autoreleasepool { + semaphore_create(mach_task_self(), &Dgo, 0, 0); + semaphore_create(mach_task_self(), &Ddone, 0, 0); + pthread_t th; + pthread_create(&th, nil, Dthread, nil); + [D self]; + pthread_join(th, nil); + } +} - testA(); - testB(); - testC(); +int main() +{ + if (is_guardmalloc() && getenv("MALLOC_PROTECT_BEFORE")) { + testwarn("fixme malloc guard before breaks this with debug libobjc"); + } + else { + alarm(10); // replace hangs with crashes + + testA(); + testB(); + testC(); + testD(); + } succeed(__FILE__); } diff --git a/test/isaValidation.m b/test/isaValidation.m new file mode 100644 index 00000000..3a00a47c --- /dev/null +++ b/test/isaValidation.m @@ -0,0 +1,267 @@ +// TEST_CRASHES +// TEST_CONFIG MEM=mrc +/* +TEST_RUN_OUTPUT +Testing object_getMethodImplementation +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_getInstanceMethod +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_getMethodImplementation +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_respondsToSelector +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_conformsToProtocol +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_copyProtocolList +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_getProperty +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_copyPropertyList +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_addMethod +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_replaceMethod +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_addIvar +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_addProtocol +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_addProperty +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_replaceProperty +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_setIvarLayout +objc\[\d+\]: \*\*\* Can't set ivar layout for already-registered class 'TestRoot' +objc\[\d+\]: \*\*\* Can't set ivar layout for already-registered class 'TestRoot' +objc\[\d+\]: \*\*\* Can't set ivar layout for already-registered class 'NSObject' +objc\[\d+\]: \*\*\* Can't set ivar layout for already-registered class 'NSObject' +objc\[\d+\]: \*\*\* Can't set ivar layout for already-registered class 'AllocatedTestClass2' +objc\[\d+\]: \*\*\* Can't set ivar layout for already-registered class 'AllocatedTestClass2' +objc\[\d+\]: \*\*\* Can't set ivar layout for already-registered class 'TestRoot' +objc\[\d+\]: \*\*\* Can't set ivar layout for already-registered class 'DuplicateClass' +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing class_setWeakIvarLayout +objc\[\d+\]: \*\*\* Can't set weak ivar layout for already-registered class 'TestRoot' +objc\[\d+\]: \*\*\* Can't set weak ivar layout for already-registered class 'TestRoot' +objc\[\d+\]: \*\*\* Can't set weak ivar layout for already-registered class 'NSObject' +objc\[\d+\]: \*\*\* Can't set weak ivar layout for already-registered class 'NSObject' +objc\[\d+\]: \*\*\* Can't set weak ivar layout for already-registered class 'AllocatedTestClass2' +objc\[\d+\]: \*\*\* Can't set weak ivar layout for already-registered class 'AllocatedTestClass2' +objc\[\d+\]: \*\*\* Can't set weak ivar layout for already-registered class 'TestRoot' +objc\[\d+\]: \*\*\* Can't set weak ivar layout for already-registered class 'DuplicateClass' +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing objc_registerClassPair +objc\[\d+\]: objc_registerClassPair: class 'TestRoot' was not allocated with objc_allocateClassPair! +objc\[\d+\]: objc_registerClassPair: class 'NSObject' was not allocated with objc_allocateClassPair! +objc\[\d+\]: objc_registerClassPair: class 'AllocatedTestClass2' was already registered! +objc\[\d+\]: objc_registerClassPair: class 'DuplicateClass' was not allocated with objc_allocateClassPair! +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing objc_duplicateClass +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Testing objc_disposeClassPair +objc\[\d+\]: objc_disposeClassPair: class 'TestRoot' was not allocated with objc_allocateClassPair! +objc\[\d+\]: objc_disposeClassPair: class 'NSObject' was not allocated with objc_allocateClassPair! +objc\[\d+\]: objc_disposeClassPair: class 'DuplicateClass' was not allocated with objc_allocateClassPair! +Completed test on good classes. +objc\[\d+\]: Attempt to use unknown class 0x[0-9a-f]+. +objc\[\d+\]: HALTED +Completed! +END + */ + +#include "test.h" +#include "testroot.i" +#include + +@protocol P +@end + +extern char **environ; + +id dummyIMP(id self, SEL _cmd, ...) { (void)_cmd; return self; } + +char *dupeName(Class cls) { + char *name; + asprintf(&name, "%sDuplicate", class_getName(cls)); + return name; +} + +typedef void (^TestBlock)(Class); +struct TestCase { + const char *name; + TestBlock block; +}; + +#define NAMED_TESTCASE(name, ...) { name, ^(Class cls) { __VA_ARGS__; } } +#define TESTCASE(...) NAMED_TESTCASE(#__VA_ARGS__, __VA_ARGS__) +#define TESTCASE_NOMETA(...) \ + NAMED_TESTCASE( #__VA_ARGS__, if(class_isMetaClass(cls)) return; __VA_ARGS__; ) +#define TESTCASE_OBJ(...) NAMED_TESTCASE( \ + #__VA_ARGS__, \ + if(class_isMetaClass(cls)) return; \ + id obj = [TestRoot alloc]; \ + *(Class *)obj = cls; \ + __VA_ARGS__; \ +) + +struct TestCase TestCases[] = { + TESTCASE_OBJ(object_getMethodImplementation(obj, @selector(init))), + + TESTCASE(class_getInstanceMethod(cls, @selector(init))), + TESTCASE(class_getMethodImplementation(cls, @selector(init))), + TESTCASE(class_respondsToSelector(cls, @selector(init))), + TESTCASE(class_conformsToProtocol(cls, @protocol(P))), + TESTCASE(free(class_copyProtocolList(cls, NULL))), + TESTCASE(class_getProperty(cls, "x")), + TESTCASE(free(class_copyPropertyList(cls, NULL))), + TESTCASE(class_addMethod(cls, @selector(nop), dummyIMP, "v@:")), + TESTCASE(class_replaceMethod(cls, @selector(nop), dummyIMP, "v@:")), + TESTCASE(class_addIvar(cls, "x", sizeof(int), sizeof(int), @encode(int))), + TESTCASE(class_addProtocol(cls, @protocol(P))), + TESTCASE(class_addProperty(cls, "x", NULL, 0)), + TESTCASE(class_replaceProperty(cls, "x", NULL, 0)), + TESTCASE(class_setIvarLayout(cls, NULL)), + TESTCASE(class_setWeakIvarLayout(cls, NULL)), + TESTCASE_NOMETA(objc_registerClassPair(cls)), + TESTCASE_NOMETA(objc_duplicateClass(cls, dupeName(cls), 0)), + TESTCASE_NOMETA(objc_disposeClassPair(cls)), +}; + +void parent(char *argv0) +{ + int testCount = sizeof(TestCases) / sizeof(*TestCases); + for (int i = 0; i < testCount; i++) { + char *testIndex; + asprintf(&testIndex, "%d", i); + char *argvSpawn[] = { + argv0, + testIndex, + NULL + }; + pid_t pid; + int result = posix_spawn(&pid, argv0, NULL, NULL, argvSpawn, environ); + if (result != 0) { + fprintf(stderr, "Could not spawn child process: (%d) %s\n", + errno, strerror(errno)); + exit(1); + } + + free(testIndex); + + result = waitpid(pid, NULL, 0); + if (result == -1) { + fprintf(stderr, "Error waiting for termination of child process: (%d) %s\n", + errno, strerror(errno)); + exit(1); + } + } + fprintf(stderr, "Completed!\n"); +} + +void child(char *argv1) +{ + long index = strtol(argv1, NULL, 10); + struct TestCase testCase = TestCases[index]; + TestBlock block = testCase.block; + + const char *name = testCase.name; + if (strncmp(name, "free(", 5) == 0) + name += 5; + const char *paren = strchr(name, '('); + long len = paren != NULL ? paren - name : strlen(name); + fprintf(stderr, "Testing %.*s\n", (int)len, name); + + // Make sure plain classes work. + block([TestRoot class]); + block(object_getClass([TestRoot class])); + + // And framework classes. + block([NSObject class]); + block(object_getClass([NSObject class])); + + // Test a constructed, unregistered class. + Class allocatedClass = objc_allocateClassPair([TestRoot class], + "AllocatedTestClass", + 0); + class_getMethodImplementation(allocatedClass, @selector(self)); + block(object_getClass(allocatedClass)); + block(allocatedClass); + + // Test a constructed, registered class. (Do this separately so + // test cases can dispose of the class if needed.) + allocatedClass = objc_allocateClassPair([TestRoot class], + "AllocatedTestClass2", + 0); + objc_registerClassPair(allocatedClass); + block(object_getClass(allocatedClass)); + block(allocatedClass); + + // Test a duplicated class. + + Class duplicatedClass = objc_duplicateClass([TestRoot class], + "DuplicateClass", + 0); + block(object_getClass(duplicatedClass)); + block(duplicatedClass); + + fprintf(stderr, "Completed test on good classes.\n"); + + // Test a fake class. + Class templateClass = objc_allocateClassPair([TestRoot class], + "TemplateClass", + 0); + void *fakeClass = malloc(malloc_size(templateClass)); + memcpy(fakeClass, templateClass, malloc_size(templateClass)); + block((Class)fakeClass); + fail("Should have died on the fake class"); +} + +int main(int argc, char **argv) +{ + // We want to run a bunch of tests, all of which end in _objc_fatal + // (at least if they succeed). Spawn one subprocess per test and + // have the parent process manage it all. The test will begin by + // running parent(), which will repeatedly re-spawn this program to + // call child() with the index of the test to run. + if (argc == 1) { + parent(argv[0]); + } else { + child(argv[1]); + } +} diff --git a/test/ivar.m b/test/ivar.m index 84bda908..871dbf0a 100644 --- a/test/ivar.m +++ b/test/ivar.m @@ -1,4 +1,19 @@ -// TEST_CONFIG +/* +TEST_BUILD_OUTPUT +.*ivar.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*ivar.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*ivar.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*ivar.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*ivar.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*ivar.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*ivar.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*ivar.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*ivar.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*ivar.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*ivar.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*ivar.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +END +*/ #include "test.h" #include "testroot.i" @@ -34,7 +49,7 @@ int main() Ivar ivar; Sub *sub = [Sub new]; sub->subIvar = [Sub class]; - testassert(((Class *)objc_unretainedPointer(sub))[2] == [Sub class]); + testassert(((Class *)(__bridge void *)sub)[2] == [Sub class]); ivar = class_getInstanceVariable([Sub class], "subIvar"); testassert(ivar); @@ -103,6 +118,14 @@ int main() testassert(NULL == object_setInstanceVariable(sub, NULL, NULL)); testassert(NULL == object_setInstanceVariable(NULL, "foo", NULL)); testassert(NULL == object_setInstanceVariable(NULL, NULL, NULL)); +#else + // provoke the same nullability warnings as the real test + objc_getClass(nil); + objc_getClass(nil); + objc_getClass(nil); + objc_getClass(nil); + objc_getClass(nil); + objc_getClass(nil); #endif succeed(__FILE__); diff --git a/test/ivarSlide.m b/test/ivarSlide.m index 43c3660e..dcc0bccc 100644 --- a/test/ivarSlide.m +++ b/test/ivarSlide.m @@ -1,6 +1,6 @@ /* TEST_BUILD - $C{COMPILE} $DIR/ivarSlide1.m $DIR/ivarSlide.m -o ivarSlide.out + $C{COMPILE} -fobjc-weak $DIR/ivarSlide1.m $DIR/ivarSlide.m -o ivarSlide.exe END */ @@ -10,14 +10,13 @@ #include #include +// fixme should check ARC layout handling +// current test checks GC layout handling which is dead +#define FIXME_CHECK_ARC_LAYOUTS 0 + // ARC doesn't like __strong void* or __weak void* -#if __OBJC_GC__ -# define gc_weak __weak -# define gc_strong __strong -#else -# define gc_weak -# define gc_strong -#endif +#define gc_weak +#define gc_strong #define OLD 1 #include "ivarSlide.h" @@ -110,8 +109,6 @@ @implementation RunsOf15Sub @end int main(int argc __attribute__((unused)), char **argv) { -#if __OBJC2__ - #if __has_feature(objc_arc) testwarn("fixme check ARC layouts too"); #endif @@ -143,7 +140,7 @@ int main(int argc __attribute__((unused)), char **argv) testassert(class_getInstanceSize([Bitfields class]) == 7*sizeof(void*)); - if (objc_collectingEnabled()) { + if (FIXME_CHECK_ARC_LAYOUTS) { const uint8_t *bitfieldlayout; bitfieldlayout = class_getIvarLayout([Bitfields class]); testassert(0 == ustrcmp(bitfieldlayout, "\x01\x21\x21")); @@ -178,23 +175,19 @@ int main(int argc __attribute__((unused)), char **argv) static Sub * volatile sub; sub = [Sub new]; sub->subIvar = 10; - testassert(((uintptr_t *)objc_unretainedPointer(sub))[2] == 10); + uintptr_t *subwords = (uintptr_t *)(__bridge void*)sub; + testassert(subwords[2] == 10); #ifdef __cplusplus - testassert(((uintptr_t *)objc_unretainedPointer(sub))[5] == 1); + testassert(subwords[5] == 1); testassert(sub->cxx.magic == 1); sub->cxx.magic++; - testassert(((uintptr_t *)objc_unretainedPointer(sub))[5] == 2); + testassert(subwords[5] == 2); testassert(sub->cxx.magic == 2); # if __has_feature(objc_arc) sub = nil; # else - if (! objc_collectingEnabled()) { - [sub dealloc]; - } else { - // hack - can't get collector to reliably delete the object - object_dispose(sub); - } + [sub dealloc]; # endif testassert(CXX::count == 2); #endif @@ -205,14 +198,13 @@ int main(int argc __attribute__((unused)), char **argv) 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) + testassert(0 == strcmp(ivar_getTypeEncoding(ivar), #if __LP64__ - testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "Q")); -#elif __clang__ - testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "L")); + "Q" #else - testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "I")); + "L" #endif + )); #ifdef __cplusplus ivar = class_getInstanceVariable([Sub class], "cxx"); @@ -228,7 +220,7 @@ int main(int argc __attribute__((unused)), char **argv) ivar = class_getInstanceVariable([Super class], "subIvar"); testassert(!ivar); - if (objc_collectingEnabled()) { + if (FIXME_CHECK_ARC_LAYOUTS) { const uint8_t *superlayout; const uint8_t *sublayout; superlayout = class_getIvarLayout([Super class]); @@ -262,8 +254,9 @@ int main(int argc __attribute__((unused)), char **argv) */ Sub2 *sub2 = [Sub2 new]; + uintptr_t *sub2words = (uintptr_t *)(__bridge void*)sub2; sub2->subIvar = (void *)10; - testassert(((uintptr_t *)objc_unretainedPointer(sub2))[11] == 10); + testassert(sub2words[11] == 10); testassert(class_getInstanceSize([Sub2 class]) == 13*sizeof(void*)); @@ -275,7 +268,7 @@ int main(int argc __attribute__((unused)), char **argv) ivar = class_getInstanceVariable([ShrinkingSuper class], "superIvar"); testassert(!ivar); - if (objc_collectingEnabled()) { + if (FIXME_CHECK_ARC_LAYOUTS) { const uint8_t *superlayout; const uint8_t *sublayout; superlayout = class_getIvarLayout([ShrinkingSuper class]); @@ -311,7 +304,7 @@ int main(int argc __attribute__((unused)), char **argv) [1 skip] d [2 skip] superc1, superc2, subc3 */ - if (objc_collectingEnabled()) { + if (FIXME_CHECK_ARC_LAYOUTS) { Ivar ivar1 = class_getInstanceVariable([NoGCChangeSub class], "superc1"); testassert(ivar1); Ivar ivar2 = class_getInstanceVariable([NoGCChangeSub class], "superc2"); @@ -326,7 +319,7 @@ int main(int argc __attribute__((unused)), char **argv) /* Ivar layout includes runs of 15 words. rdar://6859875 this would generate a truncated GC layout. */ - if (objc_collectingEnabled()) { + if (FIXME_CHECK_ARC_LAYOUTS) { const uint8_t *layout = class_getIvarLayout(objc_getClass("RunsOf15Sub")); testassert(layout); @@ -342,9 +335,6 @@ int main(int argc __attribute__((unused)), char **argv) testassert(totalScan >= 30); } -// __OBJC2__ -#endif - /* Non-strong -> strong @@ -362,7 +352,7 @@ Both new and old ABI detect this case (rdar://5774578) [2 scan] subIvar */ testassert(class_getInstanceSize([MoreStrongSub class]) == 3*sizeof(void*)); - if (objc_collectingEnabled()) { + if (FIXME_CHECK_ARC_LAYOUTS) { const uint8_t *layout; layout = class_getIvarLayout([MoreStrongSub class]); testassert(layout == NULL); @@ -388,23 +378,13 @@ Old ABI intentionally does not detect this case (rdar://5774578) [2 scan] subIvar */ testassert(class_getInstanceSize([MoreWeakSub class]) == 3*sizeof(void*)); - if (objc_collectingEnabled()) { + if (FIXME_CHECK_ARC_LAYOUTS) { 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 } @@ -424,18 +404,14 @@ Old ABI intentionally does not detect this case (rdar://5774578) [2 scan] subIvar */ testassert(class_getInstanceSize([MoreWeak2Sub class]) == 3*sizeof(void*)); - if (objc_collectingEnabled()) { + if (FIXME_CHECK_ARC_LAYOUTS) { 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 } @@ -455,16 +431,10 @@ Old ABI intentionally does not detect this case (rdar://5774578) [2 scan] subIvar */ testassert(class_getInstanceSize([LessStrongSub class]) == 3*sizeof(void*)); - if (objc_collectingEnabled()) { + if (FIXME_CHECK_ARC_LAYOUTS) { 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); @@ -487,7 +457,7 @@ Both new and old ABI detect this case (rdar://5774578 rdar://6924114) [2 scan] subIvar */ testassert(class_getInstanceSize([LessWeakSub class]) == 3*sizeof(void*)); - if (objc_collectingEnabled()) { + if (FIXME_CHECK_ARC_LAYOUTS) { const uint8_t *layout; layout = class_getIvarLayout([LessWeakSub class]); testassert(layout == NULL); @@ -513,18 +483,14 @@ Old ABI intentionally does not detect this case (rdar://5774578) [2 scan] subIvar */ testassert(class_getInstanceSize([LessWeak2Sub class]) == 3*sizeof(void*)); - if (objc_collectingEnabled()) { + if (FIXME_CHECK_ARC_LAYOUTS) { 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 } diff --git a/test/layout.m b/test/layout.m deleted file mode 100644 index 6c220fb7..00000000 --- a/test/layout.m +++ /dev/null @@ -1,97 +0,0 @@ -// TEST_CONFIG MEM=gc OS=macosx - -#include "test.h" -#include -#include - -@class NSObject; - -void printlayout(const char *name, const uint8_t *layout) -{ - testprintf("%s: ", name); - - if (!layout) { - testprintf("NULL\n"); - return; - } - - const uint8_t *c; - for (c = layout; *c; c++) { - testprintf("%02x ", *c); - } - - testprintf("00\n"); -} - -OBJC_ROOT_CLASS -@interface Super { id isa; } @end -@implementation Super @end - - -// strong: 0c 00 (0a00 without structs) -// weak: NULL -@interface AllScanned : Super { - id id1; - NSObject *o1; - __strong void *v1; - __strong intptr_t *i1; - __strong long *l1; - /* fixme - struct { - id id1; - id id2; - } str; - */ - id arr1[4]; -} -@end -@implementation AllScanned @end - -// strong: 00 -// weak: 1b 00 (18 00 without structs) -@interface AllWeak : Super { - __weak id id1; - __weak NSObject *o1; - __weak void *v1; - __weak intptr_t *i1; - __weak long *l1; - /* fixme - struct { - __weak id id1; - __weak id id2; - } str; - */ - __weak id arr1[4]; -} -@end -@implementation AllWeak @end - -// strong: "" -// weak: NULL -OBJC_ROOT_CLASS -@interface NoScanned { long i; } @end -@implementation NoScanned @end - -int main() -{ - const uint8_t *layout; - - layout = class_getIvarLayout(objc_getClass("AllScanned")); - printlayout("AllScanned", layout); - layout = class_getWeakIvarLayout(objc_getClass("AllScanned")); - printlayout("AllScanned weak", layout); - // testassert(0 == strcmp(layout, "\x0a")); - - layout = class_getIvarLayout(objc_getClass("AllWeak")); - printlayout("AllWeak", layout); - layout = class_getWeakIvarLayout(objc_getClass("AllWeak")); - printlayout("AllWeak weak", layout); - // testassert(0 == strcmp(layout, "")); - - layout = class_getIvarLayout(objc_getClass("NoScanned")); - printlayout("NoScanned", layout); - // testassert(0 == strcmp(layout, "")); - - succeed(__FILE__); - return 0; -} diff --git a/test/literals.m b/test/literals.m index 17fffd2e..e43673ad 100644 --- a/test/literals.m +++ b/test/literals.m @@ -1,4 +1,3 @@ -// TEST_CONFIG MEM=arc,mrc CC=clang LANGUAGE=objc,objc++ // TEST_CFLAGS -framework Foundation #import diff --git a/test/load-noobjc.m b/test/load-noobjc.m index e877e170..4dd9f86b 100644 --- a/test/load-noobjc.m +++ b/test/load-noobjc.m @@ -1,23 +1,12 @@ /* TEST_BUILD - $C{COMPILE} $DIR/load-noobjc.m -o load-noobjc.out - $C{COMPILE} $DIR/load-noobjc2.m -o libload-noobjc2.dylib -bundle -bundle_loader load-noobjc.out - $C{COMPILE} $DIR/load-noobjc3.m -o libload-noobjc3.dylib -bundle -bundle_loader load-noobjc.out + $C{COMPILE} $DIR/load-noobjc.m -o load-noobjc.exe + $C{COMPILE} $DIR/load-noobjc2.m -o libload-noobjc2.dylib -bundle -bundle_loader load-noobjc.exe + $C{COMPILE} $DIR/load-noobjc3.m -o libload-noobjc3.dylib -bundle -bundle_loader load-noobjc.exe END */ #include "test.h" - -#if !__OBJC2__ -// old runtime can't fix this deadlock - -int main() -{ - succeed(__FILE__); -} - -#else - #include int state = 0; @@ -25,7 +14,6 @@ int main() void *thread(void *arg __unused) { - objc_registerThreadWithCollector(); dlopen("libload-noobjc2.dylib", RTLD_LAZY); fail("dlopen should not have returned"); } @@ -48,5 +36,3 @@ int main() succeed(__FILE__); } - -#endif diff --git a/test/load-noobjc2.m b/test/load-noobjc2.m index e8cf37de..bcca510b 100644 --- a/test/load-noobjc2.m +++ b/test/load-noobjc2.m @@ -1,5 +1,4 @@ #include "test.h" -#if __OBJC2__ extern semaphore_t go; @@ -12,5 +11,3 @@ +(void)load while (1) sleep(1); } @end - -#endif diff --git a/test/load-noobjc3.m b/test/load-noobjc3.m index fc7cdb69..98e40712 100644 --- a/test/load-noobjc3.m +++ b/test/load-noobjc3.m @@ -1,7 +1,5 @@ #include "test.h" -#if __OBJC2__ - extern int state; __attribute__((constructor)) @@ -9,5 +7,3 @@ static void ctor(void) { state = 1; } - -#endif diff --git a/test/load-order.m b/test/load-order.m index fd093704..a0eb8f4a 100644 --- a/test/load-order.m +++ b/test/load-order.m @@ -3,7 +3,7 @@ $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 + $C{COMPILE} $DIR/load-order.m -o load-order.exe -x none load-order3.dylib load-order2.dylib load-order1.dylib END */ diff --git a/test/load-parallel.m b/test/load-parallel.m index f050ea55..5b5bd946 100644 --- a/test/load-parallel.m +++ b/test/load-parallel.m @@ -1,7 +1,7 @@ /* 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-parallel.m -x none load-parallel00.dylib -o load-parallel.exe -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 @@ -25,15 +25,13 @@ #error -DCOUNT=c missing #endif -extern int state; +extern atomic_int state; void *thread(void *arg) { uintptr_t num = (uintptr_t)arg; char *buf; - objc_registerThreadWithCollector(); - asprintf(&buf, "load-parallel%lu.dylib", (unsigned long)num); testprintf("%s\n", buf); void *dlh = dlopen(buf, RTLD_LAZY); @@ -58,7 +56,7 @@ int main() pthread_join(t[i], NULL); } - testprintf("loaded %d/%d\n", state, COUNT*26); + testprintf("loaded %d/%d\n", (int)state, COUNT*26); testassert(state == COUNT*26); succeed(__FILE__); diff --git a/test/load-parallel0.m b/test/load-parallel0.m index 9f3a3e1f..2e135e9f 100644 --- a/test/load-parallel0.m +++ b/test/load-parallel0.m @@ -6,14 +6,16 @@ #include #include #include -#include -extern int state; +#include "test.h" +extern atomic_int state; #define CLASS0(n,nn) \ OBJC_ROOT_CLASS \ @interface C_##n##_##nn @end \ @implementation C_##n##_##nn \ - +(void)load { OSAtomicIncrement32(&state); usleep(10); } \ + +(void)load { \ + atomic_fetch_add_explicit(&state, 1, memory_order_relaxed); \ + usleep(10); } \ @end #define CLASS(n,nn) CLASS0(n,nn) diff --git a/test/load-parallel00.m b/test/load-parallel00.m index 4bef2b64..9df43b4e 100644 --- a/test/load-parallel00.m +++ b/test/load-parallel00.m @@ -1 +1,2 @@ -int state = 0; +#include "test.h" +atomic_int state; diff --git a/test/load-reentrant.m b/test/load-reentrant.m index 25caf77d..1dac2f2e 100644 --- a/test/load-reentrant.m +++ b/test/load-reentrant.m @@ -1,7 +1,7 @@ /* 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 + $C{COMPILE} $DIR/load-reentrant.m -o load-reentrant.exe + $C{COMPILE} $DIR/load-reentrant2.m -o libload-reentrant2.dylib -bundle -bundle_loader load-reentrant.exe END */ diff --git a/test/load.m b/test/load.m index 92bcdfe6..9e728701 100644 --- a/test/load.m +++ b/test/load.m @@ -9,13 +9,6 @@ @interface Deallocator : TestRoot @end @implementation Deallocator --(id)init { - self = [super init]; - if (objc_collectingEnabled()) { - deallocstate = 1; - } - return self; -} -(void)dealloc { deallocstate = 1; SUPER_DEALLOC(); diff --git a/test/methodArgs.m b/test/methodArgs.m index 8948e9aa..91af0940 100644 --- a/test/methodArgs.m +++ b/test/methodArgs.m @@ -1,4 +1,24 @@ -// TEST_CFLAGS -Wno-deprecated-declarations +/* +TEST_CFLAGS -Wno-deprecated-declarations +TEST_BUILD_OUTPUT +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*methodArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +END +*/ #include "test.h" #include "testroot.i" @@ -22,9 +42,6 @@ int main() testassert(m); testassert(method_getNumberOfArguments(m) == 4); -#if !__OBJC2__ - testassert(method_getSizeOfArguments(m) == 16); -#endif arg = method_copyArgumentType(m, 0); testassert(arg); @@ -123,9 +140,6 @@ int main() #endif testassert(0 == method_getNumberOfArguments(NULL)); -#if !__OBJC2__ - testassert(0 == method_getSizeOfArguments(NULL)); -#endif testassert(NULL == method_copyArgumentType(NULL, 10)); testassert(NULL == method_copyReturnType(NULL)); testassert(NULL == method_getDescription(NULL)); diff --git a/test/method_getName.m b/test/method_getName.m deleted file mode 100644 index f3f74134..00000000 --- a/test/method_getName.m +++ /dev/null @@ -1,27 +0,0 @@ -// TEST_CONFIG - -#include "test.h" -#include -#include - -#undef SUPPORT_NONPOINTER_ISA // remove test.h's definition -#include "../runtime/objc-config.h" - -int main() { - unsigned i; - Class c = [NSObject class]; - unsigned numMethods; - Method *methods = class_copyMethodList(c, &numMethods); - - for (i=0; i method_getName crash on NSObject method when GC is enabled - SEL aMethod; - aMethod = method_getName(methods[i]); -#if defined(kIgnore) - if (aMethod == (SEL)kIgnore) - fail(__FILE__); -#endif - } - - succeed(__FILE__); -} diff --git a/test/msgSend-performance.m b/test/msgSend-performance.m new file mode 100644 index 00000000..30e1716b --- /dev/null +++ b/test/msgSend-performance.m @@ -0,0 +1,176 @@ +// TEST_CONFIG + +#include "test.h" +#include "testroot.i" +#include + +#if defined(__arm__) +// rdar://8331406 +# define ALIGN_() +#else +# define ALIGN_() asm(".align 4"); +#endif + + +@interface Super : TestRoot @end + +@implementation Super + +-(void)voidret_nop +{ + return; +} + +-(void)voidret_nop2 +{ + return; +} + +-(id)idret_nop +{ + return nil; +} + +-(long long)llret_nop +{ + return 0; +} + +-(struct stret)stret_nop +{ + return STRET_RESULT; +} + +-(double)fpret_nop +{ + return 0; +} + +-(long double)lfpret_nop +{ + return 0; +} + +-(vector_ulong2)vecret_nop +{ + return (vector_ulong2){0x1234567890abcdefULL, 0xfedcba0987654321ULL}; +} + +@end + + +@interface Sub : Super @end + +@implementation Sub @end + + +int main() +{ + + // cached message performance + // catches failure to cache or (abi=2) failure to fixup (#5584187) + // fixme unless they all fail + + uint64_t startTime; + uint64_t totalTime; + uint64_t targetTime; + + Sub *sub = [Sub new]; + + // fill cache first + + [sub voidret_nop]; + [sub voidret_nop2]; + [sub llret_nop]; + [sub stret_nop]; + [sub fpret_nop]; + [sub lfpret_nop]; + [sub vecret_nop]; + [sub voidret_nop]; + [sub voidret_nop2]; + [sub llret_nop]; + [sub stret_nop]; + [sub fpret_nop]; + [sub lfpret_nop]; + [sub vecret_nop]; + [sub voidret_nop]; + [sub voidret_nop2]; + [sub llret_nop]; + [sub stret_nop]; + [sub fpret_nop]; + [sub lfpret_nop]; + [sub vecret_nop]; + + // 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. + + // Use voidret because id return is too slow for perf test with ARC. + + // Pick smallest of voidret_nop and voidret_nop2 time + // in the hopes that one of them didn't collide in the method cache. + + // ALIGN_ matches loop alignment to make -O0 work + +#define COUNT 1000000 + + startTime = mach_absolute_time(); + ALIGN_(); + for (int i = 0; i < COUNT; i++) { + [sub voidret_nop]; + } + totalTime = mach_absolute_time() - startTime; + testprintf("time: voidret %llu\n", totalTime); + targetTime = totalTime; + + startTime = mach_absolute_time(); + ALIGN_(); + for (int i = 0; i < COUNT; i++) { + [sub voidret_nop2]; + } + totalTime = mach_absolute_time() - startTime; + testprintf("time: voidret2 %llu\n", totalTime); + if (totalTime < targetTime) targetTime = totalTime; + + startTime = mach_absolute_time(); + ALIGN_(); + for (int i = 0; i < COUNT; i++) { + [sub llret_nop]; + } + totalTime = mach_absolute_time() - startTime; + timecheck("llret ", totalTime, targetTime * 0.65, targetTime * 2.0); + + startTime = mach_absolute_time(); + ALIGN_(); + for (int i = 0; i < COUNT; i++) { + [sub stret_nop]; + } + totalTime = mach_absolute_time() - startTime; + timecheck("stret ", totalTime, targetTime * 0.65, targetTime * 5.0); + + startTime = mach_absolute_time(); + ALIGN_(); + for (int i = 0; i < COUNT; i++) { + [sub fpret_nop]; + } + totalTime = mach_absolute_time() - startTime; + timecheck("fpret ", totalTime, targetTime * 0.65, targetTime * 4.0); + + startTime = mach_absolute_time(); + ALIGN_(); + for (int i = 0; i < COUNT; i++) { + [sub lfpret_nop]; + } + totalTime = mach_absolute_time() - startTime; + timecheck("lfpret", totalTime, targetTime * 0.65, targetTime * 4.0); + + startTime = mach_absolute_time(); + ALIGN_(); + for (int i = 0; i < COUNT; i++) { + [sub vecret_nop]; + } + totalTime = mach_absolute_time() - startTime; + timecheck("vecret", totalTime, targetTime * 0.65, targetTime * 4.0); + + succeed(__FILE__); +} diff --git a/test/msgSend.m b/test/msgSend.m index bd3e9b16..590dcf0f 100644 --- a/test/msgSend.m +++ b/test/msgSend.m @@ -1,23 +1,22 @@ -// TEST_CFLAGS -Wno-unused-parameter -Wundeclared-selector +/* +asm-placeholder.exe is used below to disassemble objc_msgSend + +TEST_BUILD + $C{COMPILE} -x assembler $DIR/asm-placeholder.s -o asm-placeholder.exe + $C{COMPILE} $DIR/msgSend.m -o msgSend.exe -Wno-unused-parameter -Wundeclared-selector -D__DARWIN_OPAQUE_ARM_THREAD_STATE64=1 +END +*/ #include "test.h" #include "testroot.i" -#if __cplusplus && !__clang__ - -int main() -{ - // llvm-g++ is confused by @selector(foo::) and will never be fixed - succeed(__FILE__); -} - -#else - +#include #include #include #include #include #include +#include // rdar://21694990 simd.h should have a vector_equal(a, b) function static bool vector_equal(vector_ulong2 lhs, vector_ulong2 rhs) { @@ -31,6 +30,8 @@ static bool vector_equal(vector_ulong2 lhs, vector_ulong2 rhs) { # define objc_msgSendSuper2_stret objc_msgSendSuper2 # define objc_msgSend_stret_debug objc_msgSend_debug # define objc_msgSendSuper2_stret_debug objc_msgSendSuper2_debug +# define objc_msgLookup_stret objc_msgLookup +# define objc_msgLookupSuper2_stret objc_msgLookupSuper2 # define method_invoke_stret method_invoke #else # define SUPPORT_STRET 1 @@ -73,14 +74,6 @@ @interface Sub : Super @end do { \ testassert(self == SELF); \ testassert(_cmd == sel_registerName(#sel "::::::::::::::::::::::::::::::::::::"));\ - testassert(vector_all(v1 == 1)); \ - testassert(vector_all(v2 == 2)); \ - testassert(vector_all(v3 == 3)); \ - testassert(vector_all(v4 == 4)); \ - testassert(vector_all(v5 == 5)); \ - testassert(vector_all(v6 == 6)); \ - testassert(vector_all(v7 == 7)); \ - testassert(vector_all(v8 == 8)); \ testassert(i1 == 1); \ testassert(i2 == 2); \ testassert(i3 == 3); \ @@ -109,6 +102,14 @@ @interface Sub : Super @end testassert(f13 == 13.0); \ testassert(f14 == 14.0); \ testassert(f15 == 15.0); \ + testassert(vector_all(v1 == 1)); \ + testassert(vector_all(v2 == 2)); \ + testassert(vector_all(v3 == 3)); \ + testassert(vector_all(v4 == 4)); \ + testassert(vector_all(v5 == 5)); \ + testassert(vector_all(v6 == 6)); \ + testassert(vector_all(v7 == 7)); \ + testassert(vector_all(v8 == 8)); \ } while (0) #define CHECK_ARGS_NOARG(sel) \ @@ -379,10 +380,6 @@ -(vector_ulong2)vecret: "\n vmov.i32 q1, #0" "\n vmov.i32 q2, #0" "\n vmov.i32 q3, #0" - "\n vmov.i32 q4, #0" - "\n vmov.i32 q5, #0" - "\n vmov.i32 q6, #0" - "\n vmov.i32 q7, #0" "\n vmov.i32 q8, #0" "\n vmov.i32 q9, #0" "\n vmov.i32 q10, #0" @@ -517,45 +514,11 @@ -(vector_ulong2)vecret_noarg } --(void)voidret_nop -{ - return; -} - --(void)voidret_nop2 -{ - return; -} - --(id)idret_nop -{ - return ID_RESULT; -} - --(long long)llret_nop -{ - return LL_RESULT; -} - -(struct stret)stret_nop { return STRET_RESULT; } --(double)fpret_nop -{ - return FP_RESULT; -} - --(long double)lfpret_nop -{ - return LFP_RESULT; -} - --(vector_ulong2)vecret_nop -{ - return VEC_RESULT; -} #define STRET_IMP(n) \ +(struct stret_##n)stret_##n##_zero \ @@ -830,7 +793,18 @@ @implementation TaggedSub : Sub +(void)initialize { - _objc_registerTaggedPointerClass(OBJC_TAG_7, self); + _objc_registerTaggedPointerClass(OBJC_TAG_1, self); +} + +@end + +@interface ExtTaggedSub : Sub @end + +@implementation ExtTaggedSub : Sub + ++(void)initialize +{ + _objc_registerTaggedPointerClass(OBJC_TAG_First52BitPayload, self); } @end @@ -842,12 +816,16 @@ +(void)initialize #if TARGET_OS_WIN32 // unimplemented on this platform -#elif !__OBJC2__ -// 32-bit Mac doesn't use DWARF unwind -#elif TARGET_OS_IPHONE && __arm__ -// 32-bit iOS device doesn't use DWARF unwind +#define NO_DWARF_REASON "(windows)" + +#elif TARGET_OS_WATCH +// fixme unimplemented - ucontext not passed to signal handlers +#define NO_DWARF_REASON "(watchOS)" + #elif __has_feature(objc_arc) // ARC's extra RR calls hit the traps at the wrong times +#define NO_DWARF_REASON "(ARC)" + #else #define TEST_DWARF 1 @@ -867,9 +845,6 @@ @implementation SubDW @end #include #include -#define UNW_STEP_SUCCESS 1 -#define UNW_STEP_END 0 - bool caught = false; uintptr_t clobbered; @@ -880,10 +855,10 @@ @implementation SubDW @end #if __x86_64__ -#define OTOOL "/usr/bin/xcrun otool -arch x86_64 " - typedef uint8_t insn_t; -#define BREAK_INSN ((insn_t)0xcc) // int3 +typedef insn_t clobbered_insn_t; +#define BREAK_INSN ((insn_t)0x06) // undefined +#define BREAK_SIGNAL SIGILL uintptr_t r12 = 0; uintptr_t r13 = 0; @@ -905,7 +880,7 @@ void handle_exception(x86_thread_state64_t *state) testassert(!err); step = unw_step(&curs); - testassert(step == UNW_STEP_SUCCESS); + testassert(step > 0); err = unw_get_reg(&curs, UNW_X86_64_R12, ®); testassert(!err); @@ -954,15 +929,15 @@ void handle_exception(x86_thread_state64_t *state) } -void sigtrap(int sig, siginfo_t *info, void *cc) +void break_handler(int sig, siginfo_t *info, void *cc) { ucontext_t *uc = (ucontext_t *)cc; mcontext_t mc = (mcontext_t)uc->uc_mcontext; testprintf(" handled\n"); - testassert(sig == SIGTRAP); - testassert((uintptr_t)info->si_addr-1 == clobbered); + testassert(sig == BREAK_SIGNAL); + testassert((uintptr_t)info->si_addr == clobbered); handle_exception(&mc->__ss); // handle_exception changed register state for continuation @@ -1011,10 +986,10 @@ void sigtrap(int sig, siginfo_t *info, void *cc) #elif __i386__ -#define OTOOL "/usr/bin/xcrun otool -arch i386 " - typedef uint8_t insn_t; +typedef insn_t clobbered_insn_t; #define BREAK_INSN ((insn_t)0xcc) // int3 +#define BREAK_SIGNAL SIGTRAP uintptr_t eip = 0; uintptr_t esp = 0; @@ -1035,7 +1010,7 @@ void handle_exception(i386_thread_state_t *state) testassert(!err); step = unw_step(&curs); - testassert(step == UNW_STEP_SUCCESS); + testassert(step > 0); err = unw_get_reg(&curs, UNW_REG_IP, ®); testassert(!err); @@ -1074,14 +1049,14 @@ void handle_exception(i386_thread_state_t *state) } -void sigtrap(int sig, siginfo_t *info, void *cc) +void break_handler(int sig, siginfo_t *info, void *cc) { ucontext_t *uc = (ucontext_t *)cc; mcontext_t mc = (mcontext_t)uc->uc_mcontext; testprintf(" handled\n"); - testassert(sig == SIGTRAP); + testassert(sig == BREAK_SIGNAL); testassert((uintptr_t)info->si_addr-1 == clobbered); handle_exception(&mc->__ss); @@ -1134,11 +1109,10 @@ void sigtrap(int sig, siginfo_t *info, void *cc) #include -// runs on iOS device, no xcrun command present -#define OTOOL "/usr/bin/otool -arch arm64 " - typedef uint32_t insn_t; +typedef insn_t clobbered_insn_t; #define BREAK_INSN ((insn_t)0xd4200020) // brk #1 +#define BREAK_SIGNAL SIGTRAP uintptr_t x19 = 0; uintptr_t x20 = 0; @@ -1161,11 +1135,29 @@ void handle_exception(arm_thread_state64_t *state) int err; int step; - err = unw_init_local(&curs, (unw_context_t *)state); + // libunwind layout differs from mcontext layout + // GPRs are the same but vector registers are not + unw_context_t unwstate; + unw_getcontext(&unwstate); + memcpy(&unwstate, state, sizeof(*state)); + + // libunwind and xnu sign some pointers differently + // xnu: not signed (fixme this may change?) + // libunwind: PC and LR both signed with return address key and SP + void **pcp = &((arm_thread_state64_t *)&unwstate)->__opaque_pc; + *pcp = ptrauth_sign_unauthenticated((void*)__darwin_arm_thread_state64_get_pc(*state), + ptrauth_key_return_address, + (ptrauth_extra_data_t)__darwin_arm_thread_state64_get_sp(*state)); + void **lrp = &((arm_thread_state64_t *)&unwstate)->__opaque_lr; + *lrp = ptrauth_sign_unauthenticated((void*)__darwin_arm_thread_state64_get_lr(*state), + ptrauth_key_return_address, + (ptrauth_extra_data_t)__darwin_arm_thread_state64_get_sp(*state)); + + err = unw_init_local(&curs, &unwstate); testassert(!err); step = unw_step(&curs); - testassert(step == UNW_STEP_SUCCESS); + testassert(step > 0); err = unw_get_reg(&curs, UNW_ARM64_X19, ®); testassert(!err); @@ -1217,6 +1209,8 @@ void handle_exception(arm_thread_state64_t *state) err = unw_get_reg(&curs, UNW_REG_IP, ®); testassert(!err); + // libunwind's return is signed but our value is not + reg = (uintptr_t)ptrauth_strip((void *)reg, ptrauth_key_return_address); testassert(reg == pc); // libunwind restores PC into LR and doesn't track LR @@ -1224,10 +1218,10 @@ void handle_exception(arm_thread_state64_t *state) // testassert(!err); // testassert(reg == lr); - // set thread state to unwound state + // set signal handler's thread state to unwound state state->__x[19] = x19; state->__x[20] = x20; - state->__x[20] = x21; + state->__x[21] = x21; state->__x[22] = x22; state->__x[23] = x23; state->__x[24] = x24; @@ -1235,23 +1229,23 @@ void handle_exception(arm_thread_state64_t *state) state->__x[26] = x26; state->__x[27] = x27; state->__x[28] = x28; - state->__fp = fp; - state->__lr = pc; // libunwind restores PC into LR - state->__sp = sp; - state->__pc = pc; + state->__opaque_fp = (void *)fp; + state->__opaque_lr = (void *)pc; // libunwind restores PC into LR + state->__opaque_sp = (void *)sp; + state->__opaque_pc = (void *)pc; caught = true; } -void sigtrap(int sig, siginfo_t *info, void *cc) +void break_handler(int sig, siginfo_t *info, void *cc) { ucontext_t *uc = (ucontext_t *)cc; struct __darwin_mcontext64 *mc = (struct __darwin_mcontext64 *)uc->uc_mcontext; testprintf(" handled\n"); - testassert(sig == SIGTRAP); + testassert(sig == BREAK_SIGNAL); testassert((uintptr_t)info->si_addr == clobbered); handle_exception(&mc->__ss); @@ -1297,6 +1291,167 @@ void sigtrap(int sig, siginfo_t *info, void *cc) // arm64 +#elif __arm__ + +#include + +typedef uint16_t insn_t; +typedef struct { + insn_t first; + insn_t second; + bool thirty_two; +} clobbered_insn_t; +#define BREAK_INSN ((insn_t)0xdefe) // trap +#define BREAK_SIGNAL SIGILL +#define BREAK_SIGNAL2 SIGTRAP + +uintptr_t r4 = 0; +uintptr_t r5 = 0; +uintptr_t r6 = 0; +uintptr_t r7 = 0; +uintptr_t r8 = 0; +uintptr_t r10 = 0; +uintptr_t r11 = 0; +uintptr_t sp = 0; +uintptr_t pc = 0; + +void handle_exception(arm_thread_state_t *state) +{ + // No unwind tables on this architecture so no libunwind checks. + // We run the test anyway to verify instruction-level coverage. + + // set thread state to unwound state + state->__r[4] = r4; + state->__r[5] = r5; + state->__r[6] = r6; + state->__r[7] = r7; + state->__r[8] = r8; + state->__r[10] = r10; + state->__r[11] = r11; + state->__sp = sp; + state->__pc = pc; + // clear IT... bits so caller doesn't act on them + state->__cpsr &= ~0x0600fc00; + + caught = true; +} + + +void break_handler(int sig, siginfo_t *info, void *cc) +{ + ucontext_t *uc = (ucontext_t *)cc; + struct __darwin_mcontext32 *mc = (struct __darwin_mcontext32 *)uc->uc_mcontext; + + testprintf(" handled\n"); + + testassert(sig == BREAK_SIGNAL || sig == BREAK_SIGNAL2); + testassert((uintptr_t)info->si_addr == clobbered); + + handle_exception(&mc->__ss); + // handle_exception changed register state for continuation +} + + +__asm__( +"\n .text" +"\n .syntax unified" +"\n .code 16" +"\n .align 5" +"\n .globl _callit" +"\n .thumb_func" +"\n _callit:" +// save sp and return address to variables +"\n movw r12, :lower16:(_sp-1f-4)" +"\n movt r12, :upper16:(_sp-1f-4)" +"\n 1: add r12, pc" +"\n str sp, [r12]" +"\n movw r12, :lower16:(_pc-1f-4)" +"\n movt r12, :upper16:(_pc-1f-4)" +"\n 1: add r12, pc" +"\n str lr, [r12]" +// save other non-volatile registers to variables +"\n movw r12, :lower16:(_r4-1f-4)" +"\n movt r12, :upper16:(_r4-1f-4)" +"\n 1: add r12, pc" +"\n str r4, [r12]" +"\n movw r12, :lower16:(_r5-1f-4)" +"\n movt r12, :upper16:(_r5-1f-4)" +"\n 1: add r12, pc" +"\n str r5, [r12]" +"\n movw r12, :lower16:(_r6-1f-4)" +"\n movt r12, :upper16:(_r6-1f-4)" +"\n 1: add r12, pc" +"\n str r6, [r12]" +"\n movw r12, :lower16:(_r7-1f-4)" +"\n movt r12, :upper16:(_r7-1f-4)" +"\n 1: add r12, pc" +"\n str r7, [r12]" +"\n movw r12, :lower16:(_r8-1f-4)" +"\n movt r12, :upper16:(_r8-1f-4)" +"\n 1: add r12, pc" +"\n str r8, [r12]" +"\n movw r12, :lower16:(_r10-1f-4)" +"\n movt r12, :upper16:(_r10-1f-4)" +"\n 1: add r12, pc" +"\n str r10, [r12]" +"\n movw r12, :lower16:(_r11-1f-4)" +"\n movt r12, :upper16:(_r11-1f-4)" +"\n 1: add r12, pc" +"\n str r11, [r12]" +"\n bx r2" + ); + +__asm__( +"\n .text" +"\n .syntax unified" +"\n .code 16" +"\n .align 5" +"\n .globl _callit_stret" +"\n .thumb_func" +"\n _callit_stret:" +// save sp and return address to variables +"\n movw r12, :lower16:(_sp-1f-4)" +"\n movt r12, :upper16:(_sp-1f-4)" +"\n 1: add r12, pc" +"\n str sp, [r12]" +"\n movw r12, :lower16:(_pc-1f-4)" +"\n movt r12, :upper16:(_pc-1f-4)" +"\n 1: add r12, pc" +"\n str lr, [r12]" +// save other non-volatile registers to variables +"\n movw r12, :lower16:(_r4-1f-4)" +"\n movt r12, :upper16:(_r4-1f-4)" +"\n 1: add r12, pc" +"\n str r4, [r12]" +"\n movw r12, :lower16:(_r5-1f-4)" +"\n movt r12, :upper16:(_r5-1f-4)" +"\n 1: add r12, pc" +"\n str r5, [r12]" +"\n movw r12, :lower16:(_r6-1f-4)" +"\n movt r12, :upper16:(_r6-1f-4)" +"\n 1: add r12, pc" +"\n str r6, [r12]" +"\n movw r12, :lower16:(_r7-1f-4)" +"\n movt r12, :upper16:(_r7-1f-4)" +"\n 1: add r12, pc" +"\n str r7, [r12]" +"\n movw r12, :lower16:(_r8-1f-4)" +"\n movt r12, :upper16:(_r8-1f-4)" +"\n 1: add r12, pc" +"\n str r8, [r12]" +"\n movw r12, :lower16:(_r10-1f-4)" +"\n movt r12, :upper16:(_r10-1f-4)" +"\n 1: add r12, pc" +"\n str r10, [r12]" +"\n movw r12, :lower16:(_r11-1f-4)" +"\n movt r12, :upper16:(_r11-1f-4)" +"\n 1: add r12, pc" +"\n str r11, [r12]" +"\n bx r3" + ); + + +// arm #else #error unknown architecture @@ -1304,68 +1459,157 @@ void sigtrap(int sig, siginfo_t *info, void *cc) #endif +#if __arm__ +uintptr_t fnaddr(void *fn) { return (uintptr_t)fn & ~(uintptr_t)1; } +#else +uintptr_t fnaddr(void *fn) { return (uintptr_t)fn; } +#endif + insn_t set(uintptr_t dst, insn_t newvalue) { uintptr_t start = dst & ~(PAGE_MAX_SIZE-1); - mprotect((void*)start, PAGE_MAX_SIZE, PROT_READ|PROT_WRITE); + int err = mprotect((void*)start, PAGE_MAX_SIZE, PROT_READ|PROT_WRITE); + if (err) fail("mprotect(%p, RW-) failed (%d)", start, errno); insn_t oldvalue = *(insn_t *)dst; *(insn_t *)dst = newvalue; - mprotect((void*)start, PAGE_MAX_SIZE, PROT_READ|PROT_EXEC); + err = mprotect((void*)start, PAGE_MAX_SIZE, PROT_READ|PROT_EXEC); + if (err) fail("mprotect(%p, R-X) failed (%d)", start, errno); return oldvalue; } -insn_t clobber(void *fn, uintptr_t offset) +clobbered_insn_t clobber(void *fn, uintptr_t offset) { - clobbered = (uintptr_t)fn + offset; - return set((uintptr_t)fn + offset, BREAK_INSN); + clobbered = fnaddr(fn) + offset; + insn_t oldInsn = set(fnaddr(fn) + offset, BREAK_INSN); +#if __arm__ + // Need to clobber 32-bit Thumb instructions with another 32-bit instruction + // to preserve the behavior of IT... blocks. + clobbered_insn_t result = {oldInsn, 0, false}; + if (((oldInsn & 0xf000) == 0xf000) || + ((oldInsn & 0xf800) == 0xe800)) + { + testprintf("clobbering thumb-32 at offset %zu\n", offset); + // Old insn was 32-bit. Clobber all of it. + // First unclobber. + set(fnaddr(fn) + offset, oldInsn); + // f7f0 a0f0 is a "permanently undefined" Thumb-2 instruction. + // Clobber the first half last so `clobbered` gets the right value. + result.second = set(fnaddr(fn) + offset + 2, 0xa0f0); + result.first = set(fnaddr(fn) + offset, 0xf7f0); + result.thirty_two = true; + } + return result; +#else + return oldInsn; +#endif } -void unclobber(void *fn, uintptr_t offset, insn_t oldvalue) +void unclobber(void *fn, uintptr_t offset, clobbered_insn_t oldvalue) { - set((uintptr_t)fn + offset, oldvalue); +#if __arm__ + if (oldvalue.thirty_two) { + set(fnaddr(fn) + offset + 2, oldvalue.second); + } + set(fnaddr(fn) + offset, oldvalue.first); +#else + set(fnaddr(fn) + offset, oldvalue); +#endif } -uintptr_t *getOffsets(void *symbol, const char *symname, uintptr_t *outBase) +// terminator for the list of instruction offsets +#define END_OFFSETS ~0UL + +// Disassemble instructions symbol.. offsets) *p-- = END_OFFSETS; +#endif + + return p; +} + + +uintptr_t *getOffsets(const char *symname, uintptr_t *outBase) +{ + // Find the start of our function. + uintptr_t symbol = (uintptr_t)dlsym(RTLD_NEXT, symname); + if (!symbol) return nil; +#if __has_feature(ptrauth_calls) + symbol = (uintptr_t) + ptrauth_strip((void*)symbol, ptrauth_key_function_pointer); +#endif + + if (outBase) *outBase = symbol; + + // Find the end of our function by finding the start + // of the next symbol after our target symbol. + + const int insnIncrement = #if __arm64__ - // Also add breakpoints in _objc_msgSend_uncached_impcache + 4; +#elif __arm__ + 2; // in case of thumb or thumb-2 +#elif __i386__ || __x86_64__ + 1; +#else +#error unknown architecture +#endif + + uintptr_t symbolEnd; + Dl_info dli; + int ok; + for (symbolEnd = symbol + insnIncrement; + ((ok = dladdr((void*)symbolEnd, &dli))) && dli.dli_saddr == (void*)symbol; + symbolEnd += insnIncrement) + ; + + testprintf("found %s at %p..<%p %d %p %s\n", + symname, (void*)symbol, (void*)symbolEnd, ok, dli.dli_saddr, dli.dli_sname); + + // Record the offset to each non-NOP instruction. + uintptr_t *result = (uintptr_t *)malloc(1000 * sizeof(uintptr_t)); + uintptr_t *end = result + 1000; + uintptr_t *p = result; + + p = disassemble(symbol, symbolEnd, p, end); + + // Also record the offsets in _objc_msgSend_uncached when present // (which is the slow path and has a frame to unwind) - if (0 != strcmp(symname, "_objc_msgSend_uncached_impcache")) { - uintptr_t base2; - uintptr_t *more_offsets = getOffsets(symbol, "_objc_msgSend_uncached_impcache", &base2); - uintptr_t *q = more_offsets; - // Skip prologue because it's imprecisely modeled in compact unwind - testassert(*q != ~0UL); - q++; - testassert(*q != ~0UL); - q++; - while (*q != ~0UL) *p++ = *q++ + base2 - base; - // Skip return because it's imprecisely modeled in compact unwind - p--; - free(more_offsets); - } + if (!strstr(symname, "_uncached")) { + const char *uncached_symname = strstr(symname, "stret") + ? "_objc_msgSend_stret_uncached" : "_objc_msgSend_uncached"; + uintptr_t uncached_symbol; + uintptr_t *uncached_offsets = + getOffsets(uncached_symname, &uncached_symbol); + if (uncached_offsets) { + uintptr_t *q = uncached_offsets; + // Skip prologue and epilogue of objc_msgSend_uncached + // because it's imprecisely modeled in compact unwind + int prologueInstructions, epilogueInstructions; +#if __arm64e__ + prologueInstructions = 3; + epilogueInstructions = 2; +#elif __arm64__ || __x86_64__ || __i386__ || __arm__ + prologueInstructions = 2; + epilogueInstructions = 1; +#else +#error unknown architecture #endif + // skip past prologue + for (int i = 0; i < prologueInstructions; i++) { + testassert(*q != END_OFFSETS); + q++; + } + + // copy instructions + while (*q != END_OFFSETS) *p++ = *q++ + uncached_symbol - symbol; + // rewind past epilogue + for (int i = 0; i < epilogueInstructions; i++) { + testassert(p > result); + p--; + } + + free(uncached_offsets); + } + } + + // Terminate the list of offsets and return. testassert(p > result); testassert(p < end); - *p = ~0UL; -#if __x86_64__ - // hack: skip last instruction because libunwind blows up if it's - // one byte long and followed by the next function with no NOPs first - p[-1] = ~0UL; -#endif - if (outBase) *outBase = base; + *p = END_OFFSETS; + return result; } + void CALLIT(void *o, void *sel_arg, SEL s, void *f, bool stret) __attribute__((noinline)); void CALLIT(void *o, void *sel_arg, SEL s, void *f, bool stret) { @@ -1440,7 +1757,7 @@ struct stret test_dw_forward_stret(void) // sel_arg = arg to pass in sel register (may be message_ref) // uncaughtAllowed is the number of acceptable unreachable instructions // (for example, the ones that handle the corrupt-cache-error case) -void test_dw(const char *name, id sub, id tagged, bool stret, +void test_dw(const char *name, id sub, id tagged, id exttagged, bool stret, int uncaughtAllowed) { @@ -1485,11 +1802,14 @@ void test_dw(const char *name, id sub, id tagged, bool stret, IMP imp = stret ? (IMP)test_dw_forward_stret : (IMP)test_dw_forward; Class cls = object_getClass(sub); Class tagcls = object_getClass(tagged); + Class exttagcls = object_getClass(exttagged); class_replaceMethod(cls, sel, imp, ""); class_replaceMethod(tagcls, sel, imp, ""); + class_replaceMethod(exttagcls, sel, imp, ""); for (size_t i = 0; i < sizeof(lotsOfSels)/sizeof(lotsOfSels[0]); i++) { class_replaceMethod(cls, lotsOfSels[i], imp, ""); class_replaceMethod(tagcls, lotsOfSels[i], imp, ""); + class_replaceMethod(exttagcls, lotsOfSels[i], imp, ""); } } @@ -1510,26 +1830,36 @@ void test_dw(const char *name, id sub, id tagged, bool stret, } void *fn = dlsym(RTLD_DEFAULT, name); +#if __has_feature(ptrauth_calls) + fn = ptrauth_strip(fn, ptrauth_key_function_pointer); +#endif testassert(fn); // argument substitutions - void *sub_arg = (void*)objc_unretainedPointer(sub); - void *tagged_arg = (void*)objc_unretainedPointer(tagged); + void *sub_arg = (__bridge void*)sub; + void *tagged_arg = (__bridge void*)tagged; + void *exttagged_arg = (__bridge void*)exttagged; void *sel_arg = (void*)sel; struct objc_super sup_st = { sub, object_getClass(sub) }; struct objc_super tagged_sup_st = { tagged, object_getClass(tagged) }; + struct objc_super exttagged_sup_st = { exttagged, object_getClass(exttagged) }; struct { void *imp; SEL sel; } message_ref = { fn, sel }; Class cache_cls = object_getClass(sub); + Class tagged_cache_cls = object_getClass(tagged); + Class exttagged_cache_cls = object_getClass(exttagged); if (strstr(name, "Super")) { // super version - replace receiver with objc_super // clear caches of superclass cache_cls = class_getSuperclass(cache_cls); + tagged_cache_cls = class_getSuperclass(tagged_cache_cls); + exttagged_cache_cls = class_getSuperclass(exttagged_cache_cls); sub_arg = &sup_st; tagged_arg = &tagged_sup_st; + exttagged_arg = &exttagged_sup_st; } if (strstr(name, "_fixup")) { @@ -1538,18 +1868,19 @@ void test_dw(const char *name, id sub, id tagged, bool stret, } - uintptr_t *insnOffsets = getOffsets(fn, name, nil); + uintptr_t *insnOffsets = getOffsets(name, nil); + testassert(insnOffsets); uintptr_t offset; int uncaughtCount = 0; for (int oo = 0; insnOffsets[oo] != ~0UL; oo++) { offset = insnOffsets[oo]; testprintf("OFFSET %lu\n", offset); - insn_t saved_insn = clobber(fn, offset); + clobbered_insn_t saved_insn = clobber(fn, offset); caught = false; // nil - if ((void*)objc_unretainedPointer(sub) == sub_arg) { + if ((__bridge void*)sub == sub_arg) { SELF = nil; testprintf(" nil\n"); CALLIT(nil, sel_arg, sel, fn, stret); @@ -1573,21 +1904,27 @@ void test_dw(const char *name, id sub, id tagged, bool stret, // uncached,tagged SELF = tagged; testprintf(" uncached,tagged\n"); - _objc_flush_caches(cache_cls); + _objc_flush_caches(tagged_cache_cls); CALLIT(tagged_arg, sel_arg, sel, fn, stret); - _objc_flush_caches(cache_cls); + _objc_flush_caches(tagged_cache_cls); CALLIT(tagged_arg, sel_arg, sel, fn, stret); + _objc_flush_caches(exttagged_cache_cls); + CALLIT(exttagged_arg, sel_arg, sel, fn, stret); + _objc_flush_caches(exttagged_cache_cls); + CALLIT(exttagged_arg, sel_arg, sel, fn, stret); // cached,tagged SELF = tagged; testprintf(" cached,tagged\n"); CALLIT(tagged_arg, sel_arg, sel, fn, stret); CALLIT(tagged_arg, sel_arg, sel, fn, stret); + CALLIT(exttagged_arg, sel_arg, sel, fn, stret); + CALLIT(exttagged_arg, sel_arg, sel, fn, stret); // multiple SEL alignments, collisions, wraps SELF = sub; for (int a = 0; a < ALIGNCOUNT; a++) { - testprintf(" cached, SEL alignment %d\n", a); + testprintf(" cached and uncached, SEL alignment %d\n", a); // Count both up and down to be independent of // implementation's cache scan direction @@ -1667,6 +2004,7 @@ void test_basic(id receiver) // message cached noarg (as above) // fixme verify that uncached lookup didn't happen the 2nd time? SELF = receiver; + _objc_flush_caches(object_getClass(receiver)); for (int i = 0; i < 5; i++) { testprintf("idret\n"); state = 0; @@ -1705,7 +2043,6 @@ void test_basic(id receiver) testassert(state == 106); testassert(vector_equal(vecval, VEC_RESULT)); -#if __OBJC2__ // explicitly call noarg messenger, even if compiler doesn't emit it state = 0; testprintf("idret noarg\n"); @@ -1727,7 +2064,7 @@ void test_basic(id receiver) testassert(state == 113); testassert(stret_equal(stretval, STRET_RESULT)); */ -# if !__i386__ +#if !__i386__ testprintf("fpret noarg\n"); fpval = 0; fpval = ((typeof(fpmsg0))objc_msgSend_noarg)(receiver, @selector(fpret_noarg)); @@ -1739,14 +2076,13 @@ void test_basic(id receiver) vecval = ((typeof(vecmsg0))objc_msgSend_noarg)(receiver, @selector(vecret_noarg)); testassert(state == 116); testassert(vector_equal(vecval, VEC_RESULT)); -# endif -# if !__i386__ && !__x86_64__ +#endif +#if !__i386__ && !__x86_64__ testprintf("lfpret noarg\n"); lfpval = 0; lfpval = ((typeof(lfpmsg0))objc_msgSend_noarg)(receiver, @selector(lfpret_noarg)); testassert(state == 115); testassert(lfpval == LFP_RESULT); -# endif #endif } @@ -1756,8 +2092,6 @@ void test_basic(id receiver) int main() { PUSH_POOL { - int i; - id idval; long long llval; struct stret stretval; @@ -1769,10 +2103,6 @@ int main() struct stret *stretptr; #endif - uint64_t startTime; - uint64_t totalTime; - uint64_t targetTime; - Method idmethod; Method llmethod; Method stretmethod; @@ -1800,6 +2130,7 @@ int main() [Sub class]; #if OBJC_HAVE_TAGGED_POINTERS [TaggedSub class]; + [ExtTaggedSub class]; #endif ID_RESULT = [Super new]; @@ -1807,7 +2138,8 @@ int main() Sub *sub = [Sub new]; Super *sup = [Super new]; #if OBJC_HAVE_TAGGED_POINTERS - TaggedSub *tagged = objc_unretainedObject(_objc_makeTaggedPointer(OBJC_TAG_7, 999)); + TaggedSub *tagged = (__bridge id)_objc_makeTaggedPointer(OBJC_TAG_1, 999); + ExtTaggedSub *exttagged = (__bridge id)_objc_makeTaggedPointer(OBJC_TAG_First52BitPayload, 999); #endif // Basic cached and uncached dispatch. @@ -1817,6 +2149,8 @@ int main() #if OBJC_HAVE_TAGGED_POINTERS testprintf("basic tagged\n"); test_basic(tagged); + testprintf("basic ext tagged\n"); + test_basic(exttagged); #endif idmethod = class_getInstanceMethod([Super class], @selector(idret::::::::::::::::::::::::::::::::::::)); @@ -1839,113 +2173,6 @@ int main() lfpfn = (long double (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, 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; vecfn = (vector_ulong2 (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, 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 - // fill cache first - testprintf("time checks\n"); - - SELF = sub; - [sub voidret_nop]; - [sub voidret_nop2]; - [sub llret_nop]; - [sub stret_nop]; - [sub fpret_nop]; - [sub lfpret_nop]; - [sub vecret_nop]; - [sub voidret_nop]; - [sub voidret_nop2]; - [sub llret_nop]; - [sub stret_nop]; - [sub fpret_nop]; - [sub lfpret_nop]; - [sub vecret_nop]; - [sub voidret_nop]; - [sub voidret_nop2]; - [sub llret_nop]; - [sub stret_nop]; - [sub fpret_nop]; - [sub lfpret_nop]; - [sub vecret_nop]; - - // 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. - - // Use voidret because id return is too slow for perf test with ARC. - - // Pick smallest of voidret_nop and voidret_nop2 time - // in the hopes that one of them didn't collide in the method cache. - -#define COUNT 1000000 - - startTime = mach_absolute_time(); - ALIGN_(); - for (i = 0; i < COUNT; i++) { - [sub voidret_nop]; - } - totalTime = mach_absolute_time() - startTime; - testprintf("time: voidret %llu\n", totalTime); - targetTime = totalTime; - - startTime = mach_absolute_time(); - ALIGN_(); - for (i = 0; i < COUNT; i++) { - [sub voidret_nop2]; - } - totalTime = mach_absolute_time() - startTime; - testprintf("time: voidret2 %llu\n", totalTime); - if (totalTime < targetTime) targetTime = totalTime; - - startTime = mach_absolute_time(); - ALIGN_(); - for (i = 0; i < COUNT; i++) { - [sub llret_nop]; - } - totalTime = mach_absolute_time() - startTime; - timecheck("llret ", totalTime, targetTime * 0.7, targetTime * 2.0); - - startTime = mach_absolute_time(); - ALIGN_(); - for (i = 0; i < COUNT; i++) { - [sub stret_nop]; - } - totalTime = mach_absolute_time() - startTime; - timecheck("stret ", totalTime, targetTime * 0.7, targetTime * 5.0); - - startTime = mach_absolute_time(); - ALIGN_(); - for (i = 0; i < COUNT; i++) { - [sub fpret_nop]; - } - totalTime = mach_absolute_time() - startTime; - timecheck("fpret ", totalTime, targetTime * 0.7, targetTime * 4.0); - - startTime = mach_absolute_time(); - ALIGN_(); - for (i = 0; i < COUNT; i++) { - [sub lfpret_nop]; - } - totalTime = mach_absolute_time() - startTime; - timecheck("lfpret", totalTime, targetTime * 0.7, targetTime * 4.0); - - startTime = mach_absolute_time(); - ALIGN_(); - for (i = 0; i < COUNT; i++) { - [sub vecret_nop]; - } - totalTime = mach_absolute_time() - startTime; - timecheck("vecret", totalTime, targetTime * 0.7, targetTime * 4.0); - -#if __arm64__ - // Removing this testwarn(), or changing voidret_nop to nop;ret, - // changes the voidret_nop and stret_nop times above by a factor of 2. - testwarn("rdar://13896922 nop;ret is faster than ret?"); -#endif - -#undef COUNT - // method_invoke // method_invoke long long // method_invoke_stret stret @@ -2011,11 +2238,7 @@ int main() stretval = zero; stretval = [(id)NIL_RECEIVER stret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8: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); -#if __clang__ testassert(0 == memcmp(&stretval, &zero, sizeof(stretval))); -#else - // no stret result guarantee -#endif #if __x86_64__ // check stret return register @@ -2025,7 +2248,7 @@ int main() (&stretval, nil, @selector(stret_nop)); testassert(stretptr == &stretval); testassert(state == 0); - // no stret result guarantee for hand-written calls, even with clang + // no stret result guarantee for hand-written calls #endif #if __i386__ @@ -2093,7 +2316,6 @@ int main() TEST_NIL_STRUCT(d,9); -#if __OBJC2__ // message to nil noarg // explicitly call noarg messenger, even if compiler doesn't emit it state = 0; @@ -2110,7 +2332,7 @@ int main() // no stret_noarg messenger -# if !__i386__ +#if !__i386__ state = 0; fpval = FP_RESULT; fpval = ((typeof(fpmsg0))objc_msgSend_noarg)(nil, @selector(fpret_noarg)); @@ -2122,18 +2344,16 @@ int main() vecval = ((typeof(vecmsg0))objc_msgSend_noarg)(nil, @selector(vecret_noarg)); testassert(state == 0); testassert(vector_all(vecval == 0)); -# endif -# if !__i386__ && !__x86_64__ +#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 -#if __OBJC2__ // rdar://8271364 objc_msgSendSuper2 must not change objc_super testprintf("super struct\n"); struct objc_super sup_st = { @@ -2158,9 +2378,8 @@ int main() testassert(stret_equal(stretval, STRET_RESULT)); testassert(sup_st.receiver == sub); testassert(sup_st.super_class == object_getClass(sub)); -#endif -#if __OBJC2__ && !__arm64__ +#if !__arm64__ // Debug messengers. testprintf("debug messengers\n"); @@ -2222,57 +2441,233 @@ int main() // fixme fp2ret #endif -// debug messengers + // debug messengers #endif + // objc_msgLookup + +#if 1 + // fixme objc_msgLookup test hack stopped working after a compiler update + +#elif __has_feature(objc_arc) + // ARC interferes with objc_msgLookup test hacks + +#elif __i386__ && TARGET_OS_SIMULATOR + testwarn("fixme msgLookup hack doesn't work"); + +#else + // fixme hack: call the looked-up method +# if __arm64__ +# define CALL_LOOKUP(ret) \ + asm volatile ("blr x17 \n mov %x0, x0" : "=r" (ret)) +# define CALL_LOOKUP_STRET(ret) \ + asm volatile ("mov x8, %x1 \n blr x17 \n" : "=m" (ret) : "r" (&ret)) + +# elif __arm__ +# define CALL_LOOKUP(ret) \ + asm volatile ("blx r12 \n mov %0, r0" : "=r" (ret)) +# define CALL_LOOKUP_STRET(ret) \ + asm volatile ("mov r0, %1 \n blx r12 \n" : "=m" (ret) : "r" (&ret)) + +# elif __x86_64__ +# define CALL_LOOKUP(ret) \ + asm volatile ("call *%%r11 \n mov %%rax, %0" : "=r" (ret)) +# define CALL_LOOKUP_STRET(ret) \ + asm volatile ("mov %1, %%rdi \n call *%%r11 \n" : "=m" (ret) : "r" (&ret)) + +# elif __i386__ +# define CALL_LOOKUP(ret) \ + asm volatile ("call *%%eax \n mov %%eax, %0" : "=r" (ret)) +# define CALL_LOOKUP_STRET(ret) \ + asm volatile ("add $4, %%esp \n mov %1, (%%esp) \n call *%%eax \n sub $4, %%esp \n" : "=m" (ret) : "d" (&ret)) + +# else +# error unknown architecture +# endif + + // msgLookup uncached + // msgLookup uncached super + // msgLookup uncached stret + // msgLookup uncached super stret + // msgLookup uncached fpret + // msgLookup uncached fpret long double + // msgLookup cached + // msgLookup cached stret + // msgLookup cached super + // msgLookup cached super stret + // msgLookup cached fpret + // msgLookup cached fpret long double + // fixme verify that uncached lookup didn't happen the 2nd time? + SELF = sub; + _objc_flush_caches(object_getClass(sub)); + for (int i = 0; i < 5; i++) { + testprintf("objc_msgLookup\n"); + state = 0; + idmsg = (typeof(idmsg))objc_msgLookup; + idval = nil; + (*idmsg)(sub, @selector(idret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 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); + CALL_LOOKUP(idval); + testassert(state == 101); + testassert(idval == ID_RESULT); + + testprintf("objc_msgLookup_stret\n"); + state = 0; + stretmsg = (typeof(stretmsg))objc_msgLookup_stret; + stretval = zero; + (*stretmsg)(sub, @selector(stret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 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); + CALL_LOOKUP_STRET(stretval); + testassert(state == 103); + testassert(stret_equal(stretval, STRET_RESULT)); + + testprintf("objc_msgLookupSuper2\n"); + state = 100; + sup_st.receiver = sub; + sup_st.super_class = object_getClass(sub); + idmsgsuper = (typeof(idmsgsuper))objc_msgLookupSuper2; + idval = nil; + idval = (*idmsgsuper)(&sup_st, @selector(idret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 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); + CALL_LOOKUP(idval); + testassert(state == 1); + testassert(idval == ID_RESULT); + + testprintf("objc_msgLookupSuper2_stret\n"); + state = 100; + sup_st.receiver = sub; + sup_st.super_class = object_getClass(sub); + stretmsgsuper = (typeof(stretmsgsuper))objc_msgLookupSuper2_stret; + stretval = zero; + (*stretmsgsuper)(&sup_st, @selector(stret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 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); + CALL_LOOKUP_STRET(stretval); + testassert(state == 3); + testassert(stret_equal(stretval, STRET_RESULT)); + +#if __i386__ + // fixme fpret, can't test FP stack properly +#endif +#if __x86_64__ + // fixme fpret, can't test FP stack properly + // fixme fp2ret, can't test FP stack properly +#endif + + } + + // msgLookup to nil + // msgLookup to nil stret + // fixme msgLookup to nil long long + // fixme msgLookup to nil fpret + // fixme msgLookup to nil fp2ret + + testprintf("objc_msgLookup to nil\n"); + state = 0; + idmsg = (typeof(idmsg))objc_msgLookup; + idval = nil; + (*idmsg)(NIL_RECEIVER, @selector(idret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 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); + CALL_LOOKUP(idval); + testassert(state == 0); + testassert(idval == nil); + + testprintf("objc_msgLookup_stret to nil\n"); + state = 0; + stretmsg = (typeof(stretmsg))objc_msgLookup_stret; + stretval = zero; + (*stretmsg)(NIL_RECEIVER, @selector(stret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 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); + CALL_LOOKUP_STRET(stretval); + testassert(state == 0); + // no stret result guarantee + +#if __i386__ + // fixme fpret, can't test FP stack properly +#endif +#if __x86_64__ + // fixme fpret, can't test FP stack properly + // fixme fp2ret, can't test FP stack properly +#endif + + // objc_msgLookup +#endif + + + #if !TEST_DWARF - testwarn("no unwind tables in this configuration"); + testwarn("no unwind tables in this configuration " NO_DWARF_REASON); #else // DWARF unwind tables testprintf("unwind tables\n"); - // install exception handler - struct sigaction act; - act.sa_sigaction = sigtrap; - act.sa_mask = 0; - act.sa_flags = SA_SIGINFO; - sigaction(SIGTRAP, &act, NULL); + // Clear simulator-related environment variables. + // Disassembly will run llvm-objdump which is not a simulator executable. + unsetenv("DYLD_ROOT_PATH"); + unsetenv("DYLD_FALLBACK_LIBRARY_PATH"); + unsetenv("DYLD_FALLBACK_FRAMEWORK_PATH"); + + // Check mprotect() of objc_msgSend. + // It doesn't work when running on a device with no libobjc root. + // In that case we skip this part of the test without failing. + // fixme make this work + // fixme now it doesn't work even with a libobjc root in place? + int err1 = mprotect((void *)((uintptr_t)&objc_msgSend & ~(PAGE_MAX_SIZE-1)), + PAGE_MAX_SIZE, PROT_READ | PROT_WRITE); + int errno1 = errno; + int err2 = mprotect((void *)((uintptr_t)&objc_msgSend & ~(PAGE_MAX_SIZE-1)), + PAGE_MAX_SIZE, PROT_READ | PROT_EXEC); + int errno2 = errno; + if (err1 || err2) { + testwarn("can't mprotect() objc_msgSend (%d, %d). " + "Skipping unwind table test.", + err1, errno1, err2, errno2); + } + else { + // install exception handler + struct sigaction act; + act.sa_sigaction = break_handler; + act.sa_mask = 0; + act.sa_flags = SA_SIGINFO; + sigaction(BREAK_SIGNAL, &act, nil); +#if defined(BREAK_SIGNAL2) + sigaction(BREAK_SIGNAL2, &act, nil); +#endif - SubDW *dw = [[SubDW alloc] init]; + SubDW *dw = [[SubDW alloc] init]; - objc_setForwardHandler((void*)test_dw_forward, (void*)test_dw_forward_stret); + objc_setForwardHandler((void*)test_dw_forward, (void*)test_dw_forward_stret); # if __x86_64__ - test_dw("objc_msgSend", dw, tagged, false, 0); - test_dw("objc_msgSend_stret", dw, tagged, true, 0); - test_dw("objc_msgSend_fpret", dw, tagged, false, 0); - test_dw("objc_msgSend_fp2ret", dw, tagged, false, 0); - test_dw("objc_msgSendSuper", dw, tagged, false, 0); - test_dw("objc_msgSendSuper2", dw, tagged, false, 0); - test_dw("objc_msgSendSuper_stret", dw, tagged, true, 0); - test_dw("objc_msgSendSuper2_stret", dw, tagged, true, 0); + test_dw("objc_msgSend", dw, tagged, exttagged, false, 0); + test_dw("objc_msgSend_stret", dw, tagged, exttagged, true, 0); + test_dw("objc_msgSend_fpret", dw, tagged, exttagged, false, 0); + test_dw("objc_msgSend_fp2ret", dw, tagged, exttagged, false, 0); + test_dw("objc_msgSendSuper", dw, tagged, exttagged, false, 0); + test_dw("objc_msgSendSuper2", dw, tagged, exttagged, false, 0); + test_dw("objc_msgSendSuper_stret", dw, tagged, exttagged, true, 0); + test_dw("objc_msgSendSuper2_stret", dw, tagged, exttagged, true, 0); # elif __i386__ - test_dw("objc_msgSend", dw, dw, false, 0); - test_dw("objc_msgSend_stret", dw, dw, true, 0); - test_dw("objc_msgSend_fpret", dw, dw, false, 0); - test_dw("objc_msgSendSuper", dw, dw, false, 0); - test_dw("objc_msgSendSuper2", dw, dw, false, 0); - test_dw("objc_msgSendSuper_stret", dw, dw, true, 0); - test_dw("objc_msgSendSuper2_stret", dw, dw, true, 0); + test_dw("objc_msgSend", dw, dw, dw, false, 0); + test_dw("objc_msgSend_stret", dw, dw, dw, true, 0); + test_dw("objc_msgSend_fpret", dw, dw, dw, false, 0); + test_dw("objc_msgSendSuper", dw, dw, dw, false, 0); + test_dw("objc_msgSendSuper2", dw, dw, dw, false, 0); + test_dw("objc_msgSendSuper_stret", dw, dw, dw, true, 0); + test_dw("objc_msgSendSuper2_stret", dw, dw, dw, true, 0); # elif __arm64__ - test_dw("objc_msgSend", dw, tagged, false, 1); - test_dw("objc_msgSendSuper", dw, tagged, false, 1); - test_dw("objc_msgSendSuper2", dw, tagged, false, 1); + test_dw("objc_msgSend", dw, tagged, exttagged, false, 1); + test_dw("objc_msgSendSuper", dw, tagged, exttagged, false, 1); + test_dw("objc_msgSendSuper2", dw, tagged, exttagged, false, 1); +# elif __arm__ + test_dw("objc_msgSend", dw, dw, dw, false, 0); + test_dw("objc_msgSend_stret", dw, dw, dw, true, 0); + test_dw("objc_msgSendSuper", dw, dw, dw, false, 0); + test_dw("objc_msgSendSuper2", dw, dw, dw, false, 0); + test_dw("objc_msgSendSuper_stret", dw, dw, dw, true, 0); + test_dw("objc_msgSendSuper2_stret", dw, dw, dw, true, 0); # else # error unknown architecture # endif + } - // DWARF unwind tables + // end DWARF unwind test #endif } POP_POOL; succeed(__FILE__); } - -#endif diff --git a/test/nilAPIArgs.m b/test/nilAPIArgs.m index a133451a..29eee124 100644 --- a/test/nilAPIArgs.m +++ b/test/nilAPIArgs.m @@ -1,4 +1,9 @@ -// TEST_CONFIG +/* +TEST_BUILD_OUTPUT +.*nilAPIArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*nilAPIArgs.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +END +*/ #include "test.h" diff --git a/test/nonpointerisa.m b/test/nonpointerisa.m index 1fe2a705..673220d5 100644 --- a/test/nonpointerisa.m +++ b/test/nonpointerisa.m @@ -2,39 +2,31 @@ // TEST_CONFIG MEM=mrc #include "test.h" - -#if !__OBJC2__ - -int main() -{ - succeed(__FILE__); -} - -#else - #include #include #include #define ISA(x) (*((uintptr_t *)(x))) -#define INDEXED(x) (ISA(x) & 1) +#define NONPOINTER(x) (ISA(x) & 1) #if SUPPORT_NONPOINTER_ISA # if __x86_64__ # define RC_ONE (1ULL<<56) -# elif __arm64__ +# elif __arm64__ && __LP64__ # define RC_ONE (1ULL<<45) +# elif __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__) +# define RC_ONE (1ULL<<25) # else # error unknown architecture # endif #endif -void check_unindexed(id obj, Class cls) +void check_raw_pointer(id obj, Class cls) { testassert(object_getClass(obj) == cls); - testassert(!INDEXED(obj)); + testassert(!NONPOINTER(obj)); uintptr_t isa = ISA(obj); testassert((Class)isa == cls); @@ -60,11 +52,22 @@ void check_unindexed(id obj, Class cls) int main() { +#if OBJC_HAVE_NONPOINTER_ISA || OBJC_HAVE_PACKED_NONPOINTER_ISA || OBJC_HAVE_INDEXED_NONPOINTER_ISA +# error wrong +#endif + testprintf("Isa with index\n"); id index_o = [NSObject new]; - check_unindexed(index_o, [NSObject class]); + check_raw_pointer(index_o, [NSObject class]); + + // These variables DO NOT exist without non-pointer isa support. + testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_packed_isa_class_mask")); + testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_magic_mask")); + testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_magic_value")); + testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_index_mask")); + testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_index_shift")); - // These variables DO exist even without non-pointer isa support + // These variables DO exist even without non-pointer isa support. testassert(dlsym(RTLD_DEFAULT, "objc_debug_isa_class_mask")); testassert(dlsym(RTLD_DEFAULT, "objc_debug_isa_magic_mask")); testassert(dlsym(RTLD_DEFAULT, "objc_debug_isa_magic_value")); @@ -75,15 +78,26 @@ int main() #else // SUPPORT_NONPOINTER_ISA -void check_indexed(id obj, Class cls) +void check_nonpointer(id obj, Class cls) { testassert(object_getClass(obj) == cls); - testassert(INDEXED(obj)); + testassert(NONPOINTER(obj)); uintptr_t isa = ISA(obj); - testassert((Class)(isa & objc_debug_isa_class_mask) == cls); - testassert((Class)(isa & ~objc_debug_isa_class_mask) != 0); - testassert((isa & objc_debug_isa_magic_mask) == objc_debug_isa_magic_value); + + if (objc_debug_indexed_isa_magic_mask != 0) { + // Indexed isa. + testassert((isa & objc_debug_indexed_isa_magic_mask) == objc_debug_indexed_isa_magic_value); + testassert((isa & ~objc_debug_indexed_isa_index_mask) != 0); + uintptr_t index = (isa & objc_debug_indexed_isa_index_mask) >> objc_debug_indexed_isa_index_shift; + testassert(index < objc_indexed_classes_count); + testassert(objc_indexed_classes[index] == cls); + } else { + // Packed isa. + testassert((Class)(isa & objc_debug_isa_class_mask) == cls); + testassert((Class)(isa & ~objc_debug_isa_class_mask) != 0); + testassert((isa & objc_debug_isa_magic_mask) == objc_debug_isa_magic_value); + } CFRetain(obj); testassert(ISA(obj) == isa + RC_ONE); @@ -101,7 +115,7 @@ void check_indexed(id obj, Class cls) @interface OS_object -+(id)new; ++(id)alloc; @end @interface Fake_OS_object : NSObject { @@ -115,10 +129,10 @@ +(void)initialize { static bool initialized; if (!initialized) { initialized = true; - testprintf("Indexed during +initialize\n"); - testassert(INDEXED(self)); + testprintf("Nonpointer during +initialize\n"); + testassert(NONPOINTER(self)); id o = [Fake_OS_object new]; - check_indexed(o, self); + check_nonpointer(o, self); [o release]; } } @@ -135,9 +149,37 @@ int main() { uintptr_t isa; +#if SUPPORT_PACKED_ISA +# if !OBJC_HAVE_NONPOINTER_ISA || !OBJC_HAVE_PACKED_NONPOINTER_ISA || OBJC_HAVE_INDEXED_NONPOINTER_ISA +# error wrong +# endif + testassert(objc_debug_isa_class_mask == (uintptr_t)&objc_absolute_packed_isa_class_mask); + + // Indexed isa variables DO NOT exist on packed-isa platforms + testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_magic_mask")); + testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_magic_value")); + testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_index_mask")); + testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_index_shift")); + +#elif SUPPORT_INDEXED_ISA +# if !OBJC_HAVE_NONPOINTER_ISA || OBJC_HAVE_PACKED_NONPOINTER_ISA || !OBJC_HAVE_INDEXED_NONPOINTER_ISA +# error wrong +# endif + testassert(objc_debug_indexed_isa_magic_mask == (uintptr_t)&objc_absolute_indexed_isa_magic_mask); + testassert(objc_debug_indexed_isa_magic_value == (uintptr_t)&objc_absolute_indexed_isa_magic_value); + testassert(objc_debug_indexed_isa_index_mask == (uintptr_t)&objc_absolute_indexed_isa_index_mask); + testassert(objc_debug_indexed_isa_index_shift == (uintptr_t)&objc_absolute_indexed_isa_index_shift); + + // Packed isa variable DOES NOT exist on indexed-isa platforms. + testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_packed_isa_class_mask")); + +#else +# error unknown nonpointer isa format +#endif + testprintf("Isa with index\n"); id index_o = [Fake_OS_object new]; - check_indexed(index_o, [Fake_OS_object class]); + check_nonpointer(index_o, [Fake_OS_object class]); testprintf("Weakly referenced\n"); isa = ISA(index_o); @@ -153,64 +195,64 @@ int main() testprintf("Isa without index\n"); - id unindex_o = [OS_object new]; - check_unindexed(unindex_o, [OS_object class]); + id raw_o = [OS_object alloc]; + check_raw_pointer(raw_o, [OS_object class]); id buf[4]; id bufo = (id)buf; - testprintf("Change isa 0 -> unindexed\n"); + testprintf("Change isa 0 -> raw pointer\n"); bzero(buf, sizeof(buf)); object_setClass(bufo, [OS_object class]); - check_unindexed(bufo, [OS_object class]); + check_raw_pointer(bufo, [OS_object class]); - testprintf("Change isa 0 -> indexed\n"); + testprintf("Change isa 0 -> nonpointer\n"); bzero(buf, sizeof(buf)); object_setClass(bufo, [NSObject class]); - check_indexed(bufo, [NSObject class]); + check_nonpointer(bufo, [NSObject class]); - testprintf("Change isa indexed -> indexed\n"); - testassert(INDEXED(bufo)); + testprintf("Change isa nonpointer -> nonpointer\n"); + testassert(NONPOINTER(bufo)); _objc_rootRetain(bufo); testassert(_objc_rootRetainCount(bufo) == 2); object_setClass(bufo, [Fake_OS_object class]); testassert(_objc_rootRetainCount(bufo) == 2); _objc_rootRelease(bufo); testassert(_objc_rootRetainCount(bufo) == 1); - check_indexed(bufo, [Fake_OS_object class]); + check_nonpointer(bufo, [Fake_OS_object class]); - testprintf("Change isa indexed -> unindexed\n"); + testprintf("Change isa nonpointer -> raw pointer\n"); // Retain count must be preserved. // Use root* to avoid OS_object's overrides. - testassert(INDEXED(bufo)); + testassert(NONPOINTER(bufo)); _objc_rootRetain(bufo); testassert(_objc_rootRetainCount(bufo) == 2); object_setClass(bufo, [OS_object class]); testassert(_objc_rootRetainCount(bufo) == 2); _objc_rootRelease(bufo); testassert(_objc_rootRetainCount(bufo) == 1); - check_unindexed(bufo, [OS_object class]); + check_raw_pointer(bufo, [OS_object class]); - testprintf("Change isa unindexed -> indexed (doesn't happen)\n"); - testassert(!INDEXED(bufo)); + testprintf("Change isa raw pointer -> nonpointer (doesn't happen)\n"); + testassert(!NONPOINTER(bufo)); _objc_rootRetain(bufo); testassert(_objc_rootRetainCount(bufo) == 2); object_setClass(bufo, [Fake_OS_object class]); testassert(_objc_rootRetainCount(bufo) == 2); _objc_rootRelease(bufo); testassert(_objc_rootRetainCount(bufo) == 1); - check_unindexed(bufo, [Fake_OS_object class]); + check_raw_pointer(bufo, [Fake_OS_object class]); - testprintf("Change isa unindexed -> unindexed\n"); - testassert(!INDEXED(bufo)); + testprintf("Change isa raw pointer -> raw pointer\n"); + testassert(!NONPOINTER(bufo)); _objc_rootRetain(bufo); testassert(_objc_rootRetainCount(bufo) == 2); object_setClass(bufo, [Sub_OS_object class]); testassert(_objc_rootRetainCount(bufo) == 2); _objc_rootRelease(bufo); testassert(_objc_rootRetainCount(bufo) == 1); - check_unindexed(bufo, [Sub_OS_object class]); + check_raw_pointer(bufo, [Sub_OS_object class]); succeed(__FILE__); @@ -218,6 +260,3 @@ int main() // SUPPORT_NONPOINTER_ISA #endif - -// __OBJC2__ -#endif diff --git a/test/nopool.m b/test/nopool.m index 57e8fcba..f3493ded 100644 --- a/test/nopool.m +++ b/test/nopool.m @@ -20,19 +20,30 @@ int main() [[TestRoot new] autorelease]; testassert(TestRootAutorelease == 2); - objc_autoreleasePoolPop(objc_autoreleasePoolPush()); + objc_autoreleasePoolPop(objc_autoreleasePoolPush()); [[TestRoot new] autorelease]; testassert(TestRootAutorelease == 3); + testonthread(^{ [[TestRoot new] autorelease]; testassert(TestRootAutorelease == 4); testassert(TestRootDealloc == 1); }); - // thread's autoreleased object should have deallocated testassert(TestRootDealloc == 2); + + // Test no-pool autorelease after a pool was pushed and popped. + // The simplest POOL_SENTINEL check during pop gets this wrong. + testonthread(^{ + objc_autoreleasePoolPop(objc_autoreleasePoolPush()); + [[TestRoot new] autorelease]; + testassert(TestRootAutorelease == 5); + testassert(TestRootDealloc == 2); + }); + testassert(TestRootDealloc == 3 +); succeed(__FILE__); } diff --git a/test/nsexc.m b/test/nsexc.m index 4884345e..cf4ac439 100644 --- a/test/nsexc.m +++ b/test/nsexc.m @@ -1,20 +1,6 @@ /* need exception-safe ARC for exception deallocation tests TEST_CFLAGS -fobjc-arc-exceptions -framework Foundation - -llvm-gcc unavoidably warns about our deliberately out-of-order handlers - -TEST_BUILD_OUTPUT -In file included from .* -.*exc.m: In function .* -.*exc.m:\d+: warning: exception of type .* will be caught -.*exc.m:\d+: warning: by earlier handler for .* -.*exc.m:\d+: warning: exception of type .* will be caught -.*exc.m:\d+: warning: by earlier handler for .* -.*exc.m:\d+: warning: exception of type .* will be caught -.*exc.m:\d+: warning: by earlier handler for .* -OR -END */ #define USE_FOUNDATION 1 diff --git a/test/nsobject.m b/test/nsobject.m index 4a79f32c..dd64bd00 100644 --- a/test/nsobject.m +++ b/test/nsobject.m @@ -1,4 +1,4 @@ -// TEST_CONFIG MEM=mrc,gc +// TEST_CONFIG MEM=mrc #include "test.h" diff --git a/test/nsprotocol.m b/test/nsprotocol.m index 8305cc83..5315371c 100644 --- a/test/nsprotocol.m +++ b/test/nsprotocol.m @@ -1,9 +1,6 @@ // TEST_CONFIG #include "test.h" - -#if __OBJC2__ - #include int main() @@ -18,30 +15,3 @@ int main() succeed(__FILE__); } - -#else - -#include -#include - -int main() -{ - // Class Protocol is never a subclass of NSObject - // CoreFoundation adds NSObject methods to Protocol when it loads - - testassert(objc_getClass("NSObject")); - - Class cls = objc_getClass("Protocol"); - testassert(!class_getInstanceMethod(cls, sel_registerName("isProxy"))); - testassert(class_getSuperclass(cls) != objc_getClass("NSObject")); - - void *dl = dlopen("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", RTLD_LAZY); - testassert(dl); - - testassert(class_getInstanceMethod(cls, sel_registerName("isProxy"))); - testassert(class_getSuperclass(cls) != objc_getClass("NSObject")); - - succeed(__FILE__); -} - -#endif diff --git a/test/objectCopy.m b/test/objectCopy.m index 275466cd..973ac4d0 100644 --- a/test/objectCopy.m +++ b/test/objectCopy.m @@ -1,4 +1,4 @@ -// TEST_CONFIG MEM=mrc,gc +// TEST_CONFIG MEM=mrc #include "test.h" #include @@ -21,17 +21,18 @@ int main() id o2 = object_copy(o0, 0); id o3 = object_copy(o1, 0); id o4 = object_copy(o1, 32); + testassert(malloc_size(o0) == 32); testassert(malloc_size(o1) == 64); testassert(malloc_size(o2) == 32); testassert(malloc_size(o3) == 32); testassert(malloc_size(o4) == 64); - if (!objc_collecting_enabled()) { - testassert([o0 retainCount] == 2); - testassert([o1 retainCount] == 2); - testassert([o2 retainCount] == 1); - testassert([o3 retainCount] == 1); - testassert([o4 retainCount] == 1); - } + + testassert([o0 retainCount] == 2); + testassert([o1 retainCount] == 2); + testassert([o2 retainCount] == 1); + testassert([o3 retainCount] == 1); + testassert([o4 retainCount] == 1); + succeed(__FILE__); } diff --git a/test/property.m b/test/property.m index fbcabdd5..0c2de1e4 100644 --- a/test/property.m +++ b/test/property.m @@ -1,4 +1,13 @@ -// TEST_CONFIG +/* +TEST_BUILD_OUTPUT +.*property.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*property.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*property.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*property.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*property.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*property.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +END +*/ #include "test.h" #include "testroot.i" @@ -6,59 +15,142 @@ #include #include -@interface Super : TestRoot { + +@protocol SuperProto +@property(readonly) char superProtoProp; +@property(class,readonly) char superProtoProp; +@end + +@protocol SubProto +@property(readonly) uintptr_t subProtoProp; +@property(class,readonly) uintptr_t subProtoProp; +@property(readonly) uintptr_t subInstanceOnlyProtoProp; +@property(class,readonly) uintptr_t subClassOnlyProtoProp; +@end + +@interface Super : TestRoot { @public char superIvar; } @property(readonly) char superProp; +@property(class,readonly) char superProp; @end -@implementation Super +@implementation Super @synthesize superProp = superIvar; ++(char)superProp { return 'a'; } + +-(char)superProtoProp { return 'a'; } ++(char)superProtoProp { return 'a'; } @end -@interface Sub : Super { +@interface Sub : Super { @public uintptr_t subIvar; } @property(readonly) uintptr_t subProp; +@property(class,readonly) uintptr_t subProp; +@property(readonly) uintptr_t subInstanceOnlyProp; +@property(class,readonly) uintptr_t subClassOnlyProp; @end @implementation Sub @synthesize subProp = subIvar; ++(uintptr_t)subProp { return 'a'; } ++(uintptr_t)subClassOnlyProp { return 'a'; } +-(uintptr_t)subInstanceOnlyProp { return 'a'; } + +-(uintptr_t)subProtoProp { return 'a'; } ++(uintptr_t)subProtoProp { return 'a'; } ++(uintptr_t)subClassOnlyProtoProp { return 'a'; } +-(uintptr_t)subInstanceOnlyProtoProp { return 'a'; } @end - -int main() + +void test(Class subcls) { - /* - Runtime layout of Sub: - [0] isa - [1] superIvar - [2] subIvar - */ - objc_property_t prop; - prop = class_getProperty([Sub class], "subProp"); + Class supercls = class_getSuperclass(subcls); + + prop = class_getProperty(subcls, "subProp"); + testassert(prop); + + prop = class_getProperty(subcls, "subProtoProp"); testassert(prop); - prop = class_getProperty([Super class], "superProp"); + prop = class_getProperty(supercls, "superProp"); testassert(prop); - testassert(prop == class_getProperty([Sub class], "superProp")); + testassert(prop == class_getProperty(subcls, "superProp")); - prop = class_getProperty([Super class], "subProp"); + prop = class_getProperty(supercls, "superProtoProp"); + testassert(prop); + // These are distinct because Sub adopts SuperProto itself + // in addition to Super's adoption of SuperProto. + testassert(prop != class_getProperty(subcls, "superProtoProp")); + + prop = class_getProperty(supercls, "subProp"); testassert(!prop); - prop = class_getProperty(object_getClass([Sub class]), "subProp"); + prop = class_getProperty(supercls, "subProtoProp"); testassert(!prop); + testassert(nil == class_getProperty(nil, "foo")); + testassert(nil == class_getProperty(subcls, nil)); + testassert(nil == class_getProperty(nil, nil)); +} + + +int main() +{ + Class subcls = [Sub class]; + Class submeta = object_getClass(subcls); + objc_property_t prop; + + // instance properties + test(subcls); + + // class properties + test(submeta); + + // properties must not appear on the wrong side + testassert(nil == class_getProperty(subcls, "subClassOnlyProp")); + testassert(nil == class_getProperty(submeta, "subInstanceOnlyProp")); + testassert(nil == class_getProperty(subcls, "subClassOnlyProtoProp")); + testassert(nil == class_getProperty(submeta, "subInstanceOnlyProtoProp")); + + // properties with the same name on both sides are distinct + testassert(class_getProperty(subcls, "subProp") != class_getProperty(submeta, "subProp")); + testassert(class_getProperty(subcls, "superProp") != class_getProperty(submeta, "superProp")); + testassert(class_getProperty(subcls, "subProtoProp") != class_getProperty(submeta, "subProtoProp")); + testassert(class_getProperty(subcls, "superProtoProp") != class_getProperty(submeta, "superProtoProp")); + + // protocol properties + + prop = protocol_getProperty(@protocol(SubProto), "subProtoProp", YES, YES); + testassert(prop); + + prop = protocol_getProperty(@protocol(SuperProto), "superProtoProp", YES, YES); + testassert(prop == protocol_getProperty(@protocol(SubProto), "superProtoProp", YES, YES)); + + prop = protocol_getProperty(@protocol(SuperProto), "subProtoProp", YES, YES); + testassert(!prop); + + // protocol properties must not appear on the wrong side + testassert(nil == protocol_getProperty(@protocol(SubProto), "subClassOnlyProtoProp", YES, YES)); + testassert(nil == protocol_getProperty(@protocol(SubProto), "subInstanceOnlyProtoProp", YES, NO)); + + // protocol properties with the same name on both sides are distinct + testassert(protocol_getProperty(@protocol(SubProto), "subProtoProp", YES, YES) != protocol_getProperty(@protocol(SubProto), "subProtoProp", YES, NO)); + testassert(protocol_getProperty(@protocol(SubProto), "superProtoProp", YES, YES) != protocol_getProperty(@protocol(SubProto), "superProtoProp", YES, NO)); - testassert(NULL == class_getProperty(NULL, "foo")); - testassert(NULL == class_getProperty([Sub class], NULL)); - testassert(NULL == class_getProperty(NULL, NULL)); + testassert(nil == protocol_getProperty(nil, "foo", YES, YES)); + testassert(nil == protocol_getProperty(@protocol(SuperProto), nil, YES, YES)); + testassert(nil == protocol_getProperty(nil, nil, YES, YES)); + testassert(nil == protocol_getProperty(@protocol(SuperProto), "superProtoProp", NO, YES)); + testassert(nil == protocol_getProperty(@protocol(SuperProto), "superProtoProp", NO, NO)); succeed(__FILE__); return 0; diff --git a/test/propertyDesc.m b/test/propertyDesc.m index 2a816557..87d3e651 100644 --- a/test/propertyDesc.m +++ b/test/propertyDesc.m @@ -1,4 +1,15 @@ -// TEST_CONFIG +/* +TEST_BUILD_OUTPUT +.*propertyDesc.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*propertyDesc.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*propertyDesc.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*propertyDesc.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*propertyDesc.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*propertyDesc.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*propertyDesc.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*propertyDesc.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +END +*/ #include "test.h" #include "testroot.i" diff --git a/test/protocol.m b/test/protocol.m index c68bd337..1e24ad21 100644 --- a/test/protocol.m +++ b/test/protocol.m @@ -1,6 +1,11 @@ // TEST_CFLAGS -framework Foundation -Wno-deprecated-declarations // need Foundation to get NSObject compatibility additions for class Protocol // because ARC calls [protocol retain] +/* +TEST_BUILD_OUTPUT +.*protocol.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +END +*/ #include "test.h" #include "testroot.i" @@ -8,10 +13,6 @@ #include #include -#if !__OBJC2__ -#include -#endif - @protocol Proto1 +(id)proto1ClassMethod; -(id)proto1InstanceMethod; @@ -60,16 +61,11 @@ @protocol Proto6 @protocol ProtoEmpty @end -#if __OBJC2__ -#define TEST_SWIFT 1 #define SwiftV1MangledName "_TtP6Module15SwiftV1Protocol_" -#endif -#if TEST_SWIFT __attribute__((objc_runtime_name(SwiftV1MangledName))) @protocol SwiftV1Protocol @end -#endif @interface Super : TestRoot @end @implementation Super @@ -95,9 +91,6 @@ int main() Class cls; Protocol * __unsafe_unretained *list; Protocol *protocol, *empty; -#if !__OBJC2__ - struct objc_method_description *desc; -#endif struct objc_method_description desc2; objc_property_t *proplist; unsigned int count; @@ -107,12 +100,6 @@ int main() testassert(protocol); testassert(empty); -#if !__OBJC2__ - testassert([protocol isKindOf:[Protocol class]]); - testassert([empty isKindOf:[Protocol class]]); - testassert(0 == strcmp([protocol name], "Proto3")); - testassert(0 == strcmp([empty name], "ProtoEmpty")); -#endif testassert(0 == strcmp(protocol_getName(protocol), "Proto3")); testassert(0 == strcmp(protocol_getName(empty), "ProtoEmpty")); @@ -126,33 +113,9 @@ int main() testassert(protocol_conformsToProtocol(@protocol(Proto3), @protocol(Proto2))); testassert(!protocol_conformsToProtocol(@protocol(Proto2), @protocol(Proto3))); -#if !__OBJC2__ - testassert([@protocol(Proto1) isEqual:@protocol(Proto1)]); - testassert(! [@protocol(Proto1) isEqual:@protocol(Proto2)]); -#endif testassert(protocol_isEqual(@protocol(Proto1), @protocol(Proto1))); testassert(! protocol_isEqual(@protocol(Proto1), @protocol(Proto2))); -#if !__OBJC2__ - desc = [protocol descriptionForInstanceMethod:@selector(proto3InstanceMethod)]; - testassert(desc); - testassert(desc->name == @selector(proto3InstanceMethod)); - desc = [protocol descriptionForClassMethod:@selector(proto3ClassMethod)]; - testassert(desc); - testassert(desc->name == @selector(proto3ClassMethod)); - desc = [protocol descriptionForClassMethod:@selector(proto2ClassMethod)]; - testassert(desc); - testassert(desc->name == @selector(proto2ClassMethod)); - - desc = [protocol descriptionForInstanceMethod:@selector(proto3ClassMethod)]; - testassert(!desc); - desc = [protocol descriptionForClassMethod:@selector(proto3InstanceMethod)]; - testassert(!desc); - desc = [empty descriptionForInstanceMethod:@selector(proto3ClassMethod)]; - testassert(!desc); - desc = [empty descriptionForClassMethod:@selector(proto3InstanceMethod)]; - testassert(!desc); -#endif desc2 = protocol_getMethodDescription(protocol, @selector(proto3InstanceMethod), YES, YES); testassert(desc2.name && desc2.types); testassert(desc2.name == @selector(proto3InstanceMethod)); @@ -309,12 +272,10 @@ int main() testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto6), @selector(m41:), false, false), types41)); } -#if TEST_SWIFT testassert(@protocol(SwiftV1Protocol) == objc_getProtocol("Module.SwiftV1Protocol")); testassert(@protocol(SwiftV1Protocol) == objc_getProtocol(SwiftV1MangledName)); testassert(0 == strcmp(protocol_getName(@protocol(SwiftV1Protocol)), "Module.SwiftV1Protocol")); testassert(!objc_getProtocol("SwiftV1Protocol")); -#endif succeed(__FILE__); } diff --git a/test/protocol_copyMethodList.m b/test/protocol_copyMethodList.m index 53bf445e..2b5e089d 100644 --- a/test/protocol_copyMethodList.m +++ b/test/protocol_copyMethodList.m @@ -1,6 +1,14 @@ // TEST_CFLAGS -framework Foundation // need Foundation to get NSObject compatibility additions for class Protocol // because ARC calls [protocol retain] +/* +TEST_BUILD_OUTPUT +.*protocol_copyMethodList.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*protocol_copyMethodList.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*protocol_copyMethodList.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*protocol_copyMethodList.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +END +*/ #include "test.h" diff --git a/test/protocol_copyPropertyList.m b/test/protocol_copyPropertyList.m index 406a0bd1..a9ecd228 100644 --- a/test/protocol_copyPropertyList.m +++ b/test/protocol_copyPropertyList.m @@ -1,6 +1,14 @@ // TEST_CFLAGS -framework Foundation // need Foundation to get NSObject compatibility additions for class Protocol // because ARC calls [protocol retain] +/* +TEST_BUILD_OUTPUT +.*protocol_copyPropertyList.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*protocol_copyPropertyList.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*protocol_copyPropertyList.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +.*protocol_copyPropertyList.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +END +*/ #include "test.h" #include @@ -10,11 +18,15 @@ @protocol SuperProps @property int prop1; @property int prop2; +@property(class) int prop1; +@property(class) int prop2; @end @protocol SubProps @property int prop3; @property int prop4; +@property(class) int prop3; +@property(class) int prop4; @end @@ -23,16 +35,28 @@ @protocol FourProps @property int prop2; @property int prop3; @property int prop4; + +@property(class) int prop1; +@property(class) int prop2; +@property(class) int prop3; +@property(class) int prop4; @end @protocol NoProps @end +@protocol OneProp +@property int instanceProp; +@property(class) int classProp; +@end + + static int isNamed(objc_property_t p, const char *name) { return (0 == strcmp(name, property_getName(p))); } -int main() +void testfn(objc_property_t *(*copyPropertyList_fn)(Protocol*, unsigned int *), + const char *onePropName) { objc_property_t *props; unsigned int count; @@ -42,7 +66,7 @@ int main() testassert(proto); count = 100; - props = protocol_copyPropertyList(proto, &count); + props = copyPropertyList_fn(proto, &count); testassert(props); testassert(count == 2); testassert((isNamed(props[0], "prop4") && isNamed(props[1], "prop3")) || @@ -55,7 +79,7 @@ int main() testassert(proto); count = 100; - props = protocol_copyPropertyList(proto, &count); + props = copyPropertyList_fn(proto, &count); testassert(props); testassert(count == 2); testassert((isNamed(props[0], "prop1") && isNamed(props[1], "prop2")) || @@ -70,7 +94,7 @@ int main() testassert(proto); count = 100; - props = protocol_copyPropertyList(proto, &count); + props = copyPropertyList_fn(proto, &count); testassert(props); testassert(count == 4); testassert(malloc_size(props) >= 5 * sizeof(objc_property_t)); @@ -79,7 +103,7 @@ int main() free(props); // Check NULL count parameter - props = protocol_copyPropertyList(proto, NULL); + props = copyPropertyList_fn(proto, NULL); testassert(props); testassert(props[4] == NULL); testassert(props[3] != NULL); @@ -87,12 +111,12 @@ int main() // Check NULL protocol parameter count = 100; - props = protocol_copyPropertyList(NULL, &count); + props = copyPropertyList_fn(NULL, &count); testassert(!props); testassert(count == 0); // Check NULL protocol and count - props = protocol_copyPropertyList(NULL, NULL); + props = copyPropertyList_fn(NULL, NULL); testassert(!props); // Check protocol with no properties @@ -100,10 +124,84 @@ int main() testassert(proto); count = 100; - props = protocol_copyPropertyList(proto, &count); + props = copyPropertyList_fn(proto, &count); testassert(!props); testassert(count == 0); + // Check instance vs class properties + proto = @protocol(OneProp); + testassert(proto); + + count = 100; + props = copyPropertyList_fn(proto, &count); + testassert(props); + testassert(count == 1); + testassert(0 == strcmp(property_getName(props[0]), onePropName)); + free(props); +} + +objc_property_t *protocol_copyPropertyList2_YES_YES(Protocol *proto, unsigned int *outCount) +{ + return protocol_copyPropertyList2(proto, outCount, YES, YES); +} + +objc_property_t *protocol_copyPropertyList2_YES_NO(Protocol *proto, unsigned int *outCount) +{ + return protocol_copyPropertyList2(proto, outCount, YES, NO); +} + +int main() +{ + // protocol_copyPropertyList(...) is identical to + // protocol_copyPropertyList2(..., YES, YES) + testfn(protocol_copyPropertyList, "instanceProp"); + testfn(protocol_copyPropertyList2_YES_YES, "instanceProp"); + + // protocol_copyPropertyList2(..., YES, NO) is also identical + // with the protocol definitions above, except for protocol OneProp. + testfn(protocol_copyPropertyList2_YES_NO, "classProp"); + + // Check non-functionality of optional properties + + unsigned int count; + objc_property_t *props; + + count = 100; + props = protocol_copyPropertyList2(@protocol(FourProps), &count, NO, YES); + testassert(!props); + testassert(count == 0); + + count = 100; + props = protocol_copyPropertyList2(@protocol(FourProps), &count, NO, NO); + testassert(!props); + testassert(count == 0); + + // Check nil count parameter + props = protocol_copyPropertyList2(@protocol(FourProps), nil, NO, YES); + testassert(!props); + + props = protocol_copyPropertyList2(@protocol(FourProps), nil, NO, NO); + testassert(!props); + + // Check nil protocol parameter + count = 100; + props = protocol_copyPropertyList2(nil, &count, NO, YES); + testassert(!props); + testassert(count == 0); + + count = 100; + props = protocol_copyPropertyList2(nil, &count, NO, NO); + testassert(!props); + testassert(count == 0); + + // Check nil protocol and count + props = protocol_copyPropertyList2(nil, nil, NO, YES); + testassert(!props); + + props = protocol_copyPropertyList2(nil, nil, NO, NO); + testassert(!props); + + succeed(__FILE__); return 0; } diff --git a/test/protocol_cw.m b/test/protocol_cw.m deleted file mode 100644 index a82f9912..00000000 --- a/test/protocol_cw.m +++ /dev/null @@ -1,40 +0,0 @@ -// TEST_CFLAGS -Wno-deprecated-declarations - -#include "test.h" - -#if __OBJC2__ - -int main() -{ - succeed(__FILE__); -} - -#else - -// rdar://4951638 - -#include -#include - -char Protocol_name[] __attribute__((section("__OBJC,__class_names"))) = "Protocol"; - -struct st { - void *isa; - const char *protocol_name; - void *protocol_list; - void *instance_methods; - void *class_methods; -}; - -struct st Foo_protocol __attribute__((section("__OBJC,__protocol"))) = { Protocol_name, "Foo", 0, 0, 0 }; - -int main() -{ - Protocol *foo = objc_getProtocol("Foo"); - - testassert(foo == (Protocol *)&Foo_protocol); - testassert(0 == strcmp("Foo", [foo name])); - succeed(__FILE__); -} - -#endif diff --git a/test/rawisa.m b/test/rawisa.m index 3d36ed54..4e30cb39 100644 --- a/test/rawisa.m +++ b/test/rawisa.m @@ -5,12 +5,15 @@ TEST_RUN_OUTPUT objc\[\d+\]: RAW ISA: disabling non-pointer isa because the app has a __DATA,__objc_rawisa section (.* RAW ISA: .*\n)* -OK: rawisa.m +OK: rawisa.m(\n.* RAW ISA: .*)* OR (.* RAW ISA: .*\n)* no __DATA,__rawisa support -OK: rawisa.m +OK: rawisa.m(\n.* RAW ISA: .*)* END + +"RAW ISA" is allowed after "OK" because of static destructors +that provoke class realization. */ #include "test.h" @@ -18,7 +21,7 @@ int main() { fprintf(stderr, "\n"); -#if ! (SUPPORT_NONPOINTER_ISA && TARGET_OS_MAC && !TARGET_OS_IPHONE) +#if ! (SUPPORT_NONPOINTER_ISA && TARGET_OS_OSX) // only 64-bit Mac supports this fprintf(stderr, "no __DATA,__rawisa support\n"); #endif diff --git a/test/readClassPair.m b/test/readClassPair.m index 5a6b9e4d..80313b2a 100644 --- a/test/readClassPair.m +++ b/test/readClassPair.m @@ -1,16 +1,11 @@ -// TEST_CONFIG +/* +TEST_RUN_OUTPUT +objc\[\d+\]: Class Sub is implemented in both [^\s]+ \(0x[0-9a-f]+\) and [^\s]+ \(0x[0-9a-f]+\)\. One of the two will be used\. Which one is undefined\. +OK: readClassPair.m +END + */ #include "test.h" - -#if !__OBJC2__ - -int main() -{ - succeed(__FILE__); -} - -#else - #include // Reuse evil-class-def.m as a non-evil class definition. @@ -54,6 +49,7 @@ int main() testassert(!objc_getClass("Sub")); extern intptr_t OBJC_CLASS_$_Sub[OBJC_MAX_CLASS_SIZE/sizeof(void*)]; + // Make a duplicate of class Sub for use later. intptr_t Sub2_buf[OBJC_MAX_CLASS_SIZE/sizeof(void*)]; memcpy(Sub2_buf, &OBJC_CLASS_$_Sub, sizeof(Sub2_buf)); Class Sub = objc_readClassPair((__bridge Class)(void*)&OBJC_CLASS_$_Sub, &ii); @@ -68,10 +64,19 @@ int main() testassert(class_getInstanceSize(Sub) == 2*sizeof(void*)); [Sub load]; - // Reading a class whose name already exists fails. - testassert(! objc_readClassPair((__bridge Class)(void*)Sub2_buf, &ii)); + // Reading a class whose name already exists succeeds + // with a duplicate warning. + Class Sub2 = objc_readClassPair((__bridge Class)(void*)Sub2_buf, &ii); + testassert(Sub2); + testassert(Sub2 != Sub); + testassert(objc_getClass("Sub") == Sub); // didn't change + testassert(0 == strcmp(class_getName(Sub2), "Sub")); + testassert(class_getSuperclass(Sub2) == Super); + testassert(class_getClassMethod(Sub2, @selector(load))); + testassert(class_getInstanceMethod(Sub2, @selector(load))); + testassert(class_getInstanceVariable(Sub2, "sub_ivar")); + testassert(class_getInstanceSize(Sub2) == 2*sizeof(void*)); + [Sub2 load]; succeed(__FILE__); } - -#endif diff --git a/test/release-workaround.m b/test/release-workaround.m new file mode 100644 index 00000000..5e3bfa3f --- /dev/null +++ b/test/release-workaround.m @@ -0,0 +1,34 @@ +// TEST_CONFIG ARCH=x86_64 MEM=mrc +// TEST_CFLAGS -framework Foundation + +// rdar://20206767 + +#include +#include "test.h" + + +@interface Test : NSObject @end +@implementation Test +@end + + +int main() +{ + id buf[1]; + buf[0] = [Test class]; + id obj = (id)buf; + [obj retain]; + [obj retain]; + + uintptr_t rax; + + [obj release]; + asm("mov %%rax, %0" : "=r" (rax)); + testassert(rax == 0); + + objc_release(obj); + asm("mov %%rax, %0" : "=r" (rax)); + testassert(rax == 0); + + succeed(__FILE__); +} diff --git a/test/resolve.m b/test/resolve.m index ba6fdd63..913a6869 100644 --- a/test/resolve.m +++ b/test/resolve.m @@ -169,7 +169,7 @@ int main() Sub *s; id ret; - objc_setForwardHandler((void*)&forward_handler, NULL); + objc_setForwardHandler((void*)&forward_handler, (void*)&abort); // Be ready for ARC to retain the class object and call +initialize early state = 1; diff --git a/test/rr-autorelease-fast.m b/test/rr-autorelease-fast.m index f44b88dd..0de2b2b8 100644 --- a/test/rr-autorelease-fast.m +++ b/test/rr-autorelease-fast.m @@ -1,19 +1,9 @@ -// TEST_CONFIG CC=clang MEM=mrc +// TEST_CONFIG MEM=mrc // TEST_CFLAGS -Os #include "test.h" #include "testroot.i" -#if __i386__ - -int main() -{ - // no optimization on i386 (neither Mac nor Simulator) - succeed(__FILE__); -} - -#else - #include #include #include @@ -22,17 +12,77 @@ @interface TestObject : TestRoot @end @implementation TestObject @end -#ifdef __arm__ -# define MAGIC asm volatile("mov r7, r7") -# define NOT_MAGIC asm volatile("mov r6, r6") +// MAGIC and NOT_MAGIC each call two functions +// with or without the magic instruction sequence, respectively. +// +// tmp = first(obj); +// magic, or not; +// tmp = second(tmp); + +#if __arm__ + +#define NOT_MAGIC(first, second) \ + tmp = first(obj); \ + asm volatile("mov r8, r8"); \ + tmp = second(tmp); + +#define MAGIC(first, second) \ + tmp = first(obj); \ + asm volatile("mov r7, r7"); \ + tmp = second(tmp); + +// arm #elif __arm64__ -# define MAGIC asm volatile("mov x29, x29") -# define NOT_MAGIC asm volatile("mov x28, x28") + +#define NOT_MAGIC(first, second) \ + tmp = first(obj); \ + asm volatile("mov x28, x28"); \ + tmp = second(tmp); + +#define MAGIC(first, second) \ + tmp = first(obj); \ + asm volatile("mov x29, x29"); \ + tmp = second(tmp); + +// arm64 #elif __x86_64__ -# define MAGIC asm volatile("") -# define NOT_MAGIC asm volatile("nop") + +#define NOT_MAGIC(first, second) \ + tmp = first(obj); \ + asm volatile("nop"); \ + tmp = second(tmp); + +#define MAGIC(first, second) \ + tmp = first(obj); \ + tmp = second(tmp); + +// x86_64 +#elif __i386__ + +#define NOT_MAGIC(first, second) \ + tmp = first(obj); \ + tmp = second(tmp); + +#define MAGIC(first, second) \ + asm volatile("\n subl $16, %%esp" \ + "\n movl %[obj], (%%esp)" \ + "\n call _" #first \ + "\n" \ + "\n movl %%ebp, %%ebp" \ + "\n" \ + "\n movl %%eax, (%%esp)" \ + "\n call _" #second \ + "\n movl %%eax, %[tmp]" \ + "\n addl $16, %%esp" \ + : [tmp] "=r" (tmp) \ + : [obj] "r" (obj) \ + : "eax", "edx", "ecx", "cc", "memory") + +// i386 #else -# error unknown architecture + +#error unknown architecture + #endif @@ -64,9 +114,8 @@ @implementation TestObject @end TestRootAutorelease = 0; TestRootDealloc = 0; - tmp = objc_autoreleaseReturnValue(obj); - MAGIC; - tmp = objc_retainAutoreleasedReturnValue(tmp); + MAGIC(objc_autoreleaseReturnValue, + objc_retainAutoreleasedReturnValue); testassert(TestRootDealloc == 0); testassert(TestRootRetain == 0); @@ -92,9 +141,8 @@ @implementation TestObject @end TestRootAutorelease = 0; TestRootDealloc = 0; - tmp = objc_autoreleaseReturnValue(obj); - NOT_MAGIC; - tmp = objc_retainAutoreleasedReturnValue(tmp); + NOT_MAGIC(objc_autoreleaseReturnValue, + objc_retainAutoreleasedReturnValue); testassert(TestRootDealloc == 0); testassert(TestRootRetain == 1); @@ -125,9 +173,8 @@ @implementation TestObject @end TestRootAutorelease = 0; TestRootDealloc = 0; - tmp = objc_retainAutoreleaseReturnValue(obj); - MAGIC; - tmp = objc_retainAutoreleasedReturnValue(tmp); + MAGIC(objc_retainAutoreleaseReturnValue, + objc_retainAutoreleasedReturnValue); testassert(TestRootDealloc == 0); testassert(TestRootRetain == 1); @@ -159,9 +206,8 @@ @implementation TestObject @end TestRootAutorelease = 0; TestRootDealloc = 0; - tmp = objc_retainAutoreleaseReturnValue(obj); - NOT_MAGIC; - tmp = objc_retainAutoreleasedReturnValue(tmp); + NOT_MAGIC(objc_retainAutoreleaseReturnValue, + objc_retainAutoreleasedReturnValue); testassert(TestRootDealloc == 0); testassert(TestRootRetain == 2); @@ -198,9 +244,8 @@ @implementation TestObject @end TestRootAutorelease = 0; TestRootDealloc = 0; - tmp = objc_autoreleaseReturnValue(obj); - MAGIC; - tmp = objc_unsafeClaimAutoreleasedReturnValue(tmp); + MAGIC(objc_autoreleaseReturnValue, + objc_unsafeClaimAutoreleasedReturnValue); testassert(TestRootDealloc == 0); testassert(TestRootRetain == 0); @@ -226,9 +271,8 @@ @implementation TestObject @end TestRootAutorelease = 0; TestRootDealloc = 0; - tmp = objc_autoreleaseReturnValue(obj); - NOT_MAGIC; - tmp = objc_unsafeClaimAutoreleasedReturnValue(tmp); + NOT_MAGIC(objc_autoreleaseReturnValue, + objc_unsafeClaimAutoreleasedReturnValue); testassert(TestRootDealloc == 0); testassert(TestRootRetain == 0); @@ -259,9 +303,8 @@ @implementation TestObject @end TestRootAutorelease = 0; TestRootDealloc = 0; - tmp = objc_retainAutoreleaseReturnValue(obj); - MAGIC; - tmp = objc_unsafeClaimAutoreleasedReturnValue(tmp); + MAGIC(objc_retainAutoreleaseReturnValue, + objc_unsafeClaimAutoreleasedReturnValue); testassert(TestRootDealloc == 0); testassert(TestRootRetain == 0); @@ -287,9 +330,8 @@ @implementation TestObject @end TestRootAutorelease = 0; TestRootDealloc = 0; - tmp = objc_retainAutoreleaseReturnValue(obj); - NOT_MAGIC; - tmp = objc_unsafeClaimAutoreleasedReturnValue(tmp); + NOT_MAGIC(objc_retainAutoreleaseReturnValue, + objc_unsafeClaimAutoreleasedReturnValue); testassert(TestRootDealloc == 0); testassert(TestRootRetain == 1); @@ -313,5 +355,3 @@ @implementation TestObject @end return 0; } - -#endif diff --git a/test/rr-autorelease-fastarc.m b/test/rr-autorelease-fastarc.m index 364b30dd..da536f19 100644 --- a/test/rr-autorelease-fastarc.m +++ b/test/rr-autorelease-fastarc.m @@ -4,16 +4,6 @@ #include "test.h" #include "testroot.i" -#if __i386__ - -int main() -{ - // no optimization on i386 (neither Mac nor Simulator) - succeed(__FILE__); -} - -#else - #include #include #include @@ -21,21 +11,6 @@ int main() @interface TestObject : TestRoot @end @implementation TestObject @end - -#ifdef __arm__ -# define MAGIC asm volatile("mov r7, r7") -# define NOT_MAGIC asm volatile("mov r6, r6") -#elif __arm64__ -# define MAGIC asm volatile("mov x29, x29") -# define NOT_MAGIC asm volatile("mov x28, x28") -#elif __x86_64__ -# define MAGIC asm volatile("") -# define NOT_MAGIC asm volatile("nop") -#else -# error unknown architecture -#endif - - @interface Tester : NSObject @end @implementation Tester { @public @@ -55,14 +30,12 @@ -(id) return1 { OBJC_EXPORT id -objc_retainAutoreleasedReturnValue(id obj) - __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0); +objc_retainAutoreleasedReturnValue(id obj); // Accept a value returned through a +0 autoreleasing convention for use at +0. OBJC_EXPORT id -objc_unsafeClaimAutoreleasedReturnValue(id obj) - __OSX_AVAILABLE_STARTING(__MAC_10_11, __IPHONE_9_0); +objc_unsafeClaimAutoreleasedReturnValue(id obj); int diff --git a/test/rr-autorelease-stacklogging.m b/test/rr-autorelease-stacklogging.m index 40aa039a..6347364d 100644 --- a/test/rr-autorelease-stacklogging.m +++ b/test/rr-autorelease-stacklogging.m @@ -8,5 +8,6 @@ #define FOUNDATION 0 #define NAME "rr-autorelease-stacklogging" +#define DEBUG_POOL_ALLOCATION 1 #include "rr-autorelease2.m" diff --git a/test/rr-autorelease2.m b/test/rr-autorelease2.m index d68bd54f..f2420828 100644 --- a/test/rr-autorelease2.m +++ b/test/rr-autorelease2.m @@ -169,15 +169,37 @@ void cycle(void) } // Top-level thread pool popped normally. + // Check twice - once for empty placeholder, once without. +# if DEBUG_POOL_ALLOCATION || FOUNDATION + // DebugPoolAllocation disables the empty placeholder pool. + // Guard Malloc disables the empty placeholder pool (checked at runtime) + // Foundation makes RR_PUSH return an NSAutoreleasePool not the raw token. +# define CHECK_PLACEHOLDER 0 +# else +# define CHECK_PLACEHOLDER 1 +# endif testprintf("-- Thread-level pool popped normally.\n"); { state = 0; testonthread(^{ void *pool = RR_PUSH(); +#if CHECK_PLACEHOLDER + if (!is_guardmalloc()) { + testassert(pool == (void*)1); + } +#endif + RR_AUTORELEASE([[Deallocator alloc] init]); + RR_POP(pool); + pool = RR_PUSH(); +#if CHECK_PLACEHOLDER + if (!is_guardmalloc()) { + testassert(pool != (void*)1); + } +#endif RR_AUTORELEASE([[Deallocator alloc] init]); RR_POP(pool); }); - testassert(state == 1); + testassert(state == 2); } @@ -276,7 +298,7 @@ void cycle(void) int main() { pthread_attr_init(&smallstack); - pthread_attr_setstacksize(&smallstack, 16384); + pthread_attr_setstacksize(&smallstack, 32768); // inflate the refcount side table so it doesn't show up in leak checks { @@ -333,21 +355,19 @@ int main() } // check for leaks using pools not at top level + // fixme for FOUNDATION this leak mark/check needs + // to be outside the autorelease pool for some reason + leak_mark(); void *pool = RR_PUSH(); { - leak_mark(); - for (int i = 0; i < 1000; i++) { cycle(); } - leak_check(0); - slow_cycle(); - - leak_check(0); } RR_POP(pool); + leak_check(0); // NSThread. // Can't leak check this because it's too noisy. diff --git a/test/runtime.m b/test/runtime.m index b4a592cf..c70620bf 100644 --- a/test/runtime.m +++ b/test/runtime.m @@ -1,4 +1,10 @@ /* +TEST_BUILD_OUTPUT +.*runtime.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*runtime.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +.*runtime.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +END + TEST_RUN_OUTPUT objc\[\d+\]: class `SwiftV1Class\' not linked into application objc\[\d+\]: class `DoesNotExist\' not linked into application @@ -21,6 +27,11 @@ int main() { + // provoke the same nullability warnings as the real test + objc_getClass(nil); + objc_getClass(nil); + objc_getClass(nil); + testwarn("rdar://11368528 confused by Foundation"); fprintf(stderr, "confused by Foundation\n"); succeed(__FILE__); @@ -31,18 +42,11 @@ int main() @interface Sub : TestRoot @end @implementation Sub @end -#if __OBJC2__ -# define TEST_SWIFT 1 -#else -# define TEST_SWIFT 0 -#endif - #define SwiftV1MangledName "_TtC6Module12SwiftV1Class" #define SwiftV1MangledName2 "_TtC2Sw13SwiftV1Class2" -#define SwiftV1MangledName3 "_TtCSs13SwiftV1Class3" +#define SwiftV1MangledName3 "_TtCs13SwiftV1Class3" #define SwiftV1MangledName4 "_TtC6Swiftt13SwiftV1Class4" -#if TEST_SWIFT __attribute__((objc_runtime_name(SwiftV1MangledName))) @interface SwiftV1Class : TestRoot @end @implementation SwiftV1Class @end @@ -58,7 +62,6 @@ @implementation SwiftV1Class3 @end __attribute__((objc_runtime_name(SwiftV1MangledName4))) @interface SwiftV1Class4 : TestRoot @end @implementation SwiftV1Class4 @end -#endif int main() @@ -74,6 +77,7 @@ int main() int foundSwiftV1class3; int foundSwiftV1class4; const char **names; + const char **namesFromHeader; Dl_info info; [TestRoot class]; @@ -82,11 +86,7 @@ int main() dladdr(&_mh_execute_header, &info); names = objc_copyClassNamesForImage(info.dli_fname, &count); testassert(names); -#if TEST_SWIFT testassert(count == 6); -#else - testassert(count == 2); -#endif testassert(names[count] == NULL); foundTestRoot = 0; foundSub = 0; @@ -104,12 +104,18 @@ int main() } testassert(foundTestRoot == 1); testassert(foundSub == 1); -#if TEST_SWIFT testassert(foundSwiftV1 == 1); testassert(foundSwiftV1class2 == 1); testassert(foundSwiftV1class3 == 1); testassert(foundSwiftV1class4 == 1); -#endif + + // Getting the names using the header should give us the same list. + namesFromHeader = objc_copyClassNamesForImage(info.dli_fname, &count0); + testassert(namesFromHeader); + testassert(count == count0); + for (i = 0; i < count; i++) { + testassert(!strcmp(names[i], namesFromHeader[i])); + } // class Sub hasn't been touched - make sure it's in the class list too @@ -146,16 +152,13 @@ int main() } testassert(foundTestRoot == 1); testassert(foundSub == 1); -#if TEST_SWIFT testassert(foundSwiftV1 == 1); testassert(foundSwiftV1class2 == 1); testassert(foundSwiftV1class3 == 1); testassert(foundSwiftV1class4 == 1); -#endif // fixme check class handler testassert(objc_getClass("TestRoot") == [TestRoot class]); -#if TEST_SWIFT testassert(objc_getClass("Module.SwiftV1Class") == [SwiftV1Class class]); testassert(objc_getClass(SwiftV1MangledName) == [SwiftV1Class class]); testassert(objc_getClass("Sw.SwiftV1Class2") == [SwiftV1Class2 class]); @@ -164,26 +167,21 @@ int main() testassert(objc_getClass(SwiftV1MangledName3) == [SwiftV1Class3 class]); testassert(objc_getClass("Swiftt.SwiftV1Class4") == [SwiftV1Class4 class]); testassert(objc_getClass(SwiftV1MangledName4) == [SwiftV1Class4 class]); -#endif testassert(objc_getClass("SwiftV1Class") == nil); testassert(objc_getClass("DoesNotExist") == nil); testassert(objc_getClass(NULL) == nil); testassert(objc_getMetaClass("TestRoot") == object_getClass([TestRoot class])); -#if TEST_SWIFT testassert(objc_getMetaClass("Module.SwiftV1Class") == object_getClass([SwiftV1Class class])); testassert(objc_getMetaClass(SwiftV1MangledName) == object_getClass([SwiftV1Class class])); -#endif testassert(objc_getMetaClass("SwiftV1Class") == nil); testassert(objc_getMetaClass("DoesNotExist") == nil); testassert(objc_getMetaClass(NULL) == nil); // fixme check class no handler testassert(objc_lookUpClass("TestRoot") == [TestRoot class]); -#if TEST_SWIFT testassert(objc_lookUpClass("Module.SwiftV1Class") == [SwiftV1Class class]); testassert(objc_lookUpClass(SwiftV1MangledName) == [SwiftV1Class class]); -#endif testassert(objc_lookUpClass("SwiftV1Class") == nil); testassert(objc_lookUpClass("DoesNotExist") == nil); testassert(objc_lookUpClass(NULL) == nil); @@ -194,10 +192,8 @@ int main() testassert(object_isClass(object_getClass([TestRoot class]))); testassert(object_isClass([Sub class])); testassert(object_isClass(object_getClass([Sub class]))); -#if TEST_SWIFT testassert(object_isClass([SwiftV1Class class])); testassert(object_isClass(object_getClass([SwiftV1Class class]))); -#endif list2 = objc_copyClassList(&count2); testassert(count2 == count); diff --git a/test/sel.m b/test/sel.m index 44618c5e..a4c4dd31 100644 --- a/test/sel.m +++ b/test/sel.m @@ -1,4 +1,8 @@ -// TEST_CONFIG +/* +TEST_BUILD_OUTPUT +.*sel.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'testassert')? +END +*/ #include "test.h" #include @@ -13,33 +17,5 @@ int main() // sel_getName recognizes the zero SEL testassert(0 == strcmp("", sel_getName(0))); - // GC-ignored selectors. -#if __has_feature(objc_arc) - - // ARC dislikes `@selector(retain)` - -#else - -# if defined(__i386__) - // sel_getName recognizes GC-ignored SELs - if (objc_collectingEnabled()) { - testassert(0 == strcmp("", - sel_getName(@selector(retain)))); - } else { - testassert(0 == strcmp("retain", - sel_getName(@selector(retain)))); - } - - // _objc_search_builtins() shouldn't crash on GC-ignored SELs - union { - SEL sel; - const char *ptr; - } u; - u.sel = @selector(retain); - testassert(@selector(retain) == sel_registerName(u.ptr)); -# endif - -#endif - succeed(__FILE__); } diff --git a/test/setSuper.m b/test/setSuper.m index 7ce6deea..5f778407 100644 --- a/test/setSuper.m +++ b/test/setSuper.m @@ -1,4 +1,4 @@ -// TEST_CFLAGS -Wno-deprecated-declarations +// TEST_CONFIG #include "test.h" #include "testroot.i" diff --git a/test/subscripting.m b/test/subscripting.m index e05a370c..ecc2b131 100644 --- a/test/subscripting.m +++ b/test/subscripting.m @@ -1,17 +1,5 @@ -// TEST_CONFIG MEM=arc,mrc CC=clang LANGUAGE=objc,objc++ // TEST_CFLAGS -framework Foundation -#if !__OBJC2__ - -#include "test.h" - -int main() -{ - succeed(__FILE__); -} - -#else - #import #import #import @@ -116,7 +104,7 @@ int main() { id object = testIndexed[i]; testassert(object == objects[i]); } - if (getenv("VERBOSE")) { + if (testverbose()) { i = 0; for (id object in testIndexed) { NSString *message = [NSString stringWithFormat:@"testIndexed[%zu] = %@\n", i++, object]; @@ -135,7 +123,7 @@ int main() { id object = testKeyed[key]; testassert(object == objects[i]); } - if (getenv("VERBOSE")) { + if (testverbose()) { for (id key in testKeyed) { NSString *message = [NSString stringWithFormat:@"testKeyed[@\"%@\"] = %@\n", key, testKeyed[key]]; testprintf([message UTF8String]); @@ -149,6 +137,3 @@ int main() { return 0; } - -// __OBJC2__ -#endif diff --git a/test/swift-class-def.m b/test/swift-class-def.m new file mode 100644 index 00000000..3288ad71 --- /dev/null +++ b/test/swift-class-def.m @@ -0,0 +1,291 @@ +#include + +#if __LP64__ +# define PTR " .quad " +# define PTRSIZE "8" +# define LOGPTRSIZE "3" +#else +# define PTR " .long " +# define PTRSIZE "4" +# define LOGPTRSIZE "2" +#endif + +#if __has_feature(ptrauth_calls) +# define SIGNED_METHOD_LIST_IMP "@AUTH(ia,0,addr) " +#else +# define SIGNED_METHOD_LIST_IMP +#endif + +#define str(x) #x +#define str2(x) str(x) + +// Swift metadata initializers. Define these in the test. +EXTERN_C Class initSuper(Class cls, void *arg); +EXTERN_C Class initSub(Class cls, void *arg); + +@interface SwiftSuper : NSObject @end +@interface SwiftSub : SwiftSuper @end + +__BEGIN_DECLS +// not id to avoid ARC operations because the class doesn't implement RR methods +void* nop(void* self) { return self; } +__END_DECLS + +asm( + ".globl _OBJC_CLASS_$_SwiftSuper \n" + ".section __DATA,__objc_data \n" + ".align 3 \n" + "_OBJC_CLASS_$_SwiftSuper: \n" + PTR "_OBJC_METACLASS_$_SwiftSuper \n" + PTR "_OBJC_CLASS_$_NSObject \n" + PTR "__objc_empty_cache \n" + PTR "0 \n" + PTR "L_ro + 2 \n" + // pad to OBJC_MAX_CLASS_SIZE + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + "" + "_OBJC_METACLASS_$_SwiftSuper: \n" + PTR "_OBJC_METACLASS_$_NSObject \n" + PTR "_OBJC_METACLASS_$_NSObject \n" + PTR "__objc_empty_cache \n" + PTR "0 \n" + PTR "L_meta_ro \n" + // pad to OBJC_MAX_CLASS_SIZE + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + "" + "L_ro: \n" + ".long (1<<6)\n" + ".long 0 \n" + ".long "PTRSIZE" \n" +#if __LP64__ + ".long 0 \n" +#endif + PTR "0 \n" + PTR "L_super_name \n" + PTR "L_good_methods \n" + PTR "0 \n" + PTR "L_super_ivars \n" + PTR "0 \n" + PTR "0 \n" + PTR "_initSuper" SIGNED_METHOD_LIST_IMP "\n" + "" + "L_meta_ro: \n" + ".long 1 \n" + ".long 40 \n" + ".long 40 \n" +#if __LP64__ + ".long 0 \n" +#endif + PTR "0 \n" + PTR "L_super_name \n" + PTR "L_good_methods \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + + ".globl _OBJC_CLASS_$_SwiftSub \n" + ".section __DATA,__objc_data \n" + ".align 3 \n" + "_OBJC_CLASS_$_SwiftSub: \n" + PTR "_OBJC_METACLASS_$_SwiftSub \n" + PTR "_OBJC_CLASS_$_SwiftSuper \n" + PTR "__objc_empty_cache \n" + PTR "0 \n" + PTR "L_sub_ro + 2 \n" + // pad to OBJC_MAX_CLASS_SIZE + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + "" + "_OBJC_METACLASS_$_SwiftSub: \n" + PTR "_OBJC_METACLASS_$_NSObject \n" + PTR "_OBJC_METACLASS_$_SwiftSuper \n" + PTR "__objc_empty_cache \n" + PTR "0 \n" + PTR "L_sub_meta_ro \n" + // pad to OBJC_MAX_CLASS_SIZE + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + "" + "L_sub_ro: \n" + ".long (1<<6)\n" + ".long 0 \n" + ".long "PTRSIZE" \n" +#if __LP64__ + ".long 0 \n" +#endif + PTR "0 \n" + PTR "L_sub_name \n" + PTR "L_good_methods \n" + PTR "0 \n" + PTR "L_sub_ivars \n" + PTR "0 \n" + PTR "0 \n" + PTR "_initSub" SIGNED_METHOD_LIST_IMP "\n" + "" + "L_sub_meta_ro: \n" + ".long 1 \n" + ".long 40 \n" + ".long 40 \n" +#if __LP64__ + ".long 0 \n" +#endif + PTR "0 \n" + PTR "L_sub_name \n" + PTR "L_good_methods \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + PTR "0 \n" + + "L_good_methods: \n" + ".long 3*"PTRSIZE" \n" + ".long 1 \n" + PTR "L_self \n" + PTR "L_self \n" + PTR "_nop" SIGNED_METHOD_LIST_IMP "\n" + + "L_super_ivars: \n" + ".long 4*"PTRSIZE" \n" + ".long 1 \n" + PTR "L_super_ivar_offset \n" + PTR "L_super_ivar_name \n" + PTR "L_super_ivar_type \n" + ".long "LOGPTRSIZE" \n" + ".long "PTRSIZE" \n" + + "L_sub_ivars: \n" + ".long 4*"PTRSIZE" \n" + ".long 1 \n" + PTR "L_sub_ivar_offset \n" + PTR "L_sub_ivar_name \n" + PTR "L_sub_ivar_type \n" + ".long "LOGPTRSIZE" \n" + ".long "PTRSIZE" \n" + + "L_super_ivar_offset: \n" + ".long 0 \n" + "L_sub_ivar_offset: \n" + ".long "PTRSIZE" \n" + + ".cstring \n" + "L_super_name: .ascii \"SwiftSuper\\0\" \n" + "L_sub_name: .ascii \"SwiftSub\\0\" \n" + "L_load: .ascii \"load\\0\" \n" + "L_self: .ascii \"self\\0\" \n" + "L_super_ivar_name: .ascii \"super_ivar\\0\" \n" + "L_super_ivar_type: .ascii \"c\\0\" \n" + "L_sub_ivar_name: .ascii \"sub_ivar\\0\" \n" + "L_sub_ivar_type: .ascii \"@\\0\" \n" + + + ".section __DATA,__objc_classlist \n" + PTR "_OBJC_CLASS_$_SwiftSuper \n" + PTR "_OBJC_CLASS_$_SwiftSub \n" + + ".text \n" +); + +void fn(void) { } diff --git a/test/swiftMetadataInitializer.m b/test/swiftMetadataInitializer.m new file mode 100644 index 00000000..bfa54a8f --- /dev/null +++ b/test/swiftMetadataInitializer.m @@ -0,0 +1,70 @@ +// TEST_CONFIG MEM=mrc + +#include "test.h" +#include "swift-class-def.m" + + +// _objc_swiftMetadataInitializer hooks for the classes in swift-class-def.m + +Class initSuper(Class cls __unused, void *arg __unused) +{ + // This test provokes objc's callback out of superclass order. + // SwiftSub's init is first. SwiftSuper's init is never called. + + fail("SwiftSuper's init should not have been called"); +} + +bool isRealized(Class cls) +{ + // check the is-realized bits directly + +#if __LP64__ +# define mask (~(uintptr_t)7) +#else +# define mask (~(uintptr_t)3) +#endif +#define RW_REALIZED (1<<31) + + uintptr_t rw = ((uintptr_t *)cls)[4] & mask; // class_t->data + return ((uint32_t *)rw)[0] & RW_REALIZED; // class_rw_t->flags +} + +static int SubInits = 0; +Class initSub(Class cls, void *arg) +{ + testprintf("initSub callback\n"); + + extern uintptr_t OBJC_CLASS_$_SwiftSuper; + extern uintptr_t OBJC_CLASS_$_SwiftSub; + Class RawSwiftSuper = (Class)&OBJC_CLASS_$_SwiftSuper; + Class RawSwiftSub = (Class)&OBJC_CLASS_$_SwiftSub; + + testassert(SubInits == 0); + SubInits++; + testassert(arg == nil); + testassert(0 == strcmp(class_getName(cls), "SwiftSub")); + testassert(cls == RawSwiftSub); + testassert(!isRealized(RawSwiftSuper)); + testassert(!isRealized(RawSwiftSub)); + + testprintf("initSub beginning _objc_realizeClassFromSwift\n"); + _objc_realizeClassFromSwift(cls, cls); + testprintf("initSub finished _objc_realizeClassFromSwift\n"); + + testassert(isRealized(RawSwiftSuper)); + testassert(isRealized(RawSwiftSub)); + + return cls; +} + + +int main() +{ + testassert(SubInits == 0); + testprintf("calling [SwiftSub class]\n"); + [SwiftSub class]; + testprintf("finished [SwiftSub class]\n"); + testassert(SubInits == 1); + [SwiftSuper class]; + succeed(__FILE__); +} diff --git a/test/synchronized-counter.m b/test/synchronized-counter.m index 7f6bd191..7d3fd2dd 100644 --- a/test/synchronized-counter.m +++ b/test/synchronized-counter.m @@ -29,8 +29,6 @@ int n, d; int depth = 1 + (int)(intptr_t)arg % 4; - objc_registerThreadWithCollector(); - for (n = 0; n < COUNT; n++) { // Lock for (d = 0; d < depth; d++) { diff --git a/test/synchronized-grid.m b/test/synchronized-grid.m index 64c99f49..47f7cccf 100644 --- a/test/synchronized-grid.m +++ b/test/synchronized-grid.m @@ -40,8 +40,6 @@ int n, d; int depth = 1 + (int)(intptr_t)arg % 4; - objc_registerThreadWithCollector(); - for (n = 0; n < COUNT; n++) { int rrr = rand() % ROWS; int ccc = rand() % COLS; diff --git a/test/synchronized.m b/test/synchronized.m index 11922be6..cab6dc06 100644 --- a/test/synchronized.m +++ b/test/synchronized.m @@ -22,8 +22,6 @@ { int err; - objc_registerThreadWithCollector(); - // non-blocking sync_enter err = objc_sync_enter(obj); testassert(err == OBJC_SYNC_SUCCESS); diff --git a/test/taggedNSPointers.m b/test/taggedNSPointers.m index f8b5732a..86efb906 100644 --- a/test/taggedNSPointers.m +++ b/test/taggedNSPointers.m @@ -9,7 +9,7 @@ void testTaggedNumber() { NSNumber *taggedNS = [NSNumber numberWithInt: 1234]; - CFNumberRef taggedCF = (CFNumberRef)objc_unretainedPointer(taggedNS); + CFNumberRef taggedCF = (__bridge CFNumberRef)taggedNS; int result; testassert( CFGetTypeID(taggedCF) == CFNumberGetTypeID() ); @@ -71,7 +71,7 @@ int main() int main() { PUSH_POOL { - testassert(*(void **)objc_unretainedPointer([NSNumber numberWithInt:1234])); + testassert(*(void **)(__bridge void *)[NSNumber numberWithInt:1234]); } POP_POOL; succeed(__FILE__); diff --git a/test/taggedPointers.m b/test/taggedPointers.m index ae4e994a..76f16177 100644 --- a/test/taggedPointers.m +++ b/test/taggedPointers.m @@ -1,4 +1,4 @@ -// TEST_CONFIG +// TEST_CFLAGS -fobjc-weak #include "test.h" #include @@ -9,7 +9,7 @@ #if OBJC_HAVE_TAGGED_POINTERS -#if !__OBJC2__ || (!__x86_64__ && !__arm64__) +#if !__x86_64__ && !__arm64__ #error wrong architecture for tagged pointers #endif @@ -28,19 +28,13 @@ -(void) dealloc { } SUPER_DEALLOC(); } --(void) finalize { - for (unsigned int i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) { - testassert(weaks[i] == nil); - } - [super finalize]; -} @end OBJC_ROOT_CLASS -@interface TaggedBaseClass +@interface TaggedBaseClass60 @end -@implementation TaggedBaseClass +@implementation TaggedBaseClass60 -(id) self { return self; } + (void) initialize { @@ -51,7 +45,7 @@ - (void) instanceMethod { } - (uintptr_t) taggedValue { - return _objc_getTaggedPointerValue(objc_unretainedPointer(self)); + return _objc_getTaggedPointerValue((__bridge void*)self); } - (struct stret) stret: (struct stret) aStruct { @@ -64,7 +58,7 @@ - (long double) fpret: (long double) aValue { -(void) dealloc { - fail("TaggedBaseClass dealloc called!"); + fail("TaggedBaseClass60 dealloc called!"); } static void * @@ -100,10 +94,10 @@ +(void) load { @end -@interface TaggedSubclass: TaggedBaseClass +@interface TaggedSubclass52: TaggedBaseClass60 @end -@implementation TaggedSubclass +@implementation TaggedSubclass52 - (void) instanceMethod { return [super instanceMethod]; @@ -132,7 +126,7 @@ - (void) instanceMethod { } - (uintptr_t) taggedValue { - return _objc_getTaggedPointerValue(objc_unretainedPointer(self)); + return _objc_getTaggedPointerValue((__bridge void*)self); } - (struct stret) stret: (struct stret) aStruct { @@ -150,19 +144,35 @@ void testTaggedPointerValue(Class cls, objc_tag_index_t tag, uintptr_t value) testprintf("obj %p, tag %p, value %p\n", taggedAddress, (void*)tag, (void*)value); + bool ext = (tag >= OBJC_TAG_First52BitPayload); + // _objc_makeTaggedPointer must quietly mask out of range values for now - value = (value << 4) >> 4; + if (ext) { + value = (value << 12) >> 12; + } else { + value = (value << 4) >> 4; + } testassert(_objc_isTaggedPointer(taggedAddress)); testassert(_objc_getTaggedPointerTag(taggedAddress) == tag); testassert(_objc_getTaggedPointerValue(taggedAddress) == value); + testassert(objc_debug_taggedpointer_obfuscator != 0); + + if (ext) { + uintptr_t slot = ((uintptr_t)taggedAddress >> objc_debug_taggedpointer_ext_slot_shift) & objc_debug_taggedpointer_ext_slot_mask; + testassert(objc_debug_taggedpointer_ext_classes[slot] == cls); + uintptr_t deobfuscated = (uintptr_t)taggedAddress ^ objc_debug_taggedpointer_obfuscator; + testassert(((deobfuscated << objc_debug_taggedpointer_ext_payload_lshift) >> objc_debug_taggedpointer_ext_payload_rshift) == value); + } + else { + testassert(((uintptr_t)taggedAddress & objc_debug_taggedpointer_mask) == objc_debug_taggedpointer_mask); + uintptr_t slot = ((uintptr_t)taggedAddress >> objc_debug_taggedpointer_slot_shift) & objc_debug_taggedpointer_slot_mask; + testassert(objc_debug_taggedpointer_classes[slot] == cls); + uintptr_t deobfuscated = (uintptr_t)taggedAddress ^ objc_debug_taggedpointer_obfuscator; + testassert(((deobfuscated << objc_debug_taggedpointer_payload_lshift) >> objc_debug_taggedpointer_payload_rshift) == value); + } - testassert((uintptr_t)taggedAddress & objc_debug_taggedpointer_mask); - uintptr_t slot = ((uintptr_t)taggedAddress >> objc_debug_taggedpointer_slot_shift) & objc_debug_taggedpointer_slot_mask; - testassert(objc_debug_taggedpointer_classes[slot] == cls); - testassert((((uintptr_t)taggedAddress << objc_debug_taggedpointer_payload_lshift) >> objc_debug_taggedpointer_payload_rshift) == value); - - id taggedPointer = objc_unretainedObject(taggedAddress); + id taggedPointer = (__bridge id)taggedAddress; testassert(!object_isClass(taggedPointer)); testassert(object_getClass(taggedPointer) == cls); testassert([taggedPointer taggedValue] == value); @@ -191,6 +201,9 @@ void testGenericTaggedPointer(objc_tag_index_t tag, Class cls) testTaggedPointerValue(cls, tag, 0); testTaggedPointerValue(cls, tag, 1UL << 0); testTaggedPointerValue(cls, tag, 1UL << 1); + testTaggedPointerValue(cls, tag, 1UL << 50); + testTaggedPointerValue(cls, tag, 1UL << 51); + testTaggedPointerValue(cls, tag, 1UL << 52); testTaggedPointerValue(cls, tag, 1UL << 58); testTaggedPointerValue(cls, tag, 1UL << 59); testTaggedPointerValue(cls, tag, ~0UL >> 4); @@ -199,6 +212,9 @@ void testGenericTaggedPointer(objc_tag_index_t tag, Class cls) // Tagged pointers should bypass refcount tables and autorelease pools // and weak reference tables WeakContainer *w = [WeakContainer new]; + + // force sidetable retain of the WeakContainer before leak checking + objc_retain(w); #if !__has_feature(objc_arc) // prime method caches before leak checking id taggedPointer = (id)_objc_makeTaggedPointer(tag, 1234); @@ -206,19 +222,22 @@ void testGenericTaggedPointer(objc_tag_index_t tag, Class cls) [taggedPointer release]; [taggedPointer autorelease]; #endif + // prime is_debug() before leak checking + (void)is_debug(); + leak_mark(); - for (uintptr_t i = 0; i < sizeof(w->weaks)/sizeof(w->weaks[0]); i++) { - id o = objc_unretainedObject(_objc_makeTaggedPointer(tag, i)); - testassert(object_getClass(o) == cls); - - id result = WEAK_STORE(w->weaks[i], o); - testassert(result == o); - testassert(w->weaks[i] == o); - - result = WEAK_LOAD(w->weaks[i]); - testassert(result == o); - - if (!objc_collectingEnabled()) { + testonthread(^(void) { + for (uintptr_t i = 0; i < sizeof(w->weaks)/sizeof(w->weaks[0]); i++) { + id o = (__bridge id)_objc_makeTaggedPointer(tag, i); + testassert(object_getClass(o) == cls); + + id result = WEAK_STORE(w->weaks[i], o); + testassert(result == o); + testassert(w->weaks[i] == o); + + result = WEAK_LOAD(w->weaks[i]); + testassert(result == o); + uintptr_t rc = _objc_rootRetainCount(o); testassert(rc != 0); _objc_rootRelease(o); testassert(_objc_rootRetainCount(o) == rc); @@ -259,39 +278,59 @@ void testGenericTaggedPointer(objc_tag_index_t tag, Class cls) } POP_POOL; testassert(_objc_rootRetainCount(o) == rc); } + }); + if (is_debug()) { + // libobjc's debug lock checking makes this leak check fail + testwarn("skipping leak check with debug libobjc build"); + } else { + leak_check(0); } - leak_check(0); for (uintptr_t i = 0; i < 10000; i++) { testassert(w->weaks[i] != NULL); WEAK_STORE(w->weaks[i], NULL); testassert(w->weaks[i] == NULL); testassert(WEAK_LOAD(w->weaks[i]) == NULL); } + objc_release(w); RELEASE_VAR(w); } int main() { - if (objc_collecting_enabled()) { - // GC's block objects crash without this - dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", RTLD_LAZY); - } - testassert(objc_debug_taggedpointer_mask != 0); testassert(_objc_taggedPointersEnabled()); PUSH_POOL { // Avoid CF's tagged pointer tags because of rdar://11368528 + // Reserved slot should be nil until the + // first extended tag is registered. + // This test no longer works because XPC now uses extended tags. +#define HAVE_XPC_TAGS 1 + + uintptr_t extSlot = (~objc_debug_taggedpointer_obfuscator >> objc_debug_taggedpointer_slot_shift) & objc_debug_taggedpointer_slot_mask; + Class extPlaceholder = objc_getClass("__NSUnrecognizedTaggedPointer"); + testassert(extPlaceholder != nil); + +#if !HAVE_XPC_TAGS + testassert(objc_debug_taggedpointer_classes[extSlot] == nil); +#endif + _objc_registerTaggedPointerClass(OBJC_TAG_1, - objc_getClass("TaggedBaseClass")); + objc_getClass("TaggedBaseClass60")); testGenericTaggedPointer(OBJC_TAG_1, - objc_getClass("TaggedBaseClass")); + objc_getClass("TaggedBaseClass60")); - _objc_registerTaggedPointerClass(OBJC_TAG_7, - objc_getClass("TaggedSubclass")); - testGenericTaggedPointer(OBJC_TAG_7, - objc_getClass("TaggedSubclass")); +#if !HAVE_XPC_TAGS + testassert(objc_debug_taggedpointer_classes[extSlot] == nil); +#endif + + _objc_registerTaggedPointerClass(OBJC_TAG_First52BitPayload, + objc_getClass("TaggedSubclass52")); + testGenericTaggedPointer(OBJC_TAG_First52BitPayload, + objc_getClass("TaggedSubclass52")); + + testassert(objc_debug_taggedpointer_classes[extSlot] == extPlaceholder); _objc_registerTaggedPointerClass(OBJC_TAG_NSManagedObjectID, objc_getClass("TaggedNSObjectSubclass")); @@ -310,12 +349,7 @@ int main() int main() { -#if __OBJC2__ testassert(objc_debug_taggedpointer_mask == 0); -#else - testassert(!dlsym(RTLD_DEFAULT, "objc_debug_taggedpointer_mask")); -#endif - succeed(__FILE__); } diff --git a/test/taggedPointersAllClasses.m b/test/taggedPointersAllClasses.m new file mode 100644 index 00000000..9c02840d --- /dev/null +++ b/test/taggedPointersAllClasses.m @@ -0,0 +1,86 @@ +// TEST_CONFIG + +#include "test.h" +#include "testroot.i" +#include +#include + +#if OBJC_HAVE_TAGGED_POINTERS + +@interface TagSuperclass: TestRoot + +- (void)test; + +@end + +@implementation TagSuperclass + +- (void)test {} + +@end + +int main() +{ + Class classes[OBJC_TAG_Last52BitPayload + 1] = {}; + + __block uintptr_t expectedPayload; + __block uintptr_t sawPayload; + __block int sawTag; + + for (int i = 0; i <= OBJC_TAG_Last52BitPayload; i++) { + objc_tag_index_t tag = (objc_tag_index_t)i; + if (i > OBJC_TAG_Last60BitPayload && i < OBJC_TAG_First52BitPayload) + continue; + if (_objc_getClassForTag(tag) != nil) + continue; + + char *name; + asprintf(&name, "Tag%d", i); + classes[i] = objc_allocateClassPair([TagSuperclass class], name, 0); + free(name); + + IMP testIMP = imp_implementationWithBlock(^(void *self) { + testassert(i == _objc_getTaggedPointerTag(self)); + testassert(expectedPayload == _objc_getTaggedPointerValue(self)); + sawPayload = _objc_getTaggedPointerValue(self); + sawTag = i; + }); + class_addMethod(classes[i], @selector(test), testIMP, "v@@"); + + objc_registerClassPair(classes[i]); + _objc_registerTaggedPointerClass(tag, classes[i]); + } + + for (int i = 0; i <= OBJC_TAG_Last52BitPayload; i++) { + objc_tag_index_t tag = (objc_tag_index_t)i; + if (classes[i] == nil) + continue; + + for (int byte = 0; byte <= 0xff; byte++) { + uintptr_t payload; + memset(&payload, byte, sizeof(payload)); + + if (i <= OBJC_TAG_Last60BitPayload) + payload >>= _OBJC_TAG_PAYLOAD_RSHIFT; + else + payload >>= _OBJC_TAG_EXT_PAYLOAD_RSHIFT; + + expectedPayload = payload; + id obj = (__bridge id)_objc_makeTaggedPointer(tag, payload); + [obj test]; + testassert(sawPayload == payload); + testassert(sawTag == i); + } + } + + succeed(__FILE__); +} + +#else + +int main() +{ + succeed(__FILE__); +} + +#endif diff --git a/test/taggedPointersDisabled.m b/test/taggedPointersDisabled.m index 5e1ce59c..958cc7dd 100644 --- a/test/taggedPointersDisabled.m +++ b/test/taggedPointersDisabled.m @@ -1,9 +1,14 @@ -// TEST_ENV OBJC_DISABLE_TAGGED_POINTERS=YES -// TEST_CRASHES -/* +/* +TEST_ENV OBJC_DISABLE_TAGGED_POINTERS=YES +TEST_CRASHES + +TEST_BUILD_OUTPUT +.*taggedPointersDisabled.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\] +END + TEST_RUN_OUTPUT objc\[\d+\]: tagged pointers are disabled -CRASHED: SIG(ILL|TRAP) +objc\[\d+\]: HALTED OR OK: taggedPointersDisabled.m END @@ -16,6 +21,9 @@ int main() { + // provoke the same nullability warning as the real test + objc_getClass(nil); + succeed(__FILE__); } diff --git a/test/taggedPointersTagObfuscationDisabled.m b/test/taggedPointersTagObfuscationDisabled.m new file mode 100644 index 00000000..a3aad8b5 --- /dev/null +++ b/test/taggedPointersTagObfuscationDisabled.m @@ -0,0 +1,21 @@ +// TEST_ENV OBJC_DISABLE_TAG_OBFUSCATION=YES + +#include "test.h" +#include + +#if !OBJC_HAVE_TAGGED_POINTERS + +int main() +{ + succeed(__FILE__); +} + +#else + +int main() +{ + testassert(_objc_getTaggedPointerTag((void *)1) == 0); + succeed(__FILE__); +} + +#endif diff --git a/test/test.h b/test/test.h index 232b04b2..f4332a1c 100644 --- a/test/test.h +++ b/test/test.h @@ -13,6 +13,13 @@ #include #include #include +#if __cplusplus +#include +using namespace std; +#else +#include +#endif +#include #include #include #include @@ -26,21 +33,16 @@ #include #include -#if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR -static OBJC_INLINE malloc_zone_t *objc_collectableZone(void) { return nil; } +#if __has_include() +# include #endif +#include "../runtime/isa.h" -// Configuration macros - -#if !__LP64__ || TARGET_OS_WIN32 || __OBJC_GC__ || TARGET_IPHONE_SIMULATOR -# define SUPPORT_NONPOINTER_ISA 0 -#elif __x86_64__ -# define SUPPORT_NONPOINTER_ISA 1 -#elif __arm64__ -# define SUPPORT_NONPOINTER_ISA 1 +#if __cplusplus +# define EXTERN_C extern "C" #else -# error unknown architecture +# define EXTERN_C /*empty*/ #endif @@ -97,13 +99,22 @@ static inline void fail(const char *msg, ...) } -static inline void testprintf(const char *msg, ...) +// Return true if testprintf() output is enabled. +static inline bool testverbose(void) { static int verbose = -1; if (verbose < 0) verbose = atoi(getenv("VERBOSE") ?: "0"); // VERBOSE=1 prints test harness info only - if (msg && verbose >= 2) { + // VERBOSE=2 prints test info + return verbose >= 2; +} + +// Print debugging info when VERBOSE=2 is set, +// without disturbing the test's expected output. +static inline void testprintf(const char *msg, ...) +{ + if (msg && testverbose()) { char *msg2; asprintf(&msg2, "VERBOSE: %s", msg); va_list v; @@ -132,52 +143,35 @@ static inline void testwarn(const char *msg, ...) 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 -# 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) +// Prevent deprecation warnings from some runtime functions. -#else +static inline void test_objc_flush_caches(Class cls) +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + _objc_flush_caches(cls); +#pragma clang diagnostic pop +} +#define _objc_flush_caches(c) test_objc_flush_caches(c) -# define testcollect() \ - do { \ - _objc_flush_caches(NULL); \ - } while (0) -#endif +static inline Class test_class_setSuperclass(Class cls, Class supercls) +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return class_setSuperclass(cls, supercls); +#pragma clang diagnostic pop +} +#define class_setSuperclass(c, s) test_class_setSuperclass(c, s) + + +static inline void testcollect() +{ + _objc_flush_caches(nil); +} // Synchronously run test code on another thread. -// This can help force GC to kill objects promptly, which some tests depend on. // The block object is unsafe_unretained because we must not allow // ARC to retain them in non-Foundation tests @@ -185,22 +179,11 @@ typedef void(^testblock_t)(void); static __unsafe_unretained testblock_t testcodehack; static inline void *_testthread(void *arg __unused) { - objc_registerThreadWithCollector(); testcodehack(); return NULL; } static inline void testonthread(__unsafe_unretained testblock_t code) { - // GC crashes without Foundation because the block object classes - // are insufficiently initialized. - if (objc_collectingEnabled()) { - static bool foundationified = false; - if (!foundationified) { - dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", RTLD_LAZY); - foundationified = true; - } - } - pthread_t th; testcodehack = code; // force GC not-thread-local, avoid ARC void* casts pthread_create(&th, NULL, _testthread, NULL); @@ -275,8 +258,11 @@ static inline void leak_dump_heap(const char *msg) 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); - + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" system(cmd); +#pragma clang diagnostic pop dup2(outfd, STDOUT_FILENO); close(outfd); @@ -303,19 +289,33 @@ static inline void leak_mark(void) } \ inuse = leak_inuse(); \ if (inuse > _leak_start + n) { \ + fprintf(stderr, "BAD: %zu bytes leaked at %s:%u " \ + "(try LEAK_HEAP and HANG_ON_LEAK to debug)\n", \ + inuse - _leak_start, __FILE__, __LINE__); \ if (getenv("HANG_ON_LEAK")) { \ - printf("leaks %d\n", getpid()); \ + fprintf(stderr, "Hanging after leaks detected. " \ + "Leaks command:\n"); \ + fprintf(stderr, "leaks %d\n", getpid()); \ while (1) sleep(1); \ } \ - fprintf(stderr, "BAD: %zu bytes leaked at %s:%u\n", \ - inuse - _leak_start, __FILE__, __LINE__); \ } \ } while (0) +// true when running under Guard Malloc static inline bool is_guardmalloc(void) { const char *env = getenv("GUARDMALLOC"); - return (env && 0 == strcmp(env, "YES")); + return (env && 0 == strcmp(env, "1")); +} + +// true when running a debug build of libobjc +static inline bool is_debug(void) +{ + static int debugness = -1; + if (debugness == -1) { + debugness = dlsym(RTLD_DEFAULT, "_objc_isDebugBuild") ? 1 : 0; + } + return (bool)debugness; } @@ -324,31 +324,27 @@ static inline bool is_guardmalloc(void) static id self_fn(id x) __attribute__((used)); static id self_fn(id x) { return x; } +#if __has_feature(objc_arc_weak) + // __weak +# define WEAK_STORE(dst, val) (dst = (val)) +# define WEAK_LOAD(src) (src) +#else + // no __weak +# define WEAK_STORE(dst, val) objc_storeWeak((id *)&dst, val) +# define WEAK_LOAD(src) objc_loadWeak((id *)&src) +#endif + #if __has_feature(objc_arc) // ARC # define RELEASE_VAR(x) x = nil -# define WEAK_STORE(dst, val) (dst = (val)) -# define WEAK_LOAD(src) (src) # define SUPER_DEALLOC() # define RETAIN(x) (self_fn(x)) # define RELEASE_VALUE(x) ((void)self_fn(x)) # define AUTORELEASE(x) (self_fn(x)) -#elif defined(__OBJC_GC__) - // GC -# define RELEASE_VAR(x) x = nil -# define WEAK_STORE(dst, val) (dst = (val)) -# define WEAK_LOAD(src) (src) -# define SUPER_DEALLOC() [super dealloc] -# define RETAIN(x) [x self] -# define RELEASE_VALUE(x) (void)[x self] -# define AUTORELEASE(x) [x self] - #else // MRC # define RELEASE_VAR(x) do { [x release]; x = nil; } while (0) -# define WEAK_STORE(dst, val) objc_storeWeak((id *)&dst, val) -# define WEAK_LOAD(src) objc_loadWeak((id *)&src) # define SUPER_DEALLOC() [super dealloc] # define RETAIN(x) [x retain] # define RELEASE_VALUE(x) [x release] @@ -389,7 +385,6 @@ OBJC_ROOT_CLASS -(id) mutableCopy; -(id) init; -(void) dealloc; --(void) finalize; @end @interface TestRoot (RR) -(id) retain; @@ -401,27 +396,26 @@ OBJC_ROOT_CLASS @end // incremented for each call of TestRoot's methods -extern int TestRootLoad; -extern int TestRootInitialize; -extern int TestRootAlloc; -extern int TestRootAllocWithZone; -extern int TestRootCopy; -extern int TestRootCopyWithZone; -extern int TestRootMutableCopy; -extern int TestRootMutableCopyWithZone; -extern int TestRootInit; -extern int TestRootDealloc; -extern int TestRootFinalize; -extern int TestRootRetain; -extern int TestRootRelease; -extern int TestRootAutorelease; -extern int TestRootRetainCount; -extern int TestRootTryRetain; -extern int TestRootIsDeallocating; -extern int TestRootPlusRetain; -extern int TestRootPlusRelease; -extern int TestRootPlusAutorelease; -extern int TestRootPlusRetainCount; +extern atomic_int TestRootLoad; +extern atomic_int TestRootInitialize; +extern atomic_int TestRootAlloc; +extern atomic_int TestRootAllocWithZone; +extern atomic_int TestRootCopy; +extern atomic_int TestRootCopyWithZone; +extern atomic_int TestRootMutableCopy; +extern atomic_int TestRootMutableCopyWithZone; +extern atomic_int TestRootInit; +extern atomic_int TestRootDealloc; +extern atomic_int TestRootRetain; +extern atomic_int TestRootRelease; +extern atomic_int TestRootAutorelease; +extern atomic_int TestRootRetainCount; +extern atomic_int TestRootTryRetain; +extern atomic_int TestRootIsDeallocating; +extern atomic_int TestRootPlusRetain; +extern atomic_int TestRootPlusRelease; +extern atomic_int TestRootPlusAutorelease; +extern atomic_int TestRootPlusRetainCount; #endif @@ -458,7 +452,7 @@ static inline BOOL stret_equal(struct stret a, struct stret b) static struct stret STRET_RESULT __attribute__((used)) = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; -#if TARGET_IPHONE_SIMULATOR +#if TARGET_OS_SIMULATOR // Force cwd to executable's directory during launch. // sim used to do this but simctl does not. #include diff --git a/test/test.pl b/test/test.pl index dda6bfac..a72a6a76 100755 --- a/test/test.pl +++ b/test/test.pl @@ -6,32 +6,22 @@ use strict; use File::Basename; -chdir dirname $0; -chomp (my $DIR = `pwd`); - -my $TESTLIBNAME = "libobjc.A.dylib"; -my $TESTLIBPATH = "/usr/lib/$TESTLIBNAME"; +# We use encode_json() to write BATS plist files. +# JSON::PP does not exist on iOS devices, but we need not write plists there. +# So we simply load JSON:PP if it exists. +if (eval { require JSON::PP; 1; }) { + JSON::PP->import(); +} -my $BUILDDIR = "/tmp/test-$TESTLIBNAME-build"; -# xterm colors -my $red = "\e[41;37m"; -my $yellow = "\e[43;30m"; -my $nocolor = "\e[0m"; +chdir dirname $0; +chomp (my $DIR = `pwd`); -# 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") { + if ($arg eq "-h" || $arg eq "-H" || $arg eq "-help" || $arg eq "help") { print(< LANGUAGE=c,c++,objective-c,objective-c++,swift - MEM=mrc,arc,gc - STDLIB=libc++,libstdc++ + MEM=mrc,arc GUARDMALLOC=0|1|before|after - BUILD=0|1 - RUN=0|1 - VERBOSE=0|1|2 + BUILD=0|1 (build the tests?) + RUN=0|1 (run the tests?) + VERBOSE=0|1|2 (0=quieter 1=print commands executed 2=full test output) + BATS=0|1 (build for and/or run in BATS?) examples: - test installed library, x86_64, no gc + test installed library, x86_64 $0 - test buildit-built root, i386 and x86_64, MRC and ARC and GC, clang compiler - $0 ARCH=i386,x86_64 ROOT=/tmp/libclosure.roots MEM=mrc,arc,gc CC=clang + test buildit-built root, i386 and x86_64, MRC and ARC, clang compiler + $0 ARCH=i386,x86_64 ROOT=/tmp/objc4.roots MEM=mrc,arc CC=clang test buildit-built root with iOS simulator, deploy to iOS 7, run on iOS 8 - $0 ARCH=i386 ROOT=/tmp/libclosure.roots OS=iphonesimulator-7.0-8.0 + $0 ARCH=x86_64 ROOT=/tmp/objc4.roots OS=iphonesimulator-7.0-8.0 test buildit-built root on attached iOS device - $0 ARCH=armv7 ROOT=/tmp/libclosure.roots OS=iphoneos + $0 ARCH=arm64 ROOT=/tmp/objc4.roots OS=iphoneos END exit 0; } @@ -74,6 +64,9 @@ END ######################################################################### ## Tests +# Maps test name => test's filename extension. +# ex: "msgSend" => "m" +# `keys %ALL_TESTS` is also used as the list of all tests found on disk. my %ALL_TESTS; ######################################################################### @@ -85,9 +78,8 @@ END # ARCH=i386,x86_64,armv6,armv7 # OS=macosx,iphoneos,iphonesimulator (plus sdk/deployment/run versions) # LANGUAGE=c,c++,objective-c,objective-c++,swift -# CC=clang,gcc-4.2,llvm-gcc-4.2 -# MEM=mrc,arc,gc -# STDLIB=libc++,libstdc++ +# CC=clang +# MEM=mrc,arc # GUARDMALLOC=0,1,before,after # things you can set once on the command line @@ -95,12 +87,47 @@ END # BUILD=0|1 # RUN=0|1 # VERBOSE=0|1|2 +# BATS=0|1 +# environment variables from the command line +# DSTROOT +# OBJROOT +# (SRCROOT is ignored; test sources are assumed to +# be in the same directory as the test script itself.) +# fixme SYMROOT for dsymutil output? +# Some arguments as read from the command line. +my %args; my $BUILD; my $RUN; my $VERBOSE; +my $BATS; + +my @TESTLIBNAMES = ("libobjc.A.dylib", "libobjc-trampolines.dylib"); +my $TESTLIBDIR = "/usr/lib"; + +# Top level directory for intermediate and final build products. +# Intermediate files must be kept separate for XBS BATS builds. +my $OBJROOT = $ENV{OBJROOT} || ""; +my $DSTROOT = $ENV{DSTROOT} || ""; + +# Build product directory inside DSTROOT and OBJROOT. +# Each test config gets its own build directory inside this. +my $BUILDDIR; + +# Local top-level directory. +# This is the default value for $BUILDDIR. +my $LOCALBASE = "/tmp/test-$TESTLIBNAMES[0]-build"; + +# Device-side top-level directory. +# This replaces $DSTROOT$BUILDDIR/ for on-device execution. +my $REMOTEBASE = "/AppleInternal/objctest"; + +# BATS top-level directory. +# This replaces $DSTROOT$BUILDDIR/ for BATS execution. +my $BATSBASE = "/AppleInternal/CoreOS/tests/objc4"; + my $crashcatch = <<'END'; // interpose-able code to catch crashes, print, and exit cleanly @@ -115,16 +142,22 @@ END { 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; + case SIGILL: msg = "CRASHED: SIGILL"; break; + case SIGBUS: msg = "CRASHED: SIGBUS"; break; + case SIGSYS: msg = "CRASHED: SIGSYS"; break; + case SIGSEGV: msg = "CRASHED: SIGSEGV"; break; + case SIGTRAP: msg = "CRASHED: SIGTRAP"; break; + case SIGABRT: msg = "CRASHED: SIGABRT"; break; + default: msg = "unknown signal"; break; } write(STDERR_FILENO, msg, strlen(msg)); - _exit(0); + + // avoid backslash-n newline due to escaping differences somewhere + // in BATS versus local execution (perhaps different perl versions?) + char newline = 0xa; + write(STDERR_FILENO, &newline, 1); + + _exit(1); } static void setupcrash(void) __attribute__((constructor)); @@ -202,11 +235,62 @@ sub make { } sub chdir_verbose { - my $dir = shift; + my $dir = shift || die; print "cd $dir\n" if $VERBOSE; - chdir $dir || die; + chdir $dir || die "couldn't cd $dir"; +} + +sub rm_rf_verbose { + my $dir = shift || die; + print "mkdir -p $dir\n" if $VERBOSE; + `rm -rf '$dir'`; + die "couldn't rm -rf $dir" if $?; +} + +sub mkdir_verbose { + my $dir = shift || die; + print "mkdir -p $dir\n" if $VERBOSE; + `mkdir -p '$dir'`; + die "couldn't mkdir $dir" if $?; +} + + +# xterm colors +my $red = "\e[41;37m"; +my $yellow = "\e[43;30m"; +my $nocolor = "\e[0m"; +if (! -t STDIN) { + # Not isatty. Don't use colors. + $red = ""; + $yellow = ""; + $nocolor = ""; +} + +# print text with a colored prefix on each line +# fixme some callers pass an array of lines and some don't +sub colorprefix { + my $color = shift; + while (defined(my $lines = shift)) { + $lines = "\n" if ($lines eq ""); + for my $line (split(/^/, $lines)) { + chomp $line; + print "$color $nocolor$line\n"; + } + } } +# print text colored +# fixme some callers pass an array of lines and some don't +sub colorprint { + my $color = shift; + while (defined(my $lines = shift)) { + $lines = "\n" if ($lines eq ""); + for my $line (split(/^/, $lines)) { + chomp $line; + print "$color$line$nocolor\n"; + } + } +} # Return test names from the command line. # Returns all tests if no tests were named. @@ -225,7 +309,7 @@ sub gettests { open(my $in, "< $file") || die "$file"; my $contents = join "", <$in>; if (defined $ALL_TESTS{$name}) { - print "${yellow}SKIP: multiple tests named '$name'; skipping file '$file'.${nocolor}\n"; + colorprint $yellow, "SKIP: multiple tests named '$name'; skipping file '$file'."; } else { $ALL_TESTS{$name} = $ext if ($contents =~ m#^[/*\s]*TEST_#m); } @@ -318,24 +402,6 @@ sub newersdk { return $rhs; } -# Returns whether the given sdk supports -lauto -sub supportslibauto { - my ($sdk) = @_; - return 1 if $sdk =~ /^macosx/; - return 0; -} - -# 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 $nocolor$line\n"; - } - } -} - sub rewind { seek($_[0], 0, 0); } @@ -385,16 +451,16 @@ sub check_output { $bad = "(output not 'OK: $name')" if ($bad eq "" && (scalar(@output) != 1 || $output[0] !~ /^OK: $name/)); if ($bad ne "") { - print "${red}FAIL: /// test '$name' \\\\\\$nocolor\n"; - colorprint($red, @original_output); - print "${red}FAIL: \\\\\\ test '$name' ///$nocolor\n"; - print "${red}FAIL: $name: $bad$nocolor\n"; + colorprint $red, "FAIL: /// test '$name' \\\\\\"; + colorprefix $red, @original_output; + colorprint $red, "FAIL: \\\\\\ test '$name' ///"; + colorprint $red, "FAIL: $name: $bad"; $xit = 0; } elsif ($warn ne "") { - print "${yellow}PASS: /// test '$name' \\\\\\$nocolor\n"; - colorprint($yellow, @original_output); - print "${yellow}PASS: \\\\\\ test '$name' ///$nocolor\n"; + colorprint $yellow, "PASS: /// test '$name' \\\\\\"; + colorprefix $yellow, @original_output; + colorprint $yellow, "PASS: \\\\\\ test '$name' ///"; print "PASS: $name (with warnings)\n"; } else { @@ -481,21 +547,10 @@ sub filter_simulator my @new_output; for my $line (@$outputref) { - if ($line !~ /No simulator devices appear to be running/) { - push @new_output, $line; - } - } - - @$outputref = @new_output; -} - -sub filter_simulator -{ - my $outputref = shift; - - my @new_output; - for my $line (@$outputref) { - if ($line !~ /No simulator devices appear to be running/) { + if (($line !~ /No simulator devices appear to be running/) && + ($line !~ /CoreSimulator is attempting to unload a stale CoreSimulatorService job/) && + ($line !~ /Failed to locate a valid instance of CoreSimulatorService/)) + { push @new_output, $line; } } @@ -690,7 +745,7 @@ sub gather_simple { 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 $disabled)$nocolor\n"; + colorprint $yellow, "SKIP: $name (disabled by $disabled)"; return 0; } @@ -750,18 +805,57 @@ sub gather_simple { TEST_RUN_OUTPUT => $runerror, TEST_CFLAGS => $cflags, TEST_ENV => $envstring, - TEST_RUN => $run, + TEST_RUN => $run, + DSTDIR => "$C{DSTDIR}/$name.build", + OBJDIR => "$C{OBJDIR}/$name.build", }; return 1; } + +# Test description plist to write when building for BATS execution. +my %bats_plist; +$bats_plist{'Project'} = "objc4"; +$bats_plist{'Tests'} = []; # populated by append_bats_test() + +# Saves run instructions for a single test in all configurations as a BATS test. +sub append_bats_test { + my $name = shift; + + my $arch = join(',', @{$args{ARCH}}); + my $os = join(',', @{$args{OSVERSION}}); + my $mem = join(',', @{$args{MEM}}); + my $language = join(',', @{$args{LANGUAGE}}); + + push @{$bats_plist{'Tests'}}, { + "TestName" => "$name", + "Command" => [ + "/usr/bin/perl", + "$BATSBASE/test/test.pl", + $name, + "ARCH=$arch", + "OS=$os", + "MEM=$mem", + "LANGUAGE=$language", + "BUILD=0", + "RUN=1", + "VERBOSE=1", + "BATS=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"; + + mkdir_verbose $T{DSTDIR}; + chdir_verbose $T{DSTDIR}; + # we don't mkdir $T{OBJDIR} because most tests don't use it my $ext = $ALL_TESTS{$name}; my $file = "$DIR/$name.$ext"; @@ -772,12 +866,19 @@ sub build_simple { die "$?" if $?; } - my $cmd = $T{TEST_BUILD} ? eval "return \"$T{TEST_BUILD}\"" : "$C{COMPILE} $T{TEST_CFLAGS} $file -o $name.out"; + my $cmd = $T{TEST_BUILD} ? eval "return \"$T{TEST_BUILD}\"" : "$C{COMPILE} $T{TEST_CFLAGS} $file -o $name.exe"; my $output = make($cmd); + # ignore out-of-date text-based stubs (caused by ditto into SDK) + $output =~ s/ld: warning: text-based stub file.*\n//g; # rdar://10163155 $output =~ s/ld: warning: could not create compact unwind for [^\n]+: does not use standard frame\n//g; + # rdar://37937122 + $output =~ s/^warning: Cannot lower [^\n]+\n//g; + $output =~ s/^warning: key: [^\n]+\n//g; + $output =~ s/^warning: discriminator: [^\n]+\n//g; + $output =~ s/^warning: callee: [^\n]+\n//g; my $ok; if (my $builderror = $T{TEST_BUILD_OUTPUT}) { @@ -785,39 +886,50 @@ sub build_simple { if ($output =~ /$builderror/) { $ok = 1; } else { - print "${red}FAIL: /// test '$name' \\\\\\$nocolor\n"; - colorprint $red, $output; - print "${red}FAIL: \\\\\\ test '$name' ///$nocolor\n"; - print "${red}FAIL: $name (build output does not match TEST_BUILD_OUTPUT)$nocolor\n"; + colorprint $red, "FAIL: /// test '$name' \\\\\\"; + colorprefix $red, $output; + colorprint $red, "FAIL: \\\\\\ test '$name' ///"; + colorprint $red, "FAIL: $name (build output does not match TEST_BUILD_OUTPUT)"; $ok = 0; } } elsif ($?) { - print "${red}FAIL: /// test '$name' \\\\\\$nocolor\n"; - colorprint $red, $output; - print "${red}FAIL: \\\\\\ test '$name' ///$nocolor\n"; - print "${red}FAIL: $name (build failed)$nocolor\n"; + colorprint $red, "FAIL: /// test '$name' \\\\\\"; + colorprefix $red, $output; + colorprint $red, "FAIL: \\\\\\ test '$name' ///"; + colorprint $red, "FAIL: $name (build failed)"; $ok = 0; } elsif ($output ne "") { - print "${red}FAIL: /// test '$name' \\\\\\$nocolor\n"; - colorprint $red, $output; - print "${red}FAIL: \\\\\\ test '$name' ///$nocolor\n"; - print "${red}FAIL: $name (unexpected build output)$nocolor\n"; + colorprint $red, "FAIL: /// test '$name' \\\\\\"; + colorprefix $red, $output; + colorprint $red, "FAIL: \\\\\\ test '$name' ///"; + colorprint $red, "FAIL: $name (unexpected build output)"; $ok = 0; } else { $ok = 1; } - if ($ok) { - foreach my $file (glob("*.out *.dylib *.bundle")) { - make("dsymutil $file"); + foreach my $file (glob("*.exe *.dylib *.bundle")) { + if (!$BATS) { + # not for BATS to save space and build time + # fixme use SYMROOT? + make("xcrun dsymutil $file"); + } + if ($C{OS} eq "macosx" || $C{OS} =~ /simulator/) { + # setting any entitlements disables dyld environment variables + } else { + # get-task-allow entitlement is required + # to enable dyld environment variables + make("xcrun codesign -s - --entitlements $DIR/get_task_allow_entitlement.plist $file"); + die "$?" if $?; + } } } return $ok; } -# Run a simple test (testname.out, with error checking of stdout and stderr) +# Run a simple test (testname.exe, with error checking of stdout and stderr) sub run_simple { my %C = %{shift()}; my $name = shift; @@ -828,39 +940,51 @@ sub run_simple { return 1; } - my $testdir = "$C{DIR}/$name.build"; + my $testdir = $T{DSTDIR}; chdir_verbose $testdir; my $env = "$C{ENV} $T{TEST_ENV}"; - my $output; + if ($T{TEST_CRASHES}) { + $env .= " OBJC_DEBUG_DONT_CRASH=YES"; + } - if ($C{ARCH} =~ /^arm/ && `unamep -p` !~ /^arm/) { - # run on iOS or watchos device + my $output; - my $remotedir = "/var/root/objctest/" . basename($C{DIR}) . "/$name.build"; + if ($C{ARCH} =~ /^arm/ && `uname -p` !~ /^arm/) { + # run on iOS or watchos or tvos device + # fixme device selection and verification + my $remotedir = "$REMOTEBASE/" . basename($C{DSTDIR}) . "/$name.build"; # Add test dir and libobjc's dir to DYLD_LIBRARY_PATH. # Insert libcrashcatch.dylib if necessary. $env .= " DYLD_LIBRARY_PATH=$remotedir"; - $env .= ":/var/root/objctest/" if ($C{TESTLIB} ne $TESTLIBPATH); + $env .= ":$REMOTEBASE" if ($C{TESTLIBDIR} ne $TESTLIBDIR); if ($T{TEST_CRASHES}) { $env .= " DYLD_INSERT_LIBRARIES=$remotedir/libcrashcatch.dylib"; } - my $cmd = "ssh iphone 'cd $remotedir && env $env ./$name.out'"; + my $cmd = "ssh iphone 'cd $remotedir && env $env ./$name.exe'"; $output = make("$cmd"); } elsif ($C{OS} =~ /simulator/) { - # run locally in an iOS simulator - # fixme appletvsimulator and watchsimulator - # fixme SDK - my $sim = "xcrun -sdk iphonesimulator simctl spawn 'iPhone 6'"; - + # run locally in a simulator + # fixme selection of simulated OS version + my $simdevice; + if ($C{OS} =~ /iphonesimulator/) { + $simdevice = 'iPhone 6'; + } elsif ($C{OS} =~ /watchsimulator/) { + $simdevice = 'Apple Watch Series 4 - 40mm'; + } elsif ($C{OS} =~ /tvsimulator/) { + $simdevice = 'Apple TV 1080p'; + } else { + die "unknown simulator $C{OS}\n"; + } + my $sim = "xcrun -sdk iphonesimulator simctl spawn '$simdevice'"; # Add test dir and libobjc's dir to DYLD_LIBRARY_PATH. # Insert libcrashcatch.dylib if necessary. $env .= " DYLD_LIBRARY_PATH=$testdir"; - $env .= ":" . dirname($C{TESTLIB}) if ($C{TESTLIB} ne $TESTLIBPATH); + $env .= ":" . $C{TESTLIBDIR} if ($C{TESTLIBDIR} ne $TESTLIBDIR); if ($T{TEST_CRASHES}) { $env .= " DYLD_INSERT_LIBRARIES=$testdir/libcrashcatch.dylib"; } @@ -870,7 +994,7 @@ sub run_simple { $simenv .= "SIMCTL_CHILD_$keyvalue "; } # Use the full path here so hack_cwd in test.h works. - $output = make("env $simenv $sim $testdir/$name.out"); + $output = make("env $simenv $sim $testdir/$name.exe"); } else { # run locally @@ -878,12 +1002,12 @@ sub run_simple { # Add test dir and libobjc's dir to DYLD_LIBRARY_PATH. # Insert libcrashcatch.dylib if necessary. $env .= " DYLD_LIBRARY_PATH=$testdir"; - $env .= ":" . dirname($C{TESTLIB}) if ($C{TESTLIB} ne $TESTLIBPATH); + $env .= ":" . $C{TESTLIBDIR} if ($C{TESTLIBDIR} ne $TESTLIBDIR); if ($T{TEST_CRASHES}) { $env .= " DYLD_INSERT_LIBRARIES=$testdir/libcrashcatch.dylib"; } - $output = make("sh -c '$env ./$name.out'"); + $output = make("sh -c '$env ./$name.exe'"); } return check_output(\%C, $name, split("\n", $output)); @@ -906,6 +1030,19 @@ sub find_compiler { return $result; } +sub dirContainsAllTestLibs { + my $dir = shift; + + foreach my $testlib (@TESTLIBNAMES) { + my $found = (-e "$dir/$testlib"); + my $foundstr = ($found ? "found" : "didn't find"); + print "note: $foundstr $testlib in $dir\n" if ($VERBOSE); + return 0 if (!$found); + } + + return 1; +} + sub make_one_config { my $configref = shift; my $root = shift; @@ -922,66 +1059,93 @@ sub make_one_config { $deployment_arg = "default" if !defined($deployment_arg); $run_arg = "default" if !defined($run_arg); - - die "unknown OS '$os_arg' (expected iphoneos or iphonesimulator or watchos or watchsimulator or macosx)\n" if ($os_arg ne "iphoneos" && $os_arg ne "iphonesimulator" && $os_arg ne "watchos" && $os_arg ne "watchsimulator" && $os_arg ne "macosx"); + my %allowed_os_args = ( + "macosx" => "macosx", "osx" => "macosx", "macos" => "macosx", + "iphoneos" => "iphoneos", "ios" => "iphoneos", + "iphonesimulator" => "iphonesimulator", "iossimulator" => "iphonesimulator", + "watchos" => "watchos", + "watchsimulator" => "watchsimulator", "watchossimulator" => "watchsimulator", + "appletvos" => "appletvos", "tvos" => "appletvos", + "appletvsimulator" => "appletvsimulator", "tvsimulator" => "appletvsimulator", + "bridgeos" => "bridgeos", + ); - $C{OS} = $os_arg; + $C{OS} = $allowed_os_args{$os_arg} || die "unknown OS '$os_arg' (expected " . join(', ', sort keys %allowed_os_args) . ")\n"; - if ($os_arg eq "iphoneos" || $os_arg eq "iphonesimulator") { + # set the config name now, after massaging the language and OS versions, + # but before adding other settings + my $configname = config_name(%C); + die if ($configname =~ /'/); + die if ($configname =~ / /); + ($C{NAME} = $configname) =~ s/~/ /g; + (my $configdir = $configname) =~ s#/##g; + $C{DSTDIR} = "$DSTROOT$BUILDDIR/$configdir"; + $C{OBJDIR} = "$OBJROOT$BUILDDIR/$configdir"; + + # Allow tests to see BATS-edness in TEST_CONFIG. + $C{BATS} = $BATS; + + if ($C{OS} eq "iphoneos" || $C{OS} eq "iphonesimulator") { $C{TOOLCHAIN} = "ios"; - } elsif ($os_arg eq "watchos" || $os_arg eq "watchsimulator") { + } elsif ($C{OS} eq "watchos" || $C{OS} eq "watchsimulator") { $C{TOOLCHAIN} = "watchos"; - } elsif ($os_arg eq "macosx") { + } elsif ($C{OS} eq "appletvos" || $C{OS} eq "appletvsimulator") { + $C{TOOLCHAIN} = "appletvos"; + } elsif ($C{OS} eq "bridgeos") { + $C{TOOLCHAIN} = "bridgeos"; + } elsif ($C{OS} eq "macosx") { $C{TOOLCHAIN} = "osx"; } else { - print "${yellow}WARN: don't know toolchain for OS $C{OS}${nocolor}\n"; + colorprint $yellow, "WARN: don't know toolchain for OS $C{OS}"; $C{TOOLCHAIN} = "default"; } - - # Look up SDK - # Try exact match first. - # Then try lexically-last prefix match (so "macosx" => "macosx10.7internal") - my @sdks = getsdks(); - if ($VERBOSE) { - print "note: Installed SDKs: @sdks\n"; - } - my $exactsdk = undef; - my $prefixsdk = undef; - foreach my $sdk (@sdks) { - $exactsdk = $sdk if ($sdk eq $sdk_arg); - $prefixsdk = newersdk($sdk, $prefixsdk) if ($sdk =~ /^$sdk_arg/); - } - my $sdk; - if ($exactsdk) { - $sdk = $exactsdk; - } elsif ($prefixsdk) { - $sdk = $prefixsdk; + if ($BUILD) { + # Look up SDK. + # Try exact match first. + # Then try lexically-last prefix match + # (so "macosx" => "macosx10.7internal") + + $sdk_arg =~ s/$os_arg/$C{OS}/; + + my @sdks = getsdks(); + if ($VERBOSE) { + print "note: Installed SDKs: @sdks\n"; + } + my $exactsdk = undef; + my $prefixsdk = undef; + foreach my $sdk (@sdks) { + $exactsdk = $sdk if ($sdk eq $sdk_arg); + $prefixsdk = newersdk($sdk, $prefixsdk) if ($sdk =~ /^$sdk_arg/); + } + + my $sdk; + if ($exactsdk) { + $sdk = $exactsdk; + } elsif ($prefixsdk) { + $sdk = $prefixsdk; + } else { + die "unknown SDK '$sdk_arg'\nInstalled SDKs: @sdks\n"; + } + + # Set deployment target. + # fixme can't enforce version when run_arg eq "default" + # because we don't know it yet + $deployment_arg = versionsuffix($sdk) if $deployment_arg eq "default"; + if ($run_arg ne "default") { + die "Deployment target '$deployment_arg' is newer than run target '$run_arg'\n" if $deployment_arg > $run_arg; + } + $C{DEPLOYMENT_TARGET} = $deployment_arg; + $C{SDK_PATH} = getsdkpath($sdk); } else { - die "unknown SDK '$sdk_arg'\nInstalled SDKs: @sdks\n"; + # not $BUILD + $C{DEPLOYMENT_TARGET} = "unknown_deployment_target"; + $C{SDK_PATH} = "/unknown/sdk"; } - # Set deployment target and run target. - # fixme can't enforce version when run_arg eq "default" - # because we don't know it yet - $deployment_arg = versionsuffix($sdk) if $deployment_arg eq "default"; - if ($run_arg ne "default") { - die "Deployment target '$deployment_arg' is newer than run target '$run_arg'\n" if $deployment_arg > $run_arg; - } - $C{DEPLOYMENT_TARGET} = $deployment_arg; + # Set run target. $C{RUN_TARGET} = $run_arg; - # set the config name now, after massaging the language and OS versions, - # but before adding other settings - my $configname = config_name(%C); - die if ($configname =~ /'/); - die if ($configname =~ / /); - ($C{NAME} = $configname) =~ s/~/ /g; - (my $configdir = $configname) =~ s#/##g; - $C{DIR} = "$BUILDDIR/$configdir"; - - $C{SDK_PATH} = getsdkpath($sdk); - # Look up test library (possible in root or SDK_PATH) my $rootarg = $root; @@ -1000,26 +1164,45 @@ sub make_one_config { } } - 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 '$rootarg' for sdk '$C{SDK_PATH}'\n" - # . join("\n", @dstpaths) . "\n" - ; + if ($root ne "") { + # Root specified. Require that it contain our dylibs. + if (dirContainsAllTestLibs("$root$C{SDK_PATH}$TESTLIBDIR")) { + $C{TESTLIBDIR} = "$root$C{SDK_PATH}$TESTLIBDIR"; + } elsif (dirContainsAllTestLibs("$root$TESTLIBDIR")) { + $C{TESTLIBDIR} = "$root$TESTLIBDIR"; + } elsif (dirContainsAllTestLibs($root)) { + $C{TESTLIBDIR} = "$root"; + } else { + die "Didn't find some libs in root '$rootarg' for sdk '$C{SDK_PATH}'\n"; + } } + else { + # No root specified. Use the SDK or / for our dylibs. + if (dirContainsAllTestLibs("$C{SDK_PATH}$TESTLIBDIR")) { + $C{TESTLIBDIR} = "$C{SDK_PATH}$TESTLIBDIR"; + } else { + # We don't actually check in / because on devices + # there are no dylib files there. + $C{TESTLIBDIR} = $TESTLIBDIR; + } + } + + @{$C{TESTLIBS}} = map { "$C{TESTLIBDIR}/$_" } @TESTLIBNAMES; + # convenience for tests that want libobjc.dylib's path + $C{TESTLIB} = @{$C{TESTLIBS}}[0]; - if (-e "$symroot/$TESTLIBNAME.dSYM") { - $C{TESTDSYM} = "$symroot/$TESTLIBNAME.dSYM"; + foreach my $testlibname (@TESTLIBNAMES) { + if (-e "$symroot/$testlibname.dSYM") { + push(@{$C{TESTDSYMS}}, "$symroot/$testlibname.dSYM"); + } } if ($VERBOSE) { - my @uuids = `/usr/bin/dwarfdump -u '$C{TESTLIB}'`; - while (my $uuid = shift @uuids) { - print "note: $uuid"; + foreach my $testlib (@{$C{TESTLIBS}}) { + my @uuids = `/usr/bin/dwarfdump -u '$testlib'`; + while (my $uuid = shift @uuids) { + print "note: $uuid"; + } } } @@ -1036,15 +1219,28 @@ sub make_one_config { $C{CXX} = find_compiler($cxx, $C{TOOLCHAIN}, $C{SDK_PATH}); $C{SWIFT} = find_compiler($swift, $C{TOOLCHAIN}, $C{SDK_PATH}); - die "No compiler '$cc' ('$C{CC}') in toolchain '$C{TOOLCHAIN}'\n" if !-e $C{CC}; - die "No compiler '$cxx' ('$C{CXX}') in toolchain '$C{TOOLCHAIN}'\n" if !-e $C{CXX}; - die "No compiler '$swift' ('$C{SWIFT}') in toolchain '$C{TOOLCHAIN}'\n" if !-e $C{SWIFT}; + die "No C compiler '$cc' ('$C{CC}') in toolchain '$C{TOOLCHAIN}'\n" if !-e $C{CC}; + die "No C++ compiler '$cxx' ('$C{CXX}') in toolchain '$C{TOOLCHAIN}'\n" if !-e $C{CXX}; + die "No Swift compiler '$swift' ('$C{SWIFT}') in toolchain '$C{TOOLCHAIN}'\n" if !-e $C{SWIFT}; } - + + if ($C{ARCH} eq "i386" && $C{OS} eq "macosx") { + # libarclite no longer available on i386 + # fixme need an archived copy for bincompat testing + $C{FORCE_LOAD_ARCLITE} = ""; + } else { + $C{FORCE_LOAD_ARCLITE} = "-Xlinker -force_load -Xlinker " . dirname($C{CC}) . "/../lib/arc/libarclite_$C{OS}.a"; + } + # 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 $cflags = "-I$DIR -W -Wall -Wno-objc-weak-compat -Wno-arc-bridge-casts-disallowed-in-nonarc -Wshorten-64-to-32 -Qunused-arguments -fno-caret-diagnostics -Os -arch $C{ARCH} "; + if (!$BATS) { + # save-temps so dsymutil works so debug info works. + # Disabled in BATS to save disk space. + # rdar://45656803 -save-temps causes bad -Wstdlibcxx-not-found warnings + $cflags .= "-g -save-temps -Wno-stdlibcxx-not-found"; + } my $objcflags = ""; my $swiftflags = "-g "; @@ -1068,46 +1264,49 @@ sub make_one_config { $target = "$C{ARCH}-apple-watchos$C{DEPLOYMENT_TARGET}"; } elsif ($C{OS} eq "watchsimulator") { - $cflags .= " -mwatch-simulator-version-min=$C{DEPLOYMENT_TARGET}"; + $cflags .= " -mwatchos-simulator-version-min=$C{DEPLOYMENT_TARGET}"; $target = "$C{ARCH}-apple-watchos$C{DEPLOYMENT_TARGET}"; } + elsif ($C{OS} eq "appletvos") { + $cflags .= " -mtvos-version-min=$C{DEPLOYMENT_TARGET}"; + $target = "$C{ARCH}-apple-tvos$C{DEPLOYMENT_TARGET}"; + } + elsif ($C{OS} eq "appletvsimulator") { + $cflags .= " -mtvos-simulator-version-min=$C{DEPLOYMENT_TARGET}"; + $target = "$C{ARCH}-apple-tvos$C{DEPLOYMENT_TARGET}"; + } + elsif ($C{OS} eq "bridgeos") { + $cflags .= " -mbridgeos-version-min=$C{DEPLOYMENT_TARGET}"; + $target = "$C{ARCH}-apple-bridgeos$C{DEPLOYMENT_TARGET}"; + } else { $cflags .= " -mmacosx-version-min=$C{DEPLOYMENT_TARGET}"; $target = "$C{ARCH}-apple-macosx$C{DEPLOYMENT_TARGET}"; } $swiftflags .= " -target $target"; - # fixme still necessary? - if ($C{OS} eq "iphonesimulator" && $C{ARCH} eq "i386") { - $objcflags .= " -fobjc-abi-version=2 -fobjc-legacy-dispatch"; - } - + $C{TESTINCLUDEDIR} = "$C{SDK_PATH}/usr/include"; + $C{TESTLOCALINCLUDEDIR} = "$C{SDK_PATH}/usr/local/include"; if ($root ne "") { - my $library_path = dirname($C{TESTLIB}); - $cflags .= " -L$library_path"; - $cflags .= " -I '$root/usr/include'"; - $cflags .= " -I '$root/usr/local/include'"; - if ($C{SDK_PATH} ne "/") { - $cflags .= " -I '$root$C{SDK_PATH}/usr/include'"; - $cflags .= " -I '$root$C{SDK_PATH}/usr/local/include'"; + $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"; - $cflags .= " -stdlib=$C{STDLIB}"; # fixme -fno-objc-link-runtime" - $cflags .= " -Wl,-segalign,0x4000 "; + my $library_path = $C{TESTLIBDIR}; + $cflags .= " -L$library_path"; + # fixme Root vs SDKContentRoot + $C{TESTINCLUDEDIR} = "$root/../SDKContentRoot/usr/include"; + $C{TESTLOCALINCLUDEDIR} = "$root/../SDKContentRoot/usr/local/include"; + $cflags .= " -isystem '$C{TESTINCLUDEDIR}'"; + $cflags .= " -isystem '$C{TESTLOCALINCLUDEDIR}'"; } # Populate objcflags $objcflags .= " -lobjc"; - if ($C{MEM} eq "gc") { - $objcflags .= " -fobjc-gc"; - } - elsif ($C{MEM} eq "arc") { + if ($C{MEM} eq "arc") { $objcflags .= " -fobjc-arc"; } elsif ($C{MEM} eq "mrc") { @@ -1116,20 +1315,15 @@ sub make_one_config { else { die "unrecognized MEM '$C{MEM}'\n"; } - - if (supportslibauto($C{OS})) { - # do this even for non-GC tests - $objcflags .= " -lauto"; - } # Populate ENV_PREFIX $C{ENV} = "LANG=C MallocScribble=1"; $C{ENV} .= " VERBOSE=$VERBOSE" if $VERBOSE; if ($root ne "") { - die "no spaces allowed in root" if dirname($C{TESTLIB}) =~ /\s+/; + die "no spaces allowed in root" if $C{TESTLIBDIR} =~ /\s+/; } if ($C{GUARDMALLOC}) { - $ENV{GUARDMALLOC} = "1"; # checked by tests and errcheck.pl + $C{ENV} .= " GUARDMALLOC=1"; # checked by tests and errcheck.pl $C{ENV} .= " DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib"; if ($C{GUARDMALLOC} eq "before") { $C{ENV} .= " MALLOC_PROTECT_BEFORE=1"; @@ -1141,11 +1335,13 @@ sub make_one_config { } # Populate compiler commands - $C{COMPILE_C} = "env LANG=C '$C{CC}' $cflags -x c -std=gnu99"; - $C{COMPILE_CXX} = "env LANG=C '$C{CXX}' $cflags -x c++"; - $C{COMPILE_M} = "env LANG=C '$C{CC}' $cflags $objcflags -x objective-c -std=gnu99"; - $C{COMPILE_MM} = "env LANG=C '$C{CXX}' $cflags $objcflags -x objective-c++"; - $C{COMPILE_SWIFT} = "env LANG=C '$C{SWIFT}' $swiftflags"; + $C{XCRUN} = "env LANG=C /usr/bin/xcrun -toolchain '$C{TOOLCHAIN}'"; + + $C{COMPILE_C} = "$C{XCRUN} '$C{CC}' $cflags -x c -std=gnu99"; + $C{COMPILE_CXX} = "$C{XCRUN} '$C{CXX}' $cflags -x c++"; + $C{COMPILE_M} = "$C{XCRUN} '$C{CC}' $cflags $objcflags -x objective-c -std=gnu99"; + $C{COMPILE_MM} = "$C{XCRUN} '$C{CXX}' $cflags $objcflags -x objective-c++"; + $C{COMPILE_SWIFT} = "$C{XCRUN} '$C{SWIFT}' $swiftflags"; $C{COMPILE} = $C{COMPILE_C} if $C{LANGUAGE} eq "c"; $C{COMPILE} = $C{COMPILE_CXX} if $C{LANGUAGE} eq "c++"; @@ -1154,40 +1350,25 @@ sub make_one_config { $C{COMPILE} = $C{COMPILE_SWIFT} if $C{LANGUAGE} eq "swift"; die "unknown language '$C{LANGUAGE}'\n" if !defined $C{COMPILE}; - ($C{COMPILE_NOMEM} = $C{COMPILE}) =~ s/ -fobjc-(?:gc|arc)\S*//g; + ($C{COMPILE_NOMEM} = $C{COMPILE}) =~ s/ -fobjc-arc\S*//g; ($C{COMPILE_NOLINK} = $C{COMPILE}) =~ s/ '?-(?:Wl,|l)\S*//g; ($C{COMPILE_NOLINK_NOMEM} = $C{COMPILE_NOMEM}) =~ s/ '?-(?:Wl,|l)\S*//g; - # Reject some self-inconsistent configurations - if ($C{MEM} !~ /^(mrc|arc|gc)$/) { - die "unknown MEM=$C{MEM} (expected one of mrc arc gc)\n"; + # Reject some self-inconsistent and disallowed configurations + if ($C{MEM} !~ /^(mrc|arc)$/) { + die "unknown MEM=$C{MEM} (expected one of mrc arc)\n"; } - if ($C{MEM} eq "gc" && $C{OS} !~ /^macosx/) { - print "note: skipping configuration $C{NAME}\n"; - print "note: because OS=$C{OS} does not support MEM=$C{MEM}\n"; - return 0; - } - if ($C{MEM} eq "gc" && $C{ARCH} eq "x86_64h") { - print "note: skipping configuration $C{NAME}\n"; - print "note: because ARCH=$C{ARCH} does not support MEM=$C{MEM}\n"; - return 0; - } - if ($C{MEM} eq "arc" && $C{OS} =~ /^macosx/ && $C{ARCH} eq "i386") { - print "note: skipping configuration $C{NAME}\n"; - print "note: because 32-bit Mac does not support MEM=$C{MEM}\n"; - return 0; - } if ($C{MEM} eq "arc" && $C{CC} !~ /clang/) { print "note: skipping configuration $C{NAME}\n"; print "note: because CC=$C{CC} does not support MEM=$C{MEM}\n"; return 0; } - if ($C{STDLIB} ne "libstdc++" && $C{CC} !~ /clang/) { - print "note: skipping configuration $C{NAME}\n"; - print "note: because CC=$C{CC} does not support STDLIB=$C{STDLIB}\n"; + if ($C{ARCH} eq "i386" && $C{OS} eq "macosx") { + colorprint $yellow, "WARN: skipping configuration $C{NAME}\n"; + colorprint $yellow, "WARN: because 32-bit Mac is dead\n"; return 0; } @@ -1200,8 +1381,8 @@ sub make_one_config { # fixme unimplemented run targets if ($C{RUN_TARGET} ne "default" && $C{OS} !~ /simulator/) { - print "${yellow}WARN: skipping configuration $C{NAME}${nocolor}\n"; - print "${yellow}WARN: because OS=$C{OS} does not yet implement RUN_TARGET=$C{RUN_TARGET}${nocolor}\n"; + colorprint $yellow, "WARN: skipping configuration $C{NAME}"; + colorprint $yellow, "WARN: because OS=$C{OS} does not yet implement RUN_TARGET=$C{RUN_TARGET}"; } %$configref = %C; @@ -1246,13 +1427,24 @@ sub config_name { return $name; } -sub run_one_config { +sub rsync_ios { + my ($src, $timeout) = @_; + for (my $i = 0; $i < 10; $i++) { + make("$DIR/timeout.pl $timeout env RSYNC_PASSWORD=alpine rsync -av $src rsync://root\@localhost:10873/root/$REMOTEBASE/"); + return if $? == 0; + colorprint $yellow, "WARN: RETRY\n" if $VERBOSE; + } + die "Couldn't rsync tests to device. Check: device is connected; tcprelay is running; device trusts your Mac; device is unlocked; filesystem is mounted r/w\n"; +} + +sub build_and_run_one_config { my %C = %{shift()}; my @tests = @_; # Build and run my $testcount = 0; my $failcount = 0; + my $skipconfig = 0; my @gathertests; foreach my $test (@tests) { @@ -1273,15 +1465,10 @@ sub run_one_config { @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++; @@ -1300,34 +1487,67 @@ sub run_one_config { # nothing to do } else { - if ($C{ARCH} =~ /^arm/ && `unamep -p` !~ /^arm/) { + if ($C{ARCH} =~ /^arm/ && `uname -p` !~ /^arm/) { + # upload timeout - longer for slow watch devices + my $timeout = ($C{OS} =~ /watch/) ? 120 : 20; + # upload all tests to iOS device - make("RSYNC_PASSWORD=alpine rsync -av $C{DIR} rsync://root\@localhost:10873/root/var/root/objctest/"); - die "Couldn't rsync tests to device\n" if ($?); + rsync_ios($C{DSTDIR}, $timeout); # upload library to iOS device - if ($C{TESTLIB} ne $TESTLIBPATH) { - make("RSYNC_PASSWORD=alpine rsync -av $C{TESTLIB} rsync://root\@localhost:10873/root/var/root/objctest/"); - die "Couldn't rsync $C{TESTLIB} to device\n" if ($?); - make("RSYNC_PASSWORD=alpine rsync -av $C{TESTDSYM} rsync://root\@localhost:10873/root/var/root/objctest/"); + if ($C{TESTLIBDIR} ne $TESTLIBDIR) { + foreach my $thing (@{$C{TESTLIBS}}, @{$C{TESTDSYMS}}) { + rsync_ios($thing, $timeout); + } + } + } + elsif ($C{OS} =~ /simulator/) { + # run locally in a simulator + } + else { + # run locally + if ($BATS) { + # BATS execution tries to run architectures that + # aren't supported by the device. Skip those configs here. + my $machine = `machine`; + chomp $machine; + # unsupported: + # running arm64e on non-arm64e device + # running arm64 on non-arm64* device + # running armv7k on non-armv7k device + # running arm64_32 on armv7k device + # We don't need to handle all mismatches here, + # only mismatches that arise within a single OS. + $skipconfig = + (($C{ARCH} eq "arm64e" && $machine ne "arm64e") || + ($C{ARCH} eq "arm64" && $machine !~ /^arm64/) || + ($C{ARCH} eq "armv7k" && $machine ne "armv7k") || + ($C{ARCH} eq "arm64_32" && $machine eq "armv7k")); + if ($skipconfig) { + print "note: skipping configuration $C{NAME}\n"; + print "note: because test arch $C{ARCH} is not " . + "supported on device arch $machine\n"; + $testcount = 0; + } } } - foreach my $test (@builttests) { - print "\nRUN $test\n" if ($VERBOSE); - - if ($ALL_TESTS{$test}) - { - if (!run_simple(\%C, $test)) { - $failcount++; + if (!$skipconfig) { + 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"; } - } else { - die "No test named '$test'\n"; } } } - return ($testcount, $failcount); + return ($testcount, $failcount, $skipconfig); } @@ -1385,19 +1605,14 @@ sub getint { } -# main -my %args; - - -my $default_arch = (`/usr/sbin/sysctl hw.optional.x86_64` eq "hw.optional.x86_64: 1\n") ? "x86_64" : "i386"; +my $default_arch = "x86_64"; $args{ARCH} = getargs("ARCH", 0); $args{ARCH} = getargs("ARCHS", $default_arch) if !@{$args{ARCH}}[0]; $args{OSVERSION} = getargs("OS", "macosx-default-default"); $args{MEM} = getargs("MEM", "mrc"); -$args{LANGUAGE} = [ map { lc($_) } @{getargs("LANGUAGE", "objective-c,swift")} ]; -$args{STDLIB} = getargs("STDLIB", "libc++"); +$args{LANGUAGE} = [ map { lc($_) } @{getargs("LANGUAGE", "objective-c")} ]; $args{CC} = getargs("CC", "clang"); @@ -1416,12 +1631,19 @@ sub getint { $BUILD = getbool("BUILD", 1); $RUN = getbool("RUN", 1); $VERBOSE = getint("VERBOSE", 0); +$BATS = getbool("BATS", 0); +$BUILDDIR = getarg("BUILDDIR", $BATS ? $BATSBASE : $LOCALBASE); my $root = getarg("ROOT", ""); $root =~ s#/*$##; my @tests = gettests(); +if ($BUILD) { + rm_rf_verbose "$DSTROOT$BUILDDIR"; + rm_rf_verbose "$OBJROOT$BUILDDIR"; +} + print "note: -----\n"; print "note: testing root '$root'\n"; @@ -1434,15 +1656,11 @@ sub getint { print "note: configuration $configname\n"; } -if ($BUILD) { - `rm -rf '$BUILDDIR'`; - mkdir "$BUILDDIR" || die; -} - my $failed = 0; my $testconfigs = @configs; my $failconfigs = 0; +my $skipconfigs = 0; my $testcount = 0; my $failcount = 0; for my $configref (@configs) { @@ -1450,17 +1668,19 @@ sub getint { print "note: -----\n"; print "note: \nnote: $configname\nnote: \n"; - (my $t, my $f) = eval { run_one_config($configref, @tests); }; + (my $t, my $f, my $skipconfig) = + eval { build_and_run_one_config($configref, @tests); }; + $skipconfigs += $skipconfig; if ($@) { chomp $@; - print "${red}FAIL: $configname${nocolor}\n"; - print "${red}FAIL: $@${nocolor}\n"; + colorprint $red, "FAIL: $configname"; + colorprint $red, "FAIL: $@"; $failconfigs++; } else { my $color = ($f ? $red : ""); print "note:\n"; - print "${color}note: $configname$nocolor\n"; - print "${color}note: $t tests, $f failures$nocolor\n"; + colorprint $color, "note: $configname\n"; + colorprint $color, "note: $t tests, $f failures"; $testcount += $t; $failcount += $f; $failconfigs++ if ($f); @@ -1469,9 +1689,27 @@ sub getint { print "note: -----\n"; my $color = ($failconfigs ? $red : ""); -print "${color}note: $testconfigs configurations, $failconfigs with failures$nocolor\n"; -print "${color}note: $testcount tests, $failcount failures$nocolor\n"; +colorprint $color, "note: $testconfigs configurations, " . + "$failconfigs with failures, $skipconfigs skipped"; +colorprint $color, "note: $testcount tests, $failcount failures"; $failed = ($failconfigs ? 1 : 0); + +if ($BUILD && $BATS && !$failed) { + # Collect BATS execution instructions for all tests. + # Each BATS "test" is all configurations together of one of our tests. + for my $testname (@tests) { + append_bats_test($testname); + } + + # Write the BATS plist to disk. + my $json = encode_json(\%bats_plist); + my $filename = "$DSTROOT$BATSBASE/objc4.plist"; + print "note: writing BATS config to $filename\n"; + open(my $file, '>', $filename); + print $file $json; + close $file; +} + exit ($failed ? 1 : 0); diff --git a/test/testroot.i b/test/testroot.i index 118c438b..99fbec80 100644 --- a/test/testroot.i +++ b/test/testroot.i @@ -6,27 +6,26 @@ #include #include -int TestRootLoad = 0; -int TestRootInitialize = 0; -int TestRootAlloc = 0; -int TestRootAllocWithZone = 0; -int TestRootCopy = 0; -int TestRootCopyWithZone = 0; -int TestRootMutableCopy = 0; -int TestRootMutableCopyWithZone = 0; -int TestRootInit = 0; -int TestRootDealloc = 0; -int TestRootFinalize = 0; -int TestRootRetain = 0; -int TestRootRelease = 0; -int TestRootAutorelease = 0; -int TestRootRetainCount = 0; -int TestRootTryRetain = 0; -int TestRootIsDeallocating = 0; -int TestRootPlusRetain = 0; -int TestRootPlusRelease = 0; -int TestRootPlusAutorelease = 0; -int TestRootPlusRetainCount = 0; +atomic_int TestRootLoad; +atomic_int TestRootInitialize; +atomic_int TestRootAlloc; +atomic_int TestRootAllocWithZone; +atomic_int TestRootCopy; +atomic_int TestRootCopyWithZone; +atomic_int TestRootMutableCopy; +atomic_int TestRootMutableCopyWithZone; +atomic_int TestRootInit; +atomic_int TestRootDealloc; +atomic_int TestRootRetain; +atomic_int TestRootRelease; +atomic_int TestRootAutorelease; +atomic_int TestRootRetainCount; +atomic_int TestRootTryRetain; +atomic_int TestRootIsDeallocating; +atomic_int TestRootPlusRetain; +atomic_int TestRootPlusRelease; +atomic_int TestRootPlusAutorelease; +atomic_int TestRootPlusRetainCount; @implementation TestRoot @@ -35,66 +34,67 @@ int TestRootPlusRetainCount = 0; static void * retain_fn(void *self, SEL _cmd __unused) { - OSAtomicIncrement32(&TestRootRetain); + atomic_fetch_add_explicit(&TestRootRetain, 1, memory_order_relaxed); void * (*fn)(void *) = (typeof(fn))_objc_rootRetain; return fn(self); } static void release_fn(void *self, SEL _cmd __unused) { - OSAtomicIncrement32(&TestRootRelease); + atomic_fetch_add_explicit(&TestRootRelease, 1, memory_order_relaxed); void (*fn)(void *) = (typeof(fn))_objc_rootRelease; fn(self); } static void * autorelease_fn(void *self, SEL _cmd __unused) { - OSAtomicIncrement32(&TestRootAutorelease); + atomic_fetch_add_explicit(&TestRootAutorelease, 1, memory_order_relaxed); void * (*fn)(void *) = (typeof(fn))_objc_rootAutorelease; return fn(self); } static unsigned long retaincount_fn(void *self, SEL _cmd __unused) { - OSAtomicIncrement32(&TestRootRetainCount); + atomic_fetch_add_explicit(&TestRootRetainCount, 1, memory_order_relaxed); unsigned long (*fn)(void *) = (typeof(fn))_objc_rootRetainCount; return fn(self); } static void * copywithzone_fn(void *self, SEL _cmd __unused, void *zone) { - OSAtomicIncrement32(&TestRootCopyWithZone); - void * (*fn)(void *, void *) = (typeof(fn))dlsym(RTLD_DEFAULT, "object_copy"); + atomic_fetch_add_explicit(&TestRootCopyWithZone, 1, memory_order_relaxed); + void * (*fn)(void *, void *) = + (typeof(fn))dlsym(RTLD_DEFAULT, "object_copy"); return fn(self, zone); } static void * plusretain_fn(void *self __unused, SEL _cmd __unused) { - OSAtomicIncrement32(&TestRootPlusRetain); + atomic_fetch_add_explicit(&TestRootPlusRetain, 1, memory_order_relaxed); return self; } static void plusrelease_fn(void *self __unused, SEL _cmd __unused) { - OSAtomicIncrement32(&TestRootPlusRelease); + atomic_fetch_add_explicit(&TestRootPlusRelease, 1, memory_order_relaxed); } static void * plusautorelease_fn(void *self, SEL _cmd __unused) { - OSAtomicIncrement32(&TestRootPlusAutorelease); + atomic_fetch_add_explicit(&TestRootPlusAutorelease, 1, memory_order_relaxed); return self; } static unsigned long plusretaincount_fn(void *self __unused, SEL _cmd __unused) { - OSAtomicIncrement32(&TestRootPlusRetainCount); + atomic_fetch_add_explicit(&TestRootPlusRetainCount, 1, memory_order_relaxed); return ULONG_MAX; } +(void) load { - OSAtomicIncrement32(&TestRootLoad); + atomic_fetch_add_explicit(&TestRootLoad, 1, memory_order_relaxed); - // install methods that ARR refuses to compile + // install methods that ARC refuses to compile class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, ""); class_addMethod(self, sel_registerName("release"), (IMP)release_fn, ""); class_addMethod(self, sel_registerName("autorelease"), (IMP)autorelease_fn, ""); @@ -109,7 +109,7 @@ plusretaincount_fn(void *self __unused, SEL _cmd __unused) { +(void) initialize { - OSAtomicIncrement32(&TestRootInitialize); + atomic_fetch_add_explicit(&TestRootInitialize, 1, memory_order_relaxed); } -(id) self { @@ -137,15 +137,15 @@ plusretaincount_fn(void *self __unused, SEL _cmd __unused) { } +(id) alloc { - OSAtomicIncrement32(&TestRootAlloc); + atomic_fetch_add_explicit(&TestRootAlloc, 1, memory_order_relaxed); void * (*fn)(id __unsafe_unretained) = (typeof(fn))_objc_rootAlloc; - return objc_retainedObject(fn(self)); + return (__bridge_transfer id)(fn(self)); } +(id) allocWithZone:(void *)zone { - OSAtomicIncrement32(&TestRootAllocWithZone); + atomic_fetch_add_explicit(&TestRootAllocWithZone, 1, memory_order_relaxed); void * (*fn)(id __unsafe_unretained, void *) = (typeof(fn))_objc_rootAllocWithZone; - return objc_retainedObject(fn(self, zone)); + return (__bridge_transfer id)(fn(self, zone)); } +(id) copy { @@ -157,7 +157,7 @@ plusretaincount_fn(void *self __unused, SEL _cmd __unused) { } -(id) copy { - OSAtomicIncrement32(&TestRootCopy); + atomic_fetch_add_explicit(&TestRootCopy, 1, memory_order_relaxed); return [self copyWithZone:NULL]; } @@ -166,18 +166,18 @@ plusretaincount_fn(void *self __unused, SEL _cmd __unused) { } -(id) mutableCopy { - OSAtomicIncrement32(&TestRootMutableCopy); + atomic_fetch_add_explicit(&TestRootMutableCopy, 1, memory_order_relaxed); return [self mutableCopyWithZone:NULL]; } -(id) mutableCopyWithZone:(void *) __unused zone { - OSAtomicIncrement32(&TestRootMutableCopyWithZone); + atomic_fetch_add_explicit(&TestRootMutableCopyWithZone, 1, memory_order_relaxed); void * (*fn)(id __unsafe_unretained) = (typeof(fn))_objc_rootAlloc; - return objc_retainedObject(fn(object_getClass(self))); + return (__bridge_transfer id)(fn(object_getClass(self))); } -(id) init { - OSAtomicIncrement32(&TestRootInit); + atomic_fetch_add_explicit(&TestRootInit, 1, memory_order_relaxed); return _objc_rootInit(self); } @@ -186,25 +186,16 @@ plusretaincount_fn(void *self __unused, SEL _cmd __unused) { } -(void) dealloc { - OSAtomicIncrement32(&TestRootDealloc); + atomic_fetch_add_explicit(&TestRootDealloc, 1, memory_order_relaxed); _objc_rootDealloc(self); } -+(void) finalize { - fail("+finalize called"); -} - --(void) finalize { - OSAtomicIncrement32(&TestRootFinalize); - _objc_rootFinalize(self); -} - +(BOOL) _tryRetain { return YES; } -(BOOL) _tryRetain { - OSAtomicIncrement32(&TestRootTryRetain); + atomic_fetch_add_explicit(&TestRootTryRetain, 1, memory_order_relaxed); return _objc_rootTryRetain(self); } @@ -213,7 +204,7 @@ plusretaincount_fn(void *self __unused, SEL _cmd __unused) { } -(BOOL) _isDeallocating { - OSAtomicIncrement32(&TestRootIsDeallocating); + atomic_fetch_add_explicit(&TestRootIsDeallocating, 1, memory_order_relaxed); return _objc_rootIsDeallocating(self); } diff --git a/test/timeout.pl b/test/timeout.pl new file mode 100755 index 00000000..750b21e9 --- /dev/null +++ b/test/timeout.pl @@ -0,0 +1,9 @@ +#!/usr/bin/perl -w + +use strict; + +my $usage = "timeout \n"; +my $timeout = shift || die $usage; +alarm($timeout); +exec @ARGV; +die "exec failed: @ARGV"; diff --git a/test/unload.m b/test/unload.m index 5b9c76b0..33593d9e 100644 --- a/test/unload.m +++ b/test/unload.m @@ -4,8 +4,16 @@ 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 + $C{COMPILE} $DIR/unload2.m -o unload2.bundle -bundle $C{FORCE_LOAD_ARCLITE} + $C{COMPILE} $DIR/unload.m -o unload.exe -framework Foundation +END +*/ + +/* +i386 Mac doesn't have libarclite +TEST_BUILD_OUTPUT +ld: warning: ignoring file .* which is not the architecture being linked \(i386\).* +OR END */ @@ -104,7 +112,9 @@ void cycle(void) testassert(err == 0); err = dlclose(bundle); testassert(err == -1); // already closed - + + _objc_flush_caches(nil); + testassert(objc_getClass("SmallClass") == NULL); testassert(objc_getClass("BigClass") == NULL); @@ -127,9 +137,6 @@ void cycle(void) int main() { - // fixme object_dispose() not aggressive enough? - if (objc_collectingEnabled()) succeed(__FILE__); - objc_setForwardHandler((void*)&forward_handler, (void*)&forward_handler); #if defined(__arm__) || defined(__arm64__) @@ -164,8 +171,6 @@ int main() err = dlclose(dylib); testassert(err == 0); err = dlclose(dylib); - testassert(err == 0); // dlopen from libobjc itself - err = dlclose(dylib); testassert(err == -1); // already closed succeed(__FILE__); diff --git a/test/unload3.c b/test/unload3.c index 8cdb51f9..caf04733 100644 --- a/test/unload3.c +++ b/test/unload3.c @@ -1,17 +1,10 @@ // unload3: contains imageinfo but no other objc metadata // libobjc must not keep it open -// DO NOT USE __OBJC2__; this is a C file. #include -#if TARGET_OS_WIN32 || (TARGET_OS_MAC && TARGET_CPU_X86 && !TARGET_IPHONE_SIMULATOR) -// old ABI -int fake[2] __attribute__((section("__OBJC,__image_info"))) -#else -// new ABI int fake[2] __attribute__((section("__DATA,__objc_imageinfo"))) -#endif - = { 0, TARGET_IPHONE_SIMULATOR ? (1<<5) : 0 }; + = { 0, TARGET_OS_SIMULATOR ? (1<<5) : 0 }; // silence "no debug symbols in executable" warning void fn(void) { } diff --git a/test/unload4.m b/test/unload4.m index 94dd034d..9608e749 100644 --- a/test/unload4.m +++ b/test/unload4.m @@ -1,11 +1,7 @@ // unload4: contains some objc metadata other than imageinfo // libobjc must keep it open -#if __OBJC2__ 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"; diff --git a/test/unwind.m b/test/unwind.m index 7fa11592..3759147e 100644 --- a/test/unwind.m +++ b/test/unwind.m @@ -4,15 +4,6 @@ #include #include -#if !defined(__OBJC2__) - -int main() -{ - succeed(__FILE__); -} - -#else - static int state; @interface Foo : NSObject @end @@ -40,7 +31,7 @@ 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 +#if TARGET_OS_OSX objc_addExceptionHandler(&handler, 0); testassert(state == 2); #else @@ -56,14 +47,13 @@ +(BOOL) resolveClassMethod:(SEL)__unused name int main() { - int i; - // unwind exception and alt handler through objc_msgSend() PUSH_POOL { + const int count = is_guardmalloc() ? 1000 : 100000; state = 0; - for (i = 0; i < 100000; i++) { + for (int i = 0; i < count; i++) { @try { testassert(state == 0); state++; [Foo method]; @@ -89,5 +79,3 @@ int main() succeed(__FILE__); } - -#endif diff --git a/test/verify-exports.pl b/test/verify-exports.pl deleted file mode 100755 index dc544ff5..00000000 --- a/test/verify-exports.pl +++ /dev/null @@ -1,254 +0,0 @@ -#!/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 ] [/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 ] [-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_GC_UNAVAILABLE)\s*$/) if (!defined $avail); - ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(OBJC_ARC_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); diff --git a/test/weak.m b/test/weak.m index 3d1fa108..77edef66 100644 --- a/test/weak.m +++ b/test/weak.m @@ -140,12 +140,12 @@ static BOOL classInNameList(const char **names, const char *name) int main(int argc __unused, char **argv) { BOOL weakMissing; - if (strstr(argv[0], "-not-missing.out")) { + if (strstr(argv[0], "-not-missing.exe")) { weakMissing = NO; - } else if (strstr(argv[0], "-missing.out")) { + } else if (strstr(argv[0], "-missing.exe")) { weakMissing = YES; } else { - fail("executable name must be weak*-missing.out or weak*-not-missing.out"); + fail("executable name must be weak*-missing.exe or weak*-not-missing.exe"); } // class and category +load methods diff --git a/test/weakcopy.m b/test/weakcopy.m index 1ffdd8f5..778e36a1 100644 --- a/test/weakcopy.m +++ b/test/weakcopy.m @@ -1,17 +1,7 @@ -// TEST_CONFIG +// TEST_CFLAGS -fobjc-weak #include "test.h" -#if __OBJC_GC__ && __cplusplus && __i386__ - -int main() -{ - testwarn("rdar://19042235 test disabled for 32-bit objc++ GC because of unknown bit rot"); - succeed(__FILE__); -} - -#else - #include "testroot.i" #include #include @@ -28,13 +18,6 @@ @implementation Weak Weak *oldObject; Weak *newObject; -void *fn(void *arg __unused) -{ - objc_registerThreadWithCollector(); - - return NULL; -} - int main() { testonthread(^{ @@ -60,17 +43,14 @@ int main() testcollect(); TestRootDealloc = 0; - TestRootFinalize = 0; RELEASE_VAR(value); }); testcollect(); - testassert(TestRootDealloc || TestRootFinalize); + testassert(TestRootDealloc); -#if defined(__OBJC_GC__) || __has_feature(objc_arc) +#if __has_feature(objc_arc_weak) testassert(oldObject->value == nil); -#else - testassert(oldObject->value != nil); #endif testassert(newObject->value == nil); @@ -80,5 +60,3 @@ int main() succeed(__FILE__); return 0; } - -#endif diff --git a/test/weakframework-missing.m b/test/weakframework-missing.m index 43de10ba..1d924339 100644 --- a/test/weakframework-missing.m +++ b/test/weakframework-missing.m @@ -2,7 +2,7 @@ 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/weakframework-missing.m -L. -weak-lweakframework -o weakframework-missing.exe $C{COMPILE} $DIR/weak2.m -DWEAK_FRAMEWORK=1 -DWEAK_IMPORT= -DEMPTY= -dynamiclib -o libweakframework.dylib diff --git a/test/weakframework-not-missing.m b/test/weakframework-not-missing.m index 2a11104c..c348ba95 100644 --- a/test/weakframework-not-missing.m +++ b/test/weakframework-not-missing.m @@ -2,7 +2,7 @@ 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 + $C{COMPILE} $DIR/weakframework-not-missing.m -L. -weak-lweakframework -o weakframework-not-missing.exe END */ diff --git a/test/weakimport-missing.m b/test/weakimport-missing.m index bd86f431..2fcdf835 100644 --- a/test/weakimport-missing.m +++ b/test/weakimport-missing.m @@ -2,7 +2,7 @@ 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/weakimport-missing.m -L. -weak-lweakimport -o weakimport-missing.exe $C{COMPILE} $DIR/weak2.m -UWEAK_FRAMEWORK -DWEAK_IMPORT=__attribute__\\(\\(weak_import\\)\\) -DEMPTY= -dynamiclib -o libweakimport.dylib END diff --git a/test/weakimport-not-missing.m b/test/weakimport-not-missing.m index 440f79e8..6f5e18c1 100644 --- a/test/weakimport-not-missing.m +++ b/test/weakimport-not-missing.m @@ -2,7 +2,7 @@ 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 + $C{COMPILE} $DIR/weakimport-not-missing.m -L. -weak-lweakimport -o weakimport-not-missing.exe END */ diff --git a/test/xref.m b/test/xref.m deleted file mode 100644 index 6418e3f7..00000000 --- a/test/xref.m +++ /dev/null @@ -1,32 +0,0 @@ -// TEST_CFLAGS - -#include -#include -#include - -#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); - - RELEASE_VAR(object); - - succeed(__FILE__); -}