From cd5f04f5e921b81f893bb165605f65af4658baa2 Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 24 Jul 2012 15:54:49 +0000 Subject: [PATCH] objc4-532.tar.gz --- markgc.c | 31 +- objc.xcodeproj/project.pbxproj | 776 +++------- runtime/Accessors.subproj/objc-accessors.h | 33 +- .../{objc-accessors.m => objc-accessors.mm} | 105 +- runtime/Messengers.subproj/objc-msg-arm.s | 65 +- runtime/Messengers.subproj/objc-msg-i386.s | 53 +- .../objc-msg-simulator-i386.s | 41 +- runtime/Messengers.subproj/objc-msg-win32.m | 53 +- runtime/Messengers.subproj/objc-msg-x86_64.s | 210 ++- runtime/{objc-arr.mm => NSObject.mm} | 939 +++++++++-- runtime/Object.h | 82 +- runtime/Object.m | 151 +- runtime/OldClasses.subproj/List.h | 50 +- runtime/OldClasses.subproj/List.m | 54 +- runtime/Protocol.h | 17 +- runtime/{Protocol.m => Protocol.mm} | 37 +- runtime/a1a2-blocktramps-arm.s | 17 +- runtime/a1a2-blocktramps-i386.s | 2 +- runtime/a2a3-blocktramps-arm.s | 18 +- runtime/a2a3-blocktramps-i386.s | 2 +- runtime/{hashtable2.m => hashtable2.mm} | 54 +- runtime/llvm-DenseMap.h | 16 +- runtime/{maptable.m => maptable.mm} | 46 +- runtime/message.h | 86 +- runtime/objc-abi.h | 19 +- runtime/objc-api.h | 35 +- runtime/objc-auto-dump.h | 4 +- runtime/objc-auto-dump.m | 20 +- runtime/objc-auto.h | 21 +- runtime/objc-auto.m | 146 +- ...rampolines.m => objc-block-trampolines.mm} | 30 +- runtime/{objc-cache.m => objc-cache.mm} | 82 +- runtime/objc-class-old.m | 103 +- runtime/objc-class.h | 4 +- runtime/{objc-class.m => objc-class.mm} | 115 +- runtime/objc-config.h | 16 +- runtime/{objc-errors.m => objc-errors.mm} | 54 +- runtime/objc-exception.h | 2 + .../{objc-exception.m => objc-exception.mm} | 312 ++-- ...objc-externalref.m => objc-externalref.mm} | 22 +- runtime/objc-file-old.m | 49 +- runtime/objc-file.h | 6 +- runtime/objc-file.mm | 14 +- .../{objc-initialize.m => objc-initialize.mm} | 27 +- runtime/objc-internal.h | 186 ++- runtime/{objc-layout.m => objc-layout.mm} | 34 +- runtime/{objc-load.m => objc-load.mm} | 0 .../{objc-loadmethod.m => objc-loadmethod.mm} | 33 +- .../{objc-lockdebug.m => objc-lockdebug.mm} | 54 +- runtime/objc-opt.mm | 231 +++ runtime/objc-os.h | 264 ++-- runtime/{objc-os.m => objc-os.mm} | 265 ++-- runtime/objc-private.h | 92 +- runtime/objc-references.h | 2 +- runtime/objc-references.mm | 135 +- runtime/{objc-rtp.m => objc-rtp.mm} | 16 +- runtime/objc-runtime-new.h | 100 +- runtime/objc-runtime-new.mm | 1370 +++++++++++------ runtime/objc-runtime-old.h | 1 + runtime/objc-runtime-old.m | 164 +- runtime/objc-runtime.h | 4 +- runtime/{objc-runtime.m => objc-runtime.mm} | 197 ++- runtime/objc-sel-set.h | 7 +- runtime/{objc-sel-set.m => objc-sel-set.mm} | 55 +- runtime/objc-sel-table.s | 25 +- runtime/objc-sel.mm | 112 +- runtime/objc-selopt.h | 1111 ------------- runtime/{objc-sync.m => objc-sync.mm} | 10 +- ...jc-typeencoding.m => objc-typeencoding.mm} | 20 +- runtime/objc-weak.h | 2 +- runtime/objc-weak.mm | 65 +- runtime/objc.h | 82 +- runtime/objcrt.h | 5 + runtime/runtime.h | 10 +- test/ARRBase.h | 2 +- test/ARRBase.m | 2 +- test/ARRLayouts.m | 53 +- test/Makefile | 8 +- test/accessors.m | 52 +- test/accessors2.m | 143 ++ test/addMethod.m | 43 +- test/addProtocol.m | 8 +- test/arr-cast.m | 20 + test/arr-weak.m | 43 +- test/association-cf.m | 22 +- test/association.m | 97 +- test/atomicProperty.mm | 67 + test/badAltHandler.m | 20 +- test/blocksAsImps.m | 231 +-- test/cacheflush.h | 4 +- test/cacheflush.m | 32 +- test/cacheflush0.m | 5 +- test/cacheflush2.m | 2 +- test/cacheflush3.m | 2 +- test/category.m | 14 +- test/cdtors.mm | 143 +- test/classgetclass.m | 4 +- test/classname.m | 45 +- test/classpair.m | 63 +- test/classversion.m | 13 +- test/concurrentcat.m | 82 +- test/copyMethodList.m | 58 +- test/createInstance.m | 1 + test/customrr-cat1.m | 7 + test/customrr-cat2.m | 7 + test/customrr.m | 757 +++++++++ test/customrr2.m | 9 + test/debuggerMode.m | 2 + test/definitions.c | 50 + test/definitions.m | 25 - test/duplicateClass.m | 41 +- test/evil-category-0.m | 5 +- test/evil-category-00.m | 2 +- test/evil-category-000.m | 5 +- test/evil-category-1.m | 2 +- test/evil-category-2.m | 2 +- test/evil-category-3.m | 2 +- test/evil-category-4.m | 2 +- test/evil-category-def.m | 2 +- test/evil-class-0.m | 3 - test/evil-class-000.m | 3 - test/evil-class-def.m | 2 + test/exc.m | 920 ++++++----- test/exchangeImp.m | 7 +- test/foreach.m | 96 +- test/forward.m | 35 +- test/future.h | 8 +- test/future.m | 24 +- test/future0.m | 7 +- test/gcenforcer-nogc-1.m | 10 +- test/gcenforcer-nogc-2.m | 4 +- test/gcenforcer-noobjc.m | 8 +- test/gcenforcer-requiresgc-1.m | 10 +- test/gcenforcer-requiresgc-2.m | 4 +- test/gcenforcer-supportsgc.m | 8 +- test/gcenforcer.m | 8 +- test/gdb-lock.m | 44 +- test/gdb.m | 22 +- test/getMethod.m | 26 +- test/ignoredSelector.m | 96 +- test/imageorder1.m | 11 + test/imageorder2.m | 10 + test/imageorder3.m | 10 + test/initialize.m | 164 +- test/instanceSize.m | 34 +- test/ismeta.m | 11 +- test/ivar.m | 74 +- test/ivarSlide.h | 30 +- test/ivarSlide.m | 33 +- test/ivarSlide1.m | 8 +- test/layout.m | 2 +- test/literals.m | 29 + test/load.m | 26 +- test/methodArgs.m | 9 +- test/methodListSize.m | 6 +- test/method_getName.m | 2 +- test/msgSend.m | 862 ++++++++--- test/nsexc.m | 16 + test/nsobject.m | 12 +- test/nsprotocol.m | 47 + test/property.m | 8 +- test/propertyDesc.m | 30 +- test/protocol.m | 147 +- test/protocol_copyMethodList.m | 5 +- test/protocol_copyPropertyList.m | 6 +- test/resolve.m | 76 +- test/rr-autorelease-fast.m | 106 ++ test/rr-autorelease.m | 14 +- test/rr-autorelease2.m | 131 +- test/rr-nsautorelease.m | 2 +- test/runtime.m | 47 +- test/sel.m | 13 +- test/setSuper.m | 12 +- test/subscripting.m | 154 ++ test/super.m | 20 +- test/synchronized-counter.m | 4 +- test/synchronized-grid.m | 4 +- test/synchronized.m | 4 +- test/taggedNSPointers.m | 78 + test/taggedPointers.m | 341 ++-- test/test.h | 176 ++- test/test.pl | 207 ++- test/testroot.i | 229 +++ test/unload.h | 12 +- test/unload.m | 27 +- test/unload2.m | 21 +- test/unwind.m | 56 +- test/verify-exports.pl | 2 + test/weak.h | 10 + test/weak.m | 79 +- test/weak2.m | 48 +- test/weakcopy.m | 71 +- test/xref.m | 6 +- unexported_symbols | 3 + 194 files changed, 9546 insertions(+), 6214 deletions(-) rename runtime/Accessors.subproj/{objc-accessors.m => objc-accessors.mm} (56%) rename runtime/{objc-arr.mm => NSObject.mm} (63%) rename runtime/{Protocol.m => Protocol.mm} (80%) rename runtime/{hashtable2.m => hashtable2.mm} (92%) rename runtime/{maptable.m => maptable.mm} (91%) rename runtime/{objc-block-trampolines.m => objc-block-trampolines.mm} (94%) rename runtime/{objc-cache.m => objc-cache.mm} (96%) rename runtime/{objc-class.m => objc-class.mm} (95%) rename runtime/{objc-errors.m => objc-errors.mm} (82%) rename runtime/{objc-exception.m => objc-exception.mm} (80%) rename runtime/{objc-externalref.m => objc-externalref.mm} (94%) rename runtime/{objc-initialize.m => objc-initialize.mm} (95%) rename runtime/{objc-layout.m => objc-layout.mm} (98%) rename runtime/{objc-load.m => objc-load.mm} (100%) rename runtime/{objc-loadmethod.m => objc-loadmethod.mm} (93%) rename runtime/{objc-lockdebug.m => objc-lockdebug.mm} (95%) create mode 100644 runtime/objc-opt.mm rename runtime/{objc-os.m => objc-os.mm} (82%) rename runtime/{objc-rtp.m => objc-rtp.mm} (87%) rename runtime/{objc-runtime.m => objc-runtime.mm} (82%) rename runtime/{objc-sel-set.m => objc-sel-set.mm} (70%) delete mode 100644 runtime/objc-selopt.h rename runtime/{objc-sync.m => objc-sync.mm} (97%) rename runtime/{objc-typeencoding.m => objc-typeencoding.mm} (97%) create mode 100644 test/accessors2.m create mode 100644 test/arr-cast.m create mode 100644 test/atomicProperty.mm create mode 100644 test/customrr-cat1.m create mode 100644 test/customrr-cat2.m create mode 100644 test/customrr.m create mode 100644 test/customrr2.m create mode 100644 test/definitions.c delete mode 100644 test/definitions.m create mode 100644 test/literals.m create mode 100644 test/nsprotocol.m create mode 100644 test/rr-autorelease-fast.m create mode 100644 test/subscripting.m create mode 100644 test/taggedNSPointers.m create mode 100644 test/testroot.i diff --git a/markgc.c b/markgc.c index 146fd8c..fa32688 100644 --- a/markgc.c +++ b/markgc.c @@ -24,12 +24,14 @@ #include #include #include -#import +#include +#include #include -#import -#import -#import -#import +#include +#include +#include +#include +#include // from "objc-private.h" // masks for objc_image_info.flags @@ -39,10 +41,6 @@ #define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3) #define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4) -typedef char bool; -#define true 1 -#define false 0 - bool debug; bool verbose; bool quiet; @@ -145,7 +143,7 @@ uint32_t iiflags(struct imageInfo *ii, size_t size, bool needsFlip) { if (needsFlip) newvalue = OSSwapInt32(newvalue); patchFile(newvalue, (char*)(&ii->flags) - FileBase); } - for(int niis = 1; niis < size/sizeof(struct imageInfo); ++niis) { + for(unsigned niis = 1; niis < size/sizeof(struct imageInfo); ++niis) { if (needsFlip) ii[niis].flags = OSSwapInt32(ii[niis].flags); if (ii[niis].flags != flags) { // uh, oh. @@ -333,7 +331,7 @@ void doofile(void *start, size_t size, struct gcinfo *gcip) { if (debug) printf("filetype -> %d\n", mh->filetype); if (debug) printf("ncmds -> %d\n", mh->ncmds); struct load_command *lc = (is32 ? (struct load_command *)(mh + 1) : (struct load_command *)((struct mach_header_64 *)start + 1)); - int ncmds; + unsigned ncmds; for (ncmds = 0; ncmds < mh->ncmds; ++ncmds) { lc = doloadcommand(start, lc, isFlipped, is32, gcip); } @@ -383,7 +381,7 @@ void dofat(void *start) { needsFlip = true; } if (debug) printf("%d architectures\n", fh->nfat_arch); - int narchs; + unsigned narchs; struct fat_arch *arch_ptr = (struct fat_arch *)(fh + 1); for (narchs = 0; narchs < fh->nfat_arch; ++narchs) { if (debug) printf("doing arch %d\n", narchs); @@ -411,6 +409,11 @@ bool openFile(const char *filename) { close(fd); return false; } + if ((sizeof(size_t) == 4) && ((size_t)statb.st_size > SIZE_T_MAX)) { + printf("couldn't malloc %llu bytes\n", statb.st_size); + close(fd); + return false; + } FileSize = (size_t)statb.st_size; FileBase = malloc(FileSize); if (!FileBase) { @@ -418,8 +421,8 @@ bool openFile(const char *filename) { close(fd); return false; } - off_t readsize = read(fd, FileBase, FileSize); - if (readsize != FileSize) { + ssize_t readsize = read(fd, FileBase, FileSize); + if ((readsize == -1) || ((size_t)readsize != FileSize)) { printf("read %ld bytes, wanted %ld\n", (size_t)readsize, FileSize); close(fd); return false; diff --git a/objc.xcodeproj/project.pbxproj b/objc.xcodeproj/project.pbxproj index 6a4c65a..ef1d461 100644 --- a/objc.xcodeproj/project.pbxproj +++ b/objc.xcodeproj/project.pbxproj @@ -3,212 +3,118 @@ archiveVersion = 1; classes = { }; - objectVersion = 45; + objectVersion = 46; objects = { -/* Begin PBXAggregateTarget section */ - 83E2799A117F76DF00452546 /* objc-selopt */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 83E2799E117F770D00452546 /* Build configuration list for PBXAggregateTarget "objc-selopt" */; - buildPhases = ( - 83E27999117F76DF00452546 /* CopyFiles */, - ); - dependencies = ( - ); - name = "objc-selopt"; - productName = "objc-selopt"; - }; -/* End PBXAggregateTarget section */ - /* Begin PBXBuildFile section */ - 39023F7712F09D0400E1426D /* hashtable.h in Headers */ = {isa = PBXBuildFile; fileRef = 830F2A970D738DC200392440 /* hashtable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F7812F09D0400E1426D /* hashtable2.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485B70D6D687300CEA253 /* hashtable2.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F7912F09D0400E1426D /* List.h in Headers */ = {isa = PBXBuildFile; fileRef = 838486240D6D68F000CEA253 /* List.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F7A12F09D0400E1426D /* maptable.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485BB0D6D687300CEA253 /* maptable.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 39023F7B12F09D0400E1426D /* message.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485BD0D6D687300CEA253 /* message.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F7C12F09D0400E1426D /* objc-abi.h in Headers */ = {isa = PBXBuildFile; fileRef = 834EC0A311614167009B2563 /* objc-abi.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 39023F7D12F09D0400E1426D /* objc-accessors.h in Headers */ = {isa = PBXBuildFile; fileRef = 830F2A920D73876100392440 /* objc-accessors.h */; }; - 39023F7E12F09D0400E1426D /* objc-api.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485C80D6D68A200CEA253 /* objc-api.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F7F12F09D0400E1426D /* objc-auto-dump.h in Headers */ = {isa = PBXBuildFile; fileRef = BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 39023F8012F09D0400E1426D /* objc-auto.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485C90D6D68A200CEA253 /* objc-auto.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F8112F09D0400E1426D /* objc-class.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485CD0D6D68A200CEA253 /* objc-class.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F8212F09D0400E1426D /* objc-config.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485CF0D6D68A200CEA253 /* objc-config.h */; }; - 39023F8312F09D0400E1426D /* objc-exception.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D10D6D68A200CEA253 /* objc-exception.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F8412F09D0400E1426D /* objc-file-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E50FCCB24D00661494 /* objc-file-old.h */; }; - 39023F8512F09D0400E1426D /* objc-file.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E60FCCB24D00661494 /* objc-file.h */; }; - 39023F8612F09D0400E1426D /* objc-gdb.h in Headers */ = {isa = PBXBuildFile; fileRef = 834266D70E665A8B002E4DA2 /* objc-gdb.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 39023F8712F09D0400E1426D /* objc-initialize.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D40D6D68A200CEA253 /* objc-initialize.h */; }; - 39023F8812F09D0400E1426D /* objc-internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 83112ED30F00599600A5FBAF /* objc-internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 39023F8912F09D0400E1426D /* objc-load.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D70D6D68A200CEA253 /* objc-load.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F8A12F09D0400E1426D /* objc-loadmethod.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D90D6D68A200CEA253 /* objc-loadmethod.h */; }; - 39023F8B12F09D0400E1426D /* objc-os.h in Headers */ = {isa = PBXBuildFile; fileRef = 831C85D30E10CF850066E64C /* objc-os.h */; }; - 39023F8C12F09D0400E1426D /* objc-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485DC0D6D68A200CEA253 /* objc-private.h */; }; - 39023F8D12F09D0400E1426D /* objc-references.h in Headers */ = {isa = PBXBuildFile; fileRef = 393CEAC50DC69E67000B69DE /* objc-references.h */; }; - 39023F8E12F09D0400E1426D /* objc-runtime-new.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E00D6D68A200CEA253 /* objc-runtime-new.h */; }; - 39023F8F12F09D0400E1426D /* objc-runtime-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E70FCCB24D00661494 /* objc-runtime-old.h */; }; - 39023F9012F09D0400E1426D /* objc-runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E30D6D68A200CEA253 /* objc-runtime.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F9112F09D0400E1426D /* objc-sel-set.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E50D6D68A200CEA253 /* objc-sel-set.h */; }; - 39023F9212F09D0400E1426D /* objc-sync.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E90D6D68A200CEA253 /* objc-sync.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F9312F09D0400E1426D /* objc.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485EC0D6D68A200CEA253 /* objc.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F9412F09D0400E1426D /* Object.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485ED0D6D68A200CEA253 /* Object.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F9512F09D0400E1426D /* Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 838486180D6D68A800CEA253 /* Protocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F9612F09D0400E1426D /* runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384861A0D6D68A800CEA253 /* runtime.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 39023F9912F09D0400E1426D /* hashtable2.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485B80D6D687300CEA253 /* hashtable2.m */; }; - 39023F9A12F09D0400E1426D /* maptable.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485BC0D6D687300CEA253 /* maptable.m */; }; - 39023F9B12F09D0400E1426D /* objc-auto.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CA0D6D68A200CEA253 /* objc-auto.m */; }; - 39023F9C12F09D0400E1426D /* objc-cache.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CB0D6D68A200CEA253 /* objc-cache.m */; }; - 39023F9D12F09D0400E1426D /* objc-class-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CC0D6D68A200CEA253 /* objc-class-old.m */; }; - 39023F9E12F09D0400E1426D /* objc-class.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CE0D6D68A200CEA253 /* objc-class.m */; }; - 39023F9F12F09D0400E1426D /* objc-errors.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D00D6D68A200CEA253 /* objc-errors.m */; }; - 39023FA012F09D0400E1426D /* objc-exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D20D6D68A200CEA253 /* objc-exception.m */; }; - 39023FA112F09D0400E1426D /* objc-file.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D30D6D68A200CEA253 /* objc-file.mm */; }; - 39023FA212F09D0400E1426D /* objc-initialize.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D50D6D68A200CEA253 /* objc-initialize.m */; }; - 39023FA312F09D0400E1426D /* objc-layout.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D60D6D68A200CEA253 /* objc-layout.m */; }; - 39023FA412F09D0400E1426D /* objc-load.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D80D6D68A200CEA253 /* objc-load.m */; }; - 39023FA512F09D0400E1426D /* objc-loadmethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DA0D6D68A200CEA253 /* objc-loadmethod.m */; }; - 39023FA612F09D0400E1426D /* objc-lockdebug.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DB0D6D68A200CEA253 /* objc-lockdebug.m */; }; - 39023FA712F09D0400E1426D /* objc-rtp.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DF0D6D68A200CEA253 /* objc-rtp.m */; }; - 39023FA812F09D0400E1426D /* objc-runtime-new.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E10D6D68A200CEA253 /* objc-runtime-new.mm */; }; - 39023FA912F09D0400E1426D /* objc-runtime-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E20D6D68A200CEA253 /* objc-runtime-old.m */; }; - 39023FAA12F09D0400E1426D /* objc-runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E40D6D68A200CEA253 /* objc-runtime.m */; }; - 39023FAB12F09D0400E1426D /* objc-sel-set.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E60D6D68A200CEA253 /* objc-sel-set.m */; }; - 39023FAC12F09D0400E1426D /* objc-sel.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E80D6D68A200CEA253 /* objc-sel.mm */; }; - 39023FAD12F09D0400E1426D /* objc-sync.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EA0D6D68A200CEA253 /* objc-sync.m */; }; - 39023FAE12F09D0400E1426D /* objc-typeencoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EB0D6D68A200CEA253 /* objc-typeencoding.m */; }; - 39023FAF12F09D0400E1426D /* Object.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EE0D6D68A200CEA253 /* Object.m */; }; - 39023FB012F09D0400E1426D /* Protocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 838486190D6D68A800CEA253 /* Protocol.m */; }; - 39023FB112F09D0400E1426D /* List.m in Sources */ = {isa = PBXBuildFile; fileRef = 838486230D6D68F000CEA253 /* List.m */; }; - 39023FB212F09D0400E1426D /* objc-msg-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A690D737FB800392440 /* objc-msg-arm.s */; }; - 39023FB312F09D0400E1426D /* objc-msg-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A6A0D737FB800392440 /* objc-msg-i386.s */; }; - 39023FB412F09D0400E1426D /* objc-msg-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A720D737FB800392440 /* objc-msg-x86_64.s */; }; - 39023FB512F09D0400E1426D /* objc-auto-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A850D73819A00392440 /* objc-auto-i386.s */; }; - 39023FB612F09D0400E1426D /* objc-accessors.m in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A930D73876100392440 /* objc-accessors.m */; }; - 39023FB712F09D0400E1426D /* objc-references.mm in Sources */ = {isa = PBXBuildFile; fileRef = 393CEABF0DC69E3E000B69DE /* objc-references.mm */; }; - 39023FB812F09D0400E1426D /* objc-os.m in Sources */ = {isa = PBXBuildFile; fileRef = 831C85D40E10CF850066E64C /* objc-os.m */; }; - 39023FB912F09D0400E1426D /* objc-probes.d in Sources */ = {isa = PBXBuildFile; fileRef = 87BB4E900EC39633005D08E1 /* objc-probes.d */; }; - 39023FBA12F09D0400E1426D /* objc-auto-dump.m in Sources */ = {isa = PBXBuildFile; fileRef = BC07A0100EF72D9C0014EC61 /* objc-auto-dump.m */; }; - 39023FBB12F09D0400E1426D /* objc-file-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BE02E30FCCB23400661494 /* objc-file-old.m */; }; - 39023FBC12F09D0400E1426D /* a1a2-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9C116AB2820071B552 /* a1a2-blocktramps-i386.s */; }; - 39023FBD12F09D0400E1426D /* a1a2-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */; }; - 39023FBE12F09D0400E1426D /* a2a3-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */; }; - 39023FBF12F09D0400E1426D /* a2a3-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9F116AB2820071B552 /* a2a3-blocktramps-x86_64.s */; }; - 39023FC012F09D0400E1426D /* objc-block-trampolines.m in Sources */ = {isa = PBXBuildFile; fileRef = E8923DA0116AB2820071B552 /* objc-block-trampolines.m */; }; - 39023FC112F09D0400E1426D /* objc-msg-simulator-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 83B1A8BC0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s */; }; - 39023FC212F09D0400E1426D /* objc-sel-table.s in Sources */ = {isa = PBXBuildFile; fileRef = 83EB007A121C9EC200B92C16 /* objc-sel-table.s */; }; - 39023FC312F09D0400E1426D /* a1a2-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A1122600E9009290B8 /* a1a2-blocktramps-arm.s */; }; - 39023FC412F09D0400E1426D /* a2a3-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A2122600E9009290B8 /* a2a3-blocktramps-arm.s */; }; - 39023FC512F09D0400E1426D /* objc-externalref.m in Sources */ = {isa = PBXBuildFile; fileRef = 399BC72D1224831B007FBDF0 /* objc-externalref.m */; }; 393CEAC00DC69E3E000B69DE /* objc-references.mm in Sources */ = {isa = PBXBuildFile; fileRef = 393CEABF0DC69E3E000B69DE /* objc-references.mm */; }; 393CEAC60DC69E67000B69DE /* objc-references.h in Headers */ = {isa = PBXBuildFile; fileRef = 393CEAC50DC69E67000B69DE /* objc-references.h */; }; - 399BC72E1224831B007FBDF0 /* objc-externalref.m in Sources */ = {isa = PBXBuildFile; fileRef = 399BC72D1224831B007FBDF0 /* objc-externalref.m */; }; - 39ABD72112F0B61800D1054C /* objc-weak.h in Headers */ = {isa = PBXBuildFile; fileRef = 39ABD71F12F0B61800D1054C /* objc-weak.h */; }; - 39ABD72212F0B61800D1054C /* objc-weak.mm in Sources */ = {isa = PBXBuildFile; fileRef = 39ABD72012F0B61800D1054C /* objc-weak.mm */; }; + 399BC72E1224831B007FBDF0 /* objc-externalref.mm in Sources */ = {isa = PBXBuildFile; fileRef = 399BC72D1224831B007FBDF0 /* objc-externalref.mm */; }; 39ABD72312F0B61800D1054C /* objc-weak.h in Headers */ = {isa = PBXBuildFile; fileRef = 39ABD71F12F0B61800D1054C /* objc-weak.h */; }; 39ABD72412F0B61800D1054C /* objc-weak.mm in Sources */ = {isa = PBXBuildFile; fileRef = 39ABD72012F0B61800D1054C /* objc-weak.mm */; }; 39ABD72512F0B61800D1054C /* objc-weak.h in Headers */ = {isa = PBXBuildFile; fileRef = 39ABD71F12F0B61800D1054C /* objc-weak.h */; }; 39ABD72612F0B61800D1054C /* objc-weak.mm in Sources */ = {isa = PBXBuildFile; fileRef = 39ABD72012F0B61800D1054C /* objc-weak.mm */; }; - 552B2FB712F9D31E00650C0D /* objc-arr.mm in Sources */ = {isa = PBXBuildFile; fileRef = 552B2FB612F9D31E00650C0D /* objc-arr.mm */; }; - 552B2FB812F9D31E00650C0D /* objc-arr.mm in Sources */ = {isa = PBXBuildFile; fileRef = 552B2FB612F9D31E00650C0D /* objc-arr.mm */; }; - 552B2FB912F9D31E00650C0D /* objc-arr.mm in Sources */ = {isa = PBXBuildFile; fileRef = 552B2FB612F9D31E00650C0D /* objc-arr.mm */; }; 830F2A740D737FB800392440 /* objc-msg-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A690D737FB800392440 /* objc-msg-arm.s */; }; 830F2A750D737FB900392440 /* objc-msg-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A6A0D737FB800392440 /* objc-msg-i386.s */; }; 830F2A7D0D737FBB00392440 /* objc-msg-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A720D737FB800392440 /* objc-msg-x86_64.s */; }; 830F2A890D73819A00392440 /* objc-auto-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A850D73819A00392440 /* objc-auto-i386.s */; }; 830F2A940D73876100392440 /* objc-accessors.h in Headers */ = {isa = PBXBuildFile; fileRef = 830F2A920D73876100392440 /* objc-accessors.h */; }; - 830F2A950D73876100392440 /* objc-accessors.m in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A930D73876100392440 /* objc-accessors.m */; }; + 830F2A950D73876100392440 /* objc-accessors.mm in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A930D73876100392440 /* objc-accessors.mm */; }; 830F2A980D738DC200392440 /* hashtable.h in Headers */ = {isa = PBXBuildFile; fileRef = 830F2A970D738DC200392440 /* hashtable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 830F2AB10D73962200392440 /* markgc.c in Sources */ = {isa = PBXBuildFile; fileRef = 830F2AA50D7394C200392440 /* markgc.c */; }; 83112ED40F00599600A5FBAF /* objc-internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 83112ED30F00599600A5FBAF /* objc-internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; 831C85D50E10CF850066E64C /* objc-os.h in Headers */ = {isa = PBXBuildFile; fileRef = 831C85D30E10CF850066E64C /* objc-os.h */; }; - 831C85D60E10CF850066E64C /* objc-os.m in Sources */ = {isa = PBXBuildFile; fileRef = 831C85D40E10CF850066E64C /* objc-os.m */; }; + 831C85D60E10CF850066E64C /* objc-os.mm in Sources */ = {isa = PBXBuildFile; fileRef = 831C85D40E10CF850066E64C /* objc-os.mm */; }; 834266D80E665A8B002E4DA2 /* objc-gdb.h in Headers */ = {isa = PBXBuildFile; fileRef = 834266D70E665A8B002E4DA2 /* objc-gdb.h */; settings = {ATTRIBUTES = (Private, ); }; }; 834EC0A411614167009B2563 /* objc-abi.h in Headers */ = {isa = PBXBuildFile; fileRef = 834EC0A311614167009B2563 /* objc-abi.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 83725F4A14CA5BFA0014370E /* objc-opt.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83725F4914CA5BFA0014370E /* objc-opt.mm */; }; + 83725F4C14CA5C210014370E /* objc-opt.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83725F4914CA5BFA0014370E /* objc-opt.mm */; }; 8383A3A3122600E9009290B8 /* a1a2-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A1122600E9009290B8 /* a1a2-blocktramps-arm.s */; }; 8383A3A4122600E9009290B8 /* a2a3-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A2122600E9009290B8 /* a2a3-blocktramps-arm.s */; }; - 8383A3AC122600FB009290B8 /* a1a2-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A1122600E9009290B8 /* a1a2-blocktramps-arm.s */; }; - 8383A3AD122600FB009290B8 /* a2a3-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A2122600E9009290B8 /* a2a3-blocktramps-arm.s */; }; - 8383A3AE122600FB009290B8 /* hashtable2.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485B80D6D687300CEA253 /* hashtable2.m */; }; - 8383A3AF122600FB009290B8 /* maptable.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485BC0D6D687300CEA253 /* maptable.m */; }; - 8383A3B0122600FB009290B8 /* objc-accessors.m in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A930D73876100392440 /* objc-accessors.m */; }; + 8383A3AC122600FB009290B8 /* a1a2-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A1122600E9009290B8 /* a1a2-blocktramps-arm.s */; settings = {COMPILER_FLAGS = " -Qunused-arguments"; }; }; + 8383A3AD122600FB009290B8 /* a2a3-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A2122600E9009290B8 /* a2a3-blocktramps-arm.s */; settings = {COMPILER_FLAGS = " -Qunused-arguments"; }; }; + 8383A3AE122600FB009290B8 /* hashtable2.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485B80D6D687300CEA253 /* hashtable2.mm */; }; + 8383A3AF122600FB009290B8 /* maptable.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485BC0D6D687300CEA253 /* maptable.mm */; }; + 8383A3B0122600FB009290B8 /* objc-accessors.mm in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A930D73876100392440 /* objc-accessors.mm */; }; 8383A3B1122600FB009290B8 /* objc-auto.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CA0D6D68A200CEA253 /* objc-auto.m */; }; 8383A3B2122600FB009290B8 /* objc-auto-dump.m in Sources */ = {isa = PBXBuildFile; fileRef = BC07A0100EF72D9C0014EC61 /* objc-auto-dump.m */; }; - 8383A3B3122600FB009290B8 /* objc-block-trampolines.m in Sources */ = {isa = PBXBuildFile; fileRef = E8923DA0116AB2820071B552 /* objc-block-trampolines.m */; }; - 8383A3B4122600FB009290B8 /* objc-cache.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CB0D6D68A200CEA253 /* objc-cache.m */; }; + 8383A3B3122600FB009290B8 /* objc-block-trampolines.mm in Sources */ = {isa = PBXBuildFile; fileRef = E8923DA0116AB2820071B552 /* objc-block-trampolines.mm */; }; + 8383A3B4122600FB009290B8 /* objc-cache.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485CB0D6D68A200CEA253 /* objc-cache.mm */; }; 8383A3B5122600FB009290B8 /* objc-class-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CC0D6D68A200CEA253 /* objc-class-old.m */; }; - 8383A3B6122600FB009290B8 /* objc-class.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CE0D6D68A200CEA253 /* objc-class.m */; }; - 8383A3B7122600FB009290B8 /* objc-errors.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D00D6D68A200CEA253 /* objc-errors.m */; }; - 8383A3B8122600FB009290B8 /* objc-exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D20D6D68A200CEA253 /* objc-exception.m */; }; + 8383A3B6122600FB009290B8 /* objc-class.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485CE0D6D68A200CEA253 /* objc-class.mm */; }; + 8383A3B7122600FB009290B8 /* objc-errors.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D00D6D68A200CEA253 /* objc-errors.mm */; }; + 8383A3B8122600FB009290B8 /* objc-exception.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D20D6D68A200CEA253 /* objc-exception.mm */; settings = {COMPILER_FLAGS = "-fexceptions"; }; }; 8383A3B9122600FB009290B8 /* objc-file.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D30D6D68A200CEA253 /* objc-file.mm */; }; 8383A3BA122600FB009290B8 /* objc-file-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BE02E30FCCB23400661494 /* objc-file-old.m */; }; - 8383A3BB122600FB009290B8 /* objc-initialize.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D50D6D68A200CEA253 /* objc-initialize.m */; }; - 8383A3BC122600FB009290B8 /* objc-layout.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D60D6D68A200CEA253 /* objc-layout.m */; }; - 8383A3BD122600FB009290B8 /* objc-load.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D80D6D68A200CEA253 /* objc-load.m */; }; - 8383A3BE122600FB009290B8 /* objc-loadmethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DA0D6D68A200CEA253 /* objc-loadmethod.m */; }; - 8383A3BF122600FB009290B8 /* objc-lockdebug.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DB0D6D68A200CEA253 /* objc-lockdebug.m */; }; - 8383A3C0122600FB009290B8 /* objc-os.m in Sources */ = {isa = PBXBuildFile; fileRef = 831C85D40E10CF850066E64C /* objc-os.m */; }; + 8383A3BB122600FB009290B8 /* objc-initialize.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D50D6D68A200CEA253 /* objc-initialize.mm */; }; + 8383A3BC122600FB009290B8 /* objc-layout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D60D6D68A200CEA253 /* objc-layout.mm */; }; + 8383A3BD122600FB009290B8 /* objc-load.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D80D6D68A200CEA253 /* objc-load.mm */; }; + 8383A3BE122600FB009290B8 /* objc-loadmethod.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485DA0D6D68A200CEA253 /* objc-loadmethod.mm */; }; + 8383A3BF122600FB009290B8 /* objc-lockdebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485DB0D6D68A200CEA253 /* objc-lockdebug.mm */; }; + 8383A3C0122600FB009290B8 /* objc-os.mm in Sources */ = {isa = PBXBuildFile; fileRef = 831C85D40E10CF850066E64C /* objc-os.mm */; }; 8383A3C1122600FB009290B8 /* objc-references.mm in Sources */ = {isa = PBXBuildFile; fileRef = 393CEABF0DC69E3E000B69DE /* objc-references.mm */; }; - 8383A3C2122600FB009290B8 /* objc-rtp.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DF0D6D68A200CEA253 /* objc-rtp.m */; }; + 8383A3C2122600FB009290B8 /* objc-rtp.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485DF0D6D68A200CEA253 /* objc-rtp.mm */; }; 8383A3C3122600FB009290B8 /* objc-runtime-new.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E10D6D68A200CEA253 /* objc-runtime-new.mm */; }; 8383A3C4122600FB009290B8 /* objc-runtime-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E20D6D68A200CEA253 /* objc-runtime-old.m */; }; - 8383A3C5122600FB009290B8 /* objc-runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E40D6D68A200CEA253 /* objc-runtime.m */; }; - 8383A3C6122600FB009290B8 /* objc-sel-set.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E60D6D68A200CEA253 /* objc-sel-set.m */; }; - 8383A3C7122600FB009290B8 /* objc-sel-table.s in Sources */ = {isa = PBXBuildFile; fileRef = 83EB007A121C9EC200B92C16 /* objc-sel-table.s */; }; + 8383A3C5122600FB009290B8 /* objc-runtime.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E40D6D68A200CEA253 /* objc-runtime.mm */; }; + 8383A3C6122600FB009290B8 /* objc-sel-set.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E60D6D68A200CEA253 /* objc-sel-set.mm */; }; + 8383A3C7122600FB009290B8 /* objc-sel-table.s in Sources */ = {isa = PBXBuildFile; fileRef = 83EB007A121C9EC200B92C16 /* objc-sel-table.s */; settings = {COMPILER_FLAGS = " -Qunused-arguments"; }; }; 8383A3C8122600FB009290B8 /* objc-sel.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E80D6D68A200CEA253 /* objc-sel.mm */; }; - 8383A3C9122600FB009290B8 /* objc-sync.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EA0D6D68A200CEA253 /* objc-sync.m */; }; - 8383A3CA122600FB009290B8 /* objc-typeencoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EB0D6D68A200CEA253 /* objc-typeencoding.m */; }; - 8383A3CB122600FB009290B8 /* a1a2-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9C116AB2820071B552 /* a1a2-blocktramps-i386.s */; }; - 8383A3CC122600FB009290B8 /* a1a2-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */; }; - 8383A3CD122600FB009290B8 /* a2a3-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */; }; - 8383A3CE122600FB009290B8 /* a2a3-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9F116AB2820071B552 /* a2a3-blocktramps-x86_64.s */; }; - 8383A3CF122600FB009290B8 /* objc-auto-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A850D73819A00392440 /* objc-auto-i386.s */; }; - 8383A3D0122600FB009290B8 /* objc-msg-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A690D737FB800392440 /* objc-msg-arm.s */; }; - 8383A3D1122600FB009290B8 /* objc-msg-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A6A0D737FB800392440 /* objc-msg-i386.s */; }; - 8383A3D2122600FB009290B8 /* objc-msg-simulator-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 83B1A8BC0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s */; }; - 8383A3D3122600FB009290B8 /* objc-msg-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A720D737FB800392440 /* objc-msg-x86_64.s */; }; + 8383A3C9122600FB009290B8 /* objc-sync.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485EA0D6D68A200CEA253 /* objc-sync.mm */; }; + 8383A3CA122600FB009290B8 /* objc-typeencoding.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485EB0D6D68A200CEA253 /* objc-typeencoding.mm */; }; + 8383A3CB122600FB009290B8 /* a1a2-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9C116AB2820071B552 /* a1a2-blocktramps-i386.s */; settings = {COMPILER_FLAGS = " -Qunused-arguments"; }; }; + 8383A3CC122600FB009290B8 /* a1a2-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */; settings = {COMPILER_FLAGS = " -Qunused-arguments"; }; }; + 8383A3CD122600FB009290B8 /* a2a3-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */; settings = {COMPILER_FLAGS = " -Qunused-arguments"; }; }; + 8383A3CE122600FB009290B8 /* a2a3-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9F116AB2820071B552 /* a2a3-blocktramps-x86_64.s */; settings = {COMPILER_FLAGS = " -Qunused-arguments"; }; }; + 8383A3CF122600FB009290B8 /* objc-auto-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A850D73819A00392440 /* objc-auto-i386.s */; settings = {COMPILER_FLAGS = " -Qunused-arguments"; }; }; + 8383A3D0122600FB009290B8 /* objc-msg-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A690D737FB800392440 /* objc-msg-arm.s */; settings = {COMPILER_FLAGS = " -Qunused-arguments"; }; }; + 8383A3D1122600FB009290B8 /* objc-msg-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A6A0D737FB800392440 /* objc-msg-i386.s */; settings = {COMPILER_FLAGS = " -Qunused-arguments"; }; }; + 8383A3D2122600FB009290B8 /* objc-msg-simulator-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 83B1A8BC0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s */; settings = {COMPILER_FLAGS = " -Qunused-arguments"; }; }; + 8383A3D3122600FB009290B8 /* objc-msg-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A720D737FB800392440 /* objc-msg-x86_64.s */; settings = {COMPILER_FLAGS = " -Qunused-arguments"; }; }; 8383A3D4122600FB009290B8 /* objc-probes.d in Sources */ = {isa = PBXBuildFile; fileRef = 87BB4E900EC39633005D08E1 /* objc-probes.d */; }; - 8383A3DC1226291C009290B8 /* objc-externalref.m in Sources */ = {isa = PBXBuildFile; fileRef = 399BC72D1224831B007FBDF0 /* objc-externalref.m */; }; + 8383A3DC1226291C009290B8 /* objc-externalref.mm in Sources */ = {isa = PBXBuildFile; fileRef = 399BC72D1224831B007FBDF0 /* objc-externalref.mm */; }; 838485BF0D6D687300CEA253 /* hashtable2.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485B70D6D687300CEA253 /* hashtable2.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 838485C00D6D687300CEA253 /* hashtable2.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485B80D6D687300CEA253 /* hashtable2.m */; }; + 838485C00D6D687300CEA253 /* hashtable2.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485B80D6D687300CEA253 /* hashtable2.mm */; }; 838485C30D6D687300CEA253 /* maptable.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485BB0D6D687300CEA253 /* maptable.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 838485C40D6D687300CEA253 /* maptable.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485BC0D6D687300CEA253 /* maptable.m */; }; + 838485C40D6D687300CEA253 /* maptable.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485BC0D6D687300CEA253 /* maptable.mm */; }; 838485EF0D6D68A200CEA253 /* objc-api.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485C80D6D68A200CEA253 /* objc-api.h */; settings = {ATTRIBUTES = (Public, ); }; }; 838485F00D6D68A200CEA253 /* objc-auto.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485C90D6D68A200CEA253 /* objc-auto.h */; settings = {ATTRIBUTES = (Public, ); }; }; 838485F10D6D68A200CEA253 /* objc-auto.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CA0D6D68A200CEA253 /* objc-auto.m */; }; - 838485F20D6D68A200CEA253 /* objc-cache.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CB0D6D68A200CEA253 /* objc-cache.m */; }; + 838485F20D6D68A200CEA253 /* objc-cache.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485CB0D6D68A200CEA253 /* objc-cache.mm */; }; 838485F30D6D68A200CEA253 /* objc-class-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CC0D6D68A200CEA253 /* objc-class-old.m */; }; 838485F40D6D68A200CEA253 /* objc-class.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485CD0D6D68A200CEA253 /* objc-class.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 838485F50D6D68A200CEA253 /* objc-class.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485CE0D6D68A200CEA253 /* objc-class.m */; }; + 838485F50D6D68A200CEA253 /* objc-class.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485CE0D6D68A200CEA253 /* objc-class.mm */; }; 838485F60D6D68A200CEA253 /* objc-config.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485CF0D6D68A200CEA253 /* objc-config.h */; }; - 838485F70D6D68A200CEA253 /* objc-errors.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D00D6D68A200CEA253 /* objc-errors.m */; }; + 838485F70D6D68A200CEA253 /* objc-errors.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D00D6D68A200CEA253 /* objc-errors.mm */; }; 838485F80D6D68A200CEA253 /* objc-exception.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D10D6D68A200CEA253 /* objc-exception.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 838485F90D6D68A200CEA253 /* objc-exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D20D6D68A200CEA253 /* objc-exception.m */; }; + 838485F90D6D68A200CEA253 /* objc-exception.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D20D6D68A200CEA253 /* objc-exception.mm */; settings = {COMPILER_FLAGS = "-fexceptions"; }; }; 838485FA0D6D68A200CEA253 /* objc-file.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D30D6D68A200CEA253 /* objc-file.mm */; }; 838485FB0D6D68A200CEA253 /* objc-initialize.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D40D6D68A200CEA253 /* objc-initialize.h */; }; - 838485FC0D6D68A200CEA253 /* objc-initialize.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D50D6D68A200CEA253 /* objc-initialize.m */; }; - 838485FD0D6D68A200CEA253 /* objc-layout.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D60D6D68A200CEA253 /* objc-layout.m */; }; + 838485FC0D6D68A200CEA253 /* objc-initialize.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D50D6D68A200CEA253 /* objc-initialize.mm */; }; + 838485FD0D6D68A200CEA253 /* objc-layout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D60D6D68A200CEA253 /* objc-layout.mm */; }; 838485FE0D6D68A200CEA253 /* objc-load.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D70D6D68A200CEA253 /* objc-load.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 838485FF0D6D68A200CEA253 /* objc-load.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485D80D6D68A200CEA253 /* objc-load.m */; }; + 838485FF0D6D68A200CEA253 /* objc-load.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485D80D6D68A200CEA253 /* objc-load.mm */; }; 838486000D6D68A200CEA253 /* objc-loadmethod.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485D90D6D68A200CEA253 /* objc-loadmethod.h */; }; - 838486010D6D68A200CEA253 /* objc-loadmethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DA0D6D68A200CEA253 /* objc-loadmethod.m */; }; - 838486020D6D68A200CEA253 /* objc-lockdebug.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DB0D6D68A200CEA253 /* objc-lockdebug.m */; }; + 838486010D6D68A200CEA253 /* objc-loadmethod.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485DA0D6D68A200CEA253 /* objc-loadmethod.mm */; }; + 838486020D6D68A200CEA253 /* objc-lockdebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485DB0D6D68A200CEA253 /* objc-lockdebug.mm */; }; 838486030D6D68A200CEA253 /* objc-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485DC0D6D68A200CEA253 /* objc-private.h */; }; - 838486060D6D68A200CEA253 /* objc-rtp.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485DF0D6D68A200CEA253 /* objc-rtp.m */; }; + 838486060D6D68A200CEA253 /* objc-rtp.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485DF0D6D68A200CEA253 /* objc-rtp.mm */; }; 838486070D6D68A200CEA253 /* objc-runtime-new.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E00D6D68A200CEA253 /* objc-runtime-new.h */; }; 838486080D6D68A200CEA253 /* objc-runtime-new.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E10D6D68A200CEA253 /* objc-runtime-new.mm */; }; 838486090D6D68A200CEA253 /* objc-runtime-old.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E20D6D68A200CEA253 /* objc-runtime-old.m */; }; 8384860A0D6D68A200CEA253 /* objc-runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E30D6D68A200CEA253 /* objc-runtime.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8384860B0D6D68A200CEA253 /* objc-runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E40D6D68A200CEA253 /* objc-runtime.m */; }; + 8384860B0D6D68A200CEA253 /* objc-runtime.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E40D6D68A200CEA253 /* objc-runtime.mm */; }; 8384860C0D6D68A200CEA253 /* objc-sel-set.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E50D6D68A200CEA253 /* objc-sel-set.h */; }; - 8384860D0D6D68A200CEA253 /* objc-sel-set.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485E60D6D68A200CEA253 /* objc-sel-set.m */; }; + 8384860D0D6D68A200CEA253 /* objc-sel-set.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E60D6D68A200CEA253 /* objc-sel-set.mm */; }; 8384860F0D6D68A200CEA253 /* objc-sel.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485E80D6D68A200CEA253 /* objc-sel.mm */; }; 838486100D6D68A200CEA253 /* objc-sync.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485E90D6D68A200CEA253 /* objc-sync.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 838486110D6D68A200CEA253 /* objc-sync.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EA0D6D68A200CEA253 /* objc-sync.m */; }; - 838486120D6D68A200CEA253 /* objc-typeencoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EB0D6D68A200CEA253 /* objc-typeencoding.m */; }; + 838486110D6D68A200CEA253 /* objc-sync.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485EA0D6D68A200CEA253 /* objc-sync.mm */; }; + 838486120D6D68A200CEA253 /* objc-typeencoding.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485EB0D6D68A200CEA253 /* objc-typeencoding.mm */; }; 838486130D6D68A200CEA253 /* objc.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485EC0D6D68A200CEA253 /* objc.h */; settings = {ATTRIBUTES = (Public, ); }; }; 838486140D6D68A200CEA253 /* Object.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485ED0D6D68A200CEA253 /* Object.h */; settings = {ATTRIBUTES = (Public, ); }; }; 838486150D6D68A200CEA253 /* Object.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EE0D6D68A200CEA253 /* Object.m */; }; 8384861E0D6D68A800CEA253 /* Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 838486180D6D68A800CEA253 /* Protocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8384861F0D6D68A800CEA253 /* Protocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 838486190D6D68A800CEA253 /* Protocol.m */; }; + 8384861F0D6D68A800CEA253 /* Protocol.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838486190D6D68A800CEA253 /* Protocol.mm */; }; 838486200D6D68A800CEA253 /* runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384861A0D6D68A800CEA253 /* runtime.h */; settings = {ATTRIBUTES = (Public, ); }; }; 838486250D6D68F000CEA253 /* List.m in Sources */ = {isa = PBXBuildFile; fileRef = 838486230D6D68F000CEA253 /* List.m */; }; 838486260D6D68F000CEA253 /* List.h in Headers */ = {isa = PBXBuildFile; fileRef = 838486240D6D68F000CEA253 /* List.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -218,7 +124,6 @@ 83BE02E80FCCB24D00661494 /* objc-file-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E50FCCB24D00661494 /* objc-file-old.h */; }; 83BE02E90FCCB24D00661494 /* objc-file.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E60FCCB24D00661494 /* objc-file.h */; }; 83BE02EA0FCCB24D00661494 /* objc-runtime-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E70FCCB24D00661494 /* objc-runtime-old.h */; }; - 83E2799D117F76EF00452546 /* objc-selopt.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83CDE7520E2E0040003703C1 /* objc-selopt.h */; }; 83E50CDB0FF19E8200D74C19 /* hashtable2.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485B70D6D687300CEA253 /* hashtable2.h */; settings = {ATTRIBUTES = (Public, ); }; }; 83E50CDC0FF19E8200D74C19 /* maptable.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485BB0D6D687300CEA253 /* maptable.h */; settings = {ATTRIBUTES = (Private, ); }; }; 83E50CDD0FF19E8200D74C19 /* objc-api.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485C80D6D68A200CEA253 /* objc-api.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -248,7 +153,7 @@ 83E50CF70FF19E8200D74C19 /* objc-gdb.h in Headers */ = {isa = PBXBuildFile; fileRef = 834266D70E665A8B002E4DA2 /* objc-gdb.h */; settings = {ATTRIBUTES = (Private, ); }; }; 83E50CF80FF19E8200D74C19 /* objc-internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 83112ED30F00599600A5FBAF /* objc-internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; 83E50D130FF19E8200D74C19 /* Object.m in Sources */ = {isa = PBXBuildFile; fileRef = 838485EE0D6D68A200CEA253 /* Object.m */; }; - 83E50D140FF19E8200D74C19 /* Protocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 838486190D6D68A800CEA253 /* Protocol.m */; }; + 83E50D140FF19E8200D74C19 /* Protocol.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838486190D6D68A800CEA253 /* Protocol.mm */; }; 83E50D150FF19E8200D74C19 /* List.m in Sources */ = {isa = PBXBuildFile; fileRef = 838486230D6D68F000CEA253 /* List.m */; }; 83E57595121E892100295464 /* objc-abi.h in Headers */ = {isa = PBXBuildFile; fileRef = 834EC0A311614167009B2563 /* objc-abi.h */; settings = {ATTRIBUTES = (Private, ); }; }; 83E57596121E896200295464 /* objc-file-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E50FCCB24D00661494 /* objc-file-old.h */; }; @@ -256,23 +161,18 @@ 83E57598121E8A1600295464 /* objc-file.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E60FCCB24D00661494 /* objc-file.h */; }; 83EB007B121C9EC200B92C16 /* objc-sel-table.s in Sources */ = {isa = PBXBuildFile; fileRef = 83EB007A121C9EC200B92C16 /* objc-sel-table.s */; }; 87BB4EA70EC39854005D08E1 /* objc-probes.d in Sources */ = {isa = PBXBuildFile; fileRef = 87BB4E900EC39633005D08E1 /* objc-probes.d */; }; + 9672F7EE14D5F488007CEC96 /* NSObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9672F7ED14D5F488007CEC96 /* NSObject.mm */; }; + 9672F7EF14D5F488007CEC96 /* NSObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9672F7ED14D5F488007CEC96 /* NSObject.mm */; }; BC07A00C0EF72D360014EC61 /* objc-auto-dump.h in Headers */ = {isa = PBXBuildFile; fileRef = BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */; settings = {ATTRIBUTES = (Private, ); }; }; BC07A0110EF72D9C0014EC61 /* objc-auto-dump.m in Sources */ = {isa = PBXBuildFile; fileRef = BC07A0100EF72D9C0014EC61 /* objc-auto-dump.m */; }; E8923DA1116AB2820071B552 /* a1a2-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9C116AB2820071B552 /* a1a2-blocktramps-i386.s */; }; E8923DA2116AB2820071B552 /* a1a2-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */; }; E8923DA3116AB2820071B552 /* a2a3-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */; }; E8923DA4116AB2820071B552 /* a2a3-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9F116AB2820071B552 /* a2a3-blocktramps-x86_64.s */; }; - E8923DA5116AB2820071B552 /* objc-block-trampolines.m in Sources */ = {isa = PBXBuildFile; fileRef = E8923DA0116AB2820071B552 /* objc-block-trampolines.m */; }; + E8923DA5116AB2820071B552 /* objc-block-trampolines.mm in Sources */ = {isa = PBXBuildFile; fileRef = E8923DA0116AB2820071B552 /* objc-block-trampolines.mm */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 39023F7512F09D0400E1426D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 830F2AA80D7394D000392440; - remoteInfo = markgc; - }; 835720F50F8BF8EE00BD4FAD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; @@ -282,89 +182,75 @@ }; /* End PBXContainerItemProxy section */ -/* Begin PBXCopyFilesBuildPhase section */ - 83E27999117F76DF00452546 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = /usr/local/include/objc; - dstSubfolderSpec = 0; - files = ( - 83E2799D117F76EF00452546 /* objc-selopt.h in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ - 39023FCE12F09D0400E1426D /* libobjc.A.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libobjc.A.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 393CEABF0DC69E3E000B69DE /* objc-references.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-references.mm"; path = "runtime/objc-references.mm"; sourceTree = ""; }; 393CEAC50DC69E67000B69DE /* objc-references.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-references.h"; path = "runtime/objc-references.h"; sourceTree = ""; }; - 399BC72D1224831B007FBDF0 /* objc-externalref.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-externalref.m"; path = "runtime/objc-externalref.m"; sourceTree = ""; }; + 399BC72D1224831B007FBDF0 /* objc-externalref.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-externalref.mm"; path = "runtime/objc-externalref.mm"; sourceTree = ""; }; 39ABD71F12F0B61800D1054C /* objc-weak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-weak.h"; path = "runtime/objc-weak.h"; sourceTree = ""; }; 39ABD72012F0B61800D1054C /* objc-weak.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-weak.mm"; path = "runtime/objc-weak.mm"; sourceTree = ""; }; - 552B2FB612F9D31E00650C0D /* objc-arr.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-arr.mm"; path = "runtime/objc-arr.mm"; sourceTree = ""; }; 830F2A690D737FB800392440 /* objc-msg-arm.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-arm.s"; path = "runtime/Messengers.subproj/objc-msg-arm.s"; sourceTree = ""; }; 830F2A6A0D737FB800392440 /* objc-msg-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-i386.s"; path = "runtime/Messengers.subproj/objc-msg-i386.s"; sourceTree = ""; }; 830F2A720D737FB800392440 /* objc-msg-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-x86_64.s"; path = "runtime/Messengers.subproj/objc-msg-x86_64.s"; sourceTree = ""; tabWidth = 8; usesTabs = 1; }; 830F2A850D73819A00392440 /* objc-auto-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-auto-i386.s"; path = "runtime/Auto.subproj/objc-auto-i386.s"; sourceTree = ""; }; 830F2A920D73876100392440 /* objc-accessors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-accessors.h"; path = "runtime/Accessors.subproj/objc-accessors.h"; sourceTree = ""; }; - 830F2A930D73876100392440 /* objc-accessors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-accessors.m"; path = "runtime/Accessors.subproj/objc-accessors.m"; sourceTree = ""; }; + 830F2A930D73876100392440 /* objc-accessors.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-accessors.mm"; path = "runtime/Accessors.subproj/objc-accessors.mm"; sourceTree = ""; }; 830F2A970D738DC200392440 /* hashtable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hashtable.h; path = runtime/hashtable.h; sourceTree = ""; }; 830F2AA50D7394C200392440 /* markgc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = markgc.c; sourceTree = ""; }; 830F2AA90D7394D000392440 /* markgc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = markgc; sourceTree = BUILT_PRODUCTS_DIR; }; 83112ED30F00599600A5FBAF /* objc-internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-internal.h"; path = "runtime/objc-internal.h"; sourceTree = ""; }; 831C85D30E10CF850066E64C /* objc-os.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-os.h"; path = "runtime/objc-os.h"; sourceTree = ""; }; - 831C85D40E10CF850066E64C /* objc-os.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-os.m"; path = "runtime/objc-os.m"; sourceTree = ""; }; + 831C85D40E10CF850066E64C /* objc-os.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-os.mm"; path = "runtime/objc-os.mm"; sourceTree = ""; }; 834266D70E665A8B002E4DA2 /* objc-gdb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-gdb.h"; path = "runtime/objc-gdb.h"; sourceTree = ""; }; 834EC0A311614167009B2563 /* objc-abi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-abi.h"; path = "runtime/objc-abi.h"; sourceTree = ""; }; + 83725F4914CA5BFA0014370E /* objc-opt.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-opt.mm"; path = "runtime/objc-opt.mm"; sourceTree = ""; }; 8383A3A1122600E9009290B8 /* a1a2-blocktramps-arm.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a1a2-blocktramps-arm.s"; path = "runtime/a1a2-blocktramps-arm.s"; sourceTree = ""; }; 8383A3A2122600E9009290B8 /* a2a3-blocktramps-arm.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a2a3-blocktramps-arm.s"; path = "runtime/a2a3-blocktramps-arm.s"; sourceTree = ""; }; 838485B30D6D682B00CEA253 /* libobjc.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = libobjc.order; sourceTree = ""; }; 838485B40D6D683300CEA253 /* APPLE_LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = APPLE_LICENSE; sourceTree = ""; }; 838485B50D6D683300CEA253 /* ReleaseNotes.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = ReleaseNotes.rtf; sourceTree = ""; }; 838485B70D6D687300CEA253 /* hashtable2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hashtable2.h; path = runtime/hashtable2.h; sourceTree = ""; }; - 838485B80D6D687300CEA253 /* hashtable2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = hashtable2.m; path = runtime/hashtable2.m; sourceTree = ""; }; + 838485B80D6D687300CEA253 /* hashtable2.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = hashtable2.mm; path = runtime/hashtable2.mm; sourceTree = ""; }; 838485BB0D6D687300CEA253 /* maptable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = maptable.h; path = runtime/maptable.h; sourceTree = ""; }; - 838485BC0D6D687300CEA253 /* maptable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = maptable.m; path = runtime/maptable.m; sourceTree = ""; }; + 838485BC0D6D687300CEA253 /* maptable.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = maptable.mm; path = runtime/maptable.mm; sourceTree = ""; }; 838485BD0D6D687300CEA253 /* message.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = message.h; path = runtime/message.h; sourceTree = ""; }; 838485C80D6D68A200CEA253 /* objc-api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-api.h"; path = "runtime/objc-api.h"; sourceTree = ""; }; 838485C90D6D68A200CEA253 /* objc-auto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-auto.h"; path = "runtime/objc-auto.h"; sourceTree = ""; }; 838485CA0D6D68A200CEA253 /* objc-auto.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-auto.m"; path = "runtime/objc-auto.m"; sourceTree = ""; }; - 838485CB0D6D68A200CEA253 /* objc-cache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-cache.m"; path = "runtime/objc-cache.m"; sourceTree = ""; }; + 838485CB0D6D68A200CEA253 /* objc-cache.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-cache.mm"; path = "runtime/objc-cache.mm"; sourceTree = ""; }; 838485CC0D6D68A200CEA253 /* objc-class-old.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-class-old.m"; path = "runtime/objc-class-old.m"; sourceTree = ""; }; 838485CD0D6D68A200CEA253 /* objc-class.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-class.h"; path = "runtime/objc-class.h"; sourceTree = ""; }; - 838485CE0D6D68A200CEA253 /* objc-class.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-class.m"; path = "runtime/objc-class.m"; sourceTree = ""; }; + 838485CE0D6D68A200CEA253 /* objc-class.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-class.mm"; path = "runtime/objc-class.mm"; sourceTree = ""; }; 838485CF0D6D68A200CEA253 /* objc-config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-config.h"; path = "runtime/objc-config.h"; sourceTree = ""; }; - 838485D00D6D68A200CEA253 /* objc-errors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-errors.m"; path = "runtime/objc-errors.m"; sourceTree = ""; }; + 838485D00D6D68A200CEA253 /* objc-errors.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-errors.mm"; path = "runtime/objc-errors.mm"; sourceTree = ""; }; 838485D10D6D68A200CEA253 /* objc-exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-exception.h"; path = "runtime/objc-exception.h"; sourceTree = ""; }; - 838485D20D6D68A200CEA253 /* objc-exception.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-exception.m"; path = "runtime/objc-exception.m"; sourceTree = ""; }; + 838485D20D6D68A200CEA253 /* objc-exception.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-exception.mm"; path = "runtime/objc-exception.mm"; sourceTree = ""; }; 838485D30D6D68A200CEA253 /* objc-file.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-file.mm"; path = "runtime/objc-file.mm"; sourceTree = ""; }; 838485D40D6D68A200CEA253 /* objc-initialize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-initialize.h"; path = "runtime/objc-initialize.h"; sourceTree = ""; }; - 838485D50D6D68A200CEA253 /* objc-initialize.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-initialize.m"; path = "runtime/objc-initialize.m"; sourceTree = ""; }; - 838485D60D6D68A200CEA253 /* objc-layout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-layout.m"; path = "runtime/objc-layout.m"; sourceTree = ""; }; + 838485D50D6D68A200CEA253 /* objc-initialize.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-initialize.mm"; path = "runtime/objc-initialize.mm"; sourceTree = ""; }; + 838485D60D6D68A200CEA253 /* objc-layout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-layout.mm"; path = "runtime/objc-layout.mm"; sourceTree = ""; }; 838485D70D6D68A200CEA253 /* objc-load.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-load.h"; path = "runtime/objc-load.h"; sourceTree = ""; }; - 838485D80D6D68A200CEA253 /* objc-load.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-load.m"; path = "runtime/objc-load.m"; sourceTree = ""; }; + 838485D80D6D68A200CEA253 /* objc-load.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-load.mm"; path = "runtime/objc-load.mm"; sourceTree = ""; }; 838485D90D6D68A200CEA253 /* objc-loadmethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-loadmethod.h"; path = "runtime/objc-loadmethod.h"; sourceTree = ""; }; - 838485DA0D6D68A200CEA253 /* objc-loadmethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-loadmethod.m"; path = "runtime/objc-loadmethod.m"; sourceTree = ""; }; - 838485DB0D6D68A200CEA253 /* objc-lockdebug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-lockdebug.m"; path = "runtime/objc-lockdebug.m"; sourceTree = ""; }; + 838485DA0D6D68A200CEA253 /* objc-loadmethod.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-loadmethod.mm"; path = "runtime/objc-loadmethod.mm"; sourceTree = ""; }; + 838485DB0D6D68A200CEA253 /* objc-lockdebug.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-lockdebug.mm"; path = "runtime/objc-lockdebug.mm"; sourceTree = ""; }; 838485DC0D6D68A200CEA253 /* objc-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-private.h"; path = "runtime/objc-private.h"; sourceTree = ""; }; - 838485DF0D6D68A200CEA253 /* objc-rtp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-rtp.m"; path = "runtime/objc-rtp.m"; sourceTree = ""; }; + 838485DF0D6D68A200CEA253 /* objc-rtp.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-rtp.mm"; path = "runtime/objc-rtp.mm"; sourceTree = ""; }; 838485E00D6D68A200CEA253 /* objc-runtime-new.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-runtime-new.h"; path = "runtime/objc-runtime-new.h"; sourceTree = ""; }; 838485E10D6D68A200CEA253 /* objc-runtime-new.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-runtime-new.mm"; path = "runtime/objc-runtime-new.mm"; sourceTree = ""; }; 838485E20D6D68A200CEA253 /* objc-runtime-old.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-runtime-old.m"; path = "runtime/objc-runtime-old.m"; sourceTree = ""; }; 838485E30D6D68A200CEA253 /* objc-runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-runtime.h"; path = "runtime/objc-runtime.h"; sourceTree = ""; }; - 838485E40D6D68A200CEA253 /* objc-runtime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-runtime.m"; path = "runtime/objc-runtime.m"; sourceTree = ""; }; + 838485E40D6D68A200CEA253 /* objc-runtime.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-runtime.mm"; path = "runtime/objc-runtime.mm"; sourceTree = ""; }; 838485E50D6D68A200CEA253 /* objc-sel-set.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-sel-set.h"; path = "runtime/objc-sel-set.h"; sourceTree = ""; }; - 838485E60D6D68A200CEA253 /* objc-sel-set.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-sel-set.m"; path = "runtime/objc-sel-set.m"; sourceTree = ""; }; + 838485E60D6D68A200CEA253 /* objc-sel-set.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-sel-set.mm"; path = "runtime/objc-sel-set.mm"; sourceTree = ""; }; 838485E80D6D68A200CEA253 /* objc-sel.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-sel.mm"; path = "runtime/objc-sel.mm"; sourceTree = ""; }; 838485E90D6D68A200CEA253 /* objc-sync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-sync.h"; path = "runtime/objc-sync.h"; sourceTree = ""; }; - 838485EA0D6D68A200CEA253 /* objc-sync.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-sync.m"; path = "runtime/objc-sync.m"; sourceTree = ""; }; - 838485EB0D6D68A200CEA253 /* objc-typeencoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-typeencoding.m"; path = "runtime/objc-typeencoding.m"; sourceTree = ""; }; + 838485EA0D6D68A200CEA253 /* objc-sync.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-sync.mm"; path = "runtime/objc-sync.mm"; sourceTree = ""; }; + 838485EB0D6D68A200CEA253 /* objc-typeencoding.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-typeencoding.mm"; path = "runtime/objc-typeencoding.mm"; sourceTree = ""; }; 838485EC0D6D68A200CEA253 /* objc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = objc.h; path = runtime/objc.h; sourceTree = ""; }; 838485ED0D6D68A200CEA253 /* Object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Object.h; path = runtime/Object.h; sourceTree = ""; }; 838485EE0D6D68A200CEA253 /* Object.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Object.m; path = runtime/Object.m; sourceTree = ""; }; 838486180D6D68A800CEA253 /* Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Protocol.h; path = runtime/Protocol.h; sourceTree = ""; }; - 838486190D6D68A800CEA253 /* Protocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Protocol.m; path = runtime/Protocol.m; sourceTree = ""; }; + 838486190D6D68A800CEA253 /* Protocol.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Protocol.mm; path = runtime/Protocol.mm; sourceTree = ""; }; 8384861A0D6D68A800CEA253 /* runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = runtime.h; path = runtime/runtime.h; sourceTree = ""; }; 838486230D6D68F000CEA253 /* List.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = List.m; path = runtime/OldClasses.subproj/List.m; sourceTree = ""; }; 838486240D6D68F000CEA253 /* List.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = List.h; path = runtime/OldClasses.subproj/List.h; sourceTree = ""; }; @@ -373,11 +259,11 @@ 83BE02E50FCCB24D00661494 /* objc-file-old.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-file-old.h"; path = "runtime/objc-file-old.h"; sourceTree = ""; }; 83BE02E60FCCB24D00661494 /* objc-file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-file.h"; path = "runtime/objc-file.h"; sourceTree = ""; }; 83BE02E70FCCB24D00661494 /* objc-runtime-old.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-runtime-old.h"; path = "runtime/objc-runtime-old.h"; sourceTree = ""; }; - 83CDE7520E2E0040003703C1 /* objc-selopt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-selopt.h"; path = "runtime/objc-selopt.h"; sourceTree = ""; }; 83E50D2A0FF19E8200D74C19 /* libobjc.A.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libobjc.A.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 83E50D2B0FF19E9E00D74C19 /* IndigoSDK.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = IndigoSDK.xcconfig; path = AppleInternal/XcodeConfig/IndigoSDK.xcconfig; sourceTree = DEVELOPER_DIR; }; 83EB007A121C9EC200B92C16 /* objc-sel-table.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-sel-table.s"; path = "runtime/objc-sel-table.s"; sourceTree = ""; }; 87BB4E900EC39633005D08E1 /* objc-probes.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; name = "objc-probes.d"; path = "runtime/objc-probes.d"; sourceTree = ""; }; + 9672F7ED14D5F488007CEC96 /* NSObject.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = NSObject.mm; path = runtime/NSObject.mm; sourceTree = ""; }; BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-auto-dump.h"; path = "runtime/objc-auto-dump.h"; sourceTree = ""; }; BC07A0100EF72D9C0014EC61 /* objc-auto-dump.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-auto-dump.m"; path = "runtime/objc-auto-dump.m"; sourceTree = ""; }; BC8B5D1212D3D48100C78A5B /* libauto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libauto.dylib; path = /usr/lib/libauto.dylib; sourceTree = ""; }; @@ -386,17 +272,10 @@ E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a1a2-blocktramps-x86_64.s"; path = "runtime/a1a2-blocktramps-x86_64.s"; sourceTree = ""; }; E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a2a3-blocktramps-i386.s"; path = "runtime/a2a3-blocktramps-i386.s"; sourceTree = ""; }; E8923D9F116AB2820071B552 /* a2a3-blocktramps-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a2a3-blocktramps-x86_64.s"; path = "runtime/a2a3-blocktramps-x86_64.s"; sourceTree = ""; }; - E8923DA0116AB2820071B552 /* objc-block-trampolines.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-block-trampolines.m"; path = "runtime/objc-block-trampolines.m"; sourceTree = ""; }; + E8923DA0116AB2820071B552 /* objc-block-trampolines.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-block-trampolines.mm"; path = "runtime/objc-block-trampolines.mm"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 39023FC712F09D0400E1426D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 830F2AA70D7394D000392440 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -442,38 +321,41 @@ children = ( 8383A3A1122600E9009290B8 /* a1a2-blocktramps-arm.s */, 8383A3A2122600E9009290B8 /* a2a3-blocktramps-arm.s */, - 838485B80D6D687300CEA253 /* hashtable2.m */, - 838485BC0D6D687300CEA253 /* maptable.m */, - 830F2A930D73876100392440 /* objc-accessors.m */, + 838485B80D6D687300CEA253 /* hashtable2.mm */, + 838485BC0D6D687300CEA253 /* maptable.mm */, + 9672F7ED14D5F488007CEC96 /* NSObject.mm */, + 838486190D6D68A800CEA253 /* Protocol.mm */, + 830F2A930D73876100392440 /* objc-accessors.mm */, 838485CA0D6D68A200CEA253 /* objc-auto.m */, BC07A0100EF72D9C0014EC61 /* objc-auto-dump.m */, - 552B2FB612F9D31E00650C0D /* objc-arr.mm */, 39ABD72012F0B61800D1054C /* objc-weak.mm */, - E8923DA0116AB2820071B552 /* objc-block-trampolines.m */, - 838485CB0D6D68A200CEA253 /* objc-cache.m */, + E8923DA0116AB2820071B552 /* objc-block-trampolines.mm */, + 838485CB0D6D68A200CEA253 /* objc-cache.mm */, 838485CC0D6D68A200CEA253 /* objc-class-old.m */, - 838485CE0D6D68A200CEA253 /* objc-class.m */, - 838485D00D6D68A200CEA253 /* objc-errors.m */, - 838485D20D6D68A200CEA253 /* objc-exception.m */, - 399BC72D1224831B007FBDF0 /* objc-externalref.m */, + 838485CE0D6D68A200CEA253 /* objc-class.mm */, + 838485D00D6D68A200CEA253 /* objc-errors.mm */, + 838485D20D6D68A200CEA253 /* objc-exception.mm */, + 399BC72D1224831B007FBDF0 /* objc-externalref.mm */, 838485D30D6D68A200CEA253 /* objc-file.mm */, 83BE02E30FCCB23400661494 /* objc-file-old.m */, - 838485D50D6D68A200CEA253 /* objc-initialize.m */, - 838485D60D6D68A200CEA253 /* objc-layout.m */, - 838485D80D6D68A200CEA253 /* objc-load.m */, - 838485DA0D6D68A200CEA253 /* objc-loadmethod.m */, - 838485DB0D6D68A200CEA253 /* objc-lockdebug.m */, - 831C85D40E10CF850066E64C /* objc-os.m */, + 838485D50D6D68A200CEA253 /* objc-initialize.mm */, + 838485D60D6D68A200CEA253 /* objc-layout.mm */, + 838485D80D6D68A200CEA253 /* objc-load.mm */, + 838485DA0D6D68A200CEA253 /* objc-loadmethod.mm */, + 838485DB0D6D68A200CEA253 /* objc-lockdebug.mm */, + 83725F4914CA5BFA0014370E /* objc-opt.mm */, + 831C85D40E10CF850066E64C /* objc-os.mm */, + 831C85D40E10CF850066E64C /* objc-os.mm */, 393CEABF0DC69E3E000B69DE /* objc-references.mm */, - 838485DF0D6D68A200CEA253 /* objc-rtp.m */, + 838485DF0D6D68A200CEA253 /* objc-rtp.mm */, 838485E10D6D68A200CEA253 /* objc-runtime-new.mm */, 838485E20D6D68A200CEA253 /* objc-runtime-old.m */, - 838485E40D6D68A200CEA253 /* objc-runtime.m */, - 838485E60D6D68A200CEA253 /* objc-sel-set.m */, + 838485E40D6D68A200CEA253 /* objc-runtime.mm */, + 838485E60D6D68A200CEA253 /* objc-sel-set.mm */, 83EB007A121C9EC200B92C16 /* objc-sel-table.s */, 838485E80D6D68A200CEA253 /* objc-sel.mm */, - 838485EA0D6D68A200CEA253 /* objc-sync.m */, - 838485EB0D6D68A200CEA253 /* objc-typeencoding.m */, + 838485EA0D6D68A200CEA253 /* objc-sync.mm */, + 838485EB0D6D68A200CEA253 /* objc-typeencoding.mm */, E8923D9C116AB2820071B552 /* a1a2-blocktramps-i386.s */, E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */, E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */, @@ -494,7 +376,6 @@ D2AAC0630554660B00DB518D /* libobjc.A.dylib */, 830F2AA90D7394D000392440 /* markgc */, 83E50D2A0FF19E8200D74C19 /* libobjc.A.dylib */, - 39023FCE12F09D0400E1426D /* libobjc.A.dylib */, ); name = Products; sourceTree = ""; @@ -533,7 +414,6 @@ 838485BB0D6D687300CEA253 /* maptable.h */, BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */, 834266D70E665A8B002E4DA2 /* objc-gdb.h */, - 83CDE7520E2E0040003703C1 /* objc-selopt.h */, ); name = "Private Headers"; sourceTree = ""; @@ -558,7 +438,6 @@ children = ( 838486230D6D68F000CEA253 /* List.m */, 838485EE0D6D68A200CEA253 /* Object.m */, - 838486190D6D68A800CEA253 /* Protocol.m */, ); name = "Obsolete Source"; sourceTree = ""; @@ -586,46 +465,6 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - 39023F7612F09D0400E1426D /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 39023F7712F09D0400E1426D /* hashtable.h in Headers */, - 39023F7812F09D0400E1426D /* hashtable2.h in Headers */, - 39023F7912F09D0400E1426D /* List.h in Headers */, - 39023F7A12F09D0400E1426D /* maptable.h in Headers */, - 39023F7B12F09D0400E1426D /* message.h in Headers */, - 39023F7C12F09D0400E1426D /* objc-abi.h in Headers */, - 39023F7D12F09D0400E1426D /* objc-accessors.h in Headers */, - 39023F7E12F09D0400E1426D /* objc-api.h in Headers */, - 39023F7F12F09D0400E1426D /* objc-auto-dump.h in Headers */, - 39023F8012F09D0400E1426D /* objc-auto.h in Headers */, - 39023F8112F09D0400E1426D /* objc-class.h in Headers */, - 39023F8212F09D0400E1426D /* objc-config.h in Headers */, - 39023F8312F09D0400E1426D /* objc-exception.h in Headers */, - 39023F8412F09D0400E1426D /* objc-file-old.h in Headers */, - 39023F8512F09D0400E1426D /* objc-file.h in Headers */, - 39023F8612F09D0400E1426D /* objc-gdb.h in Headers */, - 39023F8712F09D0400E1426D /* objc-initialize.h in Headers */, - 39023F8812F09D0400E1426D /* objc-internal.h in Headers */, - 39023F8912F09D0400E1426D /* objc-load.h in Headers */, - 39023F8A12F09D0400E1426D /* objc-loadmethod.h in Headers */, - 39023F8B12F09D0400E1426D /* objc-os.h in Headers */, - 39023F8C12F09D0400E1426D /* objc-private.h in Headers */, - 39023F8D12F09D0400E1426D /* objc-references.h in Headers */, - 39023F8E12F09D0400E1426D /* objc-runtime-new.h in Headers */, - 39023F8F12F09D0400E1426D /* objc-runtime-old.h in Headers */, - 39023F9012F09D0400E1426D /* objc-runtime.h in Headers */, - 39023F9112F09D0400E1426D /* objc-sel-set.h in Headers */, - 39023F9212F09D0400E1426D /* objc-sync.h in Headers */, - 39023F9312F09D0400E1426D /* objc.h in Headers */, - 39023F9412F09D0400E1426D /* Object.h in Headers */, - 39023F9512F09D0400E1426D /* Protocol.h in Headers */, - 39023F9612F09D0400E1426D /* runtime.h in Headers */, - 39ABD72112F0B61800D1054C /* objc-weak.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 83E50CDA0FF19E8200D74C19 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -709,26 +548,6 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 39023F7312F09D0400E1426D /* objc-device */ = { - isa = PBXNativeTarget; - buildConfigurationList = 39023FCB12F09D0400E1426D /* Build configuration list for PBXNativeTarget "objc-device" */; - buildPhases = ( - 39023F7612F09D0400E1426D /* Headers */, - 39023F9812F09D0400E1426D /* Sources */, - 39023FC712F09D0400E1426D /* Frameworks */, - 39023FC912F09D0400E1426D /* Run Script (markgc) */, - 39023FCA12F09D0400E1426D /* Run Script (symlink) */, - ); - buildRules = ( - ); - dependencies = ( - 39023F7412F09D0400E1426D /* PBXTargetDependency */, - ); - name = "objc-device"; - productName = objc; - productReference = 39023FCE12F09D0400E1426D /* libobjc.A.dylib */; - productType = "com.apple.product-type.library.dynamic"; - }; 830F2AA80D7394D000392440 /* markgc */ = { isa = PBXNativeTarget; buildConfigurationList = 830F2AAE0D7394D600392440 /* Build configuration list for PBXNativeTarget "markgc" */; @@ -790,9 +609,10 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = NO; + LastUpgradeCheck = 0440; }; buildConfigurationList = 1DEB914E08733D8E0010E9CD /* Build configuration list for PBXProject "objc" */; - compatibilityVersion = "Xcode 3.1"; + compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 1; knownRegions = ( @@ -808,42 +628,11 @@ D2AAC0620554660B00DB518D /* objc */, 830F2AA80D7394D000392440 /* markgc */, 83E50CD70FF19E8200D74C19 /* objc-simulator */, - 39023F7312F09D0400E1426D /* objc-device */, - 83E2799A117F76DF00452546 /* objc-selopt */, ); }; /* End PBXProject section */ /* Begin PBXShellScriptBuildPhase section */ - 39023FC912F09D0400E1426D /* Run Script (markgc) */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - comments = "Set the GC-supported bit in libobjc itself.\n\nlibobjc cannot be built with -fobjc-gc, because it needs more precise control over write-barrier use.\n\nThis is done on all architectures and platforms, even though some don't actually support GC. In those cases, a program that actually tries to use GC will fail with link errors."; - files = ( - ); - inputPaths = ( - ); - name = "Run Script (markgc)"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if [ ${NATIVE_ARCH} = ${NATIVE_ARCH_32_BIT} -o ${NATIVE_ARCH} = ${NATIVE_ARCH_64_BIT} -o ${NATIVE_ARCH} = ${NATIVE_ARCH_ACTUAL} ]; then\n \"${BUILT_PRODUCTS_DIR}/markgc\" -p \"${BUILT_PRODUCTS_DIR}/libobjc.A.dylib\"\nelse\n echo \"Skipping markgc for cross compile.\"\nfi"; - }; - 39023FCA12F09D0400E1426D /* Run Script (symlink) */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - name = "Run Script (symlink)"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "cd \"${INSTALL_DIR}\"\n/bin/ln -s libobjc.A.dylib libobjc.dylib\n"; - }; 830F2AB60D739AB600392440 /* Run Script (markgc) */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -890,60 +679,6 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 39023F9812F09D0400E1426D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 39023F9912F09D0400E1426D /* hashtable2.m in Sources */, - 39023F9A12F09D0400E1426D /* maptable.m in Sources */, - 39023F9B12F09D0400E1426D /* objc-auto.m in Sources */, - 39023F9C12F09D0400E1426D /* objc-cache.m in Sources */, - 39023F9D12F09D0400E1426D /* objc-class-old.m in Sources */, - 39023F9E12F09D0400E1426D /* objc-class.m in Sources */, - 39023F9F12F09D0400E1426D /* objc-errors.m in Sources */, - 39023FA012F09D0400E1426D /* objc-exception.m in Sources */, - 39023FA112F09D0400E1426D /* objc-file.mm in Sources */, - 39023FA212F09D0400E1426D /* objc-initialize.m in Sources */, - 39023FA312F09D0400E1426D /* objc-layout.m in Sources */, - 39023FA412F09D0400E1426D /* objc-load.m in Sources */, - 39023FA512F09D0400E1426D /* objc-loadmethod.m in Sources */, - 39023FA612F09D0400E1426D /* objc-lockdebug.m in Sources */, - 39023FA712F09D0400E1426D /* objc-rtp.m in Sources */, - 39023FA812F09D0400E1426D /* objc-runtime-new.mm in Sources */, - 39023FA912F09D0400E1426D /* objc-runtime-old.m in Sources */, - 39023FAA12F09D0400E1426D /* objc-runtime.m in Sources */, - 39023FAB12F09D0400E1426D /* objc-sel-set.m in Sources */, - 39023FAC12F09D0400E1426D /* objc-sel.mm in Sources */, - 39023FAD12F09D0400E1426D /* objc-sync.m in Sources */, - 39023FAE12F09D0400E1426D /* objc-typeencoding.m in Sources */, - 39023FAF12F09D0400E1426D /* Object.m in Sources */, - 39023FB012F09D0400E1426D /* Protocol.m in Sources */, - 39023FB112F09D0400E1426D /* List.m in Sources */, - 39023FB212F09D0400E1426D /* objc-msg-arm.s in Sources */, - 39023FB312F09D0400E1426D /* objc-msg-i386.s in Sources */, - 39023FB412F09D0400E1426D /* objc-msg-x86_64.s in Sources */, - 39023FB512F09D0400E1426D /* objc-auto-i386.s in Sources */, - 39023FB612F09D0400E1426D /* objc-accessors.m in Sources */, - 39023FB712F09D0400E1426D /* objc-references.mm in Sources */, - 39023FB812F09D0400E1426D /* objc-os.m in Sources */, - 39023FB912F09D0400E1426D /* objc-probes.d in Sources */, - 39023FBA12F09D0400E1426D /* objc-auto-dump.m in Sources */, - 39023FBB12F09D0400E1426D /* objc-file-old.m in Sources */, - 39023FBC12F09D0400E1426D /* a1a2-blocktramps-i386.s in Sources */, - 39023FBD12F09D0400E1426D /* a1a2-blocktramps-x86_64.s in Sources */, - 39023FBE12F09D0400E1426D /* a2a3-blocktramps-i386.s in Sources */, - 39023FBF12F09D0400E1426D /* a2a3-blocktramps-x86_64.s in Sources */, - 39023FC012F09D0400E1426D /* objc-block-trampolines.m in Sources */, - 39023FC112F09D0400E1426D /* objc-msg-simulator-i386.s in Sources */, - 39023FC212F09D0400E1426D /* objc-sel-table.s in Sources */, - 39023FC312F09D0400E1426D /* a1a2-blocktramps-arm.s in Sources */, - 39023FC412F09D0400E1426D /* a2a3-blocktramps-arm.s in Sources */, - 39023FC512F09D0400E1426D /* objc-externalref.m in Sources */, - 39ABD72212F0B61800D1054C /* objc-weak.mm in Sources */, - 552B2FB912F9D31E00650C0D /* objc-arr.mm in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 830F2AA60D7394D000392440 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -957,39 +692,39 @@ buildActionMask = 2147483647; files = ( 83E50D130FF19E8200D74C19 /* Object.m in Sources */, - 83E50D140FF19E8200D74C19 /* Protocol.m in Sources */, + 83E50D140FF19E8200D74C19 /* Protocol.mm in Sources */, 83E50D150FF19E8200D74C19 /* List.m in Sources */, 8383A3AC122600FB009290B8 /* a1a2-blocktramps-arm.s in Sources */, 8383A3AD122600FB009290B8 /* a2a3-blocktramps-arm.s in Sources */, - 8383A3AE122600FB009290B8 /* hashtable2.m in Sources */, - 8383A3AF122600FB009290B8 /* maptable.m in Sources */, - 8383A3B0122600FB009290B8 /* objc-accessors.m in Sources */, + 8383A3AE122600FB009290B8 /* hashtable2.mm in Sources */, + 8383A3AF122600FB009290B8 /* maptable.mm in Sources */, + 8383A3B0122600FB009290B8 /* objc-accessors.mm in Sources */, 8383A3B1122600FB009290B8 /* objc-auto.m in Sources */, 8383A3B2122600FB009290B8 /* objc-auto-dump.m in Sources */, - 8383A3B3122600FB009290B8 /* objc-block-trampolines.m in Sources */, - 8383A3B4122600FB009290B8 /* objc-cache.m in Sources */, + 8383A3B3122600FB009290B8 /* objc-block-trampolines.mm in Sources */, + 8383A3B4122600FB009290B8 /* objc-cache.mm in Sources */, 8383A3B5122600FB009290B8 /* objc-class-old.m in Sources */, - 8383A3B6122600FB009290B8 /* objc-class.m in Sources */, - 8383A3B7122600FB009290B8 /* objc-errors.m in Sources */, - 8383A3B8122600FB009290B8 /* objc-exception.m in Sources */, + 8383A3B6122600FB009290B8 /* objc-class.mm in Sources */, + 8383A3B7122600FB009290B8 /* objc-errors.mm in Sources */, + 8383A3B8122600FB009290B8 /* objc-exception.mm in Sources */, 8383A3B9122600FB009290B8 /* objc-file.mm in Sources */, 8383A3BA122600FB009290B8 /* objc-file-old.m in Sources */, - 8383A3BB122600FB009290B8 /* objc-initialize.m in Sources */, - 8383A3BC122600FB009290B8 /* objc-layout.m in Sources */, - 8383A3BD122600FB009290B8 /* objc-load.m in Sources */, - 8383A3BE122600FB009290B8 /* objc-loadmethod.m in Sources */, - 8383A3BF122600FB009290B8 /* objc-lockdebug.m in Sources */, - 8383A3C0122600FB009290B8 /* objc-os.m in Sources */, + 8383A3BB122600FB009290B8 /* objc-initialize.mm in Sources */, + 8383A3BC122600FB009290B8 /* objc-layout.mm in Sources */, + 8383A3BD122600FB009290B8 /* objc-load.mm in Sources */, + 8383A3BE122600FB009290B8 /* objc-loadmethod.mm in Sources */, + 8383A3BF122600FB009290B8 /* objc-lockdebug.mm in Sources */, + 8383A3C0122600FB009290B8 /* objc-os.mm in Sources */, 8383A3C1122600FB009290B8 /* objc-references.mm in Sources */, - 8383A3C2122600FB009290B8 /* objc-rtp.m in Sources */, + 8383A3C2122600FB009290B8 /* objc-rtp.mm in Sources */, 8383A3C3122600FB009290B8 /* objc-runtime-new.mm in Sources */, 8383A3C4122600FB009290B8 /* objc-runtime-old.m in Sources */, - 8383A3C5122600FB009290B8 /* objc-runtime.m in Sources */, - 8383A3C6122600FB009290B8 /* objc-sel-set.m in Sources */, + 8383A3C5122600FB009290B8 /* objc-runtime.mm in Sources */, + 8383A3C6122600FB009290B8 /* objc-sel-set.mm in Sources */, 8383A3C7122600FB009290B8 /* objc-sel-table.s in Sources */, 8383A3C8122600FB009290B8 /* objc-sel.mm in Sources */, - 8383A3C9122600FB009290B8 /* objc-sync.m in Sources */, - 8383A3CA122600FB009290B8 /* objc-typeencoding.m in Sources */, + 8383A3C9122600FB009290B8 /* objc-sync.mm in Sources */, + 8383A3CA122600FB009290B8 /* objc-typeencoding.mm in Sources */, 8383A3CB122600FB009290B8 /* a1a2-blocktramps-i386.s in Sources */, 8383A3CC122600FB009290B8 /* a1a2-blocktramps-x86_64.s in Sources */, 8383A3CD122600FB009290B8 /* a2a3-blocktramps-i386.s in Sources */, @@ -1000,9 +735,10 @@ 8383A3D2122600FB009290B8 /* objc-msg-simulator-i386.s in Sources */, 8383A3D3122600FB009290B8 /* objc-msg-x86_64.s in Sources */, 8383A3D4122600FB009290B8 /* objc-probes.d in Sources */, - 8383A3DC1226291C009290B8 /* objc-externalref.m in Sources */, + 8383A3DC1226291C009290B8 /* objc-externalref.mm in Sources */, 39ABD72612F0B61800D1054C /* objc-weak.mm in Sources */, - 552B2FB812F9D31E00650C0D /* objc-arr.mm in Sources */, + 9672F7EF14D5F488007CEC96 /* NSObject.mm in Sources */, + 83725F4C14CA5C210014370E /* objc-opt.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1010,38 +746,38 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 838485C00D6D687300CEA253 /* hashtable2.m in Sources */, - 838485C40D6D687300CEA253 /* maptable.m in Sources */, + 838485C00D6D687300CEA253 /* hashtable2.mm in Sources */, + 838485C40D6D687300CEA253 /* maptable.mm in Sources */, 838485F10D6D68A200CEA253 /* objc-auto.m in Sources */, - 838485F20D6D68A200CEA253 /* objc-cache.m in Sources */, + 838485F20D6D68A200CEA253 /* objc-cache.mm in Sources */, 838485F30D6D68A200CEA253 /* objc-class-old.m in Sources */, - 838485F50D6D68A200CEA253 /* objc-class.m in Sources */, - 838485F70D6D68A200CEA253 /* objc-errors.m in Sources */, - 838485F90D6D68A200CEA253 /* objc-exception.m in Sources */, + 838485F50D6D68A200CEA253 /* objc-class.mm in Sources */, + 838485F70D6D68A200CEA253 /* objc-errors.mm in Sources */, + 838485F90D6D68A200CEA253 /* objc-exception.mm in Sources */, 838485FA0D6D68A200CEA253 /* objc-file.mm in Sources */, - 838485FC0D6D68A200CEA253 /* objc-initialize.m in Sources */, - 838485FD0D6D68A200CEA253 /* objc-layout.m in Sources */, - 838485FF0D6D68A200CEA253 /* objc-load.m in Sources */, - 838486010D6D68A200CEA253 /* objc-loadmethod.m in Sources */, - 838486020D6D68A200CEA253 /* objc-lockdebug.m in Sources */, - 838486060D6D68A200CEA253 /* objc-rtp.m in Sources */, + 838485FC0D6D68A200CEA253 /* objc-initialize.mm in Sources */, + 838485FD0D6D68A200CEA253 /* objc-layout.mm in Sources */, + 838485FF0D6D68A200CEA253 /* objc-load.mm in Sources */, + 838486010D6D68A200CEA253 /* objc-loadmethod.mm in Sources */, + 838486020D6D68A200CEA253 /* objc-lockdebug.mm in Sources */, + 838486060D6D68A200CEA253 /* objc-rtp.mm in Sources */, 838486080D6D68A200CEA253 /* objc-runtime-new.mm in Sources */, 838486090D6D68A200CEA253 /* objc-runtime-old.m in Sources */, - 8384860B0D6D68A200CEA253 /* objc-runtime.m in Sources */, - 8384860D0D6D68A200CEA253 /* objc-sel-set.m in Sources */, + 8384860B0D6D68A200CEA253 /* objc-runtime.mm in Sources */, + 8384860D0D6D68A200CEA253 /* objc-sel-set.mm in Sources */, 8384860F0D6D68A200CEA253 /* objc-sel.mm in Sources */, - 838486110D6D68A200CEA253 /* objc-sync.m in Sources */, - 838486120D6D68A200CEA253 /* objc-typeencoding.m in Sources */, + 838486110D6D68A200CEA253 /* objc-sync.mm in Sources */, + 838486120D6D68A200CEA253 /* objc-typeencoding.mm in Sources */, 838486150D6D68A200CEA253 /* Object.m in Sources */, - 8384861F0D6D68A800CEA253 /* Protocol.m in Sources */, + 8384861F0D6D68A800CEA253 /* Protocol.mm in Sources */, 838486250D6D68F000CEA253 /* List.m in Sources */, 830F2A740D737FB800392440 /* objc-msg-arm.s in Sources */, 830F2A750D737FB900392440 /* objc-msg-i386.s in Sources */, 830F2A7D0D737FBB00392440 /* objc-msg-x86_64.s in Sources */, 830F2A890D73819A00392440 /* objc-auto-i386.s in Sources */, - 830F2A950D73876100392440 /* objc-accessors.m in Sources */, + 830F2A950D73876100392440 /* objc-accessors.mm in Sources */, 393CEAC00DC69E3E000B69DE /* objc-references.mm in Sources */, - 831C85D60E10CF850066E64C /* objc-os.m in Sources */, + 831C85D60E10CF850066E64C /* objc-os.mm in Sources */, 87BB4EA70EC39854005D08E1 /* objc-probes.d in Sources */, BC07A0110EF72D9C0014EC61 /* objc-auto-dump.m in Sources */, 83BE02E40FCCB23400661494 /* objc-file-old.m in Sources */, @@ -1049,25 +785,21 @@ E8923DA2116AB2820071B552 /* a1a2-blocktramps-x86_64.s in Sources */, E8923DA3116AB2820071B552 /* a2a3-blocktramps-i386.s in Sources */, E8923DA4116AB2820071B552 /* a2a3-blocktramps-x86_64.s in Sources */, - E8923DA5116AB2820071B552 /* objc-block-trampolines.m in Sources */, + E8923DA5116AB2820071B552 /* objc-block-trampolines.mm in Sources */, 83B1A8BE0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s in Sources */, 83EB007B121C9EC200B92C16 /* objc-sel-table.s in Sources */, 8383A3A3122600E9009290B8 /* a1a2-blocktramps-arm.s in Sources */, 8383A3A4122600E9009290B8 /* a2a3-blocktramps-arm.s in Sources */, - 399BC72E1224831B007FBDF0 /* objc-externalref.m in Sources */, + 399BC72E1224831B007FBDF0 /* objc-externalref.mm in Sources */, 39ABD72412F0B61800D1054C /* objc-weak.mm in Sources */, - 552B2FB712F9D31E00650C0D /* objc-arr.mm in Sources */, + 9672F7EE14D5F488007CEC96 /* NSObject.mm in Sources */, + 83725F4A14CA5BFA0014370E /* objc-opt.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 39023F7412F09D0400E1426D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 830F2AA80D7394D000392440 /* markgc */; - targetProxy = 39023F7512F09D0400E1426D /* PBXContainerItemProxy */; - }; 835720F60F8BF8EE00BD4FAD /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 830F2AA80D7394D000392440 /* markgc */; @@ -1097,13 +829,15 @@ ); INSTALL_PATH = /usr/lib; ORDER_FILE = libobjc.order; - OTHER_CFLAGS = "-fdollars-in-identifiers"; - "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = "-lstdc++"; + OTHER_CFLAGS = ( + "-fdollars-in-identifiers", + "$(OTHER_CFLAGS)", + ); + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = "-lc++abi"; "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "-l_BUILD_objc-simulator_TARGET_INSTEAD"; - "OTHER_LDFLAGS[sdk=macosx10.7]" = ( + "OTHER_LDFLAGS[sdk=macosx*]" = ( "-lCrashReporterClient", "-lauto", - "-lc++", "-lc++abi", ); PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc; @@ -1129,13 +863,15 @@ ); INSTALL_PATH = /usr/lib; ORDER_FILE = libobjc.order; - OTHER_CFLAGS = "-fdollars-in-identifiers"; - "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = "-lstdc++"; + OTHER_CFLAGS = ( + "-fdollars-in-identifiers", + "$(OTHER_CFLAGS)", + ); + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = "-lc++abi"; "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "-l_BUILD_objc-simulator_TARGET_INSTEAD"; - "OTHER_LDFLAGS[sdk=macosx10.7]" = ( + "OTHER_LDFLAGS[sdk=macosx*]" = ( "-lCrashReporterClient", "-lauto", - "-lc++", "-lc++abi", ); PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc; @@ -1148,26 +884,33 @@ 1DEB914F08733D8E0010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; + "CLANG_CXX_LIBRARY[sdk=iphoneos*]" = "libstdc++"; + "CLANG_CXX_LIBRARY[sdk=iphonesimulator*]" = "libstdc++"; + CLANG_OBJC_RUNTIME = NO; + CLANG_LINK_OBJC_RUNTIME = NO; DEBUG_INFORMATION_FORMAT = dwarf; GCC_ENABLE_CPP_EXCEPTIONS = NO; GCC_ENABLE_CPP_RTTI = NO; GCC_INLINES_ARE_PRIVATE_EXTERN = YES; GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = "OS_OBJECT_USE_OBJC=0"; + GCC_STRICT_ALIASING = YES; GCC_SYMBOLS_PRIVATE_EXTERN = YES; - "GCC_VERSION[sdk=macosx10.7][arch=*]" = com.apple.compilers.llvm.clang.1_0; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_SHADOW = YES; GCC_WARN_UNUSED_VARIABLE = YES; - "OTHER_CPLUSPLUSFLAGS[sdk=macosx10.7][arch=*]" = ( - "-stdlib=libc++", - "$(OTHER_CFLAGS)", - ); - PREBINDING = NO; STANDARD_C_PLUS_PLUS_LIBRARY_TYPE = dynamic; - VALID_ARCHS = "armv6 armv7 i386 x86_64"; WARNING_CFLAGS = ( "-Wall", - "-Wno-four-char-constants", + "-Wextra", + "-Wstrict-aliasing=2", + "-Wstrict-overflow=4", + "-Wno-unused-parameter", + "-Wno-deprecated-objc-isa-usage", ); }; name = Debug; @@ -1175,100 +918,41 @@ 1DEB915008733D8E0010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; + "CLANG_CXX_LIBRARY[sdk=iphoneos*]" = "libstdc++"; + "CLANG_CXX_LIBRARY[sdk=iphonesimulator*]" = "libstdc++"; + CLANG_OBJC_RUNTIME = NO; + CLANG_LINK_OBJC_RUNTIME = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_ENABLE_CPP_EXCEPTIONS = NO; GCC_ENABLE_CPP_RTTI = NO; GCC_INLINES_ARE_PRIVATE_EXTERN = YES; - GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + GCC_PREPROCESSOR_DEFINITIONS = ( + "OS_OBJECT_USE_OBJC=0", + "NDEBUG=1", + ); + GCC_STRICT_ALIASING = YES; GCC_SYMBOLS_PRIVATE_EXTERN = YES; - "GCC_VERSION[sdk=macosx10.7][arch=*]" = com.apple.compilers.llvm.clang.1_0; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_SHADOW = YES; GCC_WARN_UNUSED_VARIABLE = YES; - "OTHER_CPLUSPLUSFLAGS[sdk=macosx10.7][arch=*]" = ( - "-stdlib=libc++", - "$(OTHER_CFLAGS)", - ); - PREBINDING = NO; + "OTHER_CFLAGS[arch=i386]" = "-momit-leaf-frame-pointer"; + "OTHER_CFLAGS[arch=x86_64]" = "-momit-leaf-frame-pointer"; STANDARD_C_PLUS_PLUS_LIBRARY_TYPE = dynamic; - VALID_ARCHS = "armv6 armv7 i386 x86_64"; WARNING_CFLAGS = ( "-Wall", - "-Wno-four-char-constants", + "-Wextra", + "-Wstrict-aliasing=2", + "-Wstrict-overflow=4", + "-Wno-unused-parameter", + "-Wno-deprecated-objc-isa-usage", ); }; name = Release; }; - 39023FCC12F09D0400E1426D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; - COPY_PHASE_STRIP = NO; - DYLIB_CURRENT_VERSION = 227; - EXECUTABLE_PREFIX = lib; - GCC_CW_ASM_SYNTAX = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_THREADSAFE_STATICS = NO; - GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; - HEADER_SEARCH_PATHS = ( - "$(DSTROOT)/usr/include/**", - "$(DSTROOT)/usr/local/include/**", - "$(CONFIGURATION_BUILD_DIR)/usr/include/**", - "$(CONFIGURATION_BUILD_DIR)/usr/local/include/**", - ); - INSTALL_PATH = /usr/lib; - ORDER_FILE = libobjc.order; - OTHER_CFLAGS = "-fdollars-in-identifiers"; - "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = "-lstdc++"; - "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "-l_BUILD_objc-simulator_TARGET_INSTEAD"; - "OTHER_LDFLAGS[sdk=macosx10.7]" = ( - "-lCrashReporterClient", - "-lauto", - "-lc++", - "-lc++abi", - ); - PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc; - PRODUCT_NAME = objc.A; - PUBLIC_HEADERS_FOLDER_PATH = /usr/include/objc; - SDKROOT = iphoneos5.0.internal; - UNEXPORTED_SYMBOLS_FILE = unexported_symbols; - }; - name = Debug; - }; - 39023FCD12F09D0400E1426D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - DYLIB_CURRENT_VERSION = 227; - EXECUTABLE_PREFIX = lib; - GCC_CW_ASM_SYNTAX = NO; - GCC_THREADSAFE_STATICS = NO; - GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; - HEADER_SEARCH_PATHS = ( - "$(DSTROOT)/usr/include/**", - "$(DSTROOT)/usr/local/include/**", - "$(CONFIGURATION_BUILD_DIR)/usr/include/**", - "$(CONFIGURATION_BUILD_DIR)/usr/local/include/**", - ); - INSTALL_PATH = /usr/lib; - ORDER_FILE = libobjc.order; - OTHER_CFLAGS = "-fdollars-in-identifiers"; - "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = "-lstdc++"; - "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "-l_BUILD_objc-simulator_TARGET_INSTEAD"; - "OTHER_LDFLAGS[sdk=macosx10.7]" = ( - "-lCrashReporterClient", - "-lauto", - "-lc++", - "-lc++abi", - ); - PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc; - PRODUCT_NAME = objc.A; - PUBLIC_HEADERS_FOLDER_PATH = /usr/include/objc; - UNEXPORTED_SYMBOLS_FILE = unexported_symbols; - }; - name = Release; - }; 830F2AAB0D7394D100392440 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1276,11 +960,9 @@ COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; INSTALL_PATH = /usr/local/bin; - PREBINDING = NO; PRODUCT_NAME = markgc; SKIP_INSTALL = YES; }; @@ -1294,34 +976,10 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_MODEL_TUNING = G5; INSTALL_PATH = /usr/local/bin; - PREBINDING = NO; PRODUCT_NAME = markgc; SKIP_INSTALL = YES; - ZERO_LINK = NO; - }; - name = Release; - }; - 83E2799B117F76E000452546 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - PRODUCT_NAME = "objc-selopt"; - }; - name = Debug; - }; - 83E2799C117F76E000452546 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - PRODUCT_NAME = "objc-selopt"; - ZERO_LINK = NO; }; name = Release; }; @@ -1350,16 +1008,11 @@ "-fobjc-abi-version=2", "-fdollars-in-identifiers", ); - OTHER_LDFLAGS = "-lstdc++"; - "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = ( - "-lc++", - "-lc++abi", - ); + OTHER_LDFLAGS = "-lc++abi"; PRIVATE_HEADERS_FOLDER_PATH = "$(INDIGO_INSTALL_PATH_PREFIX)/usr/local/include/objc"; PRODUCT_NAME = objc.A; PUBLIC_HEADERS_FOLDER_PATH = "$(INDIGO_INSTALL_PATH_PREFIX)/usr/include/objc"; UNEXPORTED_SYMBOLS_FILE = unexported_symbols; - VALID_ARCHS = i386; }; name = Debug; }; @@ -1386,16 +1039,11 @@ "-fobjc-abi-version=2", "-fdollars-in-identifiers", ); - OTHER_LDFLAGS = "-lstdc++"; - "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = ( - "-lc++", - "-lc++abi", - ); + OTHER_LDFLAGS = "-lc++abi"; PRIVATE_HEADERS_FOLDER_PATH = "$(INDIGO_INSTALL_PATH_PREFIX)/usr/local/include/objc"; PRODUCT_NAME = objc.A; PUBLIC_HEADERS_FOLDER_PATH = "$(INDIGO_INSTALL_PATH_PREFIX)/usr/include/objc"; UNEXPORTED_SYMBOLS_FILE = unexported_symbols; - VALID_ARCHS = i386; }; name = Release; }; @@ -1420,15 +1068,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 39023FCB12F09D0400E1426D /* Build configuration list for PBXNativeTarget "objc-device" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 39023FCC12F09D0400E1426D /* Debug */, - 39023FCD12F09D0400E1426D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 830F2AAE0D7394D600392440 /* Build configuration list for PBXNativeTarget "markgc" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -1438,15 +1077,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 83E2799E117F770D00452546 /* Build configuration list for PBXAggregateTarget "objc-selopt" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 83E2799B117F76E000452546 /* Debug */, - 83E2799C117F76E000452546 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 83E50D270FF19E8200D74C19 /* Build configuration list for PBXNativeTarget "objc-simulator" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/runtime/Accessors.subproj/objc-accessors.h b/runtime/Accessors.subproj/objc-accessors.h index 059461a..b842555 100644 --- a/runtime/Accessors.subproj/objc-accessors.h +++ b/runtime/Accessors.subproj/objc-accessors.h @@ -24,39 +24,18 @@ #ifndef _OBJC_ACCESSORS_H_ #define _OBJC_ACCESSORS_H_ -#import -#import +#include +#include __BEGIN_DECLS -// Called under non-GC for retain or copy attributed properties -void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy); -id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic); - // GC-specific accessors. -void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy); -id objc_getProperty_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic); +extern void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy); +extern id objc_getProperty_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic); // Non-GC accessors. -void objc_setProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy); -id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic); - -// Called under GC by compiler for copying structures containing objects or other strong pointers when -// the destination memory is not known to be stack local memory. -// Called to read instance variable structures (or other non-word sized entities) atomically -void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong); - -// OBSOLETE - -@protocol NSCopying; - -// called for @property(copy) -id object_getProperty_bycopy(id object, SEL _cmd, ptrdiff_t offset); -void object_setProperty_bycopy(id object, SEL _cmd, id value, ptrdiff_t offset); - -// called for @property(retain) -id object_getProperty_byref(id object, SEL _cmd, ptrdiff_t offset); -void object_setProperty_byref(id object, SEL _cmd, id value, ptrdiff_t offset); +extern void objc_setProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy); +extern id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic); __END_DECLS diff --git a/runtime/Accessors.subproj/objc-accessors.m b/runtime/Accessors.subproj/objc-accessors.mm similarity index 56% rename from runtime/Accessors.subproj/objc-accessors.m rename to runtime/Accessors.subproj/objc-accessors.mm index d324dbe..040c1f0 100644 --- a/runtime/Accessors.subproj/objc-accessors.m +++ b/runtime/Accessors.subproj/objc-accessors.mm @@ -21,15 +21,15 @@ * @APPLE_LICENSE_HEADER_END@ */ -#import -#import +#include +#include -#import +#include -#import "objc-private.h" -#import "objc-auto.h" -#import "runtime.h" -#import "objc-accessors.h" +#include "objc-private.h" +#include "objc-auto.h" +#include "runtime.h" +#include "objc-accessors.h" // stub interface declarations to make compiler happy. @@ -43,9 +43,9 @@ typedef uintptr_t spin_lock_t; -extern void _spin_lock(spin_lock_t *lockp); -extern int _spin_lock_try(spin_lock_t *lockp); -extern void _spin_unlock(spin_lock_t *lockp); +OBJC_EXTERN void _spin_lock(spin_lock_t *lockp); +OBJC_EXTERN int _spin_lock_try(spin_lock_t *lockp); +OBJC_EXTERN void _spin_unlock(spin_lock_t *lockp); /* need to consider cache line contention - space locks out XXX */ @@ -54,11 +54,13 @@ extern void _spin_unlock(spin_lock_t *lockp); #define GOODHASH(x) (((long)x >> 5) & GOODMASK) static spin_lock_t PropertyLocks[1 << GOODPOWER] = { 0 }; -PRIVATE_EXTERN id objc_getProperty_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) { +#define MUTABLE_COPY 2 + +id objc_getProperty_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) { return *(id*) ((char*)self + offset); } -PRIVATE_EXTERN id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) { +id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) { // Retain release world id *slot = (id*) ((char*)self + offset); if (!atomic) return *slot; @@ -83,27 +85,28 @@ id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) { (self, _cmd, offset, atomic); } -enum { OBJC_PROPERTY_RETAIN = 0, OBJC_PROPERTY_COPY = 1, OBJC_PROPERTY_MUTABLECOPY = 2 }; - #if SUPPORT_GC -PRIVATE_EXTERN void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) { +void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) { if (shouldCopy) { - newValue = (shouldCopy == OBJC_PROPERTY_MUTABLECOPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]); + newValue = (shouldCopy == MUTABLE_COPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]); } objc_assign_ivar_gc(newValue, self, offset); } #endif -PRIVATE_EXTERN void objc_setProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) { - // Retain release world - id oldValue, *slot = (id*) ((char*)self + offset); +static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) __attribute__((always_inline)); - // atomic or not, if slot would be unchanged, do nothing. - if (!shouldCopy && *slot == newValue) return; - - if (shouldCopy) { - newValue = (shouldCopy == OBJC_PROPERTY_MUTABLECOPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]); +static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) +{ + id oldValue; + id *slot = (id*) ((char*)self + offset); + + if (copy) { + newValue = [newValue copyWithZone:NULL]; + } else if (mutableCopy) { + newValue = [newValue mutableCopyWithZone:NULL]; } else { + if (*slot == newValue) return; newValue = objc_retain(newValue); } @@ -115,13 +118,42 @@ PRIVATE_EXTERN void objc_setProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, _spin_lock(slotlock); oldValue = *slot; *slot = newValue; - _spin_unlock(slotlock); + _spin_unlock(slotlock); } objc_release(oldValue); } -void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) { +void objc_setProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) +{ + bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY); + bool mutableCopy = (shouldCopy == MUTABLE_COPY); + reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy); +} + +void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset) +{ + reallySetProperty(self, _cmd, newValue, offset, true, false, false); +} + +void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset) +{ + reallySetProperty(self, _cmd, newValue, offset, false, false, false); +} + + +void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) +{ + reallySetProperty(self, _cmd, newValue, offset, true, true, false); +} + +void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) +{ + reallySetProperty(self, _cmd, newValue, offset, false, true, false); +} + + +void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) { #if SUPPORT_GC (UseGC ? objc_setProperty_gc : objc_setProperty_non_gc) #else @@ -167,3 +199,24 @@ void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, B } } +void objc_copyCppObjectAtomic(void *dest, const void *src, void (*copyHelper) (void *dest, const void *source)) { + static spin_lock_t CppObjectLocks[1 << GOODPOWER] = { 0 }; + spin_lock_t *lockfirst = &CppObjectLocks[GOODHASH(src)], *locksecond = &CppObjectLocks[GOODHASH(dest)]; + // order the locks by address so that we don't deadlock + if (lockfirst > locksecond) { + spin_lock_t *temp = lockfirst; + lockfirst = locksecond; + locksecond = temp; + } else if (lockfirst == locksecond) { + // lucky - we only need one lock + locksecond = NULL; + } + _spin_lock(lockfirst); + if (locksecond) _spin_lock(locksecond); + + // let C++ code perform the actual copy. + copyHelper(dest, src); + + _spin_unlock(lockfirst); + if (locksecond) _spin_unlock(locksecond); +} diff --git a/runtime/Messengers.subproj/objc-msg-arm.s b/runtime/Messengers.subproj/objc-msg-arm.s index c6b165d..eac3b2c 100644 --- a/runtime/Messengers.subproj/objc-msg-arm.s +++ b/runtime/Messengers.subproj/objc-msg-arm.s @@ -61,9 +61,10 @@ L ## var ## __non_lazy_ptr: ;\ #if defined(__DYNAMIC__) && defined(THUMB) #define MI_GET_ADDRESS(reg,var) \ ldr reg, 4f ;\ -3: add reg, pc, reg ;\ +3: add reg, pc ;\ ldr reg, [reg] ;\ b 5f ;\ + .align 2 ;\ 4: .long L ## var ## __non_lazy_ptr - (3b + 4) ;\ 5: #elif defined(__DYNAMIC__) @@ -71,12 +72,14 @@ L ## var ## __non_lazy_ptr: ;\ ldr reg, 4f ;\ 3: ldr reg, [pc, reg] ;\ b 5f ;\ + .align 2 ;\ 4: .long L ## var ## __non_lazy_ptr - (3b + 8) ;\ 5: #else #define MI_GET_ADDRESS(reg,var) \ ldr reg, 3f ;\ b 4f ;\ + .align 2 ;\ 3: .long var ;\ 4: #endif @@ -106,7 +109,7 @@ L ## var ## __non_lazy_ptr: ;\ #endif -MI_EXTERN(__class_lookupMethodAndLoadCache) +MI_EXTERN(__class_lookupMethodAndLoadCache3) MI_EXTERN(_FwdSel) MI_EXTERN(___objc_error) MI_EXTERN(__objc_forward_handler) @@ -194,7 +197,7 @@ _objc_exitPoints: #ifdef THUMB .thumb #endif - .align 2 + .align 5 .globl _$0 #ifdef THUMB .thumb_func @@ -207,7 +210,7 @@ _$0: #ifdef THUMB .thumb #endif - .align 2 + .align 5 .private_extern _$0 #ifdef THUMB .thumb_func @@ -356,9 +359,7 @@ LGetImpExit: ENTRY objc_msgSend # check whether receiver is nil teq a1, #0 - itt eq - moveq a2, #0 - bxeq lr + beq LMsgSendNilReceiver # save registers and load receiver's class for CacheLookup stmfd sp!, {a4,v1} @@ -376,6 +377,10 @@ LMsgSendCacheMiss: ldmfd sp!, {a4,v1} b _objc_msgSend_uncached +LMsgSendNilReceiver: + mov a2, #0 + bx lr + LMsgSendExit: END_ENTRY objc_msgSend @@ -387,11 +392,12 @@ LMsgSendExit: add r7, sp, #16 # Load class and selector - ldr a1, [a1, #ISA] /* class = receiver->isa */ - # MOVE a2, a2 /* selector already in a2 */ + ldr a3, [a1, #ISA] /* class = receiver->isa */ + /* selector already in a2 */ + /* receiver already in a1 */ # Do the lookup - MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache) + MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache3) MOVE ip, a1 # Prep for forwarding, Pop stack frame and call imp @@ -409,9 +415,7 @@ LMsgSendExit: ENTRY objc_msgSend_noarg # check whether receiver is nil teq a1, #0 - itt eq - moveq a2, #0 - bxeq lr + beq LMsgSendNilReceiver # load receiver's class for CacheLookup ldr a3, [a1, #ISA] @@ -478,11 +482,12 @@ LMsgSendStretExit: add r7, sp, #16 # Load class and selector - ldr a1, [a2, #ISA] /* class = receiver->isa */ + MOVE a1, a2 /* receiver */ MOVE a2, a3 /* selector */ + ldr a3, [a1, #ISA] /* class = receiver->isa */ # Do the lookup - MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache) + MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache3) MOVE ip, a1 # Prep for forwarding, pop stack frame and call imp @@ -533,11 +538,12 @@ LMsgSendSuperExit: add r7, sp, #16 # Load class and selector - ldr a1, [a1, #CLASS] /* class = super->class */ - # MOVE a2, a2 /* selector already in a2 */ + ldr a3, [a1, #CLASS] /* class = super->class */ + /* selector already in a2 */ + ldr a1, [a1, #RECEIVER] /* receiver = super->receiver */ # Do the lookup - MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache) + MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache3) MOVE ip, a1 # Prep for forwarding, pop stack frame and call imp @@ -582,12 +588,13 @@ LMsgSendSuper2Exit: add r7, sp, #16 # Load class and selector - ldr a1, [a1, #CLASS] /* class = super->class */ - ldr a1, [a1, #SUPERCLASS] /* class = class->superclass */ - # MOVE a2, a2 /* selector already in a2 */ + ldr a3, [a1, #CLASS] /* class = super->class */ + ldr a3, [a3, #SUPERCLASS] /* class = class->superclass */ + /* selector already in a2 */ + ldr a1, [a1, #RECEIVER] /* receiver = super->receiver */ # Do the lookup - MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache) + MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache3) MOVE ip, a1 # Prep for forwarding, pop stack frame and call imp @@ -648,11 +655,13 @@ LMsgSendSuperStretExit: add r7, sp, #16 # Load class and selector - ldr a1, [a2, #CLASS] /* class = super->class */ + MOVE a1, a2 /* struct super */ MOVE a2, a3 /* selector */ + ldr a3, [a1, #CLASS] /* class = super->class */ + ldr a1, [a1, #RECEIVER] /* receiver = super->receiver */ # Do the lookup - MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache) + MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache3) MOVE ip, a1 # Prep for forwarding, pop stack frame and call imp @@ -699,12 +708,14 @@ LMsgSendSuper2StretExit: add r7, sp, #16 # Load class and selector - ldr a1, [a2, #CLASS] /* class = super->class */ - ldr a1, [a1, #SUPERCLASS] /* class = class->superclass */ + MOVE a1, a2 /* struct super */ MOVE a2, a3 /* selector */ + ldr a3, [a1, #CLASS] /* class = super->class */ + ldr a3, [a3, #SUPERCLASS] /* class = class->superclass */ + ldr a1, [a1, #RECEIVER] /* receiver = super->receiver */ # Do the lookup - MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache) + MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache3) MOVE ip, a1 # Prep for forwarding, pop stack frame and call imp diff --git a/runtime/Messengers.subproj/objc-msg-i386.s b/runtime/Messengers.subproj/objc-msg-i386.s index 929bcba..4ce2686 100644 --- a/runtime/Messengers.subproj/objc-msg-i386.s +++ b/runtime/Messengers.subproj/objc-msg-i386.s @@ -381,13 +381,13 @@ LMsgSendMissInstrumentDone_$0_$1_$2: .if $1 == MSG_SEND // MSG_SEND popl %esi // restore callers register popl %edi // restore callers register - movl self(%esp), %eax // get messaged object - movl isa(%eax), %eax // get objects class + movl self(%esp), %edx // get messaged object + movl isa(%edx), %eax // get objects class .elseif $1 == MSG_SENDSUPER // MSG_SENDSUPER // replace "super" arg with "receiver" movl super+8(%esp), %edi // get super structure - movl receiver(%edi), %esi // get messaged object - movl %esi, super+8(%esp) // make it the first argument + movl receiver(%edi), %edx // get messaged object + movl %edx, super+8(%esp) // make it the first argument movl class(%edi), %eax // get messaged class popl %esi // restore callers register popl %edi // restore callers register @@ -399,13 +399,13 @@ LMsgSendMissInstrumentDone_$0_$1_$2: .if $1 == MSG_SEND // MSG_SEND (stret) popl %esi // restore callers register popl %edi // restore callers register - movl self_stret(%esp), %eax // get messaged object - movl isa(%eax), %eax // get objects class + movl self_stret(%esp), %edx // get messaged object + movl isa(%edx), %eax // get objects class .elseif $1 == MSG_SENDSUPER // MSG_SENDSUPER (stret) // replace "super" arg with "receiver" movl super_stret+8(%esp), %edi// get super structure - movl receiver(%edi), %esi // get messaged object - movl %esi, super_stret+8(%esp)// make it the first argument + movl receiver(%edi), %edx // get messaged object + movl %edx, super_stret+8(%esp)// make it the first argument movl class(%edi), %eax // get messaged class popl %esi // restore callers register popl %edi // restore callers register @@ -414,6 +414,9 @@ LMsgSendMissInstrumentDone_$0_$1_$2: .endif .endif + // edx = receiver + // ecx = selector + // eax = class jmp $2 // go to callers handler // eax points to matching cache entry @@ -496,21 +499,25 @@ LMsgSendHitInstrumentDone_$0_$1_$2: // MSG_SEND (first parameter is receiver) // MSG_SENDSUPER (first parameter is address of objc_super structure) // +// edx = receiver +// ecx = selector +// eax = class +// (all set by CacheLookup's miss case) +// // Stack must be at 0xXXXXXXXc on entrance. // -// On exit: Register parameters restored from CacheLookup -// imp in eax +// On exit: esp unchanged +// imp in eax // ///////////////////////////////////////////////////////////////////// .macro MethodTableLookup - - subl $$4, %esp // 16-byte align the stack - // push args (class, selector) - pushl %ecx - pushl %eax - call __class_lookupMethodAndLoadCache - addl $$12, %esp // pop parameters and alignment + // stack is already aligned + pushl %eax // class + pushl %ecx // selector + pushl %edx // receiver + call __class_lookupMethodAndLoadCache3 + addl $$12, %esp // pop parameters .endmacro @@ -1015,7 +1022,7 @@ _FwdSel: .long 0 .cstring .align 2 -LUnkSelStr: .ascii "Does not recognize selector %s\0" +LUnkSelStr: .ascii "Does not recognize selector %s (while forwarding %s)\0" .data .align 2 @@ -1082,8 +1089,9 @@ L__objc_msgForward$pic_base: ret LMsgForwardError: - // Call __objc_error(receiver, "unknown selector %s", "forward::") - subl $12, %esp // 16-byte align the stack + // Call __objc_error(receiver, "unknown selector %s %s", "forward::", forwardedSel) + subl $8, %esp // 16-byte align the stack + pushl (selector+4+4)(%ebp) // the forwarded selector movl _FwdSel-L__objc_msgForward$pic_base(%edx),%eax pushl %eax leal LUnkSelStr-L__objc_msgForward$pic_base(%edx),%eax @@ -1135,8 +1143,9 @@ L__objc_msgForwardStret$pic_base: ret $4 // pop struct return address (#2995932) LMsgForwardStretError: - // Call __objc_error(receiver, "unknown selector %s", "forward::") - subl $12, %esp // 16-byte align the stack + // Call __objc_error(receiver, "unknown selector %s %s", "forward::", forwardedSelector) + subl $8, %esp // 16-byte align the stack + pushl (selector_stret+4+4)(%ebp) // the forwarded selector leal _FwdSel-L__objc_msgForwardStret$pic_base(%edx),%eax pushl %eax leal LUnkSelStr-L__objc_msgForwardStret$pic_base(%edx),%eax diff --git a/runtime/Messengers.subproj/objc-msg-simulator-i386.s b/runtime/Messengers.subproj/objc-msg-simulator-i386.s index 7e65212..19f79f3 100644 --- a/runtime/Messengers.subproj/objc-msg-simulator-i386.s +++ b/runtime/Messengers.subproj/objc-msg-simulator-i386.s @@ -222,13 +222,13 @@ LMsgSendCacheMiss_$0_$1_$2: .if $1 == MSG_SEND // MSG_SEND popl %esi // restore callers register popl %edi // restore callers register - movl self(%esp), %eax // get messaged object - movl isa(%eax), %eax // get objects class + movl self(%esp), %edx // get messaged object + movl isa(%edx), %eax // get objects class .elseif $1 == MSG_SENDSUPER || $1 == MSG_SENDSUPER2 // MSG_SENDSUPER[2] // replace "super" arg with "receiver" movl super+8(%esp), %edi // get super structure - movl receiver(%edi), %esi // get messaged object - movl %esi, super+8(%esp) // make it the first argument + movl receiver(%edi), %edx // get messaged object + movl %edx, super+8(%esp) // make it the first argument movl class(%edi), %eax // get messaged class .if $1 == MSG_SENDSUPER2 movl superclass(%eax), %eax // get messaged class @@ -243,13 +243,13 @@ LMsgSendCacheMiss_$0_$1_$2: .if $1 == MSG_SEND // MSG_SEND (stret) popl %esi // restore callers register popl %edi // restore callers register - movl self_stret(%esp), %eax // get messaged object - movl isa(%eax), %eax // get objects class + movl self_stret(%esp), %edx // get messaged object + movl isa(%edx), %eax // get objects class .elseif $1 == MSG_SENDSUPER || $1 == MSG_SENDSUPER2 // MSG_SENDSUPER[2] (stret) // replace "super" arg with "receiver" movl super_stret+8(%esp), %edi// get super structure - movl receiver(%edi), %esi // get messaged object - movl %esi, super_stret+8(%esp)// make it the first argument + movl receiver(%edi), %edx // get messaged object + movl %edx, super_stret+8(%esp)// make it the first argument movl class(%edi), %eax // get messaged class .if $1 == MSG_SENDSUPER2 movl superclass(%eax), %eax // get messaged class @@ -261,6 +261,9 @@ LMsgSendCacheMiss_$0_$1_$2: .endif .endif + // edx = receiver + // ecx = selector + // eax = class jmp $2 // go to callers handler // eax points to matching cache entry @@ -305,21 +308,25 @@ LMsgSendCacheHit_$0_$1_$2: // MSG_SEND (first parameter is receiver) // MSG_SENDSUPER (first parameter is address of objc_super structure) // +// edx = receiver +// ecx = selector +// eax = class +// (all set by CacheLookup's miss case) +// // Stack must be at 0xXXXXXXXc on entrance. // -// On exit: Register parameters restored from CacheLookup -// imp in eax +// On exit: esp unchanged +// imp in eax // ///////////////////////////////////////////////////////////////////// .macro MethodTableLookup - - subl $$4, %esp // 16-byte align the stack - // push args (class, selector) - pushl %ecx - pushl %eax - call __class_lookupMethodAndLoadCache - addl $$12, %esp // pop parameters and alignment + // stack is already aligned + pushl %eax // class + pushl %ecx // selector + pushl %edx // receiver + call __class_lookupMethodAndLoadCache3 + addl $$12, %esp // pop parameters .endmacro diff --git a/runtime/Messengers.subproj/objc-msg-win32.m b/runtime/Messengers.subproj/objc-msg-win32.m index 1d3724c..07c78ac 100644 --- a/runtime/Messengers.subproj/objc-msg-win32.m +++ b/runtime/Messengers.subproj/objc-msg-win32.m @@ -176,13 +176,14 @@ HIT: MISS: pop esi pop edi - mov eax, SELF - mov eax, isa[eax] + mov edx, SELF + mov eax, isa[edx] // MethodTableLookup WORD_RETURN, MSG_SEND - push ecx push eax - call _class_lookupMethodAndLoadCache + push ecx + push edx + call _class_lookupMethodAndLoadCache3 mov edx, kFwdMsgSend leave @@ -249,13 +250,14 @@ HIT: MISS: pop esi pop edi - mov eax, SELF - mov eax, isa[eax] + mov edx, SELF + mov eax, isa[edx] // MethodTableLookup WORD_RETURN, MSG_SEND - push ecx push eax - call _class_lookupMethodAndLoadCache + push ecx + push edx + call _class_lookupMethodAndLoadCache3 mov edx, kFwdMsgSend leave @@ -320,15 +322,16 @@ MISS: pop esi pop edi - mov edx, SUPER - mov eax, super_receiver[edx] - mov SUPER, eax - mov eax, super_class[edx] + mov eax, SUPER + mov edx, super_receiver[eax] + mov SUPER, edx + mov eax, super_class[eax] // MethodTableLookup WORD_RETURN, MSG_SENDSUPER - push ecx push eax - call _class_lookupMethodAndLoadCache + push ecx + push edx + call _class_lookupMethodAndLoadCache3 mov edx, kFwdMsgSend leave @@ -388,13 +391,14 @@ HIT: MISS: pop esi pop edi - mov eax, SELF_STRET - mov eax, isa[eax] + mov edx, SELF_STRET + mov eax, isa[edx] // MethodTableLookup WORD_RETURN, MSG_SEND - push ecx push eax - call _class_lookupMethodAndLoadCache + push ecx + push edx + call _class_lookupMethodAndLoadCache3 mov edx, kFwdMsgSendStret leave @@ -460,15 +464,16 @@ MISS: pop esi pop edi - mov edx, SUPER_STRET - mov eax, super_receiver[edx] - mov SUPER_STRET, eax - mov eax, super_class[edx] + mov eax, SUPER_STRET + mov edx, super_receiver[eax] + mov SUPER_STRET, edx + mov eax, super_class[eax] // MethodTableLookup WORD_RETURN, MSG_SENDSUPER - push ecx push eax - call _class_lookupMethodAndLoadCache + push ecx + push edx + call _class_lookupMethodAndLoadCache3 mov edx, kFwdMsgSendStret leave diff --git a/runtime/Messengers.subproj/objc-msg-x86_64.s b/runtime/Messengers.subproj/objc-msg-x86_64.s index 823b86b..ddc44d1 100644 --- a/runtime/Messengers.subproj/objc-msg-x86_64.s +++ b/runtime/Messengers.subproj/objc-msg-x86_64.s @@ -289,7 +289,7 @@ LSCIE1: .byte DW_CFA_def_cfa .byte DW_rsp .byte 8 - // RA is at 0(%rsp) aka -8(CFA) + // RA is at 0(%rsp) aka 1*-8(CFA) .byte DW_CFA_offset | DW_ra .byte 1 @@ -309,26 +309,67 @@ LSFDE$0: .long LLENFDE$0 # FDE Length LASFDE$0: .long LASFDE$0-CIE # FDE CIE offset - .quad LF0$0-. # FDE address start - .quad LLEN$0 # FDE address range + .quad L_dw_start_$0-. # FDE address start + .quad L_dw_len_$0 # FDE address range .byte 0x0 # uleb128 0x0; Augmentation size // DW_START: set by CIE -.if $2 == 1 +.if $1 == 1 + // CacheLookup - // pushq %rbp + // push .byte DW_CFA_advance_loc4 - .long LFLEN0$0+1 - .byte DW_CFA_def_cfa_offset + .long L_dw_push_$0 - L_dw_start_$0 + .byte DW_CFA_def_cfa_offset // CFA = rsp+16 .byte 16 - .byte DW_CFA_offset | DW_rbp - .byte -16/-8 - // movq %rsp, %rbp + + // pop + .byte DW_CFA_advance_loc4 + .long L_dw_pop_$0 - L_dw_push_$0 + .byte DW_CFA_def_cfa_offset // CFA = rsp+8 + .byte 8 + + // cache miss: push is back in effect + .byte DW_CFA_advance_loc4 + .long L_dw_miss_$0 - L_dw_pop_$0 + .byte DW_CFA_def_cfa_offset // CFA = rsp+16 + .byte 16 + + // pop during cache miss + .byte DW_CFA_advance_loc4 + .long L_dw_miss_pop_$0 - L_dw_miss_$0 + .byte DW_CFA_def_cfa_offset // CFA = rsp+8 + .byte 8 + +.endif + +.if $2 == 1 + // Save/RestoreRegisters or MethodTableLookup + + // enter .byte DW_CFA_advance_loc4 - .long 3 - .byte DW_CFA_def_cfa_register +.if $1 == 1 + .long L_dw_enter_$0 - L_dw_miss_pop_$0 +.else + .long L_dw_enter_$0 - L_dw_start_$0 +.endif + .byte DW_CFA_def_cfa_offset + .byte 16 + .byte DW_CFA_offset | DW_rbp // rbp => 2*-8(CFA) + .byte 2 + .byte DW_CFA_def_cfa_register // CFA = rbp+16 (offset unchanged) .byte DW_rbp + + // leave + .byte DW_CFA_advance_loc4 + .long L_dw_leave_$0 - L_dw_enter_$0 + + .byte DW_CFA_same_value // rbp = original value + .byte DW_rbp + .byte DW_CFA_def_cfa // CFA = rsp+8 + .byte DW_rsp + .byte 8 .endif @@ -339,23 +380,47 @@ LEFDE$0: .endmacro +// Start of function .macro DW_START -LF0$0: +L_dw_start_$0: .endmacro - -.macro DW_FRAME -LF1$0: - .set LFLEN0$0, LF1$0-LF0$0 + +// After `push` in CacheLookup +.macro DW_PUSH +L_dw_push_$0: +.endmacro + +// After `pop` in CacheLookup +.macro DW_POP +L_dw_pop_$0: +.endmacro + +// After cache miss label +.macro DW_MISS +L_dw_miss_$0: +.endmacro + +// After pop in MethodTableLookup +.macro DW_MISS_POP +L_dw_miss_pop_$0: .endmacro -.macro DW_END - .set LLEN$0, .-LF0$0 - EMIT_FDE $0, LLEN$0, 1 +// After `enter` in SaveRegisters +.macro DW_ENTER +L_dw_enter_$0: .endmacro -.macro DW_END2 - .set LLEN$0, .-LF0$0 - EMIT_FDE $0, LLEN$0, 2 +// After `leave` in RestoreRegisters +.macro DW_LEAVE +L_dw_leave_$0: +.endmacro + +// End of function +// $1 == 1 iff you called CacheLookup +// $2 == 1 iff you called MethodTableLookup or Save/RestoreRegsters +.macro DW_END + .set L_dw_len_$0, . - L_dw_start_$0 + EMIT_FDE $0, $1, $2 .endmacro @@ -375,8 +440,11 @@ LF1$0: ///////////////////////////////////////////////////////////////////// .macro SaveRegisters - DW_FRAME $0 + // These instructions must match the DWARF data in EMIT_FDE. + enter $$0x80+8, $$0 // +8 for alignment + DW_ENTER $0 + movdqa %xmm0, -0x80(%rbp) push %rax // might be xmm parameter count movdqa %xmm1, -0x70(%rbp) @@ -392,6 +460,8 @@ LF1$0: movdqa %xmm6, -0x20(%rbp) push %a6 movdqa %xmm7, -0x10(%rbp) + + // These instructions must match the DWARF data in EMIT_FDE. .endmacro ///////////////////////////////////////////////////////////////////// @@ -409,6 +479,8 @@ LF1$0: ///////////////////////////////////////////////////////////////////// .macro RestoreRegisters + // These instructions must match the DWARF data in EMIT_FDE. + movdqa -0x80(%rbp), %xmm0 pop %a6 movdqa -0x70(%rbp), %xmm1 @@ -424,19 +496,24 @@ LF1$0: movdqa -0x20(%rbp), %xmm6 pop %rax movdqa -0x10(%rbp), %xmm7 + leave + DW_LEAVE $0 + + // These instructions must match the DWARF data in EMIT_FDE. .endmacro ///////////////////////////////////////////////////////////////////// // // -// CacheLookup return-type +// CacheLookup return-type, caller // // Locate the implementation for a selector in a class method cache. // // Takes: // $0 = NORMAL, FPRET, FP2RET, STRET +// $1 = caller's symbol name for DWARF // a2 or a3 (STRET) = selector // %r11 = class whose cache is to be searched // @@ -448,6 +525,8 @@ LF1$0: .macro CacheLookup push %rax + DW_PUSH $1 + movq cache(%r11), %r10 // cache = class->cache .if $0 != STRET mov %a2d, %eax // index = sel @@ -476,6 +555,7 @@ LF1$0: // cache hit, r11 = method triplet // restore saved registers pop %rax + DW_POP $1 .if $0 != STRET // eq (non-stret) flag already set above @@ -489,7 +569,7 @@ LF1$0: ///////////////////////////////////////////////////////////////////// // -// MethodTableLookup classRegister, selectorRegister, fn +// MethodTableLookup classRegister, selectorRegister, caller // // Takes: $0 = class to search (a1 or a2 or r10 ONLY) // $1 = selector to search for (a2 or a3 ONLY) @@ -505,6 +585,8 @@ LF1$0: .macro MethodTableLookup pop %rax // saved by CacheLookup + DW_MISS_POP $2 + SaveRegisters $2 // _class_lookupMethodAndLoadCache3(receiver, selector, class) @@ -666,7 +748,7 @@ LNilTestSlow: // do lookup movq %a1, %r11 // move class to r11 for CacheLookup - CacheLookup NORMAL + CacheLookup NORMAL, __cache_getMethod // cache hit, method triplet in %r11 cmpq method_imp(%r11), %a3 // if (imp==_objc_msgForward_internal) @@ -678,12 +760,14 @@ LNilTestSlow: LCacheMiss: // cache miss, return nil + DW_MISS __cache_getMethod pop %rax // pushed by CacheLookup + DW_MISS_POP __cache_getMethod xorl %eax, %eax ret LGetMethodExit: - DW_END2 __cache_getMethod + DW_END __cache_getMethod, 1, 0 END_ENTRY __cache_getMethod @@ -702,7 +786,7 @@ LGetMethodExit: // do lookup movq %a1, %r11 // move class to r11 for CacheLookup - CacheLookup NORMAL + CacheLookup NORMAL, __cache_getImp // cache hit, method triplet in %r11 movq method_imp(%r11), %rax // return method imp address @@ -710,12 +794,14 @@ LGetMethodExit: LCacheMiss: // cache miss, return nil + DW_MISS __cache_getImp pop %rax // pushed by CacheLookup + DW_MISS_POP __cache_getImp xorl %eax, %eax ret LGetImpExit: - DW_END2 __cache_getImp + DW_END __cache_getImp, 1, 0 END_ENTRY __cache_getImp @@ -737,7 +823,7 @@ __objc_tagged_isa_table: NilTest NORMAL GetIsaFast NORMAL // r11 = self->isa - CacheLookup NORMAL // r11 = method, eq set (nonstret fwd) + CacheLookup NORMAL, _objc_msgSend // r11=method, eq set (nonstret fwd) jmp *method_imp(%r11) // goto *imp NilTestSupport NORMAL @@ -746,12 +832,13 @@ __objc_tagged_isa_table: // cache miss: go search the method lists LCacheMiss: + DW_MISS _objc_msgSend GetIsa NORMAL // r11 = self->isa MethodTableLookup %a1, %a2, _objc_msgSend // r11 = IMP cmp %r11, %r11 // set eq (nonstret) for forwarding jmp *%r11 // goto *imp - DW_END _objc_msgSend + DW_END _objc_msgSend, 1, 1 END_ENTRY _objc_msgSend #if __OBJC2__ @@ -786,7 +873,7 @@ LCacheMiss: NilTestSupport NORMAL - DW_END _objc_msgSend_fixup + DW_END _objc_msgSend_fixup, 0, 1 END_ENTRY _objc_msgSend_fixup @@ -813,12 +900,13 @@ LCacheMiss: // search the cache (objc_super in %a1) movq class(%a1), %r11 // class = objc_super->class - CacheLookup NORMAL // r11 = method, eq set (nonstret fwd) + CacheLookup NORMAL, _objc_msgSendSuper // r11 = method, eq set (nonstret fwd) movq receiver(%a1), %a1 // load real receiver jmp *method_imp(%r11) // goto *imp // cache miss: go search the method lists LCacheMiss: + DW_MISS _objc_msgSendSuper movq receiver(%a1), %r10 movq class(%a1), %r11 MethodTableLookup %r10, %a2, _objc_msgSendSuper // r11 = IMP @@ -826,7 +914,7 @@ LCacheMiss: cmp %r11, %r11 // set eq (nonstret) for forwarding jmp *%r11 // goto *imp - DW_END _objc_msgSendSuper + DW_END _objc_msgSendSuper, 1, 1 END_ENTRY _objc_msgSendSuper @@ -857,7 +945,7 @@ LCacheMiss: cmp %r11, %r11 // set nonstret (eq) for forwarding jmp *%r11 - DW_END _objc_msgSendSuper2_fixup + DW_END _objc_msgSendSuper2_fixup, 0, 1 END_ENTRY _objc_msgSendSuper2_fixup @@ -874,12 +962,13 @@ LCacheMiss: // search the cache (objc_super in %a1) movq class(%a1), %r11 // cls = objc_super->class movq 8(%r11), %r11 // cls = class->superclass - CacheLookup NORMAL // r11 = method, eq set (nonstret fwd) + CacheLookup NORMAL, _objc_msgSendSuper2 // r11 = method, eq set (nonstret fwd) movq receiver(%a1), %a1 // load real receiver jmp *method_imp(%r11) // goto *imp // cache miss: go search the method lists LCacheMiss: + DW_MISS _objc_msgSendSuper2 movq receiver(%a1), %r10 movq class(%a1), %r11 movq 8(%r11), %r11 @@ -888,7 +977,7 @@ LCacheMiss: cmp %r11, %r11 // set eq (nonstret) for forwarding jmp *%r11 // goto *imp - DW_END _objc_msgSendSuper2 + DW_END _objc_msgSendSuper2, 1, 1 END_ENTRY _objc_msgSendSuper2 #endif @@ -906,7 +995,7 @@ LCacheMiss: NilTest FPRET GetIsaFast FPRET // r11 = self->isa - CacheLookup FPRET // r11 = method, eq set (nonstret fwd) + CacheLookup FPRET, _objc_msgSend_fpret // r11 = method, eq set (nonstret fwd) jmp *method_imp(%r11) // goto *imp NilTestSupport FPRET @@ -915,12 +1004,13 @@ LCacheMiss: // cache miss: go search the method lists LCacheMiss: + DW_MISS _objc_msgSend_fpret GetIsa FPRET // r11 = self->isa MethodTableLookup %a1, %a2, _objc_msgSend_fpret // r11 = IMP cmp %r11, %r11 // set eq (nonstret) for forwarding jmp *%r11 // goto *imp - DW_END _objc_msgSend_fpret + DW_END _objc_msgSend_fpret, 1, 1 END_ENTRY _objc_msgSend_fpret #if __OBJC2__ @@ -955,7 +1045,7 @@ LCacheMiss: NilTestSupport FPRET - DW_END _objc_msgSend_fpret_fixup + DW_END _objc_msgSend_fpret_fixup, 0, 1 END_ENTRY _objc_msgSend_fpret_fixup @@ -980,7 +1070,7 @@ LCacheMiss: NilTest FP2RET GetIsaFast FP2RET // r11 = self->isa - CacheLookup FP2RET // r11 = method, eq set (nonstret fwd) + CacheLookup FP2RET, _objc_msgSend_fp2ret // r11 = method, eq set (nonstret fwd) jmp *method_imp(%r11) // goto *imp NilTestSupport FP2RET @@ -989,12 +1079,13 @@ LCacheMiss: // cache miss: go search the method lists LCacheMiss: + DW_MISS _objc_msgSend_fp2ret GetIsa FP2RET // r11 = self->isa MethodTableLookup %a1, %a2, _objc_msgSend_fp2ret // r11 = IMP cmp %r11, %r11 // set eq (nonstret) for forwarding jmp *%r11 // goto *imp - DW_END _objc_msgSend_fp2ret + DW_END _objc_msgSend_fp2ret, 1, 1 END_ENTRY _objc_msgSend_fp2ret #if __OBJC2__ @@ -1029,7 +1120,7 @@ LCacheMiss: NilTestSupport FP2RET - DW_END _objc_msgSend_fp2ret_fixup + DW_END _objc_msgSend_fp2ret_fixup, 0, 1 END_ENTRY _objc_msgSend_fp2ret_fixup @@ -1060,7 +1151,7 @@ LCacheMiss: NilTest STRET GetIsaFast STRET // r11 = self->isa - CacheLookup STRET // r11 = method, ne set (stret fwd) + CacheLookup STRET, _objc_msgSend_stret // r11 = method, ne set (stret fwd) jmp *method_imp(%r11) // goto *imp NilTestSupport STRET @@ -1069,12 +1160,13 @@ LCacheMiss: // cache miss: go search the method lists LCacheMiss: + DW_MISS _objc_msgSend_stret GetIsa STRET // r11 = self->isa MethodTableLookup %a2, %a3, _objc_msgSend_stret // r11 = IMP test %r11, %r11 // set ne (stret) for forward; r11!=0 jmp *%r11 // goto *imp - DW_END _objc_msgSend_stret + DW_END _objc_msgSend_stret, 1, 1 END_ENTRY _objc_msgSend_stret #if __OBJC2__ @@ -1109,7 +1201,7 @@ LCacheMiss: NilTestSupport STRET - DW_END _objc_msgSend_stret_fixup + DW_END _objc_msgSend_stret_fixup, 0, 1 END_ENTRY _objc_msgSend_stret_fixup @@ -1145,12 +1237,13 @@ LCacheMiss: // search the cache (objc_super in %a2) movq class(%a2), %r11 // class = objc_super->class - CacheLookup STRET // r11 = method, ne set (stret fwd) + CacheLookup STRET, _objc_msgSendSuper_stret // r11 = method, ne set (stret fwd) movq receiver(%a2), %a2 // load real receiver jmp *method_imp(%r11) // goto *imp // cache miss: go search the method lists LCacheMiss: + DW_MISS _objc_msgSendSuper_stret movq receiver(%a2), %r10 movq class(%a2), %r11 MethodTableLookup %r10, %a3, _objc_msgSendSuper_stret // r11 = IMP @@ -1158,7 +1251,7 @@ LCacheMiss: test %r11, %r11 // set ne (stret) for forward; r11!=0 jmp *%r11 // goto *imp - DW_END _objc_msgSendSuper_stret + DW_END _objc_msgSendSuper_stret, 1, 1 END_ENTRY _objc_msgSendSuper_stret @@ -1187,7 +1280,7 @@ LCacheMiss: test %r11, %r11 // set stret (ne) for forward; r11!=0 jmp *%r11 // goto *imp - DW_END _objc_msgSendSuper2_stret_fixup + DW_END _objc_msgSendSuper2_stret_fixup, 0, 1 END_ENTRY _objc_msgSendSuper2_stret_fixup @@ -1203,12 +1296,13 @@ LCacheMiss: // search the cache (objc_super in %a2) movq class(%a2), %r11 // class = objc_super->class movq 8(%r11), %r11 // class = class->super_class - CacheLookup STRET // r11 = method, ne set (stret fwd) + CacheLookup STRET, _objc_msgSendSuper2_stret // r11 = method, ne set (stret fwd) movq receiver(%a2), %a2 // load real receiver jmp *method_imp(%r11) // goto *imp // cache miss: go search the method lists LCacheMiss: + DW_MISS _objc_msgSendSuper2_stret movq receiver(%a2), %r10 movq class(%a2), %r11 movq 8(%r11), %r11 @@ -1217,7 +1311,7 @@ LCacheMiss: test %r11, %r11 // set ne (stret) for forward; r11!=0 jmp *%r11 // goto *imp - DW_END _objc_msgSendSuper2_stret + DW_END _objc_msgSendSuper2_stret, 1, 1 END_ENTRY _objc_msgSendSuper2_stret #endif @@ -1237,7 +1331,7 @@ _FwdSel: .quad 0 .cstring .align 3 -LUnkSelStr: .ascii "Does not recognize selector %s\0" +LUnkSelStr: .ascii "Does not recognize selector %s (while forwarding %s)\0" .data .align 3 @@ -1328,9 +1422,10 @@ __objc_forward_stret_handler: .quad 0 ret LMsgForwardError: - // Tail-call __objc_error(receiver, "unknown selector %s", "forward::") + // Tail-call __objc_error(receiver, "unknown selector %s %s", "forward::", forwardedSel) // %a1 is already the receiver - leaq LUnkSelStr(%rip), %a2 // "unknown selector %s" + movq %a3, %a4 // the forwarded selector + leaq LUnkSelStr(%rip), %a2 // "unknown selector %s %s" movq _FwdSel(%rip), %a3 // forward:: jmp ___objc_error // never returns @@ -1402,9 +1497,10 @@ LMsgForwardError: ret LMsgForwardStretError: - // Tail-call __objc_error(receiver, "unknown selector %s", "forward::") + // Tail-call __objc_error(receiver, "unknown selector %s %s", "forward::", forwardedSel) + // %a4 is already the forwarded selector movq %a2, %a1 // receiver - leaq LUnkSelStr(%rip), %a2 // "unknown selector %s" + leaq LUnkSelStr(%rip), %a2 // "unknown selector %s %s" movq _FwdSel(%rip), %a3 // forward:: jmp ___objc_error // never returns diff --git a/runtime/objc-arr.mm b/runtime/NSObject.mm similarity index 63% rename from runtime/objc-arr.mm rename to runtime/NSObject.mm index 89a039e..d95cf93 100644 --- a/runtime/objc-arr.mm +++ b/runtime/NSObject.mm @@ -1,15 +1,15 @@ /* - * Copyright (c) 2010-2011 Apple Inc. All rights reserved. + * Copyright (c) 2010-2012 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -17,21 +17,23 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ */ +#include "objc-weak.h" +#include "objc-private.h" +#include "objc-internal.h" +#include "objc-os.h" +#if __OBJC2__ +#include "objc-runtime-new.h" +#endif +#include "runtime.h" #include "llvm-DenseMap.h" -#import "objc-weak.h" -#import "objc-private.h" -#import "objc-internal.h" -#import "objc-os.h" -#import "runtime.h" - +#include #include #include -//#include #include #include #include @@ -42,6 +44,113 @@ #include #include +@interface NSInvocation +- (SEL)selector; +@end + +// better to not rely on Foundation to build +@class NSString; +@class NSMethodSignature; +#ifdef __LP64__ +typedef unsigned long NSUInteger; +#else +typedef unsigned int NSUInteger; +#endif +typedef struct _NSZone NSZone; + +@protocol NSObject + +- (BOOL)isEqual:(id)object; +- (NSUInteger)hash; + +- (Class)superclass; +- (Class)class; +- (id)self; +- (NSZone *)zone; + +- (id)performSelector:(SEL)aSelector; +- (id)performSelector:(SEL)aSelector withObject:(id)object; +- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2; + +- (BOOL)isProxy; + +- (BOOL)isKindOfClass:(Class)aClass; +- (BOOL)isMemberOfClass:(Class)aClass; +- (BOOL)conformsToProtocol:(Protocol *)aProtocol; + +- (BOOL)respondsToSelector:(SEL)aSelector; + +- (id)retain; +- (oneway void)release; +- (id)autorelease; +- (NSUInteger)retainCount; + +- (NSString *)description; +- (NSString *)debugDescription; + +@end + +OBJC_EXPORT +@interface NSObject +{ + Class isa; +} +@end + +// HACK -- the use of these functions must be after the @implementation +id bypass_msgSend_retain(NSObject *obj) asm("-[NSObject retain]"); +void bypass_msgSend_release(NSObject *obj) asm("-[NSObject release]"); +id bypass_msgSend_autorelease(NSObject *obj) asm("-[NSObject autorelease]"); + + +#if TARGET_OS_MAC + +// NSObject used to be in Foundation/CoreFoundation. + +#define SYMBOL_ELSEWHERE_IN_3(sym, vers, n) \ + OBJC_EXPORT const char elsewhere_ ##n __asm__("$ld$hide$os" #vers "$" #sym); const char elsewhere_ ##n = 0 +#define SYMBOL_ELSEWHERE_IN_2(sym, vers, n) \ + SYMBOL_ELSEWHERE_IN_3(sym, vers, n) +#define SYMBOL_ELSEWHERE_IN(sym, vers) \ + SYMBOL_ELSEWHERE_IN_2(sym, vers, __COUNTER__) + +#if __OBJC2__ +# define NSOBJECT_ELSEWHERE_IN(vers) \ + SYMBOL_ELSEWHERE_IN(_OBJC_CLASS_$_NSObject, vers); \ + SYMBOL_ELSEWHERE_IN(_OBJC_METACLASS_$_NSObject, vers); \ + SYMBOL_ELSEWHERE_IN(_OBJC_IVAR_$_NSObject.isa, vers) +#else +# define NSOBJECT_ELSEWHERE_IN(vers) \ + SYMBOL_ELSEWHERE_IN(.objc_class_name_NSObject, vers) +#endif + +#if TARGET_OS_IPHONE + NSOBJECT_ELSEWHERE_IN(5.1); + NSOBJECT_ELSEWHERE_IN(5.0); + NSOBJECT_ELSEWHERE_IN(4.3); + NSOBJECT_ELSEWHERE_IN(4.2); + NSOBJECT_ELSEWHERE_IN(4.1); + NSOBJECT_ELSEWHERE_IN(4.0); + NSOBJECT_ELSEWHERE_IN(3.2); + NSOBJECT_ELSEWHERE_IN(3.1); + NSOBJECT_ELSEWHERE_IN(3.0); + NSOBJECT_ELSEWHERE_IN(2.2); + NSOBJECT_ELSEWHERE_IN(2.1); + NSOBJECT_ELSEWHERE_IN(2.0); +#else + NSOBJECT_ELSEWHERE_IN(10.7); + NSOBJECT_ELSEWHERE_IN(10.6); + NSOBJECT_ELSEWHERE_IN(10.5); + NSOBJECT_ELSEWHERE_IN(10.4); + NSOBJECT_ELSEWHERE_IN(10.3); + NSOBJECT_ELSEWHERE_IN(10.2); + NSOBJECT_ELSEWHERE_IN(10.1); + NSOBJECT_ELSEWHERE_IN(10.0); +#endif + +// TARGET_OS_MAC +#endif + #if SUPPORT_RETURN_AUTORELEASE // We cannot peek at where we are returning to unless we always inline this: __attribute__((always_inline)) @@ -55,15 +164,25 @@ static bool callerAcceptsFastAutorelease(const void * const ra0); static bool seen_weak_refs; -@protocol ReferenceCounted -+ (id)alloc; -+ (id)allocWithZone:(malloc_zone_t *)zone; -- (oneway void)dealloc; -- (id)retain; -- (oneway void)release; -- (id)autorelease; -- (uintptr_t)retainCount; -@end +static id defaultBadAllocHandler(Class cls) +{ + _objc_fatal("attempt to allocate object of class '%s' failed", + class_getName(cls)); +} + +static id(*badAllocHandler)(Class) = &defaultBadAllocHandler; + +static id callBadAllocHandler(Class cls) +{ + // fixme add re-entrancy protection in case allocation fails inside handler + return (*badAllocHandler)(cls); +} + +void _objc_setBadAllocHandler(id(*newHandler)(Class)) +{ + badAllocHandler = newHandler; +} + #define ARR_LOGGING 0 @@ -75,7 +194,7 @@ struct { int blockCopies; } CompilerGenerated, ExplicitlyCoded; -PRIVATE_EXTERN void (^objc_arr_log)(const char *, id param) = +void (^objc_arr_log)(const char *, id param) = ^(const char *str, id param) { printf("%s %p\n", str, param); }; #endif @@ -161,7 +280,7 @@ bool noSideTableLocksHeld(void) } // -// The -fobjc-arr flag causes the compiler to issue calls to objc_{retain/release/autorelease/retain_block} +// The -fobjc-arc flag causes the compiler to issue calls to objc_{retain/release/autorelease/retain_block} // id objc_retainBlock(id x) { @@ -180,39 +299,10 @@ BOOL objc_should_deallocate(id object) { return YES; } -// WORKAROUND: -// clang remembers variadic bit across function cast -// Clang thinks that all ObjC vtable dispatches are variadic -// vararg function defeats tail-call optimization -id objc_msgSend_hack(id, SEL) asm("_objc_msgSend"); - -// public API entry points that might be optimized later - -__attribute__((aligned(16))) -id -objc_retain(id obj) -{ - return objc_msgSend_hack(obj, @selector(retain)); -} - -__attribute__((aligned(16))) -void -objc_release(id obj) -{ - objc_msgSend_hack(obj, @selector(release)); -} - -__attribute__((aligned(16))) -id -objc_autorelease(id obj) -{ - return objc_msgSend_hack(obj, @selector(autorelease)); -} - id objc_retain_autorelease(id obj) { - return objc_autorelease(objc_retain(obj)); + return objc_autorelease(objc_retain(obj)); } id @@ -313,29 +403,29 @@ objc_loadWeak(id *location) id objc_initWeak(id *addr, id val) { - *addr = 0; - return objc_storeWeak(addr, val); + *addr = 0; + return objc_storeWeak(addr, val); } void objc_destroyWeak(id *addr) { - objc_storeWeak(addr, 0); + objc_storeWeak(addr, 0); } void objc_copyWeak(id *to, id *from) { - id val = objc_loadWeakRetained(from); - objc_initWeak(to, val); - objc_release(val); + id val = objc_loadWeakRetained(from); + objc_initWeak(to, val); + objc_release(val); } void objc_moveWeak(id *to, id *from) { - objc_copyWeak(to, from); - objc_destroyWeak(from); + objc_copyWeak(to, from); + objc_destroyWeak(from); } @@ -469,7 +559,7 @@ class AutoreleasePoolPage { (die ? _objc_fatal : _objc_inform) ("autorelease pool page %p corrupted\n" - " magic %x %x %x %x\n pthread %p\n", + " magic 0x%08x 0x%08x 0x%08x 0x%08x\n pthread %p\n", this, magic.m[0], magic.m[1], magic.m[2], magic.m[3], this->thread); } @@ -609,7 +699,7 @@ class AutoreleasePoolPage static inline AutoreleasePoolPage *hotPage() { AutoreleasePoolPage *result = (AutoreleasePoolPage *) - _pthread_getspecific_direct(key); + tls_get_direct(key); if (result) result->fastcheck(); return result; } @@ -617,7 +707,7 @@ class AutoreleasePoolPage static inline void setHotPage(AutoreleasePoolPage *page) { if (page) page->fastcheck(); - _pthread_setspecific_direct(key, (void *)page); + tls_set_direct(key, (void *)page); } static inline AutoreleasePoolPage *coldPage() @@ -794,7 +884,7 @@ public: for (int i = 0; i < count; i++) { _objc_inform("POOL HIGHWATER: %s", sym[i]); } - free(sym); + free(sym); } } @@ -824,24 +914,6 @@ _objc_rootRetain_slow(id obj) return obj; } -id -_objc_rootRetain(id obj) -{ - assert(obj); - assert(!UseGC); - - if (OBJC_IS_TAGGED_PTR(obj)) return obj; - - SideTable *table = SideTable::tableForPointer(obj); - - if (OSSpinLockTry(&table->slock)) { - table->refcnts[DISGUISE(obj)] += 2; - OSSpinLockUnlock(&table->slock); - return obj; - } - return _objc_rootRetain_slow(obj); -} - bool _objc_rootTryRetain(id obj) { @@ -965,18 +1037,6 @@ _objc_rootReleaseWasZero(id obj) return _objc_rootReleaseWasZero_slow(obj); } -void -_objc_rootRelease(id obj) -{ - assert(obj); - assert(!UseGC); - - if (_objc_rootReleaseWasZero(obj) == false) { - return; - } - objc_msgSend_hack(obj, @selector(dealloc)); -} - __attribute__((noinline,used)) static id _objc_rootAutorelease2(id obj) { @@ -984,30 +1044,6 @@ static id _objc_rootAutorelease2(id obj) return AutoreleasePoolPage::autorelease(obj); } -__attribute__((aligned(16))) -id -_objc_rootAutorelease(id obj) -{ - assert(obj); // root classes shouldn't get here, since objc_msgSend ignores nil - assert(!UseGC); - - if (UseGC) { - return obj; - } - - // no tag check here: tagged pointers DO use fast autoreleasing - -#if SUPPORT_RETURN_AUTORELEASE - assert(_pthread_getspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY) == NULL); - - if (callerAcceptsFastAutorelease(__builtin_return_address(0))) { - _pthread_setspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY, obj); - return obj; - } -#endif - return _objc_rootAutorelease2(obj); -} - uintptr_t _objc_rootRetainCount(id obj) { @@ -1036,36 +1072,44 @@ _objc_rootRetainCount(id obj) id _objc_rootInit(id obj) { - // In practice, it will be hard to rely on this function. - // Many classes do not properly chain -init calls. - return obj; + // In practice, it will be hard to rely on this function. + // Many classes do not properly chain -init calls. + return obj; } id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone) { + id obj; + #if __OBJC2__ - // allocWithZone under __OBJC2__ ignores the zone parameter - (void)zone; - return class_createInstance(cls, 0); + // allocWithZone under __OBJC2__ ignores the zone parameter + (void)zone; + obj = class_createInstance(cls, 0); #else - if (!zone || UseGC) { - return class_createInstance(cls, 0); - } - return class_createInstanceFromZone(cls, 0, zone); + if (!zone || UseGC) { + obj = class_createInstance(cls, 0); + } + else { + obj = class_createInstanceFromZone(cls, 0, zone); + } #endif + + if (!obj) obj = callBadAllocHandler(cls); + return obj; } id _objc_rootAlloc(Class cls) { -#if 0 - // once we get a bit in the class, data structure, we can call this directly - // because allocWithZone under __OBJC2__ ignores the zone parameter - return class_createInstance(cls, 0); -#else - return [cls allocWithZone: nil]; +#if 0 && __OBJC2__ + // Skip over the +allocWithZone: call if the class doesn't override it. + // fixme not - this breaks ObjectAlloc + if (! ((class_t *)cls)->isa->hasCustomAWZ()) { + return class_createInstance(cls, 0); + } #endif + return [cls allocWithZone: nil]; } void @@ -1094,26 +1138,26 @@ _objc_rootFinalize(id obj __unused) malloc_zone_t * _objc_rootZone(id obj) { - (void)obj; - if (gc_zone) { - return gc_zone; - } + (void)obj; + if (gc_zone) { + return gc_zone; + } #if __OBJC2__ - // allocWithZone under __OBJC2__ ignores the zone parameter - return malloc_default_zone(); + // allocWithZone under __OBJC2__ ignores the zone parameter + return malloc_default_zone(); #else - malloc_zone_t *rval = malloc_zone_from_ptr(obj); - return rval ? rval : malloc_default_zone(); + malloc_zone_t *rval = malloc_zone_from_ptr(obj); + return rval ? rval : malloc_default_zone(); #endif } uintptr_t _objc_rootHash(id obj) { - if (UseGC) { - return _object_getExternalHash(obj); - } - return (uintptr_t)obj; + if (UseGC) { + return _object_getExternalHash(obj); + } + return (uintptr_t)obj; } // make CF link for now @@ -1222,6 +1266,35 @@ static bool callerAcceptsFastAutorelease(const void * const ra0) } // __x86_64__ +# elif __arm__ + +static bool callerAcceptsFastAutorelease(const void *ra) +{ + // if the low bit is set, we're returning to thumb mode + if ((uintptr_t)ra & 1) { + // 3f 46 mov r7, r7 + // we mask off the low bit via subtraction + if (*(uint16_t *)((uint8_t *)ra - 1) == 0x463f) { + return true; + } + } else { + // 07 70 a0 e1 mov r7, r7 + if (*(uint32_t *)ra == 0xe1a07007) { + return true; + } + } + return false; +} + +// __arm__ +# elif __i386__ && TARGET_IPHONE_SIMULATOR + +static bool callerAcceptsFastAutorelease(const void *ra) +{ + return false; +} + +// __i386__ && TARGET_IPHONE_SIMULATOR # else #warning unknown architecture @@ -1241,10 +1314,10 @@ id objc_autoreleaseReturnValue(id obj) { #if SUPPORT_RETURN_AUTORELEASE - assert(_pthread_getspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY) == NULL); + assert(tls_get_direct(AUTORELEASE_POOL_RECLAIM_KEY) == NULL); if (callerAcceptsFastAutorelease(__builtin_return_address(0))) { - _pthread_setspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY, obj); + tls_set_direct(AUTORELEASE_POOL_RECLAIM_KEY, obj); return obj; } #endif @@ -1262,8 +1335,8 @@ id objc_retainAutoreleasedReturnValue(id obj) { #if SUPPORT_RETURN_AUTORELEASE - if (obj == _pthread_getspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY)) { - _pthread_setspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY, 0); + if (obj == tls_get_direct(AUTORELEASE_POOL_RECLAIM_KEY)) { + tls_set_direct(AUTORELEASE_POOL_RECLAIM_KEY, 0); return obj; } #endif @@ -1273,41 +1346,577 @@ objc_retainAutoreleasedReturnValue(id obj) void objc_storeStrong(id *location, id obj) { - // XXX FIXME -- GC support? - id prev = *location; - if (obj == prev) { - return; - } - objc_retain(obj); - *location = obj; - objc_release(prev); + // XXX FIXME -- GC support? + id prev = *location; + if (obj == prev) { + return; + } + objc_retain(obj); + *location = obj; + objc_release(prev); } id objc_retainAutorelease(id obj) { - return objc_autorelease(objc_retain(obj)); + return objc_autorelease(objc_retain(obj)); } void _objc_deallocOnMainThreadHelper(void *context) { - id obj = (id)context; - objc_msgSend_hack(obj, @selector(dealloc)); + id obj = (id)context; + [obj dealloc]; } +#undef objc_retainedObject +#undef objc_unretainedObject +#undef objc_unretainedPointer + // convert objc_objectptr_t to id, callee must take ownership. -NS_RETURNS_RETAINED id objc_retainedObject(objc_objectptr_t CF_CONSUMED pointer) { return (id)pointer; } +id objc_retainedObject(objc_objectptr_t pointer) { return (id)pointer; } // convert objc_objectptr_t to id, without ownership transfer. -NS_RETURNS_NOT_RETAINED id objc_unretainedObject(objc_objectptr_t pointer) { return (id)pointer; } +id objc_unretainedObject(objc_objectptr_t pointer) { return (id)pointer; } // convert id to objc_objectptr_t, no ownership transfer. -CF_RETURNS_NOT_RETAINED objc_objectptr_t objc_unretainedPointer(id object) { return object; } +objc_objectptr_t objc_unretainedPointer(id object) { return object; } -PRIVATE_EXTERN void arr_init(void) +void arr_init(void) { AutoreleasePoolPage::init(); SideTable::init(); } + +@implementation NSObject + ++ (void)load { + if (UseGC) gc_init2(); +} + ++ (void)initialize { +} + ++ (id)self { + return (id)self; +} + +- (id)self { + return self; +} + ++ (Class)class { + return self; +} + +- (Class)class { + return object_getClass(self); +} + ++ (Class)superclass { + return class_getSuperclass(self); +} + +- (Class)superclass { + return class_getSuperclass([self class]); +} + ++ (BOOL)isMemberOfClass:(Class)cls { + return object_getClass((id)self) == cls; +} + +- (BOOL)isMemberOfClass:(Class)cls { + return [self class] == cls; +} + ++ (BOOL)isKindOfClass:(Class)cls { + for (Class tcls = object_getClass((id)self); tcls; tcls = class_getSuperclass(tcls)) { + if (tcls == cls) return YES; + } + return NO; +} + +- (BOOL)isKindOfClass:(Class)cls { + for (Class tcls = [self class]; tcls; tcls = class_getSuperclass(tcls)) { + if (tcls == cls) return YES; + } + return NO; +} + ++ (BOOL)isSubclassOfClass:(Class)cls { + for (Class tcls = self; tcls; tcls = class_getSuperclass(tcls)) { + if (tcls == cls) return YES; + } + return NO; +} + ++ (BOOL)isAncestorOfObject:(NSObject *)obj { + for (Class tcls = [obj class]; tcls; tcls = class_getSuperclass(tcls)) { + if (tcls == self) return YES; + } + return NO; +} + ++ (BOOL)instancesRespondToSelector:(SEL)sel { + if (!sel) return NO; + return class_respondsToSelector(self, sel); +} + ++ (BOOL)respondsToSelector:(SEL)sel { + if (!sel) return NO; + return class_respondsToSelector(object_getClass((id)self), sel); +} + +- (BOOL)respondsToSelector:(SEL)sel { + if (!sel) return NO; + return class_respondsToSelector([self class], sel); +} + ++ (BOOL)conformsToProtocol:(Protocol *)protocol { + if (!protocol) return NO; + for (Class tcls = self; tcls; tcls = class_getSuperclass(tcls)) { + if (class_conformsToProtocol(tcls, protocol)) return YES; + } + return NO; +} + +- (BOOL)conformsToProtocol:(Protocol *)protocol { + if (!protocol) return NO; + for (Class tcls = [self class]; tcls; tcls = class_getSuperclass(tcls)) { + if (class_conformsToProtocol(tcls, protocol)) return YES; + } + return NO; +} + ++ (NSUInteger)hash { + return _objc_rootHash(self); +} + +- (NSUInteger)hash { + return _objc_rootHash(self); +} + ++ (BOOL)isEqual:(id)obj { + return obj == (id)self; +} + +- (BOOL)isEqual:(id)obj { + return obj == self; +} + + ++ (BOOL)isFault { + return NO; +} + +- (BOOL)isFault { + return NO; +} + ++ (BOOL)isProxy { + return NO; +} + +- (BOOL)isProxy { + return NO; +} + ++ (BOOL)isBlock { + return NO; +} + +- (BOOL)isBlock { + return NO; +} + + ++ (IMP)instanceMethodForSelector:(SEL)sel { + if (!sel) [self doesNotRecognizeSelector:sel]; + return class_getMethodImplementation(self, sel); +} + ++ (IMP)methodForSelector:(SEL)sel { + if (!sel) [self doesNotRecognizeSelector:sel]; + return class_getMethodImplementation(object_getClass((id)self), sel); +} + +- (IMP)methodForSelector:(SEL)sel { + if (!sel) [self doesNotRecognizeSelector:sel]; + return class_getMethodImplementation([self class], sel); +} + ++ (BOOL)resolveClassMethod:(SEL)sel { + return NO; +} + ++ (BOOL)resolveInstanceMethod:(SEL)sel { + return NO; +} + +// Replaced by CF (throws an NSException) ++ (void)doesNotRecognizeSelector:(SEL)sel { + _objc_fatal("+[%s %s]: unrecognized selector sent to instance %p", + class_getName(self), sel_getName(sel), self); +} + +// Replaced by CF (throws an NSException) +- (void)doesNotRecognizeSelector:(SEL)sel { + _objc_fatal("-[%s %s]: unrecognized selector sent to instance %p", + object_getClassName(self), sel_getName(sel), self); +} + + ++ (id)performSelector:(SEL)sel { + if (!sel) [self doesNotRecognizeSelector:sel]; + return ((id(*)(id, SEL))objc_msgSend)((id)self, sel); +} + ++ (id)performSelector:(SEL)sel withObject:(id)obj { + if (!sel) [self doesNotRecognizeSelector:sel]; + return ((id(*)(id, SEL, id))objc_msgSend)((id)self, sel, obj); +} + ++ (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 { + if (!sel) [self doesNotRecognizeSelector:sel]; + return ((id(*)(id, SEL, id, id))objc_msgSend)((id)self, sel, obj1, obj2); +} + +- (id)performSelector:(SEL)sel { + if (!sel) [self doesNotRecognizeSelector:sel]; + return ((id(*)(id, SEL))objc_msgSend)(self, sel); +} + +- (id)performSelector:(SEL)sel withObject:(id)obj { + if (!sel) [self doesNotRecognizeSelector:sel]; + return ((id(*)(id, SEL, id))objc_msgSend)(self, sel, obj); +} + +- (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 { + if (!sel) [self doesNotRecognizeSelector:sel]; + return ((id(*)(id, SEL, id, id))objc_msgSend)(self, sel, obj1, obj2); +} + + +// Replaced by CF (returns an NSMethodSignature) ++ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)sel { + _objc_fatal("+[NSObject instanceMethodSignatureForSelector:] " + "not available without CoreFoundation"); +} + +// Replaced by CF (returns an NSMethodSignature) ++ (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { + _objc_fatal("+[NSObject methodSignatureForSelector:] " + "not available without CoreFoundation"); +} + +// Replaced by CF (returns an NSMethodSignature) +- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { + _objc_fatal("-[NSObject methodSignatureForSelector:] " + "not available without CoreFoundation"); +} + ++ (void)forwardInvocation:(NSInvocation *)invocation { + [self doesNotRecognizeSelector:(invocation ? [invocation selector] : 0)]; +} + +- (void)forwardInvocation:(NSInvocation *)invocation { + [self doesNotRecognizeSelector:(invocation ? [invocation selector] : 0)]; +} + ++ (id)forwardingTargetForSelector:(SEL)sel { + return nil; +} + +- (id)forwardingTargetForSelector:(SEL)sel { + return nil; +} + + +// Replaced by CF (returns an NSString) ++ (NSString *)description { + return nil; +} + +// Replaced by CF (returns an NSString) +- (NSString *)description { + return nil; +} + ++ (NSString *)debugDescription { + return [self description]; +} + +- (NSString *)debugDescription { + return [self description]; +} + + ++ (id)new { + return [[self alloc] init]; +} + ++ (id)retain { + return (id)self; +} + +// Replaced by ObjectAlloc +- (id)retain +__attribute__((aligned(16))) +{ + if (OBJC_IS_TAGGED_PTR(self)) return self; + + SideTable *table = SideTable::tableForPointer(self); + + if (OSSpinLockTry(&table->slock)) { + table->refcnts[DISGUISE(self)] += 2; + OSSpinLockUnlock(&table->slock); + return self; + } + return _objc_rootRetain_slow(self); +} + + ++ (BOOL)_tryRetain { + return YES; +} + +// Replaced by ObjectAlloc +- (BOOL)_tryRetain { + return _objc_rootTryRetain(self); +} + ++ (BOOL)_isDeallocating { + return NO; +} + +- (BOOL)_isDeallocating { + return _objc_rootIsDeallocating(self); +} + ++ (BOOL)allowsWeakReference { + return YES; +} + ++ (BOOL)retainWeakReference { + return YES; +} + +- (BOOL)allowsWeakReference { + return ! [self _isDeallocating]; +} + +- (BOOL)retainWeakReference { + return [self _tryRetain]; +} + ++ (oneway void)release { +} + +// Replaced by ObjectAlloc +- (oneway void)release +__attribute__((aligned(16))) +{ + // tagged pointer check is inside _objc_rootReleaseWasZero(). + + if (_objc_rootReleaseWasZero(self) == false) { + return; + } + [self dealloc]; +} + ++ (id)autorelease { + return (id)self; +} + +// Replaced by ObjectAlloc +- (id)autorelease +__attribute__((aligned(16))) +{ + // no tag check here: tagged pointers DO use fast autoreleasing + +#if SUPPORT_RETURN_AUTORELEASE + assert(tls_get_direct(AUTORELEASE_POOL_RECLAIM_KEY) == NULL); + + if (callerAcceptsFastAutorelease(__builtin_return_address(0))) { + tls_set_direct(AUTORELEASE_POOL_RECLAIM_KEY, self); + return self; + } +#endif + return _objc_rootAutorelease2(self); +} + ++ (NSUInteger)retainCount { + return ULONG_MAX; +} + +- (NSUInteger)retainCount { + return _objc_rootRetainCount(self); +} + ++ (id)alloc { + return _objc_rootAlloc(self); +} + +// Replaced by ObjectAlloc ++ (id)allocWithZone:(NSZone *)zone { + return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone); +} + +// Replaced by CF (throws an NSException) ++ (id)init { + return (id)self; +} + +- (id)init { + return _objc_rootInit(self); +} + +// Replaced by CF (throws an NSException) ++ (void)dealloc { +} + +// Replaced by NSZombies +- (void)dealloc { + _objc_rootDealloc(self); +} + +// Replaced by CF (throws an NSException) ++ (void)finalize { +} + +- (void)finalize { + _objc_rootFinalize(self); +} + ++ (NSZone *)zone { + return (NSZone *)_objc_rootZone(self); +} + +- (NSZone *)zone { + return (NSZone *)_objc_rootZone(self); +} + ++ (id)copy { + return (id)self; +} + ++ (id)copyWithZone:(NSZone *)zone { + return (id)self; +} + +- (id)copy { + return [(id)self copyWithZone:NULL]; +} + ++ (id)mutableCopy { + return (id)self; +} + ++ (id)mutableCopyWithZone:(NSZone *)zone { + return (id)self; +} + +- (id)mutableCopy { + return [(id)self mutableCopyWithZone:NULL]; +} + +@end + +__attribute__((aligned(16))) +id +objc_retain(id obj) +{ + if (!obj || OBJC_IS_TAGGED_PTR(obj)) { + goto out_slow; + } +#if __OBJC2__ + if (((class_t *)obj->isa)->hasCustomRR()) { + return [obj retain]; + } + return bypass_msgSend_retain(obj); +#else + return [obj retain]; +#endif + out_slow: + // clang really wants to reorder the "mov %rdi, %rax" early + // force better code gen with a data barrier + asm volatile(""); + return obj; +} + +__attribute__((aligned(16))) +void +objc_release(id obj) +{ + if (!obj || OBJC_IS_TAGGED_PTR(obj)) { + return; + } +#if __OBJC2__ + if (((class_t *)obj->isa)->hasCustomRR()) { + return (void)[obj release]; + } + return bypass_msgSend_release(obj); +#else + [obj release]; +#endif +} + +__attribute__((aligned(16))) +id +objc_autorelease(id obj) +{ + if (!obj || OBJC_IS_TAGGED_PTR(obj)) { + goto out_slow; + } +#if __OBJC2__ + if (((class_t *)obj->isa)->hasCustomRR()) { + return [obj autorelease]; + } + return bypass_msgSend_autorelease(obj); +#else + return [obj autorelease]; +#endif + out_slow: + // clang really wants to reorder the "mov %rdi, %rax" early + // force better code gen with a data barrier + asm volatile(""); + return obj; +} + +id +_objc_rootRetain(id obj) +{ + assert(obj); + assert(!UseGC); + + if (OBJC_IS_TAGGED_PTR(obj)) return obj; + + return bypass_msgSend_retain(obj); +} + +void +_objc_rootRelease(id obj) +{ + assert(obj); + assert(!UseGC); + + if (OBJC_IS_TAGGED_PTR(obj)) return; + + bypass_msgSend_release(obj); +} + +id +_objc_rootAutorelease(id obj) +{ + assert(obj); // root classes shouldn't get here, since objc_msgSend ignores nil + // assert(!UseGC); + + if (UseGC) { + return obj; + } + + // no tag check here: tagged pointers DO use fast autoreleasing + + return bypass_msgSend_autorelease(obj); +} diff --git a/runtime/Object.h b/runtime/Object.h index 5c37078..2cd0fc1 100644 --- a/runtime/Object.h +++ b/runtime/Object.h @@ -33,22 +33,9 @@ #define _OBJC_OBJECT_H_ #include -#import +#include -#if __OBJC2__ - -__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA) -@interface Object -{ - Class isa; /* A pointer to the instance's class structure */ -} - -+class; --(BOOL) isEqual:anObject; - -@end - -#else +#if ! __OBJC2__ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA) @interface Object @@ -58,32 +45,32 @@ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA) /* Initializing classes and instances */ -+ initialize; -- init; ++ (id)initialize; +- (id)init; /* Creating, copying, and freeing instances */ -+ new; -+ free; -- free; -+ alloc; -- copy; -+ allocFromZone:(void *)zone; -- copyFromZone:(void *)zone; ++ (id)new; ++ (id)free; +- (id)free; ++ (id)alloc; +- (id)copy; ++ (id)allocFromZone:(void *)zone; +- (id)copyFromZone:(void *)zone; - (void *)zone; /* Identifying classes */ -+ class; -+ superclass; ++ (id)class; ++ (id)superclass; + (const char *) name; -- class; -- superclass; +- (id)class; +- (id)superclass; - (const char *) name; /* Identifying and comparing instances */ -- self; +- (id)self; - (unsigned int) hash; - (BOOL) isEqual:anObject; @@ -116,23 +103,23 @@ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA) /* Sending messages determined at run time */ -- perform:(SEL)aSelector; -- perform:(SEL)aSelector with:anObject; -- perform:(SEL)aSelector with:object1 with:object2; +- (id)perform:(SEL)aSelector; +- (id)perform:(SEL)aSelector with:anObject; +- (id)perform:(SEL)aSelector with:object1 with:object2; /* Posing */ -+ poseAs: aClassObject; ++ (id)poseAs: aClassObject; /* Enforcing intentions */ -- subclassResponsibility:(SEL)aSelector; -- notImplemented:(SEL)aSelector; +- (id)subclassResponsibility:(SEL)aSelector; +- (id)notImplemented:(SEL)aSelector; /* Error handling */ -- doesNotRecognize:(SEL)aSelector; -- error:(const char *)aString, ...; +- (id)doesNotRecognize:(SEL)aSelector; +- (id)error:(const char *)aString, ...; /* Debugging */ @@ -140,16 +127,16 @@ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA) /* Archiving */ -- awake; -- write:(void *)stream; -- read:(void *)stream; +- (id)awake; +- (id)write:(void *)stream; +- (id)read:(void *)stream; + (int) version; -+ setVersion: (int) aVersion; ++ (id)setVersion: (int) aVersion; /* Forwarding */ -- forward: (SEL)sel : (marg_list)args; -- performv: (SEL)sel : (marg_list)args; +- (id)forward: (SEL)sel : (marg_list)args; +- (id)performv: (SEL)sel : (marg_list)args; @end @@ -157,8 +144,8 @@ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA) @interface Object (Archiving) -- startArchiving: (void *)stream; -- finishUnarchiving; +- (id)startArchiving: (void *)stream; +- (id)finishUnarchiving; @end @@ -167,8 +154,9 @@ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA) @interface Object (DynamicLoading) //+ finishLoading:(headerType *)header; -+ finishLoading:(struct mach_header *)header; -+ startUnloading; +struct mach_header; ++ (id)finishLoading:(struct mach_header *)header; ++ (id)startUnloading; @end diff --git a/runtime/Object.m b/runtime/Object.m index bea2cb9..0589d59 100644 --- a/runtime/Object.m +++ b/runtime/Object.m @@ -27,33 +27,72 @@ #if __OBJC2__ -#import "Object.h" +#include "objc-private.h" -@implementation Object +__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA) +@interface Object { + Class isa; +} +@end + +@implementation Object -+ initialize ++ (id)initialize { return self; } -+ class ++ (id)class +{ + return self; +} + +-(id) retain +{ + return _objc_rootRetain(self); +} + +-(void) release +{ + _objc_rootRelease(self); +} + +-(id) autorelease +{ + return _objc_rootAutorelease(self); +} + ++(id) retain { return self; } ++(void) release +{ +} + ++(id) autorelease +{ + return self; +} + + @end + +// __OBJC2__ #else +// not __OBJC2__ -#import -#import -#import -#import +#include +#include +#include +#include -#import "Object.h" -#import "Protocol.h" -#import "objc-runtime.h" -#import "objc-auto.h" +#include "Object.h" +#include "Protocol.h" +#include "objc-runtime.h" +#include "objc-auto.h" // hack extern void _objc_error(id, const char *, va_list); @@ -68,25 +107,25 @@ static const char _errDoesntRecognize[] = "does not recognize selector %c%s"; -@implementation Object +@implementation Object -+ initialize ++ (id)initialize { return self; } -- awake +- (id)awake { return self; } -+ poseAs: aFactory ++ (id)poseAs: aFactory { return class_poseAs(self, aFactory); } -+ new ++ (id)new { id newObject = (*_alloc)((Class)self, 0); Class metaClass = self->isa; @@ -96,17 +135,17 @@ static const char return newObject; } -+ alloc ++ (id)alloc { return (*_zoneAlloc)((Class)self, 0, malloc_default_zone()); } -+ allocFromZone:(void *) z ++ (id)allocFromZone:(void *) z { return (*_zoneAlloc)((Class)self, 0, z); } -- init +- (id)init { return self; } @@ -131,27 +170,28 @@ static const char return anObject == self; } -- free +- (id)free { return (*_dealloc)(self); } -+ free ++ (id)free { return nil; } -- self +- (id)self { return self; } -- class + +-(id)class { return (id)isa; } -+ class ++ (id)class { return self; } @@ -162,12 +202,12 @@ static const char return z ? z : malloc_default_zone(); } -+ superclass ++ (id)superclass { return class_getSuperclass((Class)self); } -- superclass +- (id)superclass { return class_getSuperclass(isa); } @@ -177,7 +217,7 @@ static const char return class_getVersion((Class)self); } -+ setVersion: (int) aVersion ++ (id)setVersion: (int) aVersion { class_setVersion((Class)self, aVersion); return self; @@ -206,82 +246,82 @@ static const char return NO; } -- (BOOL)isMemberOfClassNamed:(const char *)aClassName +- (BOOL)isMemberOfClassNamed:(const char *)aClassName { return strcmp(aClassName, class_getName(isa)) == 0; } -+ (BOOL)instancesRespondTo:(SEL)aSelector ++ (BOOL)instancesRespondTo:(SEL)aSelector { return class_respondsToMethod((Class)self, aSelector); } -- (BOOL)respondsTo:(SEL)aSelector +- (BOOL)respondsTo:(SEL)aSelector { return class_respondsToMethod(isa, aSelector); } -- copy +- (id)copy { return [self copyFromZone: [self zone]]; } -- copyFromZone:(void *)z +- (id)copyFromZone:(void *)z { return (*_zoneCopy)(self, 0, z); } -- (IMP)methodFor:(SEL)aSelector +- (IMP)methodFor:(SEL)aSelector { return class_lookupMethod(isa, aSelector); } -+ (IMP)instanceMethodFor:(SEL)aSelector ++ (IMP)instanceMethodFor:(SEL)aSelector { return class_lookupMethod(self, aSelector); } -- perform:(SEL)aSelector +- (id)perform:(SEL)aSelector { if (aSelector) - return objc_msgSend(self, aSelector); + return ((id(*)(id, SEL))objc_msgSend)(self, aSelector); else return [self error:_errBadSel, sel_getName(_cmd), aSelector]; } -- perform:(SEL)aSelector with:anObject +- (id)perform:(SEL)aSelector with:anObject { if (aSelector) - return objc_msgSend(self, aSelector, anObject); + return ((id(*)(id, SEL, id))objc_msgSend)(self, aSelector, anObject); else return [self error:_errBadSel, sel_getName(_cmd), aSelector]; } -- perform:(SEL)aSelector with:obj1 with:obj2 +- (id)perform:(SEL)aSelector with:obj1 with:obj2 { if (aSelector) - return objc_msgSend(self, aSelector, obj1, obj2); + return ((id(*)(id, SEL, id, id))objc_msgSend)(self, aSelector, obj1, obj2); else return [self error:_errBadSel, sel_getName(_cmd), aSelector]; } -- subclassResponsibility:(SEL)aSelector +- (id)subclassResponsibility:(SEL)aSelector { return [self error:_errShouldHaveImp, sel_getName(aSelector)]; } -- notImplemented:(SEL)aSelector +- (id)notImplemented:(SEL)aSelector { return [self error:_errLeftUndone, sel_getName(aSelector)]; } -- doesNotRecognize:(SEL)aMessage +- (id)doesNotRecognize:(SEL)aMessage { return [self error:_errDoesntRecognize, class_isMetaClass(isa) ? '+' : '-', sel_getName(aMessage)]; } -- error:(const char *)aCStr, ... +- (id)error:(const char *)aCStr, ... { va_list ap; va_start(ap,aCStr); @@ -295,31 +335,31 @@ static const char { } -- write:(void *) stream +- (id)write:(void *) stream { return self; } -- read:(void *) stream +- (id)read:(void *) stream { return self; } -- forward: (SEL) sel : (marg_list) args +- (id)forward: (SEL) sel : (marg_list) args { return [self doesNotRecognize: sel]; } /* this method is not part of the published API */ -- (unsigned)methodArgSize:(SEL)sel +- (unsigned)methodArgSize:(SEL)sel { Method method = class_getInstanceMethod((Class)isa, sel); if (! method) return 0; return method_getSizeOfArguments(method); } -- performv: (SEL) sel : (marg_list) args +- (id)performv: (SEL) sel : (marg_list) args { unsigned size; @@ -406,7 +446,6 @@ static const char while ( (mlist = class_nextMethodList( cls, &iterator )) ) { for (i = 0; i < mlist->method_count; i++) if (mlist->method_list[i].method_name == aSelector) { - struct objc_method_description *m; m = (struct objc_method_description *)&mlist->method_list[i]; return m; } @@ -467,12 +506,12 @@ static const char /* Obsolete methods (for binary compatibility only). */ -+ superClass ++ (id)superClass { return [self superclass]; } -- superClass +- (id)superClass { return [self superclass]; } @@ -482,7 +521,7 @@ static const char return [self isKindOfClassNamed: aClassName]; } -- (BOOL)isMemberOfGivenName:(const char *)aClassName +- (BOOL)isMemberOfGivenName:(const char *)aClassName { return [self isMemberOfClassNamed: aClassName]; } @@ -497,12 +536,12 @@ static const char return [self descriptionForInstanceMethod: aSelector]; } -- findClass:(const char *)aClassName +- (id)findClass:(const char *)aClassName { return objc_lookUpClass(aClassName); } -- shouldNotImplement:(SEL)aSelector +- (id)shouldNotImplement:(SEL)aSelector { return [self error:_errShouldNotImp, sel_getName(aSelector)]; } diff --git a/runtime/OldClasses.subproj/List.h b/runtime/OldClasses.subproj/List.h index a67cdad..bec09da 100644 --- a/runtime/OldClasses.subproj/List.h +++ b/runtime/OldClasses.subproj/List.h @@ -34,8 +34,8 @@ #if !__OBJC2__ -#import -#import +#include +#include AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED @interface List : Object @@ -48,14 +48,14 @@ AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED /* Creating, freeing */ -- free AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- freeObjects AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- copyFromZone:(void *)z AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)free AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)freeObjects AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)copyFromZone:(void *)z AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; /* Initializing */ -- init AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- initCount:(unsigned)numSlots AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)init AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)initCount:(unsigned)numSlots AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; /* Comparing two lists */ @@ -64,35 +64,35 @@ AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED /* Managing the storage capacity */ - (unsigned)capacity AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- setAvailableCapacity:(unsigned)numSlots AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)setAvailableCapacity:(unsigned)numSlots AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; /* Manipulating objects by index */ - (unsigned)count AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- objectAt:(unsigned)index AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- lastObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- addObject:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- insertObject:anObject at:(unsigned)index AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- removeObjectAt:(unsigned)index AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- removeLastObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- replaceObjectAt:(unsigned)index with:newObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- appendList: (List *)otherList AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)objectAt:(unsigned)index AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)lastObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)addObject:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)insertObject:anObject at:(unsigned)index AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)removeObjectAt:(unsigned)index AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)removeLastObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)replaceObjectAt:(unsigned)index with:newObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)appendList: (List *)otherList AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; /* Manipulating objects by id */ - (unsigned)indexOf:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- addObjectIfAbsent:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- removeObject:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- replaceObject:anObject with:newObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)addObjectIfAbsent:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)removeObject:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)replaceObject:anObject with:newObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; /* Emptying the list */ -- empty AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)empty AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; /* Sending messages to elements of the list */ -- makeObjectsPerform:(SEL)aSelector AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -- makeObjectsPerform:(SEL)aSelector with:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)makeObjectsPerform:(SEL)aSelector AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; +- (id)makeObjectsPerform:(SEL)aSelector with:anObject AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; /* * The following new... methods are now obsolete. They remain in this @@ -100,13 +100,13 @@ AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED * and the init... methods defined in this class instead. */ -+ new AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; -+ newCount:(unsigned)numSlots AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; ++ (id)new AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; ++ (id)newCount:(unsigned)numSlots AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; @end typedef struct { - @defs(List) + @defs(List); } NXListId AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED; #define NX_ADDRESS(x) (((NXListId *)(x))->dataPtr) diff --git a/runtime/OldClasses.subproj/List.m b/runtime/OldClasses.subproj/List.m index b9217fc..a46849c 100644 --- a/runtime/OldClasses.subproj/List.m +++ b/runtime/OldClasses.subproj/List.m @@ -29,23 +29,23 @@ #ifndef __OBJC2__ -#import -#import -#import +#include +#include +#include -#import +#include #define DATASIZE(count) ((count) * sizeof(id)) @implementation List -+ initialize ++ (id)initialize { [self setVersion: 1]; return self; } -- initCount:(unsigned)numSlots +- (id)initCount:(unsigned)numSlots { maxElements = numSlots; if (maxElements) @@ -53,28 +53,28 @@ return self; } -+ newCount:(unsigned)numSlots ++ (id)newCount:(unsigned)numSlots { return [[self alloc] initCount:numSlots]; } -+ new ++ (id)new { return [self newCount:0]; } -- init +- (id)init { return [self initCount:0]; } -- free +- (id)free { free(dataPtr); return [super free]; } -- freeObjects +- (id)freeObjects { id element; while ((element = [self removeLastObject])) @@ -82,7 +82,7 @@ return self; } -- copyFromZone:(void *)z +- (id)copyFromZone:(void *)z { List *new = [[[self class] alloc] initCount: numElements]; new->numElements = numElements; @@ -109,7 +109,7 @@ return numElements; } -- objectAt:(unsigned)index +- (id)objectAt:(unsigned)index { if (index >= numElements) return nil; @@ -128,14 +128,14 @@ return NX_NOT_IN_LIST; } -- lastObject +- (id)lastObject { if (! numElements) return nil; return dataPtr[numElements - 1]; } -- setAvailableCapacity:(unsigned)numSlots +- (id)setAvailableCapacity:(unsigned)numSlots { volatile id *tempDataPtr; if (numSlots < numElements) return nil; @@ -145,7 +145,7 @@ return self; } -- insertObject:anObject at:(unsigned)index +- (id)insertObject:anObject at:(unsigned)index { register id *this, *last, *prev; if (! anObject) return nil; @@ -168,14 +168,14 @@ return self; } -- addObject:anObject +- (id)addObject:anObject { return [self insertObject:anObject at:numElements]; } -- addObjectIfAbsent:anObject +- (id)addObjectIfAbsent:anObject { register id *this, *last; if (! anObject) return nil; @@ -191,7 +191,7 @@ } -- removeObjectAt:(unsigned)index +- (id)removeObjectAt:(unsigned)index { register id *this, *last, *next; id retval; @@ -207,7 +207,7 @@ return retval; } -- removeObject:anObject +- (id)removeObject:anObject { register id *this, *last; this = dataPtr; @@ -220,20 +220,20 @@ return nil; } -- removeLastObject +- (id)removeLastObject { if (! numElements) return nil; return [self removeObjectAt: numElements - 1]; } -- empty +- (id)empty { numElements = 0; return self; } -- replaceObject:anObject with:newObject +- (id)replaceObject:anObject with:newObject { register id *this, *last; if (! newObject) @@ -250,7 +250,7 @@ return nil; } -- replaceObjectAt:(unsigned)index with:newObject +- (id)replaceObjectAt:(unsigned)index with:newObject { register id *this; id retval; @@ -264,7 +264,7 @@ return retval; } -- makeObjectsPerform:(SEL)aSelector +- (id)makeObjectsPerform:(SEL)aSelector { unsigned count = numElements; while (count--) @@ -272,7 +272,7 @@ return self; } -- makeObjectsPerform:(SEL)aSelector with:anObject +- (id)makeObjectsPerform:(SEL)aSelector with:anObject { unsigned count = numElements; while (count--) @@ -280,7 +280,7 @@ return self; } --appendList: (List *)otherList +-(id)appendList: (List *)otherList { unsigned i, count; diff --git a/runtime/Protocol.h b/runtime/Protocol.h index 698b922..07bdfb5 100644 --- a/runtime/Protocol.h +++ b/runtime/Protocol.h @@ -28,9 +28,20 @@ #ifndef _OBJC_PROTOCOL_H_ #define _OBJC_PROTOCOL_H_ -#include +#if __OBJC2__ + +#include -/* Warning: All of these methods will disappear in 64-bit. */ +// All methods of class Protocol are unavailable. +// Use the functions in objc/runtime.h instead. + +__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0) +@interface Protocol : NSObject +@end + +#else + +#include __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0) @interface Protocol : Object @@ -59,4 +70,6 @@ __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0) @end +#endif + #endif /* _OBJC_PROTOCOL_H_ */ diff --git a/runtime/Protocol.m b/runtime/Protocol.mm similarity index 80% rename from runtime/Protocol.m rename to runtime/Protocol.mm index a3c9f09..f9714e1 100644 --- a/runtime/Protocol.m +++ b/runtime/Protocol.mm @@ -31,15 +31,17 @@ #include #include -#import "Protocol.h" -#import "objc-private.h" -#import "objc-runtime-old.h" +#include "Protocol.h" +#include "objc-private.h" +#include "objc-runtime-old.h" -PRIVATE_EXTERN -@interface __IncompleteProtocol { id isa; } @end +#if __OBJC2__ +@interface __IncompleteProtocol : NSObject @end @implementation __IncompleteProtocol -+(void) initialize { } +// fixme hack - make __IncompleteProtocol a non-lazy class ++ (void) load { } @end +#endif @implementation Protocol @@ -63,9 +65,11 @@ typedef struct { { #if !__OBJC2__ return lookup_protocol_method((struct old_protocol *)self, aSel, - YES/*required*/, YES/*instance*/); + YES/*required*/, YES/*instance*/, + YES/*recursive*/); #else - return method_getDescription(_protocol_getMethod(self, aSel, YES, YES)); + return method_getDescription(_protocol_getMethod(self, aSel, + YES, YES, YES)); #endif } @@ -73,9 +77,11 @@ typedef struct { { #if !__OBJC2__ return lookup_protocol_method((struct old_protocol *)self, aSel, - YES/*required*/, NO/*instance*/); + YES/*required*/, NO/*instance*/, + YES/*recursive*/); #else - return method_getDescription(_protocol_getMethod(self, aSel, YES, NO)); + return method_getDescription(_protocol_getMethod(self, aSel, + YES, NO, YES)); #endif } @@ -90,7 +96,7 @@ typedef struct { // check isKindOf: Class cls; Class protoClass = objc_getClass("Protocol"); - for (cls = other->isa; cls; cls = class_getSuperclass(cls)) { + for (cls = object_getClass(other); cls; cls = class_getSuperclass(cls)) { if (cls == protoClass) break; } if (!cls) return NO; @@ -101,9 +107,16 @@ typedef struct { #endif } -- (unsigned int)hash +#if __OBJC2__ +- (NSUInteger)hash { return 23; } +#else +- (unsigned)hash +{ + return 23; +} +#endif @end diff --git a/runtime/a1a2-blocktramps-arm.s b/runtime/a1a2-blocktramps-arm.s index ca4ad44..36c256e 100644 --- a/runtime/a1a2-blocktramps-arm.s +++ b/runtime/a1a2-blocktramps-arm.s @@ -11,7 +11,14 @@ .private_extern __a1a2_nexttramp .private_extern __a1a2_trampend -#ifdef _ARM_ARCH_7 +// This must match a2a3-blocktramps-arm.s +#if defined(_ARM_ARCH_7) +# define THUMB2 1 +#else +# define THUMB2 0 +#endif + +#if THUMB2 .thumb .thumb_func __a1a2_tramphead .thumb_func __a1a2_firsttramp @@ -23,6 +30,7 @@ #endif .align 12 +__a1a2_tramphead_nt: __a1a2_tramphead: /* r0 == self @@ -31,7 +39,7 @@ __a1a2_tramphead: */ // calculate the trampoline's index (512 entries, 8 bytes each) -#ifdef _ARM_ARCH_7 +#if THUMB2 // PC bias is only 4, no need to correct with 8-byte trampolines ubfx r1, r1, #3, #9 #else @@ -41,7 +49,8 @@ __a1a2_tramphead: #endif // load block pointer from trampoline's data - adr r12, __a1a2_tramphead // text page + // nt label works around thumb integrated asm bug rdar://11315197 + adr r12, __a1a2_tramphead_nt // text page sub r12, r12, #4096 // data page precedes text page ldr r12, [r12, r1, LSL #3] // load block pointer from data + index*8 @@ -55,7 +64,7 @@ __a1a2_tramphead: // Make v6 and v7 match so they have the same number of TrampolineEntry // below. Debug asserts in objc-block-trampoline.m check this. -#ifdef _ARM_ARCH_7 +#if THUMB2 .space 16 #endif diff --git a/runtime/a1a2-blocktramps-i386.s b/runtime/a1a2-blocktramps-i386.s index 641160e..30cb794 100755 --- a/runtime/a1a2-blocktramps-i386.s +++ b/runtime/a1a2-blocktramps-i386.s @@ -561,4 +561,4 @@ __a1a2_nexttramp: // used to calculate size of each trampoline __a1a2_trampend: -#endif \ No newline at end of file +#endif diff --git a/runtime/a2a3-blocktramps-arm.s b/runtime/a2a3-blocktramps-arm.s index 7904bb3..c47386b 100644 --- a/runtime/a2a3-blocktramps-arm.s +++ b/runtime/a2a3-blocktramps-arm.s @@ -6,7 +6,15 @@ .text -#ifdef _ARM_ARCH_7 + +// This must match a1a2-blocktramps-arm.s +#if defined(_ARM_ARCH_7) +# define THUMB2 1 +#else +# define THUMB2 0 +#endif + +#if THUMB2 .thumb .thumb_func __a2a3_tramphead .thumb_func __a2a3_firsttramp @@ -19,6 +27,7 @@ .align 12 .private_extern __a2a3_tramphead +__a2a3_tramphead_nt: __a2a3_tramphead: /* r0 == stret @@ -28,7 +37,7 @@ __a2a3_tramphead: */ // calculate the trampoline's index (512 entries, 8 bytes each) -#ifdef _ARM_ARCH_7 +#if THUMB2 // PC bias is only 4, no need to correct with 8-byte trampolines ubfx r2, r2, #3, #9 #else @@ -38,7 +47,8 @@ __a2a3_tramphead: #endif // load block pointer from trampoline's data - adr r12, __a2a3_tramphead // text page + // nt label works around thumb integrated asm bug rdar://11315197 + adr r12, __a2a3_tramphead_nt // text page sub r12, r12, #4096 // data page precedes text page ldr r12, [r12, r2, LSL #3] // load block pointer from data + index*8 @@ -52,7 +62,7 @@ __a2a3_tramphead: // Make v6 and v7 match so they have the same number of TrampolineEntry // below. Debug asserts in objc-block-trampoline.m check this. -#ifdef _ARM_ARCH_7 +#if THUMB2 .space 16 #endif diff --git a/runtime/a2a3-blocktramps-i386.s b/runtime/a2a3-blocktramps-i386.s index a9e2637..d9109e7 100755 --- a/runtime/a2a3-blocktramps-i386.s +++ b/runtime/a2a3-blocktramps-i386.s @@ -561,4 +561,4 @@ __a2a3_nexttramp: __a2a3_trampend: -#endif \ No newline at end of file +#endif diff --git a/runtime/hashtable2.m b/runtime/hashtable2.mm similarity index 92% rename from runtime/hashtable2.m rename to runtime/hashtable2.mm index bbaee1f..7fdeb77 100644 --- a/runtime/hashtable2.m +++ b/runtime/hashtable2.mm @@ -57,13 +57,17 @@ static unsigned log2u (unsigned x) { return (x<2) ? 0 : log2u (x>>1)+1; }; # define ZONE_FROM_PTR(p) NULL # define ALLOCTABLE(z) ((NXHashTable *) malloc (sizeof (NXHashTable))) # define ALLOCBUCKETS(z,nb)((HashBucket *) calloc (nb, sizeof (HashBucket))) -# define ALLOCPAIRS(z,nb) ((const void **) calloc (nb, sizeof (void *))) +/* Return interior pointer so a table of classes doesn't look like objects */ +# define ALLOCPAIRS(z,nb) (1+(const void **) calloc (nb+1, sizeof (void *))) +# define FREEPAIRS(p) (free((void*)(-1+p))) #else # define DEFAULT_ZONE malloc_default_zone() # define ZONE_FROM_PTR(p) malloc_zone_from_ptr(p) -# define ALLOCTABLE(z) ((NXHashTable *) malloc_zone_malloc (z,sizeof (NXHashTable))) -# define ALLOCBUCKETS(z,nb)((HashBucket *) malloc_zone_calloc (z, nb, sizeof (HashBucket))) -# define ALLOCPAIRS(z,nb) ((const void **) malloc_zone_calloc (z, nb, sizeof (void *))) +# define ALLOCTABLE(z) ((NXHashTable *) malloc_zone_malloc ((malloc_zone_t *)z,sizeof (NXHashTable))) +# define ALLOCBUCKETS(z,nb)((HashBucket *) malloc_zone_calloc ((malloc_zone_t *)z, nb, sizeof (HashBucket))) +/* Return interior pointer so a table of classes doesn't look like objects */ +# define ALLOCPAIRS(z,nb) (1+(const void **) malloc_zone_calloc ((malloc_zone_t *)z, nb+1, sizeof (void *))) +# define FREEPAIRS(p) (free((void*)(-1+p))) #endif #if !SUPPORT_MOD @@ -98,7 +102,7 @@ static int isEqualPrototype (const void *info, const void *data1, const void *da static uintptr_t hashPrototype (const void *info, const void *data) { NXHashTablePrototype *proto = (NXHashTablePrototype *) data; - return NXPtrHash(info, proto->hash) ^ NXPtrHash(info, proto->isEqual) ^ NXPtrHash(info, proto->free) ^ (uintptr_t) proto->style; + return NXPtrHash(info, (void*)proto->hash) ^ NXPtrHash(info, (void*)proto->isEqual) ^ NXPtrHash(info, (void*)proto->free) ^ (uintptr_t) proto->style; }; void NXNoEffectFree (const void *info, void *data) {}; @@ -149,13 +153,13 @@ NXHashTable *NXCreateHashTableFromZone (NXHashTablePrototype prototype, unsigned _objc_inform ("*** NXCreateHashTable: invalid style\n"); return NULL; }; - proto = NXHashGet (prototypes, &prototype); + proto = (NXHashTablePrototype *)NXHashGet (prototypes, &prototype); if (! proto) { proto = (NXHashTablePrototype *) malloc(sizeof (NXHashTablePrototype)); bcopy ((const char*)&prototype, (char*)proto, sizeof (NXHashTablePrototype)); (void) NXHashInsert (prototypes, proto); - proto = NXHashGet (prototypes, &prototype); + proto = (NXHashTablePrototype *)NXHashGet (prototypes, &prototype); if (! proto) { _objc_inform ("*** NXCreateHashTable: bug\n"); return NULL; @@ -180,7 +184,7 @@ static void freeBucketPairs (void (*freeProc)(const void *info, void *data), Has (*freeProc) (info, (void *) *pairs); pairs ++; }; - free ((void*)bucket.elements.many); + FREEPAIRS (bucket.elements.many); }; static void freeBuckets (NXHashTable *table, int freeObjects) { @@ -283,11 +287,11 @@ void *NXHashGet (NXHashTable *table, const void *data) { return NULL; } -PRIVATE_EXTERN unsigned _NXHashCapacity (NXHashTable *table) { +unsigned _NXHashCapacity (NXHashTable *table) { return table->nbBuckets; } -PRIVATE_EXTERN void _NXHashRehashToCapacity (NXHashTable *table, unsigned newCapacity) { +void _NXHashRehashToCapacity (NXHashTable *table, unsigned newCapacity) { /* Rehash: we create a pseudo table pointing really to the old guys, extend self, copy the old pairs, and free the pseudo table */ NXHashTable *old; @@ -354,7 +358,7 @@ void *NXHashInsert (NXHashTable *table, const void *data) { newt = ALLOCPAIRS(z, bucket->count+1); if (bucket->count) bcopy ((const char*)bucket->elements.many, (char*)(newt+1), bucket->count * PTRSIZE); *newt = data; - free ((void*)bucket->elements.many); + FREEPAIRS (bucket->elements.many); bucket->count++; bucket->elements.many = newt; table->count++; if (table->count > table->nbBuckets) _NXHashRehash (table); @@ -395,7 +399,7 @@ void *NXHashInsertIfAbsent (NXHashTable *table, const void *data) { newt = ALLOCPAIRS(z, bucket->count+1); if (bucket->count) bcopy ((const char*)bucket->elements.many, (char*)(newt+1), bucket->count * PTRSIZE); *newt = data; - free ((void*)bucket->elements.many); + FREEPAIRS (bucket->elements.many); bucket->count++; bucket->elements.many = newt; table->count++; if (table->count > table->nbBuckets) _NXHashRehash (table); @@ -425,7 +429,7 @@ void *NXHashRemove (NXHashTable *table, const void *data) { bucket->elements.one = pairs[0]; data = pairs[1]; } else return NULL; - free ((void*)pairs); + FREEPAIRS (pairs); table->count--; bucket->count--; return (void *) data; }; @@ -439,7 +443,7 @@ void *NXHashRemove (NXHashTable *table, const void *data) { bcopy ((const char*)bucket->elements.many, (char*)newt, PTRSIZE*(bucket->count-j-1)); if (j) bcopy ((const char*)(bucket->elements.many + bucket->count-j), (char*)(newt+bucket->count-j-1), PTRSIZE*j); - free ((void*)bucket->elements.many); + FREEPAIRS (bucket->elements.many); table->count--; bucket->count--; bucket->elements.many = newt; return (void *) data; }; @@ -565,27 +569,27 @@ static mutex_t lock = MUTEX_INITIALIZER; static const char *CopyIntoReadOnly (const char *str) { size_t len = strlen (str) + 1; - char *new; + char *result; if (len > CHUNK_SIZE/2) { /* dont let big strings waste space */ - new = malloc (len); - bcopy (str, new, len); - return new; + result = (char *)malloc (len); + bcopy (str, result, len); + return result; } mutex_lock (&lock); if (zSize < len) { zSize = CHUNK_SIZE *((len + CHUNK_SIZE - 1) / CHUNK_SIZE); /* not enough room, we try to allocate. If no room left, too bad */ - z = malloc (zSize); + z = (char *)malloc (zSize); }; - new = z; - bcopy (str, new, len); + result = z; + bcopy (str, result, len); z += len; zSize -= len; mutex_unlock (&lock); - return new; + return result; }; NXAtom NXUniqueString (const char *buffer) { @@ -620,7 +624,7 @@ NXAtom NXUniqueStringWithLength (const char *buffer, int length) { char stackBuf[BUF_SIZE]; if (length+1 > BUF_SIZE) - nullTermStr = malloc (length+1); + nullTermStr = (char *)malloc (length+1); else nullTermStr = stackBuf; bcopy (buffer, nullTermStr, length); @@ -631,11 +635,11 @@ NXAtom NXUniqueStringWithLength (const char *buffer, int length) { return atom; }; -char *NXCopyStringBufferFromZone (const char *str, void *z) { +char *NXCopyStringBufferFromZone (const char *str, void *zone) { #if !SUPPORT_ZONES return strdup(str); #else - return strcpy ((char *) malloc_zone_malloc(z, strlen (str) + 1), str); + return strcpy ((char *) malloc_zone_malloc((malloc_zone_t *)zone, strlen (str) + 1), str); #endif }; diff --git a/runtime/llvm-DenseMap.h b/runtime/llvm-DenseMap.h index 0948e00..6c65726 100644 --- a/runtime/llvm-DenseMap.h +++ b/runtime/llvm-DenseMap.h @@ -24,7 +24,6 @@ #include #include - namespace objc { #if TARGET_OS_IPHONE @@ -654,6 +653,7 @@ private: bool LookupBucketFor(const KeyT &Val, BucketT *&FoundBucket) const { unsigned BucketNo = getHashValue(Val); unsigned ProbeAmt = 1; + unsigned ProbeCount = 0; BucketT *BucketsPtr = Buckets; // FoundTombstone - Keep track of whether we find a tombstone or zero value while probing. @@ -664,7 +664,7 @@ private: !KeyInfoT::isEqual(Val, TombstoneKey) && "Empty/Tombstone value shouldn't be inserted into map!"); - while (1) { + do { BucketT *ThisBucket = BucketsPtr + (BucketNo & (NumBuckets-1)); // Found Val's bucket? If so, return it. if (KeyInfoT::isEqual(ThisBucket->first, Val)) { @@ -694,7 +694,19 @@ private: // Otherwise, it's a hash collision or a tombstone, continue quadratic // probing. BucketNo += ProbeAmt++; + ProbeCount++; + } while (ProbeCount < NumBuckets); + // If we get here then we did not find a bucket. This is a bug. Emit some diagnostics and abort. + unsigned EmptyCount = 0, TombstoneCount = 0, ZeroCount = 0, ValueCount = 0; + BucketsPtr = Buckets; + for (unsigned i=0; ifirst, EmptyKey)) EmptyCount++; + else if (KeyInfoT::isEqual(BucketsPtr->first, TombstoneKey)) TombstoneCount++; + else if (KeyInfoT::isEqual(BucketsPtr->first, 0)) ZeroCount++; + else ValueCount++; + BucketsPtr++; } + _objc_fatal("DenseMap::LookupBucketFor() failed to find available bucket.\nNumBuckets = %d, EmptyCount = %d, TombstoneCount = %d, ZeroCount = %d, ValueCount = %d\n", NumBuckets, EmptyCount, TombstoneCount, ZeroCount, ValueCount); } void init(unsigned InitBuckets) { diff --git a/runtime/maptable.m b/runtime/maptable.mm similarity index 91% rename from runtime/maptable.m rename to runtime/maptable.mm index d60126c..7efa00a 100644 --- a/runtime/maptable.m +++ b/runtime/maptable.mm @@ -71,12 +71,16 @@ static INLINE unsigned nextIndex(NXMapTable *table, unsigned index) { } static INLINE void *allocBuckets(void *z, unsigned nb) { - MapPair *pairs = malloc_zone_malloc(z, (nb * sizeof(MapPair))); + MapPair *pairs = 1+(MapPair *)malloc_zone_malloc((malloc_zone_t *)z, ((nb+1) * sizeof(MapPair))); MapPair *pair = pairs; while (nb--) { pair->key = NX_MAPNOTAKEY; pair->value = NULL; pair++; } return pairs; } +static INLINE void freeBuckets(void *p) { + free(-1+(MapPair *)p); +} + /***** Global data and bootstrap **********************/ static int isEqualPrototype (const void *info, const void *data1, const void *data2) { @@ -89,7 +93,7 @@ static int isEqualPrototype (const void *info, const void *data1, const void *da static uintptr_t hashPrototype (const void *info, const void *data) { NXHashTablePrototype *proto = (NXHashTablePrototype *) data; - return NXPtrHash(info, proto->hash) ^ NXPtrHash(info, proto->isEqual) ^ NXPtrHash(info, proto->free) ^ (uintptr_t) proto->style; + return NXPtrHash(info, (void*)proto->hash) ^ NXPtrHash(info, (void*)proto->isEqual) ^ NXPtrHash(info, (void*)proto->free) ^ (uintptr_t) proto->style; }; static NXHashTablePrototype protoPrototype = { @@ -102,16 +106,16 @@ static NXHashTable *prototypes = NULL; /**** Fundamentals Operations **************/ NXMapTable *NXCreateMapTableFromZone(NXMapTablePrototype prototype, unsigned capacity, void *z) { - NXMapTable *table = malloc_zone_malloc(z, sizeof(NXMapTable)); + NXMapTable *table = (NXMapTable *)malloc_zone_malloc((malloc_zone_t *)z, sizeof(NXMapTable)); NXMapTablePrototype *proto; if (! prototypes) prototypes = NXCreateHashTable(protoPrototype, 0, NULL); if (! prototype.hash || ! prototype.isEqual || ! prototype.free || prototype.style) { _objc_inform("*** NXCreateMapTable: invalid creation parameters\n"); return NULL; } - proto = NXHashGet(prototypes, &prototype); + proto = (NXMapTablePrototype *)NXHashGet(prototypes, &prototype); if (! proto) { - proto = malloc(sizeof(NXMapTablePrototype)); + proto = (NXMapTablePrototype *)malloc(sizeof(NXMapTablePrototype)); *proto = prototype; (void)NXHashInsert(prototypes, proto); } @@ -127,12 +131,12 @@ NXMapTable *NXCreateMapTable(NXMapTablePrototype prototype, unsigned capacity) { void NXFreeMapTable(NXMapTable *table) { NXResetMapTable(table); - free(table->buckets); + freeBuckets(table->buckets); free(table); } void NXResetMapTable(NXMapTable *table) { - MapPair *pairs = table->buckets; + MapPair *pairs = (MapPair *)table->buckets; void (*freeProc)(struct _NXMapTable *, void *, void *) = table->prototype->free; unsigned index = table->nbBucketsMinusOne + 1; while (index--) { @@ -162,7 +166,7 @@ BOOL NXCompareMapTables(NXMapTable *table1, NXMapTable *table2) { unsigned NXCountMapTable(NXMapTable *table) { return table->count; } static INLINE void *_NXMapMember(NXMapTable *table, const void *key, void **value) { - MapPair *pairs = table->buckets; + MapPair *pairs = (MapPair *)table->buckets; unsigned index = bucketOf(table, key); MapPair *pair = pairs + index; if (pair->key == NX_MAPNOTAKEY) return NX_MAPNOTAKEY; @@ -193,7 +197,7 @@ void *NXMapGet(NXMapTable *table, const void *key) { } static void _NXMapRehash(NXMapTable *table) { - MapPair *pairs = table->buckets; + MapPair *pairs = (MapPair *)table->buckets; MapPair *pair = pairs; unsigned numBuckets = table->nbBucketsMinusOne + 1; unsigned index = numBuckets; @@ -210,11 +214,11 @@ static void _NXMapRehash(NXMapTable *table) { } if (oldCount != table->count) _objc_inform("*** maptable: count differs after rehashing; probably indicates a broken invariant: there are x and y such as isEqual(x, y) is TRUE but hash(x) != hash (y)\n"); - free(pairs); + freeBuckets(pairs); } void *NXMapInsert(NXMapTable *table, const void *key, const void *value) { - MapPair *pairs = table->buckets; + MapPair *pairs = (MapPair *)table->buckets; unsigned index = bucketOf(table, key); MapPair *pair = pairs + index; if (key == NX_MAPNOTAKEY) { @@ -264,7 +268,7 @@ void *NXMapInsert(NXMapTable *table, const void *key, const void *value) { static int mapRemove = 0; void *NXMapRemove(NXMapTable *table, const void *key) { - MapPair *pairs = table->buckets; + MapPair *pairs = (MapPair *)table->buckets; unsigned index = bucketOf(table, key); MapPair *pair = pairs + index; unsigned chain = 1; /* number of non-nil pairs in a row */ @@ -288,8 +292,8 @@ void *NXMapRemove(NXMapTable *table, const void *key) { /* remove then reinsert */ { MapPair buffer[16]; - MapPair *aux = (chain > 16) ? malloc(sizeof(MapPair)*(chain-1)) : buffer; - int auxnb = 0; + MapPair *aux = (chain > 16) ? (MapPair *)malloc(sizeof(MapPair)*(chain-1)) : buffer; + unsigned auxnb = 0; int nb = chain; unsigned index2 = index; while (nb--) { @@ -313,7 +317,7 @@ NXMapState NXInitMapState(NXMapTable *table) { } int NXNextMapState(NXMapTable *table, NXMapState *state, const void **key, const void **value) { - MapPair *pairs = table->buckets; + MapPair *pairs = (MapPair *)table->buckets; while (state->index--) { MapPair *pair = pairs + state->index; if (pair->key != NX_MAPNOTAKEY) { @@ -330,7 +334,7 @@ int NXNextMapState(NXMapTable *table, NXMapState *state, const void **key, const * Like NXMapInsert, but strdups the key if necessary. * Used to prevent stale pointers when bundles are unloaded. **********************************************************************/ -PRIVATE_EXTERN void *NXMapKeyCopyingInsert(NXMapTable *table, const void *key, const void *value) +void *NXMapKeyCopyingInsert(NXMapTable *table, const void *key, const void *value) { void *realKey; void *realValue = NULL; @@ -339,7 +343,7 @@ PRIVATE_EXTERN void *NXMapKeyCopyingInsert(NXMapTable *table, const void *key, c // key DOES exist in table - use table's key for insertion } else { // key DOES NOT exist in table - copy the new key before insertion - realKey = _strdup_internal(key); + realKey = (void *)_strdup_internal((char *)key); } return NXMapInsert(table, realKey, value); } @@ -350,7 +354,7 @@ PRIVATE_EXTERN void *NXMapKeyCopyingInsert(NXMapTable *table, const void *key, c * Like NXMapRemove, but frees the existing key if necessary. * Used to prevent stale pointers when bundles are unloaded. **********************************************************************/ -PRIVATE_EXTERN void *NXMapKeyFreeingRemove(NXMapTable *table, const void *key) +void *NXMapKeyFreeingRemove(NXMapTable *table, const void *key) { void *realKey; void *realValue = NULL; @@ -424,13 +428,13 @@ const NXMapTablePrototype NXStrValueMapPrototype = { /* Method prototypes */ @interface DoesNotExist -+ class; -+ initialize; ++ (id)class; ++ (id)initialize; - (id)description; - (const char *)UTF8String; - (unsigned long)hash; - (BOOL)isEqual:(id)object; -- free; +- (void)free; @end static unsigned _mapObjectHash(NXMapTable *table, const void *key) { diff --git a/runtime/message.h b/runtime/message.h index 7f842b3..392cce1 100644 --- a/runtime/message.h +++ b/runtime/message.h @@ -24,6 +24,8 @@ #ifndef _OBJC_MESSAGE_H #define _OBJC_MESSAGE_H +#pragma GCC system_header + #include #include @@ -31,11 +33,11 @@ #ifndef OBJC_SUPER #define OBJC_SUPER struct objc_super { - id receiver; + __unsafe_unretained id receiver; #if !defined(__cplusplus) && !__OBJC2__ - Class class; /* For compatibility with old objc-runtime.h header */ + __unsafe_unretained Class class; /* For compatibility with old objc-runtime.h header */ #else - Class super_class; + __unsafe_unretained Class super_class; #endif /* super_class is the first class to search */ }; @@ -51,10 +53,17 @@ struct objc_super { * These functions must be cast to an appropriate function pointer type * before being called. */ +#if !OBJC_OLD_DISPATCH_PROTOTYPES +OBJC_EXPORT void objc_msgSend(void /* id self, SEL op, ... */ ) + __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); +OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ ) + __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); +#else OBJC_EXPORT id objc_msgSend(id self, SEL op, ...) __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); OBJC_EXPORT id objc_msgSendSuper(struct objc_super *super, SEL op, ...) __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); +#endif /* Struct-returning Messaging Primitives @@ -66,22 +75,15 @@ OBJC_EXPORT id objc_msgSendSuper(struct objc_super *super, SEL op, ...) * These functions must be cast to an appropriate function pointer type * before being called. */ -#if defined(__OBJC2__) -OBJC_EXPORT void objc_msgSend_stret(id self, SEL op, ...) - __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); -OBJC_EXPORT void objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...) - __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); -#elif defined(__cplusplus) -/* For compatibility with old objc-runtime.h header */ -OBJC_EXPORT id objc_msgSend_stret(id self, SEL op, ...) +#if !OBJC_OLD_DISPATCH_PROTOTYPES +OBJC_EXPORT void objc_msgSend_stret(void /* id self, SEL op, ... */ ) __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); -OBJC_EXPORT id objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...) +OBJC_EXPORT void objc_msgSendSuper_stret(void /* struct objc_super *super, SEL op, ... */ ) __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); #else -/* For compatibility with old objc-runtime.h header */ -OBJC_EXPORT void objc_msgSend_stret(void * stretAddr, id self, SEL op, ...) +OBJC_EXPORT void objc_msgSend_stret(id self, SEL op, ...) __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); -OBJC_EXPORT void objc_msgSendSuper_stret(void * stretAddr, struct objc_super *super, SEL op, ...) +OBJC_EXPORT void objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...) __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); #endif @@ -103,7 +105,26 @@ OBJC_EXPORT void objc_msgSendSuper_stret(void * stretAddr, struct objc_super *su * These functions must be cast to an appropriate function pointer type * before being called. */ -#if defined(__i386__) +#if !OBJC_OLD_DISPATCH_PROTOTYPES + +# if defined(__i386__) + +OBJC_EXPORT void objc_msgSend_fpret(void /* id self, SEL op, ... */ ) + __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0); + +# elif defined(__x86_64__) + +OBJC_EXPORT void objc_msgSend_fpret(void /* id self, SEL op, ... */ ) + __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +OBJC_EXPORT void objc_msgSend_fp2ret(void /* id self, SEL op, ... */ ) + __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + +# endif + +// !OBJC_OLD_DISPATCH_PROTOTYPES +#else +// OBJC_OLD_DISPATCH_PROTOTYPES +# if defined(__i386__) OBJC_EXPORT double objc_msgSend_fpret(id self, SEL op, ...) __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0); @@ -111,22 +132,25 @@ OBJC_EXPORT double objc_msgSend_fpret(id self, SEL op, ...) /* Use objc_msgSendSuper() for fp-returning messages to super. */ /* See also objc_msgSendv_fpret() below. */ -#elif defined(__x86_64__) +# elif defined(__x86_64__) OBJC_EXPORT long double objc_msgSend_fpret(id self, SEL op, ...) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); -# if __STDC_VERSION__ >= 199901L +# if __STDC_VERSION__ >= 199901L OBJC_EXPORT _Complex long double objc_msgSend_fp2ret(id self, SEL op, ...) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); -# else +# else OBJC_EXPORT void objc_msgSend_fp2ret(id self, SEL op, ...) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); -# endif +# endif /* Use objc_msgSendSuper() for fp-returning messages to super. */ /* See also objc_msgSendv_fpret() below. */ +# endif + +// OBJC_OLD_DISPATCH_PROTOTYPES #endif @@ -139,10 +163,17 @@ OBJC_EXPORT void objc_msgSend_fp2ret(id self, SEL op, ...) * These functions must be cast to an appropriate function pointer type * before being called. */ +#if !OBJC_OLD_DISPATCH_PROTOTYPES +OBJC_EXPORT void method_invoke(void /* id receiver, Method m, ... */ ) + __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +OBJC_EXPORT void method_invoke_stret(void /* id receiver, Method m, ... */ ) + __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +#else OBJC_EXPORT id method_invoke(id receiver, Method m, ...) - __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); OBJC_EXPORT void method_invoke_stret(id receiver, Method m, ...) - __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +#endif /* Message Forwarding Primitives @@ -160,10 +191,17 @@ OBJC_EXPORT void method_invoke_stret(id receiver, Method m, ...) * Before Mac OS X 10.6, _objc_msgForward must not be called directly * but may be compared to other IMP values. */ +#if !OBJC_OLD_DISPATCH_PROTOTYPES +OBJC_EXPORT void _objc_msgForward(void /* id receiver, SEL sel, ... */ ) + __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); +OBJC_EXPORT void _objc_msgForward_stret(void /* id receiver, SEL sel, ... */ ) + __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0); +#else OBJC_EXPORT id _objc_msgForward(id receiver, SEL sel, ...) - __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); + __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); OBJC_EXPORT void _objc_msgForward_stret(id receiver, SEL sel, ...) - __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0); + __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0); +#endif /* Variable-argument Messaging Primitives diff --git a/runtime/objc-abi.h b/runtime/objc-abi.h index 53f64f0..b770259 100644 --- a/runtime/objc-abi.h +++ b/runtime/objc-abi.h @@ -54,14 +54,31 @@ OBJC_EXPORT void _objcInit(void) // Read or write an object property. Not all object properties use these. OBJC_EXPORT id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); -OBJC_EXPORT void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) +OBJC_EXPORT void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +OBJC_EXPORT void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset) + __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) + OBJC_GC_UNAVAILABLE; +OBJC_EXPORT void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset) + __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) + OBJC_GC_UNAVAILABLE; +OBJC_EXPORT void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) + __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) + OBJC_GC_UNAVAILABLE; +OBJC_EXPORT void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) + __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0) + OBJC_GC_UNAVAILABLE; + + // Read or write a non-object property. Not all uses are C structs, // and not all C struct properties use this. OBJC_EXPORT void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +// Perform a copy of a C++ object using striped locks. Used by non-POD C++ typed atomic properties. +OBJC_EXPORT void objc_copyCppObjectAtomic(void *dest, const void *src, void (*copyHelper) (void *dest, const void *source)) + __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0); /* Classes. */ #if __OBJC2__ diff --git a/runtime/objc-api.h b/runtime/objc-api.h index 2fcb92f..94130b9 100644 --- a/runtime/objc-api.h +++ b/runtime/objc-api.h @@ -25,6 +25,7 @@ #ifndef _OBJC_OBJC_API_H_ #define _OBJC_OBJC_API_H_ +#include #include #include @@ -32,18 +33,31 @@ # define __has_feature(x) 0 #endif +#ifndef __has_extension +# define __has_extension __has_feature +#endif + + /* * OBJC_API_VERSION 0 or undef: Tiger and earlier API only * OBJC_API_VERSION 2: Leopard and later API available */ #if !defined(OBJC_API_VERSION) -# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 +# if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_5 # define OBJC_API_VERSION 0 # else # define OBJC_API_VERSION 2 # endif #endif + +/* OBJC_OLD_DISPATCH_PROTOTYPES == 0 enforces the rule that the dispatch + * functions must be cast to an appropriate function pointer type. */ +#if !defined(OBJC_OLD_DISPATCH_PROTOTYPES) +# define OBJC_OLD_DISPATCH_PROTOTYPES 1 +#endif + + /* OBJC2_UNAVAILABLE: unavailable in objc 2.0, deprecated in Leopard */ #if !defined(OBJC2_UNAVAILABLE) # if __OBJC2__ @@ -56,12 +70,29 @@ /* OBJC_ARC_UNAVAILABLE: unavailable with -fobjc-arc */ #if !defined(OBJC_ARC_UNAVAILABLE) # if __has_feature(objc_arr) -# define OBJC_ARC_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode"))) +# if __has_extension(attribute_unavailable_with_message) +# define OBJC_ARC_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode"))) +# else +# define OBJC_ARC_UNAVAILABLE __attribute__((unavailable)) +# endif # else # define OBJC_ARC_UNAVAILABLE # endif #endif +/* OBJC_GC_UNAVAILABLE: unavailable with -fobjc-gc or -fobjc-gc-only */ +#if !defined(OBJC_GC_UNAVAILABLE) +# if __OBJC_GC__ +# if __has_extension(attribute_unavailable_with_message) +# define OBJC_GC_UNAVAILABLE __attribute__((unavailable("not available in garbage collecting mode"))) +# else +# define OBJC_GC_UNAVAILABLE __attribute__((unavailable)) +# endif +# else +# define OBJC_GC_UNAVAILABLE +# endif +#endif + #if !defined(OBJC_EXTERN) # if defined(__cplusplus) # define OBJC_EXTERN extern "C" diff --git a/runtime/objc-auto-dump.h b/runtime/objc-auto-dump.h index 9b531d2..d58e0c0 100644 --- a/runtime/objc-auto-dump.h +++ b/runtime/objc-auto-dump.h @@ -7,7 +7,8 @@ // Created by Blaine Garst on 12/8/08. // Copyright 2008 Apple, Inc. All rights reserved. // - +#ifndef _OBJC_AUTO_DUMP_H_ +#define _OBJC_AUTO_DUMP_H_ /* * Raw file format definitions @@ -51,3 +52,4 @@ Raw format, not that anyone should really care. Most programs should use the co */ +#endif diff --git a/runtime/objc-auto-dump.m b/runtime/objc-auto-dump.m index bb4e8b2..ca044d8 100644 --- a/runtime/objc-auto-dump.m +++ b/runtime/objc-auto-dump.m @@ -21,16 +21,16 @@ * @APPLE_LICENSE_HEADER_END@ */ -#import "objc-auto.h" +#include "objc-auto.h" #ifndef OBJC_NO_GC -#import -#import -#import -#import "objc-auto-dump.h" -#import "objc-private.h" -#import +#include +#include +#include +#include "objc-auto-dump.h" +#include "objc-private.h" +#include /* * Utilities @@ -106,7 +106,7 @@ static void pointer_set_dispose(pointer_set_t *set) { /* Quickly dump heap to a named file in a pretty raw format. */ -PRIVATE_EXTERN BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename) { +BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename) { // just write interesting info to disk int fd = secure_open(filename, O_WRONLY|O_CREAT, geteuid()); if (fd < 0) return NO; @@ -116,8 +116,8 @@ PRIVATE_EXTERN BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename) { } fwrite(HEADER, strlen(HEADER), 1, fp); - char type = myType(); - fwrite(&type, 1, 1, fp); + char type2 = myType(); + fwrite(&type2, 1, 1, fp); // for each thread... diff --git a/runtime/objc-auto.h b/runtime/objc-auto.h index cd89627..f72cbb1 100644 --- a/runtime/objc-auto.h +++ b/runtime/objc-auto.h @@ -24,6 +24,8 @@ #ifndef _OBJC_AUTO_H_ #define _OBJC_AUTO_H_ +#pragma GCC system_header + #include #include #include @@ -214,6 +216,12 @@ static OBJC_INLINE void objc_setCollectionThreshold(size_t threshold __unused) { static OBJC_INLINE void objc_setCollectionRatio(size_t ratio __unused) { } static OBJC_INLINE void objc_startCollectorThread(void) { } +#if __has_feature(objc_arr) + +/* Covers for GC memory operations are unavailable in ARC */ + +#else + #if TARGET_OS_WIN32 static OBJC_INLINE BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation) { void *original = InterlockedCompareExchangePointer((void * volatile *)objectLocation, (void *)replacement, (void *)predicate); return (original == predicate); } @@ -250,15 +258,18 @@ static OBJC_INLINE id objc_assign_global(id val, id *dest) static OBJC_INLINE id objc_assign_ivar(id val, id dest, ptrdiff_t offset) { return (*(id*)((char *)dest+offset) = val); } -static OBJC_INLINE void *objc_memmove_collectable(void *dst, const void *src, size_t size) - { return memmove(dst, src, size); } - static OBJC_INLINE id objc_read_weak(id *location) { return *location; } static OBJC_INLINE id objc_assign_weak(id value, id *location) { return (*location = value); } +/* MRC */ +#endif + +static OBJC_INLINE void *objc_memmove_collectable(void *dst, const void *src, size_t size) + { return memmove(dst, src, size); } + static OBJC_INLINE void objc_finalizeOnMainThread(Class cls __unused) { } static OBJC_INLINE BOOL objc_is_finalized(void *ptr __unused) { return NO; } static OBJC_INLINE void objc_clear_stack(unsigned long options __unused) { } @@ -268,10 +279,14 @@ static OBJC_INLINE void objc_set_collection_threshold(size_t threshold __unused) static OBJC_INLINE void objc_set_collection_ratio(size_t ratio __unused) { } static OBJC_INLINE void objc_start_collector_thread(void) { } +#if __has_feature(objc_arc) +extern id objc_allocate_object(Class cls, int extra) UNAVAILABLE_ATTRIBUTE; +#else OBJC_EXPORT id class_createInstance(Class cls, size_t extraBytes) __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); static OBJC_INLINE id objc_allocate_object(Class cls, int extra) { return class_createInstance(cls, extra); } +#endif static OBJC_INLINE void objc_registerThreadWithCollector() { } static OBJC_INLINE void objc_unregisterThreadWithCollector() { } diff --git a/runtime/objc-auto.m b/runtime/objc-auto.m index bc87e61..8e9f9ff 100644 --- a/runtime/objc-auto.m +++ b/runtime/objc-auto.m @@ -21,35 +21,35 @@ * @APPLE_LICENSE_HEADER_END@ */ -#import "objc-config.h" -#import "objc-auto.h" -#import "objc-accessors.h" +#include "objc-config.h" +#include "objc-auto.h" +#include "objc-accessors.h" #ifndef OBJC_NO_GC -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#import -#include - -#import "objc-private.h" -#import "objc-references.h" -#import "maptable.h" -#import "message.h" -#import "objc-gdb.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "objc-private.h" +#include "objc-references.h" +#include "maptable.h" +#include "message.h" +#include "objc-gdb.h" #if !defined(NDEBUG) && !__OBJC2__ -#import "objc-exception.h" +#include "objc-exception.h" #endif @@ -58,14 +58,14 @@ static void gc_block_init(void); static void registeredClassTableInit(void); static BOOL objc_isRegisteredClass(Class candidate); -PRIVATE_EXTERN BOOL UseGC = NO; -PRIVATE_EXTERN BOOL UseCompaction = NO; +BOOL UseGC = NO; +BOOL UseCompaction = NO; static BOOL WantsMainThreadFinalization = NO; -PRIVATE_EXTERN auto_zone_t *gc_zone = NULL; +auto_zone_t *gc_zone = NULL; // Pointer magic to make dyld happy. See notes in objc-private.h -PRIVATE_EXTERN id (*objc_assign_ivar_internal)(id, id, ptrdiff_t) = objc_assign_ivar; +id (*objc_assign_ivar_internal)(id, id, ptrdiff_t) = objc_assign_ivar; /* Method prototypes */ @@ -262,14 +262,14 @@ id objc_allocate_object(Class cls, int extra) * startup time. **********************************************************************/ -PRIVATE_EXTERN id objc_assign_strongCast_gc(id value, id *slot) { +id objc_assign_strongCast_gc(id value, id *slot) { if (!auto_zone_set_write_barrier(gc_zone, (void*)slot, value)) { // stores & returns true if slot points into GC allocated memory auto_zone_root_write_barrier(gc_zone, slot, value); // always stores } return value; } -PRIVATE_EXTERN id objc_assign_global_gc(id value, id *slot) { +id objc_assign_global_gc(id value, id *slot) { // use explicit root registration. if (value && auto_zone_is_valid_pointer(gc_zone, value)) { if (auto_zone_is_finalized(gc_zone, value)) { @@ -284,7 +284,7 @@ PRIVATE_EXTERN id objc_assign_global_gc(id value, id *slot) { return value; } -PRIVATE_EXTERN id objc_assign_threadlocal_gc(id value, id *slot) +id objc_assign_threadlocal_gc(id value, id *slot) { if (value && auto_zone_is_valid_pointer(gc_zone, value)) { auto_zone_add_root(gc_zone, slot, value); @@ -296,7 +296,7 @@ PRIVATE_EXTERN id objc_assign_threadlocal_gc(id value, id *slot) return value; } -PRIVATE_EXTERN id objc_assign_ivar_gc(id value, id base, ptrdiff_t offset) +id objc_assign_ivar_gc(id value, id base, ptrdiff_t offset) { id *slot = (id*) ((char *)base + offset); @@ -312,19 +312,19 @@ PRIVATE_EXTERN id objc_assign_ivar_gc(id value, id base, ptrdiff_t offset) return value; } -PRIVATE_EXTERN id objc_assign_strongCast_non_gc(id value, id *slot) { +id objc_assign_strongCast_non_gc(id value, id *slot) { return (*slot = value); } -PRIVATE_EXTERN id objc_assign_global_non_gc(id value, id *slot) { +id objc_assign_global_non_gc(id value, id *slot) { return (*slot = value); } -PRIVATE_EXTERN id objc_assign_threadlocal_non_gc(id value, id *slot) { +id objc_assign_threadlocal_non_gc(id value, id *slot) { return (*slot = value); } -PRIVATE_EXTERN id objc_assign_ivar_non_gc(id value, id base, ptrdiff_t offset) { +id objc_assign_ivar_non_gc(id value, id base, ptrdiff_t offset) { id *slot = (id*) ((char *)base + offset); return (*slot = value); } @@ -400,7 +400,7 @@ id objc_assign_ivar(id value, id dest, ptrdiff_t offset) typedef struct segment_command macho_segment_command; #endif -PRIVATE_EXTERN void _objc_update_stubs_in_mach_header(const struct mach_header* mh, uint32_t symbol_count, const char *symbols[], void *functions[]) { +void _objc_update_stubs_in_mach_header(const struct mach_header* mh, uint32_t symbol_count, const char *symbols[], void *functions[]) { uint32_t cmd_index, cmd_count = mh->ncmds; intptr_t slide = 0; const struct load_command* const cmds = (struct load_command*)((char*)mh + sizeof(macho_header)); @@ -554,7 +554,7 @@ BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, id replaceme * Weak ivar support **********************************************************************/ -PRIVATE_EXTERN id objc_read_weak_gc(id *location) { +id objc_read_weak_gc(id *location) { id result = *location; if (result) { result = auto_read_weak_reference(gc_zone, (void **)location); @@ -562,7 +562,7 @@ PRIVATE_EXTERN id objc_read_weak_gc(id *location) { return result; } -PRIVATE_EXTERN id objc_read_weak_non_gc(id *location) { +id objc_read_weak_non_gc(id *location) { return *location; } @@ -574,12 +574,12 @@ id objc_read_weak(id *location) { return result; } -PRIVATE_EXTERN id objc_assign_weak_gc(id value, id *location) { +id objc_assign_weak_gc(id value, id *location) { auto_assign_weak_reference(gc_zone, value, (const void **)location, NULL); return value; } -PRIVATE_EXTERN id objc_assign_weak_non_gc(id value, id *location) { +id objc_assign_weak_non_gc(id value, id *location) { return (*location = value); } @@ -593,7 +593,7 @@ id objc_assign_weak(id value, id *location) { return value; } -PRIVATE_EXTERN void gc_fixup_weakreferences(id newObject, id oldObject) { +void gc_fixup_weakreferences(id newObject, id oldObject) { // fix up weak references if any. const unsigned char *weakLayout = (const unsigned char *)class_getWeakIvarLayout(_object_getClass(newObject)); if (weakLayout) { @@ -638,8 +638,6 @@ void objc_clear_stack(unsigned long options) { * Finalization support **********************************************************************/ -static IMP _NSObject_finalize = NULL; - // Finalizer crash debugging static void *finalizing_object; @@ -654,7 +652,7 @@ static void finalizeOneObject(void *obj, void *ignored) { CRSetCrashLogMessage2(class_getName(cls)); /// call -finalize method. - objc_msgSend(object, @selector(finalize)); + ((void(*)(id, SEL))objc_msgSend)(object, @selector(finalize)); // Call C++ destructors. // This would be objc_destructInstance() but for performance. @@ -711,7 +709,7 @@ static bool batchFinalize(auto_zone_t *zone, { #if !defined(NDEBUG) && !__OBJC2__ // debug: don't call try/catch before exception handlers are installed - objc_exception_functions_t table = {0}; + objc_exception_functions_t table = {}; objc_exception_get_functions(&table); assert(table.throw_exc); #endif @@ -878,7 +876,7 @@ static void _NSResurrectedObject_finalize(id self, SEL _cmd) { originalClass = (Class) NXMapRemove(_NSResurrectedObjectMap, self); pthread_mutex_unlock(&_NSResurrectedObjectLock); if (originalClass) _objc_inform("**resurrected** object %p of class %s being finalized\n", self, class_getName(originalClass)); - _NSObject_finalize(self, _cmd); + _objc_rootFinalize(self); } static BOOL _NSResurrectedObject_resolveInstanceMethod(id self, SEL _cmd, SEL name) { @@ -934,7 +932,7 @@ static const char* objc_name_for_object(auto_zone_t *zone, void *object) { /* Compaction support */ -PRIVATE_EXTERN void objc_disableCompaction() { +void objc_disableCompaction() { if (UseCompaction) { UseCompaction = NO; auto_zone_disable_compaction(gc_zone); @@ -959,11 +957,11 @@ static const unsigned char *objc_weak_layout_for_address(auto_zone_t *zone, void return objc_isRegisteredClass(cls) ? class_getWeakIvarLayout(cls) : NULL; } -PRIVATE_EXTERN void gc_register_datasegment(uintptr_t base, size_t size) { +void gc_register_datasegment(uintptr_t base, size_t size) { auto_zone_register_datasegment(gc_zone, (void*)base, size); } -PRIVATE_EXTERN void gc_unregister_datasegment(uintptr_t base, size_t size) { +void gc_unregister_datasegment(uintptr_t base, size_t size) { auto_zone_unregister_datasegment(gc_zone, (void*)base, size); } @@ -977,7 +975,7 @@ extern id _object_readExternalReference_rr(objc_xref_t ref); extern void _object_removeExternalReference_gc(objc_xref_t ref); extern void _object_removeExternalReference_rr(objc_xref_t ref); -PRIVATE_EXTERN void gc_fixup_barrier_stubs(const struct dyld_image_info *info) { +void gc_fixup_barrier_stubs(const struct dyld_image_info *info) { static const char *symbols[] = { "_objc_assign_strongCast", "_objc_assign_ivar", "_objc_assign_global", "_objc_assign_threadlocal", @@ -1088,7 +1086,7 @@ void objc_assertRegisteredThreadWithCollector() } // Always called by _objcInit, even if GC is off. -PRIVATE_EXTERN void gc_init(BOOL wantsGC, BOOL wantsCompaction) +void gc_init(BOOL wantsGC, BOOL wantsCompaction) { UseGC = wantsGC; UseCompaction = wantsCompaction; @@ -1106,36 +1104,35 @@ PRIVATE_EXTERN void gc_init(BOOL wantsGC, BOOL wantsCompaction) dispatch_begin_thread_4GC = objc_registerThreadWithCollector; dispatch_end_thread_4GC = objc_reapThreadLocalBlocks; - // no NSObject until Foundation calls objc_collect_init() - _NSObject_finalize = &_objc_msgForward_internal; - // set up the registered classes list registeredClassTableInit(); // tell Blocks to use collectable memory. CF will cook up the classes separately. gc_block_init(); + + // Add GC state to crash log reports + _objc_inform_on_crash("garbage collection is ON"); } - - // Add GC state to crash log reports - _objc_inform_on_crash("garbage collection is %s", - wantsGC ? "ON" : "OFF"); } - -// Called by Foundation to install auto's interruption callback. -malloc_zone_t *objc_collect_init(int (*callback)(void)) +// Called by NSObject +load to perform late GC setup +// This work must wait until after all of libSystem initializes. +void gc_init2(void) { - // Find NSObject's finalize method now that Foundation is loaded. - // fixme only look for the base implementation, not a category's - _NSObject_finalize = class_getMethodImplementation(objc_getClass("NSObject"), @selector(finalize)); - if (_NSObject_finalize == &_objc_msgForward /* not _internal! */) { - _objc_fatal("GC: -[NSObject finalize] unimplemented!"); - } + assert(UseGC); // create the _NSResurrectedObject class used to track resurrections. _NSResurrectedObject_initialize(); + // tell libauto to set up its dispatch queues + auto_collect_multithreaded(gc_zone); +} + +// Called by Foundation. +// This function used to initialize NSObject stuff, but now does nothing. +malloc_zone_t *objc_collect_init(int (*callback)(void) __unused) +{ return (malloc_zone_t *)gc_zone; } @@ -1193,7 +1190,7 @@ static volatile Class *AllClasses = nil; #define SHIFT 3 #define INITIALSIZE 512 -#define REMOVED -1 +#define REMOVED ~0ul // Allocate the side table. static void registeredClassTableInit() { @@ -1256,7 +1253,6 @@ static void addClassHelper(uintptr_t *table, uintptr_t candidate) { } // lock held by callers -PRIVATE_EXTERN void objc_addRegisteredClass(Class candidate) { if (!UseGC) return; uintptr_t *table = (uintptr_t *)AllClasses; @@ -1297,7 +1293,6 @@ void objc_addRegisteredClass(Class candidate) { } // lock held by callers -PRIVATE_EXTERN void objc_removeRegisteredClass(Class candidate) { if (!UseGC) return; uintptr_t *table = (uintptr_t *)AllClasses; @@ -1356,8 +1351,8 @@ static void strlcati(char *str, uintptr_t value, size_t bufSize) static Ivar ivar_for_offset(Class cls, vm_address_t offset) { - int i; - ptrdiff_t ivar_offset; + unsigned i; + vm_address_t ivar_offset; Ivar super_ivar, result; Ivar *ivars; unsigned int ivar_count; @@ -1529,8 +1524,9 @@ static char *name_for_address(auto_zone_t *zone, vm_address_t base, vm_address_t strlcat(buf, "]]", sizeof(buf)); } - result = malloc_zone_malloc(objc_debug_zone(), 1 + strlen(buf)); - strlcpy(result, buf, sizeof(buf)); + size_t len = 1 + strlen(buf); + result = malloc_zone_malloc(objc_debug_zone(), len); + memcpy(result, buf, len); return result; #undef APPEND_SIZE diff --git a/runtime/objc-block-trampolines.m b/runtime/objc-block-trampolines.mm similarity index 94% rename from runtime/objc-block-trampolines.m rename to runtime/objc-block-trampolines.mm index 8e87a57..24a1b95 100644 --- a/runtime/objc-block-trampolines.m +++ b/runtime/objc-block-trampolines.mm @@ -115,9 +115,11 @@ static inline uint32_t _slotSize() { static inline bool trampolinesAreThumb(void) { extern void *_a1a2_firsttramp; +#if !NDEBUG extern void *_a1a2_nexttramp; extern void *_a2a3_firsttramp; extern void *_a2a3_nexttramp; +#endif // make sure thumb-edness of all trampolines match assert(((uintptr_t)&_a1a2_firsttramp) % 2 == @@ -143,11 +145,11 @@ static inline uint32_t _paddingSlotCount() { return paddingSlots; } -static inline void **_payloadAddressAtIndex(TrampolineBlockPagePair *pagePair, uint32_t index) { +static inline id *_payloadAddressAtIndex(TrampolineBlockPagePair *pagePair, uint32_t index) { uint32_t slotSize = _slotSize(); uintptr_t baseAddress = (uintptr_t) pagePair; uintptr_t payloadAddress = baseAddress + (slotSize * index); - return (void **)payloadAddress; + return (id *)payloadAddress; } static inline IMP _trampolineAddressAtIndex(TrampolineBlockPagePair *pagePair, uint32_t index) { @@ -258,8 +260,8 @@ static TrampolineBlockPagePair *_allocateTrampolinesAndData(ArgumentMode aMode) pagePair->nextAvailable = _paddingSlotCount(); pagePair->nextPagePair = NULL; pagePair->nextAvailablePage = NULL; - void **lastPageBlockPtr = _payloadAddressAtIndex(pagePair, _slotsPerPagePair() - 1); - *lastPageBlockPtr = (void*)(uintptr_t) LAST_SLOT_MARKER; + id *lastPageBlockPtr = _payloadAddressAtIndex(pagePair, _slotsPerPagePair() - 1); + *lastPageBlockPtr = (id)(uintptr_t) LAST_SLOT_MARKER; if (headPagePair) { TrampolineBlockPagePair *lastPage = headPagePair; @@ -323,7 +325,7 @@ static TrampolineBlockPagePair *_pagePairAndIndexContainingIMP(IMP anImp, uint32 } // `block` must already have been copied -static IMP _imp_implementationWithBlockNoCopy(ArgumentMode aMode, void *block) +static IMP _imp_implementationWithBlockNoCopy(ArgumentMode aMode, id block) { _assert_locked(); @@ -332,7 +334,7 @@ static IMP _imp_implementationWithBlockNoCopy(ArgumentMode aMode, void *block) headPagePairs[aMode] = pagePair; uint32_t index = pagePair->nextAvailable; - void **payloadAddress = _payloadAddressAtIndex(pagePair, index); + id *payloadAddress = _payloadAddressAtIndex(pagePair, index); assert((index < 1024) || (index == LAST_SLOT_MARKER)); uint32_t nextAvailableIndex = (uint32_t) *((uintptr_t *) payloadAddress); @@ -361,7 +363,7 @@ static IMP _imp_implementationWithBlockNoCopy(ArgumentMode aMode, void *block) return trampoline; } -static ArgumentMode _argumentModeForBlock(void *block) { +static ArgumentMode _argumentModeForBlock(id block) { ArgumentMode aMode = ReturnValueInRegisterArgumentMode; if (_Block_has_signature(block) && _Block_use_stret(block)) @@ -371,7 +373,7 @@ static ArgumentMode _argumentModeForBlock(void *block) { } #pragma mark Public API -IMP imp_implementationWithBlock(void *block) +IMP imp_implementationWithBlock(id block) { block = Block_copy(block); _lock(); @@ -381,7 +383,7 @@ IMP imp_implementationWithBlock(void *block) } -void *imp_getBlock(IMP anImp) { +id imp_getBlock(IMP anImp) { uint32_t index; TrampolineBlockPagePair *pagePair; @@ -396,7 +398,7 @@ void *imp_getBlock(IMP anImp) { return NULL; } - void *potentialBlock = *_payloadAddressAtIndex(pagePair, index); + id potentialBlock = *_payloadAddressAtIndex(pagePair, index); if ((uintptr_t) potentialBlock == (uintptr_t) LAST_SLOT_MARKER) { _unlock(); @@ -428,15 +430,15 @@ BOOL imp_removeBlock(IMP anImp) { return NO; } - void **payloadAddress = _payloadAddressAtIndex(pagePair, index); - void *block = *payloadAddress; + id *payloadAddress = _payloadAddressAtIndex(pagePair, index); + id block = *payloadAddress; // block is released below if (pagePair->nextAvailable) { - *payloadAddress = (void *) (uintptr_t) pagePair->nextAvailable; + *payloadAddress = (id) (uintptr_t) pagePair->nextAvailable; pagePair->nextAvailable = index; } else { - *payloadAddress = (void *) (uintptr_t) LAST_SLOT_MARKER; // nada after this one is used + *payloadAddress = (id) (uintptr_t) LAST_SLOT_MARKER; // nada after this one is used pagePair->nextAvailable = index; } diff --git a/runtime/objc-cache.m b/runtime/objc-cache.mm similarity index 96% rename from runtime/objc-cache.m rename to runtime/objc-cache.mm index 34728fb..8488684 100644 --- a/runtime/objc-cache.m +++ b/runtime/objc-cache.mm @@ -193,15 +193,15 @@ typedef struct static int totalCacheFills = 0; #ifdef OBJC_INSTRUMENTED -PRIVATE_EXTERN unsigned int LinearFlushCachesCount = 0; -PRIVATE_EXTERN unsigned int LinearFlushCachesVisitedCount = 0; -PRIVATE_EXTERN unsigned int MaxLinearFlushCachesVisitedCount = 0; -PRIVATE_EXTERN unsigned int NonlinearFlushCachesCount = 0; -PRIVATE_EXTERN unsigned int NonlinearFlushCachesClassCount = 0; -PRIVATE_EXTERN unsigned int NonlinearFlushCachesVisitedCount = 0; -PRIVATE_EXTERN unsigned int MaxNonlinearFlushCachesVisitedCount = 0; -PRIVATE_EXTERN unsigned int IdealFlushCachesCount = 0; -PRIVATE_EXTERN unsigned int MaxIdealFlushCachesCount = 0; +unsigned int LinearFlushCachesCount = 0; +unsigned int LinearFlushCachesVisitedCount = 0; +unsigned int MaxLinearFlushCachesVisitedCount = 0; +unsigned int NonlinearFlushCachesCount = 0; +unsigned int NonlinearFlushCachesClassCount = 0; +unsigned int NonlinearFlushCachesVisitedCount = 0; +unsigned int MaxNonlinearFlushCachesVisitedCount = 0; +unsigned int IdealFlushCachesCount = 0; +unsigned int MaxIdealFlushCachesCount = 0; #endif @@ -232,6 +232,8 @@ static Cache _cache_create(Class cls); static Cache _cache_expand(Class cls); #if __OBJC2__ static void _cache_flush(Class cls); +#else +extern "C" void _cache_flush(Class cls); #endif static int _collecting_in_critical(void); @@ -240,7 +242,7 @@ static void _cache_collect_free(void *data, size_t size); #if defined(CACHE_ALLOCATOR) static BOOL cache_allocator_is_block(void *block); -static void *cache_allocator_calloc(size_t size); +static Cache cache_allocator_calloc(size_t size); static void cache_allocator_free(void *block); #endif @@ -301,7 +303,7 @@ static Cache _cache_malloc(uintptr_t slotCount) new_cache->mask = (unsigned int)(slotCount - 1); #else if (size < CACHE_ALLOCATOR_MIN || UseInternalZone) { - new_cache = _calloc_internal(size, 1); + new_cache = (Cache)_calloc_internal(size, 1); new_cache->mask = slotCount - 1; // occupied and buckets and instrumentation are all zero } else { @@ -365,7 +367,7 @@ static void _cache_free_block(void *block) * forward:: entries in the cache ARE freed. * Cache locks: cacheUpdateLock must NOT be held by the caller. **********************************************************************/ -PRIVATE_EXTERN void _cache_free(Cache cache) +void _cache_free(Cache cache) { unsigned int i; @@ -373,7 +375,7 @@ PRIVATE_EXTERN void _cache_free(Cache cache) for (i = 0; i < cache->mask + 1; i++) { cache_entry *entry = (cache_entry *)cache->buckets[i]; - if (entry && entry->imp == &_objc_msgForward_internal) { + if (entry && entry->imp == _objc_msgForward_internal) { _cache_free_block(entry); } } @@ -462,7 +464,7 @@ static Cache _cache_expand(Class cls) old_cache->buckets[index] = NULL; // Deallocate "forward::" entry - if (oldEntry->imp == &_objc_msgForward_internal) { + if (oldEntry->imp == _objc_msgForward_internal) { _cache_collect_free (oldEntry, sizeof(cache_entry)); } } @@ -492,7 +494,7 @@ static Cache _cache_expand(Class cls) // Deallocate "forward::" entries from the old cache for (index = 0; index < old_cache->mask + 1; index++) { cache_entry *entry = (cache_entry *)old_cache->buckets[index]; - if (entry && entry->imp == &_objc_msgForward_internal) { + if (entry && entry->imp == _objc_msgForward_internal) { _cache_collect_free (entry, sizeof(cache_entry)); } } @@ -518,7 +520,7 @@ static Cache _cache_expand(Class cls) * * Cache locks: cacheUpdateLock must not be held. **********************************************************************/ -PRIVATE_EXTERN BOOL _cache_fill(Class cls, Method smt, SEL sel) +BOOL _cache_fill(Class cls, Method smt, SEL sel) { uintptr_t newOccupied; uintptr_t index; @@ -589,13 +591,13 @@ PRIVATE_EXTERN BOOL _cache_fill(Class cls, Method smt, SEL sel) * Called from class_respondsToMethod and _class_lookupMethodAndLoadCache. * Cache locks: cacheUpdateLock must not be held. **********************************************************************/ -PRIVATE_EXTERN void _cache_addForwardEntry(Class cls, SEL sel) +void _cache_addForwardEntry(Class cls, SEL sel) { cache_entry *smt; - smt = _malloc_internal(sizeof(cache_entry)); + smt = (cache_entry *)_malloc_internal(sizeof(cache_entry)); smt->name = sel; - smt->imp = &_objc_msgForward_internal; + smt->imp = _objc_msgForward_internal; if (! _cache_fill(cls, (Method)smt, sel)) { // fixme hack // Entry not added to cache. Don't leak the method struct. _free_internal(smt); @@ -613,7 +615,7 @@ PRIVATE_EXTERN void _cache_addForwardEntry(Class cls, SEL sel) #if SUPPORT_GC && !SUPPORT_IGNORED_SELECTOR_CONSTANT static cache_entry *alloc_ignored_entries(void) { - cache_entry *e = malloc(5 * sizeof(cache_entry)); + cache_entry *e = (cache_entry *)_malloc_internal(5 * sizeof(cache_entry)); e[0] = (cache_entry){ @selector(retain), 0,(IMP)&_objc_ignored_method}; e[1] = (cache_entry){ @selector(release), 0,(IMP)&_objc_ignored_method}; e[2] = (cache_entry){ @selector(autorelease),0,(IMP)&_objc_ignored_method}; @@ -623,7 +625,7 @@ static cache_entry *alloc_ignored_entries(void) } #endif -PRIVATE_EXTERN IMP _cache_addIgnoredEntry(Class cls, SEL sel) +IMP _cache_addIgnoredEntry(Class cls, SEL sel) { cache_entry *entryp = NULL; @@ -663,8 +665,6 @@ PRIVATE_EXTERN IMP _cache_addIgnoredEntry(Class cls, SEL sel) **********************************************************************/ #if __OBJC2__ static -#else -PRIVATE_EXTERN #endif void _cache_flush(Class cls) { @@ -701,7 +701,7 @@ void _cache_flush(Class cls) cache->buckets[index] = NULL; // Deallocate "forward::" entry - if (oldEntry && oldEntry->imp == &_objc_msgForward_internal) + if (oldEntry && oldEntry->imp == _objc_msgForward_internal) _cache_collect_free (oldEntry, sizeof(cache_entry)); } @@ -714,7 +714,7 @@ void _cache_flush(Class cls) * flush_cache. Flushes the instance method cache for class cls only. * Use flush_caches() if cls might have in-use subclasses. **********************************************************************/ -PRIVATE_EXTERN void flush_cache(Class cls) +void flush_cache(Class cls) { if (cls) { mutex_lock(&cacheUpdateLock); @@ -858,7 +858,7 @@ static int _collecting_in_critical(void) static size_t garbage_byte_size = 0; // do not empty the garbage until garbage_byte_size gets at least this big -static size_t garbage_threshold = 1024; +static size_t garbage_threshold = 32*1024; // table of refs to free static void **garbage_refs = 0; @@ -877,21 +877,21 @@ enum { static void _garbage_make_room(void) { static int first = 1; - volatile void *tempGarbage; // Create the collection table the first time it is needed if (first) { first = 0; - garbage_refs = _malloc_internal(INIT_GARBAGE_COUNT * sizeof(void *)); + garbage_refs = (void**) + _malloc_internal(INIT_GARBAGE_COUNT * sizeof(void *)); garbage_max = INIT_GARBAGE_COUNT; } // Double the table if it is full else if (garbage_count == garbage_max) { - tempGarbage = _realloc_internal(garbage_refs, garbage_max * 2 * sizeof(void *)); - garbage_refs = (void **) tempGarbage; + garbage_refs = (void**) + _realloc_internal(garbage_refs, garbage_max * 2 * sizeof(void *)); garbage_max *= 2; } } @@ -919,7 +919,7 @@ static void _cache_collect_free(void *data, size_t size) * collectALot tries harder to free memory. * Cache locks: cacheUpdateLock must be held by the caller. **********************************************************************/ -PRIVATE_EXTERN void _cache_collect(bool collectALot) +void _cache_collect(bool collectALot) { mutex_assert_locked(&cacheUpdateLock); @@ -964,7 +964,7 @@ PRIVATE_EXTERN void _cache_collect(bool collectALot) garbage_byte_size = 0; if (PrintCaches) { - int i; + size_t i; size_t total = 0; size_t ideal_total = 0; size_t malloc_total = 0; @@ -1064,7 +1064,7 @@ static cache_allocator_region *cache_allocator_add_region(size_t size) vm_address_t addr; cache_allocator_block *b; cache_allocator_region **rgnP; - cache_allocator_region *newRegion = + cache_allocator_region *newRegion = (cache_allocator_region *) _calloc_internal(1, sizeof(cache_allocator_region)); // Round size up to quantum boundary, and apply the minimum size. @@ -1173,7 +1173,7 @@ static void *cache_region_calloc(cache_allocator_region *rgn, size_t size) * cache->occupied and the cache contents are zero. * Cache locks: cacheUpdateLock must be held by the caller **********************************************************************/ -static void *cache_allocator_calloc(size_t size) +static Cache cache_allocator_calloc(size_t size) { cache_allocator_region *rgn; @@ -1182,13 +1182,13 @@ static void *cache_allocator_calloc(size_t size) for (rgn = cacheRegion; rgn != NULL; rgn = rgn->next) { void *p = cache_region_calloc(rgn, size); if (p) { - return p; + return (Cache)p; } } // No regions or all regions full - make a region and try one more time // In the unlikely case of a cache over 256KB, it will get its own region. - return cache_region_calloc(cache_allocator_add_region(size), size); + return (Cache)cache_region_calloc(cache_allocator_add_region(size), size); } @@ -1232,9 +1232,9 @@ static void cache_allocator_free(void *ptr) mutex_assert_locked(&cacheUpdateLock); - if (! (rgn = cache_allocator_region_for_block(ptr))) { + if (! (rgn = cache_allocator_region_for_block(dead))) { // free of non-pointer - _objc_inform("cache_allocator_free of non-pointer %p", ptr); + _objc_inform("cache_allocator_free of non-pointer %p", dead); return; } @@ -1298,7 +1298,7 @@ static void _cache_print(Cache cache) for (index = 0; index < count; index += 1) { cache_entry *entry = (cache_entry *)cache->buckets[index]; if (entry) { - if (entry->imp == &_objc_msgForward_internal) + if (entry->imp == _objc_msgForward_internal) printf ("does not recognize: \n"); printf ("%s\n", sel_getName(entry->name)); } @@ -1309,7 +1309,7 @@ static void _cache_print(Cache cache) /*********************************************************************** * _class_printMethodCaches. **********************************************************************/ -PRIVATE_EXTERN void _class_printMethodCaches(Class cls) +void _class_printMethodCaches(Class cls) { if (_cache_isEmpty(_class_getCache(cls))) { printf("no instance-method cache for class %s\n", _class_getName(cls)); @@ -1581,7 +1581,7 @@ void _class_printMethodCacheStatistics(void) entryCount += 1; // Tally "forward::" entries - if (entry->imp == &_objc_msgForward_internal) + if (entry->imp == _objc_msgForward_internal) negativeEntryCount += 1; // Calculate search distance (chain length) for this method diff --git a/runtime/objc-class-old.m b/runtime/objc-class-old.m index 3dcea04..46640ce 100644 --- a/runtime/objc-class-old.m +++ b/runtime/objc-class-old.m @@ -44,7 +44,9 @@ static const struct old_class freedObjectClass = NULL, // ivars NULL, // methodLists (Cache) &_objc_empty_cache, // cache - NULL // protocols + NULL, // protocols + NULL, // ivar_layout; + NULL // ext }; @@ -116,7 +118,7 @@ static inline struct old_method *_findNamedMethodInList(struct old_method_list * static void *fixed_up_method_list = OBJC_FIXED_UP; // sel_init() decided that selectors in the dyld shared cache are untrustworthy -PRIVATE_EXTERN void disableSharedCacheOptimizations(void) +void disableSharedCacheOptimizations(void) { fixed_up_method_list = OBJC_FIXED_UP_outside_dyld; } @@ -304,7 +306,7 @@ static inline struct old_method * _getMethod(struct old_class *cls, SEL sel) { // fixme for gc debugging temporary use -PRIVATE_EXTERN IMP findIMPInClass(struct old_class *cls, SEL sel) +IMP findIMPInClass(struct old_class *cls, SEL sel) { struct old_method *m = _findMethodInClass(cls, sel); if (m) return m->method_imp; @@ -325,15 +327,15 @@ static void _freedHandler(id obj, SEL sel) /*********************************************************************** * ABI-specific lookUpMethod helpers. **********************************************************************/ -PRIVATE_EXTERN void lockForMethodLookup(void) +void lockForMethodLookup(void) { mutex_lock(&methodListLock); } -PRIVATE_EXTERN void unlockForMethodLookup(void) +void unlockForMethodLookup(void) { mutex_unlock(&methodListLock); } -PRIVATE_EXTERN IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init) +IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init, id obj) { mutex_assert_unlocked(&methodListLock); @@ -342,7 +344,7 @@ PRIVATE_EXTERN IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init) return (IMP) _freedHandler; if (init && !_class_isInitialized(cls)) { - _class_initialize (cls); + _class_initialize (_class_getNonMetaClass(cls, obj)); // 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 @@ -356,7 +358,7 @@ PRIVATE_EXTERN IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init) /*********************************************************************** * class_getVariable. Return the named instance variable. **********************************************************************/ -PRIVATE_EXTERN + Ivar _class_getVariable(Class cls_gen, const char *name, Class *memberOf) { struct old_class *cls = oldcls(cls_gen); @@ -384,13 +386,13 @@ Ivar _class_getVariable(Class cls_gen, const char *name, Class *memberOf) } -PRIVATE_EXTERN struct old_property * +struct old_property * property_list_nth(const struct old_property_list *plist, uint32_t i) { return (struct old_property *)(i*plist->entsize + (char *)&plist->first); } -PRIVATE_EXTERN struct old_property ** +struct old_property ** copyPropertyList(struct old_property_list *plist, unsigned int *outCount) { struct old_property **result = NULL; @@ -554,7 +556,7 @@ void class_setWeakIvarLayout(Class cls_gen, const uint8_t *layout) * Atomically sets and clears some bits in cls's info field. * set and clear must not overlap. **********************************************************************/ -PRIVATE_EXTERN void _class_changeInfo(Class cls, long set, long clear) +void _class_changeInfo(Class cls, long set, long clear) { struct old_class *old = oldcls(cls); long newinfo; @@ -570,7 +572,7 @@ PRIVATE_EXTERN void _class_changeInfo(Class cls, long set, long clear) * _class_getInfo * Returns YES iff all set bits in get are also set in cls's info field. **********************************************************************/ -PRIVATE_EXTERN BOOL _class_getInfo(Class cls, int get) +BOOL _class_getInfo(Class cls, int get) { struct old_class *old = oldcls(cls); return ((old->info & get) == get) ? YES : NO; @@ -581,7 +583,7 @@ PRIVATE_EXTERN BOOL _class_getInfo(Class cls, int get) * _class_setInfo * Atomically sets some bits in cls's info field. **********************************************************************/ -PRIVATE_EXTERN void _class_setInfo(Class cls, long set) +void _class_setInfo(Class cls, long set) { _class_changeInfo(cls, set, 0); } @@ -591,7 +593,7 @@ PRIVATE_EXTERN void _class_setInfo(Class cls, long set) * _class_clearInfo * Atomically clears some bits in cls's info field. **********************************************************************/ -PRIVATE_EXTERN void _class_clearInfo(Class cls, long clear) +void _class_clearInfo(Class cls, long clear) { _class_changeInfo(cls, 0, clear); } @@ -602,7 +604,7 @@ PRIVATE_EXTERN void _class_clearInfo(Class cls, long clear) * Return YES if cls is currently being initialized. * The initializing bit is stored in the metaclass only. **********************************************************************/ -PRIVATE_EXTERN BOOL _class_isInitializing(Class cls) +BOOL _class_isInitializing(Class cls) { return _class_getInfo(_class_getMeta(cls), CLS_INITIALIZING); } @@ -613,7 +615,7 @@ PRIVATE_EXTERN BOOL _class_isInitializing(Class cls) * Return YES if cls is already initialized. * The initialized bit is stored in the metaclass only. **********************************************************************/ -PRIVATE_EXTERN BOOL _class_isInitialized(Class cls) +BOOL _class_isInitialized(Class cls) { return _class_getInfo(_class_getMeta(cls), CLS_INITIALIZED); } @@ -623,7 +625,7 @@ PRIVATE_EXTERN BOOL _class_isInitialized(Class cls) * setInitializing * Mark cls as initialization in progress. **********************************************************************/ -PRIVATE_EXTERN void _class_setInitializing(Class cls) +void _class_setInitializing(Class cls) { _class_setInfo(_class_getMeta(cls), CLS_INITIALIZING); } @@ -633,7 +635,7 @@ PRIVATE_EXTERN void _class_setInitializing(Class cls) * setInitialized * Atomically mark cls as initialized and not initializing. **********************************************************************/ -PRIVATE_EXTERN void _class_setInitialized(Class cls) +void _class_setInitialized(Class cls) { _class_changeInfo(_class_getMeta(cls), CLS_INITIALIZED, CLS_INITIALIZING); } @@ -658,13 +660,13 @@ int class_getVersion(Class cls) } -PRIVATE_EXTERN Class _class_getMeta(Class cls) +Class _class_getMeta(Class cls) { if (_class_getInfo(cls, CLS_META)) return cls; else return ((id)cls)->isa; } -PRIVATE_EXTERN BOOL _class_isMetaClass(Class cls) +BOOL _class_isMetaClass(Class cls) { if (!cls) return NO; return _class_getInfo(cls, CLS_META); @@ -676,7 +678,7 @@ PRIVATE_EXTERN BOOL _class_isMetaClass(Class cls) * Return the ordinary class for this class or metaclass. * Used by +initialize. **********************************************************************/ -PRIVATE_EXTERN Class _class_getNonMetaClass(Class cls) +Class _class_getNonMetaClass(Class cls, id obj __unused) { // fixme ick if (_class_isMetaClass(cls)) { @@ -695,30 +697,30 @@ PRIVATE_EXTERN Class _class_getNonMetaClass(Class cls) } -PRIVATE_EXTERN Class _class_getSuperclass(Class cls) +Class _class_getSuperclass(Class cls) { if (!cls) return nil; return (Class)cls->super_class; } -PRIVATE_EXTERN Cache _class_getCache(Class cls) +Cache _class_getCache(Class cls) { return cls->cache; } -PRIVATE_EXTERN void _class_setCache(Class cls, Cache cache) +void _class_setCache(Class cls, Cache cache) { cls->cache = cache; } -PRIVATE_EXTERN size_t _class_getInstanceSize(Class cls) +size_t _class_getInstanceSize(Class cls) { if (!cls) return 0; return (cls->instance_size + WORD_MASK) & ~WORD_MASK; } -PRIVATE_EXTERN const char * _class_getName(Class cls) +const char * _class_getName(Class cls) { if (!cls) return "nil"; return cls->name; @@ -726,22 +728,22 @@ PRIVATE_EXTERN const char * _class_getName(Class cls) -PRIVATE_EXTERN const char *_category_getName(Category cat) +const char *_category_getName(Category cat) { return oldcategory(cat)->category_name; } -PRIVATE_EXTERN const char *_category_getClassName(Category cat) +const char *_category_getClassName(Category cat) { return oldcategory(cat)->class_name; } -PRIVATE_EXTERN Class _category_getClass(Category cat) +Class _category_getClass(Category cat) { return (Class)objc_getClass(oldcategory(cat)->class_name); } -PRIVATE_EXTERN IMP _category_getLoadMethod(Category cat) +IMP _category_getLoadMethod(Category cat) { struct old_method_list *mlist = oldcategory(cat)->class_methods; if (mlist) { @@ -824,14 +826,14 @@ OBJC_EXPORT void class_removeMethods(Class cls, struct objc_method_list *meths) * without fixing up the entire method list. * The class is not yet in use, so methodListLock is not taken. **********************************************************************/ -PRIVATE_EXTERN IMP lookupNamedMethodInMethodList(struct old_method_list *mlist, const char *meth_name) +IMP lookupNamedMethodInMethodList(struct old_method_list *mlist, const char *meth_name) { struct old_method *m; m = meth_name ? _findNamedMethodInList(mlist, meth_name) : NULL; return (m ? m->method_imp : NULL); } -PRIVATE_EXTERN Method _class_getMethod(Class cls, SEL sel) +Method _class_getMethod(Class cls, SEL sel) { Method result; @@ -842,7 +844,7 @@ PRIVATE_EXTERN Method _class_getMethod(Class cls, SEL sel) return result; } -PRIVATE_EXTERN Method _class_getMethodNoSuper(Class cls, SEL sel) +Method _class_getMethodNoSuper(Class cls, SEL sel) { Method result; @@ -853,7 +855,7 @@ PRIVATE_EXTERN Method _class_getMethodNoSuper(Class cls, SEL sel) return result; } -PRIVATE_EXTERN Method _class_getMethodNoSuper_nolock(Class cls, SEL sel) +Method _class_getMethodNoSuper_nolock(Class cls, SEL sel) { mutex_assert_locked(&methodListLock); return (Method)_findMethodInClass(oldcls(cls), sel); @@ -888,7 +890,7 @@ static NXMapTable * posed_class_hash = NULL; /*********************************************************************** * objc_getOrigClass. **********************************************************************/ -PRIVATE_EXTERN Class _objc_getOrigClass(const char *name) +Class _objc_getOrigClass(const char *name) { Class ret; @@ -949,7 +951,6 @@ static void _objc_addOrigClass (struct old_class *origClass) * Used by class_poseAs and objc_setFutureClass * classLock must be locked. **********************************************************************/ -PRIVATE_EXTERN void change_class_references(struct old_class *imposter, struct old_class *original, struct old_class *copy, @@ -1105,7 +1106,7 @@ Class class_poseAs(Class imposter_gen, Class original_gen) * * Specifying Nil for the class "all classes." **********************************************************************/ -PRIVATE_EXTERN void flush_caches(Class target_gen, BOOL flush_meta) +void flush_caches(Class target_gen, BOOL flush_meta) { NXHashState state; struct old_class *target = oldcls(target_gen); @@ -1276,7 +1277,7 @@ PRIVATE_EXTERN void flush_caches(Class target_gen, BOOL flush_meta) * CLS_FLUSH_CACHE (and all subclasses thereof) * fixme instrument **********************************************************************/ -PRIVATE_EXTERN void flush_marked_caches(void) +void flush_marked_caches(void) { struct old_class *cls; struct old_class *supercls; @@ -1350,7 +1351,7 @@ static IMP _class_getLoadMethod_nocheck(struct old_class *cls) } -PRIVATE_EXTERN BOOL _class_hasLoadMethod(Class cls) +BOOL _class_hasLoadMethod(Class cls) { if (oldcls(cls)->isa->info & CLS_HAS_LOAD_METHOD) return YES; return (_class_getLoadMethod_nocheck(oldcls(cls)) ? YES : NO); @@ -1361,7 +1362,7 @@ PRIVATE_EXTERN BOOL _class_hasLoadMethod(Class cls) * _class_getLoadMethod * Returns cls's +load implementation, or NULL if it doesn't have one. **********************************************************************/ -PRIVATE_EXTERN IMP _class_getLoadMethod(Class cls_gen) +IMP _class_getLoadMethod(Class cls_gen) { struct old_class *cls = oldcls(cls_gen); if (cls->isa->info & CLS_HAS_LOAD_METHOD) { @@ -1371,37 +1372,37 @@ PRIVATE_EXTERN IMP _class_getLoadMethod(Class cls_gen) } -PRIVATE_EXTERN BOOL _class_shouldGrowCache(Class cls) +BOOL _class_shouldGrowCache(Class cls) { return _class_getInfo(cls, CLS_GROW_CACHE); } -PRIVATE_EXTERN void _class_setGrowCache(Class cls, BOOL grow) +void _class_setGrowCache(Class cls, BOOL grow) { if (grow) _class_setInfo(cls, CLS_GROW_CACHE); else _class_clearInfo(cls, CLS_GROW_CACHE); } -PRIVATE_EXTERN BOOL _class_hasCxxStructors(Class cls) +BOOL _class_hasCxxStructors(Class cls) { // this DOES check superclasses too, because set_superclass // propagates the flag from the superclass. return _class_getInfo(cls, CLS_HAS_CXX_STRUCTORS); } -PRIVATE_EXTERN BOOL _class_shouldFinalizeOnMainThread(Class cls) { +BOOL _class_shouldFinalizeOnMainThread(Class cls) { return _class_getInfo(cls, CLS_FINALIZE_ON_MAIN_THREAD); } -PRIVATE_EXTERN void _class_setFinalizeOnMainThread(Class cls) { +void _class_setFinalizeOnMainThread(Class cls) { _class_setInfo(cls, CLS_FINALIZE_ON_MAIN_THREAD); } -PRIVATE_EXTERN BOOL _class_instancesHaveAssociatedObjects(Class cls) { +BOOL _class_instancesHaveAssociatedObjects(Class cls) { return _class_getInfo(cls, CLS_INSTANCES_HAVE_ASSOCIATED_OBJECTS); } -PRIVATE_EXTERN void _class_setInstancesHaveAssociatedObjects(Class cls) { +void _class_setInstancesHaveAssociatedObjects(Class cls) { _class_setInfo(cls, CLS_INSTANCES_HAVE_ASSOCIATED_OBJECTS); } @@ -1410,7 +1411,7 @@ BOOL _class_usesAutomaticRetainRelease(Class cls) return NO; } -PRIVATE_EXTERN uint32_t _class_getInstanceStart(Class cls) +uint32_t _class_getInstanceStart(Class cls) { _objc_fatal("_class_getInstanceStart() unimplemented for fragile instance variables"); return 0; // PCB: never used just provided for ARR consistency. @@ -1739,7 +1740,7 @@ BOOL class_addProtocol(Class cls_gen, Protocol *protocol_gen) * Used by category attachment and class_addProperty() * Locking: acquires classLock **********************************************************************/ -PRIVATE_EXTERN BOOL +BOOL _class_addProperties(struct old_class *cls, struct old_property_list *additions) { @@ -2064,7 +2065,7 @@ Ivar *class_copyIvarList(Class cls_gen, unsigned int *outCount) /*********************************************************************** * objc_allocateClass. **********************************************************************/ -PRIVATE_EXTERN + void set_superclass(struct old_class *cls, struct old_class *supercls, BOOL cls_is_new) { @@ -2376,7 +2377,7 @@ void objc_disposeClassPair(Class cls_gen) * variables, in the specified zone. The isa field is set to the * class, C++ default constructors are called, and all other fields are zeroed. **********************************************************************/ -PRIVATE_EXTERN id +id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone) { id obj; diff --git a/runtime/objc-class.h b/runtime/objc-class.h index 1701b1e..4599f08 100644 --- a/runtime/objc-class.h +++ b/runtime/objc-class.h @@ -1,2 +1,2 @@ -#import -#import +#include +#include diff --git a/runtime/objc-class.m b/runtime/objc-class.mm similarity index 95% rename from runtime/objc-class.m rename to runtime/objc-class.mm index bf63838..4e23f5e 100644 --- a/runtime/objc-class.m +++ b/runtime/objc-class.mm @@ -231,7 +231,7 @@ Class object_setClass(id obj, Class cls) Class old; do { old = obj->isa; - } while (! OSAtomicCompareAndSwapPtrBarrier(old, cls, (void*)&obj->isa)); + } while (! OSAtomicCompareAndSwapPtrBarrier(old, cls, (void * volatile *)&obj->isa)); if (old && _class_instancesHaveAssociatedObjects(old)) { _class_setInstancesHaveAssociatedObjects(cls); @@ -326,11 +326,11 @@ void object_setIvar(id obj, Ivar ivar, id value) if (obj && ivar) { Class cls = _ivar_getClass(object_getClass(obj), ivar); ptrdiff_t ivar_offset = ivar_getOffset(ivar); + id *location = (id *)((char *)obj + ivar_offset); // if this ivar is a member of an ARR compiled class, then issue the correct barrier according to the layout. if (_class_usesAutomaticRetainRelease(cls)) { // for ARR, layout strings are relative to the instance start. uint32_t instanceStart = _class_getInstanceStart(cls); - id *location = (id *)((char *)obj + ivar_offset); const uint8_t *weak_layout = class_getWeakIvarLayout(cls); if (weak_layout && is_scanned_offset(ivar_offset - instanceStart, weak_layout)) { // use the weak system to write to this variable. @@ -348,11 +348,13 @@ void object_setIvar(id obj, Ivar ivar, id value) // for GC, check for weak references. const uint8_t *weak_layout = class_getWeakIvarLayout(cls); if (weak_layout && is_scanned_offset(ivar_offset, weak_layout)) { - objc_assign_weak(value, (id *)((char *)obj + ivar_offset)); + objc_assign_weak(value, location); } } -#endif objc_assign_ivar_internal(value, obj, ivar_offset); +#else + *location = value; +#endif } } @@ -404,7 +406,7 @@ static void object_cxxDestructFromClass(id obj, Class cls) if (!_class_hasCxxStructors(cls)) return; dtor = (void(*)(id)) lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct); - if (dtor != (void(*)(id))&_objc_msgForward_internal) { + if (dtor != (void(*)(id))_objc_msgForward_internal) { if (PrintCxxCtors) { _objc_inform("CXX: calling C++ destructors for class %s", _class_getName(cls)); @@ -420,7 +422,7 @@ static void object_cxxDestructFromClass(id obj, Class cls) * Call C++ destructors on obj, if any. * Uses methodListLock and cacheUpdateLock. The caller must hold neither. **********************************************************************/ -PRIVATE_EXTERN void object_cxxDestruct(id obj) +void object_cxxDestruct(id obj) { if (!obj) return; if (OBJC_IS_TAGGED_PTR(obj)) return; @@ -460,7 +462,7 @@ static BOOL object_cxxConstructFromClass(id obj, Class cls) // Find this class's ctor, if any. ctor = (id(*)(id))lookupMethodInClassAndLoadCache(cls, SEL_cxx_construct); - if (ctor == (id(*)(id))&_objc_msgForward_internal) return YES; // no ctor - ok + if (ctor == (id(*)(id))_objc_msgForward_internal) return YES; // no ctor - ok // Call this class's ctor. if (PrintCxxCtors) { @@ -483,7 +485,7 @@ static BOOL object_cxxConstructFromClass(id obj, Class cls) * caught and discarded. Any partial construction is destructed. * Uses methodListLock and cacheUpdateLock. The caller must hold neither. **********************************************************************/ -PRIVATE_EXTERN BOOL object_cxxConstruct(id obj) +BOOL object_cxxConstruct(id obj) { if (!obj) return YES; if (OBJC_IS_TAGGED_PTR(obj)) return YES; @@ -587,7 +589,7 @@ static Method _class_resolveInstanceMethod(Class cls, SEL sel) * the method added or NULL. * Assumes the method doesn't exist already. **********************************************************************/ -PRIVATE_EXTERN Method _class_resolveMethod(Class cls, SEL sel) +Method _class_resolveMethod(Class cls, SEL sel) { Method meth = NULL; @@ -623,7 +625,7 @@ static Method look_up_method(Class cls, SEL sel, Method meth = NULL; if (withCache) { - meth = _cache_getMethod(cls, sel, &_objc_msgForward_internal); + meth = _cache_getMethod(cls, sel, _objc_msgForward_internal); if (meth == (Method)1) { // Cache contains forward:: . Stop searching. return NULL; @@ -731,7 +733,7 @@ BOOL class_respondsToSelector(Class cls, SEL sel) // Avoids +initialize because it historically did so. // We're not returning a callable IMP anyway. - imp = lookUpMethod(cls, sel, NO/*initialize*/, YES/*cache*/); + imp = lookUpMethod(cls, sel, NO/*initialize*/, YES/*cache*/, nil); return (imp != (IMP)_objc_msgForward_internal) ? YES : NO; } @@ -759,11 +761,11 @@ IMP class_getMethodImplementation(Class cls, SEL sel) if (!cls || !sel) return NULL; - imp = lookUpMethod(cls, sel, YES/*initialize*/, YES/*cache*/); + imp = lookUpMethod(cls, sel, YES/*initialize*/, YES/*cache*/, nil); // Translate forwarding function to C-callable external version - if (imp == (IMP)&_objc_msgForward_internal) { - return (IMP)&_objc_msgForward; + if (imp == _objc_msgForward_internal) { + return _objc_msgForward; } return imp; @@ -845,7 +847,7 @@ void instrumentObjcMessageSends (BOOL flag) objcMsgLogEnabled = enabledValue; } -PRIVATE_EXTERN void logObjcMessageSends (ObjCLogProc logProc) +void logObjcMessageSends (ObjCLogProc logProc) { if (logProc) { @@ -869,7 +871,7 @@ PRIVATE_EXTERN void logObjcMessageSends (ObjCLogProc logProc) * cls is the method whose cache should be filled. * implementer is the class that owns the implementation in question. **********************************************************************/ -PRIVATE_EXTERN void +void log_and_fill_cache(Class cls, Class implementer, Method meth, SEL sel) { #if defined(MESSAGE_LOGGING) @@ -893,13 +895,9 @@ log_and_fill_cache(Class cls, Class implementer, Method meth, SEL sel) * This lookup avoids optimistic cache scan because the dispatcher * already tried that. **********************************************************************/ -PRIVATE_EXTERN IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls) -{ - return lookUpMethod(cls, sel, YES/*initialize*/, NO/*cache*/); -} -PRIVATE_EXTERN IMP _class_lookupMethodAndLoadCache(Class cls, SEL sel) +IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls) { - return lookUpMethod(cls, sel, YES/*initialize*/, NO/*cache*/); + return lookUpMethod(cls, sel, YES/*initialize*/, NO/*cache*/, obj); } @@ -909,11 +907,12 @@ PRIVATE_EXTERN IMP _class_lookupMethodAndLoadCache(Class cls, SEL sel) * initialize==NO tries to avoid +initialize (but sometimes fails) * cache==NO skips optimistic unlocked lookup (but uses cache elsewhere) * Most callers should use initialize==YES and cache==YES. +* inst is an instance of cls or a subclass thereof, or nil if none is known. +* If cls is an un-initialized metaclass then a non-nil inst is faster. * May return _objc_msgForward_internal. IMPs destined for external use * must be converted to _objc_msgForward or _objc_msgForward_stret. **********************************************************************/ -PRIVATE_EXTERN IMP lookUpMethod(Class cls, SEL sel, - BOOL initialize, BOOL cache) +IMP lookUpMethod(Class cls, SEL sel, BOOL initialize, BOOL cache, id inst) { Class curClass; IMP methodPC = NULL; @@ -927,7 +926,7 @@ PRIVATE_EXTERN IMP lookUpMethod(Class cls, SEL sel, } // realize, +initialize, and any special early exit - methodPC = prepareForMethodLookup(cls, sel, initialize); + methodPC = prepareForMethodLookup(cls, sel, initialize, inst); if (methodPC) return methodPC; @@ -963,7 +962,7 @@ PRIVATE_EXTERN IMP lookUpMethod(Class cls, SEL sel, curClass = cls; while ((curClass = _class_getSuperclass(curClass))) { // Superclass cache. - meth = _cache_getMethod(curClass, sel, &_objc_msgForward_internal); + meth = _cache_getMethod(curClass, sel, _objc_msgForward_internal); if (meth) { if (meth != (Method)1) { // Found the method in a superclass. Cache it in this class. @@ -1003,7 +1002,7 @@ PRIVATE_EXTERN IMP lookUpMethod(Class cls, SEL sel, // Use forwarding. _cache_addForwardEntry(cls, sel); - methodPC = &_objc_msgForward_internal; + methodPC = _objc_msgForward_internal; done: unlockForMethodLookup(); @@ -1045,7 +1044,7 @@ static IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel) } else { // Miss in method list. Cache objc_msgForward. _cache_addForwardEntry(cls, sel); - return &_objc_msgForward_internal; + return _objc_msgForward_internal; } } @@ -1060,67 +1059,68 @@ static IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel) * _free_internal * Convenience functions for the internal malloc zone. **********************************************************************/ -PRIVATE_EXTERN void *_malloc_internal(size_t size) +void *_malloc_internal(size_t size) { return malloc_zone_malloc(_objc_internal_zone(), size); } -PRIVATE_EXTERN void *_calloc_internal(size_t count, size_t size) +void *_calloc_internal(size_t count, size_t size) { return malloc_zone_calloc(_objc_internal_zone(), count, size); } -PRIVATE_EXTERN void *_realloc_internal(void *ptr, size_t size) +void *_realloc_internal(void *ptr, size_t size) { return malloc_zone_realloc(_objc_internal_zone(), ptr, size); } -PRIVATE_EXTERN char *_strdup_internal(const char *str) +char *_strdup_internal(const char *str) { size_t len; char *dup; if (!str) return NULL; len = strlen(str); - dup = malloc_zone_malloc(_objc_internal_zone(), len + 1); + dup = (char *)malloc_zone_malloc(_objc_internal_zone(), len + 1); memcpy(dup, str, len + 1); return dup; } -PRIVATE_EXTERN uint8_t *_ustrdup_internal(const uint8_t *str) +uint8_t *_ustrdup_internal(const uint8_t *str) { return (uint8_t *)_strdup_internal((char *)str); } // allocate a new string that concatenates s1+s2. -PRIVATE_EXTERN char *_strdupcat_internal(const char *s1, const char *s2) +char *_strdupcat_internal(const char *s1, const char *s2) { size_t len1 = strlen(s1); size_t len2 = strlen(s2); - char *dup = malloc_zone_malloc(_objc_internal_zone(), len1 + len2 + 1); + char *dup = (char *) + malloc_zone_malloc(_objc_internal_zone(), len1 + len2 + 1); memcpy(dup, s1, len1); memcpy(dup + len1, s2, len2 + 1); return dup; } -PRIVATE_EXTERN void *_memdup_internal(const void *mem, size_t len) +void *_memdup_internal(const void *mem, size_t len) { void *dup = malloc_zone_malloc(_objc_internal_zone(), len); memcpy(dup, mem, len); return dup; } -PRIVATE_EXTERN void _free_internal(void *ptr) +void _free_internal(void *ptr) { malloc_zone_free(_objc_internal_zone(), ptr); } -PRIVATE_EXTERN size_t _malloc_size_internal(void *ptr) +size_t _malloc_size_internal(void *ptr) { malloc_zone_t *zone = _objc_internal_zone(); return zone->size(zone, ptr); } -PRIVATE_EXTERN Class _calloc_class(size_t size) +Class _calloc_class(size_t size) { #if SUPPORT_GC if (UseGC) return (Class) malloc_zone_calloc(gc_zone, 1, size); @@ -1223,7 +1223,7 @@ objc_constructInstance(Class cls, void *bytes) } -PRIVATE_EXTERN id +id _objc_constructOrFree(Class cls, void *bytes) { id obj = _objc_constructInstance(cls, bytes); @@ -1247,7 +1247,7 @@ _objc_constructOrFree(Class cls, void *bytes) * Returns the number of allocated objects (possibly zero), with * the allocated pointers in *results. **********************************************************************/ -PRIVATE_EXTERN unsigned +unsigned _class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone, id *results, unsigned num_requested) { @@ -1268,7 +1268,7 @@ _class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone, { unsigned i; num_allocated = - malloc_zone_batch_malloc(zone ? zone : malloc_default_zone(), + malloc_zone_batch_malloc((malloc_zone_t *)(zone ? zone : malloc_default_zone()), size, (void**)results, num_requested); for (i = 0; i < num_allocated; i++) { bzero(results[i], size); @@ -1299,7 +1299,7 @@ _class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone, /*********************************************************************** * inform_duplicate. Complain about duplicate class implementations. **********************************************************************/ -PRIVATE_EXTERN void +void inform_duplicate(const char *name, Class oldCls, Class cls) { #if TARGET_OS_WIN32 @@ -1307,8 +1307,8 @@ inform_duplicate(const char *name, Class oldCls, Class cls) #else const header_info *oldHeader = _headerForClass(oldCls); const header_info *newHeader = _headerForClass(cls); - const char *oldName = oldHeader ? _nameForHeader(oldHeader->mhdr) : "??"; - const char *newName = newHeader ? _nameForHeader(newHeader->mhdr) : "??"; + const char *oldName = oldHeader ? oldHeader->fname : "??"; + const char *newName = newHeader ? newHeader->fname : "??"; _objc_inform ("Class %s is implemented in both %s and %s. " "One of the two will be used. " @@ -1345,7 +1345,7 @@ void _objc_insert_tagged_isa(unsigned char slotNumber, Class isa) { #endif -PRIVATE_EXTERN const char * +const char * copyPropertyAttributeString(const objc_property_attribute_t *attrs, unsigned int count) { @@ -1373,7 +1373,7 @@ copyPropertyAttributeString(const objc_property_attribute_t *attrs, } } - result = malloc(len + 1); + result = (char *)malloc(len + 1); char *s = result; for (i = 0; i < count; i++) { if (attrs[i].value) { @@ -1394,12 +1394,23 @@ copyPropertyAttributeString(const objc_property_attribute_t *attrs, /* Property attribute string format: + - Comma-separated name-value pairs. - Name and value may not contain , - Name may not contain " - Value may be empty - Name is single char, value follows - OR Name is double-quoted string of 2+ chars, value follows + + Grammar: + attribute-string: \0 + attribute-string: name-value-pair (',' name-value-pair)* + name-value-pair: unquoted-name optional-value + name-value-pair: quoted-name optional-value + unquoted-name: [^",] + quoted-name: '"' [^",]{2,} '"' + optional-value: [^,]* + */ static unsigned int iteratePropertyAttributes(const char *attrs, @@ -1501,7 +1512,7 @@ copyOneAttribute(unsigned int index, void *ctxa, void *ctxs, } -PRIVATE_EXTERN objc_property_attribute_t * +objc_property_attribute_t * copyPropertyAttributeList(const char *attrs, unsigned int *outCount) { if (!attrs) { @@ -1525,7 +1536,8 @@ copyPropertyAttributeList(const char *attrs, unsigned int *outCount) sizeof(objc_property_attribute_t) + strlen(attrs) + attrcount * 2; - objc_property_attribute_t *result = calloc(size, 1); + objc_property_attribute_t *result = (objc_property_attribute_t *) + calloc(size, 1); objc_property_attribute_t *ra = result; char *rs = (char *)(ra+attrcount+1); @@ -1553,7 +1565,7 @@ findOneAttribute(unsigned int index, void *ctxa, void *ctxs, char **resultp = (char **)ctxs; if (strlen(query) == nlen && 0 == strncmp(name, query, nlen)) { - char *result = calloc(vlen+1, 1); + char *result = (char *)calloc(vlen+1, 1); memcpy(result, value, vlen); result[vlen] = '\0'; *resultp = result; @@ -1563,7 +1575,6 @@ findOneAttribute(unsigned int index, void *ctxa, void *ctxs, return YES; } -PRIVATE_EXTERN char *copyPropertyAttributeValue(const char *attrs, const char *name) { char *result = NULL; diff --git a/runtime/objc-config.h b/runtime/objc-config.h index 9f81b9a..fc1b9f5 100644 --- a/runtime/objc-config.h +++ b/runtime/objc-config.h @@ -21,6 +21,9 @@ * @APPLE_LICENSE_HEADER_END@ */ +#ifndef _OBJC_CONFIG_H_ +#define _OBJC_CONFIG_H_ + #include // Define SUPPORT_GC=1 to enable garbage collection. @@ -52,11 +55,11 @@ # define SUPPORT_MOD 1 #endif -// Define SUPPORT_BUILTINS=1 to enable the builtin selector table from dyld -#if TARGET_OS_WIN32 -# define SUPPORT_BUILTINS 0 +// Define SUPPORT_PREOPT=1 to enable dyld shared cache optimizations +#if TARGET_OS_WIN32 || TARGET_IPHONE_SIMULATOR +# define SUPPORT_PREOPT 0 #else -# define SUPPORT_BUILTINS 1 +# define SUPPORT_PREOPT 1 #endif // Define SUPPORT_DEBUGGER_MODE=1 to enable lock-avoiding execution for debuggers @@ -137,9 +140,4 @@ // because objc-class.h is public and objc-config.h is not. //#define OBJC_INSTRUMENTED -// Get the nice macros for subroutine calling, etc. -// Not available on all architectures. Not needed -// (by us) on some configurations. -#if defined (__i386__) || defined (i386) -# include #endif diff --git a/runtime/objc-errors.m b/runtime/objc-errors.mm similarity index 82% rename from runtime/objc-errors.m rename to runtime/objc-errors.mm index 68b6e2f..f4710e1 100644 --- a/runtime/objc-errors.m +++ b/runtime/objc-errors.mm @@ -31,11 +31,11 @@ #include -PRIVATE_EXTERN void _objc_inform_on_crash(const char *fmt, ...) +void _objc_inform_on_crash(const char *fmt, ...) { } -PRIVATE_EXTERN void _objc_inform(const char *fmt, ...) +void _objc_inform(const char *fmt, ...) { va_list args; va_start(args, fmt); @@ -44,7 +44,7 @@ PRIVATE_EXTERN void _objc_inform(const char *fmt, ...) _cprintf("\n"); } -PRIVATE_EXTERN void _objc_fatal(const char *fmt, ...) +void _objc_fatal(const char *fmt, ...) { va_list args; va_start(args, fmt); @@ -55,7 +55,7 @@ PRIVATE_EXTERN void _objc_fatal(const char *fmt, ...) abort(); } -PRIVATE_EXTERN void __objc_error(id rcv, const char *fmt, ...) +void __objc_error(id rcv, const char *fmt, ...) { va_list args; va_start(args, fmt); @@ -65,7 +65,7 @@ PRIVATE_EXTERN void __objc_error(id rcv, const char *fmt, ...) abort(); } -PRIVATE_EXTERN void _objc_error(id rcv, const char *fmt, va_list args) +void _objc_error(id rcv, const char *fmt, va_list args) { _vcprintf(fmt, args); @@ -74,6 +74,7 @@ PRIVATE_EXTERN void _objc_error(id rcv, const char *fmt, va_list args) #else +#include OBJC_EXPORT void (*_error)(id, const char *, va_list); @@ -119,17 +120,32 @@ static void _objc_crashlog(const char *message) mutex_unlock(&crashlog_lock); } +// Returns true if logs should be sent to stderr as well as syslog. +// Copied from CFUtilities.c +static bool also_do_stderr(void) +{ + struct stat st; + int ret = fstat(STDERR_FILENO, &st); + if (ret < 0) return false; + mode_t m = st.st_mode & S_IFMT; + if (m == S_IFREG || m == S_IFSOCK) return true; + if (!(m == S_IFIFO || m == S_IFCHR)) return false; + + // if it could be a pipe back to launchd, fail + int64_t val = 0; + vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &val); + if (val) return false; + + return true; +} + // Print "message" to the console. static void _objc_syslog(const char *message) { - if (fcntl(STDERR_FILENO, F_GETFL, 0) != -1) { - // stderr is open - use it + syslog(LOG_ERR, "%s", message); + + if (also_do_stderr()) { write(STDERR_FILENO, message, strlen(message)); - if (message[strlen(message)-1] != '\n') { - write(STDERR_FILENO, "\n", 1); - } - } else { - syslog(LOG_ERR, "%s", message); } } @@ -137,7 +153,7 @@ static void _objc_syslog(const char *message) * _objc_error is the default *_error handler. */ #if __OBJC2__ -PRIVATE_EXTERN __attribute__((noreturn)) +__attribute__((noreturn)) #else // used by ExceptionHandling.framework #endif @@ -158,7 +174,7 @@ void _objc_error(id self, const char *fmt, va_list ap) /* * this routine handles errors that involve an object (or class). */ -PRIVATE_EXTERN void __objc_error(id rcv, const char *fmt, ...) +void __objc_error(id rcv, const char *fmt, ...) { va_list vp; @@ -174,7 +190,7 @@ PRIVATE_EXTERN void __objc_error(id rcv, const char *fmt, ...) * this routine handles severe runtime errors...like not being able * to read the mach headers, allocate space, etc...very uncommon. */ -PRIVATE_EXTERN void _objc_fatal(const char *fmt, ...) +void _objc_fatal(const char *fmt, ...) { va_list ap; char *buf1; @@ -195,7 +211,7 @@ PRIVATE_EXTERN void _objc_fatal(const char *fmt, ...) * this routine handles soft runtime errors...like not being able * add a category to a class (because it wasn't linked in). */ -PRIVATE_EXTERN void _objc_inform(const char *fmt, ...) +void _objc_inform(const char *fmt, ...) { va_list ap; char *buf1; @@ -217,7 +233,7 @@ PRIVATE_EXTERN void _objc_inform(const char *fmt, ...) * Like _objc_inform(), but prints the message only in any * forthcoming crash log, not to the console. */ -PRIVATE_EXTERN void _objc_inform_on_crash(const char *fmt, ...) +void _objc_inform_on_crash(const char *fmt, ...) { va_list ap; char *buf1; @@ -238,7 +254,7 @@ PRIVATE_EXTERN void _objc_inform_on_crash(const char *fmt, ...) /* * Like calling both _objc_inform and _objc_inform_on_crash. */ -PRIVATE_EXTERN void _objc_inform_now_and_on_crash(const char *fmt, ...) +void _objc_inform_now_and_on_crash(const char *fmt, ...) { va_list ap; char *buf1; @@ -279,7 +295,7 @@ BREAKPOINT_FUNCTION( void _objc_warn_deprecated(void) ); -PRIVATE_EXTERN void _objc_inform_deprecated(const char *oldf, const char *newf) +void _objc_inform_deprecated(const char *oldf, const char *newf) { if (PrintDeprecation) { if (newf) { diff --git a/runtime/objc-exception.h b/runtime/objc-exception.h index 153f50c..4082f5b 100644 --- a/runtime/objc-exception.h +++ b/runtime/objc-exception.h @@ -78,6 +78,8 @@ OBJC_EXPORT id objc_begin_catch(void *exc_buf) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); OBJC_EXPORT void objc_end_catch(void) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +OBJC_EXPORT void objc_terminate(void) + __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0); OBJC_EXPORT objc_exception_preprocessor objc_setExceptionPreprocessor(objc_exception_preprocessor fn) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); diff --git a/runtime/objc-exception.m b/runtime/objc-exception.mm similarity index 80% rename from runtime/objc-exception.m rename to runtime/objc-exception.mm index 1aa5881..fa9f426 100644 --- a/runtime/objc-exception.m +++ b/runtime/objc-exception.mm @@ -218,12 +218,12 @@ static void set_default_handlers() { } -PRIVATE_EXTERN void exception_init(void) +void exception_init(void) { // nothing to do } -PRIVATE_EXTERN void _destroyAltHandlerList(struct alt_handler_list *list) +void _destroyAltHandlerList(struct alt_handler_list *list) { // nothing to do } @@ -272,27 +272,20 @@ struct dwarf_eh_bases uintptr_t func; }; -extern uintptr_t _Unwind_GetIP (struct _Unwind_Context *); -extern uintptr_t _Unwind_GetCFA (struct _Unwind_Context *); -extern uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context *); +OBJC_EXTERN uintptr_t _Unwind_GetIP (struct _Unwind_Context *); +OBJC_EXTERN uintptr_t _Unwind_GetCFA (struct _Unwind_Context *); +OBJC_EXTERN uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context *); // C++ runtime types and functions // copied from cxxabi.h -#if TARGET_OS_IPHONE -typedef void (*terminate_handler) (); -// mangled std::set_terminate() -extern terminate_handler _ZSt13set_terminatePFvvE(terminate_handler); -#else -extern void (*__cxa_terminate_handler)(void); -#endif -extern void *__cxa_allocate_exception(size_t thrown_size); -extern void __cxa_throw(void *exc, void *typeinfo, void (*destructor)(void *)) __attribute__((noreturn)); -extern void *__cxa_begin_catch(void *exc); -extern void __cxa_end_catch(void); -extern void __cxa_rethrow(void); -extern void *__cxa_current_exception_type(void); +OBJC_EXTERN void *__cxa_allocate_exception(size_t thrown_size); +OBJC_EXTERN void __cxa_throw(void *exc, void *typeinfo, void (*destructor)(void *)) __attribute__((noreturn)); +OBJC_EXTERN void *__cxa_begin_catch(void *exc); +OBJC_EXTERN void __cxa_end_catch(void); +OBJC_EXTERN void __cxa_rethrow(void); +OBJC_EXTERN void *__cxa_current_exception_type(void); #if SUPPORT_ZEROCOST_EXCEPTIONS # define CXX_PERSONALITY __gxx_personality_v0 @@ -300,7 +293,7 @@ extern void *__cxa_current_exception_type(void); # define CXX_PERSONALITY __gxx_personality_sj0 #endif -extern _Unwind_Reason_Code +OBJC_EXTERN _Unwind_Reason_Code CXX_PERSONALITY(int version, _Unwind_Action actions, uint64_t exceptionClass, @@ -315,7 +308,7 @@ struct objc_typeinfo { const void **vtable; // always objc_ehtype_vtable+2 const char *name; // c++ typeinfo string - Class cls; + Class cls_unremapped; }; struct objc_exception { @@ -325,24 +318,29 @@ struct objc_exception { static void _objc_exception_noop(void) { } -static char _objc_exception_false(void) { return 0; } -static char _objc_exception_true(void) { return 1; } -static char _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo, +static bool _objc_exception_false(void) { return 0; } +// static bool _objc_exception_true(void) { return 1; } +static bool _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo, struct objc_typeinfo *throw_tinfo, void **throw_obj_p, unsigned outer); +// forward declaration +OBJC_EXPORT struct objc_typeinfo OBJC_EHTYPE_id; + +OBJC_EXPORT const void *objc_ehtype_vtable[] = { NULL, // typeinfo's vtable? - fixme - NULL, // typeinfo's typeinfo - fixme - _objc_exception_noop, // in-place destructor? - _objc_exception_noop, // destructor? - _objc_exception_true, // __is_pointer_p - _objc_exception_false, // __is_function_p - _objc_exception_do_catch, // __do_catch - _objc_exception_false, // __do_upcast + (void*)&OBJC_EHTYPE_id, // typeinfo's typeinfo - hack + (void*)_objc_exception_noop, // in-place destructor? + (void*)_objc_exception_noop, // destructor? + (void*)_objc_exception_false, // OLD __is_pointer_p + (void*)_objc_exception_false, // OLD __is_function_p + (void*)_objc_exception_do_catch, // OLD __do_catch, NEW can_catch + (void*)_objc_exception_false, // OLD __do_upcast }; +OBJC_EXPORT struct objc_typeinfo OBJC_EHTYPE_id = { objc_ehtype_vtable+2, "id", @@ -477,28 +475,32 @@ __objc_personality_v0(int version, **********************************************************************/ static void _objc_exception_destructor(void *exc_gen) { +#if SUPPORT_GC struct objc_exception *exc = (struct objc_exception *)exc_gen; if (UseGC && auto_zone_is_valid_pointer(gc_zone, exc->obj)) { // retained by objc_exception_throw auto_zone_release(gc_zone, exc->obj); } +#endif } void objc_exception_throw(id obj) { - struct objc_exception *exc = + struct objc_exception *exc = (struct objc_exception *) __cxa_allocate_exception(sizeof(struct objc_exception)); exc->obj = (*exception_preprocessor)(obj); +#if SUPPORT_GC if (UseGC && auto_zone_is_valid_pointer(gc_zone, obj)) { // exc is non-scanned memory. Retain the object for the duration. auto_zone_retain(gc_zone, obj); } +#endif exc->tinfo.vtable = objc_ehtype_vtable+2; exc->tinfo.name = object_getClassName(obj); - exc->tinfo.cls = obj ? _object_getClass(obj) : Nil; + exc->tinfo.cls_unremapped = obj ? _object_getClass(obj) : Nil; if (PrintExceptions) { _objc_inform("EXCEPTIONS: throwing %p (object %p, a %s)", @@ -553,37 +555,47 @@ void objc_end_catch(void) } -static char _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo, +// `outer` is not passed by the new libcxxabi +static bool _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo, struct objc_typeinfo *throw_tinfo, void **throw_obj_p, - unsigned outer) + unsigned outer UNAVAILABLE_ATTRIBUTE) { id exception; if (throw_tinfo->vtable != objc_ehtype_vtable+2) { // Only objc types can be caught here. if (PrintExceptions) _objc_inform("EXCEPTIONS: skipping catch(?)"); - return 0; + return false; } + // Adjust exception pointer. + // Old libcppabi: we lied about __is_pointer_p() so we have to do it here + // New libcxxabi: we have to do it here regardless + *throw_obj_p = **(void***)throw_obj_p; + // `catch (id)` always catches objc types. if (catch_tinfo == &OBJC_EHTYPE_id) { if (PrintExceptions) _objc_inform("EXCEPTIONS: catch(id)"); - return 1; + return true; } exception = *(id *)throw_obj_p; - // fixme remapped catch_tinfo->cls - if ((*exception_matcher)(catch_tinfo->cls, exception)) { + + Class handler_cls = _class_remap(catch_tinfo->cls_unremapped); + if (!handler_cls) { + // catch handler's class is weak-linked and missing. Not a match. + } + else if ((*exception_matcher)(handler_cls, exception)) { if (PrintExceptions) _objc_inform("EXCEPTIONS: catch(%s)", - class_getName(catch_tinfo->cls)); - return 1; + class_getName(handler_cls)); + return true; } if (PrintExceptions) _objc_inform("EXCEPTIONS: skipping catch(%s)", - class_getName(catch_tinfo->cls)); + class_getName(handler_cls)); - return 0; + return false; } @@ -624,13 +636,25 @@ static void _objc_terminate(void) } +/*********************************************************************** +* objc_terminate +* Calls std::terminate for clients who don't link to C++ themselves. +* Called by the compiler if an exception is thrown +* from a context where exceptions may not be thrown. +**********************************************************************/ +void objc_terminate(void) +{ + std::terminate(); +} + + /*********************************************************************** * alt handler support - zerocost implementation only **********************************************************************/ #if !SUPPORT_ALT_HANDLERS -PRIVATE_EXTERN void _destroyAltHandlerList(struct alt_handler_list *list) +void _destroyAltHandlerList(struct alt_handler_list *list) { } @@ -805,9 +829,22 @@ static uintptr_t read_address(uintptr_t *pp, } +struct frame_ips { + uintptr_t start; + uintptr_t end; +}; +struct frame_range { + uintptr_t ip_start; + uintptr_t ip_end; + uintptr_t cfa; + // precise ranges within ip_start..ip_end; NULL or {0,0} terminated + frame_ips *ips; +}; + + static bool isObjCExceptionCatcher(uintptr_t lsda, uintptr_t ip, const struct dwarf_eh_bases* bases, - uintptr_t* try_start, uintptr_t* try_end) + struct frame_range *frame) { unsigned char LPStart_enc = *(const unsigned char *)lsda++; @@ -829,23 +866,28 @@ static bool isObjCExceptionCatcher(uintptr_t lsda, uintptr_t ip, uintptr_t action_record = 0; uintptr_t p = call_site_table; + uintptr_t try_start; + uintptr_t try_end; + uintptr_t try_landing_pad; + while (p < call_site_table_end) { - uintptr_t start = read_address(&p, bases, call_site_enc); - uintptr_t len = read_address(&p, bases, call_site_enc); - uintptr_t pad = read_address(&p, bases, call_site_enc); - uintptr_t action = read_uleb(&p); + uintptr_t start = read_address(&p, bases, call_site_enc)+bases->func; + uintptr_t len = read_address(&p, bases, call_site_enc); + uintptr_t pad = read_address(&p, bases, call_site_enc); + uintptr_t action = read_uleb(&p); - if (ip < bases->func + start) { + if (ip < start) { // no more source ranges return false; } - else if (ip < bases->func + start + len) { + else if (ip < start + len) { // found the range if (!pad) return false; // ...but it has no landing pad // found the landing pad action_record = action ? action_record_table + action - 1 : 0; - *try_start = bases->func + start; - *try_end = bases->func + start + len; + try_start = start; + try_end = start + len; + try_landing_pad = pad; break; } } @@ -873,16 +915,58 @@ static bool isObjCExceptionCatcher(uintptr_t lsda, uintptr_t ip, break; } } while (offset); + + if (!has_handler) return false; - return has_handler; -} + // Count the number of source ranges with the same landing pad as our match + unsigned int range_count = 0; + p = call_site_table; + while (p < call_site_table_end) { + /*start*/ read_address(&p, bases, call_site_enc)/*+bases->func*/; + /*len*/ read_address(&p, bases, call_site_enc); + uintptr_t pad = read_address(&p, bases, call_site_enc); + /*action*/ read_uleb(&p); + + if (pad == try_landing_pad) { + range_count++; + } + } + if (range_count == 1) { + // No other source ranges with the same landing pad. We're done here. + frame->ips = NULL; + } + else { + // Record all ranges with the same landing pad as our match. + frame->ips = (frame_ips *) + _malloc_internal((range_count + 1) * sizeof(frame->ips[0])); + unsigned int r = 0; + p = call_site_table; + while (p < call_site_table_end) { + uintptr_t start = read_address(&p, bases, call_site_enc)+bases->func; + uintptr_t len = read_address(&p, bases, call_site_enc); + uintptr_t pad = read_address(&p, bases, call_site_enc); + /*action*/ read_uleb(&p); + + if (pad == try_landing_pad) { + if (start < try_start) try_start = start; + if (start+len > try_end) try_end = start+len; + frame->ips[r].start = start; + frame->ips[r].end = start+len; + r++; + } + } + + frame->ips[r].start = 0; + frame->ips[r].end = 0; + } + + frame->ip_start = try_start; + frame->ip_end = try_end; + + return true; +} -struct frame_range { - uintptr_t ip_start; - uintptr_t ip_end; - uintptr_t cfa; -}; static struct frame_range findHandler(void) { @@ -907,16 +991,16 @@ static struct frame_range findHandler(void) unw_word_t ip; unw_get_reg(&cursor, UNW_REG_IP, &ip); ip -= 1; - uintptr_t try_start; - uintptr_t try_end; - if ( isObjCExceptionCatcher(info.lsda, ip, &bases, &try_start, &try_end) ) { + struct frame_range try_range = {0, 0, 0, 0}; + if ( isObjCExceptionCatcher(info.lsda, ip, &bases, &try_range) ) { unw_word_t cfa; unw_get_reg(&cursor, UNW_REG_SP, &cfa); - return (struct frame_range){try_start, try_end, cfa}; + try_range.cfa = cfa; + return try_range; } } - return (struct frame_range){0, 0, 0}; + return (struct frame_range){0, 0, 0, 0}; } @@ -935,9 +1019,7 @@ struct alt_handler_debug { }; struct alt_handler_data { - uintptr_t ip_start; - uintptr_t ip_end; - uintptr_t cfa; + struct frame_range frame; objc_exception_handler fn; void *context; struct alt_handler_debug *debug; @@ -954,7 +1036,7 @@ static pthread_mutex_t DebugLock = PTHREAD_MUTEX_INITIALIZER; static struct alt_handler_list *DebugLists; static uintptr_t DebugCounter; -PRIVATE_EXTERN void alt_handler_error(uintptr_t token) __attribute__((noinline)); +void alt_handler_error(uintptr_t token) __attribute__((noinline)); static struct alt_handler_list * fetch_handler_list(BOOL create) @@ -965,7 +1047,7 @@ fetch_handler_list(BOOL create) struct alt_handler_list *list = data->handlerList; if (!list) { if (!create) return NULL; - list = _calloc_internal(1, sizeof(*list)); + list = (struct alt_handler_list *)_calloc_internal(1, sizeof(*list)); data->handlerList = list; if (DebugAltHandlers) { @@ -981,7 +1063,7 @@ fetch_handler_list(BOOL create) } -PRIVATE_EXTERN void _destroyAltHandlerList(struct alt_handler_list *list) +void _destroyAltHandlerList(struct alt_handler_list *list) { if (list) { if (DebugAltHandlers) { @@ -994,6 +1076,11 @@ PRIVATE_EXTERN void _destroyAltHandlerList(struct alt_handler_list *list) } if (list->handlers) { + for (unsigned int i = 0; i < list->allocated; i++) { + if (list->handlers[i].frame.ips) { + _free_internal(list->handlers[i].frame.ips); + } + } _free_internal(list->handlers); } _free_internal(list); @@ -1016,15 +1103,17 @@ uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context) if (list->used == list->allocated) { list->allocated = list->allocated*2 ?: 4; - list->handlers = _realloc_internal(list->handlers, list->allocated * sizeof(list->handlers[0])); + list->handlers = (struct alt_handler_data *) + _realloc_internal(list->handlers, + list->allocated * sizeof(list->handlers[0])); bzero(&list->handlers[list->used], (list->allocated - list->used) * sizeof(list->handlers[0])); i = list->used; } else { for (i = 0; i < list->allocated; i++) { - if (list->handlers[i].ip_start == 0 && - list->handlers[i].ip_end == 0 && - list->handlers[i].cfa == 0) + if (list->handlers[i].frame.ip_start == 0 && + list->handlers[i].frame.ip_end == 0 && + list->handlers[i].frame.cfa == 0) { break; } @@ -1036,9 +1125,7 @@ uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context) struct alt_handler_data *data = &list->handlers[i]; - data->ip_start = target_frame.ip_start; - data->ip_end = target_frame.ip_end; - data->cfa = target_frame.cfa; + data->frame = target_frame; data->fn = fn; data->context = context; list->used++; @@ -1053,7 +1140,8 @@ uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context) if (token == 0) token = DebugCounter++; if (!data->debug) { - data->debug = _calloc_internal(sizeof(*data->debug), 1); + data->debug = (struct alt_handler_debug *) + _calloc_internal(sizeof(*data->debug), 1); } else { bzero(data->debug, sizeof(*data->debug)); } @@ -1072,8 +1160,19 @@ uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context) if (PrintAltHandlers) { _objc_inform("ALT HANDLERS: installing alt handler #%lu %p(%p) on " "frame [ip=%p..%p sp=%p]", (unsigned long)token, - data->fn, data->context, (void *)data->ip_start, - (void *)data->ip_end, (void *)data->cfa); + data->fn, data->context, (void *)data->frame.ip_start, + (void *)data->frame.ip_end, (void *)data->frame.cfa); + if (data->frame.ips) { + unsigned int r = 0; + while (1) { + uintptr_t start = data->frame.ips[r].start; + uintptr_t end = data->frame.ips[r].end; + r++; + if (start == 0 && end == 0) break; + _objc_inform("ALT HANDLERS: ip=%p..%p", + (void*)start, (void*)end); + } + } } if (list->used > 1000) { @@ -1121,7 +1220,7 @@ void objc_removeExceptionHandler(uintptr_t token) struct alt_handler_data *data = &list->handlers[i]; - if (data->ip_start == 0 && data->ip_end == 0 && data->cfa == 0) { + if (data->frame.ip_start == 0 && data->frame.ip_end == 0 && data->frame.cfa == 0) { // token in range, but invalid alt_handler_error(token); __builtin_trap(); @@ -1130,18 +1229,19 @@ void objc_removeExceptionHandler(uintptr_t token) if (PrintAltHandlers) { _objc_inform("ALT HANDLERS: removing alt handler #%lu %p(%p) on " "frame [ip=%p..%p sp=%p]", (unsigned long)token, - data->fn, data->context, (void *)data->ip_start, - (void *)data->ip_end, (void *)data->cfa); + data->fn, data->context, (void *)data->frame.ip_start, + (void *)data->frame.ip_end, (void *)data->frame.cfa); } if (data->debug) _free_internal(data->debug); + if (data->frame.ips) _free_internal(data->frame.ips); bzero(data, sizeof(*data)); list->used--; } -PRIVATE_EXTERN void objc_alt_handler_error(void) __attribute__((noinline)); +void objc_alt_handler_error(void) __attribute__((noinline)); -PRIVATE_EXTERN void alt_handler_error(uintptr_t token) +void alt_handler_error(uintptr_t token) { if (!DebugAltHandlers) { _objc_inform_now_and_on_crash @@ -1157,7 +1257,7 @@ PRIVATE_EXTERN void alt_handler_error(uintptr_t token) // Search other threads' alt handler lists for this handler. struct alt_handler_list *list; for (list = DebugLists; list; list = list->next_DEBUGONLY) { - int h; + unsigned h; for (h = 0; h < list->allocated; h++) { struct alt_handler_data *data = &list->handlers[h]; if (data->debug && data->debug->token == token) { @@ -1173,7 +1273,7 @@ PRIVATE_EXTERN void alt_handler_error(uintptr_t token) for (i = 0; i < data->debug->backtraceSize; i++){ len += 4 + strlen(symbols[i]) + 1; } - symbolString = _calloc_internal(len, 1); + symbolString = (char *)_calloc_internal(len, 1); for (i = 0; i < data->debug->backtraceSize; i++){ strcat(symbolString, " "); strcat(symbolString, symbols[i]); @@ -1207,7 +1307,7 @@ PRIVATE_EXTERN void alt_handler_error(uintptr_t token) objc_alt_handler_error(); } -PRIVATE_EXTERN void objc_alt_handler_error(void) +void objc_alt_handler_error(void) { __builtin_trap(); } @@ -1225,8 +1325,27 @@ static void call_alt_handlers(struct _Unwind_Context *ctx) for (i = 0; i < list->allocated; i++) { struct alt_handler_data *data = &list->handlers[i]; - if (ip >= data->ip_start && ip < data->ip_end && data->cfa == cfa) + if (ip >= data->frame.ip_start && ip < data->frame.ip_end && data->frame.cfa == cfa) { + if (data->frame.ips) { + unsigned int r = 0; + bool found; + while (1) { + uintptr_t start = data->frame.ips[r].start; + uintptr_t end = data->frame.ips[r].end; + r++; + if (start == 0 && end == 0) { + found = false; + break; + } + if (ip >= start && ip < end) { + found = true; + break; + } + } + if (!found) continue; + } + // Copy and clear before the callback, in case the // callback manipulates the alt handler list. struct alt_handler_data copy = *data; @@ -1235,10 +1354,12 @@ static void call_alt_handlers(struct _Unwind_Context *ctx) if (PrintExceptions || PrintAltHandlers) { _objc_inform("EXCEPTIONS: calling alt handler %p(%p) from " "frame [ip=%p..%p sp=%p]", copy.fn, copy.context, - (void *)copy.ip_start, (void *)copy.ip_end, - (void *)copy.cfa); + (void *)copy.frame.ip_start, + (void *)copy.frame.ip_end, + (void *)copy.frame.cfa); } if (copy.fn) (*copy.fn)(nil, copy.context); + if (copy.frame.ips) _free_internal(copy.frame.ips); } } } @@ -1252,14 +1373,9 @@ static void call_alt_handlers(struct _Unwind_Context *ctx) * Initialize libobjc's exception handling system. * Called by map_images(). **********************************************************************/ -PRIVATE_EXTERN void exception_init(void) +void exception_init(void) { -#if TARGET_OS_IPHONE - old_terminate = _ZSt13set_terminatePFvvE(&_objc_terminate); -#else - old_terminate = __cxa_terminate_handler; - __cxa_terminate_handler = &_objc_terminate; -#endif + old_terminate = std::set_terminate(&_objc_terminate); } diff --git a/runtime/objc-externalref.m b/runtime/objc-externalref.mm similarity index 94% rename from runtime/objc-externalref.m rename to runtime/objc-externalref.mm index 121faad..4d34c6e 100644 --- a/runtime/objc-externalref.m +++ b/runtime/objc-externalref.mm @@ -122,7 +122,8 @@ inline static external_ref_list *_list_for_type(objc_xref_type_t ref_type) { // create a GC external reference -PRIVATE_EXTERN objc_xref_t _object_addExternalReference_gc(id obj, objc_xref_type_t ref_type) { +OBJC_EXTERN +objc_xref_t _object_addExternalReference_gc(id obj, objc_xref_type_t ref_type) { _initialize_gc(); __block size_t index; objc_xref_t xref; @@ -148,7 +149,8 @@ PRIVATE_EXTERN objc_xref_t _object_addExternalReference_gc(id obj, objc_xref_typ return xref; } -PRIVATE_EXTERN id _object_readExternalReference_gc(objc_xref_t ref) { +OBJC_EXTERN +id _object_readExternalReference_gc(objc_xref_t ref) { _initialize_gc(); __block id result; objc_xref_type_t ref_type = decode_type(ref); @@ -174,7 +176,8 @@ PRIVATE_EXTERN id _object_readExternalReference_gc(objc_xref_t ref) { return result; } -PRIVATE_EXTERN void _object_removeExternalReference_gc(objc_xref_t ref) { +OBJC_EXTERN +void _object_removeExternalReference_gc(objc_xref_t ref) { _initialize_gc(); objc_xref_type_t ref_type = decode_type(ref); if (ref_type != OBJC_XREF_TYPE_STATIC) { @@ -207,10 +210,11 @@ PRIVATE_EXTERN void _object_removeExternalReference_gc(objc_xref_t ref) { // SUPPORT_GC #endif -PRIVATE_EXTERN objc_xref_t _object_addExternalReference_rr(id obj, objc_xref_type_t ref_type) { +OBJC_EXTERN +objc_xref_t _object_addExternalReference_rr(id obj, objc_xref_type_t ref_type) { switch (ref_type) { case OBJC_XREF_STRONG: - objc_msgSend(obj, SEL_retain); + ((id(*)(id, SEL))objc_msgSend)(obj, SEL_retain); break; case OBJC_XREF_WEAK: break; @@ -221,17 +225,19 @@ PRIVATE_EXTERN objc_xref_t _object_addExternalReference_rr(id obj, objc_xref_typ return encode_pointer_and_type(obj, ref_type); } -PRIVATE_EXTERN id _object_readExternalReference_rr(objc_xref_t ref) { +OBJC_EXTERN +id _object_readExternalReference_rr(objc_xref_t ref) { id obj = decode_pointer(ref); return obj; } -PRIVATE_EXTERN void _object_removeExternalReference_rr(objc_xref_t ref) { +OBJC_EXTERN +void _object_removeExternalReference_rr(objc_xref_t ref) { id obj = decode_pointer(ref); objc_xref_type_t ref_type = decode_type(ref); switch (ref_type) { case OBJC_XREF_STRONG: - objc_msgSend(obj, SEL_release); + ((void(*)(id, SEL))objc_msgSend)(obj, SEL_release); break; case OBJC_XREF_WEAK: break; diff --git a/runtime/objc-file-old.m b/runtime/objc-file-old.m index 3eaa6b1..ae7b846 100644 --- a/runtime/objc-file-old.m +++ b/runtime/objc-file-old.m @@ -30,42 +30,37 @@ #if TARGET_OS_WIN32 -PRIVATE_EXTERN const char *_getObjcHeaderName(const headerType *head) -{ - return "??"; -} - /* -PRIVATE_EXTERN Module +Module _getObjcModules(const header_info *hi, size_t *nmodules) { - if (nmodules) *nmodules = hi->os.moduleCount; - return hi->os.modules; + if (nmodules) *nmodules = hi->moduleCount; + return hi->modules; } */ -PRIVATE_EXTERN SEL * +SEL * _getObjcSelectorRefs(const header_info *hi, size_t *nmess) { - if (nmess) *nmess = hi->os.selrefCount; - return hi->os.selrefs; + if (nmess) *nmess = hi->selrefCount; + return hi->selrefs; } -PRIVATE_EXTERN struct old_protocol ** +struct old_protocol ** _getObjcProtocols(const header_info *hi, size_t *nprotos) { - if (nprotos) *nprotos = hi->os.protocolCount; - return hi->os.protocols; + if (nprotos) *nprotos = hi->protocolCount; + return hi->protocols; } -PRIVATE_EXTERN struct old_class ** +struct old_class ** _getObjcClassRefs(const header_info *hi, size_t *nclasses) { - if (nclasses) *nclasses = hi->os.clsrefCount; - return (struct old_class **)hi->os.clsrefs; + if (nclasses) *nclasses = hi->clsrefCount; + return (struct old_class **)hi->clsrefs; } // __OBJC,__class_names section only emitted by CodeWarrior rdar://4951638 -PRIVATE_EXTERN const char * +const char * _getObjcClassNames(const header_info *hi, size_t *size) { if (size) *size = 0; @@ -75,7 +70,7 @@ _getObjcClassNames(const header_info *hi, size_t *size) #else #define GETSECT(name, type, sectname) \ - PRIVATE_EXTERN type *name(const header_info *hi, size_t *outCount) \ + type *name(const header_info *hi, size_t *outCount) \ { \ unsigned long byteCount = 0; \ type *data = (type *) \ @@ -91,7 +86,7 @@ GETSECT(_getObjcClassNames, const char, "__class_names"); // __OBJC,__class_names section only emitted by CodeWarrior rdar://4951638 -PRIVATE_EXTERN objc_image_info * +objc_image_info * _getObjcImageInfo(const headerType *mhdr, size_t *outBytes) { unsigned long byteCount = 0; @@ -102,7 +97,7 @@ _getObjcImageInfo(const headerType *mhdr, size_t *outBytes) } -PRIVATE_EXTERN struct old_protocol ** +struct old_protocol ** _getObjcProtocols(const header_info *hi, size_t *nprotos) { unsigned long size = 0; @@ -110,17 +105,17 @@ _getObjcProtocols(const header_info *hi, size_t *nprotos) getsectiondata(hi->mhdr, SEG_OBJC, "__protocol", &size); *nprotos = size / sizeof(struct old_protocol); - if (!hi->os.proto_refs && *nprotos) { + if (!hi->proto_refs && *nprotos) { size_t i; header_info *whi = (header_info *)hi; - whi->os.proto_refs = (struct old_protocol **) - malloc(*nprotos * sizeof(*hi->os.proto_refs)); + whi->proto_refs = (struct old_protocol **) + malloc(*nprotos * sizeof(*hi->proto_refs)); for (i = 0; i < *nprotos; i++) { - hi->os.proto_refs[i] = protos+i; + hi->proto_refs[i] = protos+i; } } - return hi->os.proto_refs; + return hi->proto_refs; } @@ -142,7 +137,7 @@ getsegbynamefromheader(const headerType *head, const char *segname) return NULL; } -PRIVATE_EXTERN BOOL +BOOL _hasObjcContents(const header_info *hi) { // Look for an __OBJC,* section other than __OBJC,__image_info diff --git a/runtime/objc-file.h b/runtime/objc-file.h index b27ddeb..c417fd8 100644 --- a/runtime/objc-file.h +++ b/runtime/objc-file.h @@ -29,12 +29,14 @@ __BEGIN_DECLS +// classref_t is not fixed up at launch; use remapClass() to convert + extern SEL *_getObjc2SelectorRefs(const header_info *hi, size_t *count); extern message_ref_t *_getObjc2MessageRefs(const header_info *hi, size_t *count); extern class_t **_getObjc2ClassRefs(const header_info *hi, size_t *count); extern class_t **_getObjc2SuperRefs(const header_info *hi, size_t *count); -extern class_t **_getObjc2ClassList(const header_info *hi, size_t *count); -extern class_t **_getObjc2NonlazyClassList(const header_info *hi, size_t *count); +extern classref_t *_getObjc2ClassList(const header_info *hi, size_t *count); +extern classref_t *_getObjc2NonlazyClassList(const header_info *hi, size_t *count); extern category_t **_getObjc2CategoryList(const header_info *hi, size_t *count); extern category_t **_getObjc2NonlazyCategoryList(const header_info *hi, size_t *count); extern protocol_t **_getObjc2ProtocolList(const header_info *hi, size_t *count); diff --git a/runtime/objc-file.mm b/runtime/objc-file.mm index e334764..525c579 100644 --- a/runtime/objc-file.mm +++ b/runtime/objc-file.mm @@ -52,7 +52,7 @@ objc_getImageSlide(const struct mach_header *header) return 0; // not reached } -PRIVATE_EXTERN uint8_t * +uint8_t * objc_getsectiondata(const struct mach_header *mh, const char *segname, const char *sectname, unsigned long *outSize) { uint32_t size = 0; @@ -85,7 +85,7 @@ objc_getsegbynamefromheader(const mach_header *head, const char *segname) return NULL; } -PRIVATE_EXTERN uint8_t * +uint8_t * objc_getsegmentdata(const struct mach_header *mh, const char *segname, unsigned long *outSize) { const struct segment_command *seg; @@ -104,7 +104,7 @@ objc_getsegmentdata(const struct mach_header *mh, const char *segname, unsigned #endif #define GETSECT(name, type, sectname) \ - PRIVATE_EXTERN type *name(const header_info *hi, size_t *outCount) \ + type *name(const header_info *hi, size_t *outCount) \ { \ unsigned long byteCount = 0; \ type *data = (type *) \ @@ -118,15 +118,15 @@ GETSECT(_getObjc2SelectorRefs, SEL, "__objc_selrefs"); GETSECT(_getObjc2MessageRefs, message_ref_t, "__objc_msgrefs"); GETSECT(_getObjc2ClassRefs, class_t *, "__objc_classrefs"); GETSECT(_getObjc2SuperRefs, class_t *, "__objc_superrefs"); -GETSECT(_getObjc2ClassList, class_t *, "__objc_classlist"); -GETSECT(_getObjc2NonlazyClassList, class_t *, "__objc_nlclslist"); +GETSECT(_getObjc2ClassList, classref_t, "__objc_classlist"); +GETSECT(_getObjc2NonlazyClassList, classref_t, "__objc_nlclslist"); GETSECT(_getObjc2CategoryList, category_t *, "__objc_catlist"); GETSECT(_getObjc2NonlazyCategoryList, category_t *, "__objc_nlcatlist"); GETSECT(_getObjc2ProtocolList, protocol_t *, "__objc_protolist"); GETSECT(_getObjc2ProtocolRefs, protocol_t *, "__objc_protorefs"); -PRIVATE_EXTERN objc_image_info * +objc_image_info * _getObjcImageInfo(const headerType *mhdr, size_t *outBytes) { unsigned long byteCount = 0; @@ -155,7 +155,7 @@ getsegbynamefromheader(const headerType *head, const char *segname) return NULL; } -PRIVATE_EXTERN BOOL +BOOL _hasObjcContents(const header_info *hi) { // Look for a __DATA,__objc* section other than __DATA,__objc_imageinfo diff --git a/runtime/objc-initialize.m b/runtime/objc-initialize.mm similarity index 95% rename from runtime/objc-initialize.m rename to runtime/objc-initialize.mm index 9d75091..17b87b8 100644 --- a/runtime/objc-initialize.m +++ b/runtime/objc-initialize.mm @@ -136,7 +136,8 @@ static _objc_initializing_classes *_fetchInitializingClassList(BOOL create) if (!create) { return NULL; } else { - list = _calloc_internal(1, sizeof(_objc_initializing_classes)); + list = (_objc_initializing_classes *) + _calloc_internal(1, sizeof(_objc_initializing_classes)); data->initializingClasses = list; } } @@ -147,7 +148,8 @@ static _objc_initializing_classes *_fetchInitializingClassList(BOOL create) // even if create == NO. // Allow 4 simultaneous class inits on this thread before realloc. list->classesAllocated = 4; - classes = _calloc_internal(list->classesAllocated, sizeof(Class)); + classes = (Class *) + _calloc_internal(list->classesAllocated, sizeof(Class)); list->metaclasses = classes; } return list; @@ -160,7 +162,7 @@ static _objc_initializing_classes *_fetchInitializingClassList(BOOL create) * Any part of the list may be NULL. * Called from _objc_pthread_destroyspecific(). **********************************************************************/ -PRIVATE_EXTERN + void _destroyInitializingClassList(struct _objc_initializing_classes *list) { if (list != NULL) { @@ -222,7 +224,9 @@ static void _setThisThreadIsInitializingClass(Class cls) // class list is full - reallocate list->classesAllocated = list->classesAllocated * 2 + 1; - list->metaclasses = _realloc_internal(list->metaclasses, list->classesAllocated * sizeof(Class)); + list->metaclasses = (Class *) + _realloc_internal(list->metaclasses, + list->classesAllocated * sizeof(Class)); // zero out the new entries list->metaclasses[i++] = cls; for ( ; i < list->classesAllocated; i++) { @@ -292,7 +296,7 @@ static void _finishInitializing(Class cls, Class supercls) // mark any subclasses that were merely waiting for this class if (!pendingInitializeMap) return; - pending = NXMapGet(pendingInitializeMap, cls); + pending = (PendingInitialize *)NXMapGet(pendingInitializeMap, cls); if (!pending) return; NXMapRemove(pendingInitializeMap, cls); @@ -335,9 +339,10 @@ static void _finishInitializingAfter(Class cls, Class supercls) // fixme pre-size this table for CF/NSObject +initialize } - pending = _malloc_internal(sizeof(*pending)); + pending = (PendingInitialize *)_malloc_internal(sizeof(*pending)); pending->subclass = cls; - pending->next = NXMapGet(pendingInitializeMap, supercls); + pending->next = (PendingInitialize *) + NXMapGet(pendingInitializeMap, supercls); NXMapInsert(pendingInitializeMap, supercls, pending); } @@ -348,15 +353,13 @@ static void _finishInitializingAfter(Class cls, Class supercls) * * Called only from _class_lookupMethodAndLoadCache (or itself). **********************************************************************/ -PRIVATE_EXTERN void _class_initialize(Class cls) +void _class_initialize(Class cls) { + assert(!_class_isMetaClass(cls)); + Class supercls; BOOL reallyInitialize = NO; - // Get the real class from the metaclass. The superclass chain - // hangs off the real class only. - cls = _class_getNonMetaClass(cls); - // Make sure super is done initializing BEFORE beginning to initialize cls. // See note about deadlock above. supercls = _class_getSuperclass(cls); diff --git a/runtime/objc-internal.h b/runtime/objc-internal.h index c960d3e..73d553a 100644 --- a/runtime/objc-internal.h +++ b/runtime/objc-internal.h @@ -44,14 +44,6 @@ __BEGIN_DECLS -// In-place construction of an Objective-C instance. -OBJC_EXPORT id objc_constructInstance(Class cls, void *bytes) - __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0) - OBJC_ARC_UNAVAILABLE; -OBJC_EXPORT void *objc_destructInstance(id obj) - __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0) - OBJC_ARC_UNAVAILABLE; - // In-place construction of an Objective-C class. OBJC_EXPORT Class objc_initializeClassPair(Class superclass_gen, const char *name, Class cls_gen, Class meta_gen) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0); @@ -91,6 +83,12 @@ OBJC_EXPORT BOOL objc_isAuto(id object) OBJC_EXPORT void instrumentObjcMessageSends(BOOL flag) __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); +// Initializer called by libSystem +#if __OBJC2__ +OBJC_EXPORT void _objc_init(void) + __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0); +#endif + // GC startup callback from Foundation OBJC_EXPORT malloc_zone_t *objc_collect_init(int (*callback)(void)) __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA); @@ -108,6 +106,11 @@ OBJC_EXPORT id objc_assign_ivar_generic(id value, id dest, ptrdiff_t offset) // Install missing-class callback. Used by the late unlamented ZeroLink. OBJC_EXPORT void _objc_setClassLoader(BOOL (*newClassLoader)(const char *)) OBJC2_UNAVAILABLE; +// Install handler for allocation failures. +// Handler may abort, or throw, or provide an object to return. +OBJC_EXPORT void _objc_setBadAllocHandler(id (*newHandler)(Class isa)) + __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0); + // This can go away when AppKit stops calling it (rdar://7811851) #if __OBJC2__ OBJC_EXPORT void objc_setMultithreaded (BOOL flag) @@ -151,6 +154,28 @@ OBJC_EXPORT const uint8_t *_object_getIvarLayout(Class cls_gen, id object) OBJC_EXPORT BOOL _class_usesAutomaticRetainRelease(Class cls) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0); +// Obsolete ARC conversions. + +// hack - remove and reinstate objc.h's definitions +#undef objc_retainedObject +#undef objc_unretainedObject +#undef objc_unretainedPointer +OBJC_EXPORT id objc_retainedObject(objc_objectptr_t pointer) + __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0); +OBJC_EXPORT id objc_unretainedObject(objc_objectptr_t pointer) + __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0); +OBJC_EXPORT objc_objectptr_t objc_unretainedPointer(id object) + __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0); +#if __has_feature(objc_arc) +# define objc_retainedObject(o) ((__bridge_transfer id)(objc_objectptr_t)(o)) +# define objc_unretainedObject(o) ((__bridge id)(objc_objectptr_t)(o)) +# define objc_unretainedPointer(o) ((__bridge objc_objectptr_t)(id)(o)) +#else +# define objc_retainedObject(o) ((id)(objc_objectptr_t)(o)) +# define objc_unretainedObject(o) ((id)(objc_objectptr_t)(o)) +# define objc_unretainedPointer(o) ((objc_objectptr_t)(id)(o)) +#endif + // API to only be called by root classes like NSObject or NSProxy OBJC_EXPORT @@ -329,6 +354,10 @@ _objc_autoreleasePoolPop(void *context) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0); +// Extra @encode data for XPC, or NULL +OBJC_EXPORT const char *_protocol_getMethodTypeEncoding(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod) + __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0); + // API to only be called by classes that provide their own reference count storage @@ -337,59 +366,102 @@ void _objc_deallocOnMainThreadHelper(void *context) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0); -#define _OBJC_SUPPORTED_INLINE_REFCNT_LOGIC(_rc_ivar, _dealloc2main) \ - -(id)retain { \ - /* this will fail to compile if _rc_ivar is an unsigned type */ \ +// On async versus sync deallocation and the _dealloc2main flag +// +// Theory: +// +// If order matters, then code must always: [self dealloc]. +// If order doesn't matter, then always async should be safe. +// +// Practice: +// +// The _dealloc2main bit is set for GUI objects that may be retained by other +// threads. Once deallocation begins on the main thread, doing more async +// deallocation will at best cause extra UI latency and at worst cause +// use-after-free bugs in unretained delegate style patterns. Yes, this is +// extremely fragile. Yes, in the long run, developers should switch to weak +// references. +// +// Note is NOT safe to do any equality check against the result of +// dispatch_get_current_queue(). The main thread can and does drain more than +// one dispatch queue. That is why we call pthread_main_np(). +// + +typedef enum { + _OBJC_RESURRECT_OBJECT = -1, /* _logicBlock has called -retain, and scheduled a -release for later. */ + _OBJC_DEALLOC_OBJECT_NOW = 1, /* call [self dealloc] immediately. */ + _OBJC_DEALLOC_OBJECT_LATER = 2 /* call [self dealloc] on the main queue. */ +} _objc_object_disposition_t; + +#define _OBJC_SUPPORTED_INLINE_REFCNT_LOGIC_BLOCK(_rc_ivar, _logicBlock) \ + -(id)retain { \ + /* this will fail to compile if _rc_ivar is an unsigned type */ \ int _retain_count_ivar_must_not_be_unsigned[0L - (__typeof__(_rc_ivar))-1] __attribute__((unused)); \ - __typeof__(_rc_ivar) _prev = __sync_fetch_and_add(&_rc_ivar, 2); \ - if (_prev < 0) { \ - __builtin_trap(); /* BUG: retain of dealloc'ed ref */ \ - } \ - return self; \ - } \ - -(oneway void)release { \ - __typeof__(_rc_ivar) _prev = __sync_fetch_and_sub(&_rc_ivar, 2); \ - if (_prev == 0) { \ - if (__sync_bool_compare_and_swap(&_rc_ivar, -2, 1)) { \ - if (_dealloc2main) { \ - dispatch_barrier_async_f(dispatch_get_main_queue(), \ - self, _objc_deallocOnMainThreadHelper); \ - } else { \ - [self dealloc]; \ - } \ - } else { \ - __builtin_trap(); /* BUG: dangling ref did a retain */ \ - } \ - } else if (_prev < 0) { \ - __builtin_trap(); /* BUG: over-release */ \ - } \ - } \ - -(NSUInteger)retainCount { \ - return (_rc_ivar + 2) >> 1; \ - } \ - -(BOOL)_tryRetain { \ - __typeof__(_rc_ivar) _prev; \ - do { \ - _prev = _rc_ivar; \ - if (_prev & 1) { \ - return 0; \ - } else if (_prev == -2) { \ - return 0; \ - } else if (_prev < -2) { \ - __builtin_trap(); /* BUG: over-release elsewhere */ \ - } \ + __typeof__(_rc_ivar) _prev = __sync_fetch_and_add(&_rc_ivar, 2); \ + if (_prev < -2) { /* specifically allow resurrection from logical 0. */ \ + __builtin_trap(); /* BUG: retain of over-released ref */ \ + } \ + return self; \ + } \ + -(oneway void)release { \ + __typeof__(_rc_ivar) _prev = __sync_fetch_and_sub(&_rc_ivar, 2); \ + if (_prev > 0) { \ + return; \ + } else if (_prev < 0) { \ + __builtin_trap(); /* BUG: over-release */ \ + } \ + _objc_object_disposition_t fate = _logicBlock(self); \ + if (fate == _OBJC_RESURRECT_OBJECT) { \ + return; \ + } \ + /* mark the object as deallocating. */ \ + if (!__sync_bool_compare_and_swap(&_rc_ivar, -2, 1)) { \ + __builtin_trap(); /* BUG: dangling ref did a retain */ \ + } \ + if (fate == _OBJC_DEALLOC_OBJECT_NOW) { \ + [self dealloc]; \ + } else if (fate == _OBJC_DEALLOC_OBJECT_LATER) { \ + dispatch_barrier_async_f(dispatch_get_main_queue(), self, \ + _objc_deallocOnMainThreadHelper); \ + } else { \ + __builtin_trap(); /* BUG: bogus fate value */ \ + } \ + } \ + -(NSUInteger)retainCount { \ + return (_rc_ivar + 2) >> 1; \ + } \ + -(BOOL)_tryRetain { \ + __typeof__(_rc_ivar) _prev; \ + do { \ + _prev = _rc_ivar; \ + if (_prev & 1) { \ + return 0; \ + } else if (_prev == -2) { \ + return 0; \ + } else if (_prev < -2) { \ + __builtin_trap(); /* BUG: over-release elsewhere */ \ + } \ } while ( ! __sync_bool_compare_and_swap(&_rc_ivar, _prev, _prev + 2)); \ - return 1; \ - } \ - -(BOOL)_isDeallocating { \ - if (_rc_ivar == -2) { \ - return 1; \ - } else if (_rc_ivar < -2) { \ - __builtin_trap(); /* BUG: over-release elsewhere */ \ - } \ - return _rc_ivar & 1; \ + return 1; \ + } \ + -(BOOL)_isDeallocating { \ + if (_rc_ivar == -2) { \ + return 1; \ + } else if (_rc_ivar < -2) { \ + __builtin_trap(); /* BUG: over-release elsewhere */ \ + } \ + return _rc_ivar & 1; \ } +#define _OBJC_SUPPORTED_INLINE_REFCNT_LOGIC(_rc_ivar, _dealloc2main) \ + _OBJC_SUPPORTED_INLINE_REFCNT_LOGIC_BLOCK(_rc_ivar, (^(id _self_ __attribute__((unused))) { \ + if (_dealloc2main && !pthread_main_np()) { \ + return _OBJC_DEALLOC_OBJECT_LATER; \ + } else { \ + return _OBJC_DEALLOC_OBJECT_NOW; \ + } \ + })) + #define _OBJC_SUPPORTED_INLINE_REFCNT(_rc_ivar) _OBJC_SUPPORTED_INLINE_REFCNT_LOGIC(_rc_ivar, 0) #define _OBJC_SUPPORTED_INLINE_REFCNT_WITH_DEALLOC2MAIN(_rc_ivar) _OBJC_SUPPORTED_INLINE_REFCNT_LOGIC(_rc_ivar, 1) diff --git a/runtime/objc-layout.m b/runtime/objc-layout.mm similarity index 98% rename from runtime/objc-layout.m rename to runtime/objc-layout.mm index 3554168..be9c2d5 100644 --- a/runtime/objc-layout.m +++ b/runtime/objc-layout.mm @@ -64,7 +64,8 @@ compress_layout(const uint8_t *bits, size_t bitmap_bits, BOOL weak) unsigned char *result; // overallocate a lot; reallocate at correct size later - unsigned char * const layout = _calloc_internal(bitmap_bits + 1, 1); + unsigned char * const layout = (unsigned char *) + _calloc_internal(bitmap_bits + 1, 1); unsigned char *l = layout; size_t i = 0; @@ -211,7 +212,7 @@ static void decompress_layout(const unsigned char *layout_string, layout_bitmap * spans an instance size of layoutStringSize); the rest is zero-filled. * The returned bitmap must be freed with layout_bitmap_free(). **********************************************************************/ -PRIVATE_EXTERN layout_bitmap +layout_bitmap layout_bitmap_create(const unsigned char *layout_string, size_t layoutStringInstanceSize, size_t instanceSize, BOOL weak) @@ -222,7 +223,7 @@ layout_bitmap_create(const unsigned char *layout_string, result.weak = weak; result.bitCount = words; result.bitsAllocated = words; - result.bits = _calloc_internal((words+7)/8, 1); + result.bits = (uint8_t *)_calloc_internal((words+7)/8, 1); if (!layout_string) { if (!weak) { @@ -247,7 +248,7 @@ layout_bitmap_create(const unsigned char *layout_string, * The bitmap is empty, to represent an object whose ivars are completely unscanned. * The returned bitmap must be freed with layout_bitmap_free(). **********************************************************************/ -PRIVATE_EXTERN layout_bitmap +layout_bitmap layout_bitmap_create_empty(size_t instanceSize, BOOL weak) { layout_bitmap result; @@ -256,18 +257,18 @@ layout_bitmap_create_empty(size_t instanceSize, BOOL weak) result.weak = weak; result.bitCount = words; result.bitsAllocated = words; - result.bits = _calloc_internal((words+7)/8, 1); + result.bits = (uint8_t *)_calloc_internal((words+7)/8, 1); return result; } -PRIVATE_EXTERN void +void layout_bitmap_free(layout_bitmap bits) { if (bits.bits) _free_internal(bits.bits); } -PRIVATE_EXTERN const unsigned char * +const unsigned char * layout_string_create(layout_bitmap bits) { const unsigned char *result = @@ -292,7 +293,7 @@ layout_string_create(layout_bitmap bits) } -PRIVATE_EXTERN void +void layout_bitmap_set_ivar(layout_bitmap bits, const char *type, size_t offset) { // fixme only handles some types @@ -325,7 +326,7 @@ layout_bitmap_set_ivar(layout_bitmap bits, const char *type, size_t offset) * Expand a layout bitmap to span newCount bits. * The new bits are undefined. **********************************************************************/ -PRIVATE_EXTERN void +void layout_bitmap_grow(layout_bitmap *bits, size_t newCount) { if (bits->bitCount >= newCount) return; @@ -333,7 +334,8 @@ layout_bitmap_grow(layout_bitmap *bits, size_t newCount) if (bits->bitsAllocated < newCount) { size_t newAllocated = bits->bitsAllocated * 2; if (newAllocated < newCount) newAllocated = newCount; - bits->bits = _realloc_internal(bits->bits, (newAllocated+7) / 8); + bits->bits = (uint8_t *) + _realloc_internal(bits->bits, (newAllocated+7) / 8); bits->bitsAllocated = newAllocated; } assert(bits->bitsAllocated >= bits->bitCount); @@ -349,7 +351,7 @@ layout_bitmap_grow(layout_bitmap *bits, size_t newCount) * The bitmap is expanded and bitCount updated if necessary. * newPos >= oldPos. **********************************************************************/ -PRIVATE_EXTERN void +void layout_bitmap_slide(layout_bitmap *bits, size_t oldPos, size_t newPos) { size_t shift; @@ -372,7 +374,7 @@ layout_bitmap_slide(layout_bitmap *bits, size_t oldPos, size_t newPos) * Like layout_bitmap_slide, but can slide backwards too. * The end of the bitmap is truncated. **********************************************************************/ -PRIVATE_EXTERN void +void layout_bitmap_slide_anywhere(layout_bitmap *bits, size_t oldPos, size_t newPos) { size_t shift; @@ -399,7 +401,7 @@ layout_bitmap_slide_anywhere(layout_bitmap *bits, size_t oldPos, size_t newPos) * dst must be at least as long as src. * Returns YES if any of dst's bits were changed. **********************************************************************/ -PRIVATE_EXTERN BOOL +BOOL layout_bitmap_splat(layout_bitmap dst, layout_bitmap src, size_t oldSrcInstanceSize) { @@ -438,7 +440,7 @@ layout_bitmap_splat(layout_bitmap dst, layout_bitmap src, * dst must be at least as long as src. * Returns YES if any of dst's bits were changed. **********************************************************************/ -PRIVATE_EXTERN BOOL +BOOL layout_bitmap_or(layout_bitmap dst, layout_bitmap src, const char *msg) { BOOL changed = NO; @@ -469,7 +471,7 @@ layout_bitmap_or(layout_bitmap dst, layout_bitmap src, const char *msg) * dst must be at least as long as src. * Returns YES if any of dst's bits were changed. **********************************************************************/ -PRIVATE_EXTERN BOOL +BOOL layout_bitmap_clear(layout_bitmap dst, layout_bitmap src, const char *msg) { BOOL changed = NO; @@ -494,7 +496,7 @@ layout_bitmap_clear(layout_bitmap dst, layout_bitmap src, const char *msg) } -PRIVATE_EXTERN void +void layout_bitmap_print(layout_bitmap bits) { size_t i; diff --git a/runtime/objc-load.m b/runtime/objc-load.mm similarity index 100% rename from runtime/objc-load.m rename to runtime/objc-load.mm diff --git a/runtime/objc-loadmethod.m b/runtime/objc-loadmethod.mm similarity index 93% rename from runtime/objc-loadmethod.m rename to runtime/objc-loadmethod.mm index e62e35c..f95e220 100644 --- a/runtime/objc-loadmethod.m +++ b/runtime/objc-loadmethod.mm @@ -29,6 +29,8 @@ #include "objc-loadmethod.h" #include "objc-private.h" +typedef void(*load_method_t)(id, SEL); + struct loadable_class { Class cls; // may be NULL IMP method; @@ -57,7 +59,7 @@ static int loadable_categories_allocated = 0; * Class cls has just become connected. Schedule it for +load if * it implements a +load method. **********************************************************************/ -PRIVATE_EXTERN void add_class_to_loadable_list(Class cls) +void add_class_to_loadable_list(Class cls) { IMP method; @@ -72,7 +74,7 @@ PRIVATE_EXTERN void add_class_to_loadable_list(Class cls) if (loadable_classes_used == loadable_classes_allocated) { loadable_classes_allocated = loadable_classes_allocated*2 + 16; - loadable_classes = + loadable_classes = (struct loadable_class *) _realloc_internal(loadable_classes, loadable_classes_allocated * sizeof(struct loadable_class)); @@ -90,7 +92,7 @@ PRIVATE_EXTERN void add_class_to_loadable_list(Class cls) * to its class. Schedule this category for +load after its parent class * becomes connected and has its own +load method called. **********************************************************************/ -PRIVATE_EXTERN void add_category_to_loadable_list(Category cat) +void add_category_to_loadable_list(Category cat) { IMP method; @@ -108,7 +110,7 @@ PRIVATE_EXTERN void add_category_to_loadable_list(Category cat) if (loadable_categories_used == loadable_categories_allocated) { loadable_categories_allocated = loadable_categories_allocated*2 + 16; - loadable_categories = + loadable_categories = (struct loadable_category *) _realloc_internal(loadable_categories, loadable_categories_allocated * sizeof(struct loadable_category)); @@ -125,7 +127,7 @@ PRIVATE_EXTERN void add_category_to_loadable_list(Category cat) * Class cls may have been loadable before, but it is now no longer * loadable (because its image is being unmapped). **********************************************************************/ -PRIVATE_EXTERN void remove_class_from_loadable_list(Class cls) +void remove_class_from_loadable_list(Class cls) { recursive_mutex_assert_locked(&loadMethodLock); @@ -149,7 +151,7 @@ PRIVATE_EXTERN void remove_class_from_loadable_list(Class cls) * Category cat may have been loadable before, but it is now no longer * loadable (because its image is being unmapped). **********************************************************************/ -PRIVATE_EXTERN void remove_category_from_loadable_list(Category cat) +void remove_category_from_loadable_list(Category cat) { recursive_mutex_assert_locked(&loadMethodLock); @@ -191,13 +193,13 @@ static void call_class_loads(void) // Call all +loads for the detached list. for (i = 0; i < used; i++) { Class cls = classes[i].cls; - IMP load_method = classes[i].method; + load_method_t load_method = (load_method_t)classes[i].method; if (!cls) continue; if (PrintLoading) { _objc_inform("LOAD: +[%s load]\n", _class_getName(cls)); } - (*load_method) ((id) cls, SEL_load); + (*load_method)(cls, SEL_load); } // Destroy the detached list. @@ -233,7 +235,7 @@ static BOOL call_category_loads(void) // Call all +loads for the detached list. for (i = 0; i < used; i++) { Category cat = cats[i].cat; - IMP load_method = cats[i].method; + load_method_t load_method = (load_method_t)cats[i].method; Class cls; if (!cat) continue; @@ -244,7 +246,7 @@ static BOOL call_category_loads(void) _class_getName(cls), _category_getName(cat)); } - (*load_method) ((id) cls, SEL_load); + (*load_method)(cls, SEL_load); cats[i].cat = NULL; } } @@ -265,8 +267,9 @@ static BOOL call_category_loads(void) for (i = 0; i < loadable_categories_used; i++) { if (used == allocated) { allocated = allocated*2 + 16; - cats = _realloc_internal(cats, allocated * - sizeof(struct loadable_category)); + cats = (struct loadable_category *) + _realloc_internal(cats, allocated * + sizeof(struct loadable_category)); } cats[used++] = loadable_categories[i]; } @@ -329,7 +332,7 @@ static BOOL call_category_loads(void) * Locking: loadMethodLock must be held by the caller * All other locks must not be held. **********************************************************************/ -PRIVATE_EXTERN void call_load_methods(void) +void call_load_methods(void) { static BOOL loading = NO; BOOL more_categories; @@ -340,6 +343,8 @@ PRIVATE_EXTERN void call_load_methods(void) if (loading) return; loading = YES; + void *pool = objc_autoreleasePoolPush(); + do { // 1. Repeatedly call class +loads until there aren't any more while (loadable_classes_used > 0) { @@ -352,6 +357,8 @@ PRIVATE_EXTERN void call_load_methods(void) // 3. Run more +loads if there are classes OR more untried categories } while (loadable_classes_used > 0 || more_categories); + objc_autoreleasePoolPop(pool); + loading = NO; } diff --git a/runtime/objc-lockdebug.m b/runtime/objc-lockdebug.mm similarity index 95% rename from runtime/objc-lockdebug.m rename to runtime/objc-lockdebug.mm index e6cae17..175460f 100644 --- a/runtime/objc-lockdebug.m +++ b/runtime/objc-lockdebug.mm @@ -76,7 +76,7 @@ getLocks(BOOL create) if (!create) { return NULL; } else { - locks = _calloc_internal(1, sizeof(_objc_lock_list) + sizeof(lockcount) * 16); + locks = (_objc_lock_list *)_calloc_internal(1, sizeof(_objc_lock_list) + sizeof(lockcount) * 16); locks->allocated = 16; locks->used = 0; tls_set(lock_tls, locks); @@ -88,7 +88,7 @@ getLocks(BOOL create) return locks; } else { _objc_lock_list *oldlocks = locks; - locks = _calloc_internal(1, sizeof(_objc_lock_list) + 2 * oldlocks->used * sizeof(lockcount)); + locks = (_objc_lock_list *)_calloc_internal(1, sizeof(_objc_lock_list) + 2 * oldlocks->used * sizeof(lockcount)); locks->used = oldlocks->used; locks->allocated = oldlocks->used * 2; memcpy(locks->list, oldlocks->list, locks->used * sizeof(lockcount)); @@ -152,7 +152,7 @@ clearLock(_objc_lock_list *locks, void *lock, int kind) * Mutex checking **********************************************************************/ -PRIVATE_EXTERN int +int _mutex_lock_debug(mutex_t *lock, const char *name) { _objc_lock_list *locks = getLocks(YES); @@ -167,7 +167,7 @@ _mutex_lock_debug(mutex_t *lock, const char *name) return _mutex_lock_nodebug(lock); } -PRIVATE_EXTERN int +int _mutex_try_lock_debug(mutex_t *lock, const char *name) { _objc_lock_list *locks = getLocks(YES); @@ -183,7 +183,7 @@ _mutex_try_lock_debug(mutex_t *lock, const char *name) return result; } -PRIVATE_EXTERN int +int _mutex_unlock_debug(mutex_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -198,7 +198,7 @@ _mutex_unlock_debug(mutex_t *lock, const char *name) return _mutex_unlock_nodebug(lock); } -PRIVATE_EXTERN void +void _mutex_assert_locked_debug(mutex_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -211,7 +211,7 @@ _mutex_assert_locked_debug(mutex_t *lock, const char *name) } -PRIVATE_EXTERN void +void _mutex_assert_unlocked_debug(mutex_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -228,7 +228,7 @@ _mutex_assert_unlocked_debug(mutex_t *lock, const char *name) * Recursive mutex checking **********************************************************************/ -PRIVATE_EXTERN int +int _recursive_mutex_lock_debug(recursive_mutex_t *lock, const char *name) { _objc_lock_list *locks = getLocks(YES); @@ -240,7 +240,7 @@ _recursive_mutex_lock_debug(recursive_mutex_t *lock, const char *name) return _recursive_mutex_lock_nodebug(lock); } -PRIVATE_EXTERN int +int _recursive_mutex_try_lock_debug(recursive_mutex_t *lock, const char *name) { _objc_lock_list *locks = getLocks(YES); @@ -255,7 +255,7 @@ _recursive_mutex_try_lock_debug(recursive_mutex_t *lock, const char *name) return result; } -PRIVATE_EXTERN int +int _recursive_mutex_unlock_debug(recursive_mutex_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -270,7 +270,7 @@ _recursive_mutex_unlock_debug(recursive_mutex_t *lock, const char *name) return _recursive_mutex_unlock_nodebug(lock); } -PRIVATE_EXTERN void +void _recursive_mutex_assert_locked_debug(recursive_mutex_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -283,7 +283,7 @@ _recursive_mutex_assert_locked_debug(recursive_mutex_t *lock, const char *name) } -PRIVATE_EXTERN void +void _recursive_mutex_assert_unlocked_debug(recursive_mutex_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -300,7 +300,7 @@ _recursive_mutex_assert_unlocked_debug(recursive_mutex_t *lock, const char *name * Monitor checking **********************************************************************/ -PRIVATE_EXTERN int +int _monitor_enter_debug(monitor_t *lock, const char *name) { _objc_lock_list *locks = getLocks(YES); @@ -315,7 +315,7 @@ _monitor_enter_debug(monitor_t *lock, const char *name) return _monitor_enter_nodebug(lock); } -PRIVATE_EXTERN int +int _monitor_exit_debug(monitor_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -330,7 +330,7 @@ _monitor_exit_debug(monitor_t *lock, const char *name) return _monitor_exit_nodebug(lock); } -PRIVATE_EXTERN int +int _monitor_wait_debug(monitor_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -344,7 +344,7 @@ _monitor_wait_debug(monitor_t *lock, const char *name) return _monitor_wait_nodebug(lock); } -PRIVATE_EXTERN void +void _monitor_assert_locked_debug(monitor_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -356,7 +356,7 @@ _monitor_assert_locked_debug(monitor_t *lock, const char *name) } } -PRIVATE_EXTERN void +void _monitor_assert_unlocked_debug(monitor_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -373,7 +373,7 @@ _monitor_assert_unlocked_debug(monitor_t *lock, const char *name) * rwlock checking **********************************************************************/ -PRIVATE_EXTERN void +void _rwlock_read_debug(rwlock_t *lock, const char *name) { _objc_lock_list *locks = getLocks(YES); @@ -392,7 +392,7 @@ _rwlock_read_debug(rwlock_t *lock, const char *name) _rwlock_read_nodebug(lock); } -PRIVATE_EXTERN int +int _rwlock_try_read_debug(rwlock_t *lock, const char *name) { _objc_lock_list *locks = getLocks(YES); @@ -409,7 +409,7 @@ _rwlock_try_read_debug(rwlock_t *lock, const char *name) return result; } -PRIVATE_EXTERN void +void _rwlock_unlock_read_debug(rwlock_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -424,7 +424,7 @@ _rwlock_unlock_read_debug(rwlock_t *lock, const char *name) _rwlock_unlock_read_nodebug(lock); } -PRIVATE_EXTERN void +void _rwlock_write_debug(rwlock_t *lock, const char *name) { _objc_lock_list *locks = getLocks(YES); @@ -444,7 +444,7 @@ _rwlock_write_debug(rwlock_t *lock, const char *name) } -PRIVATE_EXTERN int +int _rwlock_try_write_debug(rwlock_t *lock, const char *name) { _objc_lock_list *locks = getLocks(YES); @@ -461,7 +461,7 @@ _rwlock_try_write_debug(rwlock_t *lock, const char *name) return result; } -PRIVATE_EXTERN void +void _rwlock_unlock_write_debug(rwlock_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -477,7 +477,7 @@ _rwlock_unlock_write_debug(rwlock_t *lock, const char *name) } -PRIVATE_EXTERN void +void _rwlock_assert_reading_debug(rwlock_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -489,7 +489,7 @@ _rwlock_assert_reading_debug(rwlock_t *lock, const char *name) } } -PRIVATE_EXTERN void +void _rwlock_assert_writing_debug(rwlock_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -501,7 +501,7 @@ _rwlock_assert_writing_debug(rwlock_t *lock, const char *name) } } -PRIVATE_EXTERN void +void _rwlock_assert_locked_debug(rwlock_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); @@ -514,7 +514,7 @@ _rwlock_assert_locked_debug(rwlock_t *lock, const char *name) } } -PRIVATE_EXTERN void +void _rwlock_assert_unlocked_debug(rwlock_t *lock, const char *name) { _objc_lock_list *locks = getLocks(NO); diff --git a/runtime/objc-opt.mm b/runtime/objc-opt.mm new file mode 100644 index 0000000..f303a83 --- /dev/null +++ b/runtime/objc-opt.mm @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2012 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + objc-opt.mm + Management of optimizations in the dyld shared cache +*/ + +#include "objc.h" +#include "objc-private.h" + +using namespace objc_opt; + + +#if !SUPPORT_PREOPT +// Preoptimization not supported on this platform. + +bool isPreoptimized(void) +{ + return false; +} + +const objc_selopt_t *preoptimizedSelectors(void) +{ + return NULL; +} + +struct class_t * getPreoptimizedClass(const char *name) +{ + return NULL; +} + +header_info *preoptimizedHinfoForHeader(const headerType *mhdr) +{ + return NULL; +} + +void preopt_init(void) +{ + disableSharedCacheOptimizations(); + + if (PrintPreopt) { + _objc_inform("PREOPTIMIZATION: is DISABLED " + "(not supported on ths platform)"); + } +} + + +// !SUPPORT_PREOPT +#else +// SUPPORT_PREOPT + + +#include + +__BEGIN_DECLS + +// preopt: the actual opt used at runtime +// _objc_opt_data: opt data possibly written by dyld +// empty_opt_data: empty data to use if dyld didn't cooperate or DisablePreopt + +static const objc_opt_t *opt = NULL; +static bool preoptimized; + +extern const objc_opt_t _objc_opt_data; // in __TEXT, __objc_opt_ro +static const uint32_t empty_opt_data[] = OPT_INITIALIZER; + +bool isPreoptimized(void) +{ + return preoptimized; +} + + +const objc_selopt_t *preoptimizedSelectors(void) +{ + assert(opt); + return opt->selopt(); +} + +struct class_t * getPreoptimizedClass(const char *name) +{ + assert(opt); + objc_clsopt_t *classes = opt->clsopt(); + if (!classes) return NULL; + + void *cls; + void *hi; + uint32_t count = classes->getClassAndHeader(name, cls, hi); + if (count == 1 && ((header_info *)hi)->loaded) { + // exactly one matching class, and it's image is loaded + return (struct class_t *)cls; + } + if (count == 2) { + // more than one matching class - find one that is loaded + void *clslist[count]; + void *hilist[count]; + classes->getClassesAndHeaders(name, clslist, hilist); + for (uint32_t i = 0; i < count; i++) { + if (((header_info *)hilist[i])->loaded) { + return (struct class_t *)clslist[i]; + } + } + } + + // no match that is loaded + return NULL; +} + +namespace objc_opt { +struct objc_headeropt_t { + uint32_t count; + uint32_t entsize; + header_info headers[0]; // sorted by mhdr address + + header_info *get(const headerType *mhdr) + { + assert(entsize == sizeof(header_info)); + + int32_t start = 0; + int32_t end = count; + while (start <= end) { + int32_t i = (start+end)/2; + header_info *hi = headers+i; + if (mhdr == hi->mhdr) return hi; + else if (mhdr < hi->mhdr) end = i-1; + else start = i+1; + } + +#if !NDEBUG + for (uint32_t i = 0; i < count; i++) { + header_info *hi = headers+i; + if (mhdr == hi->mhdr) { + _objc_fatal("failed to find header %p (%d/%d)", + mhdr, i, count); + } + } +#endif + + return NULL; + } +}; +}; + + +header_info *preoptimizedHinfoForHeader(const headerType *mhdr) +{ + assert(opt); + objc_headeropt_t *hinfos = opt->headeropt(); + if (hinfos) return hinfos->get(mhdr); + else return NULL; +} + + +void preopt_init(void) +{ + // `opt` not set at compile time in order to detect too-early usage + const char *failure = NULL; + opt = &_objc_opt_data; + + if (DisablePreopt) { + // OBJC_DISABLE_PREOPTIMIZATION is set + // If opt->version != VERSION then you continue at your own risk. + failure = "(by OBJC_DISABLE_PREOPTIMIZATION)"; + } + else if (opt->version != objc_opt::VERSION) { + // This shouldn't happen. You probably forgot to + // change OPT_INITIALIZER and objc-sel-table.s. + // If dyld really did write the wrong optimization version, + // then we must halt because we don't know what bits dyld twiddled. + _objc_fatal("bad objc preopt version (want %d, got %d)", + objc_opt::VERSION, opt->version); + } + else if (!opt->selopt() || !opt->headeropt()) { + // One of the tables is missing. + failure = "(dyld shared cache is absent or out of date)"; + } +#if SUPPORT_IGNORED_SELECTOR_CONSTANT + else if (UseGC) { + // GC is on, which renames some selectors + // Non-selector optimizations are still valid, but we don't have + // any of those yet + failure = "(GC is on)"; + } +#endif + + if (failure) { + // All preoptimized selector references are invalid. + preoptimized = NO; + opt = (objc_opt_t *)empty_opt_data; + disableSharedCacheOptimizations(); + + if (PrintPreopt) { + _objc_inform("PREOPTIMIZATION: is DISABLED %s", failure); + } + } + else { + // Valid optimization data written by dyld shared cache + preoptimized = YES; + + if (PrintPreopt) { + _objc_inform("PREOPTIMIZATION: is ENABLED " + "(version %d)", opt->version); + } + } +} + + +__END_DECLS + +// SUPPORT_PREOPT +#endif diff --git a/runtime/objc-os.h b/runtime/objc-os.h index 5866799..f668570 100644 --- a/runtime/objc-os.h +++ b/runtime/objc-os.h @@ -72,6 +72,12 @@ # include # include "objc-probes.h" // generated dtrace probe definitions. + +#if defined(__i386__) || defined(__x86_64__) + +// Inlined spinlock. +// Not for arm on iOS because it hurts uniprocessor performance. + #define ARR_SPINLOCK_INIT 0 // XXX -- Careful: OSSpinLock isn't volatile, but should be typedef volatile int ARRSpinLock; @@ -97,14 +103,22 @@ static inline void ARRSpinLockUnlock(ARRSpinLock *l) { __sync_lock_release(l); } +__attribute__((always_inline)) +static inline int ARRSpinLockTry(ARRSpinLock *l) +{ + return __sync_bool_compare_and_swap(l, 0, 1); +} #define OSSpinLock ARRSpinLock -#define OSSpinLockTry(l) __sync_bool_compare_and_swap(l, 0, 1) +#define OSSpinLockTry(l) ARRSpinLockTry(l) #define OSSpinLockLock(l) ARRSpinLockLock(l) #define OSSpinLockUnlock(l) ARRSpinLockUnlock(l) #undef OS_SPINLOCK_INIT #define OS_SPINLOCK_INIT ARR_SPINLOCK_INIT +#endif + + #if !TARGET_OS_IPHONE # include #else @@ -129,9 +143,8 @@ static inline void ARRSpinLockUnlock(ARRSpinLock *l) # if __cplusplus # include # include +# include using namespace std; -# include - using namespace __gnu_cxx; # endif # define PRIVATE_EXTERN __attribute__((visibility("hidden"))) @@ -143,8 +156,8 @@ static inline void ARRSpinLockUnlock(ARRSpinLock *l) /* Use this for functions that are intended to be breakpoint hooks. If you do not, the compiler may optimize them away. BREAKPOINT_FUNCTION( void stop_on_error(void) ); */ -# define BREAKPOINT_FUNCTION(prototype) \ - __attribute__((noinline, visibility("hidden"))) \ +# define BREAKPOINT_FUNCTION(prototype) \ + OBJC_EXTERN __attribute__((noinline, visibility("hidden"))) \ prototype { asm(""); } #elif TARGET_OS_WIN32 @@ -169,9 +182,8 @@ static inline void ARRSpinLockUnlock(ARRSpinLock *l) # if __cplusplus # include # include +# include using namespace std; -# include - using namespace stdext; # define __BEGIN_DECLS extern "C" { # define __END_DECLS } # else @@ -356,7 +368,7 @@ static __inline int _mutex_lock_nodebug(mutex_t *m) { EnterCriticalSection(m->lock); return 0; } -static __inline int _mutex_try_lock_nodebug(mutex_t *m) { +static __inline bool _mutex_try_lock_nodebug(mutex_t *m) { // fixme error check if (!m->lock) { mutex_init(m); @@ -386,7 +398,7 @@ static __inline int _recursive_mutex_lock_nodebug(recursive_mutex_t *m) { assert(m->mutex); return WaitForSingleObject(m->mutex, INFINITE); } -static __inline int _recursive_mutex_try_lock_nodebug(recursive_mutex_t *m) { +static __inline bool _recursive_mutex_try_lock_nodebug(recursive_mutex_t *m) { assert(m->mutex); return (WAIT_OBJECT_0 == WaitForSingleObject(m->mutex, 0)); } @@ -400,7 +412,7 @@ static __inline int _recursive_mutex_unlock_nodebug(recursive_mutex_t *m) { typedef HANDLE mutex_t; static inline void mutex_init(HANDLE *m) { *m = CreateMutex(NULL, FALSE, NULL); } static inline void _mutex_lock(mutex_t *m) { WaitForSingleObject(*m, INFINITE); } -static inline int mutex_try_lock(mutex_t *m) { return WaitForSingleObject(*m, 0) == WAIT_OBJECT_0; } +static inline bool mutex_try_lock(mutex_t *m) { return WaitForSingleObject(*m, 0) == WAIT_OBJECT_0; } static inline void _mutex_unlock(mutex_t *m) { ReleaseMutex(*m); } */ @@ -498,20 +510,6 @@ static inline int monitor_notifyAll(monitor_t *c) { #define _rwlock_unlock_write_nodebug(m) _mutex_unlock_nodebug(m) -typedef struct { - struct objc_module **modules; - size_t moduleCount; - struct old_protocol **protocols; - size_t protocolCount; - void *imageinfo; - size_t imageinfoBytes; - SEL *selrefs; - size_t selrefCount; - struct objc_class **clsrefs; - size_t clsrefCount; - TCHAR *moduleName; -} os_header_info; - typedef IMAGE_DOS_HEADER headerType; // fixme YES bundle? NO bundle? sometimes? #define headerIsBundle(hi) YES @@ -568,10 +566,66 @@ static inline void tls_set(tls_key_t k, void *value) { } #if SUPPORT_DIRECT_THREAD_KEYS + +#if !NDEBUG +static bool is_valid_direct_key(tls_key_t k) { + return ( k == SYNC_DATA_DIRECT_KEY + || k == SYNC_COUNT_DIRECT_KEY + || k == AUTORELEASE_POOL_KEY +# if SUPPORT_RETURN_AUTORELEASE + || k == AUTORELEASE_POOL_RECLAIM_KEY +# endif + ); +} +#endif + +#if __arm__ + +// rdar://9162780 _pthread_get/setspecific_direct are inefficient +// copied from libdispatch + +__attribute__((always_inline)) __attribute__((const)) +static inline void** +tls_base(void) +{ + uintptr_t p; +#if defined(__arm__) && defined(_ARM_ARCH_6) + __asm__("mrc p15, 0, %[p], c13, c0, 3" : [p] "=&r" (p)); + return (void**)(p & ~0x3ul); +#else +#error tls_base not implemented +#endif +} + +__attribute__((always_inline)) +static inline void +tls_set_direct(void **tsdb, tls_key_t k, void *v) +{ + assert(is_valid_direct_key(k)); + + tsdb[k] = v; +} +#define tls_set_direct(k, v) \ + tls_set_direct(tls_base(), (k), (v)) + +__attribute__((always_inline)) +static inline void * +tls_get_direct(void **tsdb, tls_key_t k) +{ + assert(is_valid_direct_key(k)); + + return tsdb[k]; +} +#define tls_get_direct(k) \ + tls_get_direct(tls_base(), (k)) + +// arm +#else +// not arm + static inline void *tls_get_direct(tls_key_t k) { - assert(k == SYNC_DATA_DIRECT_KEY || - k == SYNC_COUNT_DIRECT_KEY); + assert(is_valid_direct_key(k)); if (_pthread_has_direct_tsd()) { return _pthread_getspecific_direct(k); @@ -581,8 +635,7 @@ static inline void *tls_get_direct(tls_key_t k) } static inline void tls_set_direct(tls_key_t k, void *value) { - assert(k == SYNC_DATA_DIRECT_KEY || - k == SYNC_COUNT_DIRECT_KEY); + assert(is_valid_direct_key(k)); if (_pthread_has_direct_tsd()) { _pthread_setspecific_direct(k, value); @@ -590,6 +643,11 @@ static inline void tls_set_direct(tls_key_t k, void *value) pthread_setspecific(k, value); } } + +// not arm +#endif + +// SUPPORT_DIRECT_THREAD_KEYS #endif @@ -610,12 +668,12 @@ static inline int _mutex_lock_nodebug(mutex_t *m) { } return pthread_mutex_lock(m); } -static inline int _mutex_try_lock_nodebug(mutex_t *m) { +static inline bool _mutex_try_lock_nodebug(mutex_t *m) { if (DebuggerMode && isManagedDuringDebugger(m)) { if (! isLockedDuringDebugger(m)) { gdb_objc_debuggerModeFailure(); } - return 1; + return true; } return !pthread_mutex_trylock(m); } @@ -644,13 +702,13 @@ static inline int _recursive_mutex_lock_nodebug(recursive_mutex_t *m) { } return pthread_mutex_lock(m->mutex); } -static inline int _recursive_mutex_try_lock_nodebug(recursive_mutex_t *m) { +static inline bool _recursive_mutex_try_lock_nodebug(recursive_mutex_t *m) { assert(m->mutex); if (DebuggerMode && isManagedDuringDebugger(m)) { if (! isLockedDuringDebugger((mutex_t *)m)) { gdb_objc_debuggerModeFailure(); } - return 1; + return true; } return !pthread_mutex_trylock(m->mutex); } @@ -721,10 +779,7 @@ static inline semaphore_t create_semaphore(void) z: readers allowed flag */ typedef struct { - volatile int32_t state; - semaphore_t readersDone; - semaphore_t writerDone; - pthread_mutex_t writerMutex; + pthread_rwlock_t rwl; } rwlock_t; extern BOOL isReadingDuringDebugger(rwlock_t *lock); @@ -732,10 +787,8 @@ extern BOOL isWritingDuringDebugger(rwlock_t *lock); static inline void rwlock_init(rwlock_t *l) { - l->state = 1; - l->readersDone = create_semaphore(); - l->writerDone = create_semaphore(); - l->writerMutex = (mutex_t)MUTEX_INITIALIZER; + int err __unused = pthread_rwlock_init(&l->rwl, NULL); + assert(err == 0); } static inline void _rwlock_read_nodebug(rwlock_t *l) @@ -746,28 +799,8 @@ static inline void _rwlock_read_nodebug(rwlock_t *l) } return; } - while (1) { - // Increment "blocked readers" or "active readers" count. - int32_t old = l->state; - if (old % 2 == 1) { - // Readers OK. Increment active reader count. - if (OSAtomicCompareAndSwap32Barrier(old, old + 2, &l->state)) { - // Success. Read lock acquired. - return; - } else { - // CAS failed (writer or another reader). Redo from start. - } - } - else { - // Readers not OK. Increment blocked reader count. - if (OSAtomicCompareAndSwap32(old, old + 0x10000, &l->state)) { - // Success. Wait for writer to complete, then retry. - semaphore_wait(l->writerDone); - } else { - // CAS failed (writer or another reader). Redo from start. - } - } - } + int err __unused = pthread_rwlock_rdlock(&l->rwl); + assert(err == 0); } static inline void _rwlock_unlock_read_nodebug(rwlock_t *l) @@ -775,45 +808,22 @@ static inline void _rwlock_unlock_read_nodebug(rwlock_t *l) if (DebuggerMode && isManagedDuringDebugger(l)) { return; } - // Decrement "active readers" count. - int32_t newState = OSAtomicAdd32Barrier(-2, &l->state); - if ((newState & 0xffff) == 0) { - // No active readers, and readers OK flag is clear. - // We're the last reader out and there's a writer waiting. Wake it. - semaphore_signal(l->readersDone); - } + int err __unused = pthread_rwlock_unlock(&l->rwl); + assert(err == 0); } -static inline int _rwlock_try_read_nodebug(rwlock_t *l) +static inline bool _rwlock_try_read_nodebug(rwlock_t *l) { - int i; if (DebuggerMode && isManagedDuringDebugger(l)) { if (! isReadingDuringDebugger(l)) { gdb_objc_debuggerModeFailure(); } - return 1; + return true; } - for (i = 0; i < 16; i++) { - int32_t old = l->state; - if (old % 2 != 1) { - // Readers not OK. Fail. - return 0; - } else { - // Readers OK. - if (OSAtomicCompareAndSwap32Barrier(old, old + 2, &l->state)) { - // Success. Read lock acquired. - return 1; - } else { - // CAS failed (writer or another reader). Redo from start. - // trylock will fail against writer, - // but retry a few times against reader. - } - } - } - - // Too many retries. Give up. - return 0; + int err = pthread_rwlock_tryrdlock(&l->rwl); + assert(err == 0 || err == EBUSY); + return (err == 0); } @@ -825,20 +835,8 @@ static inline void _rwlock_write_nodebug(rwlock_t *l) } return; } - - // Only one writer allowed at a time. - pthread_mutex_lock(&l->writerMutex); - - // Clear "readers OK" bit and "blocked readers" count. - int32_t newState = OSAtomicAnd32(0x0000fffe, (uint32_t *)&l->state); - - if (newState == 0) { - // No "active readers". Success. - OSMemoryBarrier(); - } else { - // Wait for "active readers" to complete. - semaphore_wait(l->readersDone); - } + int err __unused = pthread_rwlock_wrlock(&l->rwl); + assert(err == 0); } static inline void _rwlock_unlock_write_nodebug(rwlock_t *l) @@ -846,54 +844,21 @@ static inline void _rwlock_unlock_write_nodebug(rwlock_t *l) if (DebuggerMode && isManagedDuringDebugger(l)) { return; } - - // Reinstate "readers OK" bit and clear reader counts. - int32_t oldState; - do { - oldState = l->state; - } while (!OSAtomicCompareAndSwap32Barrier(oldState, 0x1, &l->state)); - - // Unblock any "blocked readers" that arrived while we held the lock - oldState = oldState >> 16; - while (oldState--) { - semaphore_signal(l->writerDone); - } - - // Allow a new writer. - pthread_mutex_unlock(&l->writerMutex); + int err __unused = pthread_rwlock_unlock(&l->rwl); + assert(err == 0); } -static inline int _rwlock_try_write_nodebug(rwlock_t *l) +static inline bool _rwlock_try_write_nodebug(rwlock_t *l) { if (DebuggerMode && isManagedDuringDebugger(l)) { if (! isWritingDuringDebugger(l)) { gdb_objc_debuggerModeFailure(); } - return 1; - } - - if (pthread_mutex_trylock(&l->writerMutex)) { - // Some other writer is in the way - fail - return 0; - } - - // Similar to _rwlock_write_nodebug, but less intrusive with readers active - - int32_t oldState, newState; - oldState = l->state; - newState = oldState & 0x0000fffe; - if (newState != 0) { - // Readers active. Give up. - pthread_mutex_unlock(&l->writerMutex); - return 0; - } - if (!OSAtomicCompareAndSwap32Barrier(oldState, newState, &l->state)) { - // CAS failed (reader interupted). Give up. - pthread_mutex_unlock(&l->writerMutex); - return 0; + return true; } - - return 1; + int err = pthread_rwlock_trywrlock(&l->rwl); + assert(err == 0 || err == EBUSY); + return (err == 0); } @@ -909,13 +874,6 @@ typedef struct section_64 sectionType; #define headerIsBundle(hi) (hi->mhdr->filetype == MH_BUNDLE) #define libobjc_header ((headerType *)&_mh_dylib_header) -typedef struct { - Dl_info dl_info; -#if !__OBJC2__ - struct old_protocol **proto_refs; -#endif -} os_header_info; - // Prototypes /* Secure /tmp usage */ diff --git a/runtime/objc-os.m b/runtime/objc-os.mm similarity index 82% rename from runtime/objc-os.m rename to runtime/objc-os.mm index 4a8aa4d..06bb95e 100644 --- a/runtime/objc-os.m +++ b/runtime/objc-os.mm @@ -107,8 +107,8 @@ WINBOOL APIENTRY DllMain( HMODULE hModule, environ_init(); tls_init(); lock_init(); - sel_init(NO); - exception_init(); + sel_init(NO, 3500); // old selector heuristic + exception_init(); break; case DLL_THREAD_ATTACH: @@ -129,40 +129,40 @@ OBJC_EXPORT void *_objc_init_image(HMODULE image, const objc_sections *sects) hi->mhdr = (const headerType *)image; hi->info = sects->iiStart; hi->allClassesRealized = NO; - hi->os.modules = sects->modStart ? (Module *)((void **)sects->modStart+1) : 0; - hi->os.moduleCount = (Module *)sects->modEnd - hi->os.modules; - hi->os.protocols = sects->protoStart ? (struct old_protocol **)((void **)sects->protoStart+1) : 0; - hi->os.protocolCount = (struct old_protocol **)sects->protoEnd - hi->os.protocols; - hi->os.imageinfo = NULL; - hi->os.imageinfoBytes = 0; - // hi->os.imageinfo = sects->iiStart ? (uint8_t *)((void **)sects->iiStart+1) : 0;; -// hi->os.imageinfoBytes = (uint8_t *)sects->iiEnd - hi->os.imageinfo; - hi->os.selrefs = sects->selrefsStart ? (SEL *)((void **)sects->selrefsStart+1) : 0; - hi->os.selrefCount = (SEL *)sects->selrefsEnd - hi->os.selrefs; - hi->os.clsrefs = sects->clsrefsStart ? (Class *)((void **)sects->clsrefsStart+1) : 0; - hi->os.clsrefCount = (Class *)sects->clsrefsEnd - hi->os.clsrefs; + hi->modules = sects->modStart ? (Module *)((void **)sects->modStart+1) : 0; + hi->moduleCount = (Module *)sects->modEnd - hi->modules; + hi->protocols = sects->protoStart ? (struct old_protocol **)((void **)sects->protoStart+1) : 0; + hi->protocolCount = (struct old_protocol **)sects->protoEnd - hi->protocols; + hi->imageinfo = NULL; + hi->imageinfoBytes = 0; + // hi->imageinfo = sects->iiStart ? (uint8_t *)((void **)sects->iiStart+1) : 0;; +// hi->imageinfoBytes = (uint8_t *)sects->iiEnd - hi->imageinfo; + hi->selrefs = sects->selrefsStart ? (SEL *)((void **)sects->selrefsStart+1) : 0; + hi->selrefCount = (SEL *)sects->selrefsEnd - hi->selrefs; + hi->clsrefs = sects->clsrefsStart ? (Class *)((void **)sects->clsrefsStart+1) : 0; + hi->clsrefCount = (Class *)sects->clsrefsEnd - hi->clsrefs; count = 0; - for (i = 0; i < hi->os.moduleCount; i++) { - if (hi->os.modules[i]) count++; + for (i = 0; i < hi->moduleCount; i++) { + if (hi->modules[i]) count++; } hi->mod_count = 0; hi->mod_ptr = 0; if (count > 0) { hi->mod_ptr = malloc(count * sizeof(struct objc_module)); - for (i = 0; i < hi->os.moduleCount; i++) { - if (hi->os.modules[i]) memcpy(&hi->mod_ptr[hi->mod_count++], hi->os.modules[i], sizeof(struct objc_module)); + for (i = 0; i < hi->moduleCount; i++) { + if (hi->modules[i]) memcpy(&hi->mod_ptr[hi->mod_count++], hi->modules[i], sizeof(struct objc_module)); } } - hi->os.moduleName = malloc(MAX_PATH * sizeof(TCHAR)); - GetModuleFileName((HMODULE)(hi->mhdr), hi->os.moduleName, MAX_PATH * sizeof(TCHAR)); + hi->moduleName = malloc(MAX_PATH * sizeof(TCHAR)); + GetModuleFileName((HMODULE)(hi->mhdr), hi->moduleName, MAX_PATH * sizeof(TCHAR)); - _objc_appendHeader(hi); + appendHeader(hi); if (PrintImages) { _objc_inform("IMAGES: loading image for %s%s%s\n", - _nameForHeader(hi->mhdr), + hi->fname, headerIsBundle(hi) ? " (bundle)" : "", _objcHeaderIsReplacement(hi) ? " (replacement)":""); } @@ -184,7 +184,7 @@ OBJC_EXPORT void _objc_unload_image(HMODULE image, header_info *hinfo) } -PRIVATE_EXTERN bool crashlog_header_name(header_info *hi) +bool crashlog_header_name(header_info *hi) { return true; } @@ -197,13 +197,13 @@ PRIVATE_EXTERN bool crashlog_header_name(header_info *hi) #include "objc-file-old.h" #endif -PRIVATE_EXTERN void mutex_init(mutex_t *m) +void mutex_init(mutex_t *m) { pthread_mutex_init(m, NULL); } -PRIVATE_EXTERN void recursive_mutex_init(recursive_mutex_t *m) +void recursive_mutex_init(recursive_mutex_t *m) { // fixme error checking pthread_mutex_t *newmutex; @@ -211,7 +211,7 @@ PRIVATE_EXTERN void recursive_mutex_init(recursive_mutex_t *m) // Build recursive mutex attributes, if needed static pthread_mutexattr_t *attr; if (!attr) { - pthread_mutexattr_t *newattr = + pthread_mutexattr_t *newattr = (pthread_mutexattr_t *) _malloc_internal(sizeof(pthread_mutexattr_t)); pthread_mutexattr_init(newattr); pthread_mutexattr_settype(newattr, PTHREAD_MUTEX_RECURSIVE); @@ -227,7 +227,7 @@ PRIVATE_EXTERN void recursive_mutex_init(recursive_mutex_t *m) attr_done: // Build the mutex itself - newmutex = _malloc_internal(sizeof(pthread_mutex_t)); + newmutex = (pthread_mutex_t *)_malloc_internal(sizeof(pthread_mutex_t)); pthread_mutex_init(newmutex, attr); while (!m->mutex) { if (OSAtomicCompareAndSwapPtrBarrier(0, newmutex, (void**)&m->mutex)) { @@ -245,88 +245,100 @@ PRIVATE_EXTERN void recursive_mutex_init(recursive_mutex_t *m) * bad_magic. * Return YES if the header has invalid Mach-o magic. **********************************************************************/ -PRIVATE_EXTERN BOOL bad_magic(const headerType *mhdr) +BOOL bad_magic(const headerType *mhdr) { return (mhdr->magic != MH_MAGIC && mhdr->magic != MH_MAGIC_64 && mhdr->magic != MH_CIGAM && mhdr->magic != MH_CIGAM_64); } -static header_info * _objc_addHeader(const headerType *mhdr) +static header_info * addHeader(const headerType *mhdr) { - size_t info_size = 0; - unsigned long seg_size; - const uint8_t *objc_segment; - const objc_image_info *image_info; - header_info *result; + header_info *hi; if (bad_magic(mhdr)) return NULL; - // Weed out duplicates - for (result = FirstHeader; result; result = result->next) { - if (mhdr == result->mhdr) return NULL; +#if __OBJC2__ + // Look for hinfo from the dyld shared cache. + hi = preoptimizedHinfoForHeader(mhdr); + if (hi) { + // Found an hinfo in the dyld shared cache. + + // Weed out duplicates. + if (hi->loaded) { + return NULL; + } + + // Initialize fields not set by the shared cache + // hi->next is set by appendHeader + hi->fname = dyld_image_path_containing_address(hi->mhdr); + hi->loaded = true; + hi->inSharedCache = true; + + if (PrintPreopt) { + _objc_inform("PREOPTIMIZATION: honoring preoptimized header info at %p for %s", hi, hi->fname); + } + +# if !NDEBUG + // Verify image_info + size_t info_size = 0; + const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size); + assert(image_info == hi->info); +# endif } + else +#endif + { + // Didn't find an hinfo in the dyld shared cache. - // Locate the __OBJC segment - image_info = _getObjcImageInfo(mhdr, &info_size); - objc_segment = getsegmentdata(mhdr, SEG_OBJC, &seg_size); - if (!objc_segment && !image_info) return NULL; + // Weed out duplicates + for (hi = FirstHeader; hi; hi = hi->next) { + if (mhdr == hi->mhdr) return NULL; + } - // Allocate a header_info entry. - result = _calloc_internal(sizeof(header_info), 1); + // Locate the __OBJC segment + size_t info_size = 0; + unsigned long seg_size; + const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size); + const uint8_t *objc_segment = getsegmentdata(mhdr,SEG_OBJC,&seg_size); + if (!objc_segment && !image_info) return NULL; - // Set up the new header_info entry. - result->mhdr = mhdr; + // Allocate a header_info entry. + hi = (header_info *)_calloc_internal(sizeof(header_info), 1); + + // Set up the new header_info entry. + hi->mhdr = mhdr; #if !__OBJC2__ - // mhdr must already be set - result->mod_count = 0; - result->mod_ptr = _getObjcModules(result, &result->mod_count); + // mhdr must already be set + hi->mod_count = 0; + hi->mod_ptr = _getObjcModules(hi, &hi->mod_count); #endif - result->info = image_info; - dladdr(result->mhdr, &result->os.dl_info); - result->allClassesRealized = NO; + hi->info = image_info; + hi->fname = dyld_image_path_containing_address(hi->mhdr); + hi->loaded = true; + hi->inSharedCache = false; + hi->allClassesRealized = NO; + } // dylibs are not allowed to unload // ...except those with image_info and nothing else (5359412) - if (result->mhdr->filetype == MH_DYLIB && _hasObjcContents(result)) { - dlopen(result->os.dl_info.dli_fname, RTLD_NOLOAD); - } - - // Make sure every copy of objc_image_info in this image is the same. - // This means same version and same bitwise contents. - if (result->info) { - const objc_image_info *start = result->info; - const objc_image_info *end = - (objc_image_info *)(info_size + (uint8_t *)start); - const objc_image_info *info = start; - while (info < end) { - // version is byte size, except for version 0 - size_t struct_size = info->version; - if (struct_size == 0) struct_size = 2 * sizeof(uint32_t); - if (info->version != start->version || - 0 != memcmp(info, start, struct_size)) - { - _objc_inform("'%s' has inconsistently-compiled Objective-C " - "code. Please recompile all code in it.", - _nameForHeader(mhdr)); - } - info = (objc_image_info *)(struct_size + (uint8_t *)info); - } + if (hi->mhdr->filetype == MH_DYLIB && _hasObjcContents(hi)) { + dlopen(hi->fname, RTLD_NOLOAD); } - _objc_appendHeader(result); + appendHeader(hi); - return result; + return hi; } #if !SUPPORT_GC -PRIVATE_EXTERN const char *_gcForHInfo(const header_info *hinfo) +const char *_gcForHInfo(const header_info *hinfo) { return ""; } -PRIVATE_EXTERN const char *_gcForHInfo2(const header_info *hinfo) +const char *_gcForHInfo2(const header_info *hinfo) { return ""; } @@ -336,7 +348,7 @@ PRIVATE_EXTERN const char *_gcForHInfo2(const header_info *hinfo) /*********************************************************************** * _gcForHInfo. **********************************************************************/ -PRIVATE_EXTERN const char *_gcForHInfo(const header_info *hinfo) +const char *_gcForHInfo(const header_info *hinfo) { if (_objcHeaderRequiresGC(hinfo)) { if (_objcHeaderSupportsCompaction(hinfo)) @@ -352,7 +364,7 @@ PRIVATE_EXTERN const char *_gcForHInfo(const header_info *hinfo) return "does not support GC"; } } -PRIVATE_EXTERN const char *_gcForHInfo2(const header_info *hinfo) +const char *_gcForHInfo2(const header_info *hinfo) { if (_objcHeaderRequiresGC(hinfo)) { if (_objcHeaderSupportsCompaction(hinfo)) @@ -381,7 +393,7 @@ static void check_wants_gc(BOOL *appWantsGC, BOOL *appSupportsCompaction) // Environment variables can override the following. if (DisableGC) { - _objc_inform("GC: forcing GC OFF because OBJC_DISABLE_GC is set"); + _objc_inform_on_crash("GC: forcing GC OFF because OBJC_DISABLE_GC is set"); *appWantsGC = NO; *appSupportsCompaction = NO; } @@ -398,7 +410,7 @@ static void check_wants_gc(BOOL *appWantsGC, BOOL *appSupportsCompaction) *appSupportsCompaction = (*appWantsGC && _objcHeaderSupportsCompaction(hi)) ? YES : NO; if (PrintGC) { _objc_inform("GC: executable '%s' %s", - _nameForHeader(hi->mhdr), _gcForHInfo(hi)); + hi->fname, _gcForHInfo(hi)); } } } @@ -432,7 +444,7 @@ static void verify_gc_readiness(BOOL wantsGC, BOOL *wantsCompaction, _objc_inform_now_and_on_crash ("'%s' was not compiled with -fobjc-gc or -fobjc-gc-only, " "but the application requires GC", - _nameForHeader(hi->mhdr)); + hi->fname); busted = YES; } else if (!wantsGC && _objcHeaderRequiresGC(hi)) { @@ -440,7 +452,7 @@ static void verify_gc_readiness(BOOL wantsGC, BOOL *wantsCompaction, _objc_inform_now_and_on_crash ("'%s' was compiled with -fobjc-gc-only, " "but the application does not support GC", - _nameForHeader(hi->mhdr)); + hi->fname); busted = YES; } @@ -449,7 +461,7 @@ static void verify_gc_readiness(BOOL wantsGC, BOOL *wantsCompaction, _objc_inform_now_and_on_crash ("'%s' was not linked with -Xlinker -objc_gc_compaction, " "but the application wants compaction.", - _nameForHeader(hi->mhdr)); + hi->fname); // Simply warn for now until radars are filed. Eventually, // objc_disableCompaction() will block until any current compaction completes. objc_disableCompaction(); @@ -458,7 +470,7 @@ static void verify_gc_readiness(BOOL wantsGC, BOOL *wantsCompaction, if (PrintGC) { _objc_inform("GC: library '%s' %s", - _nameForHeader(hi->mhdr), _gcForHInfo(hi)); + hi->fname, _gcForHInfo(hi)); } } @@ -576,7 +588,13 @@ static const char *gc_enforcer(enum dyld_image_states state, * * Locking: loadMethodLock(old) or runtimeLock(new) acquired by map_images. **********************************************************************/ -PRIVATE_EXTERN const char * +#if __OBJC2__ +#include "objc-file.h" +#else +#include "objc-file-old.h" +#endif + +const char * map_images_nolock(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]) { @@ -587,11 +605,13 @@ map_images_nolock(enum dyld_image_states state, uint32_t infoCount, header_info *hi; header_info *hList[infoCount]; uint32_t hCount; + size_t selrefCount = 0; // Perform first-time initialization if necessary. // This function is called before ordinary library initializers. // fixme defer initialization until an objc-using image is found? if (firstTime) { + preopt_init(); #if SUPPORT_GC InitialDyldRegistration = YES; dyld_register_image_state_change_handler(dyld_image_state_mapped, 0 /* batch */, &gc_enforcer); @@ -610,18 +630,29 @@ map_images_nolock(enum dyld_image_states state, uint32_t infoCount, while (i--) { const headerType *mhdr = (headerType *)infoList[i].imageLoadAddress; - hi = _objc_addHeader(mhdr); + hi = addHeader(mhdr); if (!hi) { // no objc data in this entry continue; } + if (mhdr->filetype == MH_EXECUTE) { +#if __OBJC2__ + size_t count; + _getObjc2SelectorRefs(hi, &count); + selrefCount += count; + _getObjc2MessageRefs(hi, &count); + selrefCount += count; +#else + _getObjcSelectorRefs(hi, &selrefCount); +#endif + } hList[hCount++] = hi; if (PrintImages) { _objc_inform("IMAGES: loading image for %s%s%s%s%s\n", - _nameForHeader(mhdr), + hi->fname, mhdr->filetype == MH_BUNDLE ? " (bundle)" : "", _objcHeaderIsReplacement(hi) ? " (replacement)" : "", _objcHeaderOptimizedByDyld(hi)?" (preoptimized)" : "", @@ -673,7 +704,7 @@ map_images_nolock(enum dyld_image_states state, uint32_t infoCount, if (firstTime) { extern SEL FwdSel; // in objc-msg-*.s - sel_init(wantsGC); + sel_init(wantsGC, selrefCount); FwdSel = sel_registerName("forward::"); arr_init(); @@ -694,7 +725,7 @@ map_images_nolock(enum dyld_image_states state, uint32_t infoCount, * * Locking: loadMethodLock(both) and runtimeLock(new) acquired by load_images **********************************************************************/ -PRIVATE_EXTERN BOOL +BOOL load_images_nolock(enum dyld_image_states state,uint32_t infoCount, const struct dyld_image_info infoList[]) { @@ -725,7 +756,7 @@ load_images_nolock(enum dyld_image_states state,uint32_t infoCount, * * Locking: loadMethodLock(both) and runtimeLock(new) acquired by unmap_image. **********************************************************************/ -PRIVATE_EXTERN void +void unmap_image_nolock(const struct mach_header *mh) { if (PrintImages) { @@ -745,7 +776,7 @@ unmap_image_nolock(const struct mach_header *mh) if (PrintImages) { _objc_inform("IMAGES: unloading image for %s%s%s%s\n", - _nameForHeader(hi->mhdr), + hi->fname, hi->mhdr->filetype == MH_BUNDLE ? " (bundle)" : "", _objcHeaderIsReplacement(hi) ? " (replacement)" : "", _gcForHInfo2(hi)); @@ -767,18 +798,26 @@ unmap_image_nolock(const struct mach_header *mh) _unload_image(hi); // Remove header_info from header list - _objc_removeHeader(hi); + removeHeader(hi); _free_internal(hi); } /*********************************************************************** * _objc_init -* Static initializer. Registers our image notifier with dyld. +* Bootstrap initialization. Registers our image notifier with dyld. +* Old ABI: called by dyld as a library initializer +* New ABI: called by libSystem BEFORE library initialization time **********************************************************************/ +#if !__OBJC2__ static __attribute__((constructor)) +#endif void _objc_init(void) { + static bool initialized = false; + if (initialized) return; + initialized = true; + // fixme defer initialization until an objc-using image is found? environ_init(); tls_init(); @@ -830,7 +869,7 @@ static const header_info *_headerForAddress(void *addr) * Return the image header containing this class, or NULL. * Returns NULL on runtime-constructed classes, and the NSCF classes. **********************************************************************/ -PRIVATE_EXTERN const header_info *_headerForClass(Class cls) +const header_info *_headerForClass(Class cls) { return _headerForAddress(cls); } @@ -847,7 +886,7 @@ PRIVATE_EXTERN const header_info *_headerForClass(Class cls) * 4. link count == 1 * Returns a file descriptor or -1. Errno may or may not be set on error. **********************************************************************/ -PRIVATE_EXTERN int secure_open(const char *filename, int flags, uid_t euid) +int secure_open(const char *filename, int flags, uid_t euid) { struct stat fs, ls; int fd = -1; @@ -926,7 +965,7 @@ PRIVATE_EXTERN int secure_open(const char *filename, int flags, uid_t euid) * By default this is the default malloc zone, but a dedicated zone is * used if environment variable OBJC_USE_INTERNAL_ZONE is set. **********************************************************************/ -PRIVATE_EXTERN malloc_zone_t *_objc_internal_zone(void) +malloc_zone_t *_objc_internal_zone(void) { static malloc_zone_t *z = (malloc_zone_t *)-1; if (z == (malloc_zone_t *)-1) { @@ -941,26 +980,12 @@ PRIVATE_EXTERN malloc_zone_t *_objc_internal_zone(void) } -PRIVATE_EXTERN const char * -_getObjcHeaderName(const headerType *header) -{ - Dl_info info; - - if (dladdr(header, &info)) { - return info.dli_fname; - } - else { - return (*_NSGetArgv())[0]; - } -} - - -PRIVATE_EXTERN bool crashlog_header_name(header_info *hi) +bool crashlog_header_name(header_info *hi) { - return crashlog_header_name_string(hi ? hi->os.dl_info.dli_fname : NULL); + return crashlog_header_name_string(hi ? hi->fname : NULL); } -PRIVATE_EXTERN bool crashlog_header_name_string(const char *name) +bool crashlog_header_name_string(const char *name) { CRSetCrashLogMessage2(name); return true; @@ -969,19 +994,19 @@ PRIVATE_EXTERN bool crashlog_header_name_string(const char *name) #if TARGET_OS_IPHONE -PRIVATE_EXTERN const char *__crashreporter_info__ = NULL; +const char *__crashreporter_info__ = NULL; -PRIVATE_EXTERN const char *CRSetCrashLogMessage(const char *msg) +const char *CRSetCrashLogMessage(const char *msg) { __crashreporter_info__ = msg; return msg; } -PRIVATE_EXTERN const char *CRGetCrashLogMessage(void) +const char *CRGetCrashLogMessage(void) { return __crashreporter_info__; } -PRIVATE_EXTERN const char *CRSetCrashLogMessage2(const char *msg) +const char *CRSetCrashLogMessage2(const char *msg) { // sorry return msg; diff --git a/runtime/objc-private.h b/runtime/objc-private.h index 36f49ce..334b42c 100644 --- a/runtime/objc-private.h +++ b/runtime/objc-private.h @@ -69,9 +69,10 @@ __BEGIN_DECLS #if SUPPORT_GC # include - extern PRIVATE_EXTERN BOOL UseGC; // equivalent to calling objc_collecting_enabled() - extern PRIVATE_EXTERN BOOL UseCompaction; // if binary has opted-in for compaction. - extern PRIVATE_EXTERN auto_zone_t *gc_zone; // the GC zone, or NULL if no GC + // PRIVATE_EXTERN is needed to help the compiler know "how" extern these are + PRIVATE_EXTERN extern BOOL UseGC; // equivalent to calling objc_collecting_enabled() + PRIVATE_EXTERN extern BOOL UseCompaction; // if binary has opted-in for compaction. + PRIVATE_EXTERN extern auto_zone_t *gc_zone; // the GC zone, or NULL if no GC extern void objc_addRegisteredClass(Class c); extern void objc_removeRegisteredClass(Class c); extern void objc_disableCompaction(); @@ -79,7 +80,6 @@ __BEGIN_DECLS # define UseGC NO # define UseCompaction NO # define gc_zone NULL -# define objc_assign_ivar_internal objc_assign_ivar # define objc_addRegisteredClass(c) do {} while(0) # define objc_removeRegisteredClass(c) do {} while(0) /* Uses of the following must be protected with UseGC. */ @@ -149,34 +149,52 @@ typedef struct { typedef struct _header_info { struct _header_info *next; - const headerType * mhdr; + const headerType *mhdr; + const objc_image_info *info; + const char *fname; // same as Dl_info.dli_fname + bool loaded; + bool inSharedCache; + bool allClassesRealized; + + // Do not add fields without editing ObjCModernAbstraction.hpp + +#if !__OBJC2__ + struct old_protocol **proto_refs; struct objc_module *mod_ptr; size_t mod_count; - const objc_image_info *info; - BOOL allClassesRealized; - os_header_info os; +# if TARGET_OS_WIN32 + struct objc_module **modules; + size_t moduleCount; + struct old_protocol **protocols; + size_t protocolCount; + void *imageinfo; + size_t imageinfoBytes; + SEL *selrefs; + size_t selrefCount; + struct objc_class **clsrefs; + size_t clsrefCount; + TCHAR *moduleName; +# endif +#endif } header_info; extern header_info *FirstHeader; extern header_info *LastHeader; extern int HeaderCount; -extern void _objc_appendHeader(header_info *hi); -extern void _objc_removeHeader(header_info *hi); -extern const char *_nameForHeader(const headerType*); +extern void appendHeader(header_info *hi); +extern void removeHeader(header_info *hi); extern objc_image_info *_getObjcImageInfo(const headerType *head, size_t *size); -extern const char *_getObjcHeaderName(const headerType *head); extern BOOL _hasObjcContents(const header_info *hi); /* selectors */ -extern void sel_init(BOOL gc); +extern void sel_init(BOOL gc, size_t selrefCount); extern SEL sel_registerNameNoLock(const char *str, BOOL copy); extern void sel_lock(void); extern void sel_unlock(void); extern BOOL sel_preoptimizationValid(const header_info *hi); -extern void disableSharedCacheOptimizations(void); extern SEL SEL_load; extern SEL SEL_initialize; @@ -189,11 +207,25 @@ extern SEL SEL_release; extern SEL SEL_autorelease; extern SEL SEL_retainCount; extern SEL SEL_alloc; +extern SEL SEL_allocWithZone; extern SEL SEL_copy; extern SEL SEL_new; extern SEL SEL_finalize; extern SEL SEL_forwardInvocation; +/* preoptimization */ +extern void preopt_init(void); +extern void disableSharedCacheOptimizations(void); +extern bool isPreoptimized(void); +extern header_info *preoptimizedHinfoForHeader(const headerType *mhdr); + +#if __cplusplus +namespace objc_opt { struct objc_selopt_t; }; +extern const struct objc_opt::objc_selopt_t *preoptimizedSelectors(void); +extern struct class_t * getPreoptimizedClass(const char *name); +#endif + + /* optional malloc zone for runtime data */ extern malloc_zone_t *_objc_internal_zone(void); @@ -209,18 +241,24 @@ extern size_t _malloc_size_internal(void *ptr); extern Class _calloc_class(size_t size); -extern IMP lookUpMethod(Class, SEL, BOOL initialize, BOOL cache); +extern IMP lookUpMethod(Class, SEL, BOOL initialize, BOOL cache, id obj); extern void lockForMethodLookup(void); extern void unlockForMethodLookup(void); -extern IMP prepareForMethodLookup(Class cls, SEL sel, BOOL initialize); +extern IMP prepareForMethodLookup(Class cls, SEL sel, BOOL initialize, id obj); extern IMP _cache_getImp(Class cls, SEL sel); extern Method _cache_getMethod(Class cls, SEL sel, IMP objc_msgForward_internal_imp); /* message dispatcher */ extern IMP _class_lookupMethodAndLoadCache3(id, SEL, Class); -extern id _objc_msgForward_internal(id self, SEL sel, ...); -extern id _objc_ignored_method(id self, SEL _cmd); + +#if !OBJC_OLD_DISPATCH_PROTOTYPES +extern void _objc_msgForward_internal(void); +extern void _objc_ignored_method(void); +#else +extern id _objc_msgForward_internal(id, SEL, ...); +extern id _objc_ignored_method(id, SEL, ...); +#endif /* errors */ extern void __objc_error(id, const char *, ...) __attribute__((format (printf, 2, 3), noreturn)); @@ -239,6 +277,10 @@ extern Class _objc_getFreedObjectClass (void); extern void *NXMapKeyCopyingInsert(NXMapTable *table, const void *key, const void *value); extern void *NXMapKeyFreeingRemove(NXMapTable *table, const void *key); +/* hash table additions */ +extern unsigned _NXHashCapacity(NXHashTable *table); +extern void _NXHashRehashToCapacity(NXHashTable *table, unsigned newCapacity); + /* property attribute parsing */ extern const char *copyPropertyAttributeString(const objc_property_attribute_t *attrs, unsigned int count); extern objc_property_attribute_t *copyPropertyAttributeList(const char *attrs, unsigned int *outCount); @@ -439,13 +481,14 @@ static inline int ignoreSelectorNamed(const char *sel) /* Protocol implementation */ #if !__OBJC2__ struct old_protocol; -PRIVATE_EXTERN struct objc_method_description * lookup_protocol_method(struct old_protocol *proto, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod); +struct objc_method_description * lookup_protocol_method(struct old_protocol *proto, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod, BOOL recursive); #else -PRIVATE_EXTERN Method _protocol_getMethod(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod); +Method _protocol_getMethod(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod, BOOL recursive); #endif /* GC and RTP startup */ extern void gc_init(BOOL wantsGC, BOOL wantsCompaction); +extern void gc_init2(void); extern void rtp_init(void); /* Exceptions */ @@ -539,6 +582,8 @@ ENV(PrintDeprecation); // env OBJC_PRINT_DEPRECATION_WARNINGS ENV(PrintReplacedMethods); // env OBJC_PRINT_REPLACED_METHODS ENV(PrintCaches); // env OBJC_PRINT_CACHE_SETUP ENV(PrintPoolHiwat); // env OBJC_PRINT_POOL_HIGHWATER +ENV(PrintCustomRR); // env OBJC_PRINT_CUSTOM_RR +ENV(PrintCustomAWZ); // env OBJC_PRINT_CUSTOM_AWZ ENV(UseInternalZone); // env OBJC_USE_INTERNAL_ZONE ENV(DebugUnload); // env OBJC_DEBUG_UNLOAD @@ -603,7 +648,7 @@ extern mutex_t cacheUpdateLock; // encoding.h extern unsigned int encoding_getNumberOfArguments(const char *typedesc); extern unsigned int encoding_getSizeOfArguments(const char *typedesc); -extern unsigned int encoding_getArgumentInfo(const char *typedesc, int arg, const char **type, int *offset); +extern unsigned int encoding_getArgumentInfo(const char *typedesc, unsigned int arg, const char **type, int *offset); extern void encoding_getReturnType(const char *t, char *dst, size_t dst_len); extern char * encoding_copyReturnType(const char *t); extern void encoding_getArgumentType(const char *t, unsigned int index, char *dst, size_t dst_len); @@ -659,6 +704,7 @@ extern Class _objc_allocateFutureClass(const char *name); extern const header_info *_headerForClass(Class cls); extern Class _class_getSuperclass(Class cls); +extern Class _class_remap(Class cls); extern BOOL _class_getInfo(Class cls, int info); extern const char *_class_getName(Class cls); extern size_t _class_getInstanceSize(Class cls); @@ -670,7 +716,7 @@ extern BOOL _class_isInitializing(Class cls); extern BOOL _class_isInitialized(Class cls); extern void _class_setInitializing(Class cls); extern void _class_setInitialized(Class cls); -extern Class _class_getNonMetaClass(Class cls); +extern Class _class_getNonMetaClass(Class cls, id obj); extern Method _class_getMethod(Class cls, SEL sel); extern Method _class_getMethodNoSuper(Class cls, SEL sel); extern Method _class_getMethodNoSuper_nolock(Class cls, SEL sel); @@ -745,7 +791,7 @@ static inline Class _object_getClass(id obj) // Global operator new and delete. We must not use any app overrides. // This ALSO REQUIRES each of these be in libobjc's unexported symbol list. #if __cplusplus -#import +#include inline void* operator new(std::size_t size) throw (std::bad_alloc) { return _malloc_internal(size); } inline void* operator new[](std::size_t size) throw (std::bad_alloc) { return _malloc_internal(size); } inline void* operator new(std::size_t size, const std::nothrow_t&) throw() { return _malloc_internal(size); } diff --git a/runtime/objc-references.h b/runtime/objc-references.h index e573d7d..1e49f7c 100644 --- a/runtime/objc-references.h +++ b/runtime/objc-references.h @@ -24,7 +24,7 @@ * objc-references.h */ -#if !defined(_OBJC_REFERENCES_H_) +#ifndef _OBJC_REFERENCES_H_ #define _OBJC_REFERENCES_H_ #include "objc-api.h" diff --git a/runtime/objc-references.mm b/runtime/objc-references.mm index cf259a6..4feeba7 100644 --- a/runtime/objc-references.mm +++ b/runtime/objc-references.mm @@ -29,37 +29,36 @@ #include #include +#if _LIBCPP_VERSION +# include +#else +# include + using namespace tr1; +#endif + // wrap all the murky C++ details in a namespace to get them out of the way. namespace objc_references_support { - struct ObjcPointerEqual { - bool operator()(void *p1, void *p2) const { + struct DisguisedPointerEqual { + bool operator()(uintptr_t p1, uintptr_t p2) const { return p1 == p2; } }; - - struct ObjectPointerLess { - bool operator()(const void *p1, const void *p2) const { - return p1 < p2; - } - }; - struct ObjcPointerHash { - uintptr_t operator()(void *p) const { - uintptr_t k = (uintptr_t)p; - + struct DisguisedPointerHash { + uintptr_t operator()(uintptr_t k) const { // borrowed from CFSet.c - #if __LP64__ +#if __LP64__ uintptr_t a = 0x4368726973746F70ULL; uintptr_t b = 0x686572204B616E65ULL; - #else +#else uintptr_t a = 0x4B616E65UL; uintptr_t b = 0x4B616E65UL; - #endif +#endif uintptr_t c = 1; a += k; - #if __LP64__ +#if __LP64__ a -= b; a -= c; a ^= (c >> 43); b -= c; b -= a; b ^= (a << 9); c -= a; c -= b; c ^= (b >> 8); @@ -72,7 +71,7 @@ namespace objc_references_support { a -= b; a -= c; a ^= (c >> 12); b -= c; b -= a; b ^= (a << 18); c -= a; c -= b; c ^= (b >> 22); - #else +#else a -= b; a -= c; a ^= (c >> 13); b -= c; b -= a; b ^= (a << 8); c -= a; c -= b; c ^= (b >> 13); @@ -82,10 +81,22 @@ namespace objc_references_support { a -= b; a -= c; a ^= (c >> 3); b -= c; b -= a; b ^= (a << 10); c -= a; c -= b; c ^= (b >> 15); - #endif +#endif return c; } }; + + struct ObjectPointerLess { + bool operator()(const void *p1, const void *p2) const { + return p1 < p2; + } + }; + + struct ObjcPointerHash { + uintptr_t operator()(void *p) const { + return DisguisedPointerHash()(uintptr_t(p)); + } + }; // STL allocator that uses the runtime's internal allocator. @@ -136,24 +147,40 @@ namespace objc_references_support { typedef const void *const_pointer; template struct rebind { typedef ObjcAllocator other; }; }; - - struct ObjcAssociation { - uintptr_t policy; - id value; - ObjcAssociation(uintptr_t newPolicy, id newValue) : policy(newPolicy), value(newValue) { } - ObjcAssociation() : policy(0), value(0) { } + + typedef uintptr_t disguised_ptr_t; + inline disguised_ptr_t DISGUISE(id value) { return ~uintptr_t(value); } + inline id UNDISGUISE(disguised_ptr_t dptr) { return id(~dptr); } + + class ObjcAssociation { + uintptr_t _policy; + id _value; + public: + ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {} + ObjcAssociation() : _policy(0), _value(nil) {} + + uintptr_t policy() const { return _policy; } + id value() const { return _value; } + + bool hasValue() { return _value != nil; } }; #if TARGET_OS_WIN32 typedef hash_map ObjectAssociationMap; - typedef hash_map AssociationsHashMap; + typedef hash_map AssociationsHashMap; #else - class ObjectAssociationMap : public std::map > > { + typedef ObjcAllocator > ObjectAssociationMapAllocator; + class ObjectAssociationMap : public std::map { + public: + void *operator new(size_t n) { return ::_malloc_internal(n); } + void operator delete(void *ptr) { ::_free_internal(ptr); } + }; + typedef ObjcAllocator > AssociationsHashMapAllocator; + class AssociationsHashMap : public unordered_map { public: void *operator new(size_t n) { return ::_malloc_internal(n); } void operator delete(void *ptr) { ::_free_internal(ptr); } }; - typedef hash_map > AssociationsHashMap; #endif } @@ -172,7 +199,7 @@ public: AssociationsHashMap &associations() { if (_map == NULL) - _map = new(::_malloc_internal(sizeof(AssociationsHashMap))) AssociationsHashMap(); + _map = new AssociationsHashMap(); return *_map; } }; @@ -191,26 +218,27 @@ enum { OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8) }; -PRIVATE_EXTERN id _object_get_associative_reference(id object, void *key) { +id _object_get_associative_reference(id object, void *key) { id value = nil; uintptr_t policy = OBJC_ASSOCIATION_ASSIGN; { AssociationsManager manager; AssociationsHashMap &associations(manager.associations()); - AssociationsHashMap::iterator i = associations.find(object); + disguised_ptr_t disguised_object = DISGUISE(object); + AssociationsHashMap::iterator i = associations.find(disguised_object); if (i != associations.end()) { ObjectAssociationMap *refs = i->second; ObjectAssociationMap::iterator j = refs->find(key); if (j != refs->end()) { ObjcAssociation &entry = j->second; - value = (id)entry.value; - policy = entry.policy; - if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) objc_msgSend(value, SEL_retain); + value = entry.value(); + policy = entry.policy(); + if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain); } } } if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) { - objc_msgSend(value, SEL_autorelease); + ((id(*)(id, SEL))objc_msgSend)(value, SEL_autorelease); } return value; } @@ -218,81 +246,78 @@ PRIVATE_EXTERN id _object_get_associative_reference(id object, void *key) { static id acquireValue(id value, uintptr_t policy) { switch (policy & 0xFF) { case OBJC_ASSOCIATION_SETTER_RETAIN: - return objc_msgSend(value, SEL_retain); + return ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain); case OBJC_ASSOCIATION_SETTER_COPY: - return objc_msgSend(value, SEL_copy); + return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy); } return value; } static void releaseValue(id value, uintptr_t policy) { if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) { - objc_msgSend(value, SEL_release); + ((id(*)(id, SEL))objc_msgSend)(value, SEL_release); } } struct ReleaseValue { void operator() (ObjcAssociation &association) { - releaseValue(association.value, association.policy); + releaseValue(association.value(), association.policy()); } }; -PRIVATE_EXTERN void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) { +void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) { // retain the new value (if any) outside the lock. - uintptr_t old_policy = 0; // NOTE: old_policy is always assigned to when old_value is non-nil. - id new_value = value ? acquireValue(value, policy) : nil, old_value = nil; + ObjcAssociation old_association(0, nil); + id new_value = value ? acquireValue(value, policy) : nil; { AssociationsManager manager; AssociationsHashMap &associations(manager.associations()); + disguised_ptr_t disguised_object = DISGUISE(object); if (new_value) { // break any existing association. - AssociationsHashMap::iterator i = associations.find(object); + AssociationsHashMap::iterator i = associations.find(disguised_object); if (i != associations.end()) { // secondary table exists ObjectAssociationMap *refs = i->second; ObjectAssociationMap::iterator j = refs->find(key); if (j != refs->end()) { - ObjcAssociation &old_entry = j->second; - old_policy = old_entry.policy; - old_value = old_entry.value; - old_entry.policy = policy; - old_entry.value = new_value; + old_association = j->second; + j->second = ObjcAssociation(policy, new_value); } else { (*refs)[key] = ObjcAssociation(policy, new_value); } } else { // create the new association (first time). ObjectAssociationMap *refs = new ObjectAssociationMap; - associations[object] = refs; + associations[disguised_object] = refs; (*refs)[key] = ObjcAssociation(policy, new_value); _class_setInstancesHaveAssociatedObjects(_object_getClass(object)); } } else { // setting the association to nil breaks the association. - AssociationsHashMap::iterator i = associations.find(object); + AssociationsHashMap::iterator i = associations.find(disguised_object); if (i != associations.end()) { ObjectAssociationMap *refs = i->second; ObjectAssociationMap::iterator j = refs->find(key); if (j != refs->end()) { - ObjcAssociation &old_entry = j->second; - old_policy = old_entry.policy; - old_value = (id) old_entry.value; + old_association = j->second; refs->erase(j); } } } } // release the old value (outside of the lock). - if (old_value) releaseValue(old_value, old_policy); + if (old_association.hasValue()) ReleaseValue()(old_association); } -PRIVATE_EXTERN void _object_remove_assocations(id object) { +void _object_remove_assocations(id object) { vector< ObjcAssociation,ObjcAllocator > elements; { AssociationsManager manager; AssociationsHashMap &associations(manager.associations()); if (associations.size() == 0) return; - AssociationsHashMap::iterator i = associations.find(object); + disguised_ptr_t disguised_object = DISGUISE(object); + AssociationsHashMap::iterator i = associations.find(disguised_object); if (i != associations.end()) { // copy all of the associations that need to be removed. ObjectAssociationMap *refs = i->second; diff --git a/runtime/objc-rtp.m b/runtime/objc-rtp.mm similarity index 87% rename from runtime/objc-rtp.m rename to runtime/objc-rtp.mm index 2fa1b6c..d85e669 100644 --- a/runtime/objc-rtp.m +++ b/runtime/objc-rtp.mm @@ -21,8 +21,8 @@ * @APPLE_LICENSE_HEADER_END@ */ -#import "objc-private.h" -#import +#include "objc-private.h" +#include #if defined(__i386__) @@ -51,7 +51,7 @@ static void rtp_swap_imp(unsigned *address, void *code, const char *name) } -PRIVATE_EXTERN void rtp_init(void) +void rtp_init(void) { // At load time, the page on which the objc_assign_* routines live is not // marked as executable. We fix that here, regardless of the GC choice. @@ -59,13 +59,13 @@ PRIVATE_EXTERN void rtp_init(void) if (UseGC) { rtp_swap_imp((unsigned*)objc_assign_ivar, - objc_assign_ivar_gc, "objc_assign_ivar"); + (void*)objc_assign_ivar_gc, "objc_assign_ivar"); rtp_swap_imp((unsigned*)objc_assign_global, - objc_assign_global_gc, "objc_assign_global"); + (void*)objc_assign_global_gc, "objc_assign_global"); rtp_swap_imp((unsigned*)objc_assign_threadlocal, - objc_assign_threadlocal_gc, "objc_assign_threadlocal"); + (void*)objc_assign_threadlocal_gc, "objc_assign_threadlocal"); rtp_swap_imp((unsigned*)objc_assign_strongCast, - objc_assign_strongCast_gc, "objc_assign_strongCast"); + (void*)objc_assign_strongCast_gc, "objc_assign_strongCast"); } else #endif @@ -80,7 +80,7 @@ PRIVATE_EXTERN void rtp_init(void) #else -PRIVATE_EXTERN void rtp_init(void) +void rtp_init(void) { if (PrintRTP) { _objc_inform("RTP: no rtp implementation for this platform"); diff --git a/runtime/objc-runtime-new.h b/runtime/objc-runtime-new.h index 64e8736..28df7ff 100644 --- a/runtime/objc-runtime-new.h +++ b/runtime/objc-runtime-new.h @@ -26,6 +26,12 @@ __BEGIN_DECLS +// We cannot store flags in the low bits of the 'data' field until we work with +// the 'leaks' team to not think that objc is leaking memory. See radar 8955342 +// for more info. +#define CLASS_FAST_FLAGS_VIA_RW_DATA 0 + + // Values for class_ro_t->flags // These are emitted by the compiler and are part of the ABI. // class is a metaclass @@ -42,7 +48,7 @@ __BEGIN_DECLS #define RO_EXCEPTION (1<<5) // this bit is available for reassignment // #define RO_REUSE_ME (1<<6) -// class compiled with -fobjc-arr (automatic retain/release) +// class compiled with -fobjc-arc (automatic retain/release) #define RO_IS_ARR (1<<7) // class is in an unloadable bundle - must never be set by compiler @@ -81,10 +87,18 @@ __BEGIN_DECLS #define RW_HAS_CXX_STRUCTORS (1<<20) // class has instance-specific GC layout #define RW_HAS_INSTANCE_SPECIFIC_LAYOUT (1 << 19) -// class or superclass has non-trivial .release_ivars implementation -#define RW_HAS_IVAR_RELEASER (1<<18) -// class or superclass has custom retain/release/autorelease/retainCount impl -#define RW_HAS_CUSTOM_RR (1<<17) +// class's method list is an array of method lists +#define RW_METHOD_ARRAY (1<<18) + +#if !CLASS_FAST_FLAGS_VIA_RW_DATA + // class or superclass has custom retain/release/autorelease/retainCount +# define RW_HAS_CUSTOM_RR (1<<17) + // class or superclass has custom allocWithZone: implementation +# define RW_HAS_CUSTOM_AWZ (1<<16) +#endif + +// classref_t is unremapped class_t* +typedef struct classref * classref_t; struct method_t { SEL name; @@ -137,21 +151,21 @@ typedef struct method_list_t { , method(&mlist.get(start)) { } - const method_iterator& operator += (ptrdiff_t count) { - method = (method_t*)((uint8_t *)method + count*entsize); - index += (int32_t)count; + const method_iterator& operator += (ptrdiff_t delta) { + method = (method_t*)((uint8_t *)method + delta*entsize); + index += (int32_t)delta; return *this; } - const method_iterator& operator -= (ptrdiff_t count) { - method = (method_t*)((uint8_t *)method - count*entsize); - index -= (int32_t)count; + const method_iterator& operator -= (ptrdiff_t delta) { + method = (method_t*)((uint8_t *)method - delta*entsize); + index -= (int32_t)delta; return *this; } - const method_iterator operator + (ptrdiff_t count) const { - return method_iterator(*this) += count; + const method_iterator operator + (ptrdiff_t delta) const { + return method_iterator(*this) += delta; } - const method_iterator operator - (ptrdiff_t count) const { - return method_iterator(*this) -= count; + const method_iterator operator - (ptrdiff_t delta) const { + return method_iterator(*this) -= delta; } method_iterator& operator ++ () { *this += 1; return *this; } @@ -231,6 +245,17 @@ typedef struct protocol_t { method_list_t *optionalInstanceMethods; method_list_t *optionalClassMethods; property_list_t *instanceProperties; + uint32_t size; // sizeof(protocol_t) + uint32_t flags; + const char **extendedMethodTypes; + + bool hasExtendedMethodTypesField() const { + return size >= (offsetof(protocol_t, extendedMethodTypes) + + sizeof(extendedMethodTypes)); + } + bool hasExtendedMethodTypes() const { + return hasExtendedMethodTypesField() && extendedMethodTypes; + } } protocol_t; typedef struct protocol_list_t { @@ -263,8 +288,11 @@ typedef struct class_rw_t { uint32_t version; const class_ro_t *ro; - - method_list_t **methods; + + union { + method_list_t **method_lists; // RW_METHOD_ARRAY == 1 + method_list_t *method_list; // RW_METHOD_ARRAY == 0 + }; struct chained_property_list *properties; const protocol_list_t ** protocols; @@ -272,48 +300,38 @@ typedef struct class_rw_t { struct class_t *nextSiblingClass; } class_rw_t; -// We cannot store flags in the low bits of the 'data' field until we work with -// the 'leaks' team to not think that objc is leaking memory. See radar 8955342 -// for more info. -#define CLASS_FAST_FLAGS_VIA_RW_DATA 0 - typedef struct class_t { struct class_t *isa; struct class_t *superclass; Cache cache; IMP *vtable; - uintptr_t data_NEVER_USE; // class_rw_t * plus flags + uintptr_t data_NEVER_USE; // class_rw_t * plus custom rr/alloc flags class_rw_t *data() const { -#if CLASS_FAST_FLAGS_VIA_RW_DATA - return (class_rw_t *)(data_NEVER_USE & ~3UL); -#else - return (class_rw_t *)data_NEVER_USE; -#endif + return (class_rw_t *)(data_NEVER_USE & ~(uintptr_t)3); } void setData(class_rw_t *newData) { -#if CLASS_FAST_FLAGS_VIA_RW_DATA - uintptr_t flags = (uintptr_t)data_NEVER_USE & 3UL; + uintptr_t flags = (uintptr_t)data_NEVER_USE & (uintptr_t)3; data_NEVER_USE = (uintptr_t)newData | flags; -#else - data_NEVER_USE = (uintptr_t)newData; -#endif } bool hasCustomRR() const { #if CLASS_FAST_FLAGS_VIA_RW_DATA - return data_NEVER_USE & 1UL; + return data_NEVER_USE & (uintptr_t)1; #else - return (data()->flags & RW_HAS_CUSTOM_RR); + return data()->flags & RW_HAS_CUSTOM_RR; #endif } - void setHasCustomRR(); - void unsetHasCustomRR(); + void setHasCustomRR(bool inherited = false); - bool hasIvarReleaser() const { - return (data()->flags & RW_HAS_IVAR_RELEASER); + bool hasCustomAWZ() const { +#if CLASS_FAST_FLAGS_VIA_RW_DATA + return data_NEVER_USE & (uintptr_t)2; +#else + return data()->flags & RW_HAS_CUSTOM_AWZ; +#endif } - void setHasIvarReleaser(); + void setHasCustomAWZ(bool inherited = false); bool isRootClass() const { return superclass == NULL; @@ -325,7 +343,7 @@ typedef struct class_t { typedef struct category_t { const char *name; - struct class_t *cls; + classref_t cls; struct method_list_t *instanceMethods; struct method_list_t *classMethods; struct protocol_list_t *protocols; diff --git a/runtime/objc-runtime-new.mm b/runtime/objc-runtime-new.mm index abf133c..f041c75 100644 --- a/runtime/objc-runtime-new.mm +++ b/runtime/objc-runtime-new.mm @@ -46,7 +46,8 @@ static uint32_t unalignedInstanceSize(class_t *cls); static uint32_t alignedInstanceSize(class_t *cls); static BOOL isMetaClass(class_t *cls); static class_t *getSuperclass(class_t *cls); -static void unload_class(class_t *cls, BOOL isMeta); +static void detach_class(class_t *cls, BOOL isMeta); +static void free_class(class_t *cls); static class_t *setSuperclass(class_t *cls, class_t *newSuper); static class_t *realizeClass(class_t *cls); static void flushCaches(class_t *cls); @@ -58,9 +59,12 @@ static IMP _method_getImplementation(method_t *m); static BOOL hasCxxStructors(class_t *cls); static IMP addMethod(class_t *cls, SEL name, IMP imp, const char *types, BOOL replace); static NXHashTable *realizedClasses(void); -static BOOL isRRSelector(SEL sel); +static bool isRRSelector(SEL sel); +static bool isAWZSelector(SEL sel); +static void updateCustomRR_AWZ(class_t *cls, method_t *meth); +static method_t *search_method_list(const method_list_t *mlist, SEL sel); -PRIVATE_EXTERN id objc_noop_imp(id self, SEL _cmd __unused) { +id objc_noop_imp(id self, SEL _cmd __unused) { return self; } @@ -69,10 +73,10 @@ PRIVATE_EXTERN id objc_noop_imp(id self, SEL _cmd __unused) { * Every lock used anywhere must be managed here. * Locks not managed here may cause gdb deadlocks. **********************************************************************/ -PRIVATE_EXTERN rwlock_t runtimeLock = {0}; -PRIVATE_EXTERN rwlock_t selLock = {0}; -PRIVATE_EXTERN mutex_t cacheUpdateLock = MUTEX_INITIALIZER; -PRIVATE_EXTERN recursive_mutex_t loadMethodLock = RECURSIVE_MUTEX_INITIALIZER; +rwlock_t runtimeLock; +rwlock_t selLock; +mutex_t cacheUpdateLock = MUTEX_INITIALIZER; +recursive_mutex_t loadMethodLock = RECURSIVE_MUTEX_INITIALIZER; static int debugger_runtimeLock; static int debugger_selLock; static int debugger_cacheUpdateLock; @@ -80,7 +84,7 @@ static int debugger_loadMethodLock; #define RDONLY 1 #define RDWR 2 -PRIVATE_EXTERN void lock_init(void) +void lock_init(void) { rwlock_init(&selLock); rwlock_init(&runtimeLock); @@ -98,7 +102,7 @@ PRIVATE_EXTERN void lock_init(void) * attempt to manipulate them will cause a trap. * Locks not handled here may cause deadlocks in gdb. **********************************************************************/ -PRIVATE_EXTERN int startDebuggerMode(void) +int startDebuggerMode(void) { int result = DEBUGGER_FULL; @@ -156,7 +160,7 @@ PRIVATE_EXTERN int startDebuggerMode(void) * endDebuggerMode * Relinquish locks acquired in startDebuggerMode(). **********************************************************************/ -PRIVATE_EXTERN void endDebuggerMode(void) +void endDebuggerMode(void) { assert(debugger_runtimeLock != 0); @@ -181,7 +185,7 @@ PRIVATE_EXTERN void endDebuggerMode(void) * Returns YES if the given lock is handled specially during debugger * mode (i.e. debugger mode tries to acquire it). **********************************************************************/ -PRIVATE_EXTERN BOOL isManagedDuringDebugger(void *lock) +BOOL isManagedDuringDebugger(void *lock) { if (lock == &selLock) return YES; if (lock == &cacheUpdateLock) return YES; @@ -196,13 +200,12 @@ PRIVATE_EXTERN BOOL isManagedDuringDebugger(void *lock) * Locking a managed mutex during debugger mode causes a trap unless * this returns YES. **********************************************************************/ -PRIVATE_EXTERN BOOL isLockedDuringDebugger(void *lock) +BOOL isLockedDuringDebugger(void *lock) { assert(DebuggerMode); if (lock == &cacheUpdateLock) return YES; if (lock == (mutex_t *)&loadMethodLock) return YES; - return NO; } @@ -212,7 +215,7 @@ PRIVATE_EXTERN BOOL isLockedDuringDebugger(void *lock) * Read-locking a managed rwlock during debugger mode causes a trap unless * this returns YES. **********************************************************************/ -PRIVATE_EXTERN BOOL isReadingDuringDebugger(rwlock_t *lock) +BOOL isReadingDuringDebugger(rwlock_t *lock) { assert(DebuggerMode); @@ -229,7 +232,7 @@ PRIVATE_EXTERN BOOL isReadingDuringDebugger(rwlock_t *lock) * Write-locking a managed rwlock during debugger mode causes a trap unless * this returns YES. **********************************************************************/ -PRIVATE_EXTERN BOOL isWritingDuringDebugger(rwlock_t *lock) +BOOL isWritingDuringDebugger(rwlock_t *lock) { assert(DebuggerMode); @@ -374,40 +377,40 @@ static const char * const defaultVtableGC[] = { "countByEnumeratingWithState:objects:count:", }; -OBJC_EXTERN id objc_msgSend_vtable0(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable1(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable2(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable3(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable4(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable5(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable6(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable7(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable8(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable9(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable10(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable11(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable12(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable13(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable14(id, SEL, ...); -OBJC_EXTERN id objc_msgSend_vtable15(id, SEL, ...); +OBJC_EXTERN void objc_msgSend_vtable0(void); +OBJC_EXTERN void objc_msgSend_vtable1(void); +OBJC_EXTERN void objc_msgSend_vtable2(void); +OBJC_EXTERN void objc_msgSend_vtable3(void); +OBJC_EXTERN void objc_msgSend_vtable4(void); +OBJC_EXTERN void objc_msgSend_vtable5(void); +OBJC_EXTERN void objc_msgSend_vtable6(void); +OBJC_EXTERN void objc_msgSend_vtable7(void); +OBJC_EXTERN void objc_msgSend_vtable8(void); +OBJC_EXTERN void objc_msgSend_vtable9(void); +OBJC_EXTERN void objc_msgSend_vtable10(void); +OBJC_EXTERN void objc_msgSend_vtable11(void); +OBJC_EXTERN void objc_msgSend_vtable12(void); +OBJC_EXTERN void objc_msgSend_vtable13(void); +OBJC_EXTERN void objc_msgSend_vtable14(void); +OBJC_EXTERN void objc_msgSend_vtable15(void); static IMP const defaultVtableTrampolines[] = { - objc_msgSend_vtable0, - objc_msgSend_vtable1, - objc_msgSend_vtable2, - objc_msgSend_vtable3, - objc_msgSend_vtable4, - objc_msgSend_vtable5, - objc_msgSend_vtable6, - objc_msgSend_vtable7, - objc_msgSend_vtable8, - objc_msgSend_vtable9, - objc_msgSend_vtable10, - objc_msgSend_vtable11, - objc_msgSend_vtable12, - objc_msgSend_vtable13, - objc_msgSend_vtable14, - objc_msgSend_vtable15, + (IMP)objc_msgSend_vtable0, + (IMP)objc_msgSend_vtable1, + (IMP)objc_msgSend_vtable2, + (IMP)objc_msgSend_vtable3, + (IMP)objc_msgSend_vtable4, + (IMP)objc_msgSend_vtable5, + (IMP)objc_msgSend_vtable6, + (IMP)objc_msgSend_vtable7, + (IMP)objc_msgSend_vtable8, + (IMP)objc_msgSend_vtable9, + (IMP)objc_msgSend_vtable10, + (IMP)objc_msgSend_vtable11, + (IMP)objc_msgSend_vtable12, + (IMP)objc_msgSend_vtable13, + (IMP)objc_msgSend_vtable14, + (IMP)objc_msgSend_vtable15, }; extern objc_trampoline_header defaultVtableTrampolineDescriptors; @@ -727,17 +730,17 @@ static void updateVtable(class_t *cls, BOOL force) if (!needVtable) { if (PrintVtables) { - _objc_inform("VTABLES: USING SUPERCLASS vtable for class %s%s", - getName(cls), isMetaClass(cls) ? "(meta)" : ""); + _objc_inform("VTABLES: USING SUPERCLASS vtable for class %s%s %p", + getName(cls), isMetaClass(cls) ? "(meta)" : "", cls); } cls->vtable = supercls->vtable; } else { if (PrintVtables) { - _objc_inform("VTABLES: %s vtable for class %s%s", + _objc_inform("VTABLES: %s vtable for class %s%s %p", (cls->data()->flags & RW_SPECIALIZED_VTABLE) ? "UPDATING SPECIALIZED" : "CREATING SPECIALIZED", - getName(cls), isMetaClass(cls) ? "(meta)" : ""); + getName(cls), isMetaClass(cls) ? "(meta)" : "", cls); } if (PrintVtables || PrintVtableImages) { printVtableOverrides(cls, supercls); @@ -750,6 +753,12 @@ static void updateVtable(class_t *cls, BOOL force) if (cls->data()->flags & RW_SPECIALIZED_VTABLE) { // update cls->vtable in place new_vtable = cls->vtable; + if (new_vtable == &_objc_empty_vtable) { + // oops - our vtable is not as specialized as we thought + // This is probably the broken memcpy of __NSCFConstantString. + // rdar://8770551 + new_vtable = (IMP*)malloc(vtableCount * sizeof(IMP)); + } assert(new_vtable != &_objc_empty_vtable); } else { // make new vtable @@ -811,10 +820,15 @@ typedef struct { #define FOREACH_METHOD_LIST(_mlist, _cls, code) \ do { \ const method_list_t *_mlist; \ - if (_cls->data()->methods) { \ - method_list_t **_mlistp; \ - for (_mlistp = _cls->data()->methods; *_mlistp; _mlistp++) { \ - _mlist = *_mlistp; \ + if (_cls->data()->method_lists) { \ + if (_cls->data()->flags & RW_METHOD_ARRAY) { \ + method_list_t **_mlistp; \ + for (_mlistp=_cls->data()->method_lists; *_mlistp; _mlistp++){\ + _mlist = *_mlistp; \ + code \ + } \ + } else { \ + _mlist = _cls->data()->method_list; \ code \ } \ } \ @@ -869,7 +883,7 @@ typedef struct { static uint32_t fixed_up_method_list = 3; -PRIVATE_EXTERN void +void disableSharedCacheOptimizations(void) { fixed_up_method_list = 1; @@ -914,9 +928,31 @@ static size_t method_list_size(const method_list_t *mlist) static method_t *method_list_nth(const method_list_t *mlist, uint32_t i) { + assert(i < mlist->count); return (method_t *)(i*method_list_entsize(mlist) + (char *)&mlist->first); } +static uint32_t method_list_count(const method_list_t *mlist) +{ + return mlist ? mlist->count : 0; +} + +static void method_list_swap(method_list_t *mlist, uint32_t i, uint32_t j) +{ + size_t entsize = method_list_entsize(mlist); + char temp[entsize]; + memcpy(temp, method_list_nth(mlist, i), entsize); + memcpy(method_list_nth(mlist, i), method_list_nth(mlist, j), entsize); + memcpy(method_list_nth(mlist, j), temp, entsize); +} + +static uint32_t method_list_index(const method_list_t *mlist,const method_t *m) +{ + uint32_t i = (uint32_t)(((uintptr_t)m - (uintptr_t)mlist) / method_list_entsize(mlist)); + assert(i < mlist->count); + return i; +} + static size_t ivar_list_size(const ivar_list_t *ilist) { @@ -1042,8 +1078,7 @@ static void addUnattachedCategoryForClass(category_t *cat, class_t *cls, BOOL catFromBundle = (catHeader->mhdr->filetype == MH_BUNDLE) ? YES: NO; - // DO NOT use cat->cls! - // cls may be cat->cls->isa, or cat->cls may have been remapped. + // DO NOT use cat->cls! cls may be cat->cls->isa instead NXMapTable *cats = unattachedCategories(); category_list *list; @@ -1069,8 +1104,7 @@ static void removeUnattachedCategoryForClass(category_t *cat, class_t *cls) { rwlock_assert_writing(&runtimeLock); - // DO NOT use cat->cls! - // cls may be cat->cls->isa, or cat->cls may have been remapped. + // DO NOT use cat->cls! cls may be cat->cls->isa instead NXMapTable *cats = unattachedCategories(); category_list *list; @@ -1129,6 +1163,18 @@ static BOOL isFuture(class_t *cls) #endif +/*********************************************************************** +* classNSObject +* Returns class NSObject. +* Locking: none +**********************************************************************/ +static class_t *classNSObject(void) +{ + extern class_t OBJC_CLASS_$_NSObject; + return &OBJC_CLASS_$_NSObject; +} + + /*********************************************************************** * printReplacements * Implementation of PrintReplacedMethods / OBJC_PRINT_REPLACED_METHODS. @@ -1197,7 +1243,7 @@ static BOOL isBundleClass(class_t *cls) static method_list_t * -fixupMethodList(method_list_t *mlist, BOOL bundleCopy) +fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort) { assert(!isMethodListFixedUp(mlist)); @@ -1222,8 +1268,10 @@ fixupMethodList(method_list_t *mlist, BOOL bundleCopy) sel_unlock(); // Sort by selector address. - method_t::SortBySELAddress sorter; - std::stable_sort(mlist->begin(), mlist->end(), sorter); + if (sort) { + method_t::SortBySELAddress sorter; + std::stable_sort(mlist->begin(), mlist->end(), sorter); + } // Mark method list as uniqued and sorted setMethodListFixedUp(mlist); @@ -1234,29 +1282,67 @@ fixupMethodList(method_list_t *mlist, BOOL bundleCopy) static void attachMethodLists(class_t *cls, method_list_t **addedLists, int addedCount, - BOOL methodsFromBundle, BOOL *inoutVtablesAffected) + BOOL baseMethods, BOOL methodsFromBundle, + BOOL *inoutVtablesAffected) { rwlock_assert_writing(&runtimeLock); // Don't scan redundantly - BOOL scanForCustomRR = !UseGC && !cls->hasCustomRR(); + bool scanForCustomRR = !UseGC && !cls->hasCustomRR(); + bool scanForCustomAWZ = !UseGC && !cls->hasCustomAWZ(); + + // RR special cases: + // NSObject's base instance methods are not custom RR. + // All other root classes are custom RR. + // updateCustomRR_AWZ also knows about these cases. + if (baseMethods && scanForCustomRR && cls->isRootClass()) { + if (cls != classNSObject()) { + cls->setHasCustomRR(); + } + scanForCustomRR = false; + } + + // AWZ special cases: + // NSObject's base class methods are not custom AWZ. + // All other root metaclasses are custom AWZ. + // updateCustomRR_AWZ also knows about these cases. + if (baseMethods && scanForCustomAWZ && cls->isRootMetaclass()) { + if (cls != classNSObject()->isa) { + cls->setHasCustomAWZ(); + } + scanForCustomAWZ = false; + } // Method list array is NULL-terminated. // Some elements of lists are NULL; we must filter them out. - method_list_t **oldLists = cls->data()->methods; + method_list_t *oldBuf[2]; + method_list_t **oldLists; int oldCount = 0; + if (cls->data()->flags & RW_METHOD_ARRAY) { + oldLists = cls->data()->method_lists; + } else { + oldBuf[0] = cls->data()->method_list; + oldBuf[1] = NULL; + oldLists = oldBuf; + } if (oldLists) { while (oldLists[oldCount]) oldCount++; } - int newCount = oldCount + 1; // including NULL terminator + int newCount = oldCount; for (int i = 0; i < addedCount; i++) { if (addedLists[i]) newCount++; // only non-NULL entries get added } - method_list_t **newLists = (method_list_t **) - _malloc_internal(newCount * sizeof(*newLists)); + method_list_t *newBuf[2]; + method_list_t **newLists; + if (newCount > 1) { + newLists = (method_list_t **) + _malloc_internal((1 + newCount) * sizeof(*newLists)); + } else { + newLists = newBuf; + } // Add method lists to array. // Reallocate un-fixed method lists. @@ -1270,7 +1356,7 @@ attachMethodLists(class_t *cls, method_list_t **addedLists, int addedCount, // Fixup selectors if necessary if (!isMethodListFixedUp(mlist)) { - mlist = fixupMethodList(mlist, methodsFromBundle); + mlist = fixupMethodList(mlist, methodsFromBundle, true/*sort*/); } // Scan for vtable updates @@ -1286,16 +1372,18 @@ attachMethodLists(class_t *cls, method_list_t **addedLists, int addedCount, } // Scan for method implementations tracked by the class's flags - if (scanForCustomRR) { - uint32_t m; - for (m = 0; m < mlist->count; m++) { - SEL sel = method_list_nth(mlist, m)->name; - if (isRRSelector(sel)) { - cls->setHasCustomRR(); - scanForCustomRR = NO; - break; - } - } + for (uint32_t m = 0; + (scanForCustomRR || scanForCustomAWZ) && m < mlist->count; + m++) + { + SEL sel = method_list_nth(mlist, m)->name; + if (scanForCustomRR && isRRSelector(sel)) { + cls->setHasCustomRR(); + scanForCustomRR = false; + } else if (scanForCustomAWZ && isAWZSelector(sel)) { + cls->setHasCustomAWZ(); + scanForCustomAWZ = false; + } } // Fill method list array @@ -1306,11 +1394,20 @@ attachMethodLists(class_t *cls, method_list_t **addedLists, int addedCount, for (i = 0; i < oldCount; i++) { newLists[newCount++] = oldLists[i]; } - if (oldLists) free(oldLists); + if (oldLists && oldLists != oldBuf) free(oldLists); // NULL-terminate - newLists[newCount++] = NULL; - cls->data()->methods = newLists; + newLists[newCount] = NULL; + + if (newCount > 1) { + assert(newLists != newBuf); + cls->data()->method_lists = newLists; + changeInfo(cls, RW_METHOD_ARRAY, 0); + } else { + assert(newLists == newBuf); + cls->data()->method_list = newLists[0]; + assert(!(cls->data()->flags & RW_METHOD_ARRAY)); + } } static void @@ -1336,7 +1433,7 @@ attachCategoryMethods(class_t *cls, category_list *cats, } } - attachMethodLists(cls, mlists, mcount, fromBundle, inoutVtablesAffected); + attachMethodLists(cls, mlists, mcount, NO, fromBundle, inoutVtablesAffected); _free_internal(mlists); @@ -1346,7 +1443,6 @@ attachCategoryMethods(class_t *cls, category_list *cats, static chained_property_list * buildPropertyList(const property_list_t *plist, category_list *cats, BOOL isMeta) { - // Do NOT use cat->cls! It may have been remapped. chained_property_list *newlist; uint32_t count = 0; uint32_t p, c; @@ -1412,7 +1508,6 @@ static const protocol_list_t ** buildProtocolList(category_list *cats, const protocol_list_t *base, const protocol_list_t **protos) { - // Do NOT use cat->cls! It may have been remapped. const protocol_list_t **p, **newp; const protocol_list_t **newprotos; unsigned int count = 0; @@ -1486,28 +1581,16 @@ static void methodizeClass(class_t *cls) // Build method and protocol and property lists. // Include methods and protocols and properties from categories, if any - // Do NOT use cat->cls! It may have been remapped. attachMethodLists(cls, (method_list_t **)&cls->data()->ro->baseMethods, 1, - isBundleClass(cls), NULL); + YES, isBundleClass(cls), NULL); // Root classes get bonus method implementations if they don't have // them already. These apply before category replacements. - if (cls->isRootClass()) { - // root class - if (!UseGC) { - // Assume custom RR except NSObject, even without MM method imps. - if (0 != strcmp(getName(cls), "NSObject")) cls->setHasCustomRR(); - } - } - else if (cls->isRootMetaclass()) { + if (cls->isRootMetaclass()) { // root metaclass addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO); - if (!UseGC) { - // Assume custom RR always. - cls->setHasCustomRR(); - } } cats = unattachedCategoriesForClass(cls); @@ -1627,21 +1710,28 @@ static void changeInfo(class_t *cls, unsigned int set, unsigned int clear) /*********************************************************************** -* namedClasses -* Returns the classname => class map of all non-meta classes. -* Locking: runtimeLock must be read- or write-locked by the caller +* getClass +* Looks up a class by name. The class MIGHT NOT be realized. +* Locking: runtimeLock must be read- or write-locked by the caller. **********************************************************************/ +// This is a misnomer: gdb_objc_realized_classes is actually a list of +// named classes not in the dyld shared cache, whether realized or not. NXMapTable *gdb_objc_realized_classes; // exported for debuggers in objc-gdb.h -static NXMapTable *namedClasses(void) +static class_t *getClass(const char *name) { rwlock_assert_locked(&runtimeLock); // allocated in _read_images assert(gdb_objc_realized_classes); - return gdb_objc_realized_classes; + // Try runtime-allocated table + class_t *result = (class_t *)NXMapGet(gdb_objc_realized_classes, name); + if (result) return result; + + // Try table from dyld shared cache + return getPreoptimizedClass(name); } @@ -1655,10 +1745,10 @@ static void addNamedClass(class_t *cls, const char *name) { rwlock_assert_writing(&runtimeLock); class_t *old; - if ((old = (class_t *)NXMapGet(namedClasses(), name))) { + if ((old = getClass(name))) { inform_duplicate(name, (Class)old, (Class)cls); } else { - NXMapInsert(namedClasses(), name, cls); + NXMapInsert(gdb_objc_realized_classes, name, cls); } assert(!(cls->data()->flags & RO_META)); @@ -1676,8 +1766,8 @@ static void removeNamedClass(class_t *cls, const char *name) { rwlock_assert_writing(&runtimeLock); assert(!(cls->data()->flags & RO_META)); - if (cls == NXMapGet(namedClasses(), name)) { - NXMapRemove(namedClasses(), name); + if (cls == NXMapGet(gdb_objc_realized_classes, name)) { + NXMapRemove(gdb_objc_realized_classes, name); } else { // cls has a name collision with another class - don't remove the other } @@ -1782,108 +1872,33 @@ static void removeRealizedMetaclass(class_t *cls) /*********************************************************************** -* uninitializedClasses -* Returns the metaclass => class map for un-+initialized classes -* Replaces the 32-bit cls = objc_getName(metacls) during +initialize. -* Locking: runtimeLock must be read- or write-locked by the caller -**********************************************************************/ -static NXMapTable *uninitialized_class_map = NULL; -static NXMapTable *uninitializedClasses(void) -{ - rwlock_assert_locked(&runtimeLock); - - // allocated in _read_images - assert(uninitialized_class_map); - - return uninitialized_class_map; -} - - -/*********************************************************************** -* addUninitializedClass -* Adds metacls => cls to the un-+initialized class map -* Locking: runtimeLock must be held by the caller -**********************************************************************/ -static void addUninitializedClass(class_t *cls, class_t *metacls) -{ - rwlock_assert_writing(&runtimeLock); - void *old; - old = NXMapInsert(uninitializedClasses(), metacls, cls); - assert(isRealized(metacls) ? isMetaClass(metacls) : metacls->data()->flags & RO_META); - assert(! (isRealized(cls) ? isMetaClass(cls) : cls->data()->flags & RO_META)); - assert(!old); -} - - -static void removeUninitializedClass(class_t *cls) -{ - rwlock_assert_writing(&runtimeLock); - NXMapRemove(uninitializedClasses(), cls->isa); -} - - -/*********************************************************************** -* getNonMetaClass -* Return the ordinary class for this class or metaclass. -* Used by +initialize. -* Locking: runtimeLock must be read- or write-locked by the caller -**********************************************************************/ -static class_t *getNonMetaClass(class_t *cls) -{ - rwlock_assert_locked(&runtimeLock); - if (isMetaClass(cls)) { - cls = (class_t *)NXMapGet(uninitializedClasses(), cls); - } - return cls; -} - - -/*********************************************************************** -* _class_getNonMetaClass -* Return the ordinary class for this class or metaclass. -* Used by +initialize. -* Locking: acquires runtimeLock -**********************************************************************/ -PRIVATE_EXTERN Class _class_getNonMetaClass(Class cls_gen) -{ - class_t *cls = newcls(cls_gen); - rwlock_write(&runtimeLock); - cls = getNonMetaClass(cls); - realizeClass(cls); - rwlock_unlock_write(&runtimeLock); - - return (Class)cls; -} - - - -/*********************************************************************** -* futureClasses +* futureNamedClasses * Returns the classname => future class map for unrealized future classes. * Locking: runtimeLock must be held by the caller **********************************************************************/ -static NXMapTable *futureClasses(void) +static NXMapTable *futureNamedClasses(void) { rwlock_assert_writing(&runtimeLock); - static NXMapTable *future_class_map = NULL; + static NXMapTable *future_named_class_map = NULL; - if (future_class_map) return future_class_map; + if (future_named_class_map) return future_named_class_map; - // future_class_map is big enough to hold CF's classes and a few others - future_class_map = NXCreateMapTableFromZone(NXStrValueMapPrototype, 32, - _objc_internal_zone()); + // future_named_class_map is big enough for CF's classes and a few others + future_named_class_map = + NXCreateMapTableFromZone(NXStrValueMapPrototype, 32, + _objc_internal_zone()); - return future_class_map; + return future_named_class_map; } /*********************************************************************** -* addFutureClass +* addFutureNamedClass * Installs cls as the class structure to use for the named class if it appears. * Locking: runtimeLock must be held by the caller **********************************************************************/ -static void addFutureClass(const char *name, class_t *cls) +static void addFutureNamedClass(const char *name, class_t *cls) { void *old; @@ -1896,22 +1911,22 @@ static void addFutureClass(const char *name, class_t *cls) cls->setData((class_rw_t *)_calloc_internal(sizeof(*cls->data()), 1)); cls->data()->flags = RO_FUTURE; - old = NXMapKeyCopyingInsert(futureClasses(), name, cls); + old = NXMapKeyCopyingInsert(futureNamedClasses(), name, cls); assert(!old); } /*********************************************************************** -* removeFutureClass +* removeFutureNamedClass * Removes the named class from the unrealized future class list, * because it has been realized. * Locking: runtimeLock must be held by the caller **********************************************************************/ -static void removeFutureClass(const char *name) +static void removeFutureNamedClass(const char *name) { rwlock_assert_writing(&runtimeLock); - NXMapKeyFreeingRemove(futureClasses(), name); + NXMapKeyFreeingRemove(futureNamedClasses(), name); } @@ -1966,7 +1981,7 @@ static void addRemappedClass(class_t *oldcls, class_t *newcls) if (PrintFuture) { _objc_inform("FUTURE: using %p instead of %p for %s", - oldcls, newcls, getName(newcls)); + oldcls, newcls, getName(oldcls)); } void *old; @@ -1997,6 +2012,18 @@ static class_t *remapClass(class_t *cls) } } +static class_t *remapClass(classref_t cls) +{ + return remapClass((class_t *)cls); +} + +Class _class_remap(Class cls_gen) +{ + rwlock_read(&runtimeLock); + Class result = (Class)remapClass(newcls(cls_gen)); + rwlock_unlock_read(&runtimeLock); + return result; +} /*********************************************************************** * remapClassRef @@ -2013,6 +2040,171 @@ static void remapClassRef(class_t **clsref) } +/*********************************************************************** +* nonMetaClasses +* Returns the memoized metaclass => class map +* Used for some cases of +initialize. +* This map does not contain all classes and metaclasses. It only +* contains memoized results from the slow path in getNonMetaClass(), +* and classes that the slow path can't find (like objc_registerClassPair). +* Locking: runtimeLock must be read- or write-locked by the caller +**********************************************************************/ +static NXMapTable *nonmeta_class_map = NULL; +static NXMapTable *nonMetaClasses(void) +{ + rwlock_assert_locked(&runtimeLock); + + if (nonmeta_class_map) return nonmeta_class_map; + + // nonmeta_class_map is typically small + INIT_ONCE_PTR(nonmeta_class_map, + NXCreateMapTableFromZone(NXPtrValueMapPrototype, 32, + _objc_internal_zone()), + NXFreeMapTable(v)); + + return nonmeta_class_map; +} + + +/*********************************************************************** +* addNonMetaClass +* Adds metacls => cls to the memoized metaclass map +* Locking: runtimeLock must be held by the caller +**********************************************************************/ +static void addNonMetaClass(class_t *cls) +{ + rwlock_assert_writing(&runtimeLock); + void *old; + old = NXMapInsert(nonMetaClasses(), cls->isa, cls); + + assert(isRealized(cls)); + assert(isRealized(cls->isa)); + assert(!isMetaClass(cls)); + assert(isMetaClass(cls->isa)); + assert(!old); +} + + +static void removeNonMetaClass(class_t *cls) +{ + rwlock_assert_writing(&runtimeLock); + NXMapRemove(nonMetaClasses(), cls->isa); +} + + +/*********************************************************************** +* getNonMetaClass +* 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. +* Used by +initialize. +* Locking: runtimeLock must be read- or write-locked by the caller +**********************************************************************/ +static class_t *getNonMetaClass(class_t *metacls, id inst) +{ + static int total, slow, memo; + rwlock_assert_locked(&runtimeLock); + + realizeClass(metacls); + + total++; + + // return cls itself if it's already a non-meta class + if (!isMetaClass(metacls)) return metacls; + + // metacls really is a metaclass + + // special case for root metaclass + // where inst == inst->isa == metacls is possible + if (metacls->isa == metacls) { + class_t *cls = metacls->superclass; + assert(isRealized(cls)); + assert(!isMetaClass(cls)); + assert(cls->isa == metacls); + if (cls->isa == metacls) return cls; + } + + // use inst if available + if (inst) { + class_t *cls = (class_t *)inst; + realizeClass(cls); + // cls may be a subclass - find the real class for metacls + while (cls && cls->isa != metacls) { + cls = cls->superclass; + realizeClass(cls); + } + if (cls) { + assert(!isMetaClass(cls)); + assert(cls->isa == metacls); + return cls; + } +#if !NDEBUG + _objc_fatal("cls is not an instance of metacls"); +#else + // release build: be forgiving and fall through to slow lookups +#endif + } + + // try memoized table + class_t *cls = (class_t *)NXMapGet(nonMetaClasses(), metacls); + if (cls) { + memo++; + if (PrintInitializing) { + _objc_inform("INITIALIZE: %d/%d (%g%%) memoized metaclass lookups", + memo, total, memo*100.0/total); + } + + assert(isRealized(cls)); + assert(!isMetaClass(cls)); + assert(cls->isa == metacls); + return cls; + } + + // try slow lookup + slow++; + if (PrintInitializing) { + _objc_inform("INITIALIZE: %d/%d (%g%%) slow metaclass lookups", + slow, total, slow*100.0/total); + } + + for (header_info *hi = FirstHeader; hi; hi = hi->next) { + size_t count; + classref_t *classlist = _getObjc2ClassList(hi, &count); + for (size_t i = 0; i < count; i++) { + cls = remapClass(classlist[i]); + if (cls && cls->isa == metacls) { + // memoize result + realizeClass(cls); + addNonMetaClass(cls); + return cls; + } + } + } + + _objc_fatal("no class for metaclass %p", metacls); + + return cls; +} + + +/*********************************************************************** +* _class_getNonMetaClass +* Return the ordinary class for this class or metaclass. +* Used by +initialize. +* Locking: acquires runtimeLock +**********************************************************************/ +Class _class_getNonMetaClass(Class cls_gen, id obj) +{ + class_t *cls = newcls(cls_gen); + rwlock_write(&runtimeLock); + cls = getNonMetaClass(cls, obj); + assert(isRealized(cls)); + rwlock_unlock_write(&runtimeLock); + + return (Class)cls; +} + + /*********************************************************************** * addSubclass * Adds subcls as a subclass of supercls. @@ -2033,7 +2225,11 @@ static void addSubclass(class_t *supercls, class_t *subcls) } if (supercls->hasCustomRR()) { - subcls->setHasCustomRR(); + subcls->setHasCustomRR(true); + } + + if (supercls->hasCustomAWZ()) { + subcls->setHasCustomAWZ(true); } } } @@ -2047,6 +2243,8 @@ static void addSubclass(class_t *supercls, class_t *subcls) static void removeSubclass(class_t *supercls, class_t *subcls) { rwlock_assert_writing(&runtimeLock); + assert(isRealized(supercls)); + assert(isRealized(subcls)); assert(getSuperclass(subcls) == supercls); class_t **cp; @@ -2439,12 +2637,13 @@ static class_t *realizeClass(class_t *cls) supercls = realizeClass(remapClass(cls->superclass)); metacls = realizeClass(remapClass(cls->isa)); - // Check for remapped superclass - // fixme doesn't handle remapped metaclass - assert(metacls == cls->isa); + // Check for remapped superclass and metaclass if (supercls != cls->superclass) { cls->superclass = supercls; } + if (metacls != cls->isa) { + cls->isa = metacls; + } /* debug: print them all if (ro->ivars) { @@ -2484,19 +2683,6 @@ static class_t *realizeClass(class_t *cls) } -/*********************************************************************** -* getClass -* Looks up a class by name. The class MIGHT NOT be realized. -* Locking: runtimeLock must be read- or write-locked by the caller. -**********************************************************************/ -static class_t *getClass(const char *name) -{ - rwlock_assert_locked(&runtimeLock); - - return (class_t *)NXMapGet(namedClasses(), name); -} - - /*********************************************************************** * missingWeakSuperclass * Return YES if some superclass of cls was weak-linked and is missing. @@ -2512,6 +2698,8 @@ missingWeakSuperclass(class_t *cls) } else { // superclass not NULL. Check if a higher superclass is missing. class_t *supercls = remapClass(cls->superclass); + assert(cls != cls->superclass); + assert(cls != supercls); if (!supercls) return YES; if (isRealized(supercls)) return NO; return missingWeakSuperclass(supercls); @@ -2529,7 +2717,7 @@ static void realizeAllClassesInImage(header_info *hi) rwlock_assert_writing(&runtimeLock); size_t count, i; - class_t **classlist; + classref_t *classlist; if (hi->allClassesRealized) return; @@ -2566,21 +2754,21 @@ static void realizeAllClasses(void) * Assumes the named class doesn't exist yet. * Locking: acquires runtimeLock **********************************************************************/ -PRIVATE_EXTERN Class _objc_allocateFutureClass(const char *name) +Class _objc_allocateFutureClass(const char *name) { rwlock_write(&runtimeLock); class_t *cls; - NXMapTable *future_class_map = futureClasses(); + NXMapTable *future_named_class_map = futureNamedClasses(); - if ((cls = (class_t *)NXMapGet(future_class_map, name))) { + if ((cls = (class_t *)NXMapGet(future_named_class_map, name))) { // Already have a future class for this name. rwlock_unlock_write(&runtimeLock); return (Class)cls; } cls = (class_t *)_calloc_class(sizeof(*cls)); - addFutureClass(name, cls); + addFutureNamedClass(name, cls); rwlock_unlock_write(&runtimeLock); return (Class)cls; @@ -2639,7 +2827,7 @@ static void flushCaches(class_t *cls) * and optionally its metaclass. * Locking: acquires runtimeLock **********************************************************************/ -PRIVATE_EXTERN void flush_caches(Class cls_gen, BOOL flush_meta) +void flush_caches(Class cls_gen, BOOL flush_meta) { class_t *cls = newcls(cls_gen); rwlock_write(&runtimeLock); @@ -2662,7 +2850,7 @@ PRIVATE_EXTERN void flush_caches(Class cls_gen, BOOL flush_meta) * * Locking: write-locks runtimeLock **********************************************************************/ -PRIVATE_EXTERN const char * +const char * map_images(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]) { @@ -2682,7 +2870,7 @@ map_images(enum dyld_image_states state, uint32_t infoCount, * * Locking: write-locks runtimeLock and loadMethodLock **********************************************************************/ -PRIVATE_EXTERN const char * +const char * load_images(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]) { @@ -2714,7 +2902,7 @@ load_images(enum dyld_image_states state, uint32_t infoCount, * * Locking: write-locks runtimeLock and loadMethodLock **********************************************************************/ -PRIVATE_EXTERN void +void unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide) { recursive_mutex_lock(&loadMethodLock); @@ -2737,7 +2925,7 @@ unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide) * * Locking: runtimeLock acquired by map_images **********************************************************************/ -PRIVATE_EXTERN void _read_images(header_info **hList, uint32_t hCount) +void _read_images(header_info **hList, uint32_t hCount) { header_info *hi; uint32_t hIndex; @@ -2745,6 +2933,10 @@ PRIVATE_EXTERN void _read_images(header_info **hList, uint32_t hCount) size_t i; class_t **resolvedFutureClasses = NULL; size_t resolvedFutureClassCount = 0; + static unsigned int totalMethodLists; + static unsigned int preoptimizedMethodLists; + static unsigned int totalClasses; + static unsigned int preoptimizedClasses; static BOOL doneOnce; rwlock_assert_writing(&runtimeLock); @@ -2759,27 +2951,26 @@ PRIVATE_EXTERN void _read_images(header_info **hList, uint32_t hCount) initVtables(); // Count classes. Size various table based on the total. - unsigned int total = 0; + size_t total = 0; + size_t unoptimizedTotal = 0; for (EACH_HEADER) { if (_getObjc2ClassList(hi, &count)) { - total += (unsigned int)count; + total += count; + if (!hi->inSharedCache) unoptimizedTotal += count; } } if (PrintConnecting) { - _objc_inform("CLASS: found %u classes during launch", total); + _objc_inform("CLASS: found %zu classes during launch", total); } // namedClasses (NOT realizedClasses) + // Preoptimized classes don't go in this table. // 4/3 is NXMapTable's load factor + size_t namedClassesSize = + (isPreoptimized() ? unoptimizedTotal : total) * 4 / 3; gdb_objc_realized_classes = - NXCreateMapTableFromZone(NXStrValueMapPrototype, total*4/3, - _objc_internal_zone()); - - // uninitializedClasses - // 4/3 is NXMapTable's load factor - uninitialized_class_map = - NXCreateMapTableFromZone(NXPtrValueMapPrototype, total*4/3, + NXCreateMapTableFromZone(NXStrValueMapPrototype, namedClassesSize, _objc_internal_zone()); // realizedClasses and realizedMetaclasses - less than the full total @@ -2793,57 +2984,99 @@ PRIVATE_EXTERN void _read_images(header_info **hList, uint32_t hCount) // Discover classes. Fix up unresolved future classes. Mark bundle classes. - NXMapTable *future_class_map = futureClasses(); + NXMapTable *future_named_class_map = futureNamedClasses(); + for (EACH_HEADER) { - class_t **classlist = _getObjc2ClassList(hi, &count); + bool headerIsBundle = (hi->mhdr->filetype == MH_BUNDLE); + bool headerInSharedCache = hi->inSharedCache; + + classref_t *classlist = _getObjc2ClassList(hi, &count); for (i = 0; i < count; i++) { - const char *name = getName(classlist[i]); + class_t *cls = (class_t *)classlist[i]; + const char *name = getName(cls); - if (missingWeakSuperclass(classlist[i])) { + if (missingWeakSuperclass(cls)) { // No superclass (probably weak-linked). // Disavow any knowledge of this subclass. if (PrintConnecting) { _objc_inform("CLASS: IGNORING class '%s' with " "missing weak-linked superclass", name); } - addRemappedClass(classlist[i], NULL); - classlist[i]->superclass = NULL; - classlist[i] = NULL; + addRemappedClass(cls, NULL); + cls->superclass = NULL; continue; } - if (NXCountMapTable(future_class_map) > 0) { - class_t *newCls = (class_t *)NXMapGet(future_class_map, name); - if (newCls) { - // Copy class_t to future class's struct. - // Preserve future's rw data block. - class_rw_t *rw = newCls->data(); - memcpy(newCls, classlist[i], sizeof(class_t)); - rw->ro = (class_ro_t *)newCls->data(); - newCls->setData(rw); - - removeFutureClass(name); - addRemappedClass(classlist[i], newCls); - classlist[i] = newCls; - // Non-lazily realize the class below. - resolvedFutureClasses = (class_t **) - _realloc_internal(resolvedFutureClasses, - (resolvedFutureClassCount+1) - * sizeof(class_t *)); - resolvedFutureClasses[resolvedFutureClassCount++] = newCls; - } + class_t *newCls = NULL; + if (NXCountMapTable(future_named_class_map) > 0) { + newCls = (class_t *)NXMapGet(future_named_class_map, name); + removeFutureNamedClass(name); + } + + if (newCls) { + // Copy class_t to future class's struct. + // Preserve future's rw data block. + class_rw_t *rw = newCls->data(); + memcpy(newCls, cls, sizeof(class_t)); + rw->ro = (class_ro_t *)newCls->data(); + newCls->setData(rw); + + addRemappedClass(cls, newCls); + cls = newCls; + + // Non-lazily realize the class below. + resolvedFutureClasses = (class_t **) + _realloc_internal(resolvedFutureClasses, + (resolvedFutureClassCount+1) + * sizeof(class_t *)); + resolvedFutureClasses[resolvedFutureClassCount++] = newCls; } - addNamedClass(classlist[i], name); - addUninitializedClass(classlist[i], classlist[i]->isa); - if (hi->mhdr->filetype == MH_BUNDLE) { - classlist[i]->data()->flags |= RO_FROM_BUNDLE; - classlist[i]->isa->data()->flags |= RO_FROM_BUNDLE; + + totalClasses++; + if (headerInSharedCache && isPreoptimized()) { + // class list built in shared cache + // fixme strict assert doesn't work because of duplicates + // assert(cls == getClass(name)); + assert(getClass(name)); + preoptimizedClasses++; + } else { + addNamedClass(cls, name); + } + + // for future reference: shared cache never contains MH_BUNDLEs + if (headerIsBundle) { + cls->data()->flags |= RO_FROM_BUNDLE; + cls->isa->data()->flags |= RO_FROM_BUNDLE; + } + + if (PrintPreopt) { + const method_list_t *mlist; + if ((mlist = ((class_ro_t *)cls->data())->baseMethods)) { + totalMethodLists++; + if (isMethodListFixedUp(mlist)) preoptimizedMethodLists++; + } + if ((mlist = ((class_ro_t *)cls->isa->data())->baseMethods)) { + totalMethodLists++; + if (isMethodListFixedUp(mlist)) preoptimizedMethodLists++; + } } } } + if (PrintPreopt && totalMethodLists) { + _objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) method lists pre-sorted", + preoptimizedMethodLists, totalMethodLists, + 100.0*preoptimizedMethodLists/totalMethodLists); + } + if (PrintPreopt && totalClasses) { + _objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) classes pre-registered", + preoptimizedClasses, totalClasses, + 100.0*preoptimizedClasses/totalClasses); + } + // Fix up remapped classes - // classlist is up to date, but classrefs may not be + // Class list and nonlazy class list remain unremapped. + // Class refs and super refs are remapped for message dispatching. if (!noClassesRemapped()) { for (EACH_HEADER) { @@ -2866,11 +3099,11 @@ PRIVATE_EXTERN void _read_images(header_info **hList, uint32_t hCount) if (PrintPreopt) { if (sel_preoptimizationValid(hi)) { _objc_inform("PREOPTIMIZATION: honoring preoptimized selectors in %s", - _nameForHeader(hi->mhdr)); + hi->fname); } else if (_objcHeaderOptimizedByDyld(hi)) { _objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors in %s", - _nameForHeader(hi->mhdr)); + hi->fname); } } @@ -2919,7 +3152,7 @@ PRIVATE_EXTERN void _read_images(header_info **hList, uint32_t hCount) // Realize non-lazy classes (for +load methods and static instances) for (EACH_HEADER) { - class_t **classlist = + classref_t *classlist = _getObjc2NonlazyClassList(hi, &count); for (i = 0; i < count; i++) { realizeClass(remapClass(classlist[i])); @@ -2940,7 +3173,6 @@ PRIVATE_EXTERN void _read_images(header_info **hList, uint32_t hCount) _getObjc2CategoryList(hi, &count); for (i = 0; i < count; i++) { category_t *cat = catlist[i]; - // Do NOT use cat->cls! It may have been remapped. class_t *cls = remapClass(cat->cls); if (!cls) { @@ -3025,13 +3257,13 @@ static void schedule_class_load(class_t *cls) changeInfo(cls, RW_LOADED, 0); } -PRIVATE_EXTERN void prepare_load_methods(header_info *hi) +void prepare_load_methods(header_info *hi) { size_t count, i; rwlock_assert_writing(&runtimeLock); - class_t **classlist = + classref_t *classlist = _getObjc2NonlazyClassList(hi, &count); for (i = 0; i < count; i++) { schedule_class_load(remapClass(classlist[i])); @@ -3040,7 +3272,6 @@ PRIVATE_EXTERN void prepare_load_methods(header_info *hi) category_t **categorylist = _getObjc2NonlazyCategoryList(hi, &count); for (i = 0; i < count; i++) { category_t *cat = categorylist[i]; - // Do NOT use cat->cls! It may have been remapped. class_t *cls = remapClass(cat->cls); if (!cls) continue; // category for ignored weak-linked class realizeClass(cls); @@ -3055,7 +3286,7 @@ PRIVATE_EXTERN void prepare_load_methods(header_info *hi) * Only handles MH_BUNDLE for now. * Locking: write-lock and loadMethodLock acquired by unmap_image **********************************************************************/ -PRIVATE_EXTERN void _unload_image(header_info *hi) +void _unload_image(header_info *hi) { size_t count, i; @@ -3082,15 +3313,25 @@ PRIVATE_EXTERN void _unload_image(header_info *hi) // Unload classes. - class_t **classlist = _getObjc2ClassList(hi, &count); + classref_t *classlist = _getObjc2ClassList(hi, &count); + + // First detach classes from each other. Then free each class. + // This avoid bugs where this loop unloads a subclass before its superclass + for (i = 0; i < count; i++) { - class_t *cls = classlist[i]; - // fixme remapped classes? - // fixme ignored weak-linked classes + class_t *cls = remapClass(classlist[i]); if (cls) { remove_class_from_loadable_list((Class)cls); - unload_class(cls->isa, YES); - unload_class(cls, NO); + detach_class(cls->isa, YES); + detach_class(cls, NO); + } + } + + for (i = 0; i < count; i++) { + class_t *cls = remapClass(classlist[i]); + if (cls) { + free_class(cls->isa); + free_class(cls); } } @@ -3187,15 +3428,16 @@ _method_setImplementation(class_t *cls, method_t *m, IMP imp) m->imp = imp; // No cache flushing needed - cache contains Methods not IMPs. + + // vtable and RR/AWZ updates are slow if cls is NULL (i.e. unknown) + // fixme build list of classes whose Methods are known externally? - if (vtable_containsSelector(newmethod(m)->name)) { - // Will be slow if cls is NULL (i.e. unknown) - // fixme build list of classes whose Methods are known externally? + if (vtable_containsSelector(m->name)) { flushVtables(cls); } - // fixme catch NSObject changing to custom RR - // cls->setCustomRR(); + // Catch changes to retain/release and allocWithZone implementations + updateCustomRR_AWZ(cls, m); // fixme update monomorphism if necessary @@ -3235,6 +3477,9 @@ void method_exchangeImplementations(Method m1_gen, Method m2_gen) m1->imp = m2->imp; m2->imp = m1_imp; + // vtable and RR/AWZ updates are slow because class is unknown + // fixme build list of classes whose Methods are known externally? + if (vtable_containsSelector(m1->name) || vtable_containsSelector(m2->name)) { @@ -3243,8 +3488,8 @@ void method_exchangeImplementations(Method m1_gen, Method m2_gen) flushVtables(NULL); } - // fixme catch NSObject changing to custom RR - // cls->setCustomRR(); + updateCustomRR_AWZ(nil, m1); + updateCustomRR_AWZ(nil, m2); // fixme update monomorphism if necessary @@ -3329,17 +3574,68 @@ char * property_copyAttributeValue(objc_property_t prop, const char *name) } +/*********************************************************************** +* getExtendedTypesIndexesForMethod +* Returns: +* a is the count of methods in all method lists before m's method list +* b is the index of m in m's method list +* a+b is the index of m's extended types in the extended types array +**********************************************************************/ +static void getExtendedTypesIndexesForMethod(protocol_t *proto, const method_t *m, BOOL isRequiredMethod, BOOL isInstanceMethod, uint32_t& a, uint32_t &b) +{ + a = 0; + + if (isRequiredMethod && isInstanceMethod) { + b = method_list_index(proto->instanceMethods, m); + return; + } + a += method_list_count(proto->instanceMethods); + + if (isRequiredMethod && !isInstanceMethod) { + b = method_list_index(proto->classMethods, m); + return; + } + a += method_list_count(proto->classMethods); + + if (!isRequiredMethod && isInstanceMethod) { + b = method_list_index(proto->optionalInstanceMethods, m); + return; + } + a += method_list_count(proto->optionalInstanceMethods); + + if (!isRequiredMethod && !isInstanceMethod) { + b = method_list_index(proto->optionalClassMethods, m); + return; + } + a += method_list_count(proto->optionalClassMethods); +} + + +/*********************************************************************** +* getExtendedTypesIndexForMethod +* Returns the index of m's extended types in proto's extended types array. +**********************************************************************/ +static uint32_t getExtendedTypesIndexForMethod(protocol_t *proto, const method_t *m, BOOL isRequiredMethod, BOOL isInstanceMethod) +{ + uint32_t a; + uint32_t b; + getExtendedTypesIndexesForMethod(proto, m, isRequiredMethod, + isInstanceMethod, a, b); + return a + b; +} + + /*********************************************************************** * _protocol_getMethod_nolock * Locking: runtimeLock must be write-locked by the caller **********************************************************************/ -static Method +static method_t * _protocol_getMethod_nolock(protocol_t *proto, SEL sel, - BOOL isRequiredMethod, BOOL isInstanceMethod) + BOOL isRequiredMethod, BOOL isInstanceMethod, + BOOL recursive) { rwlock_assert_writing(&runtimeLock); - uint32_t i; if (!proto || !sel) return NULL; method_list_t **mlistp = NULL; @@ -3361,21 +3657,44 @@ _protocol_getMethod_nolock(protocol_t *proto, SEL sel, if (*mlistp) { method_list_t *mlist = *mlistp; if (!isMethodListFixedUp(mlist)) { - mlist = fixupMethodList(mlist, YES/*always copy for simplicity*/); + bool hasExtendedMethodTypes = proto->hasExtendedMethodTypes(); + mlist = fixupMethodList(mlist, true/*always copy for simplicity*/, + !hasExtendedMethodTypes/*sort if no ext*/); *mlistp = mlist; + + if (hasExtendedMethodTypes) { + // Sort method list and extended method types together. + // fixupMethodList() can't do this. + // fixme COW stomp + uint32_t count = method_list_count(mlist); + uint32_t prefix; + uint32_t unused; + getExtendedTypesIndexesForMethod(proto, method_list_nth(mlist, 0), isRequiredMethod, isInstanceMethod, prefix, unused); + const char **types = proto->extendedMethodTypes; + for (uint32_t i = 0; i < count; i++) { + for (uint32_t j = i+1; j < count; j++) { + method_t *mi = method_list_nth(mlist, i); + method_t *mj = method_list_nth(mlist, j); + if (mi->name > mj->name) { + method_list_swap(mlist, i, j); + std::swap(types[prefix+i], types[prefix+j]); + } + } + } + } } - for (i = 0; i < mlist->count; i++) { - method_t *m = method_list_nth(mlist, i); - if (sel == m->name) return (Method)m; - } + + method_t *m = search_method_list(mlist, sel); + if (m) return m; } - if (proto->protocols) { - Method m; - for (i = 0; i < proto->protocols->count; i++) { + if (recursive && proto->protocols) { + method_t *m; + for (uint32_t i = 0; i < proto->protocols->count; i++) { protocol_t *realProto = remapProtocol(proto->protocols->list[i]); m = _protocol_getMethod_nolock(realProto, sel, - isRequiredMethod, isInstanceMethod); + isRequiredMethod, isInstanceMethod, + true); if (m) return m; } } @@ -3389,17 +3708,75 @@ _protocol_getMethod_nolock(protocol_t *proto, SEL sel, * fixme * Locking: write-locks runtimeLock **********************************************************************/ -PRIVATE_EXTERN Method -_protocol_getMethod(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod) +Method +_protocol_getMethod(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod, BOOL recursive) { rwlock_write(&runtimeLock); - Method result = _protocol_getMethod_nolock(newprotocol(p), sel, - isRequiredMethod, - isInstanceMethod); + method_t *result = _protocol_getMethod_nolock(newprotocol(p), sel, + isRequiredMethod, + isInstanceMethod, + recursive); rwlock_unlock_write(&runtimeLock); - return result; + return (Method)result; +} + + +/*********************************************************************** +* _protocol_getMethodTypeEncoding_nolock +* Return the @encode string for the requested protocol method. +* Returns NULL if the compiler did not emit any extended @encode data. +* Locking: runtimeLock must be held for writing by the caller +**********************************************************************/ +const char * +_protocol_getMethodTypeEncoding_nolock(protocol_t *proto, SEL sel, + BOOL isRequiredMethod, + BOOL isInstanceMethod) +{ + rwlock_assert_writing(&runtimeLock); + + if (!proto) return NULL; + if (!proto->hasExtendedMethodTypes()) return NULL; + + method_t *m = + _protocol_getMethod_nolock(proto, sel, + isRequiredMethod, isInstanceMethod, false); + if (m) { + uint32_t i = getExtendedTypesIndexForMethod(proto, m, + isRequiredMethod, + isInstanceMethod); + return proto->extendedMethodTypes[i]; + } + + // No method with that name. Search incorporated protocols. + if (proto->protocols) { + for (uintptr_t i = 0; i < proto->protocols->count; i++) { + const char *enc = + _protocol_getMethodTypeEncoding_nolock(remapProtocol(proto->protocols->list[i]), sel, isRequiredMethod, isInstanceMethod); + if (enc) return enc; + } + } + + return NULL; } +/*********************************************************************** +* _protocol_getMethodTypeEncoding +* Return the @encode string for the requested protocol method. +* Returns NULL if the compiler did not emit any extended @encode data. +* Locking: runtimeLock must not be held by the caller +**********************************************************************/ +const char * +_protocol_getMethodTypeEncoding(Protocol *proto_gen, SEL sel, + BOOL isRequiredMethod, BOOL isInstanceMethod) +{ + const char *enc; + rwlock_write(&runtimeLock); + enc = _protocol_getMethodTypeEncoding_nolock(newprotocol(proto_gen), sel, + isRequiredMethod, + isInstanceMethod); + rwlock_unlock_write(&runtimeLock); + return enc; +} /*********************************************************************** * protocol_getName @@ -3423,7 +3800,7 @@ protocol_getMethodDescription(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod) { Method m = - _protocol_getMethod(p, aSel, isRequiredMethod, isInstanceMethod); + _protocol_getMethod(p, aSel, isRequiredMethod, isInstanceMethod, true); if (m) return *method_getDescription(m); else return (struct objc_method_description){NULL, NULL}; } @@ -4222,7 +4599,7 @@ class_copyPropertyList(Class cls_gen, unsigned int *outCount) * Called only from add_class_to_loadable_list. * Locking: runtimeLock must be read- or write-locked by the caller. **********************************************************************/ -PRIVATE_EXTERN IMP +IMP _class_getLoadMethod(Class cls_gen) { rwlock_assert_locked(&runtimeLock); @@ -4253,7 +4630,7 @@ _class_getLoadMethod(Class cls_gen) * Returns a category's name. * Locking: none **********************************************************************/ -PRIVATE_EXTERN const char * +const char * _category_getName(Category cat) { return newcategory(cat)->name; @@ -4267,11 +4644,10 @@ _category_getName(Category cat) * remove_category_from_loadable_list. * Locking: runtimeLock must be read- or write-locked by the caller **********************************************************************/ -PRIVATE_EXTERN const char * +const char * _category_getClassName(Category cat) { rwlock_assert_locked(&runtimeLock); - // cat->cls may have been remapped return getName(remapClass(newcategory(cat)->cls)); } @@ -4282,11 +4658,10 @@ _category_getClassName(Category cat) * Called only by call_category_loads. * Locking: read-locks runtimeLock **********************************************************************/ -PRIVATE_EXTERN Class +Class _category_getClass(Category cat) { rwlock_read(&runtimeLock); - // cat->cls may have been remapped class_t *result = remapClass(newcategory(cat)->cls); assert(isRealized(result)); // ok for call_category_loads' usage rwlock_unlock_read(&runtimeLock); @@ -4300,7 +4675,7 @@ _category_getClass(Category cat) * Called only from add_category_to_loadable_list * Locking: runtimeLock must be read- or write-locked by the caller **********************************************************************/ -PRIVATE_EXTERN IMP +IMP _category_getLoadMethod(Category cat) { rwlock_assert_locked(&runtimeLock); @@ -4371,11 +4746,11 @@ class_copyProtocolList(Class cls_gen, unsigned int *outCount) * fixme * Locking: read-locks runtimeLock **********************************************************************/ -PRIVATE_EXTERN const char ** +const char ** _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount) { size_t count, i, shift; - class_t **classlist; + classref_t *classlist; const char **names; rwlock_read(&runtimeLock); @@ -4387,7 +4762,7 @@ _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount) for (i = 0; i < count; i++) { class_t *cls = remapClass(classlist[i]); if (cls) { - names[i-shift] = getName(classlist[i]); + names[i-shift] = getName(cls); } else { shift++; // ignored weak-linked class } @@ -4407,7 +4782,7 @@ _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount) * fixme * Locking: none **********************************************************************/ -PRIVATE_EXTERN Cache +Cache _class_getCache(Class cls) { return newcls(cls)->cache; @@ -4420,7 +4795,7 @@ _class_getCache(Class cls) * obj + class_getInstanceSize(obj->isa) == object_getIndexedIvars(obj) * Locking: none **********************************************************************/ -PRIVATE_EXTERN size_t +size_t _class_getInstanceSize(Class cls) { if (!cls) return 0; @@ -4459,7 +4834,6 @@ alignedInstanceStart(class_t *cls) return (uint32_t)((cls->data()->ro->instanceStart + WORD_MASK) & ~WORD_MASK); } -PRIVATE_EXTERN uint32_t _class_getInstanceStart(Class cls_gen) { class_t *cls = newcls(cls_gen); return alignedInstanceStart(cls); @@ -4485,7 +4859,7 @@ class_getVersion(Class cls) * fixme * Locking: none **********************************************************************/ -PRIVATE_EXTERN void +void _class_setCache(Class cls, Cache cache) { newcls(cls)->cache = cache; @@ -4511,7 +4885,7 @@ class_setVersion(Class cls, int version) * fixme * Locking: acquires runtimeLock **********************************************************************/ -PRIVATE_EXTERN const char *_class_getName(Class cls) +const char *_class_getName(Class cls) { if (!cls) return "nil"; // fixme hack rwlock_write(&runtimeLock); @@ -4631,7 +5005,7 @@ getMethodNoSuper_nolock(class_t *cls, SEL sel) * fixme * Locking: read-locks runtimeLock **********************************************************************/ -PRIVATE_EXTERN Method +Method _class_getMethodNoSuper(Class cls, SEL sel) { rwlock_read(&runtimeLock); @@ -4645,7 +5019,7 @@ _class_getMethodNoSuper(Class cls, SEL sel) * For use inside lockForMethodLookup() only. * Locking: read-locks runtimeLock **********************************************************************/ -PRIVATE_EXTERN Method +Method _class_getMethodNoSuper_nolock(Class cls, SEL sel) { return (Method)getMethodNoSuper_nolock(newcls(cls), sel); @@ -4682,7 +5056,7 @@ getMethod_nolock(class_t *cls, SEL sel) * fixme * Locking: read-locks runtimeLock **********************************************************************/ -PRIVATE_EXTERN Method _class_getMethod(Class cls, SEL sel) +Method _class_getMethod(Class cls, SEL sel) { Method m; rwlock_read(&runtimeLock); @@ -4696,16 +5070,16 @@ PRIVATE_EXTERN Method _class_getMethod(Class cls, SEL sel) * ABI-specific lookUpMethod helpers. * Locking: read- and write-locks runtimeLock. **********************************************************************/ -PRIVATE_EXTERN void lockForMethodLookup(void) +void lockForMethodLookup(void) { rwlock_read(&runtimeLock); } -PRIVATE_EXTERN void unlockForMethodLookup(void) +void unlockForMethodLookup(void) { rwlock_unlock_read(&runtimeLock); } -PRIVATE_EXTERN IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init) +IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init, id obj) { rwlock_assert_unlocked(&runtimeLock); @@ -4716,7 +5090,7 @@ PRIVATE_EXTERN IMP prepareForMethodLookup(Class cls, SEL sel, BOOL init) } if (init && !_class_isInitialized(cls)) { - _class_initialize (cls); + _class_initialize (_class_getNonMetaClass(cls, obj)); // 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 @@ -4766,7 +5140,7 @@ objc_property_t class_getProperty(Class cls_gen, const char *name) /*********************************************************************** * Locking: fixme **********************************************************************/ -PRIVATE_EXTERN BOOL _class_isMetaClass(Class cls) +BOOL _class_isMetaClass(Class cls) { if (!cls) return NO; return isMetaClass(newcls(cls)); @@ -4780,12 +5154,15 @@ isMetaClass(class_t *cls) return (cls->data()->ro->flags & RO_META) ? YES : NO; } +class_t *getMeta(class_t *cls) +{ + if (isMetaClass(cls)) return cls; + else return cls->isa; +} -PRIVATE_EXTERN Class _class_getMeta(Class cls) +Class _class_getMeta(Class cls) { - assert(cls); - if (isMetaClass(newcls(cls))) return cls; - else return ((id)cls)->isa; + return (Class)getMeta(newcls(cls)); } Class gdb_class_getClass(Class cls) @@ -4820,7 +5197,7 @@ BOOL gdb_objc_isRuntimeLocked() /*********************************************************************** * Locking: fixme **********************************************************************/ -PRIVATE_EXTERN BOOL +BOOL _class_isInitializing(Class cls_gen) { class_t *cls = newcls(_class_getMeta(cls_gen)); @@ -4831,7 +5208,7 @@ _class_isInitializing(Class cls_gen) /*********************************************************************** * Locking: fixme **********************************************************************/ -PRIVATE_EXTERN BOOL +BOOL _class_isInitialized(Class cls_gen) { class_t *cls = newcls(_class_getMeta(cls_gen)); @@ -4842,9 +5219,10 @@ _class_isInitialized(Class cls_gen) /*********************************************************************** * Locking: fixme **********************************************************************/ -PRIVATE_EXTERN void +void _class_setInitializing(Class cls_gen) { + assert(!_class_isMetaClass(cls_gen)); class_t *cls = newcls(_class_getMeta(cls_gen)); changeInfo(cls, RW_INITIALIZING, 0); } @@ -4853,16 +5231,18 @@ _class_setInitializing(Class cls_gen) /*********************************************************************** * Locking: write-locks runtimeLock **********************************************************************/ -PRIVATE_EXTERN void +void _class_setInitialized(Class cls_gen) { - class_t *metacls; class_t *cls; rwlock_write(&runtimeLock); - metacls = newcls(_class_getMeta(cls_gen)); - cls = getNonMetaClass(metacls); + + assert(!_class_isMetaClass(cls_gen)); + + cls = newcls(cls_gen); + metacls = getMeta(cls); // Update vtables (initially postponed pending +initialize completion) // Do cls first because root metacls is a subclass of root cls @@ -4878,7 +5258,7 @@ _class_setInitialized(Class cls_gen) /*********************************************************************** * Locking: fixme **********************************************************************/ -PRIVATE_EXTERN BOOL +BOOL _class_shouldGrowCache(Class cls) { return YES; // fixme good or bad for memory use? @@ -4888,7 +5268,7 @@ _class_shouldGrowCache(Class cls) /*********************************************************************** * Locking: fixme **********************************************************************/ -PRIVATE_EXTERN void +void _class_setGrowCache(Class cls, BOOL grow) { // fixme good or bad for memory use? @@ -4900,7 +5280,7 @@ _class_setGrowCache(Class cls, BOOL grow) * fixme * Locking: none **********************************************************************/ -PRIVATE_EXTERN BOOL +BOOL _class_isLoadable(Class cls) { assert(isRealized(newcls(cls))); @@ -4920,7 +5300,7 @@ hasCxxStructors(class_t *cls) return (cls->data()->flags & RW_HAS_CXX_STRUCTORS) ? YES : NO; } -PRIVATE_EXTERN BOOL +BOOL _class_hasCxxStructors(Class cls) { return hasCxxStructors(newcls(cls)); @@ -4930,7 +5310,7 @@ _class_hasCxxStructors(Class cls) /*********************************************************************** * Locking: fixme **********************************************************************/ -PRIVATE_EXTERN BOOL +BOOL _class_shouldFinalizeOnMainThread(Class cls) { assert(isRealized(newcls(cls))); @@ -4941,7 +5321,7 @@ _class_shouldFinalizeOnMainThread(Class cls) /*********************************************************************** * Locking: fixme **********************************************************************/ -PRIVATE_EXTERN void +void _class_setFinalizeOnMainThread(Class cls) { assert(isRealized(newcls(cls))); @@ -4953,7 +5333,7 @@ _class_setFinalizeOnMainThread(Class cls) * _class_instancesHaveAssociatedObjects * May manipulate unrealized future classes in the CF-bridged case. **********************************************************************/ -PRIVATE_EXTERN BOOL +BOOL _class_instancesHaveAssociatedObjects(Class cls_gen) { class_t *cls = newcls(cls_gen); @@ -4966,7 +5346,7 @@ _class_instancesHaveAssociatedObjects(Class cls_gen) * _class_setInstancesHaveAssociatedObjects * May manipulate unrealized future classes in the CF-bridged case. **********************************************************************/ -PRIVATE_EXTERN void +void _class_setInstancesHaveAssociatedObjects(Class cls_gen) { class_t *cls = newcls(cls_gen); @@ -4977,7 +5357,7 @@ _class_setInstancesHaveAssociatedObjects(Class cls_gen) /*********************************************************************** * _class_usesAutomaticRetainRelease - * Returns YES if class was compiled with -fobjc-arr + * Returns YES if class was compiled with -fobjc-arc **********************************************************************/ BOOL _class_usesAutomaticRetainRelease(Class cls_gen) { @@ -4989,10 +5369,19 @@ BOOL _class_usesAutomaticRetainRelease(Class cls_gen) /*********************************************************************** * Return YES if sel is used by retain/release implementors **********************************************************************/ -static BOOL isRRSelector(SEL sel) +static bool isRRSelector(SEL sel) { return (sel == SEL_retain || sel == SEL_release || - sel == SEL_autorelease || sel == SEL_retainCount) ? YES : NO; + sel == SEL_autorelease || sel == SEL_retainCount); +} + + +/*********************************************************************** +* Return YES if sel is used by allocWithZone implementors +**********************************************************************/ +static bool isAWZSelector(SEL sel) +{ + return (sel == SEL_allocWithZone); } @@ -5000,35 +5389,108 @@ static BOOL isRRSelector(SEL sel) * Mark this class and all of its subclasses as implementors or * inheritors of custom RR (retain/release/autorelease/retainCount) **********************************************************************/ -void class_t::setHasCustomRR() +void class_t::setHasCustomRR(bool inherited) { rwlock_assert_writing(&runtimeLock); if (hasCustomRR()) return; FOREACH_REALIZED_CLASS_AND_SUBCLASS(c, this, { - // rdar://8955342 c->data_NEVER_USE |= 1UL; + if (PrintCustomRR && !c->hasCustomRR()) { + _objc_inform("CUSTOM RR: %s%s%s", getName(c), + isMetaClass(c) ? " (meta)" : "", + (inherited || c != this) ? " (inherited)" : ""); + } +#if CLASS_FAST_FLAGS_VIA_RW_DATA + c->data_NEVER_USE |= (uintptr_t)1; +#else c->data()->flags |= RW_HAS_CUSTOM_RR; +#endif }); } /*********************************************************************** -* Unmark custom RR. Not recursive. Almost never used. +* Mark this class and all of its subclasses as implementors or +* inheritors of custom allocWithZone: **********************************************************************/ -void class_t::unsetHasCustomRR() +void class_t::setHasCustomAWZ(bool inherited ) { rwlock_assert_writing(&runtimeLock); - this->data_NEVER_USE &= ~1UL; + if (hasCustomAWZ()) return; + + FOREACH_REALIZED_CLASS_AND_SUBCLASS(c, this, { + if (PrintCustomAWZ && !c->hasCustomAWZ()) { + _objc_inform("CUSTOM AWZ: %s%s%s", getName(c), + isMetaClass(c) ? " (meta)" : "", + (inherited || c != this) ? " (inherited)" : ""); + } +#if CLASS_FAST_FLAGS_VIA_RW_DATA + c->data_NEVER_USE |= (uintptr_t)2; +#else + c->data()->flags |= RW_HAS_CUSTOM_AWZ; +#endif + }); } +/*********************************************************************** +* Update custom RR and AWZ when a method changes its IMP +**********************************************************************/ +static void +updateCustomRR_AWZ(class_t *cls, method_t *meth) +{ + // In almost all cases, IMP swizzling does not affect custom RR/AWZ bits. + // The class is already marked for custom RR/AWZ, so changing the IMP + // does not transition from non-custom to custom. + // + // The only cases where IMP swizzling can affect the RR/AWZ bits is + // if the swizzled method is one of the methods that is assumed to be + // non-custom. These special cases come from attachMethodLists(). + // We look for such cases here if we do not know the affected class. + + if (isRRSelector(meth->name)) { + if (cls) { + cls->setHasCustomRR(); + } else { + // Don't know the class. + // The only special case is class NSObject. + FOREACH_METHOD_LIST(mlist, classNSObject(), { + for (uint32_t i = 0; i < mlist->count; i++) { + if (meth == method_list_nth(mlist, i)) { + // Yep, they're swizzling NSObject. + classNSObject()->setHasCustomRR(); + return; + } + } + }); + } + } + else if (isAWZSelector(meth->name)) { + if (cls) { + cls->setHasCustomAWZ(); + } else { + // Don't know the class. + // The only special case is metaclass NSObject. + FOREACH_METHOD_LIST(mlist, classNSObject()->isa, { + for (uint32_t i = 0; i < mlist->count; i++) { + if (meth == method_list_nth(mlist, i)) { + // Yep, they're swizzling metaclass NSObject. + classNSObject()->isa->setHasCustomRR(); + return; + } + } + }); + } + } +} + /*********************************************************************** * Locking: none * fixme assert realized to get superclass remapping? **********************************************************************/ -PRIVATE_EXTERN Class +Class _class_getSuperclass(Class cls) { return (Class)getSuperclass(newcls(cls)); @@ -5181,7 +5643,7 @@ class_setWeakIvarLayout(Class cls_gen, const uint8_t *layout) * fixme * Locking: read-locks runtimeLock **********************************************************************/ -PRIVATE_EXTERN Ivar +Ivar _class_getVariable(Class cls, const char *name, Class *memberOf) { rwlock_read(&runtimeLock); @@ -5276,7 +5738,7 @@ addMethod(class_t *cls, SEL name, IMP imp, const char *types, BOOL replace) } BOOL vtablesAffected = NO; - attachMethodLists(cls, &newlist, 1, NO, &vtablesAffected); + attachMethodLists(cls, &newlist, 1, NO, NO, &vtablesAffected); flushCaches(cls); if (vtablesAffected) flushVtables(cls); @@ -5504,7 +5966,7 @@ class_replaceProperty(Class cls_gen, const char *name, * Look up a class by name, and realize it. * Locking: acquires runtimeLock **********************************************************************/ -PRIVATE_EXTERN id +id look_up_class(const char *name, BOOL includeUnconnected __attribute__((unused)), BOOL includeClassHandler __attribute__((unused))) @@ -5563,15 +6025,21 @@ objc_duplicateClass(Class original_gen, const char *name, _memdup_internal(original->data()->ro, sizeof(*original->data()->ro)); *(char **)&duplicate->data()->ro->name = _strdup_internal(name); - if (original->data()->methods) { - duplicate->data()->methods = (method_list_t **) - _memdup_internal(original->data()->methods, - malloc_size(original->data()->methods)); + if (original->data()->flags & RW_METHOD_ARRAY) { + duplicate->data()->method_lists = (method_list_t **) + _memdup_internal(original->data()->method_lists, + malloc_size(original->data()->method_lists)); method_list_t **mlistp; - for (mlistp = duplicate->data()->methods; *mlistp; mlistp++) { + for (mlistp = duplicate->data()->method_lists; *mlistp; mlistp++) { *mlistp = (method_list_t *) _memdup_internal(*mlistp, method_list_size(*mlistp)); } + } else { + if (original->data()->method_list) { + duplicate->data()->method_list = (method_list_t *) + _memdup_internal(original->data()->method_list, + method_list_size(original->data()->method_list)); + } } // fixme dies when categories are added to the base @@ -5849,104 +6317,121 @@ void objc_registerClassPair(Class cls_gen) cls->data()->flags |= RW_CONSTRUCTED; cls->isa->data()->flags |= RW_CONSTRUCTED; - // Add to realized and uninitialized classes + // Add to named and realized classes addNamedClass(cls, cls->data()->ro->name); addRealizedClass(cls); addRealizedMetaclass(cls->isa); - addUninitializedClass(cls, cls->isa); + addNonMetaClass(cls); rwlock_unlock_write(&runtimeLock); } -static void unload_class(class_t *cls, BOOL isMeta) +/*********************************************************************** +* detach_class +* Disconnect a class from other data structures. +* Exception: does not remove the class from the +load list +* Call this before free_class. +* Locking: runtimeLock must be held by the caller. +**********************************************************************/ +static void detach_class(class_t *cls, BOOL isMeta) { - // Detach class from various lists + rwlock_assert_writing(&runtimeLock); // categories not yet attached to this class category_list *cats; cats = unattachedCategoriesForClass(cls); if (cats) free(cats); + // superclass's subclass list + if (isRealized(cls)) { + class_t *supercls = getSuperclass(cls); + if (supercls) { + removeSubclass(supercls, cls); + } + } + // class tables and +load queue if (!isMeta) { removeNamedClass(cls, getName(cls)); removeRealizedClass(cls); - removeUninitializedClass(cls); + removeNonMetaClass(cls); } else { removeRealizedMetaclass(cls); } +} - // superclass's subclass list - if (isRealized(cls)) { - class_t *supercls = getSuperclass(cls); - if (supercls) removeSubclass(supercls, cls); - } +/*********************************************************************** +* free_class +* Frees a class's data structures. +* Call this after detach_class. +* Locking: runtimeLock must be held by the caller +**********************************************************************/ +static void free_class(class_t *cls) +{ + rwlock_assert_writing(&runtimeLock); - // Dispose the class's own data structures + if (! isRealized(cls)) return; - if (isRealized(cls)) { - uint32_t i; + uint32_t i; + + // Dereferences the cache contents; do this before freeing methods + if (cls->cache != (Cache)&_objc_empty_cache) _cache_free(cls->cache); - // Dereferences the cache contents; do this before freeing methods - if (cls->cache != (Cache)&_objc_empty_cache) _cache_free(cls->cache); - - if (cls->data()->methods) { - method_list_t **mlistp; - for (mlistp = cls->data()->methods; *mlistp; mlistp++) { - for (i = 0; i < (**mlistp).count; i++) { - method_t *m = method_list_nth(*mlistp, i); - try_free(m->types); - } - try_free(*mlistp); - } - try_free(cls->data()->methods); + FOREACH_METHOD_LIST(mlist, cls, { + for (i = 0; i < mlist->count; i++) { + method_t *m = method_list_nth(mlist, i); + try_free(m->types); } - - const ivar_list_t *ilist = cls->data()->ro->ivars; - if (ilist) { - for (i = 0; i < ilist->count; i++) { - const ivar_t *ivar = ivar_list_nth(ilist, i); - try_free(ivar->offset); - try_free(ivar->name); - try_free(ivar->type); - } - try_free(ilist); + try_free(mlist); + }); + if (cls->data()->flags & RW_METHOD_ARRAY) { + try_free(cls->data()->method_lists); + } + + const ivar_list_t *ilist = cls->data()->ro->ivars; + if (ilist) { + for (i = 0; i < ilist->count; i++) { + const ivar_t *ivar = ivar_list_nth(ilist, i); + try_free(ivar->offset); + try_free(ivar->name); + try_free(ivar->type); } - - const protocol_list_t **plistp; - for (plistp = cls->data()->protocols; plistp && *plistp; plistp++) { - try_free(*plistp); + try_free(ilist); + } + + const protocol_list_t **plistp; + for (plistp = cls->data()->protocols; plistp && *plistp; plistp++) { + try_free(*plistp); + } + try_free(cls->data()->protocols); + + const chained_property_list *proplist = cls->data()->properties; + while (proplist) { + for (i = 0; i < proplist->count; i++) { + const property_t *prop = proplist->list+i; + try_free(prop->name); + try_free(prop->attributes); } - try_free(cls->data()->protocols); - - const chained_property_list *proplist = cls->data()->properties; - while (proplist) { - for (uint32_t i = 0; i < proplist->count; i++) { - const property_t *prop = proplist->list+i; - try_free(prop->name); - try_free(prop->attributes); - } - { - const chained_property_list *temp = proplist; - proplist = proplist->next; - try_free(temp); - } + { + const chained_property_list *temp = proplist; + proplist = proplist->next; + try_free(temp); } - - - if (cls->vtable != &_objc_empty_vtable && - cls->data()->flags & RW_SPECIALIZED_VTABLE) try_free(cls->vtable); - try_free(cls->data()->ro->ivarLayout); - try_free(cls->data()->ro->weakIvarLayout); - try_free(cls->data()->ro->name); - try_free(cls->data()->ro); - try_free(cls->data()); - try_free(cls); } + + if (cls->vtable != &_objc_empty_vtable && + cls->data()->flags & RW_SPECIALIZED_VTABLE) try_free(cls->vtable); + try_free(cls->data()->ro->ivarLayout); + try_free(cls->data()->ro->weakIvarLayout); + try_free(cls->data()->ro->name); + try_free(cls->data()->ro); + try_free(cls->data()); + try_free(cls); } + void objc_disposeClassPair(Class cls_gen) { class_t *cls = newcls(cls_gen); @@ -5986,8 +6471,10 @@ void objc_disposeClassPair(Class cls_gen) // don't remove_class_from_loadable_list() // - it's not there and we don't have the lock - unload_class(cls->isa, YES); - unload_class(cls, NO); + detach_class(cls->isa, YES); + detach_class(cls, NO); + free_class(cls->isa); + free_class(cls); rwlock_unlock_write(&runtimeLock); } @@ -6278,7 +6765,7 @@ OBJC_EXTERN id objc_msgSend_fp2ret_fixedup(id, SEL, ...); * obj is the receiver. supr is NULL for non-super messages * Locking: acquires runtimeLock **********************************************************************/ -OBJC_EXTERN PRIVATE_EXTERN IMP +OBJC_EXTERN IMP _objc_fixupMessageRef(id obj, struct objc_super2 *supr, message_ref_t *msg) { IMP imp; @@ -6331,7 +6818,7 @@ _objc_fixupMessageRef(id obj, struct objc_super2 *supr, message_ref_t *msg) #endif else { // ordinary dispatch - imp = lookUpMethod((Class)isa, msg->sel, YES/*initialize*/, YES/*cache*/); + imp = lookUpMethod((Class)isa, msg->sel, YES/*initialize*/, YES/*cache*/, obj); if (msg->imp == (IMP)&objc_msgSend_fixup) { msg->imp = (IMP)&objc_msgSend_fixedup; @@ -6375,6 +6862,9 @@ static class_t *setSuperclass(class_t *cls, class_t *newSuper) rwlock_assert_writing(&runtimeLock); + assert(isRealized(cls)); + assert(isRealized(newSuper)); + oldSuper = cls->superclass; removeSubclass(oldSuper, cls); removeSubclass(oldSuper->isa, cls->isa); @@ -6384,11 +6874,11 @@ static class_t *setSuperclass(class_t *cls, class_t *newSuper) addSubclass(newSuper, cls); addSubclass(newSuper->isa, cls->isa); - flushCaches(cls); flushCaches(cls->isa); - flushVtables(cls); flushVtables(cls->isa); - + flushCaches(cls); + flushVtables(cls); + return oldSuper; } diff --git a/runtime/objc-runtime-old.h b/runtime/objc-runtime-old.h index 1523b3e..fe319e6 100644 --- a/runtime/objc-runtime-old.h +++ b/runtime/objc-runtime-old.h @@ -115,6 +115,7 @@ struct old_protocol_ext { struct objc_method_description_list *optional_instance_methods; struct objc_method_description_list *optional_class_methods; struct old_property_list *instance_properties; + const char **extendedMethodTypes; }; diff --git a/runtime/objc-runtime-old.m b/runtime/objc-runtime-old.m index 9d53fdc..4c30d94 100644 --- a/runtime/objc-runtime-old.m +++ b/runtime/objc-runtime-old.m @@ -145,10 +145,6 @@ #include "objc-runtime-old.h" #include "objc-loadmethod.h" -/* NXHashTable SPI */ -extern unsigned _NXHashCapacity(NXHashTable *table); -extern void _NXHashRehashToCapacity(NXHashTable *table, unsigned newCapacity); - typedef struct _objc_unresolved_category { @@ -193,10 +189,10 @@ static BOOL _objc_register_category(struct old_category *cat, int version); // Function called when a class is loaded from an image -PRIVATE_EXTERN void (*callbackFunction)(Class, Category) = 0; +void (*callbackFunction)(Class, Category) = 0; // Hash table of classes -PRIVATE_EXTERN NXHashTable * class_hash = 0; +NXHashTable * class_hash = 0; static NXHashTablePrototype classHashPrototype = { (uintptr_t (*) (const void *, const void *)) classHash, @@ -235,7 +231,7 @@ static BOOL (*_objc_classLoader)(const char *) = NULL; /*********************************************************************** * objc_dump_class_hash. Log names of all known classes. **********************************************************************/ -PRIVATE_EXTERN void objc_dump_class_hash(void) +void objc_dump_class_hash(void) { NXHashTable *table; unsigned count; @@ -254,7 +250,7 @@ PRIVATE_EXTERN void objc_dump_class_hash(void) * _objc_init_class_hash. Return the class lookup table, create it if * necessary. **********************************************************************/ -PRIVATE_EXTERN void _objc_init_class_hash(void) +void _objc_init_class_hash(void) { // Do nothing if class hash table already exists if (class_hash) @@ -516,7 +512,7 @@ static void makeFutureClass(struct old_class *cls, const char *name) * Assumes the named class doesn't exist yet. * Not thread safe. **********************************************************************/ -PRIVATE_EXTERN Class _objc_allocateFutureClass(const char *name) +Class _objc_allocateFutureClass(const char *name) { struct old_class *cls; @@ -628,7 +624,7 @@ Protocol *objc_getProtocol(const char *name) * 3. classLoader callback * 4. classHandler callback (optional) **********************************************************************/ -PRIVATE_EXTERN id look_up_class(const char *aClassName, BOOL includeUnconnected, BOOL includeClassHandler) +id look_up_class(const char *aClassName, BOOL includeUnconnected, BOOL includeClassHandler) { BOOL includeClassLoader = YES; // class loader cannot be skipped id result = nil; @@ -696,7 +692,7 @@ static BOOL class_is_connected(struct old_class *cls) * Returns TRUE if class cls is ready for its +load method to be called. * A class is ready for +load if it is connected. **********************************************************************/ -PRIVATE_EXTERN BOOL _class_isLoadable(Class cls) +BOOL _class_isLoadable(Class cls) { return class_is_connected(oldcls(cls)); } @@ -912,7 +908,7 @@ static void really_connect_class(struct old_class *cls, layout_bitmap_free(superBitmap); if (layoutChanged) { - layout_bitmap weakBitmap = {0}; + layout_bitmap weakBitmap = {}; BOOL weakLayoutChanged = NO; if (cls->ext && cls->ext->weak_ivar_layout) { @@ -1531,12 +1527,13 @@ lookup_method(struct objc_method_description_list *mlist, SEL aSel) /*********************************************************************** * lookup_protocol_method -* Recursively search for a selector in a protocol -* (and all incorporated protocols) +* Search for a selector in a protocol +* (and optionally recursively all incorporated protocols) **********************************************************************/ -PRIVATE_EXTERN struct objc_method_description * +struct objc_method_description * lookup_protocol_method(struct old_protocol *proto, SEL aSel, - BOOL isRequiredMethod, BOOL isInstanceMethod) + BOOL isRequiredMethod, BOOL isInstanceMethod, + BOOL recursive) { struct objc_method_description *m = NULL; struct old_protocol_ext *ext; @@ -1555,11 +1552,11 @@ lookup_protocol_method(struct old_protocol *proto, SEL aSel, } } - if (!m && proto->protocol_list) { + if (!m && recursive && proto->protocol_list) { int i; for (i = 0; !m && i < proto->protocol_list->count; i++) { m = lookup_protocol_method(proto->protocol_list->list[i], aSel, - isRequiredMethod, isInstanceMethod); + isRequiredMethod,isInstanceMethod,true); } } @@ -1595,7 +1592,7 @@ protocol_getMethodDescription(Protocol *p, SEL aSel, if (!proto) return empty; desc = lookup_protocol_method(proto, aSel, - isRequiredMethod, isInstanceMethod); + isRequiredMethod, isInstanceMethod, true); if (desc) return *desc; else return empty; } @@ -1660,7 +1657,7 @@ objc_property_t protocol_getProperty(Protocol *p, const char *name, { struct old_protocol *proto = oldprotocol(p); struct old_protocol_ext *ext; - struct old_protocol_list *plist; + struct old_protocol_list *proto_list; if (!proto || !name) return NULL; @@ -1682,11 +1679,11 @@ objc_property_t protocol_getProperty(Protocol *p, const char *name, } } - if ((plist = proto->protocol_list)) { + if ((proto_list = proto->protocol_list)) { int i; - for (i = 0; i < plist->count; i++) { + for (i = 0; i < proto_list->count; i++) { objc_property_t prop = - protocol_getProperty((Protocol *)plist->list[i], name, + protocol_getProperty((Protocol *)proto_list->list[i], name, isRequiredProperty, isInstanceProperty); if (prop) return prop; } @@ -1792,6 +1789,72 @@ BOOL protocol_isEqual(Protocol *self, Protocol *other) } +/*********************************************************************** +* _protocol_getMethodTypeEncoding +* Return the @encode string for the requested protocol method. +* Returns NULL if the compiler did not emit any extended @encode data. +* Locking: runtimeLock must not be held by the caller +**********************************************************************/ +const char * +_protocol_getMethodTypeEncoding(Protocol *proto_gen, SEL sel, + BOOL isRequiredMethod, BOOL isInstanceMethod) +{ + struct old_protocol *proto = oldprotocol(proto_gen); + if (!proto) return NULL; + struct old_protocol_ext *ext = ext_for_protocol(proto); + if (!ext) return NULL; + if (ext->size < offsetof(struct old_protocol_ext, extendedMethodTypes) + sizeof(ext->extendedMethodTypes)) return NULL; + if (! ext->extendedMethodTypes) return NULL; + + struct objc_method_description *m = + lookup_protocol_method(proto, sel, + isRequiredMethod, isInstanceMethod, false); + if (!m) { + // No method with that name. Search incorporated protocols. + if (proto->protocol_list) { + for (int i = 0; i < proto->protocol_list->count; i++) { + const char *enc = + _protocol_getMethodTypeEncoding((Protocol *)proto->protocol_list->list[i], sel, isRequiredMethod, isInstanceMethod); + if (enc) return enc; + } + } + return NULL; + } + + int i = 0; + if (isRequiredMethod && isInstanceMethod) { + i += ((uintptr_t)m - (uintptr_t)proto->instance_methods) / sizeof(proto->instance_methods->list[0]); + goto done; + } else if (proto->instance_methods) { + i += proto->instance_methods->count; + } + + if (isRequiredMethod && !isInstanceMethod) { + i += ((uintptr_t)m - (uintptr_t)proto->class_methods) / sizeof(proto->class_methods->list[0]); + goto done; + } else if (proto->class_methods) { + i += proto->class_methods->count; + } + + if (!isRequiredMethod && isInstanceMethod) { + i += ((uintptr_t)m - (uintptr_t)ext->optional_instance_methods) / sizeof(ext->optional_instance_methods->list[0]); + goto done; + } else if (ext->optional_instance_methods) { + i += ext->optional_instance_methods->count; + } + + if (!isRequiredMethod && !isInstanceMethod) { + i += ((uintptr_t)m - (uintptr_t)ext->optional_class_methods) / sizeof(ext->optional_class_methods->list[0]); + goto done; + } else if (ext->optional_class_methods) { + i += ext->optional_class_methods->count; + } + + done: + return ext->extendedMethodTypes[i]; +} + + /*********************************************************************** * objc_allocateProtocol * Creates a new protocol. The protocol may not be used until @@ -2150,11 +2213,11 @@ static void _objc_fixup_selector_refs (const header_info *hi) if (PrintPreopt) { if (sel_preoptimizationValid(hi)) { _objc_inform("PREOPTIMIZATION: honoring preoptimized selectors in %s", - _nameForHeader(hi->mhdr)); + hi->fname); } else if (_objcHeaderOptimizedByDyld(hi)) { _objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors in %s", - _nameForHeader(hi->mhdr)); + hi->fname); } } @@ -2180,7 +2243,7 @@ static inline BOOL _is_threaded() { * mh is mach_header instead of headerType because that's what * dyld_priv.h says even for 64-bit. **********************************************************************/ -PRIVATE_EXTERN void +void unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide) { recursive_mutex_lock(&loadMethodLock); @@ -2194,7 +2257,7 @@ unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide) * Process the given images which are being mapped in by dyld. * Calls ABI-agnostic code after taking ABI-specific locks. **********************************************************************/ -PRIVATE_EXTERN const char * +const char * map_images(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]) { @@ -2215,7 +2278,7 @@ map_images(enum dyld_image_states state, uint32_t infoCount, * * Locking: acquires classLock and loadMethodLock **********************************************************************/ -PRIVATE_EXTERN const char * +const char * load_images(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]) { @@ -2242,7 +2305,7 @@ load_images(enum dyld_image_states state, uint32_t infoCount, * _read_images * Perform metadata processing for hCount images starting with firstNewHeader **********************************************************************/ -PRIVATE_EXTERN void _read_images(header_info **hList, uint32_t hCount) +void _read_images(header_info **hList, uint32_t hCount) { uint32_t i; BOOL categoriesLoaded = NO; @@ -2310,7 +2373,7 @@ static void schedule_class_load(struct old_class *cls) cls->info |= CLS_LOADED; } -PRIVATE_EXTERN void prepare_load_methods(header_info *hi) +void prepare_load_methods(header_info *hi) { Module mods; unsigned int midx; @@ -2375,7 +2438,7 @@ PRIVATE_EXTERN void prepare_load_methods(header_info *hi) #if TARGET_OS_WIN32 -PRIVATE_EXTERN void unload_class(struct old_class *cls) +void unload_class(struct old_class *cls) { } @@ -2412,7 +2475,7 @@ static void rependClassReferences(struct old_class **refs, size_t count, } -PRIVATE_EXTERN void try_free(const void *p) +void try_free(const void *p) { if (p && malloc_size(p)) free((void *)p); } @@ -2443,7 +2506,7 @@ static void unload_property_list(struct old_property_list *proplist) // Deallocate all memory in a class. -PRIVATE_EXTERN void unload_class(struct old_class *cls) +void unload_class(struct old_class *cls) { // Free method cache // This dereferences the cache contents; do this before freeing methods @@ -2674,7 +2737,7 @@ static void unload_paranoia(header_info *hi) seg = (uintptr_t)getsegmentdata(hi->mhdr, "__OBJC", &seg_size); _objc_inform("UNLOAD DEBUG: unloading image '%s' [%p..%p]", - _nameForHeader(hi->mhdr), (void *)seg, (void*)(seg+seg_size)); + hi->fname, (void *)seg, (void*)(seg+seg_size)); mutex_lock(&classLock); @@ -2743,7 +2806,7 @@ static void unload_paranoia(header_info *hi) * Only handles MH_BUNDLE for now. * Locking: loadMethodLock acquired by unmap_image **********************************************************************/ -PRIVATE_EXTERN void _unload_image(header_info *hi) +void _unload_image(header_info *hi) { recursive_mutex_assert_locked(&loadMethodLock); @@ -2850,7 +2913,7 @@ static void _objcTweakMethodListPointerForClass(struct old_class *cls) * Does not take any locks. * If the class is already in use, use class_addMethods() instead. **********************************************************************/ -PRIVATE_EXTERN void _objc_insertMethods(struct old_class *cls, +void _objc_insertMethods(struct old_class *cls, struct old_method_list *mlist, struct old_category *cat) { @@ -2930,7 +2993,7 @@ PRIVATE_EXTERN void _objc_insertMethods(struct old_class *cls, * Does not flush any method caches. * If the class is currently in use, use class_removeMethods() instead. **********************************************************************/ -PRIVATE_EXTERN void _objc_removeMethods(struct old_class *cls, +void _objc_removeMethods(struct old_class *cls, struct old_method_list *mlist) { struct old_method_list ***list; @@ -3202,7 +3265,7 @@ static BOOL _objc_register_category(struct old_category *cat, int version) } -PRIVATE_EXTERN const char ** +const char ** _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount) { Module mods; @@ -3290,17 +3353,17 @@ BOOL gdb_objc_isRuntimeLocked() * Every lock used anywhere must be managed here. * Locks not managed here may cause gdb deadlocks. **********************************************************************/ -PRIVATE_EXTERN rwlock_t selLock = {0}; -PRIVATE_EXTERN mutex_t classLock = MUTEX_INITIALIZER; -PRIVATE_EXTERN mutex_t methodListLock = MUTEX_INITIALIZER; -PRIVATE_EXTERN mutex_t cacheUpdateLock = MUTEX_INITIALIZER; -PRIVATE_EXTERN recursive_mutex_t loadMethodLock = RECURSIVE_MUTEX_INITIALIZER; +rwlock_t selLock = {}; +mutex_t classLock = MUTEX_INITIALIZER; +mutex_t methodListLock = MUTEX_INITIALIZER; +mutex_t cacheUpdateLock = MUTEX_INITIALIZER; +recursive_mutex_t loadMethodLock = RECURSIVE_MUTEX_INITIALIZER; static int debugger_selLock; static int debugger_loadMethodLock; #define RDONLY 1 #define RDWR 2 -PRIVATE_EXTERN void lock_init(void) +void lock_init(void) { rwlock_init(&selLock); recursive_mutex_init(&loadMethodLock); @@ -3319,7 +3382,7 @@ PRIVATE_EXTERN void lock_init(void) * attempt to manipulate them will cause a trap. * Locks not handled here may cause deadlocks in gdb. **********************************************************************/ -PRIVATE_EXTERN int startDebuggerMode(void) +int startDebuggerMode(void) { int result = DEBUGGER_FULL; @@ -3374,7 +3437,7 @@ PRIVATE_EXTERN int startDebuggerMode(void) * endDebuggerMode * Relinquish locks acquired in startDebuggerMode(). **********************************************************************/ -PRIVATE_EXTERN void endDebuggerMode(void) +void endDebuggerMode(void) { if (debugger_loadMethodLock) { recursive_mutex_unlock(&loadMethodLock); @@ -3392,7 +3455,7 @@ PRIVATE_EXTERN void endDebuggerMode(void) * Returns YES if the given lock is handled specially during debugger * mode (i.e. debugger mode tries to acquire it). **********************************************************************/ -PRIVATE_EXTERN BOOL isManagedDuringDebugger(void *lock) +BOOL isManagedDuringDebugger(void *lock) { if (lock == &selLock) return YES; if (lock == &classLock) return YES; @@ -3408,7 +3471,7 @@ PRIVATE_EXTERN BOOL isManagedDuringDebugger(void *lock) * Locking a managed mutex during debugger mode causes a trap unless * this returns YES. **********************************************************************/ -PRIVATE_EXTERN BOOL isLockedDuringDebugger(void *lock) +BOOL isLockedDuringDebugger(void *lock) { assert(DebuggerMode); @@ -3416,7 +3479,6 @@ PRIVATE_EXTERN BOOL isLockedDuringDebugger(void *lock) if (lock == &methodListLock) return YES; if (lock == &cacheUpdateLock) return YES; if (lock == (mutex_t *)&loadMethodLock) return YES; - return NO; } @@ -3426,7 +3488,7 @@ PRIVATE_EXTERN BOOL isLockedDuringDebugger(void *lock) * Read-locking a managed rwlock during debugger mode causes a trap unless * this returns YES. **********************************************************************/ -PRIVATE_EXTERN BOOL isReadingDuringDebugger(rwlock_t *lock) +BOOL isReadingDuringDebugger(rwlock_t *lock) { assert(DebuggerMode); @@ -3442,7 +3504,7 @@ PRIVATE_EXTERN BOOL isReadingDuringDebugger(rwlock_t *lock) * Write-locking a managed rwlock during debugger mode causes a trap unless * this returns YES. **********************************************************************/ -PRIVATE_EXTERN BOOL isWritingDuringDebugger(rwlock_t *lock) +BOOL isWritingDuringDebugger(rwlock_t *lock) { assert(DebuggerMode); diff --git a/runtime/objc-runtime.h b/runtime/objc-runtime.h index 1701b1e..4599f08 100644 --- a/runtime/objc-runtime.h +++ b/runtime/objc-runtime.h @@ -1,2 +1,2 @@ -#import -#import +#include +#include diff --git a/runtime/objc-runtime.m b/runtime/objc-runtime.mm similarity index 82% rename from runtime/objc-runtime.m rename to runtime/objc-runtime.mm index d933e86..01eb72d 100644 --- a/runtime/objc-runtime.m +++ b/runtime/objc-runtime.mm @@ -46,40 +46,42 @@ OBJC_EXPORT Class getOriginalClassForPosingClass(Class); // Settings from environment variables #if SUPPORT_ENVIRON -PRIVATE_EXTERN int PrintImages = -1; // env OBJC_PRINT_IMAGES -PRIVATE_EXTERN int PrintLoading = -1; // env OBJC_PRINT_LOAD_METHODS -PRIVATE_EXTERN int PrintInitializing = -1; // env OBJC_PRINT_INITIALIZE_METHODS -PRIVATE_EXTERN int PrintResolving = -1; // env OBJC_PRINT_RESOLVED_METHODS -PRIVATE_EXTERN int PrintConnecting = -1; // env OBJC_PRINT_CLASS_SETUP -PRIVATE_EXTERN int PrintProtocols = -1; // env OBJC_PRINT_PROTOCOL_SETUP -PRIVATE_EXTERN int PrintIvars = -1; // env OBJC_PRINT_IVAR_SETUP -PRIVATE_EXTERN int PrintVtables = -1; // env OBJC_PRINT_VTABLE_SETUP -PRIVATE_EXTERN int PrintVtableImages = -1;//env OBJC_PRINT_VTABLE_IMAGES -PRIVATE_EXTERN int PrintFuture = -1; // env OBJC_PRINT_FUTURE_CLASSES -PRIVATE_EXTERN int PrintRTP = -1; // env OBJC_PRINT_RTP -PRIVATE_EXTERN int PrintGC = -1; // env OBJC_PRINT_GC -PRIVATE_EXTERN int PrintPreopt = -1; // env OBJC_PRINT_PREOPTIMIZATION -PRIVATE_EXTERN int PrintCxxCtors = -1; // env OBJC_PRINT_CXX_CTORS -PRIVATE_EXTERN int PrintExceptions = -1; // env OBJC_PRINT_EXCEPTIONS -PRIVATE_EXTERN int PrintExceptionThrow = -1; // env OBJC_PRINT_EXCEPTION_THROW -PRIVATE_EXTERN int PrintAltHandlers = -1; // env OBJC_PRINT_ALT_HANDLERS -PRIVATE_EXTERN int PrintDeprecation = -1;// env OBJC_PRINT_DEPRECATION_WARNINGS -PRIVATE_EXTERN int PrintReplacedMethods = -1; // env OBJC_PRINT_REPLACED_METHODS -PRIVATE_EXTERN int PrintCaches = -1; // env OBJC_PRINT_CACHE_SETUP -PRIVATE_EXTERN int PrintPoolHiwat = -1; // env OBJC_PRINT_POOL_HIGHWATER - -PRIVATE_EXTERN int UseInternalZone = -1; // env OBJC_USE_INTERNAL_ZONE - -PRIVATE_EXTERN int DebugUnload = -1; // env OBJC_DEBUG_UNLOAD -PRIVATE_EXTERN int DebugFragileSuperclasses = -1; // env OBJC_DEBUG_FRAGILE_SUPERCLASSES -PRIVATE_EXTERN int DebugNilSync = -1; // env OBJC_DEBUG_NIL_SYNC -PRIVATE_EXTERN int DebugNonFragileIvars = -1; // env OBJC_DEBUG_NONFRAGILE_IVARS -PRIVATE_EXTERN int DebugAltHandlers = -1;// env OBJC_DEBUG_ALT_HANDLERS - -PRIVATE_EXTERN int DisableGC = -1; // env OBJC_DISABLE_GC -PRIVATE_EXTERN int DisableVtables = -1; // env OBJC_DISABLE_VTABLES -PRIVATE_EXTERN int DisablePreopt = -1; // env OBJC_DISABLE_PREOPTIMIZATION -PRIVATE_EXTERN int DebugFinalizers = -1; // env OBJC_DEBUG_FINALIZERS +int PrintImages = -1; // env OBJC_PRINT_IMAGES +int PrintLoading = -1; // env OBJC_PRINT_LOAD_METHODS +int PrintInitializing = -1; // env OBJC_PRINT_INITIALIZE_METHODS +int PrintResolving = -1; // env OBJC_PRINT_RESOLVED_METHODS +int PrintConnecting = -1; // env OBJC_PRINT_CLASS_SETUP +int PrintProtocols = -1; // env OBJC_PRINT_PROTOCOL_SETUP +int PrintIvars = -1; // env OBJC_PRINT_IVAR_SETUP +int PrintVtables = -1; // env OBJC_PRINT_VTABLE_SETUP +int PrintVtableImages = -1;//env OBJC_PRINT_VTABLE_IMAGES +int PrintFuture = -1; // env OBJC_PRINT_FUTURE_CLASSES +int PrintRTP = -1; // env OBJC_PRINT_RTP +int PrintGC = -1; // env OBJC_PRINT_GC +int PrintPreopt = -1; // env OBJC_PRINT_PREOPTIMIZATION +int PrintCxxCtors = -1; // env OBJC_PRINT_CXX_CTORS +int PrintExceptions = -1; // env OBJC_PRINT_EXCEPTIONS +int PrintExceptionThrow = -1; // env OBJC_PRINT_EXCEPTION_THROW +int PrintAltHandlers = -1; // env OBJC_PRINT_ALT_HANDLERS +int PrintDeprecation = -1;// env OBJC_PRINT_DEPRECATION_WARNINGS +int PrintReplacedMethods = -1; // env OBJC_PRINT_REPLACED_METHODS +int PrintCaches = -1; // env OBJC_PRINT_CACHE_SETUP +int PrintPoolHiwat = -1; // env OBJC_PRINT_POOL_HIGHWATER +int PrintCustomRR = -1; // env OBJC_PRINT_CUSTOM_RR +int PrintCustomAWZ = -1; // env OBJC_PRINT_CUSTOM_AWZ + +int UseInternalZone = -1; // env OBJC_USE_INTERNAL_ZONE + +int DebugUnload = -1; // env OBJC_DEBUG_UNLOAD +int DebugFragileSuperclasses = -1; // env OBJC_DEBUG_FRAGILE_SUPERCLASSES +int DebugNilSync = -1; // env OBJC_DEBUG_NIL_SYNC +int DebugNonFragileIvars = -1; // env OBJC_DEBUG_NONFRAGILE_IVARS +int DebugAltHandlers = -1;// env OBJC_DEBUG_ALT_HANDLERS + +int DisableGC = -1; // env OBJC_DISABLE_GC +int DisableVtables = -1; // env OBJC_DISABLE_VTABLES +int DisablePreopt = -1; // env OBJC_DISABLE_PREOPTIMIZATION +int DebugFinalizers = -1; // env OBJC_DEBUG_FINALIZERS #endif @@ -87,25 +89,26 @@ PRIVATE_EXTERN int DebugFinalizers = -1; // env OBJC_DEBUG_FINALIZERS static tls_key_t _objc_pthread_key; // Selectors -PRIVATE_EXTERN SEL SEL_load = NULL; -PRIVATE_EXTERN SEL SEL_initialize = NULL; -PRIVATE_EXTERN SEL SEL_resolveInstanceMethod = NULL; -PRIVATE_EXTERN SEL SEL_resolveClassMethod = NULL; -PRIVATE_EXTERN SEL SEL_cxx_construct = NULL; -PRIVATE_EXTERN SEL SEL_cxx_destruct = NULL; -PRIVATE_EXTERN SEL SEL_retain = NULL; -PRIVATE_EXTERN SEL SEL_release = NULL; -PRIVATE_EXTERN SEL SEL_autorelease = NULL; -PRIVATE_EXTERN SEL SEL_retainCount = NULL; -PRIVATE_EXTERN SEL SEL_alloc = NULL; -PRIVATE_EXTERN SEL SEL_copy = NULL; -PRIVATE_EXTERN SEL SEL_new = NULL; -PRIVATE_EXTERN SEL SEL_finalize = NULL; -PRIVATE_EXTERN SEL SEL_forwardInvocation = NULL; - -PRIVATE_EXTERN header_info *FirstHeader = 0; // NULL means empty list -PRIVATE_EXTERN header_info *LastHeader = 0; // NULL means invalid; recompute it -PRIVATE_EXTERN int HeaderCount = 0; +SEL SEL_load = NULL; +SEL SEL_initialize = NULL; +SEL SEL_resolveInstanceMethod = NULL; +SEL SEL_resolveClassMethod = NULL; +SEL SEL_cxx_construct = NULL; +SEL SEL_cxx_destruct = NULL; +SEL SEL_retain = NULL; +SEL SEL_release = NULL; +SEL SEL_autorelease = NULL; +SEL SEL_retainCount = NULL; +SEL SEL_alloc = NULL; +SEL SEL_allocWithZone = NULL; +SEL SEL_copy = NULL; +SEL SEL_new = NULL; +SEL SEL_finalize = NULL; +SEL SEL_forwardInvocation = NULL; + +header_info *FirstHeader = 0; // NULL means empty list +header_info *LastHeader = 0; // NULL means invalid; recompute it +int HeaderCount = 0; @@ -203,18 +206,9 @@ id objc_getMetaClass(const char *aClassName) /*********************************************************************** -* _nameForHeader. +* appendHeader. Add a newly-constructed header_info to the list. **********************************************************************/ -PRIVATE_EXTERN const char *_nameForHeader(const headerType *header) -{ - return _getObjcHeaderName ((headerType *) header); -} - - -/*********************************************************************** -* _objc_appendHeader. Add a newly-constructed header_info to the list. -**********************************************************************/ -PRIVATE_EXTERN void _objc_appendHeader(header_info *hi) +void appendHeader(header_info *hi) { // Add the header to the header list. // The header is appended to the list, to preserve the bottom-up order. @@ -237,13 +231,13 @@ PRIVATE_EXTERN void _objc_appendHeader(header_info *hi) /*********************************************************************** -* _objc_RemoveHeader +* removeHeader * Remove the given header from the header list. * FirstHeader is updated. * LastHeader is set to NULL. Any code that uses LastHeader must * detect this NULL and recompute LastHeader by traversing the list. **********************************************************************/ -PRIVATE_EXTERN void _objc_removeHeader(header_info *hi) +void removeHeader(header_info *hi) { header_info **hiP; @@ -271,7 +265,7 @@ PRIVATE_EXTERN void _objc_removeHeader(header_info *hi) * Read environment variables that affect the runtime. * Also print environment variable help, if requested. **********************************************************************/ -PRIVATE_EXTERN void environ_init(void) +void environ_init(void) { #if SUPPORT_ENVIRON int PrintHelp = (getenv("OBJC_HELP") != NULL); @@ -350,7 +344,11 @@ PRIVATE_EXTERN void environ_init(void) OPTION(PrintDeprecation, OBJC_PRINT_DEPRECATION_WARNINGS, "warn about calls to deprecated runtime functions"); OPTION(PrintPoolHiwat, OBJC_PRINT_POOL_HIGHWATER, - "print high-water marks for autorelease pools"); + "log high-water marks for autorelease pools"); + OPTION(PrintCustomRR, OBJC_PRINT_CUSTOM_RR, + "log classes with un-optimized custom retain/release methods"); + OPTION(PrintCustomAWZ, OBJC_PRINT_CUSTOM_AWZ, + "log classes with un-optimized custom allocWithZone methods"); OPTION(DebugUnload, OBJC_DEBUG_UNLOAD, "warn about poorly-behaving bundles when unloaded"); @@ -384,7 +382,7 @@ PRIVATE_EXTERN void environ_init(void) * logReplacedMethod * OBJC_PRINT_REPLACED_METHODS implementation **********************************************************************/ -PRIVATE_EXTERN void +void logReplacedMethod(const char *className, SEL s, BOOL isMeta, const char *catName, IMP oldImp, IMP newImp) @@ -400,8 +398,8 @@ logReplacedMethod(const char *className, SEL s, #else Dl_info dl; - if (dladdr(oldImp, &dl) && dl.dli_fname) oldImage = dl.dli_fname; - if (dladdr(newImp, &dl) && dl.dli_fname) newImage = dl.dli_fname; + if (dladdr((void*)oldImp, &dl) && dl.dli_fname) oldImage = dl.dli_fname; + if (dladdr((void*)newImp, &dl) && dl.dli_fname) newImage = dl.dli_fname; #endif _objc_inform("REPLACED: %c[%s %s] %s%s (IMP was %p (%s), now %p (%s))", @@ -429,13 +427,14 @@ void objc_setMultithreaded (BOOL flag) * If the data doesn't exist yet and create is NO, return NULL. * If the data doesn't exist yet and create is YES, allocate and return it. **********************************************************************/ -PRIVATE_EXTERN _objc_pthread_data *_objc_fetch_pthread_data(BOOL create) +_objc_pthread_data *_objc_fetch_pthread_data(BOOL create) { _objc_pthread_data *data; - data = tls_get(_objc_pthread_key); + data = (_objc_pthread_data *)tls_get(_objc_pthread_key); if (!data && create) { - data = _calloc_internal(1, sizeof(_objc_pthread_data)); + data = (_objc_pthread_data *) + _calloc_internal(1, sizeof(_objc_pthread_data)); tls_set(_objc_pthread_key, data); } @@ -449,7 +448,7 @@ PRIVATE_EXTERN _objc_pthread_data *_objc_fetch_pthread_data(BOOL create) * arg shouldn't be NULL, but we check anyway. **********************************************************************/ extern void _destroyInitializingClassList(struct _objc_initializing_classes *list); -PRIVATE_EXTERN void _objc_pthread_destroyspecific(void *arg) +void _objc_pthread_destroyspecific(void *arg) { _objc_pthread_data *data = (_objc_pthread_data *)arg; if (data != NULL) { @@ -464,7 +463,7 @@ PRIVATE_EXTERN void _objc_pthread_destroyspecific(void *arg) } -PRIVATE_EXTERN void tls_init(void) +void tls_init(void) { #if SUPPORT_DIRECT_THREAD_KEYS _objc_pthread_key = TLS_DIRECT_KEY; @@ -528,12 +527,12 @@ void objc_setForwardHandler(void *fwd, void *fwd_stret) * Returns the number of BYTES needed * for a branch from entry to target. **********************************************************************/ -PRIVATE_EXTERN size_t objc_branch_size(void *entry, void *target) +size_t objc_branch_size(void *entry, void *target) { return objc_cond_branch_size(entry, target, COND_ALWAYS); } -PRIVATE_EXTERN size_t +size_t objc_cond_branch_size(void *entry, void *target, unsigned cond) { // For simplicity, always use 32-bit relative jumps. @@ -547,12 +546,12 @@ objc_cond_branch_size(void *entry, void *target, unsigned cond) * The sequence written will be objc_branch_size(entry, target) BYTES. * Returns the number of BYTES written. **********************************************************************/ -PRIVATE_EXTERN size_t objc_write_branch(void *entry, void *target) +size_t objc_write_branch(void *entry, void *target) { return objc_write_cond_branch(entry, target, COND_ALWAYS); } -PRIVATE_EXTERN size_t +size_t objc_write_cond_branch(void *entry, void *target, unsigned cond) { uint8_t *address = (uint8_t *)entry; // instructions written to here @@ -580,7 +579,7 @@ objc_write_cond_branch(void *entry, void *target, unsigned cond) #if !__OBJC2__ // GrP fixme -extern Class _objc_getOrigClass(const char *name); +OBJC_EXTERN Class _objc_getOrigClass(const char *name); #endif const char *class_getImageName(Class cls) { @@ -624,19 +623,19 @@ const char **objc_copyImageNames(unsigned int *outCount) int count = 0; int max = HeaderCount; #if TARGET_OS_WIN32 - const TCHAR **names = calloc(max+1, sizeof(TCHAR *)); + const TCHAR **names = (const TCHAR **)calloc(max+1, sizeof(TCHAR *)); #else - const char **names = calloc(max+1, sizeof(char *)); + const char **names = (const char **)calloc(max+1, sizeof(char *)); #endif for (hi = FirstHeader; hi != NULL && count < max; hi = hi->next) { #if TARGET_OS_WIN32 - if (hi->os.moduleName) { - names[count++] = hi->os.moduleName; + if (hi->moduleName) { + names[count++] = hi->moduleName; } #else - if (hi->os.dl_info.dli_fname) { - names[count++] = hi->os.dl_info.dli_fname; + if (hi->fname) { + names[count++] = hi->fname; } #endif } @@ -669,9 +668,9 @@ objc_copyClassNamesForImage(const char *image, unsigned int *outCount) // Find the image. for (hi = FirstHeader; hi != NULL; hi = hi->next) { #if TARGET_OS_WIN32 - if (0 == wcscmp((TCHAR *)image, hi->os.moduleName)) break; + if (0 == wcscmp((TCHAR *)image, hi->moduleName)) break; #else - if (0 == strcmp(image, hi->os.dl_info.dli_fname)) break; + if (0 == strcmp(image, hi->fname)) break; #endif } @@ -716,19 +715,19 @@ void objc_setEnumerationMutationHandler(void (*handler)(id)) { **********************************************************************/ #if SUPPORT_GC -PRIVATE_EXTERN id objc_getAssociatedObject_gc(id object, const void *key) { - return auto_zone_get_associative_ref(gc_zone, object, (void *)key); +id objc_getAssociatedObject_gc(id object, const void *key) { + return (id)auto_zone_get_associative_ref(gc_zone, object, (void *)key); } #endif -PRIVATE_EXTERN id objc_getAssociatedObject_non_gc(id object, const void *key) { +id objc_getAssociatedObject_non_gc(id object, const void *key) { return _object_get_associative_reference(object, (void *)key); } id objc_getAssociatedObject(id object, const void *key) { #if SUPPORT_GC if (UseGC) { - return auto_zone_get_associative_ref(gc_zone, object, (void *)key); + return (id)auto_zone_get_associative_ref(gc_zone, object, (void *)key); } else #endif { @@ -737,15 +736,15 @@ id objc_getAssociatedObject(id object, const void *key) { } #if SUPPORT_GC -PRIVATE_EXTERN void objc_setAssociatedObject_gc(id object, const void *key, id value, objc_AssociationPolicy policy) { +void objc_setAssociatedObject_gc(id object, const void *key, id value, objc_AssociationPolicy policy) { if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) { - value = objc_msgSend(value, SEL_copy); + value = ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy); } auto_zone_set_associative_ref(gc_zone, object, (void *)key, value); } #endif -PRIVATE_EXTERN void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy) { +void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy) { _object_set_associative_reference(object, (void *)key, value, policy); } @@ -753,7 +752,7 @@ void objc_setAssociatedObject(id object, const void *key, id value, objc_Associa #if SUPPORT_GC if (UseGC) { if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) { - value = objc_msgSend(value, SEL_copy); + value = ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy); } auto_zone_set_associative_ref(gc_zone, object, (void *)key, value); } else @@ -801,8 +800,8 @@ BOOL class_instancesHaveAssociatedObjects(Class cls) { #if SUPPORT_DEBUGGER_MODE -PRIVATE_EXTERN int DebuggerMode = DEBUGGER_OFF; -PRIVATE_EXTERN objc_thread_t DebuggerModeThread = 0; +int DebuggerMode = DEBUGGER_OFF; +objc_thread_t DebuggerModeThread = 0; static int DebuggerModeCount; /********************************************************************** diff --git a/runtime/objc-sel-set.h b/runtime/objc-sel-set.h index c545b00..517123e 100644 --- a/runtime/objc-sel-set.h +++ b/runtime/objc-sel-set.h @@ -26,6 +26,9 @@ * A set of SELs used for SEL uniquing. */ +#ifndef _OBJC_SEL_SET_H_ +#define _OBJC_SEL_SET_H_ + #include #include "objc-os.h" @@ -33,8 +36,10 @@ __BEGIN_DECLS struct __objc_sel_set; -extern struct __objc_sel_set *__objc_sel_set_create(uint32_t capacity); +extern struct __objc_sel_set *__objc_sel_set_create(size_t selrefCount); extern SEL __objc_sel_set_get(struct __objc_sel_set *sset, SEL candidate); extern void __objc_sel_set_add(struct __objc_sel_set *sset, SEL value); __END_DECLS + +#endif diff --git a/runtime/objc-sel-set.m b/runtime/objc-sel-set.mm similarity index 70% rename from runtime/objc-sel-set.m rename to runtime/objc-sel-set.mm index d24b306..7be1545 100644 --- a/runtime/objc-sel-set.m +++ b/runtime/objc-sel-set.mm @@ -41,15 +41,15 @@ #define SIZE 27 static const uint32_t __objc_sel_set_capacities[SIZE+1] = { - 3, 6, 12, 24, 48, 96, 192, 384, 768, 1536, 3072, 6144, 12288, 24576, 49152, - 98304, 196608, 393216, 786432, 1572864, 3145728, 6291456, 12582912, 25165824, - 50331648, 100663296, 201326592, UINT32_MAX + 3, 6, 12, 24, 48, 96, 192, 384, 768, 1536, 3072, 6144, 12288, 24576, + 49152, 98304, 196608, 393216, 786432, 1572864, 3145728, 6291456, + 12582912, 25165824, 50331648, 100663296, 201326592, UINT32_MAX }; static const uint32_t __objc_sel_set_buckets[SIZE] = { // powers of 2 - 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, - 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, - 67108864, 134217728, 268435456 + 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, + 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, + 16777216, 33554432, 67108864, 134217728, 268435456 }; #else @@ -59,17 +59,19 @@ static const uint32_t __objc_sel_set_buckets[SIZE] = { // powers of 2 #define SIZE 42 static const uint32_t __objc_sel_set_capacities[SIZE+1] = { - 4, 8, 17, 29, 47, 76, 123, 199, 322, 521, 843, 1364, 2207, 3571, 5778, 9349, - 15127, 24476, 39603, 64079, 103682, 167761, 271443, 439204, 710647, 1149851, 1860498, - 3010349, 4870847, 7881196, 12752043, 20633239, 33385282, 54018521, 87403803, 141422324, - 228826127, 370248451, 599074578, 969323029, 1568397607, 2537720636U, UINT32_MAX + 4, 8, 17, 29, 47, 76, 123, 199, 322, 521, 843, 1364, 2207, 3571, + 5778, 9349, 15127, 24476, 39603, 64079, 103682, 167761, 271443, + 439204, 710647, 1149851, 1860498, 3010349, 4870847, 7881196, 12752043, + 20633239, 33385282, 54018521, 87403803, 141422324, 228826127, 370248451, + 599074578, 969323029, 1568397607, 2537720636U, UINT32_MAX }; static const uint32_t __objc_sel_set_buckets[SIZE] = { // primes - 5, 11, 23, 41, 67, 113, 199, 317, 521, 839, 1361, 2207, 3571, 5779, 9349, 15121, - 24473, 39607, 64081, 103681, 167759, 271429, 439199, 710641, 1149857, 1860503, 3010349, - 4870843, 7881193, 12752029, 20633237, 33385273, 54018521, 87403763, 141422317, 228826121, - 370248451, 599074561, 969323023, 1568397599, 2537720629U, 4106118251U + 5, 11, 23, 41, 67, 113, 199, 317, 521, 839, 1361, 2207, 3571, 5779, + 9349, 15121, 24473, 39607, 64081, 103681, 167759, 271429, 439199, + 710641, 1149857, 1860503, 3010349, 4870843, 7881193, 12752029, 20633237, + 33385273, 54018521, 87403763, 141422317, 228826121, 370248451, 599074561, + 969323023, 1568397599, 2537720629U, 4106118251U }; #endif @@ -106,29 +108,39 @@ static struct __objc_sel_set_finds __objc_sel_set_findBuckets(struct __objc_sel_ } // create a set with given starting capacity, will resize as needed -PRIVATE_EXTERN struct __objc_sel_set *__objc_sel_set_create(uint32_t capacity) { +struct __objc_sel_set *__objc_sel_set_create(size_t selrefs) { uint32_t idx; - struct __objc_sel_set *sset = _malloc_internal(sizeof(struct __objc_sel_set)); + struct __objc_sel_set *sset = (struct __objc_sel_set *) + _malloc_internal(sizeof(struct __objc_sel_set)); if (!sset) _objc_fatal("objc_sel_set failure"); sset->_count = 0; - for (idx = 0; __objc_sel_set_capacities[idx] < capacity; idx++); + // heuristic to convert executable's selrefs count to table size +#if TARGET_OS_IPHONE + for (idx = 0; __objc_sel_set_capacities[idx] < selrefs; idx++); + if (idx > 0 && selrefs < 1536) idx--; +#else + if (selrefs < 1024) selrefs = 1024; + for (idx = 0; __objc_sel_set_capacities[idx] < selrefs; idx++); + idx++; +#endif + if (SIZE <= idx) _objc_fatal("objc_sel_set failure"); sset->_capacity = __objc_sel_set_capacities[idx]; sset->_bucketsNum = __objc_sel_set_buckets[idx]; - sset->_buckets = _calloc_internal(sset->_bucketsNum, sizeof(SEL)); + sset->_buckets = (SEL *)_calloc_internal(sset->_bucketsNum, sizeof(SEL)); if (!sset->_buckets) _objc_fatal("objc_sel_set failure"); return sset; } // returns 0 on failure; candidate may not be 0 -PRIVATE_EXTERN SEL __objc_sel_set_get(struct __objc_sel_set *sset, SEL candidate) { +SEL __objc_sel_set_get(struct __objc_sel_set *sset, SEL candidate) { return __objc_sel_set_findBuckets(sset, candidate).match; } // value may not be 0; should not be called unless it is known the value is not in the set -PRIVATE_EXTERN void __objc_sel_set_add(struct __objc_sel_set *sset, SEL value) { +void __objc_sel_set_add(struct __objc_sel_set *sset, SEL value) { if (sset->_count == sset->_capacity) { SEL *oldbuckets = sset->_buckets; uint32_t oldnbuckets = sset->_bucketsNum; @@ -137,7 +149,8 @@ PRIVATE_EXTERN void __objc_sel_set_add(struct __objc_sel_set *sset, SEL value) { if (SIZE <= idx) _objc_fatal("objc_sel_set failure"); sset->_capacity = __objc_sel_set_capacities[idx]; sset->_bucketsNum = __objc_sel_set_buckets[idx]; - sset->_buckets = _calloc_internal(sset->_bucketsNum, sizeof(SEL)); + sset->_buckets = (SEL *) + _calloc_internal(sset->_bucketsNum, sizeof(SEL)); if (!sset->_buckets) _objc_fatal("objc_sel_set failure"); for (idx = 0; idx < oldnbuckets; idx++) { SEL currentSel = oldbuckets[idx]; diff --git a/runtime/objc-sel-table.s b/runtime/objc-sel-table.s index 3852405..e5c63e9 100644 --- a/runtime/objc-sel-table.s +++ b/runtime/objc-sel-table.s @@ -3,19 +3,28 @@ .align 3 .private_extern __objc_opt_data __objc_opt_data: -.long 10 /* table.version */ +.long 12 /* table.version */ .long 0 /* table.selopt_offset */ -.space 4096-8 +.long 0 /* table.headeropt_offset */ +.long 0 /* table.clsopt_offset */ +.space 4096-16 /* space for selopt, smax/capacity=131072, blen/mask=65535+1 */ .space 65536 -.space 131072*4 /* offsets */ .space 131072 /* checkbytes */ +.space 131072*4 /* offsets */ + + +/* space for clsopt, smax/capacity=16384, blen/mask=4095+1 */ +.space 4096 +.space 16384 /* checkbytes */ +.space 16384*12 /* offsets to name and class and header_info */ +.space 4096 /* some duplicate classes */ + -#if __arm__ .section __DATA,__objc_opt_rw .align 3 -.space 256*1024 -#else -/* optimization performed in linker, not shared cache */ -#endif +.private_extern __objc_opt_rw_data +__objc_opt_rw_data: +/* space for header_info structures */ +.space 4096*4 diff --git a/runtime/objc-sel.mm b/runtime/objc-sel.mm index 8e010c3..2ddae46 100644 --- a/runtime/objc-sel.mm +++ b/runtime/objc-sel.mm @@ -33,37 +33,22 @@ #include "objc-auto.h" #include "objc-sel-set.h" -#if SUPPORT_BUILTINS -#include "objc-selopt.h" -#endif - -__BEGIN_DECLS - -#if SUPPORT_BUILTINS -// opt: the actual opt used at runtime -// builtins: the actual selector table used at runtime -// _objc_opt_data: opt data possibly written by dyld -// empty_opt_data: empty data to use if dyld didn't cooperate or DisablePreopt +#if SUPPORT_PREOPT +#include using namespace objc_opt; static const objc_selopt_t *builtins = NULL; -static const objc_opt_t *opt = NULL; -static BOOL preoptimized; - -extern const objc_opt_t _objc_opt_data; // in __TEXT, __objc_selopt -static const uint32_t empty_opt_data[] = OPT_INITIALIZER; #endif +__BEGIN_DECLS -#define NUM_NONBUILTIN_SELS 3500 -// objc_sel_set grows at 3571, 5778, 9349. -// Most apps use 2000..7000 extra sels. Most apps will grow zero to two times. +static size_t SelrefCount = 0; static const char *_objc_empty_selector = ""; static struct __objc_sel_set *_objc_selectors = NULL; -#if SUPPORT_BUILTINS -PRIVATE_EXTERN void dump_builtins(void) +#if SUPPORT_PREOPT +void dump_builtins(void) { uint32_t occupied = builtins->occupied; uint32_t capacity = builtins->capacity; @@ -83,10 +68,13 @@ PRIVATE_EXTERN void dump_builtins(void) const int32_t *offsets = builtins->offsets(); uint32_t i; for (i = 0; i < capacity; i++) { - if (offsets[i] != offsetof(objc_selopt_t, zero)) { + if (offsets[i] != offsetof(objc_stringhash_t, zero)) { const char *str = (const char *)builtins + offsets[i]; _objc_inform("BUILTIN SELECTORS: %6d: %+8d %s", i, offsets[i], str); + if ((const char *)sel_registerName(str) != str) { + _objc_fatal("bogus"); + } } else { _objc_inform("BUILTIN SELECTORS: %6d: ", i); } @@ -108,7 +96,8 @@ static SEL _objc_search_builtins(const char *key) #endif if ('\0' == *key) return (SEL)_objc_empty_selector; -#if SUPPORT_BUILTINS +#if SUPPORT_PREOPT + assert(builtins); return (SEL)builtins->get(key); #endif @@ -167,7 +156,7 @@ static SEL __sel_registerName(const char *name, int lock, int copy) if (lock) rwlock_write(&selLock); if (!_objc_selectors) { - _objc_selectors = __objc_sel_set_create(NUM_NONBUILTIN_SELS); + _objc_selectors = __objc_sel_set_create(SelrefCount); } if (lock) { // Rescan in case it was added while we dropped the lock @@ -190,16 +179,16 @@ SEL sel_registerName(const char *name) { return __sel_registerName(name, 1, 1); // YES lock, YES copy } -PRIVATE_EXTERN SEL sel_registerNameNoLock(const char *name, BOOL copy) { +SEL sel_registerNameNoLock(const char *name, BOOL copy) { return __sel_registerName(name, 0, copy); // NO lock, maybe copy } -PRIVATE_EXTERN void sel_lock(void) +void sel_lock(void) { rwlock_write(&selLock); } -PRIVATE_EXTERN void sel_unlock(void) +void sel_unlock(void) { rwlock_unlock_write(&selLock); } @@ -225,9 +214,9 @@ BOOL sel_isEqual(SEL lhs, SEL rhs) * Return YES if this image's selector fixups are valid courtesy * of the dyld shared cache. **********************************************************************/ -PRIVATE_EXTERN BOOL sel_preoptimizationValid(const header_info *hi) +BOOL sel_preoptimizationValid(const header_info *hi) { -#if !SUPPORT_BUILTINS +#if !SUPPORT_PREOPT return NO; @@ -239,7 +228,7 @@ PRIVATE_EXTERN BOOL sel_preoptimizationValid(const header_info *hi) # endif // preoptimization disabled for some reason - if (!preoptimized) return NO; + if (!isPreoptimized()) return NO; // image not from shared cache, or not fixed inside shared cache if (!_objcHeaderOptimizedByDyld(hi)) return NO; @@ -254,65 +243,13 @@ PRIVATE_EXTERN BOOL sel_preoptimizationValid(const header_info *hi) * sel_init * Initialize selector tables and register selectors used internally. **********************************************************************/ -PRIVATE_EXTERN void sel_init(BOOL wantsGC) +void sel_init(BOOL wantsGC, size_t selrefCount) { -#if !SUPPORT_BUILTINS - - disableSharedCacheOptimizations(); - -#else - // not set at compile time in order to detect too-early selector operations - const char *failure = NULL; - opt = &_objc_opt_data; - - if (DisablePreopt) { - // OBJC_DISABLE_PREOPTIMIZATION is set - // If opt->version != VERSION then you continue at your own risk. - failure = "(by OBJC_DISABLE_PREOPTIMIZATION)"; - } - else if (opt->version != objc_opt::VERSION) { - // This shouldn't happen. You probably forgot to - // change OPT_INITIALIZER and objc-sel-table.s. - // If dyld really did write the wrong optimization version, - // then we must halt because we don't know what bits dyld twiddled. - _objc_fatal("bad objc opt version (want %d, got %d)", - objc_opt::VERSION, opt->version); - } - else if (!opt->selopt()) { - // No selector table. dyld must not have written one. - failure = "(dyld shared cache is absent or out of date)"; - } -#if SUPPORT_IGNORED_SELECTOR_CONSTANT - else if (UseGC) { - // GC is on, which renames some selectors - // Non-selector optimizations are still valid, but we don't have - // any of those yet - failure = "(GC is on)"; - } -#endif - - if (failure) { - // All preoptimized selector references are invalid. - preoptimized = NO; - opt = (objc_opt_t *)empty_opt_data; - builtins = opt->selopt(); - disableSharedCacheOptimizations(); - - if (PrintPreopt) { - _objc_inform("PREOPTIMIZATION: is DISABLED %s", failure); - } - } - else { - // Valid optimization data written by dyld shared cache - preoptimized = YES; - builtins = opt->selopt(); - - if (PrintPreopt) { - _objc_inform("PREOPTIMIZATION: is ENABLED " - "(version %d)", opt->version); - } - } + // save this value for later + SelrefCount = selrefCount; +#if SUPPORT_PREOPT + builtins = preoptimizedSelectors(); #endif // Register selectors used by libobjc @@ -339,6 +276,7 @@ PRIVATE_EXTERN void sel_init(BOOL wantsGC) s(autorelease); s(retainCount); s(alloc); + t(allocWithZone:, allocWithZone); s(copy); s(new); s(finalize); diff --git a/runtime/objc-selopt.h b/runtime/objc-selopt.h deleted file mode 100644 index 9bb5637..0000000 --- a/runtime/objc-selopt.h +++ /dev/null @@ -1,1111 +0,0 @@ -/* - * Copyright (c) 2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -/* -Portions derived from: - --------------------------------------------------------------------- -lookup8.c, by Bob Jenkins, January 4 1997, Public Domain. -hash(), hash2(), hash3, and mix() are externally useful functions. -Routines to test the hash are included if SELF_TEST is defined. -You can use this free for any purpose. It has no warranty. --------------------------------------------------------------------- - ------------------------------------------------------------------------------- -perfect.c: code to generate code for a hash for perfect hashing. -(c) Bob Jenkins, September 1996, December 1999 -You may use this code in any way you wish, and it is free. No warranty. -I hereby place this in the public domain. -Source is http://burtleburtle.net/bob/c/perfect.c ------------------------------------------------------------------------------- -*/ - -/* - * objc-selopt.h - * Interface between libobjc and dyld - * for selector uniquing in the dyld shared cache. - * - * When building the shared cache, dyld locates all selectors and selector - * references in the cached images. It builds a perfect hash table out of - * them and writes the table into the shared cache copy of libobjc. - * libobjc then uses that table as the builtin selector list. - * - * Versioning - * The table has a version number. dyld and objc can both ignore the table - * if the other used the wrong version number. - * - * Completeness - * Not all libraries are in the shared cache. Libraries that are in the - * shared cache and were optimized are specially marked. Libraries on - * disk never include those marks. - * - * Coherency - * Libraries optimized in the shared cache can be replaced by unoptimized - * copies from disk when loaded. The copy from disk is not marked and will - * be fixed up by libobjc. The shared cache copy is still mapped into the - * process, so the table can point to cstring data in that library's part - * of the shared cache without trouble. - * - * Atomicity - * dyld writes the table itself last. If dyld marks some metadata as - * updated but then fails to write a table for some reason, libobjc - * fixes up all metadata as if it were not marked. - */ - -#ifndef _OBJC_SELOPT_H -#define _OBJC_SELOPT_H - -/* - DO NOT INCLUDE ANY objc HEADERS HERE - dyld USES THIS FILE AND CANNOT SEE THEM -*/ -#include -#include -#include -/* - DO NOT INCLUDE ANY objc HEADERS HERE - dyld USES THIS FILE AND CANNOT SEE THEM -*/ - -#ifndef STATIC_ASSERT -# define STATIC_ASSERT(x) _STATIC_ASSERT2(x, __LINE__) -# define _STATIC_ASSERT2(x, line) _STATIC_ASSERT3(x, line) -# define _STATIC_ASSERT3(x, line) \ - typedef struct { \ - int _static_assert[(x) ? 0 : -1]; \ - } _static_assert_ ## line __attribute__((unavailable)) -#endif - -#define SELOPT_DEBUG 0 - -namespace objc_opt { - - typedef int32_t objc_selopt_offset_t; - typedef uint8_t objc_selopt_check_t; - -#ifdef SELOPT_WRITE - -// Perfect hash code is at the end of this file. - -struct perfect_hash { - uint32_t capacity; - uint32_t occupied; - uint32_t shift; - uint32_t mask; - uint64_t salt; - - uint32_t scramble[256]; - uint8_t *tab; // count == mask+1; free with delete[] - - perfect_hash() : tab(0) { } - - ~perfect_hash() { if (tab) delete[] tab; } -}; - -struct eqstr { - bool operator()(const char* s1, const char* s2) const { - return strcmp(s1, s2) == 0; - } -}; - -typedef __gnu_cxx::hash_map, eqstr> string_map; - -static perfect_hash make_perfect(const string_map& strings); - -#endif - -static uint64_t lookup8( uint8_t *k, size_t length, uint64_t level); - -// Edit objc-sel-table.s and OPT_INITIALIZER if you change this structure. -struct objc_selopt_t { - uint32_t capacity; - uint32_t occupied; - uint32_t shift; - uint32_t mask; - uint32_t zero; - uint32_t unused; // alignment pad - uint64_t salt; - - uint32_t scramble[256]; - uint8_t tab[0]; /* tab[mask+1] (always power-of-2) */ - // int32_t offsets[capacity]; /* offsets from &capacity to cstrings */ - // uint8_t checkbytes[capacity]; /* check byte for each selector string */ - - objc_selopt_offset_t *offsets() { return (objc_selopt_offset_t *)&tab[mask+1]; } - const objc_selopt_offset_t *offsets() const { return (const objc_selopt_offset_t *)&tab[mask+1]; } - - objc_selopt_check_t *checkbytes() { return (objc_selopt_check_t *)&offsets()[capacity]; } - const objc_selopt_check_t *checkbytes() const { return (const objc_selopt_check_t *)&offsets()[capacity]; } - - uint32_t hash(const char *key) const - { - uint64_t val = lookup8((uint8_t*)key, strlen(key), salt); - uint32_t index = (uint32_t)(val>>shift) ^ scramble[tab[val&mask]]; - return index; - } - - // The check bytes areused to reject strings that aren't in the table - // without paging in the table's cstring data. This checkbyte calculation - // catches 4785/4815 rejects when launching Safari; a perfect checkbyte - // would catch 4796/4815. - objc_selopt_check_t checkbyte(const char *key) const - { - return - ((key[0] & 0x7) << 5) - | - (strlen(key) & 0x1f); - } - - const char *get(const char *key) const - { - uint32_t h = hash(key); - - // Use check byte to reject without paging in the table's cstrings - objc_selopt_check_t h_check = checkbytes()[h]; - objc_selopt_check_t key_check = checkbyte(key); - bool check_fail = (h_check != key_check); -#if ! SELOPT_DEBUG - if (check_fail) return NULL; -#endif - - const char *result = (const char *)this + offsets()[h]; - if (0 != strcmp(key, result)) return NULL; - -#if SELOPT_DEBUG - if (check_fail) abort(); -#endif - - return result; - } - -#ifdef SELOPT_WRITE - void set(const char *key, objc_selopt_offset_t value) - { - uint32_t h = hash(key); - offsets()[h] = value; - checkbytes()[h] = checkbyte(key); - } -#endif -}; - - -// Edit objc-sel-table.s if you change this value. -enum { VERSION = 10 }; - -// Edit objc-sel-table.s and OPT_INITIALIZER if you change this structure. -struct objc_opt_t { - uint32_t version; - uint32_t selopt_offset; - - const objc_selopt_t* selopt() const { - if (selopt_offset == 0) return NULL; - return (objc_selopt_t *)((uint8_t *)this + selopt_offset); - } - objc_selopt_t* selopt() { - if (selopt_offset == 0) return NULL; - return (objc_selopt_t *)((uint8_t *)this + selopt_offset); - } -}; - -// sizeof(objc_opt_t) must be pointer-aligned -STATIC_ASSERT(sizeof(objc_opt_t) % sizeof(void*) == 0); - -// Initializer for empty opt of type uint32_t[]. -#define X8(x) x, x, x, x, x, x, x, x -#define X64(x) X8(x), X8(x), X8(x), X8(x), X8(x), X8(x), X8(x), X8(x) -#define X256(x) X64(x), X64(x), X64(x), X64(x) -#define OPT_INITIALIZER { \ - /* objc_opt_t */ \ - objc_opt::VERSION, 8, \ - /* objc_selopt_t */ \ - 4, 4, 63, 3, 0, 0, 0,0, X256(0), 0, 16, 16, 16, 16, 0 \ -} - - -/* --------------------------------------------------------------------- -mix -- mix 3 64-bit values reversibly. -mix() takes 48 machine instructions, but only 24 cycles on a superscalar - machine (like Intel's new MMX architecture). It requires 4 64-bit - registers for 4::2 parallelism. -All 1-bit deltas, all 2-bit deltas, all deltas composed of top bits of - (a,b,c), and all deltas of bottom bits were tested. All deltas were - tested both on random keys and on keys that were nearly all zero. - These deltas all cause every bit of c to change between 1/3 and 2/3 - of the time (well, only 113/400 to 287/400 of the time for some - 2-bit delta). These deltas all cause at least 80 bits to change - among (a,b,c) when the mix is run either forward or backward (yes it - is reversible). -This implies that a hash using mix64 has no funnels. There may be - characteristics with 3-bit deltas or bigger, I didn't test for - those. --------------------------------------------------------------------- -*/ -#define mix64(a,b,c) \ -{ \ - a -= b; a -= c; a ^= (c>>43); \ - b -= c; b -= a; b ^= (a<<9); \ - c -= a; c -= b; c ^= (b>>8); \ - a -= b; a -= c; a ^= (c>>38); \ - b -= c; b -= a; b ^= (a<<23); \ - c -= a; c -= b; c ^= (b>>5); \ - a -= b; a -= c; a ^= (c>>35); \ - b -= c; b -= a; b ^= (a<<49); \ - c -= a; c -= b; c ^= (b>>11); \ - a -= b; a -= c; a ^= (c>>12); \ - b -= c; b -= a; b ^= (a<<18); \ - c -= a; c -= b; c ^= (b>>22); \ -} - -/* --------------------------------------------------------------------- -hash() -- hash a variable-length key into a 64-bit value - k : the key (the unaligned variable-length array of bytes) - len : the length of the key, counting by bytes - level : can be any 8-byte value -Returns a 64-bit value. Every bit of the key affects every bit of -the return value. No funnels. Every 1-bit and 2-bit delta achieves -avalanche. About 41+5len instructions. - -The best hash table sizes are powers of 2. There is no need to do -mod a prime (mod is sooo slow!). If you need less than 64 bits, -use a bitmask. For example, if you need only 10 bits, do - h = (h & hashmask(10)); -In which case, the hash table should have hashsize(10) elements. - -If you are hashing n strings (uint8_t **)k, do it like this: - for (i=0, h=0; i= 24) - { - a += (k[0] +((uint64_t)k[ 1]<< 8)+((uint64_t)k[ 2]<<16)+((uint64_t)k[ 3]<<24) - +((uint64_t)k[4 ]<<32)+((uint64_t)k[ 5]<<40)+((uint64_t)k[ 6]<<48)+((uint64_t)k[ 7]<<56)); - b += (k[8] +((uint64_t)k[ 9]<< 8)+((uint64_t)k[10]<<16)+((uint64_t)k[11]<<24) - +((uint64_t)k[12]<<32)+((uint64_t)k[13]<<40)+((uint64_t)k[14]<<48)+((uint64_t)k[15]<<56)); - c += (k[16] +((uint64_t)k[17]<< 8)+((uint64_t)k[18]<<16)+((uint64_t)k[19]<<24) - +((uint64_t)k[20]<<32)+((uint64_t)k[21]<<40)+((uint64_t)k[22]<<48)+((uint64_t)k[23]<<56)); - mix64(a,b,c); - k += 24; len -= 24; - } - - /*------------------------------------- handle the last 23 bytes */ - c += length; - switch(len) /* all the case statements fall through */ - { - case 23: c+=((uint64_t)k[22]<<56); - case 22: c+=((uint64_t)k[21]<<48); - case 21: c+=((uint64_t)k[20]<<40); - case 20: c+=((uint64_t)k[19]<<32); - case 19: c+=((uint64_t)k[18]<<24); - case 18: c+=((uint64_t)k[17]<<16); - case 17: c+=((uint64_t)k[16]<<8); - /* the first byte of c is reserved for the length */ - case 16: b+=((uint64_t)k[15]<<56); - case 15: b+=((uint64_t)k[14]<<48); - case 14: b+=((uint64_t)k[13]<<40); - case 13: b+=((uint64_t)k[12]<<32); - case 12: b+=((uint64_t)k[11]<<24); - case 11: b+=((uint64_t)k[10]<<16); - case 10: b+=((uint64_t)k[ 9]<<8); - case 9: b+=((uint64_t)k[ 8]); - case 8: a+=((uint64_t)k[ 7]<<56); - case 7: a+=((uint64_t)k[ 6]<<48); - case 6: a+=((uint64_t)k[ 5]<<40); - case 5: a+=((uint64_t)k[ 4]<<32); - case 4: a+=((uint64_t)k[ 3]<<24); - case 3: a+=((uint64_t)k[ 2]<<16); - case 2: a+=((uint64_t)k[ 1]<<8); - case 1: a+=((uint64_t)k[ 0]); - /* case 0: nothing left to add */ - } - mix64(a,b,c); - /*-------------------------------------------- report the result */ - return c; -} - - -#ifdef SELOPT_WRITE - -static const char * -write_selopt(void *dst, uint64_t base, size_t dstSize, - string_map& strings, bool little_endian, - size_t *outSize) -{ - if (strings.size() == 0) return false; - - perfect_hash phash = make_perfect(strings); - if (phash.capacity == 0) { - return "perfect hash failed (selectors not optimized)"; - } - - size_t size = sizeof(objc_selopt_t) - + phash.mask+1 - + phash.capacity * sizeof(objc_selopt_offset_t) - + phash.capacity * sizeof(objc_selopt_check_t); - if (size > dstSize) { - return "selector section too small (selectors not optimized)"; - } - - uint8_t *buf = new uint8_t[size]; - objc_selopt_t *selopt = (objc_selopt_t *)buf; - - // Set header - selopt->capacity = phash.capacity; - selopt->occupied = phash.occupied; - selopt->shift = phash.shift; - selopt->mask = phash.mask; - selopt->zero = 0; - selopt->unused = 0; - selopt->salt = phash.salt; - - // Set hash data - for (uint32_t i = 0; i < 256; i++) { - selopt->scramble[i] = phash.scramble[i]; - } - for (uint32_t i = 0; i < phash.mask+1; i++) { - selopt->tab[i] = phash.tab[i]; - } - - // Set offsets to "" - for (uint32_t i = 0; i < phash.capacity; i++) { - selopt->offsets()[i] = - (objc_selopt_offset_t)offsetof(objc_selopt_t, zero); - } - // Set checkbytes to 0 - for (uint32_t i = 0; i < phash.capacity; i++) { - selopt->checkbytes()[i] = 0; - } - - // Set real string offsets and checkbytes -# define SHIFT (64 - 8*sizeof(objc_selopt_offset_t)) - string_map::const_iterator s; - for (s = strings.begin(); s != strings.end(); ++s) { - int64_t offset = s->second - base; - if ((offset<>SHIFT != offset) { - delete[] buf; - return "selector offset too big (selectors not optimized)"; - } - selopt->set(s->first, (objc_selopt_offset_t)offset); - } -# undef SHIFT; - - // Byte-swap everything -#define S32(x) x = little_endian ? OSSwapHostToLittleInt32(x) : OSSwapHostToBigInt32(x) -#define S64(x) x = little_endian ? OSSwapHostToLittleInt64(x) : OSSwapHostToBigInt64(x) - for (uint32_t i = 0; i < 256; i++) { - S32(selopt->scramble[i]); - } - // tab is array of bytes, no swap needed - for (uint32_t i = 0; i < phash.capacity; i++) { - S32(selopt->offsets()[i]); - } - - S32(selopt->capacity); - S32(selopt->occupied); - S32(selopt->shift); - S32(selopt->mask); - S32(selopt->zero); - S64(selopt->salt); -#undef S32 -#undef S64 - - memcpy(dst, selopt, size); - if (outSize) *outSize = size; - - delete[] buf; - return NULL; -} - - -/* ------------------------------------------------------------------------------- -This generates a minimal perfect hash function. That means, given a -set of n keys, this determines a hash function that maps each of -those keys into a value in 0..n-1 with no collisions. - -The perfect hash function first uses a normal hash function on the key -to determine (a,b) such that the pair (a,b) is distinct for all -keys, then it computes a^scramble[tab[b]] to get the final perfect hash. -tab[] is an array of 1-byte values and scramble[] is a 256-term array of -2-byte or 4-byte values. If there are n keys, the length of tab[] is a -power of two between n/3 and n. - -I found the idea of computing distinct (a,b) values in "Practical minimal -perfect hash functions for large databases", Fox, Heath, Chen, and Daoud, -Communications of the ACM, January 1992. They found the idea in Chichelli -(CACM Jan 1980). Beyond that, our methods differ. - -The key is hashed to a pair (a,b) where a in 0..*alen*-1 and b in -0..*blen*-1. A fast hash function determines both a and b -simultaneously. Any decent hash function is likely to produce -hashes so that (a,b) is distinct for all pairs. I try the hash -using different values of *salt* until all pairs are distinct. - -The final hash is (a XOR scramble[tab[b]]). *scramble* is a -predetermined mapping of 0..255 into 0..smax-1. *tab* is an -array that we fill in in such a way as to make the hash perfect. - -First we fill in all values of *tab* that are used by more than one -key. We try all possible values for each position until one works. - -This leaves m unmapped keys and m values that something could hash to. -If you treat unmapped keys as lefthand nodes and unused hash values -as righthand nodes, and draw a line connecting each key to each hash -value it could map to, you get a bipartite graph. We attempt to -find a perfect matching in this graph. If we succeed, we have -determined a perfect hash for the whole set of keys. - -*scramble* is used because (a^tab[i]) clusters keys around *a*. ------------------------------------------------------------------------------- -*/ - -typedef uint64_t ub8; -#define UB8MAXVAL 0xffffffffffffffffLL -#define UB8BITS 64 -typedef uint32_t ub4; -#define UB4MAXVAL 0xffffffff -#define UB4BITS 32 -typedef uint16_t ub2; -#define UB2MAXVAL 0xffff -#define UB2BITS 16 -typedef uint8_t ub1; -#define UB1MAXVAL 0xff -#define UB1BITS 8 - -#define TRUE 1 -#define FALSE 0 - -#define SCRAMBLE_LEN 256 // ((ub4)1<<16) /* length of *scramble* */ -#define RETRY_INITKEY 2048 /* number of times to try to find distinct (a,b) */ -#define RETRY_PERFECT 4 /* number of times to try to make a perfect hash */ - - -/* representation of a key */ -struct key -{ - ub1 *name_k; /* the actual key */ - ub4 len_k; /* the length of the actual key */ - ub4 hash_k; /* the initial hash value for this key */ -/* beyond this point is mapping-dependent */ - ub4 a_k; /* a, of the key maps to (a,b) */ - ub4 b_k; /* b, of the key maps to (a,b) */ - struct key *nextb_k; /* next key with this b */ -}; -typedef struct key key; - -/* things indexed by b of original (a,b) pair */ -struct bstuff -{ - ub2 val_b; /* hash=a^tabb[b].val_b */ - key *list_b; /* tabb[i].list_b is list of keys with b==i */ - ub4 listlen_b; /* length of list_b */ - ub4 water_b; /* high watermark of who has visited this map node */ -}; -typedef struct bstuff bstuff; - -/* things indexed by final hash value */ -struct hstuff -{ - key *key_h; /* tabh[i].key_h is the key with a hash of i */ -}; -typedef struct hstuff hstuff; - -/* things indexed by queue position */ -struct qstuff -{ - bstuff *b_q; /* b that currently occupies this hash */ - ub4 parent_q; /* queue position of parent that could use this hash */ - ub2 newval_q; /* what to change parent tab[b] to to use this hash */ - ub2 oldval_q; /* original value of tab[b] */ -}; -typedef struct qstuff qstuff; - - -/* ------------------------------------------------------------------------------- -Find the mapping that will produce a perfect hash ------------------------------------------------------------------------------- -*/ - -/* return the ceiling of the log (base 2) of val */ -static ub4 log2u(ub4 val) -{ - ub4 i; - for (i=0; ((ub4)1<>const3)); - x = (x+(x<>const5)); - } - return x; -} - -/* initialize scramble[] with distinct random values in 0..smax-1 */ -static void scrambleinit(ub4 *scramble, ub4 smax) -// ub4 *scramble; /* hash is a^scramble[tab[b]] */ -// ub4 smax; /* scramble values should be in 0..smax-1 */ -{ - ub4 i; - - /* fill scramble[] with distinct random integers in 0..smax-1 */ - for (i=0; ib_k - * check if the initial hash might work - */ -static int inittab(bstuff *tabb, ub4 blen, key *keys, ub4 nkeys, int complete) -// bstuff *tabb; /* output, list of keys with b for (a,b) */ -// ub4 blen; /* length of tabb */ -// key *keys; /* list of keys already hashed */ -// int complete; /* TRUE means to complete init despite collisions */ -{ - int nocollision = TRUE; - ub4 i; - - memset((void *)tabb, 0, (size_t)(sizeof(bstuff)*blen)); - - /* Two keys with the same (a,b) guarantees a collision */ - for (i = 0; i < nkeys; i++) { - key *mykey = keys+i; - key *otherkey; - - for (otherkey=tabb[mykey->b_k].list_b; - otherkey; - otherkey=otherkey->nextb_k) - { - if (mykey->a_k == otherkey->a_k) - { - nocollision = FALSE; - if (!complete) - return FALSE; - } - } - ++tabb[mykey->b_k].listlen_b; - mykey->nextb_k = tabb[mykey->b_k].list_b; - tabb[mykey->b_k].list_b = mykey; - } - - /* no two keys have the same (a,b) pair */ - return nocollision; -} - - -/* Do the initial hash for normal mode (use lookup and checksum) */ -static void initnorm(key *keys, ub4 nkeys, ub4 alen, ub4 blen, ub4 smax, ub8 salt) -// key *keys; /* list of all keys */ -// ub4 alen; /* (a,b) has a in 0..alen-1, a power of 2 */ -// ub4 blen; /* (a,b) has b in 0..blen-1, a power of 2 */ -// ub4 smax; /* maximum range of computable hash values */ -// ub4 salt; /* used to initialize the hash function */ -// gencode *final; /* output, code for the final hash */ -{ - ub4 loga = log2u(alen); /* log based 2 of blen */ - ub4 i; - for (i = 0; i < nkeys; i++) { - key *mykey = keys+i; - ub8 hash = lookup8(mykey->name_k, mykey->len_k, salt); - mykey->a_k = (loga > 0) ? hash>>(UB8BITS-loga) : 0; - mykey->b_k = (blen > 1) ? hash&(blen-1) : 0; - } -} - - -/* Try to apply an augmenting list */ -static int apply(bstuff *tabb, hstuff *tabh, qstuff *tabq, ub4 blen, ub4 *scramble, ub4 tail, int rollback) -// bstuff *tabb; -// hstuff *tabh; -// qstuff *tabq; -// ub4 blen; -// ub4 *scramble; -// ub4 tail; -// int rollback; /* FALSE applies augmenting path, TRUE rolls back */ -{ - ub4 hash; - key *mykey; - bstuff *pb; - ub4 child; - ub4 parent; - ub4 stabb; /* scramble[tab[b]] */ - - /* walk from child to parent */ - for (child=tail-1; child; child=parent) - { - parent = tabq[child].parent_q; /* find child's parent */ - pb = tabq[parent].b_q; /* find parent's list of siblings */ - - /* erase old hash values */ - stabb = scramble[pb->val_b]; - for (mykey=pb->list_b; mykey; mykey=mykey->nextb_k) - { - hash = mykey->a_k^stabb; - if (mykey == tabh[hash].key_h) - { /* erase hash for all of child's siblings */ - tabh[hash].key_h = (key *)0; - } - } - - /* change pb->val_b, which will change the hashes of all parent siblings */ - pb->val_b = (rollback ? tabq[child].oldval_q : tabq[child].newval_q); - - /* set new hash values */ - stabb = scramble[pb->val_b]; - for (mykey=pb->list_b; mykey; mykey=mykey->nextb_k) - { - hash = mykey->a_k^stabb; - if (rollback) - { - if (parent == 0) continue; /* root never had a hash */ - } - else if (tabh[hash].key_h) - { - /* very rare: roll back any changes */ - apply(tabb, tabh, tabq, blen, scramble, tail, TRUE); - return FALSE; /* failure, collision */ - } - tabh[hash].key_h = mykey; - } - } - return TRUE; -} - - -/* -------------------------------------------------------------------------------- -augment(): Add item to the mapping. - -Construct a spanning tree of *b*s with *item* as root, where each -parent can have all its hashes changed (by some new val_b) with -at most one collision, and each child is the b of that collision. - -I got this from Tarjan's "Data Structures and Network Algorithms". The -path from *item* to a *b* that can be remapped with no collision is -an "augmenting path". Change values of tab[b] along the path so that -the unmapped key gets mapped and the unused hash value gets used. - -Assuming 1 key per b, if m out of n hash values are still unused, -you should expect the transitive closure to cover n/m nodes before -an unused node is found. Sum(i=1..n)(n/i) is about nlogn, so expect -this approach to take about nlogn time to map all single-key b's. -------------------------------------------------------------------------------- -*/ -static int augment(bstuff *tabb, hstuff *tabh, qstuff *tabq, ub4 blen, ub4 *scramble, ub4 smax, bstuff *item, ub4 nkeys, - ub4 highwater) -// bstuff *tabb; /* stuff indexed by b */ -// hstuff *tabh; /* which key is associated with which hash, indexed by hash */ -// qstuff *tabq; /* queue of *b* values, this is the spanning tree */ -// ub4 blen; /* length of tabb */ -// ub4 *scramble; /* final hash is a^scramble[tab[b]] */ -// ub4 smax; /* highest value in scramble */ -// bstuff *item; /* &tabb[b] for the b to be mapped */ -// ub4 nkeys; /* final hash must be in 0..nkeys-1 */ -// ub4 highwater; /* a value higher than any now in tabb[].water_b */ -{ - ub4 q; /* current position walking through the queue */ - ub4 tail; /* tail of the queue. 0 is the head of the queue. */ - ub4 limit=UB1MAXVAL+1; - ub4 highhash = smax; - - /* initialize the root of the spanning tree */ - tabq[0].b_q = item; - tail = 1; - - /* construct the spanning tree by walking the queue, add children to tail */ - for (q=0; qval_b */ - - if (q == 1) - break; /* don't do transitive closure */ - - for (i=0; ilist_b; mykey; mykey=mykey->nextb_k) - { - key *childkey; - ub4 hash = mykey->a_k^scramble[i]; - - if (hash >= highhash) break; /* out of bounds */ - childkey = tabh[hash].key_h; - - if (childkey) - { - bstuff *hitb = &tabb[childkey->b_k]; - - if (childb) - { - if (childb != hitb) break; /* hit at most one child b */ - } - else - { - childb = hitb; /* remember this as childb */ - if (childb->water_b == highwater) break; /* already explored */ - } - } - } - if (mykey) continue; /* myb with i has multiple collisions */ - - /* add childb to the queue of reachable things */ - if (childb) childb->water_b = highwater; - tabq[tail].b_q = childb; - tabq[tail].newval_q = i; /* how to make parent (myb) use this hash */ - tabq[tail].oldval_q = myb->val_b; /* need this for rollback */ - tabq[tail].parent_q = q; - ++tail; - - if (!childb) - { /* found an *i* with no collisions? */ - /* try to apply the augmenting path */ - if (apply(tabb, tabh, tabq, blen, scramble, tail, FALSE)) - return TRUE; /* success, item was added to the perfect hash */ - - --tail; /* don't know how to handle such a child! */ - } - } - } - return FALSE; -} - - -/* find a mapping that makes this a perfect hash */ -static int perfect(bstuff *tabb, hstuff *tabh, qstuff *tabq, ub4 blen, ub4 smax, ub4 *scramble, ub4 nkeys) -{ - ub4 maxkeys; /* maximum number of keys for any b */ - ub4 i, j; - -#if SELOPT_DEBUG - fprintf(stderr, " blen %d smax %d nkeys %d\n", blen, smax, nkeys); -#endif - - /* clear any state from previous attempts */ - memset((void *)tabh, 0, sizeof(hstuff)*smax); - memset((void *)tabq, 0, sizeof(qstuff)*(blen+1)); - - for (maxkeys=0,i=0; i maxkeys) - maxkeys = tabb[i].listlen_b; - - /* In descending order by number of keys, map all *b*s */ - for (j=maxkeys; j>0; --j) - for (i=0; i= RETRY_INITKEY) - { - /* Try to put more bits in (A,B) to make distinct (A,B) more likely */ - if (*alen < maxalen) - { - *alen *= 2; - } - else if (*blen < smax) - { - *blen *= 2; - delete[] tabq; - delete[] *tabb; - *tabb = new bstuff[*blen]; - tabq = new qstuff[*blen+1]; - } - bad_initkey = 0; - bad_perfect = 0; - } - continue; /* two keys have same (a,b) pair */ - } - - /* Given distinct (A,B) for all keys, build a perfect hash */ - if (!perfect(*tabb, tabh, tabq, *blen, smax, scramble, nkeys)) - { - if (++bad_perfect >= RETRY_PERFECT) - { - if (*blen < smax) - { - *blen *= 2; - delete[] *tabb; - delete[] tabq; - *tabb = new bstuff[*blen]; - tabq = new qstuff[*blen+1]; - --si; /* we know this salt got distinct (A,B) */ - } - else - { - return 0; - } - bad_perfect = 0; - } - continue; - } - - break; - } - - /* free working memory */ - delete[] tabh; - delete[] tabq; - - return 1; -} - -/* ------------------------------------------------------------------------------- -Input/output type routines ------------------------------------------------------------------------------- -*/ - -/* get the list of keys */ -static void getkeys(key **keys, ub4 *nkeys, const string_map& strings) -{ - key *buf = new key[strings.size()]; - size_t i; - string_map::const_iterator s; - for (i = 0, s = strings.begin(); s != strings.end(); ++s, ++i) { - key *mykey = buf+i; - mykey->name_k = (ub1 *)s->first; - mykey->len_k = (ub4)strlen(s->first); - } - *keys = buf; - *nkeys = strings.size(); -} - - -static perfect_hash -make_perfect(const string_map& strings) -{ - ub4 nkeys; /* number of keys */ - key *keys; /* head of list of keys */ - bstuff *tab; /* table indexed by b */ - ub4 smax; /* scramble[] values in 0..smax-1, a power of 2 */ - ub4 alen; /* a in 0..alen-1, a power of 2 */ - ub4 blen; /* b in 0..blen-1, a power of 2 */ - ub8 salt; /* a parameter to the hash function */ - ub4 scramble[SCRAMBLE_LEN]; /* used in final hash function */ - int ok; - int i; - perfect_hash result; - - /* read in the list of keywords */ - getkeys(&keys, &nkeys, strings); - - /* find the hash */ - smax = ((ub4)1<syncCache = calloc(1, sizeof(SyncCache) + - count*sizeof(SyncCacheItem)); + data->syncCache = (SyncCache *) + calloc(1, sizeof(SyncCache) + count*sizeof(SyncCacheItem)); data->syncCache->allocated = count; } } @@ -95,7 +95,7 @@ static SyncCache *fetch_cache(BOOL create) // Make sure there's at least one open slot in the list. if (data->syncCache->allocated == data->syncCache->used) { data->syncCache->allocated *= 2; - data->syncCache = + data->syncCache = (SyncCache *) realloc(data->syncCache, sizeof(SyncCache) + data->syncCache->allocated * sizeof(SyncCacheItem)); } @@ -104,7 +104,7 @@ static SyncCache *fetch_cache(BOOL create) } -PRIVATE_EXTERN void _destroySyncCache(struct SyncCache *cache) +void _destroySyncCache(struct SyncCache *cache) { if (cache) free(cache); } @@ -119,7 +119,7 @@ static SyncData* id2data(id object, enum usage why) #if SUPPORT_DIRECT_THREAD_KEYS // Check per-thread single-entry fast cache for matching object BOOL fastCacheOccupied = NO; - SyncData *data = tls_get_direct(SYNC_DATA_DIRECT_KEY); + SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY); if (data) { fastCacheOccupied = YES; diff --git a/runtime/objc-typeencoding.m b/runtime/objc-typeencoding.mm similarity index 97% rename from runtime/objc-typeencoding.m rename to runtime/objc-typeencoding.mm index 4ba8eed..03255d2 100644 --- a/runtime/objc-typeencoding.m +++ b/runtime/objc-typeencoding.mm @@ -106,7 +106,7 @@ static const char * SkipFirstType (const char * type) /*********************************************************************** * encoding_getNumberOfArguments. **********************************************************************/ -PRIVATE_EXTERN unsigned int +unsigned int encoding_getNumberOfArguments(const char *typedesc) { unsigned nargs; @@ -144,7 +144,7 @@ encoding_getNumberOfArguments(const char *typedesc) /*********************************************************************** * encoding_getSizeOfArguments. **********************************************************************/ -PRIVATE_EXTERN unsigned +unsigned encoding_getSizeOfArguments(const char *typedesc) { unsigned stack_size; @@ -166,8 +166,8 @@ encoding_getSizeOfArguments(const char *typedesc) /*********************************************************************** * encoding_getArgumentInfo. **********************************************************************/ -PRIVATE_EXTERN unsigned int -encoding_getArgumentInfo(const char *typedesc, int arg, +unsigned int +encoding_getArgumentInfo(const char *typedesc, unsigned int arg, const char **type, int *offset) { unsigned nargs = 0; @@ -270,7 +270,7 @@ encoding_getArgumentInfo(const char *typedesc, int arg, } -PRIVATE_EXTERN void +void encoding_getReturnType(const char *t, char *dst, size_t dst_len) { size_t len; @@ -292,7 +292,7 @@ encoding_getReturnType(const char *t, char *dst, size_t dst_len) * encoding_copyReturnType. Returns the method's return type string * on the heap. **********************************************************************/ -PRIVATE_EXTERN char * +char * encoding_copyReturnType(const char *t) { size_t len; @@ -303,14 +303,14 @@ encoding_copyReturnType(const char *t) end = SkipFirstType(t); len = end - t; - result = malloc(len + 1); + result = (char *)malloc(len + 1); strncpy(result, t, len); result[len] = '\0'; return result; } -PRIVATE_EXTERN void +void encoding_getArgumentType(const char *t, unsigned int index, char *dst, size_t dst_len) { @@ -342,7 +342,7 @@ encoding_getArgumentType(const char *t, unsigned int index, * encoding_copyArgumentType. Returns a single argument's type string * on the heap. Argument 0 is `self`; argument 1 is `_cmd`. **********************************************************************/ -PRIVATE_EXTERN char * +char * encoding_copyArgumentType(const char *t, unsigned int index) { size_t len; @@ -358,7 +358,7 @@ encoding_copyArgumentType(const char *t, unsigned int index) end = SkipFirstType(t); len = end - t; - result = malloc(len + 1); + result = (char *)malloc(len + 1); strncpy(result, t, len); result[len] = '\0'; return result; diff --git a/runtime/objc-weak.h b/runtime/objc-weak.h index e40da16..3dfe4dc 100644 --- a/runtime/objc-weak.h +++ b/runtime/objc-weak.h @@ -22,7 +22,7 @@ */ #include -#import "objc-config.h" +#include "objc-config.h" __BEGIN_DECLS diff --git a/runtime/objc-weak.mm b/runtime/objc-weak.mm index f8a34b4..587b4b8 100644 --- a/runtime/objc-weak.mm +++ b/runtime/objc-weak.mm @@ -21,14 +21,14 @@ * @APPLE_LICENSE_HEADER_END@ */ -#import "objc-weak.h" -#import "objc-os.h" -#import "objc-private.h" +#include "objc-weak.h" +#include "objc-os.h" +#include "objc-private.h" -#import -#import -#import -#import +#include +#include +#include +#include template struct WeakAllocator { @@ -90,19 +90,19 @@ public: // // Accessors // - inline Range& range() { return *this; } - inline void *address() const { return _address; } - inline void *end() const { return _end; } - inline const size_t size() const { return (uintptr_t)_end - (uintptr_t)_address; } - inline void set_address(void *address) { _address = address; } - inline void set_end(void *end) { _end = end; } - inline void set_size(size_t size) { _end = displace(_address, size); } - inline void set_range(void *address, void *end) { _address = address; _end = end; } - inline void set_range(void *address, size_t size) { _address = address; _end = displace(address, size); } - inline void set_range(Range range) { _address = range.address(); _end = range.end(); } - inline void adjust_address(intptr_t delta) { _address = displace(_address, delta); } - inline void adjust_end(intptr_t delta) { _end = displace(_end, delta); } - inline void adjust(intptr_t delta) { _address = displace(_address, delta), _end = displace(_end, delta); } + inline Range& range() { return *this; } + inline void *address() const { return _address; } + inline void *end() const { return _end; } + inline size_t size() const { return (uintptr_t)_end - (uintptr_t)_address; } + inline void set_address(void *address) { _address = address; } + inline void set_end(void *end) { _end = end; } + inline void set_size(size_t size) { _end = displace(_address, size); } + inline void set_range(void *address, void *end) { _address = address; _end = end; } + inline void set_range(void *address, size_t size) { _address = address; _end = displace(address, size); } + inline void set_range(Range range) { _address = range.address(); _end = range.end(); } + inline void adjust_address(intptr_t delta) { _address = displace(_address, delta); } + inline void adjust_end(intptr_t delta) { _end = displace(_end, delta); } + inline void adjust(intptr_t delta) { _address = displace(_address, delta), _end = displace(_end, delta); } // @@ -119,22 +119,22 @@ public: // Returns true if the specified address is in range. // This form reduces the number of branches. Works well with invariant lo and hi. // - static inline const bool in_range(void *lo, void *hi, void *address) { + static inline bool in_range(void *lo, void *hi, void *address) { uintptr_t lo_as_int = (uintptr_t)lo; uintptr_t hi_as_int = (uintptr_t)hi; uintptr_t diff = hi_as_int - lo_as_int; uintptr_t address_as_int = (uintptr_t)address; return (address_as_int - lo_as_int) < diff; } - inline const bool in_range(void *address) const { return in_range(_address, _end, address); } + inline bool in_range(void *address) const { return in_range(_address, _end, address); } // // operator == // // Used to locate entry in list or hash table (use is_range for exaxt match.) - inline const bool operator==(const Range *range) const { return _address == range->_address; } - inline const bool operator==(const Range &range) const { return _address == range._address; } + inline bool operator==(const Range *range) const { return _address == range->_address; } + inline bool operator==(const Range &range) const { return _address == range._address; } // @@ -142,7 +142,7 @@ public: // // Return true if the ranges are equivalent. // - inline const bool is_range(const Range& range) const { return _address == range._address && _end == range._end; } + inline bool is_range(const Range& range) const { return _address == range._address && _end == range._end; } // @@ -358,9 +358,9 @@ static void weak_entry_remove_no_lock(weak_table_t *weak_table, weak_entry_t *en do { index++; if (index == table_size) index = 0; if (!weak_entries[index].referent) return; - weak_entry_t entry = weak_entries[index]; + weak_entry_t slot = weak_entries[index]; weak_entries[index].referent = NULL; - weak_entry_insert_no_lock(weak_table, &entry); + weak_entry_insert_no_lock(weak_table, &slot); } while (index != hash_index); } @@ -418,7 +418,7 @@ static weak_entry_t *weak_entry_for_referent(weak_table_t *weak_table, id refere // Does not zero referrer. // fixme currently requires old referent value to be passed in (lame) // fixme unregistration should be automatic if referrer is collected -PRIVATE_EXTERN void weak_unregister_no_lock(weak_table_t *weak_table, id referent, id *referrer) +void weak_unregister_no_lock(weak_table_t *weak_table, id referent, id *referrer) { weak_entry_t *entry; @@ -434,7 +434,8 @@ PRIVATE_EXTERN void weak_unregister_no_lock(weak_table_t *weak_table, id referen // value not change. } -PRIVATE_EXTERN void + +void arr_clear_deallocating(weak_table_t *weak_table, id referent) { { weak_entry_t *entry = weak_entry_for_referent(weak_table, referent); @@ -444,7 +445,7 @@ arr_clear_deallocating(weak_table_t *weak_table, id referent) { return; } // zero out references - for (int i = 0; i < entry->referrers.num_allocated; ++i) { + for (size_t i = 0; i < entry->referrers.num_allocated; ++i) { id *referrer = entry->referrers.refs[i].referrer; if (referrer) { if (*referrer == referent) { @@ -462,7 +463,7 @@ arr_clear_deallocating(weak_table_t *weak_table, id referent) { } -PRIVATE_EXTERN id weak_register_no_lock(weak_table_t *weak_table, id referent, id *referrer) { +id weak_register_no_lock(weak_table_t *weak_table, id referent, id *referrer) { if (referent && !OBJC_IS_TAGGED_PTR(referent)) { // ensure that the referenced object is viable BOOL (*allowsWeakReference)(id, SEL) = (BOOL(*)(id, SEL)) @@ -503,7 +504,7 @@ PRIVATE_EXTERN id weak_register_no_lock(weak_table_t *weak_table, id referent, i // Automated Retain Release (ARR) support -PRIVATE_EXTERN id +id arr_read_weak_reference(weak_table_t *weak_table, id *referrer) { id referent; // find entry and mark that it needs retaining diff --git a/runtime/objc.h b/runtime/objc.h index 455858d..26bd0a9 100644 --- a/runtime/objc.h +++ b/runtime/objc.h @@ -39,23 +39,42 @@ typedef struct objc_object { } *id; -typedef struct objc_selector *SEL; -typedef id (*IMP)(id, SEL, ...); +typedef struct objc_selector *SEL; + +#if !OBJC_OLD_DISPATCH_PROTOTYPES +typedef void (*IMP)(void /* id, SEL, ... */ ); +#else +typedef id (*IMP)(id, SEL, ...); +#endif + +#define OBJC_BOOL_DEFINED + typedef signed char BOOL; // BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C" // even if -funsigned-char is used. -#define OBJC_BOOL_DEFINED - -#define YES (BOOL)1 -#define NO (BOOL)0 +#if __has_feature(objc_bool) +#define YES __objc_yes +#define NO __objc_no +#else +#define YES ((BOOL)1) +#define NO ((BOOL)0) +#endif #ifndef Nil -#define Nil __DARWIN_NULL /* id of Nil class */ +# if __has_feature(cxx_nullptr) +# define Nil nullptr +# else +# define Nil __DARWIN_NULL +# endif #endif #ifndef nil -#define nil __DARWIN_NULL /* id of Nil instance */ +# if __has_feature(cxx_nullptr) +# define nil nullptr +# else +# define nil __DARWIN_NULL +# endif #endif #if ! (defined(__OBJC_GC__) || __has_feature(objc_arr)) @@ -67,6 +86,7 @@ typedef signed char BOOL; #define __autoreleasing /* empty */ #endif + OBJC_EXPORT const char *sel_getName(SEL sel) __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); OBJC_EXPORT SEL sel_registerName(const char *str) @@ -81,47 +101,21 @@ OBJC_EXPORT SEL sel_getUid(const char *str) __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); -#ifndef CF_CONSUMED -#if __has_feature(attribute_cf_consumed) -#define CF_CONSUMED __attribute__((cf_consumed)) -#else -#define CF_CONSUMED -#endif -#endif - -#ifndef CF_RETURNS_NOT_RETAINED -#if __has_feature(attribute_cf_not_retained) -#define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained)) -#else -#define CF_RETURNS_NOT_RETAINED -#endif -#endif +// Obsolete ARC conversions. Deprecation forthcoming. +// Use CFBridgingRetain, CFBridgingRelease, and __bridge casts instead. -#ifndef NS_RETURNS_RETAINED -#if __has_feature(attribute_ns_returns_retained) -#define NS_RETURNS_RETAINED __attribute__((ns_returns_retained)) -#else -#define NS_RETURNS_RETAINED -#endif -#endif +typedef const void* objc_objectptr_t; -#ifndef NS_RETURNS_NOT_RETAINED -#if __has_feature(attribute_ns_returns_not_retained) -#define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) +#if __has_feature(objc_arc) +# define objc_retainedObject(o) ((__bridge_transfer id)(objc_objectptr_t)(o)) +# define objc_unretainedObject(o) ((__bridge id)(objc_objectptr_t)(o)) +# define objc_unretainedPointer(o) ((__bridge objc_objectptr_t)(id)(o)) #else -#define NS_RETURNS_NOT_RETAINED -#endif +# define objc_retainedObject(o) ((id)(objc_objectptr_t)(o)) +# define objc_unretainedObject(o) ((id)(objc_objectptr_t)(o)) +# define objc_unretainedPointer(o) ((objc_objectptr_t)(id)(o)) #endif -typedef const void* objc_objectptr_t; - -OBJC_EXPORT NS_RETURNS_RETAINED id objc_retainedObject(objc_objectptr_t CF_CONSUMED pointer) - __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0); -OBJC_EXPORT NS_RETURNS_NOT_RETAINED id objc_unretainedObject(objc_objectptr_t pointer) - __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0); -OBJC_EXPORT CF_RETURNS_NOT_RETAINED objc_objectptr_t objc_unretainedPointer(id object) - __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0); - #if !__OBJC2__ diff --git a/runtime/objcrt.h b/runtime/objcrt.h index b399545..4e6fefd 100644 --- a/runtime/objcrt.h +++ b/runtime/objcrt.h @@ -1,3 +1,6 @@ +#ifndef _OBJC_RT_H_ +#define _OBJC_RT_H_ + #include @@ -18,3 +21,5 @@ typedef struct { OBJC_EXPORT void *_objc_init_image(HMODULE image, const objc_sections *sects); OBJC_EXPORT void _objc_load_image(HMODULE image, void *hinfo); OBJC_EXPORT void _objc_unload_image(HMODULE image, void *hinfo); + +#endif diff --git a/runtime/runtime.h b/runtime/runtime.h index 4ad1728..1979dee 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -120,9 +120,11 @@ OBJC_EXPORT id objc_lookUpClass(const char *name) OBJC_EXPORT id objc_getRequiredClass(const char *name) __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); OBJC_EXPORT Class objc_getFutureClass(const char *name) - __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0) + OBJC_ARC_UNAVAILABLE; OBJC_EXPORT void objc_setFutureClass(Class cls, const char *name) - __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); + __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0) + OBJC_ARC_UNAVAILABLE; OBJC_EXPORT int objc_getClassList(Class *buffer, int bufferCount) __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0); OBJC_EXPORT Class *objc_copyClassList(unsigned int *outCount) @@ -322,9 +324,9 @@ OBJC_EXPORT void objc_setEnumerationMutationHandler(void (*handler)(id)) OBJC_EXPORT void objc_setForwardHandler(void *fwd, void *fwd_stret) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); -OBJC_EXPORT IMP imp_implementationWithBlock(void *block) +OBJC_EXPORT IMP imp_implementationWithBlock(id block) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); -OBJC_EXPORT void *imp_getBlock(IMP anImp) +OBJC_EXPORT id imp_getBlock(IMP anImp) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); OBJC_EXPORT BOOL imp_removeBlock(IMP anImp) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); diff --git a/test/ARRBase.h b/test/ARRBase.h index be8bdf4..81b8fed 100644 --- a/test/ARRBase.h +++ b/test/ARRBase.h @@ -9,7 +9,7 @@ #import @interface ARRBase : NSObject -@property double number; +@property long number; @property(retain) id object; @property void *pointer; @property(weak) __weak id delegate; diff --git a/test/ARRBase.m b/test/ARRBase.m index 5fbe357..737f1c6 100644 --- a/test/ARRBase.m +++ b/test/ARRBase.m @@ -11,7 +11,7 @@ #if 1 @interface ARRBase () { @private - double number; + long number; id object; void *pointer; __weak id delegate; diff --git a/test/ARRLayouts.m b/test/ARRLayouts.m index 45c6ac7..5db5213 100644 --- a/test/ARRLayouts.m +++ b/test/ARRLayouts.m @@ -1,19 +1,16 @@ -// clang -objc-arr -print-ivar-layout /* -TEST_DISABLED -TEST_CONFIG GC=0 CC=clang +TEST_CONFIG MEM=arc CC=clang TEST_BUILD - clang -c $DIR/MRRBase.m $DIR/MRRARR.m - libtool -static MRRBase.o MRRARR.o -framework Foundation -o libMRR.a - clang -fobjc-arr -c $DIR/ARRBase.m $DIR/ARRMRR.m - libtool -static ARRBase.o ARRMRR.o -framework Foundation -o libARR.a - $C{COMPILE} -fobjc-arr $DIR/ARRLayouts.m -L . -lMRR -lARR -framework Foundation -o ARRLayouts.out + $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 @@ -27,32 +24,34 @@ END void printlayout(const char *name, const uint8_t *layout) { - printf("%s: ", name); + if (! getenv("VERBOSE")) return; + + testprintf("%s: ", name); if (!layout) { - printf("NULL\n"); + fprintf(stderr, "NULL\n"); return; } const uint8_t *c; for (c = layout; *c; c++) { - printf("%02x ", *c); + fprintf(stderr, "%02x ", *c); } - printf("00\n"); + fprintf(stderr, "00\n"); } @implementation NSObject (Layouts) + (const char *)strongLayout { const uint8_t *layout = class_getIvarLayout(self); - // printlayout("strong", layout); + printlayout("strong", layout); return (const char *)layout; } + (const char *)weakLayout { const uint8_t *weakLayout = class_getWeakIvarLayout(self); - // printlayout("weak", weakLayout); + printlayout("weak", weakLayout); return (const char *)weakLayout; } @@ -64,14 +63,14 @@ void printlayout(const char *name, const uint8_t *layout) int main (int argc __unused, const char * argv[] __unused) { // Under ARR, layout strings are relative to the class' own ivars. - assert(strcmp([ARRBase strongLayout], "\x11\x20") == 0); - assert(strcmp([ARRBase weakLayout], "\x31") == 0); - assert([MRRBase strongLayout] == NULL); - assert([MRRBase weakLayout] == NULL); - assert(strcmp([ARRMRR strongLayout], "\x01") == 0); - assert([ARRMRR weakLayout] == NULL); - assert([MRRARR strongLayout] == NULL); - assert([MRRARR weakLayout] == NULL); + 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]; @@ -82,18 +81,18 @@ int main (int argc __unused, const char * argv[] __unused) { am.number = M_PI; object_setIvar(am, [ARRMRR instanceVariable:"object"], am_description); - assert(CFGetRetainCount(objc_unretainedPointer(am_description)) == 1); + testassert(CFGetRetainCount(objc_unretainedPointer(am_description)) == 1); am.pointer = @selector(ARRMRR); object_setIvar(am, [ARRMRR instanceVariable:"delegate"], ma); - assert(CFGetRetainCount(objc_unretainedPointer(ma)) == 1); + testassert(CFGetRetainCount(objc_unretainedPointer(ma)) == 1); ma.number = M_E; object_setIvar(ma, [MRRARR instanceVariable:"object"], ma_description); - assert(CFGetRetainCount(objc_unretainedPointer(ma_description)) == 2); + testassert(CFGetRetainCount(objc_unretainedPointer(ma_description)) == 2); ma.pointer = @selector(MRRARR); ma.delegate = am; object_setIvar(ma, [MRRARR instanceVariable:"delegate"], am); - assert(CFGetRetainCount(objc_unretainedPointer(am)) == 1); + testassert(CFGetRetainCount(objc_unretainedPointer(am)) == 1); succeed(__FILE__); return 0; diff --git a/test/Makefile b/test/Makefile index acf0082..b208678 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,17 +4,17 @@ all: # default-arch but otherwise comprehensive test for buildbot buildbot: - perl test.pl $(MAKEFLAGS) GC=0,1 CC=clang,llvm-gcc-4.2,gcc-4.2 LANGUAGE=objc,objc++ + perl test.pl $(MAKEFLAGS) MEM=mrc,arc,gc CC=clang,llvm-gcc-4.2 LANGUAGE=objc,objc++ # comprehensive tests mac macos macosx: - perl test.pl $(MAKEFLAGS) ARCH=x86_64,i386 GC=0,1 CC=clang,llvm-gcc-4.2,gcc-4.2 LANGUAGE=objc,objc++ + perl test.pl $(MAKEFLAGS) ARCH=x86_64,i386 MEM=mrc,arc,gc CC=clang,llvm-gcc-4.2 LANGUAGE=objc,objc++ iphonesimulator: - perl test.pl $(MAKEFLAGS) ARCH=i386 SDK=iphonesimulator GC=0 CC=clang,llvm-gcc-4.2,gcc-4.2 LANGUAGE=objc,objc++ + perl test.pl $(MAKEFLAGS) ARCH=i386 SDK=iphonesimulator MEM=mrc,arc CC=clang,llvm-gcc-4.2 LANGUAGE=objc,objc++ iphoneos: - perl test.pl $(MAKEFLAGS) ARCH=armv6,armv7 SDK=iphoneos GC=0 CC=clang,llvm-gcc-4.2,gcc-4.2 LANGUAGE=objc,objc++ + perl test.pl $(MAKEFLAGS) ARCH=armv6,armv7 SDK=iphoneos MEM=mrc,arc CC=clang,llvm-gcc-4.2 LANGUAGE=objc,objc++ clean: @ perl test.pl clean diff --git a/test/accessors.m b/test/accessors.m index 3acd7c7..9111e64 100644 --- a/test/accessors.m +++ b/test/accessors.m @@ -16,21 +16,23 @@ @end typedef struct { - id isa; - NSString *_value; + void *isa; + void *_value; // _object is at the last optimized property offset - id _object __attribute__((aligned(64))); + void *_object __attribute__((aligned(64))); } TestDefs; @implementation Test // Question: why can't this code be automatically generated? +#if !__has_feature(objc_arc) - (void)dealloc { self.value = nil; self.object = nil; [super dealloc]; } +#endif - (Class)cls { return objc_getProperty(self, _cmd, 0, YES); } @@ -47,29 +49,29 @@ typedef struct { @end int main() { - NSAutoreleasePool *pool = [NSAutoreleasePool new]; - - NSMutableString *value = [NSMutableString stringWithUTF8String:"test"]; - id object = [NSNumber numberWithInt:11]; - Test *t = [[Test new] autorelease]; - t.value = value; - [value setString:@"yuck"]; // mutate the string. - testassert(t.value != value); // must copy, since it was mutable. - testassert([t.value isEqualToString:@"test"]); - - Class testClass = [Test class]; - Class cls = t.cls; - testassert(testClass == cls); - cls = t.cls; - testassert(testClass == cls); - - t.object = object; - t.object = object; - - // NSLog(@"t.object = %@, t.value = %@", t.object, t.value); - // NSLog(@"t.object = %@, t.value = %@", t.object, t.value); // second call will optimized getters. + PUSH_POOL { - [pool drain]; + NSMutableString *value = [NSMutableString stringWithUTF8String:"test"]; + id object = [NSNumber numberWithInt:11]; + Test *t = AUTORELEASE([Test new]); + t.value = value; + [value setString:@"yuck"]; // mutate the string. + testassert(t.value != value); // must copy, since it was mutable. + testassert([t.value isEqualToString:@"test"]); + + Class testClass = [Test class]; + Class cls = t.cls; + testassert(testClass == cls); + cls = t.cls; + testassert(testClass == cls); + + t.object = object; + t.object = object; + + // NSLog(@"t.object = %@, t.value = %@", t.object, t.value); + // NSLog(@"t.object = %@, t.value = %@", t.object, t.value); // second call will optimized getters. + + } POP_POOL; succeed(__FILE__); diff --git a/test/accessors2.m b/test/accessors2.m new file mode 100644 index 0000000..3c21b9f --- /dev/null +++ b/test/accessors2.m @@ -0,0 +1,143 @@ +// TEST_CONFIG MEM=mrc,arc + +#include "test.h" +#include +#include + +#include "testroot.i" + +@interface Base : TestRoot { + @public + id ivar; +} +@end +@implementation Base @end + +int main() +{ + SEL _cmd = @selector(foo); + Base *o = [Base new]; + ptrdiff_t offset = ivar_getOffset(class_getInstanceVariable([Base class], "ivar")); + testassert(offset == sizeof(id)); + + TestRoot *value = [TestRoot new]; + + // fixme test atomicity + + // Original setter API + + testprintf("original nonatomic retain\n"); + o->ivar = nil; + TestRootRetain = 0; + TestRootCopyWithZone = 0; + TestRootMutableCopyWithZone = 0; + objc_setProperty(o, _cmd, offset, value, NO/*atomic*/, NO/*copy*/); + testassert(TestRootRetain == 1); + testassert(TestRootCopyWithZone == 0); + testassert(TestRootMutableCopyWithZone == 0); + testassert(o->ivar == value); + + testprintf("original atomic retain\n"); + o->ivar = nil; + TestRootRetain = 0; + TestRootCopyWithZone = 0; + TestRootMutableCopyWithZone = 0; + objc_setProperty(o, _cmd, offset, value, YES/*atomic*/, NO/*copy*/); + testassert(TestRootRetain == 1); + testassert(TestRootCopyWithZone == 0); + testassert(TestRootMutableCopyWithZone == 0); + testassert(o->ivar == value); + + testprintf("original nonatomic copy\n"); + o->ivar = nil; + TestRootRetain = 0; + TestRootCopyWithZone = 0; + TestRootMutableCopyWithZone = 0; + objc_setProperty(o, _cmd, offset, value, NO/*atomic*/, YES/*copy*/); + testassert(TestRootRetain == 0); + testassert(TestRootCopyWithZone == 1); + testassert(TestRootMutableCopyWithZone == 0); + testassert(o->ivar && o->ivar != value); + + testprintf("original atomic copy\n"); + o->ivar = nil; + TestRootRetain = 0; + TestRootCopyWithZone = 0; + TestRootMutableCopyWithZone = 0; + objc_setProperty(o, _cmd, offset, value, YES/*atomic*/, YES/*copy*/); + testassert(TestRootRetain == 0); + testassert(TestRootCopyWithZone == 1); + testassert(TestRootMutableCopyWithZone == 0); + testassert(o->ivar && o->ivar != value); + + testprintf("original nonatomic mutablecopy\n"); + o->ivar = nil; + TestRootRetain = 0; + TestRootCopyWithZone = 0; + TestRootMutableCopyWithZone = 0; + objc_setProperty(o, _cmd, offset, value, NO/*atomic*/, 2/*copy*/); + testassert(TestRootRetain == 0); + testassert(TestRootCopyWithZone == 0); + testassert(TestRootMutableCopyWithZone == 1); + testassert(o->ivar && o->ivar != value); + + testprintf("original atomic mutablecopy\n"); + o->ivar = nil; + TestRootRetain = 0; + TestRootCopyWithZone = 0; + TestRootMutableCopyWithZone = 0; + objc_setProperty(o, _cmd, offset, value, YES/*atomic*/, 2/*copy*/); + testassert(TestRootRetain == 0); + testassert(TestRootCopyWithZone == 0); + testassert(TestRootMutableCopyWithZone == 1); + testassert(o->ivar && o->ivar != value); + + + // Optimized setter API + + testprintf("optimized nonatomic retain\n"); + o->ivar = nil; + TestRootRetain = 0; + TestRootCopyWithZone = 0; + TestRootMutableCopyWithZone = 0; + objc_setProperty_nonatomic(o, _cmd, value, offset); + testassert(TestRootRetain == 1); + testassert(TestRootCopyWithZone == 0); + testassert(TestRootMutableCopyWithZone == 0); + testassert(o->ivar == value); + + testprintf("optimized atomic retain\n"); + o->ivar = nil; + TestRootRetain = 0; + TestRootCopyWithZone = 0; + TestRootMutableCopyWithZone = 0; + objc_setProperty_atomic(o, _cmd, value, offset); + testassert(TestRootRetain == 1); + testassert(TestRootCopyWithZone == 0); + testassert(TestRootMutableCopyWithZone == 0); + testassert(o->ivar == value); + + testprintf("optimized nonatomic copy\n"); + o->ivar = nil; + TestRootRetain = 0; + TestRootCopyWithZone = 0; + TestRootMutableCopyWithZone = 0; + objc_setProperty_nonatomic_copy(o, _cmd, value, offset); + testassert(TestRootRetain == 0); + testassert(TestRootCopyWithZone == 1); + testassert(TestRootMutableCopyWithZone == 0); + testassert(o->ivar && o->ivar != value); + + testprintf("optimized atomic copy\n"); + o->ivar = nil; + TestRootRetain = 0; + TestRootCopyWithZone = 0; + TestRootMutableCopyWithZone = 0; + objc_setProperty_atomic_copy(o, _cmd, value, offset); + testassert(TestRootRetain == 0); + testassert(TestRootCopyWithZone == 1); + testassert(TestRootMutableCopyWithZone == 0); + testassert(o->ivar && o->ivar != value); + + succeed(__FILE__); +} diff --git a/test/addMethod.m b/test/addMethod.m index 3c4f6f0..0b1f96c 100644 --- a/test/addMethod.m +++ b/test/addMethod.m @@ -1,14 +1,11 @@ // TEST_CONFIG #include "test.h" +#include "testroot.i" #include -@interface Super { @public id isa; } @end +@interface Super : TestRoot @end @implementation Super -+(void)initialize { } -+class { return self; } -+(id) new { return class_createInstance(self, 0); } - -(int)superMethod { return 0; } -(int)bothMethod { return 0; } @end @@ -48,58 +45,58 @@ int main() IMP imp; // class_addMethod doesn't replace existing implementations - ok = class_addMethod([Super class], @selector(superMethod), &fn, NULL); + ok = class_addMethod([Super class], @selector(superMethod), (IMP)fn, NULL); testassert(!ok); testassert(class_getMethodImplementation([Super class], @selector(superMethod)) == superMethodFromSuper); // class_addMethod does override superclass implementations - ok = class_addMethod([Sub class], @selector(superMethod), &fn, NULL); + ok = class_addMethod([Sub class], @selector(superMethod), (IMP)fn, NULL); testassert(ok); - testassert(class_getMethodImplementation([Sub class], @selector(superMethod)) == &fn); + testassert(class_getMethodImplementation([Sub class], @selector(superMethod)) == (IMP)fn); // class_addMethod does add root implementations - ok = class_addMethod([Super class], @selector(superMethodNew2), &fn, NULL); + ok = class_addMethod([Super class], @selector(superMethodNew2), (IMP)fn, NULL); testassert(ok); - testassert(class_getMethodImplementation([Super class], @selector(superMethodNew2)) == &fn); - testassert(class_getMethodImplementation([Sub class], @selector(superMethodNew2)) == &fn); + testassert(class_getMethodImplementation([Super class], @selector(superMethodNew2)) == (IMP)fn); + testassert(class_getMethodImplementation([Sub class], @selector(superMethodNew2)) == (IMP)fn); // class_replaceMethod does add new implementations, // returning NULL if super has an implementation - imp = class_replaceMethod([Sub2 class], @selector(superMethod), &fn, NULL); + imp = class_replaceMethod([Sub2 class], @selector(superMethod), (IMP)fn, NULL); testassert(imp == NULL); - testassert(class_getMethodImplementation([Sub2 class], @selector(superMethod)) == fn); + 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), &fn, NULL); + imp = class_replaceMethod([Sub2 class], @selector(subMethodNew), (IMP)fn, NULL); testassert(imp == NULL); - testassert(class_getMethodImplementation([Sub2 class], @selector(subMethodNew)) == fn); + 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), &fn, NULL); + imp = class_replaceMethod([Super class], @selector(superMethodNew), (IMP)fn, NULL); testassert(imp == NULL); - testassert(class_getMethodImplementation([Super class], @selector(superMethodNew)) == fn); + 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), &fn, NULL); + imp = class_replaceMethod([Sub2 class], @selector(subMethod), (IMP)fn, NULL); testassert(imp == subMethodFromSub2); - testassert(class_getMethodImplementation([Sub2 class], @selector(subMethod)) == fn); + 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), &fn, NULL); + imp = class_replaceMethod([Sub2 class], @selector(bothMethod), (IMP)fn, NULL); testassert(imp == bothMethodFromSub2); - testassert(class_getMethodImplementation([Sub2 class], @selector(bothMethod)) == fn); + 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), &fn, NULL); + imp = class_replaceMethod([Super class], @selector(superMethod), (IMP)fn, NULL); testassert(imp == superMethodFromSuper); - testassert(class_getMethodImplementation([Super class], @selector(superMethod)) == fn); + testassert(class_getMethodImplementation([Super class], @selector(superMethod)) == (IMP)fn); // fixme actually try calling them diff --git a/test/addProtocol.m b/test/addProtocol.m index c483a49..14cbee8 100644 --- a/test/addProtocol.m +++ b/test/addProtocol.m @@ -22,7 +22,7 @@ END int main() { Protocol *proto, *proto2; - Protocol * const *protolist; + Protocol * __unsafe_unretained *protolist; struct objc_method_description *desclist; objc_property_t *proplist; unsigned int count; @@ -96,7 +96,7 @@ int main() testassert(protolist[0] == @protocol(SuperProto) && protolist[1] == @protocol(SuperProto2) && protolist[2] == proto2); - free((void*)protolist); + free(protolist); testassert(protocol_conformsToProtocol(proto, proto2)); testassert(protocol_conformsToProtocol(proto, @protocol(SuperProto))); @@ -185,8 +185,8 @@ int main() // NULL protocols ignored - protocol_addProtocol((Protocol *)1, NULL); - protocol_addProtocol(NULL, (Protocol *)1); + protocol_addProtocol((Protocol *)objc_unretainedObject((void*)1), NULL); + protocol_addProtocol(NULL, (Protocol *)objc_unretainedObject((void*)1)); protocol_addProtocol(NULL, NULL); protocol_addMethodDescription(NULL, @selector(foo), "", YES, YES); diff --git a/test/arr-cast.m b/test/arr-cast.m new file mode 100644 index 0000000..ddb6e21 --- /dev/null +++ b/test/arr-cast.m @@ -0,0 +1,20 @@ +// TEST_CONFIG + +#include "test.h" + +// objc.h redefines these calls into bridge casts. +// This test verifies that the function implementations are exported. +__BEGIN_DECLS +extern void *retainedObject(void *arg) __asm__("_objc_retainedObject"); +extern void *unretainedObject(void *arg) __asm__("_objc_unretainedObject"); +extern void *unretainedPointer(void *arg) __asm__("_objc_unretainedPointer"); +__END_DECLS + +int main() +{ + void *p = (void*)&main; + testassert(p == retainedObject(p)); + testassert(p == unretainedObject(p)); + testassert(p == unretainedPointer(p)); + succeed(__FILE__); +} diff --git a/test/arr-weak.m b/test/arr-weak.m index aeb94e6..2d6b6ce 100644 --- a/test/arr-weak.m +++ b/test/arr-weak.m @@ -1,8 +1,15 @@ -// TEST_CFLAGS -framework Foundation -// TEST_CONFIG GC=0 +// TEST_CONFIG MEM=mrc +// TEST_CRASHES +/* +TEST_RUN_OUTPUT +objc\[\d+\]: cannot form weak reference to instance \(0x[0-9a-f]+\) of class Crash +CRASHED: SIG(ILL|TRAP) +END +*/ #include "test.h" -#include + +#include static id weak; static id weak2; @@ -14,11 +21,6 @@ static bool did_dealloc; testassert(weak == self); testassert(weak2 == self); - testprintf("Weak store fails while deallocating\n"); - id result = objc_storeWeak(&weak, self); - testassert(result == NULL); - testassert(weak == NULL); - testprintf("Weak references clear during super dealloc\n"); testassert(weak2 != NULL); [super dealloc]; @@ -28,6 +30,19 @@ static bool did_dealloc; } @end +@interface Crash : NSObject @end +@implementation Crash +-(void)dealloc { + testassert(weak == self); + testassert(weak2 == self); + + testprintf("Weak store crashes while deallocating\n"); + objc_storeWeak(&weak, self); + fail("objc_storeWeak of deallocating value should have crashed"); + [super dealloc]; +} +@end + int main() { Test *obj = [Test new]; @@ -70,5 +85,15 @@ int main() testassert(weak == NULL); testassert(weak2 == NULL); - succeed(__FILE__); + Crash *obj3 = [Crash new]; + result = objc_storeWeak(&weak, obj3); + testassert(result == obj3); + testassert(weak == obj3); + + result = objc_storeWeak(&weak2, obj3); + testassert(result == obj3); + testassert(weak2 == obj3); + + [obj3 release]; + fail("should have crashed in -[Crash dealloc]"); } diff --git a/test/association-cf.m b/test/association-cf.m index bed3b40..5930c5c 100644 --- a/test/association-cf.m +++ b/test/association-cf.m @@ -5,22 +5,34 @@ #include "test.h" +#if __has_feature(objc_arc) + +int main() +{ + testwarn("rdar://11368528 confused by Foundation"); + succeed(__FILE__); +} + +#else + int main() { // rdar://6164781 setAssociatedObject on pure-CF object crashes LP64 id obj; - CFArrayRef array = CFArrayCreate(0, 0, 0, 0); + id array = objc_retainedObject(CFArrayCreate(0, 0, 0, 0)); testassert(array); testassert(! objc_getClass("NSCFArray")); - objc_setAssociatedObject((id)array, (void*)1, (id)array, OBJC_ASSOCIATION_ASSIGN); + objc_setAssociatedObject(array, (void*)1, array, OBJC_ASSOCIATION_ASSIGN); - obj = objc_getAssociatedObject((id)array, (void*)1); - testassert(obj == (id)array); + obj = objc_getAssociatedObject(array, (void*)1); + testassert(obj == array); - CFRelease(array); + RELEASE_VAR(array); succeed(__FILE__); } + +#endif diff --git a/test/association.m b/test/association.m index 5d1536a..429a1c0 100644 --- a/test/association.m +++ b/test/association.m @@ -1,10 +1,11 @@ -// TEST_CFLAGS -framework Foundation +// TEST_CONFIG #include "test.h" -#include +#include #include static int values; +static int supers; static int subs; static const char *key = "key"; @@ -14,6 +15,9 @@ static const char *key = "key"; @interface Super : NSObject @end @interface Sub : NSObject @end +@interface Super2 : NSObject @end +@interface Sub2 : NSObject @end + @implementation Super -(id) init { @@ -21,20 +25,71 @@ static const char *key = "key"; id value = [Value new]; objc_setAssociatedObject(self, &key, value, OBJC_ASSOCIATION_RETAIN); - [value release]; + RELEASE_VAR(value); object_setClass(self, [Sub class]); return self; } +-(void) dealloc +{ + supers++; + SUPER_DEALLOC(); +} +-(void) finalize +{ + supers++; + [super finalize]; +} + @end @implementation Sub -(void) dealloc { subs++; - [super dealloc]; + SUPER_DEALLOC(); +} +-(void) finalize +{ + subs++; + [super finalize]; +} +@end + +@implementation Super2 +-(id) init +{ + // rdar://9617109 don't lose associations after isa swizzling + + id value = [Value new]; + object_setClass(self, [Sub2 class]); + objc_setAssociatedObject(self, &key, value, OBJC_ASSOCIATION_RETAIN); + RELEASE_VAR(value); + object_setClass(self, [Super2 class]); + + return self; +} + +-(void) dealloc +{ + supers++; + SUPER_DEALLOC(); +} +-(void) finalize +{ + supers++; + [super finalize]; +} + +@end + +@implementation Sub2 +-(void) dealloc +{ + subs++; + SUPER_DEALLOC(); } -(void) finalize { @@ -46,7 +101,7 @@ static const char *key = "key"; @implementation Value -(void) dealloc { values++; - [super dealloc]; + SUPER_DEALLOC(); } -(void) finalize { values++; @@ -54,17 +109,37 @@ static const char *key = "key"; } @end + int main() { - int i; - for (i = 0; i < 100; i++) { - [[[Super alloc] init] release]; - } - + testonthread(^{ + int i; + for (i = 0; i < 100; i++) { + RELEASE_VALUE([[Super alloc] init]); + } + }); testcollect(); - + + testassert(supers == 0); testassert(subs > 0); testassert(subs == values); + + supers = 0; + subs = 0; + values = 0; + + testonthread(^{ + int i; + for (i = 0; i < 100; i++) { + RELEASE_VALUE([[Super2 alloc] init]); + } + }); + testcollect(); + + testassert(supers > 0); + testassert(subs == 0); + testassert(supers == values); + succeed(__FILE__); } diff --git a/test/atomicProperty.mm b/test/atomicProperty.mm new file mode 100644 index 0000000..aeb06e0 --- /dev/null +++ b/test/atomicProperty.mm @@ -0,0 +1,67 @@ +// TEST_CONFIG CC=clang + +#include "test.h" +#include +#include +#import + +class SerialNumber { + size_t _number; +public: + SerialNumber() : _number(42) {} + SerialNumber(const SerialNumber &number) : _number(number._number + 1) {} + SerialNumber &operator=(const SerialNumber &number) { _number = number._number + 1; return *this; } + + int operator==(const SerialNumber &number) { return _number == number._number; } + int operator!=(const SerialNumber &number) { return _number != number._number; } +}; + +@interface TestAtomicProperty : NSObject { + SerialNumber number; +} +@property(atomic) SerialNumber number; +@end + +@implementation TestAtomicProperty + +#if 1 // with new enough compiler, this will be synthesized automatically. + +extern void objc_copyCppObjectAtomic(void *dest, const void *src, void (*copyHelper) (void *dest, const void *source)); + +static void copySerialNumber(void *d, const void *s) { + SerialNumber *dest = (SerialNumber *)d; + const SerialNumber *src = (const SerialNumber *)s; + dest->operator=(*src); +} + +- (SerialNumber)number { + SerialNumber result; + objc_copyCppObjectAtomic(&result, &number, copySerialNumber); + return result; +} + +- (void)setNumber:(SerialNumber)aNumber { + objc_copyCppObjectAtomic(&number, &aNumber, copySerialNumber); +} + ++(void)initialize { + testwarn("rdar://6137845 compiler should synthesize calls to objc_copyCppObjectAtomic"); +} + +#else +@synthesize number; +#endif + +@end + +int main() +{ + PUSH_POOL { + SerialNumber number; + TestAtomicProperty *test = [TestAtomicProperty new]; + test.number = number; + testassert(test.number != number); + } POP_POOL; + + succeed(__FILE__); +} diff --git a/test/badAltHandler.m b/test/badAltHandler.m index 5325442..0b827aa 100644 --- a/test/badAltHandler.m +++ b/test/badAltHandler.m @@ -10,7 +10,6 @@ END #include "test.h" -#include #include /* @@ -25,16 +24,6 @@ void handler(id unused __unused, void *context __unused) { } -void *fn(void *arg __unused) -{ - @try { - Token = objc_addExceptionHandler(&handler, NULL); - } @catch (...) { - } - - return NULL; -} - int main() { #if __clang__ && __cplusplus @@ -61,9 +50,12 @@ int main() // Create an alt handler on another thread // that collides with one of the removed handlers - pthread_t th; - pthread_create(&th, NULL, &fn, NULL); - pthread_join(th, NULL); + testonthread(^{ + @try { + Token = objc_addExceptionHandler(&handler, NULL); + } @catch (...) { + } + }); // Incorrectly remove the other thread's handler objc_removeExceptionHandler(Token); diff --git a/test/blocksAsImps.m b/test/blocksAsImps.m index 3574898..5376efb 100644 --- a/test/blocksAsImps.m +++ b/test/blocksAsImps.m @@ -6,20 +6,19 @@ #include -#if !__clang__ && !__llvm__ - // gcc will never support struct-return marking -# define STRET_OK 0 -#elif !clang -// llvm-gcc waiting for rdar://8143947 +#if !__has_feature(objc_arc) +# define __bridge +#endif + +#if !__clang__ + // gcc and llvm-gcc will never support struct-return marking # define STRET_OK 0 #else # define STRET_OK 1 #endif -typedef uint32_t (*funcptr)(); - typedef struct BigStruct { - unsigned int datums[200]; + uintptr_t datums[200]; } BigStruct; @interface Foo:NSObject @@ -38,7 +37,8 @@ typedef struct BigStruct { - (float) methodThatReturnsFloat: (float) aFloat; @end -typedef uint32_t (*FuncPtr)(id, SEL); +// This is void* instead of id to prevent interference from ARC. +typedef uintptr_t (*FuncPtr)(void *, SEL); typedef BigStruct (*BigStructFuncPtr)(id, SEL, BigStruct); typedef float (*FloatFuncPtr)(id, SEL, float); @@ -50,15 +50,21 @@ BigStruct bigfunc(BigStruct a) { @implementation Deallocator -(void) methodThatNobodyElseCalls1 { } -(void) methodThatNobodyElseCalls2 { } --(id) retain { +id retain_imp(Deallocator *self, SEL _cmd) { _objc_flush_caches([Deallocator class]); [self methodThatNobodyElseCalls1]; - return [super retain]; + struct objc_super sup = { self, [[Deallocator class] superclass] }; + return ((id(*)(struct objc_super *, SEL))objc_msgSendSuper)(&sup, _cmd); } --(void) dealloc { +void dealloc_imp(Deallocator *self, SEL _cmd) { _objc_flush_caches([Deallocator class]); [self methodThatNobodyElseCalls2]; - [super dealloc]; + struct objc_super sup = { self, [[Deallocator class] superclass] }; + ((void(*)(struct objc_super *, SEL))objc_msgSendSuper)(&sup, _cmd); +} ++(void) load { + class_addMethod(self, sel_registerName("retain"), (IMP)retain_imp, ""); + class_addMethod(self, sel_registerName("dealloc"), (IMP)dealloc_imp, ""); } @end @@ -70,9 +76,9 @@ typedef enum { ArgumentModeMax } ArgumentMode; -static ArgumentMode _argumentModeForBlock(void *block) { +static ArgumentMode _argumentModeForBlock(id block) { ArgumentMode aMode = ReturnValueInRegisterArgumentMode; - if ( _Block_use_stret(block) ) + if ( _Block_use_stret((__bridge void *)block) ) aMode = ReturnValueOnStackArgumentMode; return aMode; @@ -80,11 +86,6 @@ static ArgumentMode _argumentModeForBlock(void *block) { /* End copied code */ int main () { -#if __llvm__ && !__clang__ - // edit STRET_OK above when you remove this - testwarn(" struct-return blocks not yet integrated in llvm-gcc"); -#endif - // make sure the bits are in place int (^registerReturn)() = ^(){ return 42; }; ArgumentMode aMode; @@ -101,52 +102,51 @@ int main () { #define TEST_QUANTITY 100000 static FuncPtr funcArray[TEST_QUANTITY]; - uint32_t i; + uintptr_t i; for(i = 0; i 37.1212) ); + + + IMP floatIMP = imp_implementationWithBlock(^float (id self __attribute__((unused)), float aFloat ) { + return aFloat; + }); + class_addMethod([Foo class], @selector(methodThatReturnsFloat:), floatIMP, "ooh.. type string unspecified again... oh noe... runtime might punish. not."); + + float e = (float)0.001; + float retF = (float)[f methodThatReturnsFloat: 37.1212f]; + testassert( ((retF - e) < 37.1212) && ((retF + e) > 37.1212) ); + +#if !__has_feature(objc_arc) + // Make sure imp_implementationWithBlock() and imp_removeBlock() + // don't deadlock while calling Block_copy() and Block_release() + Deallocator *dead = [[Deallocator alloc] init]; + IMP deadlockImp = imp_implementationWithBlock(^{ [dead self]; }); + [dead release]; + imp_removeBlock(deadlockImp); +#endif - // Make sure imp_implementationWithBlock() and imp_removeBlock() - // don't deadlock while calling Block_copy() and Block_release() - Deallocator *dead = [[Deallocator alloc] init]; - IMP deadlockImp = imp_implementationWithBlock(^{ [dead self]; }); - [dead release]; - imp_removeBlock(deadlockImp); + } POP_POOL; - [p drain]; succeed(__FILE__); } diff --git a/test/cacheflush.h b/test/cacheflush.h index 50ccaad..5553dbb 100644 --- a/test/cacheflush.h +++ b/test/cacheflush.h @@ -1,7 +1,7 @@ #include +#include "test.h" -@interface Super { id isa; } -+class; +@interface TestRoot(cat) +(int)classMethod; -(int)instanceMethod; @end diff --git a/test/cacheflush.m b/test/cacheflush.m index 2624a8c..85136d7 100644 --- a/test/cacheflush.m +++ b/test/cacheflush.m @@ -13,47 +13,45 @@ END #include "cacheflush.h" -@interface Sub : Super @end +@interface Sub : TestRoot @end @implementation Sub @end int main() { - uintptr_t buf[10]; - uintptr_t buf2[10]; - buf[0] = (uintptr_t)[Super class]; - buf2[0] = (uintptr_t)[Sub class]; + TestRoot *sup = [TestRoot new]; + Sub *sub = [Sub new]; // Fill method cache - testassert(1 == [Super classMethod]); - testassert(1 == [(Super *)buf instanceMethod]); - testassert(1 == [Super classMethod]); - testassert(1 == [(Super *)buf instanceMethod]); + testassert(1 == [TestRoot classMethod]); + testassert(1 == [sup instanceMethod]); + testassert(1 == [TestRoot classMethod]); + testassert(1 == [sup instanceMethod]); testassert(1 == [Sub classMethod]); - testassert(1 == [(Sub *)buf2 instanceMethod]); + testassert(1 == [sub instanceMethod]); testassert(1 == [Sub classMethod]); - testassert(1 == [(Sub *)buf2 instanceMethod]); + testassert(1 == [sub instanceMethod]); // Dynamically load a category dlopen("cacheflush2.dylib", 0); // Make sure old cache results are gone - testassert(2 == [Super classMethod]); - testassert(2 == [(Super *)buf instanceMethod]); + testassert(2 == [TestRoot classMethod]); + testassert(2 == [sup instanceMethod]); testassert(2 == [Sub classMethod]); - testassert(2 == [(Sub *)buf2 instanceMethod]); + testassert(2 == [sub instanceMethod]); // Dynamically load another category dlopen("cacheflush3.dylib", 0); // Make sure old cache results are gone - testassert(3 == [Super classMethod]); - testassert(3 == [(Super *)buf instanceMethod]); + testassert(3 == [TestRoot classMethod]); + testassert(3 == [sup instanceMethod]); testassert(3 == [Sub classMethod]); - testassert(3 == [(Sub *)buf2 instanceMethod]); + testassert(3 == [sub instanceMethod]); // fixme test subclasses diff --git a/test/cacheflush0.m b/test/cacheflush0.m index 991471b..8536071 100644 --- a/test/cacheflush0.m +++ b/test/cacheflush0.m @@ -1,8 +1,7 @@ #include "cacheflush.h" +#include "testroot.i" -@implementation Super -+(void)initialize { } -+class { return self; } +@implementation TestRoot(cat) +(int)classMethod { return 1; } -(int)instanceMethod { return 1; } @end diff --git a/test/cacheflush2.m b/test/cacheflush2.m index 439c178..0337e3f 100644 --- a/test/cacheflush2.m +++ b/test/cacheflush2.m @@ -1,6 +1,6 @@ #include "cacheflush.h" -@implementation Super (Category2) +@implementation TestRoot (Category2) +(int)classMethod { return 2; } -(int)instanceMethod { return 2; } @end diff --git a/test/cacheflush3.m b/test/cacheflush3.m index a5b5210..c180e98 100644 --- a/test/cacheflush3.m +++ b/test/cacheflush3.m @@ -1,6 +1,6 @@ #include "cacheflush.h" -@implementation Super (Category3) +@implementation TestRoot (Category3) +(int)classMethod { return 3; } -(int)instanceMethod { return 3; } @end diff --git a/test/category.m b/test/category.m index 6b4a208..6733462 100644 --- a/test/category.m +++ b/test/category.m @@ -1,15 +1,14 @@ -// TEST_CONFIG +// TEST_CFLAGS -Wl,-no_objc_category_merging #include "test.h" +#include "testroot.i" #include -#include +#include static int state = 0; -@interface Super { id isa; } @end +@interface Super : TestRoot @end @implementation Super -+(void)initialize { } -+class { return self; } -(void)instancemethod { fail("-instancemethod not overridden by category"); } +(void)method { fail("+method not overridden by category"); } @end @@ -70,13 +69,10 @@ static int state = 0; int main() { - id buf[10] = {0}; - // methods introduced by category state = 0; [Super method]; - buf[0] = [Super class]; - [(Super *)buf instancemethod]; + [[Super new] instancemethod]; testassert(state == 2); // property introduced by category diff --git a/test/cdtors.mm b/test/cdtors.mm index d993265..e3a4baa 100644 --- a/test/cdtors.mm +++ b/test/cdtors.mm @@ -1,8 +1,10 @@ // TEST_CONFIG -#include #include "test.h" + +#include #include "objc/objc-internal.h" +#include "testroot.i" static unsigned ctors1 = 0; static unsigned dtors1 = 0; @@ -30,7 +32,7 @@ class cxx2 { /* Class hierarchy: - Base + TestRoot CXXBase NoCXXSub CXXSub @@ -39,21 +41,7 @@ class cxx2 { */ -@interface Base { id isa; } -+class; -+new; --(void)dealloc; -@end -@implementation Base -+(void)initialize { } -+class { return self; } --class { return self->isa; } -+new { return class_createInstance(self, 0); } --(void)dealloc { object_dispose(self); } --(void)finalize { } -@end - -@interface CXXBase : Base { +@interface CXXBase : TestRoot { cxx1 baseIvar; } @end @@ -72,132 +60,136 @@ class cxx2 { @implementation CXXSub @end -void *test_single(void *arg __unused) +void test_single(void) { - volatile id o; - // Single allocation - objc_registerThreadWithCollector(); - ctors1 = dtors1 = ctors2 = dtors2 = 0; - o = [Base new]; - testassert(ctors1 == 0 && dtors1 == 0 && - ctors2 == 0 && dtors2 == 0); - testassert([o class] == [Base class]); - [o dealloc], o = nil; + testonthread(^{ + id o = [TestRoot new]; + testassert(ctors1 == 0 && dtors1 == 0 && + ctors2 == 0 && dtors2 == 0); + testassert([o class] == [TestRoot class]); + RELEASE_VAR(o); + }); testcollect(); testassert(ctors1 == 0 && dtors1 == 0 && ctors2 == 0 && dtors2 == 0); ctors1 = dtors1 = ctors2 = dtors2 = 0; - o = [CXXBase new]; - testassert(ctors1 == 1 && dtors1 == 0 && - ctors2 == 0 && dtors2 == 0); - testassert([o class] == [CXXBase class]); - [o dealloc], o = nil; + testonthread(^{ + id o = [CXXBase new]; + testassert(ctors1 == 1 && dtors1 == 0 && + ctors2 == 0 && dtors2 == 0); + testassert([o class] == [CXXBase class]); + RELEASE_VAR(o); + }); testcollect(); testassert(ctors1 == 1 && dtors1 == 1 && ctors2 == 0 && dtors2 == 0); ctors1 = dtors1 = ctors2 = dtors2 = 0; - o = [NoCXXSub new]; - testassert(ctors1 == 1 && dtors1 == 0 && - ctors2 == 0 && dtors2 == 0); - testassert([o class] == [NoCXXSub class]); - [o dealloc], o = nil; + testonthread(^{ + id o = [NoCXXSub new]; + testassert(ctors1 == 1 && dtors1 == 0 && + ctors2 == 0 && dtors2 == 0); + testassert([o class] == [NoCXXSub class]); + RELEASE_VAR(o); + }); testcollect(); testassert(ctors1 == 1 && dtors1 == 1 && ctors2 == 0 && dtors2 == 0); ctors1 = dtors1 = ctors2 = dtors2 = 0; - o = [CXXSub new]; - testassert(ctors1 == 1 && dtors1 == 0 && - ctors2 == 1 && dtors2 == 0); - testassert([o class] == [CXXSub class]); - [o dealloc], o = nil; + testonthread(^{ + id o = [CXXSub new]; + testassert(ctors1 == 1 && dtors1 == 0 && + ctors2 == 1 && dtors2 == 0); + testassert([o class] == [CXXSub class]); + RELEASE_VAR(o); + }); testcollect(); testassert(ctors1 == 1 && dtors1 == 1 && ctors2 == 1 && dtors2 == 1); - - return NULL; } -void *test_inplace(void *arg __unused) +void test_inplace(void) { - volatile id o; + __unsafe_unretained volatile id o; char o2[64]; - // In-place allocation + id (*objc_constructInstance_fn)(Class, void*) = (id(*)(Class, void*))dlsym(RTLD_DEFAULT, "objc_constructInstance"); + void (*objc_destructInstance_fn)(id) = (void(*)(id))dlsym(RTLD_DEFAULT, "objc_destructInstance"); - objc_registerThreadWithCollector(); + // In-place allocation ctors1 = dtors1 = ctors2 = dtors2 = 0; - o = objc_constructInstance([Base class], o2); + o = objc_constructInstance_fn([TestRoot class], o2); testassert(ctors1 == 0 && dtors1 == 0 && ctors2 == 0 && dtors2 == 0); - testassert([o class] == [Base class]); - objc_destructInstance(o), o = nil; + testassert([o class] == [TestRoot class]); + objc_destructInstance_fn(o), o = nil; testcollect(); testassert(ctors1 == 0 && dtors1 == 0 && ctors2 == 0 && dtors2 == 0); ctors1 = dtors1 = ctors2 = dtors2 = 0; - o = objc_constructInstance([CXXBase class], o2); + o = objc_constructInstance_fn([CXXBase class], o2); testassert(ctors1 == 1 && dtors1 == 0 && ctors2 == 0 && dtors2 == 0); testassert([o class] == [CXXBase class]); - objc_destructInstance(o), o = nil; + objc_destructInstance_fn(o), o = nil; testcollect(); testassert(ctors1 == 1 && dtors1 == 1 && ctors2 == 0 && dtors2 == 0); ctors1 = dtors1 = ctors2 = dtors2 = 0; - o = objc_constructInstance([NoCXXSub class], o2); + o = objc_constructInstance_fn([NoCXXSub class], o2); testassert(ctors1 == 1 && dtors1 == 0 && ctors2 == 0 && dtors2 == 0); testassert([o class] == [NoCXXSub class]); - objc_destructInstance(o), o = nil; + objc_destructInstance_fn(o), o = nil; testcollect(); testassert(ctors1 == 1 && dtors1 == 1 && ctors2 == 0 && dtors2 == 0); ctors1 = dtors1 = ctors2 = dtors2 = 0; - o = objc_constructInstance([CXXSub class], o2); + o = objc_constructInstance_fn([CXXSub class], o2); testassert(ctors1 == 1 && dtors1 == 0 && ctors2 == 1 && dtors2 == 0); testassert([o class] == [CXXSub class]); - objc_destructInstance(o), o = nil; + objc_destructInstance_fn(o), o = nil; testcollect(); testassert(ctors1 == 1 && dtors1 == 1 && ctors2 == 1 && dtors2 == 1); - - return NULL; } -void *test_batch(void *arg __unused) +void test_batch(void) { +#if __has_feature(objc_arc) + // not converted to ARC yet + return; +#else + id o2[100]; unsigned int count, i; // Batch allocation - objc_registerThreadWithCollector(); - for (i = 0; i < 100; i++) { - o2[i] = (id)malloc(class_getInstanceSize([Base class])); + o2[i] = (id)malloc(class_getInstanceSize([TestRoot class])); } for (i = 0; i < 100; i++) { free(o2[i]); } ctors1 = dtors1 = ctors2 = dtors2 = 0; - count = class_createInstances([Base class], 0, o2, 10); + count = class_createInstances([TestRoot class], 0, o2, 10); testassert(count > 0); testassert(ctors1 == 0 && dtors1 == 0 && ctors2 == 0 && dtors2 == 0); - for (i = 0; i < count; i++) testassert([o2[i] class] == [Base class]); + for (i = 0; i < count; i++) testassert([o2[i] class] == [TestRoot class]); for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil; testcollect(); testassert(ctors1 == 0 && dtors1 == 0 && @@ -205,7 +197,7 @@ void *test_batch(void *arg __unused) for (i = 0; i < 100; i++) { // prime batch allocator - free(malloc(class_getInstanceSize([Base class]))); + free(malloc(class_getInstanceSize([TestRoot class]))); } ctors1 = dtors1 = ctors2 = dtors2 = 0; @@ -221,7 +213,7 @@ void *test_batch(void *arg __unused) for (i = 0; i < 100; i++) { // prime batch allocator - free(malloc(class_getInstanceSize([Base class]))); + free(malloc(class_getInstanceSize([TestRoot class]))); } ctors1 = dtors1 = ctors2 = dtors2 = 0; @@ -237,7 +229,7 @@ void *test_batch(void *arg __unused) for (i = 0; i < 100; i++) { // prime batch allocator - free(malloc(class_getInstanceSize([Base class]))); + free(malloc(class_getInstanceSize([TestRoot class]))); } ctors1 = dtors1 = ctors2 = dtors2 = 0; @@ -250,24 +242,17 @@ void *test_batch(void *arg __unused) testcollect(); testassert(ctors1 == count && dtors1 == count && ctors2 == count && dtors2 == count); - - return NULL; +#endif } int main() { - pthread_t th; - - testassert(0 == pthread_create(&th, NULL, test_single, NULL)); - pthread_join(th, NULL); - - testassert(0 == pthread_create(&th, NULL, test_inplace, NULL)); - pthread_join(th, NULL); + testonthread(^{ test_single(); }); + testonthread(^{ test_inplace(); }); leak_mark(); - testassert(0 == pthread_create(&th, NULL, test_batch, NULL)); - pthread_join(th, NULL); + testonthread(^{ test_batch(); }); // fixme can't get this to zero; may or may not be a real leak leak_check(64); diff --git a/test/classgetclass.m b/test/classgetclass.m index 64f7dd4..70686eb 100644 --- a/test/classgetclass.m +++ b/test/classgetclass.m @@ -1,9 +1,9 @@ -// TEST_CFLAGS -framework Foundation +// TEST_CONFIG #include "test.h" #include #include -#import +#import @interface Foo:NSObject @end diff --git a/test/classname.m b/test/classname.m index 1e9eb7e..11fd09e 100644 --- a/test/classname.m +++ b/test/classname.m @@ -1,45 +1,38 @@ // TEST_CONFIG #include "test.h" +#include "testroot.i" #include -#include +#include -@interface Super { @public id isa; } @end -@implementation Super -+(void)initialize { } -+class { return self; } -@end - -@interface Fake { @public id isa; } @end -@implementation Fake -+(void)initialize { } -+class { return self; } -@end +@interface Fake : TestRoot @end +@implementation Fake @end int main() { - id buf[10]; - Super *obj = (Super *)buf; - buf[0] = [Fake class]; + TestRoot *obj = [TestRoot new]; + Class __unsafe_unretained * buf = (Class *)objc_unretainedPointer(obj); + *buf = [Fake class]; - testassert(obj->isa == [Fake class]); - testassert(object_setClass(obj, [Super class]) == [Fake class]); - testassert(obj->isa == [Super class]); - testassert(object_setClass(nil, [Super class]) == nil); + testassert(object_getClass(obj) == [Fake class]); + testassert(object_setClass(obj, [TestRoot class]) == [Fake class]); + testassert(object_getClass(obj) == [TestRoot class]); + testassert(object_setClass(nil, [TestRoot class]) == nil); - bzero(buf, sizeof(buf)); - testassert(object_setClass(obj, [Super class]) == nil); + testassert(malloc_size(buf) >= sizeof(id)); + bzero(buf, malloc_size(buf)); + testassert(object_setClass(obj, [TestRoot class]) == nil); testassert(object_getClass(obj) == buf[0]); - testassert(object_getClass([Super class]) == [Super class]->isa); + testassert(object_getClass([TestRoot class]) == object_getClass([TestRoot class])); testassert(object_getClass(nil) == Nil); - testassert(0 == strcmp(object_getClassName(obj), "Super")); - testassert(0 == strcmp(object_getClassName([Super class]), "Super")); + testassert(0 == strcmp(object_getClassName(obj), "TestRoot")); + testassert(0 == strcmp(object_getClassName([TestRoot class]), "TestRoot")); testassert(0 == strcmp(object_getClassName(nil), "nil")); - testassert(0 == strcmp(class_getName([Super class]), "Super")); - testassert(0 == strcmp(class_getName([Super class]->isa), "Super")); + testassert(0 == strcmp(class_getName([TestRoot class]), "TestRoot")); + testassert(0 == strcmp(class_getName(object_getClass([TestRoot class])), "TestRoot")); testassert(0 == strcmp(class_getName(nil), "nil")); succeed(__FILE__); diff --git a/test/classpair.m b/test/classpair.m index 0fb9758..9ff124e 100644 --- a/test/classpair.m +++ b/test/classpair.m @@ -1,6 +1,8 @@ // TEST_CFLAGS -Wno-deprecated-declarations #include "test.h" + +#include "testroot.i" #include #include #ifndef OBJC_NO_GC @@ -34,15 +36,12 @@ static int super_initialize; -@interface Super { @public id isa; } +@interface Super : TestRoot @property int superProp; @end @implementation Super @dynamic superProp; +(void)initialize { super_initialize++; } -+class { return self; } -+(id) new { return class_createInstance(self, 0); } --(void) free { object_dispose(self); } +(void) classMethod { fail("+[Super classMethod] called"); } +(void) classMethod2 { fail("+[Super classMethod2] called"); } @@ -57,13 +56,13 @@ static int state; static void instance_fn(id self, SEL _cmd __attribute__((unused))) { - testassert(!class_isMetaClass(self->isa)); + testassert(!class_isMetaClass(object_getClass(self))); state++; } static void class_fn(id self, SEL _cmd __attribute__((unused))) { - testassert(class_isMetaClass(self->isa)); + testassert(class_isMetaClass(object_getClass(self))); state++; } @@ -72,6 +71,7 @@ static void fail_fn(id self __attribute__((unused)), SEL _cmd) fail("fail_fn '%s' called", sel_getName(_cmd)); } + static void cycle(void) { Class cls; @@ -88,18 +88,18 @@ static void cycle(void) testassert(cls); #ifndef OBJC_NO_GC if (objc_collectingEnabled()) { - testassert(auto_zone_size(objc_collectableZone(), cls)); - testassert(auto_zone_size(objc_collectableZone(), cls->isa)); + 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@:"); - class_addMethod(cls->isa, @selector(classMethod), + class_addMethod(object_getClass(cls), @selector(classMethod), (IMP)&class_fn, "v@:"); - class_addMethod(cls->isa, @selector(initialize), + class_addMethod(object_getClass(cls), @selector(initialize), (IMP)&class_fn, "v@:"); - class_addMethod(cls->isa, @selector(load), + class_addMethod(object_getClass(cls), @selector(load), (IMP)&fail_fn, "v@:"); ok = class_addProtocol(cls, @protocol(Proto)); @@ -160,15 +160,17 @@ static void cycle(void) ok = class_addIvar(cls, "ivar", 4, 2, "i"); testassert(!ok); - ok = class_addIvar(cls->isa, "classvar", 4, 2, "i"); + ok = class_addIvar(object_getClass(cls), "classvar", 4, 2, "i"); testassert(!ok); objc_registerClassPair(cls); // should call cls's +initialize, not super's + // Provoke +initialize using class_getMethodImplementation(class method) + // in order to test getNonMetaClass's slow case super_initialize = 0; state = 0; - [cls class]; + class_getMethodImplementation(object_getClass(cls), @selector(class)); testassert(super_initialize == 0); testassert(state == 1); @@ -176,10 +178,10 @@ static void cycle(void) testassert(cls == objc_getClass("Sub")); testassert(!class_isMetaClass(cls)); - testassert(class_isMetaClass(cls->isa)); + testassert(class_isMetaClass(object_getClass(cls))); testassert(class_getSuperclass(cls) == [Super class]); - testassert(class_getSuperclass(cls->isa) == [Super class]->isa); + testassert(class_getSuperclass(object_getClass(cls)) == object_getClass([Super class])); testassert(class_getInstanceSize(cls) >= sizeof(Class) + 4 + 3*size); testassert(class_conformsToProtocol(cls, @protocol(Proto))); @@ -191,12 +193,12 @@ static void cycle(void) class_addMethod(cls, @selector(instanceMethod2), (IMP)&instance_fn, "v@:"); - class_addMethod(cls->isa, @selector(classMethod2), + class_addMethod(object_getClass(cls), @selector(classMethod2), (IMP)&class_fn, "v@:"); ok = class_addIvar(cls, "ivar2", 4, 4, "i"); testassert(!ok); - ok = class_addIvar(cls->isa, "classvar2", 4, 4, "i"); + ok = class_addIvar(object_getClass(cls), "classvar2", 4, 4, "i"); testassert(!ok); ok = class_addProtocol(cls, @protocol(Proto2)); @@ -240,12 +242,16 @@ static void cycle(void) [cls classMethod2]; testassert(state == 2); - id obj = [cls new]; - state = 0; - [obj instanceMethod]; - [obj instanceMethod2]; - testassert(state == 2); - [obj free]; + // put instance tests on a separate thread so they + // are reliably GC'd before class destruction + testonthread(^{ + id obj = [cls new]; + state = 0; + [obj instanceMethod]; + [obj instanceMethod2]; + testassert(state == 2); + RELEASE_VAR(obj); + }); // Test ivar layouts of sub-subclass Class cls2 = objc_allocateClassPair(cls, "SubSub", 0); @@ -287,8 +293,8 @@ static void cycle(void) 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")); @@ -348,13 +354,16 @@ static void cycle(void) int main() { - int count = 1000; cycle(); + cycle(); + + int count = 1000; leak_mark(); while (count--) { - cycle(); + testonthread(^{ cycle(); }); } - leak_check(0); + leak_check(256); // fixme should be 0 succeed(__FILE__); } + diff --git a/test/classversion.m b/test/classversion.m index ab0e6ac..c34b98e 100644 --- a/test/classversion.m +++ b/test/classversion.m @@ -1,19 +1,14 @@ // TEST_CONFIG #include "test.h" -#include - -@interface Super { id isa; } @end -@implementation Super -+class { return self; } -+(void)initialize { } -@end +#include "testroot.i" +#include int main() { - Class cls = [Super class]; + Class cls = [TestRoot class]; testassert(class_getVersion(cls) == 0); - testassert(class_getVersion(cls->isa) > 5); + testassert(class_getVersion(object_getClass(cls)) > 5); class_setVersion(cls, 100); testassert(class_getVersion(cls) == 100); diff --git a/test/concurrentcat.m b/test/concurrentcat.m index 4f5205f..0f6ef69 100644 --- a/test/concurrentcat.m +++ b/test/concurrentcat.m @@ -51,10 +51,6 @@ END @end @implementation TargetClass -+(void)initialize { } -+(id)new { - return class_createInstance(self, 0); -} - (void) m0 { fail("shoulda been loaded!"); } - (void) m1 { fail("shoulda been loaded!"); } - (void) m2 { fail("shoulda been loaded!"); } @@ -69,45 +65,45 @@ void *threadFun(void *aTargetClassName) { objc_registerThreadWithCollector(); - NSAutoreleasePool *p = [NSAutoreleasePool new]; - - Class targetSubclass = objc_getClass(className); - testassert(targetSubclass); - - id target = [targetSubclass new]; - testassert(target); - - while(1) { - [target m0]; - [target retain]; - [target addObserver: target forKeyPath: @"m3" options: 0 context: NULL]; - [target addObserver: target forKeyPath: @"m4" options: 0 context: NULL]; - [target m1]; - [target release]; - [target m2]; - [target autorelease]; - [target m3]; - [target retain]; - [target removeObserver: target forKeyPath: @"m4"]; - [target addObserver: target forKeyPath: @"m5" options: 0 context: NULL]; - [target m4]; - [target retain]; - [target m5]; - [target autorelease]; - [target m6]; - [target m7]; - [target m8]; - [target m9]; - [target m10]; - [target m11]; - [target m12]; - [target m13]; - [target m14]; - [target m15]; - [target removeObserver: target forKeyPath: @"m3"]; - [target removeObserver: target forKeyPath: @"m5"]; - } - [p drain]; + PUSH_POOL { + + Class targetSubclass = objc_getClass(className); + testassert(targetSubclass); + + id target = [targetSubclass new]; + testassert(target); + + while(1) { + [target m0]; + RETAIN(target); + [target addObserver: target forKeyPath: @"m3" options: 0 context: NULL]; + [target addObserver: target forKeyPath: @"m4" options: 0 context: NULL]; + [target m1]; + RELEASE_VALUE(target); + [target m2]; + AUTORELEASE(target); + [target m3]; + RETAIN(target); + [target removeObserver: target forKeyPath: @"m4"]; + [target addObserver: target forKeyPath: @"m5" options: 0 context: NULL]; + [target m4]; + RETAIN(target); + [target m5]; + AUTORELEASE(target); + [target m6]; + [target m7]; + [target m8]; + [target m9]; + [target m10]; + [target m11]; + [target m12]; + [target m13]; + [target m14]; + [target m15]; + [target removeObserver: target forKeyPath: @"m3"]; + [target removeObserver: target forKeyPath: @"m5"]; + } + } POP_POOL; return NULL; } diff --git a/test/copyMethodList.m b/test/copyMethodList.m index 584d8b5..18a0d6c 100644 --- a/test/copyMethodList.m +++ b/test/copyMethodList.m @@ -1,20 +1,11 @@ -// TEST_CONFIG +// TEST_CFLAGS -Wl,-no_objc_category_merging #include "test.h" +#include "testroot.i" #include -#include - -#if __OBJC2__ -// methods added by the runtime: +initialize -# define MC 1 // class methods -# define MI 0 // instance methods -#else -// no magic -# define MC 0 -# define MI 0 -#endif - -@interface SuperMethods { } @end +#include + +@interface SuperMethods : TestRoot { } @end @implementation SuperMethods +(BOOL)SuperMethodClass { return NO; } +(BOOL)SuperMethodClass2 { return NO; } @@ -22,7 +13,7 @@ -(BOOL)SuperMethodInstance2 { return NO; } @end -@interface SubMethods { } @end +@interface SubMethods : SuperMethods { } @end @implementation SubMethods +(BOOL)SubMethodClass { return NO; } +(BOOL)SubMethodClass2 { return NO; } @@ -47,7 +38,7 @@ @end -@interface FourMethods @end +@interface FourMethods : TestRoot @end @implementation FourMethods -(void)one { } -(void)two { } @@ -55,7 +46,7 @@ -(void)four { } @end -@interface NoMethods @end +@interface NoMethods : TestRoot @end @implementation NoMethods @end static void checkReplacement(Method *list, const char *name) @@ -99,9 +90,9 @@ int main() count = 100; methods = class_copyMethodList(cls, &count); testassert(methods); - testassert(count == 4+MI); + testassert(count == 4); // methods[] should be null-terminated - testassert(methods[4+MI] == NULL); + testassert(methods[4] == NULL); // Class and category methods may be mixed in the method list thanks // to linker / shared cache sorting, but a category's replacement should // always precede the class's implementation. @@ -112,11 +103,11 @@ int main() testprintf("calling class_copyMethodList(SubMethods(meta)) (should be unmethodized)\n"); count = 100; - methods = class_copyMethodList(cls->isa, &count); + methods = class_copyMethodList(object_getClass(cls), &count); testassert(methods); - testassert(count == 4+MC); + testassert(count == 4); // methods[] should be null-terminated - testassert(methods[4+MC] == NULL); + testassert(methods[4] == NULL); // Class and category methods may be mixed in the method list thanks // to linker / shared cache sorting, but a category's replacement should // always precede the class's implementation. @@ -130,17 +121,17 @@ int main() cls = objc_getClass("FourMethods"); methods = class_copyMethodList(cls, &count); testassert(methods); - testassert(count == 4+MI); - testassert(malloc_size(methods) >= (4+MI+1) * sizeof(Method)); - testassert(methods[3+MI] != NULL); - testassert(methods[4+MI] == NULL); + testassert(count == 4); + testassert(malloc_size(methods) >= (4+1) * sizeof(Method)); + testassert(methods[3] != NULL); + testassert(methods[4] == NULL); free(methods); // Check NULL count parameter methods = class_copyMethodList(cls, NULL); testassert(methods); - testassert(methods[4+MI] == NULL); - testassert(methods[3+MI] != NULL); + testassert(methods[4] == NULL); + testassert(methods[3] != NULL); free(methods); // Check NULL class parameter @@ -157,15 +148,8 @@ int main() count = 100; cls = objc_getClass("NoMethods"); methods = class_copyMethodList(cls, &count); - if (MI == 0) { - testassert(!methods); - testassert(count == 0); - } else { - testassert(methods); - testassert(count == MI); - testassert(methods[MI] == NULL); - testassert(methods[MI-1] != NULL); - } + testassert(!methods); + testassert(count == 0); succeed(__FILE__); } diff --git a/test/createInstance.m b/test/createInstance.m index 61cb2a0..dbe7d5f 100644 --- a/test/createInstance.m +++ b/test/createInstance.m @@ -1,3 +1,4 @@ +// TEST_CONFIG MEM=mrc,gc // TEST_CFLAGS -Wno-deprecated-declarations #import diff --git a/test/customrr-cat1.m b/test/customrr-cat1.m new file mode 100644 index 0000000..823238d --- /dev/null +++ b/test/customrr-cat1.m @@ -0,0 +1,7 @@ +@interface InheritingSubCat @end + +@interface InheritingSubCat (NonClobberingCategory) @end + +@implementation InheritingSubCat (NonClobberingCategory) +-(id) unrelatedMethod { return self; } +@end diff --git a/test/customrr-cat2.m b/test/customrr-cat2.m new file mode 100644 index 0000000..55c96df --- /dev/null +++ b/test/customrr-cat2.m @@ -0,0 +1,7 @@ +@interface InheritingSubCat @end + +@interface InheritingSubCat (ClobberingCategory) @end + +@implementation InheritingSubCat (ClobberingCategory) +-(int) retainCount { return 1; } +@end diff --git a/test/customrr.m b/test/customrr.m new file mode 100644 index 0000000..1354fe3 --- /dev/null +++ b/test/customrr.m @@ -0,0 +1,757 @@ +// These options must match customrr2.m +// TEST_CONFIG MEM=mrc +/* +TEST_BUILD + $C{COMPILE} $DIR/customrr.m '-Wl,-exported_symbol,.objc_class_name_InheritingSubCat' -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 +END +*/ + + +#include "test.h" +#include +#include + +#if !__OBJC2__ + +// pacify exported symbols list +@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; +static int RetainCounts; +static int PlusRetains; +static int PlusReleases; +static int PlusAutoreleases; +static int PlusRetainCounts; + +static int SubRetains; +static int SubReleases; +static int SubAutoreleases; +static int SubRetainCounts; +static int SubPlusRetains; +static int SubPlusReleases; +static int SubPlusAutoreleases; +static int SubPlusRetainCounts; + +static int Imps; + +static id imp_fn(id self, SEL _cmd __unused, ...) +{ + Imps++; + return self; +} + +static void zero(void) { + Retains = 0; + Releases = 0; + Autoreleases = 0; + RetainCounts = 0; + PlusRetains = 0; + PlusReleases = 0; + PlusAutoreleases = 0; + PlusRetainCounts = 0; + + SubRetains = 0; + SubReleases = 0; + SubAutoreleases = 0; + SubRetainCounts = 0; + SubPlusRetains = 0; + SubPlusReleases = 0; + SubPlusAutoreleases = 0; + SubPlusRetainCounts = 0; + + Imps = 0; +} + + +id HackRetain(id self, SEL _cmd __unused) { Retains++; return self; } +void HackRelease(id self __unused, SEL _cmd __unused) { Releases++; } +id HackAutorelease(id self, SEL _cmd __unused) { Autoreleases++; return self; } +NSUInteger HackRetainCount(id self __unused, SEL _cmd __unused) { RetainCounts++; return 1; } +id HackPlusRetain(id self, SEL _cmd __unused) { PlusRetains++; return self; } +void HackPlusRelease(id self __unused, SEL _cmd __unused) { PlusReleases++; } +id HackPlusAutorelease(id self, SEL _cmd __unused) { PlusAutoreleases++; return self; } +NSUInteger HackPlusRetainCount(id self __unused, SEL _cmd __unused) { PlusRetainCounts++; return 1; } + + +@interface OverridingSub : NSObject @end +@implementation OverridingSub + +-(id) retain { SubRetains++; return self; } ++(id) retain { SubPlusRetains++; return self; } +-(oneway void) release { SubReleases++; } ++(oneway void) release { SubPlusReleases++; } +-(id) autorelease { SubAutoreleases++; return self; } ++(id) autorelease { SubPlusAutoreleases++; return self; } +-(NSUInteger) retainCount { SubRetainCounts++; return 1; } ++(NSUInteger) retainCount { SubPlusRetainCounts++; return 1; } + +@end + +@interface InheritingSub : NSObject @end +@implementation InheritingSub @end + +@interface InheritingSub2 : NSObject @end +@implementation InheritingSub2 @end +@interface InheritingSub2_2 : InheritingSub2 @end +@implementation InheritingSub2_2 @end + +@interface InheritingSub3 : NSObject @end +@implementation InheritingSub3 @end +@interface InheritingSub3_2 : InheritingSub3 @end +@implementation InheritingSub3_2 @end + +@interface InheritingSub4 : NSObject @end +@implementation InheritingSub4 @end +@interface InheritingSub4_2 : InheritingSub4 @end +@implementation InheritingSub4_2 @end + +@interface InheritingSub5 : NSObject @end +@implementation InheritingSub5 @end +@interface InheritingSub5_2 : InheritingSub5 @end +@implementation InheritingSub5_2 @end + +@interface InheritingSub6 : NSObject @end +@implementation InheritingSub6 @end +@interface InheritingSub6_2 : InheritingSub6 @end +@implementation InheritingSub6_2 @end + +@interface InheritingSub7 : NSObject @end +@implementation InheritingSub7 @end +@interface InheritingSub7_2 : InheritingSub7 @end +@implementation InheritingSub7_2 @end + +@interface InheritingSubCat : NSObject @end +@implementation InheritingSubCat @end +@interface InheritingSubCat_2 : InheritingSubCat @end +@implementation InheritingSubCat_2 @end + + +int main(int argc __unused, char **argv) +{ + objc_autoreleasePoolPush(); + + // Hack NSObject's RR methods. + // Don't use runtime functions to do this - + // we want the runtime to think that these are NSObject's real code + { + Class cls = [NSObject class]; + IMP *m; + IMP imp; + imp = class_getMethodImplementation(cls, @selector(retain)); + m = (IMP *)class_getInstanceMethod(cls, @selector(retain)); + testassert(m[2] == imp); // verify Method struct is as we expect + + m = (IMP *)class_getInstanceMethod(cls, @selector(retain)); + m[2] = (IMP)HackRetain; + m = (IMP *)class_getInstanceMethod(cls, @selector(release)); + m[2] = (IMP)HackRelease; + m = (IMP *)class_getInstanceMethod(cls, @selector(autorelease)); + m[2] = (IMP)HackAutorelease; + m = (IMP *)class_getInstanceMethod(cls, @selector(retainCount)); + m[2] = (IMP)HackRetainCount; + m = (IMP *)class_getClassMethod(cls, @selector(retain)); + m[2] = (IMP)HackPlusRetain; + m = (IMP *)class_getClassMethod(cls, @selector(release)); + m[2] = (IMP)HackPlusRelease; + m = (IMP *)class_getClassMethod(cls, @selector(autorelease)); + m[2] = (IMP)HackPlusAutorelease; + m = (IMP *)class_getClassMethod(cls, @selector(retainCount)); + m[2] = (IMP)HackPlusRetainCount; + + _objc_flush_caches(cls); + + imp = class_getMethodImplementation(cls, @selector(retain)); + testassert(imp == (IMP)HackRetain); // verify hack worked + } + + Class cls = [NSObject class]; + Class icl = [InheritingSub class]; + Class ocl = [OverridingSub class]; + NSObject *obj = [NSObject new]; + InheritingSub *inh = [InheritingSub new]; + OverridingSub *ovr = [OverridingSub new]; + + Class ccc; + id ooo; + Class cc2; + id oo2; + + void *dlh; + + + testprintf("method dispatch does not bypass\n"); + zero(); + + [obj retain]; + testassert(Retains == 1); + [obj release]; + testassert(Releases == 1); + [obj autorelease]; + testassert(Autoreleases == 1); + + [cls retain]; + testassert(PlusRetains == 1); + [cls release]; + testassert(PlusReleases == 1); + [cls autorelease]; + testassert(PlusAutoreleases == 1); + + [inh retain]; + testassert(Retains == 2); + [inh release]; + testassert(Releases == 2); + [inh autorelease]; + testassert(Autoreleases == 2); + + [icl retain]; + testassert(PlusRetains == 2); + [icl release]; + testassert(PlusReleases == 2); + [icl autorelease]; + testassert(PlusAutoreleases == 2); + + [ovr retain]; + testassert(SubRetains == 1); + [ovr release]; + testassert(SubReleases == 1); + [ovr autorelease]; + testassert(SubAutoreleases == 1); + + [ocl retain]; + testassert(SubPlusRetains == 1); + [ocl release]; + testassert(SubPlusReleases == 1); + [ocl autorelease]; + testassert(SubPlusAutoreleases == 1); + + + testprintf("arc function bypasses instance but not class or override\n"); + zero(); + + objc_retain(obj); + testassert(Retains == 0); + objc_release(obj); + testassert(Releases == 0); + objc_autorelease(obj); + testassert(Autoreleases == 0); + + objc_retain(cls); + testassert(PlusRetains == 1); + objc_release(cls); + testassert(PlusReleases == 1); + objc_autorelease(cls); + testassert(PlusAutoreleases == 1); + + objc_retain(inh); + testassert(Retains == 0); + objc_release(inh); + testassert(Releases == 0); + objc_autorelease(inh); + testassert(Autoreleases == 0); + + objc_retain(icl); + testassert(PlusRetains == 2); + objc_release(icl); + testassert(PlusReleases == 2); + objc_autorelease(icl); + testassert(PlusAutoreleases == 2); + + objc_retain(ovr); + testassert(SubRetains == 1); + objc_release(ovr); + testassert(SubReleases == 1); + objc_autorelease(ovr); + testassert(SubAutoreleases == 1); + + objc_retain(ocl); + testassert(SubPlusRetains == 1); + objc_release(ocl); + testassert(SubPlusReleases == 1); + objc_autorelease(ocl); + testassert(SubPlusAutoreleases == 1); + + + testprintf("unrelated addMethod does not clobber\n"); + zero(); + + class_addMethod(cls, @selector(unrelatedMethod), (IMP)imp_fn, ""); + + objc_retain(obj); + testassert(Retains == 0); + objc_release(obj); + testassert(Releases == 0); + objc_autorelease(obj); + testassert(Autoreleases == 0); + + + testprintf("add class method does not clobber\n"); + zero(); + + objc_retain(obj); + testassert(Retains == 0); + objc_release(obj); + testassert(Releases == 0); + objc_autorelease(obj); + testassert(Autoreleases == 0); + + class_addMethod(cls->isa, @selector(retain), (IMP)imp_fn, ""); + + objc_retain(obj); + testassert(Retains == 0); + objc_release(obj); + testassert(Releases == 0); + objc_autorelease(obj); + testassert(Autoreleases == 0); + + + testprintf("addMethod clobbers (InheritingSub2, retain)\n"); + zero(); + + ccc = [InheritingSub2 class]; + ooo = [ccc new]; + cc2 = [InheritingSub2_2 class]; + oo2 = [cc2 new]; + + objc_retain(ooo); + testassert(Retains == 0); + objc_release(ooo); + testassert(Releases == 0); + objc_autorelease(ooo); + testassert(Autoreleases == 0); + + objc_retain(oo2); + testassert(Retains == 0); + objc_release(oo2); + testassert(Releases == 0); + objc_autorelease(oo2); + testassert(Autoreleases == 0); + + class_addMethod(ccc, @selector(retain), (IMP)imp_fn, ""); + + objc_retain(ooo); + testassert(Retains == 0); + testassert(Imps == 1); + objc_release(ooo); + testassert(Releases == 1); + objc_autorelease(ooo); + testassert(Autoreleases == 1); + + objc_retain(oo2); + testassert(Retains == 0); + testassert(Imps == 2); + objc_release(oo2); + testassert(Releases == 2); + objc_autorelease(oo2); + testassert(Autoreleases == 2); + + + testprintf("addMethod clobbers (InheritingSub3, release)\n"); + zero(); + + ccc = [InheritingSub3 class]; + ooo = [ccc new]; + cc2 = [InheritingSub3_2 class]; + oo2 = [cc2 new]; + + objc_retain(ooo); + testassert(Retains == 0); + objc_release(ooo); + testassert(Releases == 0); + objc_autorelease(ooo); + testassert(Autoreleases == 0); + + objc_retain(oo2); + testassert(Retains == 0); + objc_release(oo2); + testassert(Releases == 0); + objc_autorelease(oo2); + testassert(Autoreleases == 0); + + class_addMethod(ccc, @selector(release), (IMP)imp_fn, ""); + + objc_retain(ooo); + testassert(Retains == 1); + objc_release(ooo); + testassert(Releases == 0); + testassert(Imps == 1); + objc_autorelease(ooo); + testassert(Autoreleases == 1); + + objc_retain(oo2); + testassert(Retains == 2); + objc_release(oo2); + testassert(Releases == 0); + testassert(Imps == 2); + objc_autorelease(oo2); + testassert(Autoreleases == 2); + + + testprintf("addMethod clobbers (InheritingSub4, autorelease)\n"); + zero(); + + ccc = [InheritingSub4 class]; + ooo = [ccc new]; + cc2 = [InheritingSub4_2 class]; + oo2 = [cc2 new]; + + objc_retain(ooo); + testassert(Retains == 0); + objc_release(ooo); + testassert(Releases == 0); + objc_autorelease(ooo); + testassert(Autoreleases == 0); + + objc_retain(oo2); + testassert(Retains == 0); + objc_release(oo2); + testassert(Releases == 0); + objc_autorelease(oo2); + testassert(Autoreleases == 0); + + class_addMethod(ccc, @selector(autorelease), (IMP)imp_fn, ""); + + objc_retain(ooo); + testassert(Retains == 1); + objc_release(ooo); + testassert(Releases == 1); + objc_autorelease(ooo); + testassert(Autoreleases == 0); + testassert(Imps == 1); + + objc_retain(oo2); + testassert(Retains == 2); + objc_release(oo2); + testassert(Releases == 2); + objc_autorelease(oo2); + testassert(Autoreleases == 0); + testassert(Imps == 2); + + + testprintf("addMethod clobbers (InheritingSub5, retainCount)\n"); + zero(); + + ccc = [InheritingSub5 class]; + ooo = [ccc new]; + cc2 = [InheritingSub5_2 class]; + oo2 = [cc2 new]; + + objc_retain(ooo); + testassert(Retains == 0); + objc_release(ooo); + testassert(Releases == 0); + objc_autorelease(ooo); + testassert(Autoreleases == 0); + + objc_retain(oo2); + testassert(Retains == 0); + objc_release(oo2); + testassert(Releases == 0); + objc_autorelease(oo2); + testassert(Autoreleases == 0); + + class_addMethod(ccc, @selector(retainCount), (IMP)imp_fn, ""); + + objc_retain(ooo); + testassert(Retains == 1); + objc_release(ooo); + testassert(Releases == 1); + objc_autorelease(ooo); + testassert(Autoreleases == 1); + // no bypassing call for -retainCount + + objc_retain(oo2); + testassert(Retains == 2); + objc_release(oo2); + testassert(Releases == 2); + objc_autorelease(oo2); + testassert(Autoreleases == 2); + // no bypassing call for -retainCount + + + testprintf("setSuperclass to clean super does not clobber (InheritingSub6)\n"); + zero(); + + ccc = [InheritingSub6 class]; + ooo = [ccc new]; + cc2 = [InheritingSub6_2 class]; + oo2 = [cc2 new]; + + objc_retain(ooo); + testassert(Retains == 0); + objc_release(ooo); + testassert(Releases == 0); + objc_autorelease(ooo); + testassert(Autoreleases == 0); + + objc_retain(oo2); + testassert(Retains == 0); + objc_release(oo2); + testassert(Releases == 0); + objc_autorelease(oo2); + testassert(Autoreleases == 0); + + class_setSuperclass(ccc, [InheritingSub class]); + + objc_retain(ooo); + testassert(Retains == 0); + objc_release(ooo); + testassert(Releases == 0); + objc_autorelease(ooo); + testassert(Autoreleases == 0); + + objc_retain(oo2); + testassert(Retains == 0); + objc_release(oo2); + testassert(Releases == 0); + objc_autorelease(oo2); + testassert(Autoreleases == 0); + + + testprintf("setSuperclass to dirty super clobbers (InheritingSub7)\n"); + zero(); + + ccc = [InheritingSub7 class]; + ooo = [ccc new]; + cc2 = [InheritingSub7_2 class]; + oo2 = [cc2 new]; + + objc_retain(ooo); + testassert(Retains == 0); + objc_release(ooo); + testassert(Releases == 0); + objc_autorelease(ooo); + testassert(Autoreleases == 0); + + objc_retain(oo2); + testassert(Retains == 0); + objc_release(oo2); + testassert(Releases == 0); + objc_autorelease(oo2); + testassert(Autoreleases == 0); + + class_setSuperclass(ccc, [OverridingSub class]); + + objc_retain(ooo); + testassert(SubRetains == 1); + objc_release(ooo); + testassert(SubReleases == 1); + objc_autorelease(ooo); + testassert(SubAutoreleases == 1); + + objc_retain(oo2); + testassert(SubRetains == 2); + objc_release(oo2); + testassert(SubReleases == 2); + objc_autorelease(oo2); + testassert(SubAutoreleases == 2); + + + testprintf("category replacement of unrelated method does not clobber (InheritingSubCat)\n"); + zero(); + + ccc = [InheritingSubCat class]; + ooo = [ccc new]; + cc2 = [InheritingSubCat_2 class]; + oo2 = [cc2 new]; + + objc_retain(ooo); + testassert(Retains == 0); + objc_release(ooo); + testassert(Releases == 0); + objc_autorelease(ooo); + testassert(Autoreleases == 0); + + objc_retain(oo2); + testassert(Retains == 0); + objc_release(oo2); + testassert(Releases == 0); + objc_autorelease(oo2); + testassert(Autoreleases == 0); + + dlh = dlopen("customrr-cat1.dylib", RTLD_LAZY); + testassert(dlh); + + objc_retain(ooo); + testassert(Retains == 0); + objc_release(ooo); + testassert(Releases == 0); + objc_autorelease(ooo); + testassert(Autoreleases == 0); + + objc_retain(oo2); + testassert(Retains == 0); + objc_release(oo2); + testassert(Releases == 0); + objc_autorelease(oo2); + testassert(Autoreleases == 0); + + + testprintf("category replacement clobbers (InheritingSubCat)\n"); + zero(); + + ccc = [InheritingSubCat class]; + ooo = [ccc new]; + cc2 = [InheritingSubCat_2 class]; + oo2 = [cc2 new]; + + objc_retain(ooo); + testassert(Retains == 0); + objc_release(ooo); + testassert(Releases == 0); + objc_autorelease(ooo); + testassert(Autoreleases == 0); + + objc_retain(oo2); + testassert(Retains == 0); + objc_release(oo2); + testassert(Releases == 0); + objc_autorelease(oo2); + testassert(Autoreleases == 0); + + dlh = dlopen("customrr-cat2.dylib", RTLD_LAZY); + testassert(dlh); + + objc_retain(ooo); + testassert(Retains == 1); + objc_release(ooo); + testassert(Releases == 1); + objc_autorelease(ooo); + testassert(Autoreleases == 1); + + objc_retain(oo2); + testassert(Retains == 2); + objc_release(oo2); + testassert(Releases == 2); + objc_autorelease(oo2); + testassert(Autoreleases == 2); + + + testprintf("allocateClassPair with clean super does not clobber\n"); + zero(); + + objc_retain(inh); + testassert(Retains == 0); + objc_release(inh); + testassert(Releases == 0); + objc_autorelease(inh); + testassert(Autoreleases == 0); + + ccc = objc_allocateClassPair([InheritingSub class], "CleanClassPair", 0); + objc_registerClassPair(ccc); + ooo = [ccc new]; + + objc_retain(inh); + testassert(Retains == 0); + objc_release(inh); + testassert(Releases == 0); + objc_autorelease(inh); + testassert(Autoreleases == 0); + + objc_retain(ooo); + testassert(Retains == 0); + objc_release(ooo); + testassert(Releases == 0); + objc_autorelease(ooo); + testassert(Autoreleases == 0); + + + testprintf("allocateClassPair with clobbered super clobbers\n"); + zero(); + + ccc = objc_allocateClassPair([OverridingSub class], "DirtyClassPair", 0); + objc_registerClassPair(ccc); + ooo = [ccc new]; + + objc_retain(ooo); + testassert(SubRetains == 1); + objc_release(ooo); + testassert(SubReleases == 1); + objc_autorelease(ooo); + testassert(SubAutoreleases == 1); + + + testprintf("allocateClassPair with clean super and override clobbers\n"); + zero(); + + ccc = objc_allocateClassPair([InheritingSub class], "Dirty2ClassPair", 0); + class_addMethod(ccc, @selector(autorelease), (IMP)imp_fn, ""); + objc_registerClassPair(ccc); + ooo = [ccc new]; + + objc_retain(ooo); + testassert(Retains == 1); + objc_release(ooo); + testassert(Releases == 1); + objc_autorelease(ooo); + testassert(Autoreleases == 0); + testassert(Imps == 1); + + + // method_setImplementation and method_exchangeImplementations only + // clobber when manipulating NSObject. We can only test one at a time. + // To test both, we need two tests: customrr and customrr2. + + // These tests also check recursive clobber. + +#if TEST_EXCHANGEIMPLEMENTATIONS + testprintf("exchangeImplementations clobbers (recursive)\n"); +#else + testprintf("setImplementation clobbers (recursive)\n"); +#endif + zero(); + + objc_retain(obj); + testassert(Retains == 0); + objc_release(obj); + testassert(Releases == 0); + objc_autorelease(obj); + testassert(Autoreleases == 0); + + objc_retain(inh); + testassert(Retains == 0); + objc_release(inh); + testassert(Releases == 0); + objc_autorelease(inh); + testassert(Autoreleases == 0); + + Method meth = class_getInstanceMethod(cls, @selector(retainCount)); + testassert(meth); +#if TEST_EXCHANGEIMPLEMENTATIONS + method_exchangeImplementations(meth, meth); +#else + method_setImplementation(meth, (IMP)imp_fn); +#endif + + objc_retain(obj); + testassert(Retains == 1); + objc_release(obj); + testassert(Releases == 1); + objc_autorelease(obj); + testassert(Autoreleases == 1); + + objc_retain(inh); + testassert(Retains == 2); + objc_release(inh); + testassert(Releases == 2); + objc_autorelease(inh); + testassert(Autoreleases == 2); + + + // do not add more tests here - the recursive test must be LAST + + succeed(basename(argv[0])); +} + +#endif diff --git a/test/customrr2.m b/test/customrr2.m new file mode 100644 index 0000000..1f612d7 --- /dev/null +++ b/test/customrr2.m @@ -0,0 +1,9 @@ +// These options must match customrr.m +// TEST_CONFIG MEM=mrc +/* +TEST_BUILD + $C{COMPILE} $DIR/customrr.m '-Wl,-exported_symbol,.objc_class_name_InheritingSubCat' -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 +END +*/ diff --git a/test/debuggerMode.m b/test/debuggerMode.m index b760706..aef2faf 100644 --- a/test/debuggerMode.m +++ b/test/debuggerMode.m @@ -8,6 +8,8 @@ #define _OBJC_PRIVATE_H_ #include +#warning this test needs to be augmented for the side table machienery + @interface Super { id isa; } @end @implementation Super diff --git a/test/definitions.c b/test/definitions.c new file mode 100644 index 0000000..479246a --- /dev/null +++ b/test/definitions.c @@ -0,0 +1,50 @@ +// TEST_CONFIG + +// DO NOT include anything else here +#include +// DO NOT include anything else here +Class c = Nil; +SEL s; +IMP i; +id o = nil; +BOOL b = YES; +BOOL b2 = NO; +#if !__has_feature(objc_arr) +__strong void *p; +#endif +id __unsafe_unretained u; +id __weak w; + +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 +BOOL (^block1)(void) = ^{ return YES; }; +BOOL (^block2)(void) = ^{ return NO; }; +#endif + +#include "test.h" + +int main() +{ + testassert(YES); + testassert(!NO); + testassert(!nil); + testassert(!Nil); + +#if __has_feature(objc_bool) + // YES[array] is disallowed for objc just as true[array] is for C++ +#else + // this will fail if YES and NO do not have enough parentheses + int array[2] = { 888, 999 }; + testassert(NO[array] == 888); + testassert(YES[array] == 999); +#endif + + succeed(__FILE__); +} diff --git a/test/definitions.m b/test/definitions.m deleted file mode 100644 index 78bb8d1..0000000 --- a/test/definitions.m +++ /dev/null @@ -1,25 +0,0 @@ -// TEST_CONFIG - -// DO NOT include anything else here -#include -// DO NOT include anything else here -Class c = Nil; -SEL s; -IMP i; -id o = nil; -BOOL b = YES; -BOOL b2 = NO; -__strong void *p; - - -#include "test.h" - -int main() -{ - testassert(YES); - testassert(!NO); - testassert(!nil); - testassert(!Nil); - - succeed(__FILE__); -} diff --git a/test/duplicateClass.m b/test/duplicateClass.m index e712dac..0ac2671 100644 --- a/test/duplicateClass.m +++ b/test/duplicateClass.m @@ -1,7 +1,8 @@ -// TEST_CFLAGS -Wno-deprecated-declarations +// TEST_CFLAGS -Wno-deprecated-declarations -Wl,-no_objc_category_merging #include "test.h" -#include +#include "testroot.i" +#include #ifndef OBJC_NO_GC #include #include @@ -10,11 +11,11 @@ static int state; @protocol Proto -+class; ++(void)classMethod; +-(void)instanceMethod; @end -@interface Super { - id isa; +@interface Super : TestRoot { int i; } @property int i; @@ -23,11 +24,6 @@ static int state; @implementation Super @synthesize i; -+(void)initialize { } -+class { return self; } -+new { return class_createInstance(self, 0); } --(void)dealloc { object_dispose(self); } - +(void)classMethod { state = 1; } @@ -39,6 +35,11 @@ static int state; @end +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation" +#endif + @implementation Super (Category) +(void)classMethod { @@ -51,6 +52,10 @@ static int state; @end +#if __clang__ +#pragma clang diagnostic pop +#endif + int main() { @@ -63,14 +68,14 @@ int main() clone = objc_duplicateClass(cls, "Super_copy", 0); #ifndef OBJC_NO_GC if (objc_collectingEnabled()) { - testassert(auto_zone_size(objc_collectableZone(), clone)); + 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(clone->isa == cls->isa); + testassert(object_getClass(clone) == object_getClass(cls)); testassert(class_getSuperclass(clone) == class_getSuperclass(cls)); testassert(class_getVersion(clone) == class_getVersion(cls)); testassert(class_isMetaClass(clone) == class_isMetaClass(cls)); @@ -109,16 +114,16 @@ int main() free(i2); // Check protocol list - Protocol * const *p1 = class_copyProtocolList(cls, NULL); - Protocol * const *p2 = class_copyProtocolList(clone, NULL); + Protocol * __unsafe_unretained *p1 = class_copyProtocolList(cls, NULL); + Protocol * __unsafe_unretained *p2 = class_copyProtocolList(clone, NULL); testassert(p1); testassert(p2); for (i = 0; p1[i] && p2[i]; i++) { testassert(p1[i] == p2[i]); // protocols are not deep-copied } testassert(p1[i] == NULL && p2[i] == NULL); - free((void*)p1); - free((void*)p2); + free(p1); + free(p2); // Check property list objc_property_t *o1 = class_copyPropertyList(cls, NULL); @@ -147,13 +152,13 @@ int main() state = 0; [obj instanceMethod]; testassert(state == 4); - [obj dealloc]; + RELEASE_VAR(obj); obj = [clone new]; state = 0; [obj instanceMethod]; testassert(state == 4); - [obj dealloc]; + RELEASE_VAR(obj); succeed(__FILE__); } diff --git a/test/evil-category-0.m b/test/evil-category-0.m index 8dfa8d7..a0d6060 100644 --- a/test/evil-category-0.m +++ b/test/evil-category-0.m @@ -1,11 +1,8 @@ /* rdar://8553305 -TEST_CONFIG -TEST_CRASHES - TEST_BUILD - $C{COMPILE} $DIR/evil-category-0.m -dynamiclib -o libevil.dylib -framework Foundation + $C{COMPILE} $DIR/evil-category-0.m -dynamiclib -o libevil.dylib $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-category-0.out END */ diff --git a/test/evil-category-00.m b/test/evil-category-00.m index e3030dd..cff2a3e 100644 --- a/test/evil-category-00.m +++ b/test/evil-category-00.m @@ -5,7 +5,7 @@ TEST_CONFIG SDK=iphoneos TEST_CRASHES TEST_BUILD - $C{COMPILE} $DIR/evil-category-00.m $DIR/evil-main.m -framework Foundation -o evil-category-00.out + $C{COMPILE} $DIR/evil-category-00.m $DIR/evil-main.m -o evil-category-00.out END TEST_RUN_OUTPUT diff --git a/test/evil-category-000.m b/test/evil-category-000.m index 67b5c6b..e451706 100644 --- a/test/evil-category-000.m +++ b/test/evil-category-000.m @@ -1,11 +1,8 @@ /* rdar://8553305 -TEST_CONFIG -TEST_CRASHES - TEST_BUILD - $C{COMPILE} $DIR/evil-category-000.m -dynamiclib -o libevil.dylib -framework Foundation + $C{COMPILE} $DIR/evil-category-000.m -dynamiclib -o libevil.dylib $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-category-000.out END */ diff --git a/test/evil-category-1.m b/test/evil-category-1.m index 7e6d07f..5067c51 100644 --- a/test/evil-category-1.m +++ b/test/evil-category-1.m @@ -5,7 +5,7 @@ TEST_CONFIG SDK=iphoneos TEST_CRASHES TEST_BUILD - $C{COMPILE} $DIR/evil-category-1.m -dynamiclib -o libevil.dylib -framework Foundation + $C{COMPILE} $DIR/evil-category-1.m -dynamiclib -o libevil.dylib $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-1.out END diff --git a/test/evil-category-2.m b/test/evil-category-2.m index 21d1d66..2cbf93c 100644 --- a/test/evil-category-2.m +++ b/test/evil-category-2.m @@ -5,7 +5,7 @@ TEST_CONFIG SDK=iphoneos TEST_CRASHES TEST_BUILD - $C{COMPILE} $DIR/evil-category-2.m -dynamiclib -o libevil.dylib -framework Foundation + $C{COMPILE} $DIR/evil-category-2.m -dynamiclib -o libevil.dylib $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-2.out END diff --git a/test/evil-category-3.m b/test/evil-category-3.m index 9c097ae..7ee8de2 100644 --- a/test/evil-category-3.m +++ b/test/evil-category-3.m @@ -5,7 +5,7 @@ TEST_CONFIG SDK=iphoneos TEST_CRASHES TEST_BUILD - $C{COMPILE} $DIR/evil-category-3.m -dynamiclib -o libevil.dylib -framework Foundation + $C{COMPILE} $DIR/evil-category-3.m -dynamiclib -o libevil.dylib $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-3.out END diff --git a/test/evil-category-4.m b/test/evil-category-4.m index d024b8f..a38e976 100644 --- a/test/evil-category-4.m +++ b/test/evil-category-4.m @@ -5,7 +5,7 @@ TEST_CONFIG SDK=iphoneos TEST_CRASHES TEST_BUILD - $C{COMPILE} $DIR/evil-category-4.m -dynamiclib -o libevil.dylib -framework Foundation + $C{COMPILE} $DIR/evil-category-4.m -dynamiclib -o libevil.dylib $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-4.out END diff --git a/test/evil-category-def.m b/test/evil-category-def.m index 5917e1a..1b3faec 100644 --- a/test/evil-category-def.m +++ b/test/evil-category-def.m @@ -17,7 +17,6 @@ void nop(void) { } __END_DECLS asm( - ".globl L_category \n" ".section __DATA,__objc_data \n" ".align 3 \n" "L_category: \n" @@ -64,6 +63,7 @@ asm( PTR "L_category \n" #endif + ".text \n" ); // __OBJC2__ diff --git a/test/evil-class-0.m b/test/evil-class-0.m index f468315..7d35ad8 100644 --- a/test/evil-class-0.m +++ b/test/evil-class-0.m @@ -1,9 +1,6 @@ /* rdar://8553305 -TEST_CONFIG -TEST_CRASHES - TEST_BUILD $C{COMPILE} $DIR/evil-class-0.m -dynamiclib -o libevil.dylib $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-class-0.out diff --git a/test/evil-class-000.m b/test/evil-class-000.m index 7987ef6..003d4d4 100644 --- a/test/evil-class-000.m +++ b/test/evil-class-000.m @@ -1,9 +1,6 @@ /* rdar://8553305 -TEST_CONFIG -TEST_CRASHES - TEST_BUILD $C{COMPILE} $DIR/evil-class-000.m -dynamiclib -o libevil.dylib $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-class-000.out diff --git a/test/evil-class-def.m b/test/evil-class-def.m index 4e418f8..d986c9c 100644 --- a/test/evil-class-def.m +++ b/test/evil-class-def.m @@ -161,6 +161,8 @@ asm( #if !OMIT_NL_SUB PTR "_OBJC_CLASS_$_Sub \n" #endif + + ".text \n" ); // __OBJC2__ diff --git a/test/exc.m b/test/exc.m index e811aae..06b1372 100644 --- a/test/exc.m +++ b/test/exc.m @@ -1,6 +1,20 @@ -// TEST_CONFIG +/* +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" +#include "testroot.i" #include #include @@ -11,29 +25,26 @@ static volatile int state = 0; #include -static NSAutoreleasePool *p; -void pool(void) { [p release]; p = [NSAutoreleasePool new]; } - @interface Super : NSException @end @implementation Super -+new { return [[self exceptionWithName:@"Super" reason:@"reason" userInfo:nil] retain]; } ++(id)exception { return AUTORELEASE([[self alloc] initWithName:@"Super" reason:@"reason" userInfo:nil]); } -(void)check { state++; } +(void)check { testassert(!"caught class object, not instance"); } @end -#else +#define FILENAME "nsexc.m" -void pool(void) { } +#else -@interface Super { id isa; } @end +@interface Super : TestRoot @end @implementation Super -+new { return class_createInstance(self, 0); } -+(void)initialize { } ++(id)exception { return AUTORELEASE([self new]); } -(void)check { state++; } +(void)check { testassert(!"caught class object, not instance"); } --(void)release { object_dispose(self); } @end +#define FILENAME "exc.m" + #endif @interface Sub : Super @end @@ -41,8 +52,7 @@ void pool(void) { } @end -#if __OBJC2__ && !TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR - +#if __OBJC2__ && !TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE void altHandlerFail(id unused __unused, void *context __unused) { fail("altHandlerFail called"); @@ -56,6 +66,7 @@ void altHandlerFail(id unused __unused, void *context __unused) state++; \ } +ALT_HANDLER(1) ALT_HANDLER(2) ALT_HANDLER(3) ALT_HANDLER(4) @@ -71,7 +82,7 @@ static void throwWithAltHandler(void) state++; uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3); // state++ inside alt handler - @throw [Super new]; + @throw [Super exception]; state = BAD; objc_removeExceptionHandler(token); } @@ -89,7 +100,7 @@ static void throwWithAltHandlerAndRethrow(void) state++; uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3); // state++ inside alt handler - @throw [Super new]; + @throw [Super exception]; state = BAD; objc_removeExceptionHandler(token); } @@ -103,457 +114,323 @@ static void throwWithAltHandlerAndRethrow(void) #endif +#if __cplusplus && __OBJC2__ +#include +void terminator() { + succeed(FILENAME); +} +#endif + int main() { - pool(); - - testprintf("try-catch-finally, exception caught exactly\n"); + PUSH_POOL { - state = 0; - @try { - state++; - @try { - state++; - @throw [Super new]; - state = BAD; - } - @catch (Super *e) { - state++; - [e check]; // state++ - [e release]; - } - @finally { - state++; - } - state++; - } - @catch (...) { - state = BAD; - } - testassert(state == 6); - - - testprintf("try-finally, no exception thrown\n"); - - state = 0; - @try { - state++; - @try { - state++; - } - @finally { - state++; - } - state++; - } - @catch (...) { - state = BAD; - } - testassert(state == 4); - - - testprintf("try-finally, with exception\n"); - - state = 0; - @try { - state++; + testprintf("try-catch-finally, exception caught exactly\n"); + + state = 0; @try { state++; - @throw [Super new]; - state = BAD; - } - @finally { - state++; - } - state = BAD; - } - @catch (id e) { - state++; - [e check]; // state++ - [e release]; - } - testassert(state == 5); - - - testprintf("try-catch-finally, no exception\n"); - - state = 0; - @try { - state++; - @try { + @try { + state++; + @throw [Super exception]; + state = BAD; + } + @catch (Super *e) { + state++; + [e check]; // state++ + } + @finally { + state++; + } state++; } @catch (...) { state = BAD; } - @finally { - state++; - } - state++; - } @catch (...) { - state = BAD; - } - testassert(state == 4); - - - testprintf("try-catch-finally, exception not caught\n"); - - state = 0; - @try { - state++; + testassert(state == 6); + + + testprintf("try-finally, no exception thrown\n"); + + state = 0; @try { state++; - @throw [Super new]; - state = BAD; + @try { + state++; + } + @finally { + state++; + } + state++; } - @catch (Sub *e) { + @catch (...) { state = BAD; } - @finally { - state++; - } - state = BAD; - } - @catch (id e) { - state++; - [e check]; // state++ - [e release]; - } - testassert(state == 5); - - - testprintf("try-catch-finally, exception caught exactly, rethrown\n"); - - state = 0; - @try { - state++; + testassert(state == 4); + + + testprintf("try-finally, with exception\n"); + + state = 0; @try { state++; - @throw [Super new]; + @try { + state++; + @throw [Super exception]; + state = BAD; + } + @finally { + state++; + } state = BAD; } - @catch (Super *e) { + @catch (id e) { state++; [e check]; // state++ - @throw; - state = BAD; } - @finally { - state++; - } - state = BAD; - } - @catch (id e) { - state++; - [e check]; // state++ - [e release]; - } - testassert(state == 7); - - - testprintf("try-catch, no exception\n"); - - state = 0; - @try { - state++; + testassert(state == 5); + + + testprintf("try-catch-finally, no exception\n"); + + state = 0; @try { state++; - } - @catch (...) { - state = BAD; - } - state++; - } @catch (...) { - state = BAD; - } - testassert(state == 3); - - - testprintf("try-catch, exception not caught\n"); - - state = 0; - @try { - state++; - @try { + @try { + state++; + } + @catch (...) { + state = BAD; + } + @finally { + state++; + } state++; - @throw [Super new]; - state = BAD; - } - @catch (Sub *e) { + } @catch (...) { state = BAD; } - state = BAD; - } - @catch (id e) { - state++; - [e check]; // state++ - [e release]; - } - testassert(state == 4); - - - testprintf("try-catch, exception caught exactly\n"); - - state = 0; - @try { - state++; + testassert(state == 4); + + + testprintf("try-catch-finally, exception not caught\n"); + + state = 0; @try { state++; - @throw [Super new]; + @try { + state++; + @throw [Super exception]; + state = BAD; + } + @catch (Sub *e) { + state = BAD; + } + @finally { + state++; + } state = BAD; } - @catch (Super *e) { + @catch (id e) { state++; [e check]; // state++ - [e release]; } - state++; - } - @catch (...) { - state = BAD; - } - testassert(state == 5); - - - testprintf("try-catch, exception caught exactly, rethrown\n"); - - state = 0; - @try { - state++; + testassert(state == 5); + + + testprintf("try-catch-finally, exception caught exactly, rethrown\n"); + + state = 0; @try { state++; - @throw [Super new]; + @try { + state++; + @throw [Super exception]; + state = BAD; + } + @catch (Super *e) { + state++; + [e check]; // state++ + @throw; + state = BAD; + } + @finally { + state++; + } state = BAD; } - @catch (Super *e) { + @catch (id e) { state++; [e check]; // state++ - @throw; - state = BAD; } - state = BAD; - } - @catch (id e) { - state++; - [e check]; // state++ - [e release]; - } - testassert(state == 6); - - - testprintf("try-catch, exception caught exactly, thrown again explicitly\n"); - - state = 0; - @try { - state++; + testassert(state == 7); + + + testprintf("try-catch, no exception\n"); + + state = 0; @try { state++; - @throw [Super new]; - state = BAD; - } - @catch (Super *e) { + @try { + state++; + } + @catch (...) { + state = BAD; + } state++; - [e check]; // state++ - @throw e; + } @catch (...) { state = BAD; } - state = BAD; - } - @catch (id e) { - state++; - [e check]; // state++ - [e release]; - } - testassert(state == 6); - - - testprintf("try-catch, default catch, rethrown\n"); - - state = 0; - @try { - state++; + testassert(state == 3); + + + testprintf("try-catch, exception not caught\n"); + + state = 0; @try { state++; - @throw [Super new]; + @try { + state++; + @throw [Super exception]; + state = BAD; + } + @catch (Sub *e) { + state = BAD; + } state = BAD; } - @catch (...) { + @catch (id e) { state++; - @throw; - state = BAD; + [e check]; // state++ } - state = BAD; - } - @catch (id e) { - state++; - [e check]; // state++ - [e release]; - } - testassert(state == 5); - - - testprintf("try-catch, default catch, rethrown and caught inside nested handler\n"); - - state = 0; - @try { - state++; + testassert(state == 4); + + + testprintf("try-catch, exception caught exactly\n"); + + state = 0; @try { state++; - @throw [Super new]; - state = BAD; - } - @catch (...) { - state++; - @try { state++; - @throw; - state = BAD; - } @catch (Sub *e) { + @throw [Super exception]; state = BAD; - } @catch (Super *e) { + } + @catch (Super *e) { state++; [e check]; // state++ - [e release]; - } @catch (...) { - state = BAD; - } @finally { - state++; } - state++; - } - state++; - } - @catch (...) { - state = BAD; - } - testassert(state == 9); - - - testprintf("try-catch, default catch, rethrown inside nested handler but not caught\n"); - - state = 0; - @try { - state++; - @try { - state++; - @throw [Super new]; - state = BAD; } @catch (...) { + state = BAD; + } + testassert(state == 5); + + + testprintf("try-catch, exception caught exactly, rethrown\n"); + + state = 0; + @try { state++; - @try { state++; - @throw; - state = BAD; - } - @catch (Sub *e) { + @throw [Super exception]; state = BAD; } - @finally { + @catch (Super *e) { state++; + [e check]; // state++ + @throw; + state = BAD; } - state = BAD; + } + @catch (id e) { + state++; + [e check]; // state++ } - state = BAD; - } - @catch (id e) { - state++; - [e check]; // state++ - [e release]; - } - testassert(state == 7); - - -#if __OBJC2__ && !TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE - // alt handlers - // run a lot to catch failed unregistration (runtime complains at 1000) -#define ALT_HANDLER_REPEAT 2000 - int i; - - testprintf("alt handler, no exception\n"); - - for (i = 0; i < ALT_HANDLER_REPEAT; i++) { - pool(); - + testassert(state == 6); + + + testprintf("try-catch, exception caught exactly, thrown again explicitly\n"); + state = 0; @try { state++; @try { - uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0); state++; - objc_removeExceptionHandler(token); + @throw [Super exception]; + state = BAD; } - @catch (...) { + @catch (Super *e) { + state++; + [e check]; // state++ + @throw e; state = BAD; } - state++; - } @catch (...) { state = BAD; + } + @catch (id e) { + state++; + [e check]; // state++ } - testassert(state == 3); - } - - testprintf("alt handler, exception thrown through\n"); - - for (i = 0; i < ALT_HANDLER_REPEAT; i++) { - pool(); - + testassert(state == 6); + + + testprintf("try-catch, default catch, rethrown\n"); + state = 0; @try { state++; @try { state++; - uintptr_t token = objc_addExceptionHandler(altHandler2, (void*)altHandler2); - // state++ inside alt handler - @throw [Super new]; + @throw [Super exception]; state = BAD; - objc_removeExceptionHandler(token); } - @catch (Sub *e) { + @catch (...) { + state++; + @throw; state = BAD; } state = BAD; } @catch (id e) { - testassert(state == 3); state++; [e check]; // state++ - [e release]; } testassert(state == 5); - } - - - testprintf("alt handler, nested\n"); - - for (i = 0; i < ALT_HANDLER_REPEAT; i++) { - pool(); - + + + testprintf("try-catch, default catch, rethrown and caught inside nested handler\n"); + state = 0; @try { state++; @try { state++; - // same-level handlers called in FIFO order (not stack-like) - uintptr_t token = objc_addExceptionHandler(altHandler4, (void*)altHandler4); - // state++ inside alt handler - uintptr_t token2 = objc_addExceptionHandler(altHandler5, (void*)altHandler5); - // state++ inside alt handler - throwWithAltHandler(); // state += 2 inside + @throw [Super exception]; state = BAD; - objc_removeExceptionHandler(token); - objc_removeExceptionHandler(token2); - } - @catch (id e) { - testassert(state == 6); + } + @catch (...) { + state++; + + @try { + state++; + @throw; + state = BAD; + } @catch (Sub *e) { + state = BAD; + } @catch (Super *e) { + state++; + [e check]; // state++ + } @catch (...) { + state = BAD; + } @finally { + state++; + } + state++; - [e check]; // state++; - [e release]; } state++; } @@ -561,81 +438,318 @@ int main() state = BAD; } testassert(state == 9); - } - - - testprintf("alt handler, nested, rethrows in between\n"); - - for (i = 0; i < ALT_HANDLER_REPEAT; i++) { - pool(); - + + + testprintf("try-catch, default catch, rethrown inside nested handler but not caught\n"); + state = 0; @try { state++; @try { state++; - // same-level handlers called in FIFO order (not stack-like) - uintptr_t token = objc_addExceptionHandler(altHandler5, (void*)altHandler5); - // state++ inside alt handler - uintptr_t token2 = objc_addExceptionHandler(altHandler6, (void*)altHandler6); - // state++ inside alt handler - throwWithAltHandlerAndRethrow(); // state += 3 inside + @throw [Super exception]; state = BAD; - objc_removeExceptionHandler(token); - objc_removeExceptionHandler(token2); - } + } @catch (...) { - testassert(state == 7); state++; - @throw; + + @try { + state++; + @throw; + state = BAD; + } + @catch (Sub *e) { + state = BAD; + } + @finally { + state++; + } + + state = BAD; } state = BAD; } @catch (id e) { - testassert(state == 8); state++; [e check]; // state++ - [e release]; } - testassert(state == 10); - } + testassert(state == 7); + + +#if __cplusplus && __OBJC2__ + testprintf("C++ try/catch, Objective-C exception superclass\n"); + state = 0; + try { + state++; + try { + state++; + try { + state++; + @throw [Super exception]; + state = BAD; + } catch (...) { + state++; + throw; + state = BAD; + } + state = BAD; + } catch (void *e) { + state = BAD; + } catch (int e) { + state = BAD; + } catch (Sub *e) { + state = BAD; + } catch (Super *e) { + state++; + [e check]; // state++ + throw; + } catch (...) { + state = BAD; + } + } catch (id e) { + state++; + [e check]; // state++; + } + testassert(state == 8); - testprintf("alt handler, exception thrown and caught inside\n"); - for (i = 0; i < ALT_HANDLER_REPEAT; i++) { - pool(); + testprintf("C++ try/catch, Objective-C exception subclass\n"); state = 0; - @try { + try { state++; - uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0); - @try { + try { state++; - @throw [Super new]; + try { + state++; + @throw [Sub exception]; + state = BAD; + } catch (...) { + state++; + throw; + state = BAD; + } state = BAD; - } - @catch (Super *e) { + } catch (void *e) { + state = BAD; + } catch (int e) { + state = BAD; + } catch (Super *e) { state++; [e check]; // state++ - [e release]; + throw; + } catch (Sub *e) { + state = BAD; + } catch (...) { + state = BAD; } + } catch (id e) { state++; - objc_removeExceptionHandler(token); - } - @catch (...) { - state = BAD; + [e check]; // state++; + } + testassert(state == 8); + +#endif + + +#if !__OBJC2__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE + // alt handlers for modern Mac OS only + +#else + // alt handlers + // run a lot to catch failed unregistration (runtime complains at 1000) +#define ALT_HANDLER_REPEAT 2000 + int i; + + testprintf("alt handler, no exception\n"); + + for (i = 0; i < ALT_HANDLER_REPEAT; i++) { + PUSH_POOL { + state = 0; + @try { + state++; + @try { + uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0); + state++; + objc_removeExceptionHandler(token); + } + @catch (...) { + state = BAD; + } + state++; + } @catch (...) { + state = BAD; + } + testassert(state == 3); + } POP_POOL; + } + + testprintf("alt handler, exception thrown through\n"); + + for (i = 0; i < ALT_HANDLER_REPEAT; i++) { + PUSH_POOL { + state = 0; + @try { + state++; + @try { + state++; + uintptr_t token = objc_addExceptionHandler(altHandler2, (void*)altHandler2); + // state++ inside alt handler + @throw [Super exception]; + state = BAD; + objc_removeExceptionHandler(token); + } + @catch (Sub *e) { + state = BAD; + } + state = BAD; + } + @catch (id e) { + testassert(state == 3); + state++; + [e check]; // state++ + } + testassert(state == 5); + } POP_POOL; + } + + + testprintf("alt handler, nested\n"); + + for (i = 0; i < ALT_HANDLER_REPEAT; i++) { + PUSH_POOL { + state = 0; + @try { + state++; + @try { + state++; + // same-level handlers called in FIFO order (not stack-like) + uintptr_t token = objc_addExceptionHandler(altHandler4, (void*)altHandler4); + // state++ inside alt handler + uintptr_t token2 = objc_addExceptionHandler(altHandler5, (void*)altHandler5); + // state++ inside alt handler + throwWithAltHandler(); // state += 2 inside + state = BAD; + objc_removeExceptionHandler(token); + objc_removeExceptionHandler(token2); + } + @catch (id e) { + testassert(state == 6); + state++; + [e check]; // state++; + } + state++; + } + @catch (...) { + state = BAD; + } + testassert(state == 9); + } POP_POOL; + } + + + testprintf("alt handler, nested, rethrows in between\n"); + + for (i = 0; i < ALT_HANDLER_REPEAT; i++) { + PUSH_POOL { + state = 0; + @try { + state++; + @try { + state++; + // same-level handlers called in FIFO order (not stack-like) + uintptr_t token = objc_addExceptionHandler(altHandler5, (void*)altHandler5); + // state++ inside alt handler + uintptr_t token2 = objc_addExceptionHandler(altHandler6, (void*)altHandler6); + // state++ inside alt handler + throwWithAltHandlerAndRethrow(); // state += 3 inside + state = BAD; + objc_removeExceptionHandler(token); + objc_removeExceptionHandler(token2); + } + @catch (...) { + testassert(state == 7); + state++; + @throw; + } + state = BAD; + } + @catch (id e) { + testassert(state == 8); + state++; + [e check]; // state++ + } + testassert(state == 10); + } POP_POOL; + } + + + testprintf("alt handler, exception thrown and caught inside\n"); + + for (i = 0; i < ALT_HANDLER_REPEAT; i++) { + PUSH_POOL { + state = 0; + @try { + state++; + uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0); + @try { + state++; + @throw [Super exception]; + state = BAD; + } + @catch (Super *e) { + state++; + [e check]; // state++ + } + state++; + objc_removeExceptionHandler(token); + } + @catch (...) { + state = BAD; + } + testassert(state == 5); + } POP_POOL; } - testassert(state == 5); - } -#endif #if defined(USE_FOUNDATION) - [p release]; - succeed("nsexc.m"); + testprintf("alt handler, rdar://10055775\n"); + + for (i = 0; i < ALT_HANDLER_REPEAT; i++) { + PUSH_POOL { + state = 0; + @try { + uintptr_t token = objc_addExceptionHandler(altHandler1, (void*)altHandler1); + { + id x = [NSArray array]; + x = [NSArray array]; + } + state++; + // state++ inside alt handler + [NSException raise:@"foo" format:@"bar"]; + state = BAD; + objc_removeExceptionHandler(token); + } @catch (id e) { + testassert(state == 2); + } + } POP_POOL; + } +// defined(USE_FOUNDATION) +#endif + + +// alt handlers +#endif + + } POP_POOL; + +#if __cplusplus && __OBJC2__ + std::set_terminate(terminator); + objc_terminate(); + fail("should not have returned from objc_terminate()"); #else - succeed("exc.m"); + succeed(FILENAME); #endif } diff --git a/test/exchangeImp.m b/test/exchangeImp.m index b8e3357..7ffb640 100644 --- a/test/exchangeImp.m +++ b/test/exchangeImp.m @@ -1,7 +1,8 @@ // TEST_CONFIG #include "test.h" -#include +#include "testroot.i" +#include static int state; @@ -10,10 +11,8 @@ static int state; #define LENGTH 3 #define COUNT 4 -@interface Super { id isa; } @end +@interface Super : TestRoot @end @implementation Super -+class { return self; } -+(void)initialize { } +(void) one { state = ONE; } +(void) two { state = TWO; } +(void) length { state = LENGTH; } diff --git a/test/foreach.m b/test/foreach.m index 8a65a19..4386db3 100644 --- a/test/foreach.m +++ b/test/foreach.m @@ -16,7 +16,7 @@ bool testHandwritten(const char *style, const char *test, const char *message, i if ([reference member:elem]) ++counter; */ NSFastEnumerationState state; - id buffer[4]; + id __unsafe_unretained buffer[4]; state.state = 0; NSUInteger limit = [collection countByEnumeratingWithState:&state objects:buffer count:4]; if (limit != 0) { @@ -80,26 +80,26 @@ void testContinue(NSArray *array) { // array is filled with NSNumbers, in order, from 0 - N bool testBreak(unsigned int where, NSArray *array) { - unsigned int counter = 0; - NSAutoreleasePool *pool = [NSAutoreleasePool new]; - id enumerator = [array objectEnumerator]; - for (id __unused elem in enumerator) { - if (++counter == where) - break; - } - if (counter != where) { - ++Errors; - printf("*** break at %d didn't work (actual was %d)\n", where, counter); - return false; - } - for (id __unused elem in enumerator) - ++counter; - if (counter != [array count]) { - ++Errors; - printf("*** break at %d didn't finish (actual was %d)\n", where, counter); - return false; - } - [pool drain]; + PUSH_POOL { + unsigned int counter = 0; + id enumerator = [array objectEnumerator]; + for (id __unused elem in enumerator) { + if (++counter == where) + break; + } + if (counter != where) { + ++Errors; + printf("*** break at %d didn't work (actual was %d)\n", where, counter); + return false; + } + for (id __unused elem in enumerator) + ++counter; + if (counter != [array count]) { + ++Errors; + printf("*** break at %d didn't finish (actual was %d)\n", where, counter); + return false; + } + } POP_POOL; return true; } @@ -139,27 +139,27 @@ void makeReferences(int n) { NSNumber *number = [[NSNumber alloc] initWithInt:i]; [ReferenceSet addObject:number]; [ReferenceArray addObject:number]; - [number release]; + RELEASE_VAR(number); } } } void testCollections(const char *test, NSArray *array, NSSet *set) { - NSAutoreleasePool *pool = [NSAutoreleasePool new]; - id collection; - collection = [NSMutableArray arrayWithArray:array]; - testCompleteness(test, "mutable array", collection, set); - testEnumerator(test, "mutable array enumerator", collection, set); - collection = [NSArray arrayWithArray:array]; - testCompleteness(test, "immutable array", collection, set); - testEnumerator(test, "immutable array enumerator", collection, set); - collection = set; - testCompleteness(test, "immutable set", collection, set); - testEnumerator(test, "immutable set enumerator", collection, set); - collection = [NSMutableSet setWithArray:array]; - testCompleteness(test, "mutable set", collection, set); - testEnumerator(test, "mutable set enumerator", collection, set); - [pool drain]; + PUSH_POOL { + id collection; + collection = [NSMutableArray arrayWithArray:array]; + testCompleteness(test, "mutable array", collection, set); + testEnumerator(test, "mutable array enumerator", collection, set); + collection = [NSArray arrayWithArray:array]; + testCompleteness(test, "immutable array", collection, set); + testEnumerator(test, "immutable array enumerator", collection, set); + collection = set; + testCompleteness(test, "immutable set", collection, set); + testEnumerator(test, "immutable set enumerator", collection, set); + collection = [NSMutableSet setWithArray:array]; + testCompleteness(test, "mutable set", collection, set); + testEnumerator(test, "mutable set enumerator", collection, set); + } POP_POOL; } void testInnerDecl(const char *test, const char *message, id collection) { @@ -212,16 +212,16 @@ void testExpressions(const char *message, id collection) { int main() { - NSAutoreleasePool *pool = [NSAutoreleasePool new]; - testCollections("nil", nil, nil); - testCollections("empty", [NSArray array], [NSSet set]); - makeReferences(100); - testCollections("100 item", ReferenceArray, ReferenceSet); - testExpressions("array", ReferenceArray); - testBreaks(ReferenceArray); - testContinue(ReferenceArray); - if (Errors == 0) succeed(__FILE__); - else fail("foreach %d errors detected\n", Errors); - [pool drain]; + PUSH_POOL { + testCollections("nil", nil, nil); + testCollections("empty", [NSArray array], [NSSet set]); + makeReferences(100); + testCollections("100 item", ReferenceArray, ReferenceSet); + testExpressions("array", ReferenceArray); + testBreaks(ReferenceArray); + testContinue(ReferenceArray); + if (Errors == 0) succeed(__FILE__); + else fail("foreach %d errors detected\n", Errors); + } POP_POOL; exit(Errors); } diff --git a/test/forward.m b/test/forward.m index 9a48b17..7ad0d97 100644 --- a/test/forward.m +++ b/test/forward.m @@ -1,12 +1,13 @@ +// TEST_CONFIG MEM=mrc,gc // TEST_CFLAGS -Wno-deprecated-declarations #include "test.h" -#ifdef __cplusplus +#if __cplusplus && !__clang__ int main() { - testwarn("c++ rdar://8366474 @selector(foo::)"); + // llvm-g++ is confused by @selector(foo::) and will never be fixed succeed(__FILE__); } @@ -15,28 +16,11 @@ int main() #include #include -struct stret { - int a; - int b; - int c; - int d; - int e; -}; - -BOOL stret_equal(struct stret a, struct stret b) -{ - return (a.a == b.a && - a.b == b.b && - a.c == b.c && - a.d == b.d && - a.e == b.e); -} - id ID_RESULT = (id)0x12345678; long long LL_RESULT = __LONG_LONG_MAX__ - 2LL*__INT_MAX__; -struct stret STRET_RESULT = {1, 2, 3, 4, 5}; double FP_RESULT = __DBL_MIN__ + __DBL_EPSILON__; long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; +// STRET_RESULT in test.h static int state = 0; @@ -229,7 +213,7 @@ struct stret forward_stret_handler(id self, SEL _cmd, long i1, long i2, long i3, @implementation Super +(void)initialize { } -+class { return self; } ++(id)class { return self; } -(long long) forward:(SEL)sel :(marg_list)args { @@ -251,7 +235,7 @@ struct stret forward_stret_handler(id self, SEL _cmd, long i1, long i2, long i3, testassert(self == receiver); testassert(_cmd == sel_registerName("forward::")); - p = args; + p = (char *)args; #if defined(__x86_64__) p += 8*16 + 4*8; // skip over xmm and linkage if (sel == @selector(stret::::::::::::::::::::::::::::) || @@ -310,7 +294,7 @@ struct stret forward_stret_handler(id self, SEL _cmd, long i1, long i2, long i3, #elif defined(__x86_64__) - fp = args; // xmm, double-wide + fp = (double *)args; // xmm, double-wide testassert(*fp++ == 1.0); fp++; testassert(*fp++ == 2.0); fp++; testassert(*fp++ == 3.0); fp++; @@ -396,8 +380,9 @@ typedef double (*fp_fn_t)(id self, SEL _cmd, long i1, long i2, long i3, long i4, typedef struct stret (*st_fn_t)(id self, SEL _cmd, long i1, long i2, long i3, long i4, long i5, long i6, long i7, long i8, long i9, long i10, long i11, long i12, long i13, double f1, double f2, double f3, double f4, double f5, double f6, double f7, double f8, double f9, double f10, double f11, double f12, double f13, double f14, double f15); - +__BEGIN_DECLS extern void *getSP(void); +__END_DECLS #if defined(__x86_64__) asm(".text \n _getSP: movq %rsp, %rax \n retq \n"); @@ -636,7 +621,7 @@ int main() // Test user-defined forward handler - objc_setForwardHandler(&forward_handler, &forward_stret_handler); + objc_setForwardHandler((void*)&forward_handler, (void*)&forward_stret_handler); state = 11; sp1 = getSP(); diff --git a/test/future.h b/test/future.h index 71470cd..a48dc9a 100644 --- a/test/future.h +++ b/test/future.h @@ -1,15 +1,11 @@ #include "test.h" -@interface Super { id isa; } -+class; -@end - -@interface Sub1 : Super +@interface Sub1 : TestRoot +(int)method; +(Class)classref; @end -@interface Sub2 : Super +@interface Sub2 : TestRoot +(int)method; +(Class)classref; @end diff --git a/test/future.m b/test/future.m index 2734e30..15adf5c 100644 --- a/test/future.m +++ b/test/future.m @@ -7,7 +7,19 @@ END */ #include "test.h" -#include + +#if __has_feature(objc_arc) + +int main() +{ + testwarn("rdar://10041403 future class API is not ARC-compatible"); + succeed(__FILE__); +} + + +#else + +#include #include #include #include @@ -30,7 +42,7 @@ END int main() { - Class oldSuper; + Class oldTestRoot; Class oldSub1; Class newSub1; #if !__OBJC2__ @@ -40,13 +52,13 @@ int main() #endif // objc_getFutureClass with existing class - oldSuper = objc_getFutureClass("Super"); - testassert(oldSuper == [Super class]); + oldTestRoot = objc_getFutureClass("TestRoot"); + testassert(oldTestRoot == [TestRoot class]); // objc_getFutureClass with missing class oldSub1 = objc_getFutureClass("Sub1"); testassert(oldSub1); - testassert(malloc_size(oldSub1) > 0); + testassert(malloc_size(objc_unretainedPointer(oldSub1)) > 0); testassert(objc_getClass("Sub1") == Nil); // objc_getFutureClass a second time @@ -92,3 +104,5 @@ int main() succeed(__FILE__); } + +#endif diff --git a/test/future0.m b/test/future0.m index c9aef34..4dd3146 100644 --- a/test/future0.m +++ b/test/future0.m @@ -1,7 +1,2 @@ #include "future.h" - -@implementation Super -+class { return self; } -+(void)initialize { } -@end - +#include "testroot.i" diff --git a/test/gcenforcer-nogc-1.m b/test/gcenforcer-nogc-1.m index 64b2b9c..1c6664b 100644 --- a/test/gcenforcer-nogc-1.m +++ b/test/gcenforcer-nogc-1.m @@ -1,14 +1,14 @@ // gc-off app loading gc-off dylib: should work /* -TEST_CONFIG GC=0 SDK=macos +TEST_CONFIG MEM=mrc,arc SDK=macos TEST_BUILD $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib - $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib - $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc - $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only - $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib + $C{COMPILE_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 index 9058ca1..4cde2e3 100644 --- a/test/gcenforcer-nogc-2.m +++ b/test/gcenforcer-nogc-2.m @@ -1,7 +1,7 @@ // gc-on app loading gc-off dylib: should crash /* -TEST_CONFIG GC=1 SDK=macos +TEST_CONFIG MEM=gc SDK=macos TEST_CRASHES TEST_RUN_OUTPUT @@ -12,7 +12,7 @@ END TEST_BUILD $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib - $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib + $C{COMPILE_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 diff --git a/test/gcenforcer-noobjc.m b/test/gcenforcer-noobjc.m index 46de0c6..c3398fb 100644 --- a/test/gcenforcer-noobjc.m +++ b/test/gcenforcer-noobjc.m @@ -3,10 +3,10 @@ TEST_CONFIG SDK=macos TEST_BUILD $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib - $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib - $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc - $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only - $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib + $C{COMPILE_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-requiresgc-1.m b/test/gcenforcer-requiresgc-1.m index 0cb971a..53eb978 100644 --- a/test/gcenforcer-requiresgc-1.m +++ b/test/gcenforcer-requiresgc-1.m @@ -2,7 +2,7 @@ // linker sees librequiresgc.fake.dylib, runtime uses librequiresgc.dylib /* -TEST_CONFIG GC=0 SDK=macos +TEST_CONFIG MEM=mrc,arc SDK=macos TEST_CRASHES TEST_RUN_OUTPUT @@ -13,10 +13,10 @@ END TEST_BUILD $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib - $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib - $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc - $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only - $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib + $C{COMPILE_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 index 3493e52..cd79d9a 100644 --- a/test/gcenforcer-requiresgc-2.m +++ b/test/gcenforcer-requiresgc-2.m @@ -2,11 +2,11 @@ // linker sees librequiresgc.fake.dylib, runtime uses librequiresgc.dylib /* -TEST_CONFIG GC=1 SDK=macos +TEST_CONFIG MEM=gc SDK=macos TEST_BUILD $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib - $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib + $C{COMPILE_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 diff --git a/test/gcenforcer-supportsgc.m b/test/gcenforcer-supportsgc.m index 92ff952..dadbbea 100644 --- a/test/gcenforcer-supportsgc.m +++ b/test/gcenforcer-supportsgc.m @@ -3,10 +3,10 @@ TEST_CONFIG SDK=macos TEST_BUILD $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib - $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib - $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc - $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only - $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib + $C{COMPILE_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 index f170933..4bdc14f 100644 --- a/test/gcenforcer.m +++ b/test/gcenforcer.m @@ -3,10 +3,10 @@ TEST_CONFIG SDK=macos TEST_BUILD $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib - $C{COMPILE_NOGC} $DIR/gc.m -dynamiclib -o libnogc.dylib - $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc - $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only - $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib + $C{COMPILE_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 diff --git a/test/gdb-lock.m b/test/gdb-lock.m index 322e45d..7170a8c 100644 --- a/test/gdb-lock.m +++ b/test/gdb-lock.m @@ -20,33 +20,33 @@ IMP fooIMP = method_getImplementation(foo); const char *fooTypes = method_getTypeEncoding(foo); while(1) { - NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init]; - char newSELName[100]; - sprintf(newSELName, "a%u", x++); - SEL newSEL = sel_registerName(newSELName); - class_addMethod([Foo class], newSEL, fooIMP, fooTypes); - [self performSelector: newSEL]; - [p drain]; + PUSH_POOL { + char newSELName[100]; + sprintf(newSELName, "a%u", x++); + SEL newSEL = sel_registerName(newSELName); + class_addMethod([Foo class], newSEL, fooIMP, fooTypes); + ((void(*)(id, SEL))objc_msgSend)(self, newSEL); + } POP_POOL; } } @end int main() { - NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init]; - [NSThread detachNewThreadSelector: @selector(test:) toTarget: [Foo new] withObject: nil]; - unsigned int x = 0; - unsigned int lockCount = 0; - while(1) { - if (gdb_objc_isRuntimeLocked()) - lockCount++; - x++; - if (x > 1000000) - break; - } - if (lockCount < 10) { - fail("Runtime not locked very much."); - } - [p drain]; + PUSH_POOL { + [NSThread detachNewThreadSelector: @selector(test:) toTarget: [Foo new] withObject: nil]; + unsigned int x = 0; + unsigned int lockCount = 0; + while(1) { + if (gdb_objc_isRuntimeLocked()) + lockCount++; + x++; + if (x > 1000000) + break; + } + if (lockCount < 10) { + fail("Runtime not locked very much."); + } + } POP_POOL; succeed(__FILE__); diff --git a/test/gdb.m b/test/gdb.m index 4305aa0..a5f571a 100644 --- a/test/gdb.m +++ b/test/gdb.m @@ -11,16 +11,10 @@ int main() #else +#include "testroot.i" #include #include -@interface Super { @public id isa; } @end -@implementation Super -+(void)initialize { } -+class { return self; } -@end - - int main() { // Class hashes @@ -30,17 +24,17 @@ int main() // Class should not be realized yet // fixme not true during class hash rearrangement - // result = NXMapGet(gdb_objc_realized_classes, "Super"); + // result = NXMapGet(gdb_objc_realized_classes, "TestRoot"); // testassert(!result); - [Super class]; + [TestRoot class]; // Now class should be realized - result = (Class)NXMapGet(gdb_objc_realized_classes, "Super"); + result = (Class)objc_unretainedObject(NXMapGet(gdb_objc_realized_classes, "TestRoot")); testassert(result); - testassert(result == [Super class]); + testassert(result == [TestRoot class]); - result = (Class)NXMapGet(gdb_objc_realized_classes, "DoesNotExist"); + result = (Class)objc_unretainedObject(NXMapGet(gdb_objc_realized_classes, "DoesNotExist")); testassert(!result); #else @@ -48,10 +42,10 @@ int main() struct objc_class query; Class result; - query.name = "Super"; + query.name = "TestRoot"; result = (Class)NXHashGet(_objc_debug_class_hash, &query); testassert(result); - testassert((id)result == [Super class]); + testassert((id)result == [TestRoot class]); query.name = "DoesNotExist"; result = (Class)NXHashGet(_objc_debug_class_hash, &query); diff --git a/test/getMethod.m b/test/getMethod.m index 88da706..e2a72ee 100644 --- a/test/getMethod.m +++ b/test/getMethod.m @@ -1,15 +1,14 @@ // TEST_CONFIG #include "test.h" +#include "testroot.i" #include #include static int state = 0; -@interface Super { id isa; } @end +@interface Super : TestRoot @end @implementation Super -+class { return self; } -+(void)initialize { } +(void)classMethod { state = 1; } -(void)instanceMethod { state = 4; } +(void)classMethodSuperOnly { state = 3; } @@ -22,6 +21,7 @@ static int state = 0; -(void)instanceMethod { state = 5; } @end +typedef void (*imp_t)(id, SEL); int main() { @@ -40,9 +40,9 @@ int main() testassert(m); testassert(sel == method_getName(m)); imp = method_getImplementation(m); - testassert(imp == class_getMethodImplementation(Super_cls->isa, sel)); + testassert(imp == class_getMethodImplementation(object_getClass(Super_cls), sel)); state = 0; - (*imp)(Super_cls, sel); + (*(imp_t)imp)(Super_cls, sel); testassert(state == 1); sel = sel_registerName("classMethod"); @@ -50,9 +50,9 @@ int main() testassert(m); testassert(sel == method_getName(m)); imp = method_getImplementation(m); - testassert(imp == class_getMethodImplementation(Sub_cls->isa, sel)); + testassert(imp == class_getMethodImplementation(object_getClass(Sub_cls), sel)); state = 0; - (*imp)(Sub_cls, sel); + (*(imp_t)imp)(Sub_cls, sel); testassert(state == 2); sel = sel_registerName("classMethodSuperOnly"); @@ -60,9 +60,9 @@ int main() testassert(m); testassert(sel == method_getName(m)); imp = method_getImplementation(m); - testassert(imp == class_getMethodImplementation(Sub_cls->isa, sel)); + testassert(imp == class_getMethodImplementation(object_getClass(Sub_cls), sel)); state = 0; - (*imp)(Sub_cls, sel); + (*(imp_t)imp)(Sub_cls, sel); testassert(state == 3); sel = sel_registerName("instanceMethod"); @@ -73,7 +73,7 @@ int main() testassert(imp == class_getMethodImplementation(Super_cls, sel)); state = 0; buf[0] = Super_cls; - (*imp)((Super *)buf, sel); + (*(imp_t)imp)(objc_unretainedObject(buf), sel); testassert(state == 4); sel = sel_registerName("instanceMethod"); @@ -84,7 +84,7 @@ int main() testassert(imp == class_getMethodImplementation(Sub_cls, sel)); state = 0; buf[0] = Sub_cls; - (*imp)((Sub *)buf, sel); + (*(imp_t)imp)(objc_unretainedObject(buf), sel); testassert(state == 5); sel = sel_registerName("instanceMethodSuperOnly"); @@ -95,12 +95,12 @@ int main() testassert(imp == class_getMethodImplementation(Sub_cls, sel)); state = 0; buf[0] = Sub_cls; - (*imp)((Sub *)buf, sel); + (*(imp_t)imp)(objc_unretainedObject(buf), sel); testassert(state == 6); // check class_getClassMethod(cls) == class_getInstanceMethod(cls->isa) sel = sel_registerName("classMethod"); - testassert(class_getClassMethod(Sub_cls, sel) == class_getInstanceMethod(Sub_cls->isa, sel)); + testassert(class_getClassMethod(Sub_cls, sel) == class_getInstanceMethod(object_getClass(Sub_cls), sel)); sel = sel_registerName("nonexistent"); testassert(! class_getInstanceMethod(Sub_cls, sel)); diff --git a/test/ignoredSelector.m b/test/ignoredSelector.m index 251bae8..c36a4b6 100644 --- a/test/ignoredSelector.m +++ b/test/ignoredSelector.m @@ -1,3 +1,4 @@ +// TEST_CONFIG MEM=mrc,gc // TEST_CFLAGS -Wno-deprecated-declarations #include "test.h" @@ -9,14 +10,14 @@ static int state = 0; @interface Super { id isa; } @end @implementation Super -+class { return self; } ++(id)class { return self; } +(void)initialize { } -+normal { state = 1; return self; } -+normal2 { testassert(0); } -+retain { state = 2; return self; } -+release { state = 3; return self; } -+autorelease { state = 4; return self; } ++(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 @@ -29,9 +30,9 @@ static int state = 0; @interface Empty { id isa; } @end @implementation Empty -+class { return self; } ++(id)class { return self; } +(void)initialize { } -+forward:(SEL)sel :(marg_list)margs { ++(id)forward:(SEL)sel :(marg_list)margs { (void)sel; (void)margs; state = 1; return nil; @@ -39,10 +40,10 @@ static int state = 0; @end @interface Empty (Unimplemented) -+normal; -+retain; -+release; -+autorelease; ++(id)ordinary; ++(id)retain; ++(void)release; ++(id)autorelease; +(void)dealloc; +(uintptr_t)retainCount; @end @@ -57,8 +58,8 @@ static int state = 0; } while (0) -static IMP normal, normal2, retain, release, autorelease, dealloc, retainCount; -static Method normalMethod, normal2Method, retainMethod, releaseMethod, autoreleaseMethod, deallocMethod, retainCountMethod; +static IMP ordinary, ordinary2, retain, release, autorelease, dealloc, retainCount; +static Method ordinaryMethod, ordinary2Method, retainMethod, releaseMethod, autoreleaseMethod, deallocMethod, retainCountMethod; void cycle(Class cls) { @@ -84,14 +85,14 @@ void cycle(Class cls) } // no ignored selector matches a real selector - testassert(@selector(normal) != @selector(retain) && - @selector(normal) != @selector(release) && - @selector(normal) != @selector(autorelease) && - @selector(normal) != @selector(dealloc) && - @selector(normal) != @selector(retainCount) ); - - getImp(normal); - getImp(normal2); + 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); @@ -114,15 +115,15 @@ void cycle(Class cls) } // no ignored selector IMP matches a real selector IMP - testassert(normal != retain && - normal != release && - normal != autorelease && - normal != dealloc && - normal != retainCount ); + testassert(ordinary != retain && + ordinary != release && + ordinary != autorelease && + ordinary != dealloc && + ordinary != retainCount ); // Test calls via method_invoke - idVal = ((id(*)(id, Method))method_invoke)(cls, normalMethod); + idVal = ((id(*)(id, Method))method_invoke)(cls, ordinaryMethod); testassert(state == 1); testassert(idVal == cls); @@ -131,9 +132,8 @@ void cycle(Class cls) testassert(state == (objc_collectingEnabled() ? 0 : 2)); testassert(idVal == cls); - idVal = ((id(*)(id, Method))method_invoke)(cls, releaseMethod); + (void) ((void(*)(id, Method))method_invoke)(cls, releaseMethod); testassert(state == (objc_collectingEnabled() ? 0 : 3)); - testassert(idVal == cls); idVal = ((id(*)(id, Method))method_invoke)(cls, autoreleaseMethod); testassert(state == (objc_collectingEnabled() ? 0 : 4)); @@ -150,7 +150,7 @@ void cycle(Class cls) // Test calls via compiled objc_msgSend state = 0; - idVal = [cls normal]; + idVal = [cls ordinary]; testassert(state == 1); testassert(idVal == cls); @@ -159,9 +159,8 @@ void cycle(Class cls) testassert(state == (objc_collectingEnabled() ? 0 : 2)); testassert(idVal == cls); - idVal = [cls release]; + (void) [cls release]; testassert(state == (objc_collectingEnabled() ? 0 : 3)); - testassert(idVal == cls); idVal = [cls autorelease]; testassert(state == (objc_collectingEnabled() ? 0 : 4)); @@ -177,7 +176,7 @@ void cycle(Class cls) // Test calls via handwritten objc_msgSend state = 0; - idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(normal)); + idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(ordinary)); testassert(state == 1); testassert(idVal == cls); @@ -186,15 +185,14 @@ void cycle(Class cls) testassert(state == (objc_collectingEnabled() ? 0 : 2)); testassert(idVal == cls); - idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(release)); + (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(release)); testassert(state == (objc_collectingEnabled() ? 0 : 3)); - testassert(idVal == cls); idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(autorelease)); testassert(state == (objc_collectingEnabled() ? 0 : 4)); testassert(idVal == cls); - (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(dealloc)); + (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)); @@ -245,13 +243,13 @@ int main() method_setImplementation(retainCountMethod, (IMP)1); cycle(cls); - testassert(normal2 != dealloc); - method_exchangeImplementations(retainMethod, releaseMethod); - method_exchangeImplementations(autoreleaseMethod, retainCountMethod); - method_exchangeImplementations(deallocMethod, normal2Method); + testassert(ordinary2 != retainCount); + method_exchangeImplementations(retainMethod, autoreleaseMethod); + method_exchangeImplementations(deallocMethod, releaseMethod); + method_exchangeImplementations(retainCountMethod, ordinary2Method); cycle(cls); - // normal2 exchanged with ignored method is now ignored too - testassert(normal2 == dealloc); + // ordinary2 exchanged with ignored method is now ignored too + testassert(ordinary2 == retainCount); // replace == replace existing class_replaceMethod(cls, @selector(retain), (IMP)1, ""); @@ -299,9 +297,8 @@ int main() testassert(state == 0); testassert(idVal == cls); - idVal = [Empty release]; + (void) [Empty release]; testassert(state == 0); - testassert(idVal == cls); idVal = [Empty autorelease]; testassert(state == 0); @@ -314,7 +311,7 @@ int main() testassert(state == 0); testassert(intVal == (uintptr_t)cls); - idVal = [Empty normal]; + idVal = [Empty ordinary]; testassert(state == 1); testassert(idVal == nil); @@ -324,22 +321,21 @@ int main() testassert(state == 0); testassert(idVal == cls); - idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(release)); + (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(release)); testassert(state == 0); - testassert(idVal == cls); idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(autorelease)); testassert(state == 0); testassert(idVal == cls); - (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(dealloc)); + (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(dealloc)); testassert(state == 0); intVal = ((uintptr_t(*)(id,SEL))objc_msgSend)(cls, @selector(retainCount)); testassert(state == 0); testassert(intVal == (uintptr_t)cls); - idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(normal)); + idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(ordinary)); testassert(state == 1); testassert(idVal == nil); } diff --git a/test/imageorder1.m b/test/imageorder1.m index 98ba1a4..2cc1d80 100644 --- a/test/imageorder1.m +++ b/test/imageorder1.m @@ -12,6 +12,12 @@ static void c1(void) cstate = 1; } + +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation" +#endif + @implementation Super (cat1) +(void) method { fail("+[Super(cat1) method] not replaced!"); @@ -25,6 +31,11 @@ static void c1(void) } @end +#if __clang__ +#pragma clang diagnostic pop +#endif + + @implementation Super +(void) initialize { } +(void) method { diff --git a/test/imageorder2.m b/test/imageorder2.m index 0b443a5..e4d690b 100644 --- a/test/imageorder2.m +++ b/test/imageorder2.m @@ -9,6 +9,12 @@ static void c2(void) cstate = 2; } + +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation" +#endif + @implementation Super (cat2) +(void) method { fail("+[Super(cat2) method] not replaced!"); @@ -21,3 +27,7 @@ static void c2(void) state = 2; } @end + +#if __clang__ +#pragma clang diagnostic pop +#endif diff --git a/test/imageorder3.m b/test/imageorder3.m index 15d9591..217130f 100644 --- a/test/imageorder3.m +++ b/test/imageorder3.m @@ -9,6 +9,12 @@ static void c3(void) cstate = 3; } + +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation" +#endif + @implementation Super (cat3) +(void) method { state = 3; @@ -21,3 +27,7 @@ static void c3(void) state = 3; } @end + +#if __clang__ +#pragma clang diagnostic pop +#endif diff --git a/test/initialize.m b/test/initialize.m index 849d2e0..c3bced1 100644 --- a/test/initialize.m +++ b/test/initialize.m @@ -6,18 +6,21 @@ // * superclass +initialize before subclass +initialize // * subclass inheritance of superclass implementation // * messaging during +initialize +// * +initialize provoked by class_getMethodImplementation +// * +initialize not provoked by objc_getClass #include "test.h" +#include "testroot.i" int state = 0; -@interface Super0 { } @end +@interface Super0 : TestRoot @end @implementation Super0 +(void)initialize { fail("objc_getClass() must not trigger +initialize"); } @end -@interface Super {} @end +@interface Super : TestRoot @end @implementation Super +(void)initialize { testprintf("in [Super initialize]\n"); @@ -29,7 +32,7 @@ int state = 0; } @end -@interface Sub : Super { } @end +@interface Sub : Super @end @implementation Sub +(void)initialize { testprintf("in [Sub initialize]\n"); @@ -44,11 +47,10 @@ int state = 0; @end -@interface Super2 { } @end -@interface Sub2 : Super2 { } @end +@interface Super2 : TestRoot @end +@interface Sub2 : Super2 @end @implementation Super2 -+(id)class { return self; } +(void)initialize { if (self == objc_getClass("Sub2")) { testprintf("in [Super2 initialize] of Sub2\n"); @@ -74,11 +76,10 @@ int state = 0; @end -@interface Super3 { } @end -@interface Sub3 : Super3 { } @end +@interface Super3 : TestRoot @end +@interface Sub3 : Super3 @end @implementation Super3 -+(id)class { return self; } +(void)initialize { if (self == [Sub3 class]) { // this message triggers [Sub3 initialize] testprintf("in [Super3 initialize] of Sub3\n"); @@ -103,8 +104,126 @@ int state = 0; // nothing here @end + +@interface Super4 : TestRoot @end +@implementation Super4 +-(void)instanceMethod { + testassert(state == 1); + state = 2; +} ++(void)initialize { + testprintf("in [Super4 initialize]\n"); + testassert(state == 0); + state = 1; + id x = [[self alloc] init]; + [x instanceMethod]; + RELEASE_VALUE(x); +} +@end + + +@interface Super5 : TestRoot @end +@implementation Super5 +-(void)instanceMethod { +} ++(void)classMethod { + testassert(state == 1); + state = 2; +} ++(void)initialize { + testprintf("in [Super5 initialize]\n"); + testassert(state == 0); + state = 1; + class_getMethodImplementation(self, @selector(instanceMethod)); + // this is the "memoized" case for getNonMetaClass + class_getMethodImplementation(object_getClass(self), @selector(classMethod)); + [self classMethod]; +} +@end + + +@interface Super6 : TestRoot @end +@interface Sub6 : Super6 @end +@implementation Super6 ++(void)initialize { + static bool once; + bool wasOnce; + testprintf("in [Super6 initialize] (#%d)\n", 1+(int)once); + if (!once) { + once = true; + wasOnce = true; + testassert(state == 0); + state = 1; + } else { + wasOnce = false; + testassert(state == 2); + state = 3; + } + [Sub6 class]; + if (wasOnce) { + testassert(state == 5); + state = 6; + } else { + testassert(state == 3); + state = 4; + } +} +@end +@implementation Sub6 ++(void)initialize { + testprintf("in [Sub6 initialize]\n"); + testassert(state == 1); + state = 2; + [super initialize]; + testassert(state == 4); + state = 5; +} +@end + + +@interface Super7 : TestRoot @end +@interface Sub7 : Super7 @end +@implementation Super7 ++(void)initialize { + static bool once; + bool wasOnce; + testprintf("in [Super7 initialize] (#%d)\n", 1+(int)once); + if (!once) { + once = true; + wasOnce = true; + testassert(state == 0); + state = 1; + } else { + wasOnce = false; + testassert(state == 2); + state = 3; + } + [Sub7 class]; + if (wasOnce) { + testassert(state == 5); + state = 6; + } else { + testassert(state == 3); + state = 4; + } +} +@end +@implementation Sub7 ++(void)initialize { + testprintf("in [Sub7 initialize]\n"); + testassert(state == 1); + state = 2; + [super initialize]; + testassert(state == 4); + state = 5; +} +@end + + int main() { + Class cls; + // objc_getClass() must not +initialize anything state = 0; objc_getClass("Super0"); @@ -125,6 +244,33 @@ int main() [Sub3 method]; testassert(state == 3); + // check class_getMethodImplementation (instance method) + state = 0; + cls = objc_getClass("Super4"); + testassert(state == 0); + class_getMethodImplementation(cls, @selector(classMethod)); + testassert(state == 2); + + // check class_getMethodImplementation (class method) + // this is the "slow" case for getNonMetaClass + state = 0; + cls = objc_getClass("Super5"); + testassert(state == 0); + class_getMethodImplementation(object_getClass(cls), @selector(instanceMethod)); + testassert(state == 2); + + // check +initialize cycles + // this is the "cls is a subclass" case for getNonMetaClass + state = 0; + [Super6 class]; + testassert(state == 6); + + // check +initialize cycles + // this is the "cls is a subclass" case for getNonMetaClass + state = 0; + [Sub7 class]; + testassert(state == 6); + succeed(__FILE__); return 0; diff --git a/test/instanceSize.m b/test/instanceSize.m index ef46cf4..8034a5c 100644 --- a/test/instanceSize.m +++ b/test/instanceSize.m @@ -1,19 +1,11 @@ // TEST_CONFIG #include "test.h" - +#include "testroot.i" #include -@interface Super { id isa; } @end -@implementation Super -+(void)initialize { } -+class { return self; } -+new { return class_createInstance(self, 0); } --(void)dealloc { object_dispose(self); } -@end - -@interface Sub1 : Super { +@interface Sub1 : TestRoot { // id isa; // 0..4 BOOL b; // 4..5 } @@ -41,25 +33,27 @@ int main() { - testassert(sizeof(id) == class_getInstanceSize([Super class])); + testassert(sizeof(id) == class_getInstanceSize([TestRoot class])); testassert(2*sizeof(id) == class_getInstanceSize([Sub1 class])); testassert(3*sizeof(id) == class_getInstanceSize([Sub2 class])); testassert(4*sizeof(id) == class_getInstanceSize([Sub3 class])); +#if !__has_feature(objc_arc) id o; - o = [Super new]; - testassert(object_getIndexedIvars(o) == (char *)o + class_getInstanceSize(o->isa)); - [o dealloc]; + o = [TestRoot new]; + testassert(object_getIndexedIvars(o) == (char *)o + class_getInstanceSize(object_getClass(o))); + RELEASE_VAR(o); o = [Sub1 new]; - testassert(object_getIndexedIvars(o) == (char *)o + class_getInstanceSize(o->isa)); - [o dealloc]; + testassert(object_getIndexedIvars(o) == (char *)o + class_getInstanceSize(object_getClass(o))); + RELEASE_VAR(o); o = [Sub2 new]; - testassert(object_getIndexedIvars(o) == (char *)o + class_getInstanceSize(o->isa)); - [o dealloc]; + testassert(object_getIndexedIvars(o) == (char *)o + class_getInstanceSize(object_getClass(o))); + RELEASE_VAR(o); o = [Sub3 new]; - testassert(object_getIndexedIvars(o) == (char *)o + class_getInstanceSize(o->isa)); - [o dealloc]; + testassert(object_getIndexedIvars(o) == (char *)o + class_getInstanceSize(object_getClass(o))); + RELEASE_VAR(o); +#endif succeed(__FILE__); } diff --git a/test/ismeta.m b/test/ismeta.m index 050778a..d0e580d 100644 --- a/test/ismeta.m +++ b/test/ismeta.m @@ -1,18 +1,13 @@ // TEST_CONFIG #include "test.h" +#include "testroot.i" #include -@interface Super { id isa; } @end -@implementation Super -+(void)initialize { } -+class { return self; } -@end - int main() { - testassert(!class_isMetaClass([Super class])); - testassert(class_isMetaClass([Super class]->isa)); + testassert(!class_isMetaClass([TestRoot class])); + testassert(class_isMetaClass(object_getClass([TestRoot class]))); testassert(!class_isMetaClass(nil)); succeed(__FILE__); } diff --git a/test/ivar.m b/test/ivar.m index a45b18b..84bda90 100644 --- a/test/ivar.m +++ b/test/ivar.m @@ -1,30 +1,24 @@ // TEST_CONFIG #include "test.h" +#include "testroot.i" #include #include #include -@interface Super { +@interface Super : TestRoot { @public - id isa; - char superIvar; + char superIvar; } @end -@implementation Super -+(void)initialize { } -+class { return self; } -+new { return class_createInstance(self, 0); } -@end - - @interface Sub : Super { @public - uintptr_t subIvar; + id subIvar; } @end +@implementation Super @end @implementation Sub @end @@ -38,22 +32,15 @@ int main() */ Ivar ivar; - uintptr_t value; Sub *sub = [Sub new]; - sub->subIvar = 10; - testassert(((uintptr_t *)sub)[2] == 10); + sub->subIvar = [Sub class]; + testassert(((Class *)objc_unretainedPointer(sub))[2] == [Sub class]); ivar = class_getInstanceVariable([Sub class], "subIvar"); testassert(ivar); testassert(2*sizeof(intptr_t) == (size_t)ivar_getOffset(ivar)); testassert(0 == strcmp(ivar_getName(ivar), "subIvar")); -#if __LP64__ - testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "Q")); -#elif __clang__ - testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "L")); -#else - testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "I")); -#endif + testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "@")); ivar = class_getInstanceVariable([Super class], "superIvar"); testassert(ivar); @@ -65,26 +52,13 @@ int main() ivar = class_getInstanceVariable([Super class], "subIvar"); testassert(!ivar); - ivar = class_getInstanceVariable([Sub class]->isa, "subIvar"); + ivar = class_getInstanceVariable(object_getClass([Sub class]), "subIvar"); testassert(!ivar); - - sub->subIvar = 10; - value = 0; - object_getInstanceVariable(sub, "subIvar", (void **)&value); - testassert(value == 10); - - object_setInstanceVariable(sub, "subIvar", (void *)11); - testassert(sub->subIvar == 11); - ivar = class_getInstanceVariable([Sub class], "subIvar"); - object_setIvar(sub, ivar, (id)12); - testassert(sub->subIvar == 12); - testassert((id)12 == object_getIvar(sub, ivar)); - - ivar = class_getInstanceVariable([Sub class], "subIvar"); - testassert(ivar == object_getInstanceVariable(sub, "subIvar", NULL)); - + object_setIvar(sub, ivar, sub); + testassert(sub->subIvar == sub); + testassert(sub == object_getIvar(sub, ivar)); testassert(NULL == class_getInstanceVariable(NULL, "foo")); testassert(NULL == class_getInstanceVariable([Sub class], NULL)); @@ -94,6 +68,25 @@ int main() testassert(NULL == object_getIvar(NULL, ivar)); testassert(NULL == object_getIvar(NULL, NULL)); + object_setIvar(sub, NULL, NULL); + object_setIvar(NULL, ivar, NULL); + object_setIvar(NULL, NULL, NULL); + +#if !__has_feature(objc_arc) + + uintptr_t value; + + sub->subIvar = (id)10; + value = 0; + object_getInstanceVariable(sub, "subIvar", (void **)&value); + testassert(value == 10); + + object_setInstanceVariable(sub, "subIvar", (id)11); + testassert(sub->subIvar == (id)11); + + ivar = class_getInstanceVariable([Sub class], "subIvar"); + testassert(ivar == object_getInstanceVariable(sub, "subIvar", NULL)); + testassert(NULL == object_getInstanceVariable(sub, NULL, NULL)); testassert(NULL == object_getInstanceVariable(NULL, "foo", NULL)); testassert(NULL == object_getInstanceVariable(NULL, NULL, NULL)); @@ -107,13 +100,10 @@ int main() testassert(NULL == object_getInstanceVariable(NULL, NULL, (void **)&value)); testassert(value == 0); - object_setIvar(sub, NULL, NULL); - object_setIvar(NULL, ivar, NULL); - object_setIvar(NULL, NULL, NULL); - testassert(NULL == object_setInstanceVariable(sub, NULL, NULL)); testassert(NULL == object_setInstanceVariable(NULL, "foo", NULL)); testassert(NULL == object_setInstanceVariable(NULL, NULL, NULL)); +#endif succeed(__FILE__); return 0; diff --git a/test/ivarSlide.h b/test/ivarSlide.h index c0e5a13..d4e89ca 100644 --- a/test/ivarSlide.h +++ b/test/ivarSlide.h @@ -1,14 +1,4 @@ -@interface Base { - @public - id isa; -} - -+class; -+new; --(void)dealloc; -@end - -@interface Super : Base { +@interface Super : TestRoot { @public #if OLD // nothing @@ -19,7 +9,7 @@ @end -@interface ShrinkingSuper : Base { +@interface ShrinkingSuper : TestRoot { @public #if OLD id superIvar[5]; @@ -31,7 +21,7 @@ @end; -@interface MoreStrongSuper : Base { +@interface MoreStrongSuper : TestRoot { @public #if OLD void *superIvar; @@ -42,7 +32,7 @@ @end; -@interface MoreWeakSuper : Base { +@interface MoreWeakSuper : TestRoot { @public #if OLD id superIvar; @@ -52,7 +42,7 @@ } @end; -@interface MoreWeak2Super : Base { +@interface MoreWeak2Super : TestRoot { @public #if OLD void *superIvar; @@ -62,7 +52,7 @@ } @end; -@interface LessStrongSuper : Base { +@interface LessStrongSuper : TestRoot { @public #if OLD id superIvar; @@ -72,7 +62,7 @@ } @end; -@interface LessWeakSuper : Base { +@interface LessWeakSuper : TestRoot { @public #if OLD __weak id superIvar; @@ -82,7 +72,7 @@ } @end; -@interface LessWeak2Super : Base { +@interface LessWeak2Super : TestRoot { @public #if OLD __weak id superIvar; @@ -92,7 +82,7 @@ } @end; -@interface NoGCChangeSuper : Base { +@interface NoGCChangeSuper : TestRoot { @public intptr_t d; char superc1; @@ -104,7 +94,7 @@ } @end -@interface RunsOf15 : Base { +@interface RunsOf15 : TestRoot { @public id scan1; intptr_t skip15[15]; diff --git a/test/ivarSlide.m b/test/ivarSlide.m index 056f6f8..88867d9 100644 --- a/test/ivarSlide.m +++ b/test/ivarSlide.m @@ -10,6 +10,15 @@ END #include #include +// 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 OLD 1 #include "ivarSlide.h" @@ -47,8 +56,8 @@ uintptr_t CXX::count; @interface Sub : Super { @public uintptr_t subIvar; - __strong void* subIvar2; - __weak void* subIvar3; + gc_strong void* subIvar2; + gc_weak void* subIvar3; #ifdef __cplusplus CXX cxx; #else @@ -63,8 +72,8 @@ uintptr_t CXX::count; @interface Sub2 : ShrinkingSuper { @public - __weak void* subIvar; - __strong void* subIvar2; + gc_weak void* subIvar; + gc_strong void* subIvar2; } @end @@ -103,6 +112,10 @@ int main(int argc __attribute__((unused)), char **argv) { #if __OBJC2__ +#if __has_feature(objc_arc) + testwarn("fixme check ARC layouts too"); +#endif + /* Bitfield ivars. rdar://5723893 anonymous bitfield ivars crash when slid @@ -165,20 +178,24 @@ int main(int argc __attribute__((unused)), char **argv) static Sub * volatile sub; sub = [Sub new]; sub->subIvar = 10; - testassert(((uintptr_t *)sub)[2] == 10); + testassert(((uintptr_t *)objc_unretainedPointer(sub))[2] == 10); #ifdef __cplusplus - testassert(((uintptr_t *)sub)[5] == 1); + testassert(((uintptr_t *)objc_unretainedPointer(sub))[5] == 1); testassert(sub->cxx.magic == 1); sub->cxx.magic++; - testassert(((uintptr_t *)sub)[5] == 2); + testassert(((uintptr_t *)objc_unretainedPointer(sub))[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); } +# endif testassert(CXX::count == 2); #endif @@ -247,7 +264,7 @@ int main(int argc __attribute__((unused)), char **argv) Sub2 *sub2 = [Sub2 new]; sub2->isa = [Sub2 class]; sub2->subIvar = (void *)10; - testassert(((uintptr_t *)sub2)[11] == 10); + testassert(((uintptr_t *)objc_unretainedPointer(sub2))[11] == 10); testassert(class_getInstanceSize([Sub2 class]) == 13*sizeof(void*)); diff --git a/test/ivarSlide1.m b/test/ivarSlide1.m index 59299a0..47b2e63 100644 --- a/test/ivarSlide1.m +++ b/test/ivarSlide1.m @@ -5,13 +5,7 @@ #define OLD 0 #include "ivarSlide.h" -@implementation Base -+(void)initialize { } -+class { return self; } -+new { return class_createInstance(self, 0); } --(void)dealloc { object_dispose(self); } --(void)finalize { } -@end +#include "testroot.i" @implementation Super @end diff --git a/test/layout.m b/test/layout.m index a2f7ac5..662513e 100644 --- a/test/layout.m +++ b/test/layout.m @@ -1,4 +1,4 @@ -// TEST_CONFIG GC=1 SDK=macos +// TEST_CONFIG MEM=gc SDK=macos #include "test.h" #include diff --git a/test/literals.m b/test/literals.m new file mode 100644 index 0000000..833948c --- /dev/null +++ b/test/literals.m @@ -0,0 +1,29 @@ +// TEST_CONFIG MEM=arc,mrc CC=clang LANGUAGE=objc,objc++ +// TEST_CFLAGS -framework Foundation + +#import +#import +#import +#import +#import +#include "test.h" + +int main() { + PUSH_POOL { + +#if __has_feature(objc_bool) // placeholder until we get a more precise macro. + NSArray *array = @[ @1, @2, @YES, @NO, @"Hello", @"World" ]; + testassert([array count] == 6); + NSDictionary *dict = @{ @"Name" : @"John Q. Public", @"Age" : @42 }; + testassert([dict count] == 2); + NSDictionary *numbers = @{ @"π" : @M_PI, @"e" : @M_E }; + testassert([[numbers objectForKey:@"π"] doubleValue] == M_PI); + testassert([[numbers objectForKey:@"e"] doubleValue] == M_E); +#endif + + } POP_POOL; + + succeed(__FILE__); + + return 0; +} diff --git a/test/load.m b/test/load.m index 7d97815..92bcdfe 100644 --- a/test/load.m +++ b/test/load.m @@ -1,13 +1,30 @@ // TEST_CONFIG #include "test.h" +#include "testroot.i" int state = 0; int catstate = 0; +int deallocstate = 0; -@interface Super { id isa; } @end +@interface Deallocator : TestRoot @end +@implementation Deallocator +-(id)init { + self = [super init]; + if (objc_collectingEnabled()) { + deallocstate = 1; + } + return self; +} +-(void)dealloc { + deallocstate = 1; + SUPER_DEALLOC(); +} +@end + + +@interface Super : TestRoot @end @implementation Super -+class { return self; } +(void)initialize { if (self == [Super class]) { testprintf("in +[Super initialize]\n"); @@ -58,6 +75,10 @@ int catstate = 0; testprintf("in +[Sub(Category) load]\n"); testassert(state >= 2); catstate++; + + // test autorelease pool + __autoreleasing id x; + x = AUTORELEASE([Deallocator new]); } @end @@ -76,6 +97,7 @@ int main() { testassert(state == 2); testassert(catstate == 3); + testassert(deallocstate == 1); [Sub class]; testassert(state == 4); testassert(catstate == 3); diff --git a/test/methodArgs.m b/test/methodArgs.m index 3e3635d..8948e9a 100644 --- a/test/methodArgs.m +++ b/test/methodArgs.m @@ -1,15 +1,14 @@ // TEST_CFLAGS -Wno-deprecated-declarations #include "test.h" +#include "testroot.i" #include #include -@interface Super { id isa; } @end +@interface Super : TestRoot @end @implementation Super -+(void)initialize { } -+class { return self; } -+(id)method:(int)arg :(void(^)(void))arg2 { - return (id)((intptr_t)arg+(intptr_t)arg2); ++(id)method:(int)__unused arg :(void(^)(void)) __unused arg2 { + return 0; } @end diff --git a/test/methodListSize.m b/test/methodListSize.m index 05216ad..3821844 100644 --- a/test/methodListSize.m +++ b/test/methodListSize.m @@ -1,11 +1,10 @@ // TEST_CONFIG - // rdar://8052003 rdar://8077031 -#include +#include "test.h" + #include #include -#include "test.h" // add SELCOUNT methods to each of CLASSCOUNT classes #define CLASSCOUNT 100 @@ -54,3 +53,4 @@ int main() succeed(__FILE__); } + diff --git a/test/method_getName.m b/test/method_getName.m index 3d12119..45f6581 100644 --- a/test/method_getName.m +++ b/test/method_getName.m @@ -1,4 +1,4 @@ -// TEST_CFLAGS -framework Foundation +// TEST_CONFIG #include "test.h" #include diff --git a/test/msgSend.m b/test/msgSend.m index 583a622..115264a 100644 --- a/test/msgSend.m +++ b/test/msgSend.m @@ -1,19 +1,21 @@ // TEST_CONFIG #include "test.h" +#include "testroot.i" -#ifdef __cplusplus +#if __cplusplus && !__clang__ int main() { - testwarn("c++ rdar://8366474 @selector(foo::)"); + // llvm-g++ is confused by @selector(foo::) and will never be fixed succeed(__FILE__); } #else #include -#include +#include +#include #include #if defined(__arm__) @@ -23,18 +25,25 @@ int main() # define ALIGN_() asm(".align 4"); #endif -@interface Super { id isa; } -+class; -@end +@interface Super : TestRoot @end @interface Sub : Super @end static int state = 0; +static id SELF; + +// for typeof() shorthand only +id (*idmsg0)(id, SEL) __attribute__((unused)); +long long (*llmsg0)(id, SEL) __attribute__((unused)); +// struct stret (*stretmsg0)(id, SEL) __attribute__((unused)); +double (*fpmsg0)(id, SEL) __attribute__((unused)); +long double (*lfpmsg0)(id, SEL) __attribute__((unused)); + -#define CHECK_ARGS(cls, sel) \ +#define CHECK_ARGS(sel) \ do { \ - testassert(self == [cls class]); \ + testassert(self == SELF); \ testassert(_cmd == sel_registerName(#sel "::::::::::::::::::::::::::::"));\ testassert(i1 == 1); \ testassert(i2 == 2); \ @@ -66,193 +75,196 @@ do { \ testassert(f15 == 15.0); \ } while (0) -#define CHECK_ARGS_NOARG(cls, sel) \ +#define CHECK_ARGS_NOARG(sel) \ do { \ - testassert(self == [cls class]); \ + testassert(self == SELF); \ testassert(_cmd == sel_registerName(#sel "_noarg"));\ } while (0) -struct stret { - int a; - int b; - int c; - int d; - int e; -}; - -BOOL stret_equal(struct stret a, struct stret b) -{ - return (a.a == b.a && - a.b == b.b && - a.c == b.c && - a.d == b.d && - a.e == b.e); -} - -id ID_RESULT = (id)0x12345678; +id ID_RESULT; long long LL_RESULT = __LONG_LONG_MAX__ - 2LL*__INT_MAX__; -struct stret STRET_RESULT = {1, 2, 3, 4, 5}; double FP_RESULT = __DBL_MIN__ + __DBL_EPSILON__; long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; +// STRET_RESULT in test.h + +static struct stret zero; @implementation Super -+class { return self; } -+(struct stret)stret { return STRET_RESULT; } -+(void)initialize { } +-(struct stret)stret { return STRET_RESULT; } -+(id)idret: +-(id)idret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { - if (state == 100) CHECK_ARGS(Sub, idret); - else CHECK_ARGS(Super, idret); + CHECK_ARGS(idret); state = 1; return ID_RESULT; } -+(long long)llret: +-(long long)llret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { - if (state == 100) CHECK_ARGS(Sub, llret); - else CHECK_ARGS(Super, llret); + CHECK_ARGS(llret); state = 2; return LL_RESULT; } -+(struct stret)stret: +-(struct stret)stret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { - if (state == 100) CHECK_ARGS(Sub, stret); - else CHECK_ARGS(Super, stret); + CHECK_ARGS(stret); state = 3; return STRET_RESULT; } -+(double)fpret: +-(double)fpret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { - if (state == 100) CHECK_ARGS(Sub, fpret); - else CHECK_ARGS(Super, fpret); + CHECK_ARGS(fpret); state = 4; return FP_RESULT; } -+(long double)lfpret: +-(long double)lfpret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { - if (state == 100) CHECK_ARGS(Sub, lfpret); - else CHECK_ARGS(Super, lfpret); + CHECK_ARGS(lfpret); state = 5; return LFP_RESULT; } -+(id)idret_noarg +-(id)idret_noarg { - if (state == 100) CHECK_ARGS_NOARG(Sub, idret); - else CHECK_ARGS_NOARG(Super, idret); + CHECK_ARGS_NOARG(idret); state = 11; return ID_RESULT; } -+(long long)llret_noarg +-(long long)llret_noarg { - if (state == 100) CHECK_ARGS_NOARG(Sub, llret); - else CHECK_ARGS_NOARG(Super, llret); + CHECK_ARGS_NOARG(llret); state = 12; return LL_RESULT; } -/* no objc_msgSend_stret_noarg -+(struct stret)stret_noarg +-(struct stret)stret_noarg { - if (state == 100) CHECK_ARGS_NOARG(Sub, stret); - else CHECK_ARGS_NOARG(Super, stret); + CHECK_ARGS_NOARG(stret); state = 13; return STRET_RESULT; } -*/ -+(double)fpret_noarg +-(double)fpret_noarg { - if (state == 100) CHECK_ARGS_NOARG(Sub, fpret); - else CHECK_ARGS_NOARG(Super, fpret); + CHECK_ARGS_NOARG(fpret); state = 14; return FP_RESULT; } -+(long double)lfpret_noarg +-(long double)lfpret_noarg { - if (state == 100) CHECK_ARGS_NOARG(Sub, lfpret); - else CHECK_ARGS_NOARG(Super, lfpret); + CHECK_ARGS_NOARG(lfpret); state = 15; return LFP_RESULT; } --(id)idret: +-(void)voidret_nop +{ + 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; +} + + + ++(id)idret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { - fail("-idret called instead of +idret"); - CHECK_ARGS(Super, idret); + fail("+idret called instead of -idret"); + CHECK_ARGS(idret); } --(long long)llret: ++(long long)llret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { - fail("-llret called instead of +llret"); - CHECK_ARGS(Super, llret); + fail("+llret called instead of -llret"); + CHECK_ARGS(llret); } --(struct stret)stret: ++(struct stret)stret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { - fail("-stret called instead of +stret"); - CHECK_ARGS(Super, stret); + fail("+stret called instead of -stret"); + CHECK_ARGS(stret); } --(double)fpret: ++(double)fpret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { - fail("-fpret called instead of +fpret"); - CHECK_ARGS(Super, fpret); + fail("+fpret called instead of -fpret"); + CHECK_ARGS(fpret); } --(long double)lfpret: ++(long double)lfpret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { - fail("-lfpret called instead of +lfpret"); - CHECK_ARGS(Super, lfpret); + fail("+lfpret called instead of -lfpret"); + CHECK_ARGS(lfpret); } --(id)idret_noarg ++(id)idret_noarg { - fail("-idret_ called instead of +idret_noarg"); - CHECK_ARGS_NOARG(Super, idret); + fail("+idret_noarg called instead of -idret_noarg"); + CHECK_ARGS_NOARG(idret); } --(long long)llret_noarg ++(long long)llret_noarg { - fail("-llret_noarg called instead of +llret_noarg"); - CHECK_ARGS_NOARG(Super, llret); + fail("+llret_noarg called instead of -llret_noarg"); + CHECK_ARGS_NOARG(llret); } --(struct stret)stret_noarg ++(struct stret)stret_noarg { - fail("-stret_noarg called instead of +stret_noarg"); - CHECK_ARGS_NOARG(Super, stret); + fail("+stret_noarg called instead of -stret_noarg"); + CHECK_ARGS_NOARG(stret); } --(double)fpret_noarg ++(double)fpret_noarg { - fail("-fpret_noarg called instead of +fpret_noarg"); - CHECK_ARGS_NOARG(Super, fpret); + fail("+fpret_noarg called instead of -fpret_noarg"); + CHECK_ARGS_NOARG(fpret); } --(long double)lfpret_noarg ++(long double)lfpret_noarg { - fail("-lfpret_noarg called instead of +lfpret_noarg"); - CHECK_ARGS_NOARG(Super, lfpret); + fail("+lfpret_noarg called instead of -lfpret_noarg"); + CHECK_ARGS_NOARG(lfpret); } @end @@ -260,11 +272,11 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; @implementation Sub -+(id)idret: +-(id)idret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { id result; - CHECK_ARGS(Sub, idret); + CHECK_ARGS(idret); state = 100; result = [super idret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15]; testassert(state == 1); @@ -273,11 +285,11 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; return result; } -+(long long)llret: +-(long long)llret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { long long result; - CHECK_ARGS(Sub, llret); + CHECK_ARGS(llret); state = 100; result = [super llret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15]; testassert(state == 2); @@ -286,11 +298,11 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; return result; } -+(struct stret)stret: +-(struct stret)stret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { struct stret result; - CHECK_ARGS(Sub, stret); + CHECK_ARGS(stret); state = 100; result = [super stret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15]; testassert(state == 3); @@ -299,11 +311,11 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; return result; } -+(double)fpret: +-(double)fpret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { double result; - CHECK_ARGS(Sub, fpret); + CHECK_ARGS(fpret); state = 100; result = [super fpret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15]; testassert(state == 4); @@ -312,11 +324,11 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; return result; } -+(long double)lfpret: +-(long double)lfpret: (int)i1:(int)i2:(int)i3:(int)i4:(int)i5:(int)i6:(int)i7:(int)i8:(int)i9:(int)i10:(int)i11:(int)i12:(int)i13 :(double)f1:(double)f2:(double)f3:(double)f4:(double)f5:(double)f6:(double)f7:(double)f8:(double)f9:(double)f10:(double)f11:(double)f12:(double)f13:(double)f14:(double)f15 { long double result; - CHECK_ARGS(Sub, lfpret); + CHECK_ARGS(lfpret); state = 100; result = [super lfpret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15]; testassert(state == 5); @@ -326,10 +338,10 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; } -+(id)idret_noarg +-(id)idret_noarg { id result; - CHECK_ARGS_NOARG(Sub, idret); + CHECK_ARGS_NOARG(idret); state = 100; result = [super idret_noarg]; testassert(state == 11); @@ -338,10 +350,10 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; return result; } -+(long long)llret_noarg +-(long long)llret_noarg { long long result; - CHECK_ARGS_NOARG(Sub, llret); + CHECK_ARGS_NOARG(llret); state = 100; result = [super llret_noarg]; testassert(state == 12); @@ -349,11 +361,11 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; state = 112; return result; } -/* -+(struct stret)stret_noarg + +-(struct stret)stret_noarg { struct stret result; - CHECK_ARGS_NOARG(Sub, stret); + CHECK_ARGS_NOARG(stret); state = 100; result = [super stret_noarg]; testassert(state == 13); @@ -361,11 +373,11 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; state = 113; return result; } -*/ -+(double)fpret_noarg + +-(double)fpret_noarg { double result; - CHECK_ARGS_NOARG(Sub, fpret); + CHECK_ARGS_NOARG(fpret); state = 100; result = [super fpret_noarg]; testassert(state == 14); @@ -374,10 +386,10 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; return result; } -+(long double)lfpret_noarg +-(long double)lfpret_noarg { long double result; - CHECK_ARGS_NOARG(Sub, lfpret); + CHECK_ARGS_NOARG(lfpret); state = 100; result = [super lfpret_noarg]; testassert(state == 15); @@ -386,79 +398,350 @@ long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; return result; } +@end + + +#if __x86_64__ +@interface TaggedSub : Sub @end -// performance-test code (do nothing for better comparability) +@implementation TaggedSub : Sub -+(id)idret_perf +#define TAG_VALUE(tagSlot, value) (objc_unretainedObject((void*)(1UL | (((uintptr_t)(tagSlot)) << 1) | (((uintptr_t)(value)) << 4)))) + ++(void)initialize { - return ID_RESULT; + _objc_insert_tagged_isa(2, self); } -+(long long)llret_perf +@end + +// DWARF checking machinery + +#include +#include +#include +#include + +#define UNW_STEP_SUCCESS 1 +#define UNW_STEP_END 0 + +bool caught = false; +uintptr_t clobbered; + +uintptr_t r12 = 0; +uintptr_t r13 = 0; +uintptr_t r14 = 0; +uintptr_t r15 = 0; +uintptr_t rbx = 0; +uintptr_t rbp = 0; +uintptr_t rsp = 0; +uintptr_t rip = 0; + +void handle_exception(x86_thread_state64_t *state) { - return LL_RESULT; + unw_cursor_t curs; + unw_word_t reg; + int err; + int step; + + err = unw_init_local(&curs, (unw_context_t *)state); + testassert(!err); + + step = unw_step(&curs); + testassert(step == UNW_STEP_SUCCESS); + + err = unw_get_reg(&curs, UNW_X86_64_R12, ®); + testassert(!err); + testassert(reg == r12); + + err = unw_get_reg(&curs, UNW_X86_64_R13, ®); + testassert(!err); + testassert(reg == r13); + + err = unw_get_reg(&curs, UNW_X86_64_R14, ®); + testassert(!err); + testassert(reg == r14); + + err = unw_get_reg(&curs, UNW_X86_64_R15, ®); + testassert(!err); + testassert(reg == r15); + + err = unw_get_reg(&curs, UNW_X86_64_RBX, ®); + testassert(!err); + testassert(reg == rbx); + + err = unw_get_reg(&curs, UNW_X86_64_RBP, ®); + testassert(!err); + testassert(reg == rbp); + + err = unw_get_reg(&curs, UNW_X86_64_RSP, ®); + testassert(!err); + testassert(reg == rsp); + + err = unw_get_reg(&curs, UNW_REG_IP, ®); + testassert(!err); + testassert(reg == rip); + + + // set thread state to unwound state + state->__r12 = r12; + state->__r13 = r13; + state->__r14 = r14; + state->__r15 = r15; + state->__rbx = rbx; + state->__rbp = rbp; + state->__rsp = rsp; + state->__rip = rip; + + caught = true; } -+(struct stret)stret_perf + +void sigtrap(int sig, siginfo_t *info, void *cc) { - return STRET_RESULT; + 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); + + handle_exception(&mc->__ss); + // handle_exception changed register state for continuation } -+(double)fpret_perf + +uint8_t set(uintptr_t dst, uint8_t newvalue) { - return FP_RESULT; + uintptr_t start = dst & ~(4096-1); + mprotect((void*)start, 4096, PROT_READ|PROT_WRITE); + // int3 + uint8_t oldvalue = *(uint8_t *)dst; + *(uint8_t *)dst = newvalue; + mprotect((void*)start, 4096, PROT_READ|PROT_EXEC); + return oldvalue; } -+(long double)lfpret_perf +uint8_t clobber(void *fn, uintptr_t offset) { - return LFP_RESULT; + clobbered = (uintptr_t)fn + offset; + return set((uintptr_t)fn + offset, 0xcc /*int3*/); } -@end +void unclobber(void *fn, uintptr_t offset, uint8_t oldvalue) +{ + set((uintptr_t)fn + offset, oldvalue); +} -int main() +__BEGIN_DECLS +extern void callit(void *obj, void *sel, void *fn); +extern struct stret callit_stret(void *obj, void *sel, void *fn); +__END_DECLS + +__asm__( +"\n .text" +"\n .globl _callit" +"\n _callit:" +// save rsp and rip registers to variables +"\n movq (%rsp), %r10" +"\n movq %r10, _rip(%rip)" +"\n movq %rsp, _rsp(%rip)" +"\n addq $8, _rsp(%rip)" // rewind to pre-call value +// save other non-volatile registers to variables +"\n movq %rbx, _rbx(%rip)" +"\n movq %rbp, _rbp(%rip)" +"\n movq %r12, _r12(%rip)" +"\n movq %r13, _r13(%rip)" +"\n movq %r14, _r14(%rip)" +"\n movq %r15, _r15(%rip)" +"\n jmpq *%rdx" + ); + +__asm__( +"\n .text" +"\n .globl _callit_stret" +"\n _callit_stret:" +// save rsp and rip registers to variables +"\n movq (%rsp), %r10" +"\n movq %r10, _rip(%rip)" +"\n movq %rsp, _rsp(%rip)" +"\n addq $8, _rsp(%rip)" // rewind to pre-call value +// save other non-volatile registers to variables +"\n movq %rbx, _rbx(%rip)" +"\n movq %rbp, _rbp(%rip)" +"\n movq %r12, _r12(%rip)" +"\n movq %r13, _r13(%rip)" +"\n movq %r14, _r14(%rip)" +"\n movq %r15, _r15(%rip)" +"\n jmpq *%rcx" + ); + +uintptr_t *getOffsets(void *symbol, const char *symname) { - int i; + uintptr_t *result = (uintptr_t *)malloc(4096 * sizeof(uintptr_t)); + uintptr_t *end = result + 4096; + uintptr_t *p = result; + + // find library + Dl_info dl; + dladdr(symbol, &dl); + + // call `otool` on library + unsetenv("DYLD_LIBRARY_PATH"); + unsetenv("DYLD_ROOT_PATH"); + unsetenv("DYLD_INSERT_LIBRARIES"); + char *cmd; + asprintf(&cmd, "/usr/bin/xcrun otool -arch x86_64 -tv -p _%s %s", + symname, dl.dli_fname); + FILE *disa = popen(cmd, "r"); + free(cmd); + testassert(disa); + + // read past "_symname:" line + char *line; + size_t len; + while ((line = fgetln(disa, &len))) { + if (0 == strncmp(1+line, symname, MIN(len-1, strlen(symname)))) break; + } - id idval; - long long llval; - struct stret stretval; - double fpval; - long double lfpval; + // read instructions and save offsets + char op[128]; + long base = 0; + long addr; + while (2 == fscanf(disa, "%lx%s%*[^\n]\n", &addr, op)) { + if (base == 0) base = addr; + if (0 != strncmp(op, "nop", 3)) { + testassert(p < end); + *p++ = addr - base; + } else { + // assume nops are unreached (e.g. alignment padding) + } + } + pclose(disa); - uint64_t startTime; - uint64_t totalTime; - uint64_t targetTime; + testassert(p > result); + testassert(p < end); + *p = ~0UL; + return result; +} - Method idmethod; - Method llmethod; - Method stretmethod; - Method fpmethod; - Method lfpmethod; +void CALLIT(void *o, void *sel_arg, SEL s, void *f) __attribute__((noinline)); +void CALLIT(void *o, void *sel_arg, SEL s, void *f) +{ + uintptr_t message_ref[2]; + if (sel_arg != s) { + // fixup dispatch + // copy to a local buffer to keep sel_arg un-fixed-up + memcpy(message_ref, sel_arg, sizeof(message_ref)); + sel_arg = message_ref; + } + if (s == @selector(idret_nop)) callit(o, sel_arg, f); + else if (s == @selector(fpret_nop)) callit(o, sel_arg, f); + else if (s == @selector(stret_nop)) callit_stret(o, sel_arg, f); + else fail("test_dw selector"); +} - id (*idfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); - long long (*llfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); - struct stret (*stretfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); - double (*fpfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); - long double (*lfpfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); +// sub = ordinary receiver object +// tagged = tagged receiver object +// SEL = selector to send +// sub_arg = arg to pass in receiver register (may be objc_super struct) +// tagged_arg = arg to pass in receiver register (may be objc_super struct) +// sel_arg = arg to pass in sel register (may be message_ref) +void test_dw(const char *name, id sub, id tagged, SEL sel) +{ + testprintf("DWARF FOR %s\n", name); - id (*idmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); - long long (*llmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); - struct stret (*stretmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); - double (*fpmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); - long double (*lfpmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); + void *fn = dlsym(RTLD_DEFAULT, name); + testassert(fn); - id (*idmsg0)(id, SEL) __attribute__((unused)); - long long (*llmsg0)(id, SEL) __attribute__((unused)); - // struct stret (*stretmsg0)(id, SEL) __attribute__((unused)); - double (*fpmsg0)(id, SEL) __attribute__((unused)); - long double (*lfpmsg0)(id, SEL) __attribute__((unused)); + // argument substitutions - struct stret zero = {0, 0, 0, 0, 0}; + void *sub_arg = (void*)objc_unretainedPointer(sub); + void *tagged_arg = (void*)objc_unretainedPointer(tagged); + void *sel_arg = (void*)sel; - // get +initialize out of the way - [Sub class]; + struct objc_super sup_st = { sub, object_getClass(sub) }; + struct objc_super tagged_sup_st = { tagged, object_getClass(tagged) }; + struct { void *imp; SEL sel; } message_ref = { fn, sel }; + + if (strstr(name, "Super")) { + // super version - replace receiver with objc_super + sub_arg = &sup_st; + tagged_arg = &tagged_sup_st; + } + + if (strstr(name, "_fixup")) { + // fixup version - replace sel with message_ref + sel_arg = &message_ref; + } + + + uintptr_t *insnOffsets = getOffsets(fn, name); + uintptr_t *offsetp = insnOffsets; + uintptr_t offset; + while ((offset = *offsetp++) != ~0UL) { + testprintf("OFFSET %lu\n", offset); + + uint8_t insn_byte = clobber(fn, offset); + caught = false; + + // nil + if ((void*)objc_unretainedPointer(sub) == sub_arg) { + SELF = nil; + testprintf(" nil\n"); + CALLIT(nil, sel_arg, sel, fn); + CALLIT(nil, sel_arg, sel, fn); + } + + // uncached + SELF = sub; + testprintf(" uncached\n"); + _objc_flush_caches(object_getClass(sub)); + CALLIT(sub_arg, sel_arg, sel, fn); + _objc_flush_caches(object_getClass(sub)); + CALLIT(sub_arg, sel_arg, sel, fn); + + // cached + SELF = sub; + testprintf(" cached\n"); + CALLIT(sub_arg, sel_arg, sel, fn); + CALLIT(sub_arg, sel_arg, sel, fn); + + // uncached,tagged + SELF = tagged; + testprintf(" uncached,tagged\n"); + _objc_flush_caches(object_getClass(tagged)); + CALLIT(tagged_arg, sel_arg, sel, fn); + _objc_flush_caches(object_getClass(tagged)); + CALLIT(tagged_arg, sel_arg, sel, fn); + + // cached,tagged + SELF = tagged; + testprintf(" cached,tagged\n"); + CALLIT(tagged_arg, sel_arg, sel, fn); + CALLIT(tagged_arg, sel_arg, sel, fn); + + unclobber(fn, offset, insn_byte); + + // require at least one path above to trip this offset + if (!caught) fprintf(stderr, "OFFSET %lu NOT CAUGHT\n", offset); + } + free(insnOffsets); +} + +// x86_64 +#endif + + +void test_basic(id receiver) +{ + id idval; + long long llval; + struct stret stretval; + double fpval; + long double lfpval; // message uncached // message uncached long long @@ -473,31 +756,31 @@ int main() // message cached fpret long double // message cached noarg (as above) // fixme verify that uncached lookup didn't happen the 2nd time? - // Do this first before anything below caches stuff. - for (i = 0; i < 5; i++) { + SELF = receiver; + for (int i = 0; i < 5; i++) { state = 0; idval = nil; - idval = [Sub idret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; + idval = [receiver idret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; testassert(state == 101); testassert(idval == ID_RESULT); llval = 0; - llval = [Sub llret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; + llval = [receiver llret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; testassert(state == 102); testassert(llval == LL_RESULT); stretval = zero; - stretval = [Sub stret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; + stretval = [receiver stret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; testassert(state == 103); testassert(stret_equal(stretval, STRET_RESULT)); fpval = 0; - fpval = [Sub fpret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; + fpval = [receiver fpret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; testassert(state == 104); testassert(fpval == FP_RESULT); lfpval = 0; - lfpval = [Sub lfpret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; + lfpval = [receiver lfpret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; testassert(state == 105); testassert(lfpval == LFP_RESULT); @@ -505,45 +788,103 @@ int main() // explicitly call noarg messenger, even if compiler doesn't emit it state = 0; idval = nil; - idval = ((typeof(idmsg0))objc_msgSend_noarg)([Sub class], @selector(idret_noarg)); + idval = ((typeof(idmsg0))objc_msgSend_noarg)(receiver, @selector(idret_noarg)); testassert(state == 111); testassert(idval == ID_RESULT); llval = 0; - llval = ((typeof(llmsg0))objc_msgSend_noarg)([Sub class], @selector(llret_noarg)); + llval = ((typeof(llmsg0))objc_msgSend_noarg)(receiver, @selector(llret_noarg)); testassert(state == 112); testassert(llval == LL_RESULT); /* + no objc_msgSend_stret_noarg stretval = zero; - stretval = ((typeof(stretmsg0))objc_msgSend_stret_noarg)([Sub class], @selector(stret_noarg)); - stretval = [Sub stret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; + stretval = ((typeof(stretmsg0))objc_msgSend_stret_noarg)(receiver, @selector(stret_noarg)); + stretval = [receiver stret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; testassert(state == 113); testassert(stret_equal(stretval, STRET_RESULT)); */ # if !__i386__ fpval = 0; - fpval = ((typeof(fpmsg0))objc_msgSend_noarg)([Sub class], @selector(fpret_noarg)); + fpval = ((typeof(fpmsg0))objc_msgSend_noarg)(receiver, @selector(fpret_noarg)); testassert(state == 114); testassert(fpval == FP_RESULT); # endif # if !__i386__ && !__x86_64__ lfpval = 0; - lfpval = ((typeof(lfpmsg0))objc_msgSend_noarg)([Sub class], @selector(lfpret_noarg)); + lfpval = ((typeof(lfpmsg0))objc_msgSend_noarg)(receiver, @selector(lfpret_noarg)); testassert(state == 115); testassert(lfpval == LFP_RESULT); # endif #endif } +} + +int main() +{ + PUSH_POOL { + int i; + + id idval; + long long llval; + struct stret stretval; + double fpval; + long double lfpval; + + uint64_t startTime; + uint64_t totalTime; + uint64_t targetTime; + + Method idmethod; + Method llmethod; + Method stretmethod; + Method fpmethod; + Method lfpmethod; + + id (*idfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); + long long (*llfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); + struct stret (*stretfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); + double (*fpfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); + long double (*lfpfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); + + id (*idmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); + id (*idmsgsuper)(struct objc_super *, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); + long long (*llmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); + struct stret (*stretmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); + struct stret (*stretmsgsuper)(struct objc_super *, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); + double (*fpmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); + long double (*lfpmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); + + // get +initialize out of the way + [Sub class]; +#if __x86_64__ + [TaggedSub class]; +#endif + + ID_RESULT = [Super new]; - idmethod = class_getClassMethod([Super class], @selector(idret::::::::::::::::::::::::::::)); + Sub *sub = [Sub new]; + Super *sup = [Super new]; +#if __x86_64__ + TaggedSub *tagged = TAG_VALUE(2, 999); +#endif + + // Basic cached and uncached dispatch. + // Do this first before anything below caches stuff. + test_basic(sub); +#if __x86_64__ + test_basic(tagged); +#endif + + idmethod = class_getInstanceMethod([Super class], @selector(idret::::::::::::::::::::::::::::)); testassert(idmethod); - llmethod = class_getClassMethod([Super class], @selector(llret::::::::::::::::::::::::::::)); + llmethod = class_getInstanceMethod([Super class], @selector(llret::::::::::::::::::::::::::::)); testassert(llmethod); - stretmethod = class_getClassMethod([Super class], @selector(stret::::::::::::::::::::::::::::)); + stretmethod = class_getInstanceMethod([Super class], @selector(stret::::::::::::::::::::::::::::)); testassert(stretmethod); - fpmethod = class_getClassMethod([Super class], @selector(fpret::::::::::::::::::::::::::::)); + fpmethod = class_getInstanceMethod([Super class], @selector(fpret::::::::::::::::::::::::::::)); testassert(fpmethod); - lfpmethod = class_getClassMethod([Super class], @selector(lfpret::::::::::::::::::::::::::::)); + lfpmethod = class_getInstanceMethod([Super class], @selector(lfpret::::::::::::::::::::::::::::)); testassert(lfpmethod); idfn = (id (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke; @@ -557,21 +898,22 @@ int main() // fixme unless they all fail // `.align 4` matches loop alignment to make -O0 work // fill cache first - [Sub idret_perf]; - [Sub llret_perf]; - [Sub stret_perf]; - [Sub fpret_perf]; - [Sub lfpret_perf]; - [Sub idret_perf]; - [Sub llret_perf]; - [Sub stret_perf]; - [Sub fpret_perf]; - [Sub lfpret_perf]; - [Sub idret_perf]; - [Sub llret_perf]; - [Sub stret_perf]; - [Sub fpret_perf]; - [Sub lfpret_perf]; + SELF = sub; + [sub voidret_nop]; + [sub llret_nop]; + [sub stret_nop]; + [sub fpret_nop]; + [sub lfpret_nop]; + [sub voidret_nop]; + [sub llret_nop]; + [sub stret_nop]; + [sub fpret_nop]; + [sub lfpret_nop]; + [sub voidret_nop]; + [sub llret_nop]; + [sub stret_nop]; + [sub fpret_nop]; + [sub lfpret_nop]; // Some of these times have high variance on some compilers. // The errors we're trying to catch should be catastrophically slow, @@ -581,7 +923,16 @@ int main() startTime = mach_absolute_time(); ALIGN_(); for (i = 0; i < COUNT; i++) { - [Sub idret_perf]; + [sub voidret_nop]; // id return is too slow for perf test with ARC + } + totalTime = mach_absolute_time() - startTime; + testprintf("time: idret %llu\n", totalTime); + targetTime = totalTime; + + startTime = mach_absolute_time(); + ALIGN_(); + for (i = 0; i < COUNT; i++) { + [sub voidret_nop]; // id return is too slow for perf test with ARC } totalTime = mach_absolute_time() - startTime; testprintf("time: idret %llu\n", totalTime); @@ -590,34 +941,34 @@ int main() startTime = mach_absolute_time(); ALIGN_(); for (i = 0; i < COUNT; i++) { - [Sub llret_perf]; + [sub llret_nop]; } totalTime = mach_absolute_time() - startTime; - timecheck("llret ", totalTime, targetTime * 0.8, targetTime * 2.0); + timecheck("llret ", totalTime, targetTime * 0.7, targetTime * 2.0); startTime = mach_absolute_time(); ALIGN_(); for (i = 0; i < COUNT; i++) { - [Sub stret_perf]; + [sub stret_nop]; } totalTime = mach_absolute_time() - startTime; - timecheck("stret ", totalTime, targetTime * 0.8, targetTime * 5.0); + timecheck("stret ", totalTime, targetTime * 0.7, targetTime * 5.0); startTime = mach_absolute_time(); ALIGN_(); for (i = 0; i < COUNT; i++) { - [Sub fpret_perf]; + [sub fpret_nop]; } totalTime = mach_absolute_time() - startTime; - timecheck("fpret ", totalTime, targetTime * 0.8, targetTime * 4.0); + timecheck("fpret ", totalTime, targetTime * 0.7, targetTime * 4.0); startTime = mach_absolute_time(); ALIGN_(); for (i = 0; i < COUNT; i++) { - [Sub lfpret_perf]; + [sub lfpret_nop]; } totalTime = mach_absolute_time() - startTime; - timecheck("lfpret", totalTime, targetTime * 0.8, targetTime * 4.0); + timecheck("lfpret", totalTime, targetTime * 0.7, targetTime * 4.0); #undef COUNT // method_invoke @@ -625,30 +976,31 @@ int main() // method_invoke_stret stret // method_invoke_stret fpret // method_invoke fpret long double + SELF = sup; state = 0; idval = nil; - idval = (*idfn)([Super class], idmethod, 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); + idval = (*idfn)(sup, idmethod, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); testassert(state == 1); testassert(idval == ID_RESULT); llval = 0; - llval = (*llfn)([Super class], llmethod, 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); + llval = (*llfn)(sup, llmethod, 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 == 2); testassert(llval == LL_RESULT); stretval = zero; - stretval = (*stretfn)([Super class], stretmethod, 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); + stretval = (*stretfn)(sup, stretmethod, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); testassert(state == 3); testassert(stret_equal(stretval, STRET_RESULT)); fpval = 0; - fpval = (*fpfn)([Super class], fpmethod, 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); + fpval = (*fpfn)(sup, fpmethod, 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 == 4); testassert(fpval == FP_RESULT); lfpval = 0; - lfpval = (*lfpfn)([Super class], lfpmethod, 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); + lfpval = (*lfpfn)(sup, lfpmethod, 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 == 5); testassert(lfpval == LFP_RESULT); @@ -674,7 +1026,11 @@ int main() stretval = zero; stretval = [(id)nil stret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; testassert(state == 0); +#if __clang__ + testassert(0 == memcmp(&stretval, &zero, sizeof(stretval))); +#else // no stret result guarantee +#endif state = 0; fpval = FP_RESULT; @@ -688,7 +1044,7 @@ int main() testassert(state == 0); testassert(lfpval == 0.0); -#if __OBCJ2__ +#if __OBJC2__ // message to nil noarg // explicitly call noarg messenger, even if compiler doesn't emit it state = 0; @@ -702,13 +1058,9 @@ int main() llval = ((typeof(llmsg0))objc_msgSend_noarg)(nil, @selector(llret_noarg)); testassert(state == 0); testassert(llval == 0LL); - /* - state = 0; - stretval = zero; - stretval = [(id)nil stret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; - testassert(state == 0); - // no stret result guarantee - */ + + // no stret_noarg messenger + # if !__i386__ state = 0; fpval = FP_RESULT; @@ -734,26 +1086,28 @@ int main() #if __OBJC2__ // rdar://8271364 objc_msgSendSuper2 must not change objc_super - struct objc_super sup = { - [Sub class], - object_getClass([Sub class]), + struct objc_super sup_st = { + sub, + object_getClass(sub), }; + SELF = sub; + state = 100; idval = nil; - idval = ((id(*)(struct objc_super *, SEL, int,int,int,int,int,int,int,int,int,int,int,int,int, double,double,double,double,double,double,double,double,double,double,double,double,double,double,double))objc_msgSendSuper2) (&sup, @selector(idret::::::::::::::::::::::::::::), 1,2,3,4,5,6,7,8,9,10,11,12,13, 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0); + idval = ((id(*)(struct objc_super *, SEL, int,int,int,int,int,int,int,int,int,int,int,int,int, double,double,double,double,double,double,double,double,double,double,double,double,double,double,double))objc_msgSendSuper2) (&sup_st, @selector(idret::::::::::::::::::::::::::::), 1,2,3,4,5,6,7,8,9,10,11,12,13, 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0); testassert(state == 1); testassert(idval == ID_RESULT); - testassert(sup.receiver == [Sub class]); - testassert(sup.super_class == object_getClass([Sub class])); + testassert(sup_st.receiver == sub); + testassert(sup_st.super_class == object_getClass(sub)); state = 100; stretval = zero; - stretval = ((struct stret(*)(struct objc_super *, SEL, int,int,int,int,int,int,int,int,int,int,int,int,int, double,double,double,double,double,double,double,double,double,double,double,double,double,double,double))objc_msgSendSuper2_stret) (&sup, @selector(stret::::::::::::::::::::::::::::), 1,2,3,4,5,6,7,8,9,10,11,12,13, 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0); + stretval = ((struct stret(*)(struct objc_super *, SEL, int,int,int,int,int,int,int,int,int,int,int,int,int, double,double,double,double,double,double,double,double,double,double,double,double,double,double,double))objc_msgSendSuper2_stret) (&sup_st, @selector(stret::::::::::::::::::::::::::::), 1,2,3,4,5,6,7,8,9,10,11,12,13, 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0); testassert(state == 3); testassert(stret_equal(stretval, STRET_RESULT)); - testassert(sup.receiver == [Sub class]); - testassert(sup.super_class == object_getClass([Sub class])); + testassert(sup_st.receiver == sub); + testassert(sup_st.super_class == object_getClass(sub)); #endif #if __OBJC2__ @@ -761,39 +1115,39 @@ int main() state = 0; idmsg = (typeof(idmsg))objc_msgSend_debug; idval = nil; - idval = (*idmsg)([Sub class], @selector(idret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); + idval = (*idmsg)(sub, @selector(idret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); testassert(state == 101); testassert(idval == ID_RESULT); state = 0; llmsg = (typeof(llmsg))objc_msgSend_debug; llval = 0; - llval = (*llmsg)([Sub class], @selector(llret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); + llval = (*llmsg)(sub, @selector(llret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); testassert(state == 102); testassert(llval == LL_RESULT); state = 0; stretmsg = (typeof(stretmsg))objc_msgSend_stret_debug; stretval = zero; - stretval = (*stretmsg)([Sub class], @selector(stret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); + stretval = (*stretmsg)(sub, @selector(stret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); testassert(state == 103); testassert(stret_equal(stretval, STRET_RESULT)); state = 100; - sup.receiver = [Sub class]; - sup.super_class = object_getClass([Sub class]); - idmsg = (typeof(idmsg))objc_msgSendSuper2_debug; + sup_st.receiver = sub; + sup_st.super_class = object_getClass(sub); + idmsgsuper = (typeof(idmsgsuper))objc_msgSendSuper2_debug; idval = nil; - idval = (*idmsg)((id)&sup, @selector(idret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); + idval = (*idmsgsuper)(&sup_st, @selector(idret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); testassert(state == 1); testassert(idval == ID_RESULT); state = 100; - sup.receiver = [Sub class]; - sup.super_class = object_getClass([Sub class]); - stretmsg = (typeof(stretmsg))objc_msgSendSuper2_stret_debug; + sup_st.receiver = sub; + sup_st.super_class = object_getClass(sub); + stretmsgsuper = (typeof(stretmsgsuper))objc_msgSendSuper2_stret_debug; stretval = zero; - stretval = (*stretmsg)((id)&sup, @selector(stret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); + stretval = (*stretmsgsuper)(&sup_st, @selector(stret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); testassert(state == 3); testassert(stret_equal(stretval, STRET_RESULT)); @@ -801,7 +1155,7 @@ int main() state = 0; fpmsg = (typeof(fpmsg))objc_msgSend_fpret_debug; fpval = 0; - fpval = (*fpmsg)([Sub class], @selector(fpret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); + fpval = (*fpmsg)(sub, @selector(fpret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); testassert(state == 104); testassert(fpval == FP_RESULT); #endif @@ -809,7 +1163,7 @@ int main() state = 0; lfpmsg = (typeof(lfpmsg))objc_msgSend_fpret_debug; lfpval = 0; - lfpval = (*lfpmsg)([Sub class], @selector(lfpret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); + lfpval = (*lfpmsg)(sub, @selector(lfpret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); testassert(state == 105); testassert(lfpval == LFP_RESULT); @@ -819,6 +1173,38 @@ int main() // debug messengers #endif + +#if __x86_64__ && !__has_feature(objc_arc) + // DWARF unwind tables + // Not for ARC because the extra RR calls hit the traps at the wrong times + + // install exception handler + struct sigaction act; + act.sa_sigaction = sigtrap; + act.sa_mask = 0; + act.sa_flags = SA_SIGINFO; + sigaction(SIGTRAP, &act, NULL); + + // use _nop methods because other methods make more calls + // which can die in the trapped messenger + + test_dw("objc_msgSend", sub,tagged,@selector(idret_nop)); + test_dw("objc_msgSend_fixup", sub,tagged,@selector(idret_nop)); + test_dw("objc_msgSend_stret", sub,tagged,@selector(stret_nop)); + test_dw("objc_msgSend_stret_fixup", sub,tagged,@selector(stret_nop)); + test_dw("objc_msgSend_fpret", sub,tagged,@selector(fpret_nop)); + test_dw("objc_msgSend_fpret_fixup", sub,tagged,@selector(fpret_nop)); + // fixme fp2ret + test_dw("objc_msgSendSuper", sub,tagged,@selector(idret_nop)); + test_dw("objc_msgSendSuper2", sub,tagged,@selector(idret_nop)); + test_dw("objc_msgSendSuper2_fixup", sub,tagged,@selector(idret_nop)); + test_dw("objc_msgSendSuper_stret", sub,tagged,@selector(stret_nop)); + test_dw("objc_msgSendSuper2_stret", sub,tagged,@selector(stret_nop)); + test_dw("objc_msgSendSuper2_stret_fixup", sub,tagged,@selector(stret_nop)); + + // DWARF unwind tables +#endif + } POP_POOL; succeed(__FILE__); } diff --git a/test/nsexc.m b/test/nsexc.m index 61b04f7..55e0545 100644 --- a/test/nsexc.m +++ b/test/nsexc.m @@ -1,4 +1,20 @@ // TEST_CFLAGS -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 #include "exc.m" diff --git a/test/nsobject.m b/test/nsobject.m index a1d515a..e85ffe2 100644 --- a/test/nsobject.m +++ b/test/nsobject.m @@ -1,12 +1,12 @@ -// TEST_CFLAGS -framework Foundation +// TEST_CONFIG MEM=mrc,gc #include "test.h" -#import +#import @interface Sub : NSObject { } @end @implementation Sub -+allocWithZone:(NSZone *)zone { ++(id)allocWithZone:(NSZone *)zone { testprintf("in +[Sub alloc]\n"); return [super allocWithZone:zone]; } @@ -18,9 +18,9 @@ int main() { - NSAutoreleasePool *pool = [NSAutoreleasePool new]; - [[Sub new] autorelease]; - [pool release]; + PUSH_POOL { + [[Sub new] autorelease]; + } POP_POOL; succeed(__FILE__); } diff --git a/test/nsprotocol.m b/test/nsprotocol.m new file mode 100644 index 0000000..8305cc8 --- /dev/null +++ b/test/nsprotocol.m @@ -0,0 +1,47 @@ +// TEST_CONFIG + +#include "test.h" + +#if __OBJC2__ + +#include + +int main() +{ + // Class Protocol is always a subclass of NSObject + + testassert(objc_getClass("NSObject")); + + Class cls = objc_getClass("Protocol"); + testassert(class_getInstanceMethod(cls, sel_registerName("isProxy"))); + testassert(class_getSuperclass(cls) == objc_getClass("NSObject")); + + 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/property.m b/test/property.m index bfa217c..fbcabdd 100644 --- a/test/property.m +++ b/test/property.m @@ -1,13 +1,13 @@ // TEST_CONFIG #include "test.h" +#include "testroot.i" #include #include #include -@interface Super { +@interface Super : TestRoot { @public - id isa; char superIvar; } @@ -16,8 +16,6 @@ @implementation Super @synthesize superProp = superIvar; -+(void)initialize { } -+class { return self; } @end @@ -54,7 +52,7 @@ int main() prop = class_getProperty([Super class], "subProp"); testassert(!prop); - prop = class_getProperty([Sub class]->isa, "subProp"); + prop = class_getProperty(object_getClass([Sub class]), "subProp"); testassert(!prop); diff --git a/test/propertyDesc.m b/test/propertyDesc.m index 7db8e04..2a81655 100644 --- a/test/propertyDesc.m +++ b/test/propertyDesc.m @@ -1,17 +1,11 @@ // TEST_CONFIG #include "test.h" - +#include "testroot.i" #include #include #include -@interface Foo { id isa; } @end -@implementation Foo -+class { return self; } -+(void)initialize { } -@end - struct objc_property { const char *name; const char *attr; @@ -246,20 +240,20 @@ int main() objc_property_t prop2; // null name - ok = class_addProperty([Foo class], NULL, (objc_property_attribute_t *)1, 1); + ok = class_addProperty([TestRoot class], NULL, (objc_property_attribute_t *)1, 1); testassert(!ok); // null description - ok = class_addProperty([Foo class], "test-null-desc", NULL, 0); + ok = class_addProperty([TestRoot class], "test-null-desc", NULL, 0); testassert(ok); - prop2 = class_getProperty([Foo class], "test-null-desc"); + prop2 = class_getProperty([TestRoot class], "test-null-desc"); testassert(prop2); testassert(0 == strcmp(property_getAttributes(prop2), "")); // empty description - ok = class_addProperty([Foo class], "test-empty-desc", (objc_property_attribute_t*)1, 0); + ok = class_addProperty([TestRoot class], "test-empty-desc", (objc_property_attribute_t*)1, 0); testassert(ok); - prop2 = class_getProperty([Foo class], "test-empty-desc"); + prop2 = class_getProperty([TestRoot class], "test-empty-desc"); testassert(prop2); testassert(0 == strcmp(property_getAttributes(prop2), "")); @@ -271,9 +265,9 @@ int main() { "?!?!", "YY" }, { "''''", "" } }; - ok = class_addProperty([Foo class], "test-unrecognized", attrs2, 5); + ok = class_addProperty([TestRoot class], "test-unrecognized", attrs2, 5); testassert(ok); - prop2 = class_getProperty([Foo class], "test-unrecognized"); + prop2 = class_getProperty([TestRoot class], "test-unrecognized"); testassert(prop2); testassert(0 == strcmp(property_getAttributes(prop2), "?XX,',\"?!?!\"YY,\"''''\"")); @@ -291,9 +285,9 @@ int main() { "V", "2222" }, { "T", "11" }, }; - ok = class_addProperty([Foo class], "test-recognized", attrs3, 11); + ok = class_addProperty([TestRoot class], "test-recognized", attrs3, 11); testassert(ok); - prop2 = class_getProperty([Foo class], "test-recognized"); + prop2 = class_getProperty([TestRoot class], "test-recognized"); testassert(prop2); testassert(0 == strcmp(property_getAttributes(prop2), "&,C,N,R,D,P,W,G\"44444444,S333333\",V2222,T11")); @@ -317,9 +311,9 @@ int main() { "?!?!", "YY" }, { "''''", "" } }; - ok = class_addProperty([Foo class], "test-sink", attrs4, 16); + ok = class_addProperty([TestRoot class], "test-sink", attrs4, 16); testassert(ok); - prop2 = class_getProperty([Foo class], "test-sink"); + prop2 = class_getProperty([TestRoot class], "test-sink"); testassert(prop2); testassert(0 == strcmp(property_getAttributes(prop2), "&,C,N,R,D,P,W,G\"44444444,S333333\",V2222,T11," diff --git a/test/protocol.m b/test/protocol.m index dc8345b..4d0e28f 100644 --- a/test/protocol.m +++ b/test/protocol.m @@ -1,51 +1,77 @@ -// TEST_CFLAGS -Wno-deprecated-declarations +// TEST_CFLAGS -framework Foundation -Wno-deprecated-declarations +// need Foundation to get NSObject compatibility additions for class Protocol +// because ARC calls [protocol retain] #include "test.h" - +#include "testroot.i" #include -#include +#include +#include #if !__OBJC2__ #include #endif @protocol Proto1 -+proto1ClassMethod; --proto1InstanceMethod; ++(id)proto1ClassMethod; +-(id)proto1InstanceMethod; @end @protocol Proto2 -+proto2ClassMethod; --proto2InstanceMethod; ++(id)proto2ClassMethod; +-(id)proto2InstanceMethod; @end @protocol Proto3 -+proto3ClassMethod; --proto3InstanceMethod; ++(id)proto3ClassMethod; +-(id)proto3InstanceMethod; @end @protocol Proto4 @property int i; @end + +// Force some of Proto5's selectors out of address order rdar://10582325 +SEL fn(int x) { if (x) return @selector(m12:); else return @selector(m22:); } + +// This declaration order deliberately looks weird because it determines the +// selector address order on some architectures rdar://10582325 +@protocol Proto5 +-(id)m11:(id)a; +-(void)m12:(id)a; +-(int)m13:(id)a; ++(void)m22:(TestRoot*)a; ++(int)m23:(TestRoot*)a; ++(TestRoot*)m21:(TestRoot*)a; +@optional +-(id(^)(id))m31:(id(^)(id))a; +-(void)m32:(id(^)(id))a; +-(int)m33:(id(^)(id))a; ++(void)m42:(TestRoot*(^)(TestRoot*))a; ++(int)m43:(TestRoot*(^)(TestRoot*))a; ++(TestRoot*(^)(TestRoot*))m41:(TestRoot*(^)(TestRoot*))a; +@end + +@protocol Proto6 +@optional ++(TestRoot*(^)(TestRoot*))n41:(TestRoot*(^)(TestRoot*))a; +@end + @protocol ProtoEmpty @end -@interface Super { id isa; } @end +@interface Super : TestRoot @end @implementation Super -+class { return self; } -+(void)initialize { } -+proto1ClassMethod { return self; } --proto1InstanceMethod { return self; } ++(id)proto1ClassMethod { return self; } +-(id)proto1InstanceMethod { return self; } @end -@interface SubNoProtocols : Super { } @end +@interface SubNoProtocols : Super @end @implementation SubNoProtocols @end -@interface SuperNoProtocols { id isa; } @end +@interface SuperNoProtocols : TestRoot @end @implementation SuperNoProtocols -+class { return self; } -+(void)initialize { } @end @interface SubProp : Super { int i; } @end @@ -57,7 +83,7 @@ int main() { Class cls; - Protocol * const *list; + Protocol * __unsafe_unretained *list; Protocol *protocol, *empty; #if !__OBJC2__ struct objc_method_description *desc; @@ -104,6 +130,10 @@ int main() 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)]; @@ -119,6 +149,9 @@ int main() desc2 = protocol_getMethodDescription(protocol, @selector(proto3ClassMethod), YES, NO); testassert(desc2.name && desc2.types); testassert(desc2.name == @selector(proto3ClassMethod)); + desc2 = protocol_getMethodDescription(protocol, @selector(proto2ClassMethod), YES, NO); + testassert(desc2.name && desc2.types); + testassert(desc2.name == @selector(proto2ClassMethod)); desc2 = protocol_getMethodDescription(protocol, @selector(proto3ClassMethod), YES, YES); testassert(!desc2.name && !desc2.types); @@ -139,7 +172,7 @@ int main() testassert(count == 1); testassert(protocol_isEqual(list[0], @protocol(Proto2))); testassert(!list[1]); - free((void*)list); + free(list); count = 100; cls = objc_getClass("Super"); @@ -149,7 +182,7 @@ int main() testassert(list[count] == NULL); testassert(count == 1); testassert(0 == strcmp(protocol_getName(list[0]), "Proto1")); - free((void*)list); + free(list); count = 100; cls = objc_getClass("SuperNoProtocols"); @@ -175,7 +208,7 @@ int main() testassert(cls); list = class_copyProtocolList(cls, NULL); testassert(list); - free((void*)list); + free(list); count = 100; list = class_copyProtocolList(NULL, &count); @@ -193,7 +226,7 @@ int main() testassert(count == 1); testassert(0 == strcmp(protocol_getName(list[0]), "Proto4")); testassert(list[1] == NULL); - free((void*)list); + free(list); count = 100; proplist = class_copyPropertyList(cls, &count); @@ -203,5 +236,73 @@ int main() testassert(proplist[1] == NULL); free(proplist); + // Check extended type encodings + testassert(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(DoesNotExist), true, true) == NULL); + testassert(_protocol_getMethodTypeEncoding(NULL, @selector(m11), true, true) == NULL); + testassert(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m11), true, false) == NULL); + testassert(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m11), false, false) == NULL); + testassert(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m11), false, true) == NULL); + testassert(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m21), true, true) == NULL); +#if __LP64__ + const char *types11 = "@24@0:8@\"\"16"; + const char *types12 = "v24@0:8@\"\"16"; + const char *types13 = "i24@0:8@\"\"16"; + const char *types21 = "@\"TestRoot\"24@0:8@\"TestRoot\"16"; + const char *types22 = "v24@0:8@\"TestRoot\"16"; + const char *types23 = "i24@0:8@\"TestRoot\"16"; + const char *types31 = "@?<@@?@>24@0:8@?<@\"\"@?@\"\">16"; + const char *types32 = "v24@0:8@?<@\"\"@?@\"\">16"; + const char *types33 = "i24@0:8@?<@\"\"@?@\"\">16"; + const char *types41 = "@?<@\"TestRoot\"@?@\"TestRoot\">24@0:8@?<@\"TestRoot\"@?@\"TestRoot\">16"; + const char *types42 = "v24@0:8@?<@\"TestRoot\"@?@\"TestRoot\">16"; + const char *types43 = "i24@0:8@?<@\"TestRoot\"@?@\"TestRoot\">16"; +#else + const char *types11 = "@12@0:4@\"\"8"; + const char *types12 = "v12@0:4@\"\"8"; + const char *types13 = "i12@0:4@\"\"8"; + const char *types21 = "@\"TestRoot\"12@0:4@\"TestRoot\"8"; + const char *types22 = "v12@0:4@\"TestRoot\"8"; + const char *types23 = "i12@0:4@\"TestRoot\"8"; + const char *types31 = "@?<@@?@>12@0:4@?<@\"\"@?@\"\">8"; + const char *types32 = "v12@0:4@?<@\"\"@?@\"\">8"; + const char *types33 = "i12@0:4@?<@\"\"@?@\"\">8"; + const char *types41 = "@?<@\"TestRoot\"@?@\"TestRoot\">12@0:4@?<@\"TestRoot\"@?@\"TestRoot\">8"; + const char *types42 = "v12@0:4@?<@\"TestRoot\"@?@\"TestRoot\">8"; + const char *types43 = "i12@0:4@?<@\"TestRoot\"@?@\"TestRoot\">8"; +#endif + + // Make sure some of Proto5's selectors are out of order rdar://10582325 + // These comparisons deliberately look weird because they determine the + // selector order on some architectures. + testassert(sel_registerName("m11:") > sel_registerName("m12:") || + sel_registerName("m21:") > sel_registerName("m22:") || + sel_registerName("m32:") < sel_registerName("m31:") || + sel_registerName("m42:") < sel_registerName("m41:") ); + + if (!_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m11:), true, true)) { +#if __clang__ + testwarn("rdar://10492418 extended type encodings not present (is compiler old?)"); +#else + // extended type encodings quietly not supported + testwarn("rdar://10492418 extended type encodings not present (compiler is not clang?)"); +#endif + } else { + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m11:), true, true), types11)); + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m12:), true, true), types12)); + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m13:), true, true), types13)); + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m21:), true, false), types21)); + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m22:), true, false), types22)); + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m23:), true, false), types23)); + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m31:), false, true), types31)); + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m32:), false, true), types32)); + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m33:), false, true), types33)); + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m41:), false, false), types41)); + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m42:), false, false), types42)); + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m43:), false, false), types43)); + + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto6), @selector(n41:), false, false), types41)); + testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto6), @selector(m41:), false, false), types41)); + } + succeed(__FILE__); } diff --git a/test/protocol_copyMethodList.m b/test/protocol_copyMethodList.m index 9e6acd3..53bf445 100644 --- a/test/protocol_copyMethodList.m +++ b/test/protocol_copyMethodList.m @@ -1,4 +1,7 @@ -// TEST_CONFIG +// TEST_CFLAGS -framework Foundation +// need Foundation to get NSObject compatibility additions for class Protocol +// because ARC calls [protocol retain] + #include "test.h" #include diff --git a/test/protocol_copyPropertyList.m b/test/protocol_copyPropertyList.m index 8c8b3d1..406a0bd 100644 --- a/test/protocol_copyPropertyList.m +++ b/test/protocol_copyPropertyList.m @@ -1,9 +1,11 @@ -// TEST_CONFIG +// TEST_CFLAGS -framework Foundation +// need Foundation to get NSObject compatibility additions for class Protocol +// because ARC calls [protocol retain] #include "test.h" #include #include -#include +#include @protocol SuperProps @property int prop1; diff --git a/test/resolve.m b/test/resolve.m index d03cc87..bc11f61 100644 --- a/test/resolve.m +++ b/test/resolve.m @@ -9,31 +9,43 @@ TEST_RUN_OUTPUT objc\[\d+\]: \+\[Sub resolveClassMethod:lyingClassMethod\] returned YES, but no new implementation of \+\[Sub lyingClassMethod\] was found objc\[\d+\]: \+\[Sub resolveInstanceMethod:lyingInstanceMethod\] returned YES, but no new implementation of -\[Sub lyingInstanceMethod\] was found OK: resolve\.m +OR +confused by Foundation +OK: resolve\.m END */ #include "test.h" +#include "testroot.i" #include #include #include +#if __has_feature(objc_arc) + +int main() +{ + testwarn("rdar://11368528 confused by Foundation"); + fprintf(stderr, "confused by Foundation\n"); + succeed(__FILE__); +} + +#else + static int state = 0; -@interface Super { id isa; } @end +@interface Super : TestRoot @end @interface Sub : Super @end @implementation Super -+class { return self; } +(void)initialize { if (self == [Super class]) { testassert(state == 1); state = 2; } } -+new { return class_createInstance(self, 0); } --(void)dealloc { object_dispose(self); } --forward:(SEL)sel :(marg_list)args ++(id)forward:(SEL)sel :(marg_list)__unused args { if (sel == @selector(missingClassMethod)) { testassert(state == 21 || state == 25 || state == 80); @@ -46,7 +58,13 @@ static int state = 0; if (state == 31) state = 32; if (state == 35) state = 36; return nil; - } else if (sel == @selector(missingInstanceMethod)) { + } + fail("+forward:: shouldn't be called with sel %s", sel_getName(sel)); + return nil; +} +-(id)forward:(SEL)sel :(marg_list)__unused args +{ + if (sel == @selector(missingInstanceMethod)) { testassert(state == 61 || state == 65); if (state == 61) state = 62; if (state == 65) state = 66; @@ -57,27 +75,25 @@ static int state = 0; if (state == 75) state = 76; return nil; } - fail("forward:: shouldn't be called (sel %s)", sel_getName(sel)); - return (id)args; // unused + fail("-forward:: shouldn't be called with sel %s", sel_getName(sel)); + return nil; } @end -static id classMethod_c(id self, SEL sel) +static id classMethod_c(id __unused self, SEL __unused sel) { testassert(state == 4 || state == 10); if (state == 4) state = 5; if (state == 10) state = 11; - self = (id)sel; // unused return [Super class]; } -static id instanceMethod_c(id self, SEL sel) +static id instanceMethod_c(id __unused self, SEL __unused sel) { testassert(state == 41 || state == 50); if (state == 41) state = 42; if (state == 50) state = 51; - self = (id)sel; // unused return [Sub class]; } @@ -101,7 +117,7 @@ static id instanceMethod_c(id self, SEL sel) if (sel == @selector(classMethod)) { testassert(state == 3); state = 4; - class_addMethod(self->isa, sel, (IMP)&classMethod_c, ""); + class_addMethod(object_getClass(self), sel, (IMP)&classMethod_c, ""); return YES; } else if (sel == @selector(missingClassMethod)) { testassert(state == 20); @@ -143,16 +159,16 @@ static id instanceMethod_c(id self, SEL sel) @end @interface Super (MissingMethods) -+missingClassMethod; ++(id)missingClassMethod; @end @interface Sub (ResolvedMethods) -+classMethod; --instanceMethod; -+missingClassMethod; --missingInstanceMethod; -+lyingClassMethod; --lyingInstanceMethod; ++(id)classMethod; +-(id)instanceMethod; ++(id)missingClassMethod; +-(id)missingInstanceMethod; ++(id)lyingClassMethod; +-(id)lyingInstanceMethod; @end @@ -160,11 +176,14 @@ int main() { Sub *s; id ret; + + // Be ready for ARC to retain the class object and call +initialize early + state = 1; + Class dup = objc_duplicateClass(objc_getClass("Sub"), "Sub_copy", 0); // Resolve a class method - // +initialize should fire first - state = 1; + // +initialize should fire first (if it hasn't already) ret = [Sub classMethod]; testassert(state == 5); testassert(ret == [Super class]); @@ -176,7 +195,7 @@ int main() testassert(state == 11); testassert(ret == [Super class]); - _objc_flush_caches([Sub class]->isa); + _objc_flush_caches(object_getClass([Sub class])); // Call a method that won't get resolved state = 20; @@ -191,7 +210,7 @@ int main() testassert(state == 26); testassert(ret == nil); - _objc_flush_caches([Sub class]->isa); + _objc_flush_caches(object_getClass([Sub class])); // Call a method that won't get resolved but the resolver lies about it state = 30; @@ -206,7 +225,7 @@ int main() testassert(state == 36); testassert(ret == nil); - _objc_flush_caches([Sub class]->isa); + _objc_flush_caches(object_getClass([Sub class])); // Resolve an instance method @@ -260,7 +279,7 @@ int main() ret = [Super missingClassMethod]; testassert(state == 81); testassert(ret == nil); - [s dealloc]; + RELEASE_VAR(s); // Resolve an instance method on a class duplicated before resolving s = [dup new]; @@ -275,8 +294,11 @@ int main() ret = [s instanceMethod]; testassert(state == 51); testassert(ret == [Sub class]); - [s dealloc]; + RELEASE_VAR(s); succeed(__FILE__); return 0; } + +#endif + diff --git a/test/rr-autorelease-fast.m b/test/rr-autorelease-fast.m new file mode 100644 index 0000000..6558b4d --- /dev/null +++ b/test/rr-autorelease-fast.m @@ -0,0 +1,106 @@ +// TEST_CONFIG CC=clang MEM=mrc +// TEST_CFLAGS -Os + +#include "test.h" + +#if __i386__ + +int main() +{ + // no optimization on i386 (neither Mac nor Simulator) + succeed(__FILE__); +} + +#else + +#include +#include +#include + +static int did_dealloc; + +@interface TestObject : NSObject +@end +@implementation TestObject +-(void)dealloc +{ + did_dealloc = 1; + [super dealloc]; +} +@end + +// rdar://9319305 clang transforms objc_retainAutoreleasedReturnValue() +// into objc_retain() sometimes +extern id objc_retainAutoreleasedReturnValue(id obj) __asm__("_objc_retainAutoreleasedReturnValue"); + +int +main() +{ + TestObject *tmp, *obj; + +#ifdef __x86_64__ + // need to get DYLD to resolve the stubs on x86 + PUSH_POOL { + TestObject *warm_up = [[TestObject alloc] init]; + testassert(warm_up); + warm_up = objc_retainAutoreleasedReturnValue(_objc_rootAutorelease(warm_up)); + [warm_up release]; + warm_up = nil; + } POP_POOL; +#endif + + testprintf("Successful return autorelease handshake\n"); + + PUSH_POOL { + obj = [[TestObject alloc] init]; + testassert(obj); + + did_dealloc = 0; + tmp = _objc_rootAutorelease(obj); +#ifdef __arm__ + asm volatile("mov r7, r7"); +#endif + tmp = objc_retainAutoreleasedReturnValue(tmp); + testassert(!did_dealloc); + + did_dealloc = 0; + [tmp release]; + testassert(did_dealloc); + + did_dealloc = 0; + } POP_POOL; + testassert(!did_dealloc); + + + testprintf("Failed return autorelease handshake\n"); + + PUSH_POOL { + obj = [[TestObject alloc] init]; + testassert(obj); + + did_dealloc = 0; + tmp = _objc_rootAutorelease(obj); +#ifdef __arm__ + asm volatile("mov r6, r6"); +#elif __x86_64__ + asm volatile("mov %rdi, %rdi"); +#endif + tmp = objc_retainAutoreleasedReturnValue(tmp); + testassert(!did_dealloc); + + did_dealloc = 0; + [tmp release]; + testassert(!did_dealloc); + + did_dealloc = 0; + } POP_POOL; + testassert(did_dealloc); + + + succeed(__FILE__); + + return 0; +} + + +#endif diff --git a/test/rr-autorelease.m b/test/rr-autorelease.m index b09c9d5..01eb89e 100644 --- a/test/rr-autorelease.m +++ b/test/rr-autorelease.m @@ -1,21 +1,9 @@ // TEST_CFLAGS -framework Foundation -// TEST_CONFIG GC=0 +// TEST_CONFIG MEM=mrc #include "test.h" -#if TARGET_OS_IPHONE - -int main() -{ - testwarn("iOS Foundation doesn't call _objc_root* yet"); - succeed(__FILE__); -} - -#else - #define FOUNDATION 0 #define NAME "rr-autorelease" #include "rr-autorelease2.m" - -#endif diff --git a/test/rr-autorelease2.m b/test/rr-autorelease2.m index b3a8aaa..eb6a37b 100644 --- a/test/rr-autorelease2.m +++ b/test/rr-autorelease2.m @@ -1,21 +1,22 @@ // Define FOUNDATION=1 for NSObject and NSAutoreleasePool // Define FOUNDATION=0 for _objc_root* and _objc_autoreleasePool* +#include "test.h" + #if FOUNDATION -# define PUSH() [[NSAutoreleasePool alloc] init] -# define POP(p) [(id)p release] -# define RETAIN(o) [o retain] -# define RELEASE(o) [o release] -# define AUTORELEASE(o) [o autorelease] +# define RR_PUSH() [[NSAutoreleasePool alloc] init] +# define RR_POP(p) [(id)p release] +# define RR_RETAIN(o) [o retain] +# define RR_RELEASE(o) [o release] +# define RR_AUTORELEASE(o) [o autorelease] #else -# define PUSH() _objc_autoreleasePoolPush() -# define POP(p) _objc_autoreleasePoolPop(p) -# define RETAIN(o) _objc_rootRetain((id)o) -# define RELEASE(o) _objc_rootRelease((id)o) -# define AUTORELEASE(o) _objc_rootAutorelease((id)o) +# define RR_PUSH() _objc_autoreleasePoolPush() +# define RR_POP(p) _objc_autoreleasePoolPop(p) +# define RR_RETAIN(o) _objc_rootRetain((id)o) +# define RR_RELEASE(o) _objc_rootRelease((id)o) +# define RR_AUTORELEASE(o) _objc_rootAutorelease((id)o) #endif -#include "test.h" #include #include @@ -38,7 +39,7 @@ static int state; -(void) dealloc { state++; - AUTORELEASE([[Deallocator alloc] init]); + RR_AUTORELEASE([[Deallocator alloc] init]); [super dealloc]; } @end @@ -49,19 +50,19 @@ static int state; { // caller's pool for (int i = 0; i < NESTED_COUNT; i++) { - AUTORELEASE([[Deallocator alloc] init]); + RR_AUTORELEASE([[Deallocator alloc] init]); } // local pool, popped - void *pool = PUSH(); + void *pool = RR_PUSH(); for (int i = 0; i < NESTED_COUNT; i++) { - AUTORELEASE([[Deallocator alloc] init]); + RR_AUTORELEASE([[Deallocator alloc] init]); } - POP(pool); + RR_POP(pool); // caller's pool again for (int i = 0; i < NESTED_COUNT; i++) { - AUTORELEASE([[Deallocator alloc] init]); + RR_AUTORELEASE([[Deallocator alloc] init]); } #if FOUNDATION @@ -73,9 +74,9 @@ static int state; state += NESTED_COUNT; #else // local pool, not popped - PUSH(); + RR_PUSH(); for (int i = 0; i < NESTED_COUNT; i++) { - AUTORELEASE([[Deallocator alloc] init]); + RR_AUTORELEASE([[Deallocator alloc] init]); } #endif @@ -83,14 +84,6 @@ static int state; } @end -void *nopop_fn(void *arg __unused) -{ - PUSH(); - AUTORELEASE([[Deallocator alloc] init]); - // pool not popped - return NULL; -} - void *autorelease_lots_fn(void *singlePool) { // Enough to blow out the stack if AutoreleasePoolPage is recursive. @@ -99,42 +92,37 @@ void *autorelease_lots_fn(void *singlePool) int p = 0; void **pools = (void**)malloc((COUNT+1) * sizeof(void*)); - pools[p++] = PUSH(); + pools[p++] = RR_PUSH(); - id obj = AUTORELEASE([[Deallocator alloc] init]); + id obj = RR_AUTORELEASE([[Deallocator alloc] init]); for (int i = 0; i < COUNT; i++) { if (rand() % 1000 == 0 && !singlePool) { - pools[p++] = PUSH(); + pools[p++] = RR_PUSH(); } else { - AUTORELEASE(RETAIN(obj)); + RR_AUTORELEASE(RR_RETAIN(obj)); } } testassert(state == 0); while (--p) { - POP(pools[p]); + RR_POP(pools[p]); } testassert(state == 0); - POP(pools[0]); + RR_POP(pools[0]); testassert(state == 1); free(pools); return NULL; } -void *pop_fn(void *arg __unused) -{ - void *pool = PUSH(); - AUTORELEASE([[Deallocator alloc] init]); - POP(pool); - return NULL; -} - -void *nsthread_fn(void *arg) +void *nsthread_fn(void *arg __unused) { [NSThread currentThread]; - return pop_fn(arg); + void *pool = RR_PUSH(); + RR_AUTORELEASE([[Deallocator alloc] init]); + RR_POP(pool); + return NULL; } void cycle(void) @@ -142,11 +130,11 @@ void cycle(void) // Normal autorelease. testprintf("-- Normal autorelease.\n"); { - void *pool = PUSH(); + void *pool = RR_PUSH(); state = 0; - AUTORELEASE([[Deallocator alloc] init]); + RR_AUTORELEASE([[Deallocator alloc] init]); testassert(state == 0); - POP(pool); + RR_POP(pool); testassert(state == 1); } @@ -154,22 +142,22 @@ void cycle(void) // That autorelease is handled by the popping pool, not the one above it. testprintf("-- Autorelease during dealloc during autoreleasepool-pop.\n"); { - void *pool = PUSH(); + void *pool = RR_PUSH(); state = 0; - AUTORELEASE([[AutoreleaseDuringDealloc alloc] init]); + RR_AUTORELEASE([[AutoreleaseDuringDealloc alloc] init]); testassert(state == 0); - POP(pool); + RR_POP(pool); testassert(state == 2); } // Autorelease pool during dealloc during autoreleasepool-pop. testprintf("-- Autorelease pool during dealloc during autoreleasepool-pop.\n"); { - void *pool = PUSH(); + void *pool = RR_PUSH(); state = 0; - AUTORELEASE([[AutoreleasePoolDuringDealloc alloc] init]); + RR_AUTORELEASE([[AutoreleasePoolDuringDealloc alloc] init]); testassert(state == 0); - POP(pool); + RR_POP(pool); testassert(state == 4 * NESTED_COUNT); } @@ -177,9 +165,11 @@ void cycle(void) testprintf("-- Thread-level pool popped normally.\n"); { state = 0; - pthread_t th; - pthread_create(&th, NULL, &pop_fn, NULL); - pthread_join(th, NULL); + testonthread(^{ + void *pool = RR_PUSH(); + RR_AUTORELEASE([[Deallocator alloc] init]); + RR_POP(pool); + }); testassert(state == 1); } @@ -195,9 +185,11 @@ void cycle(void) testprintf("-- Thread-level pool not popped.\n"); { state = 0; - pthread_t th; - pthread_create(&th, NULL, &nopop_fn, NULL); - pthread_join(th, NULL); + testonthread(^{ + RR_PUSH(); + RR_AUTORELEASE([[Deallocator alloc] init]); + // pool not popped + }); testassert(state == 1); } #endif @@ -213,12 +205,12 @@ void cycle(void) #else testprintf("-- Intermediate pool not popped.\n"); { - void *pool = PUSH(); - void *pool2 = PUSH(); - AUTORELEASE([[Deallocator alloc] init]); + void *pool = RR_PUSH(); + void *pool2 = RR_PUSH(); + RR_AUTORELEASE([[Deallocator alloc] init]); state = 0; (void)pool2; // pool2 not popped - POP(pool); + RR_POP(pool); testassert(state == 1); } #endif @@ -235,11 +227,11 @@ void cycle(void) /* testprintf("-- pop(0).\n"); { - PUSH(); + RR_PUSH(); state = 0; - AUTORELEASE([[AutoreleaseDuringDealloc alloc] init]); + RR_AUTORELEASE([[AutoreleaseDuringDealloc alloc] init]); testassert(state == 0); - POP(0); + RR_POP(0); testassert(state == 2); } */ @@ -253,11 +245,11 @@ int main() int count = 10000; id *objs = (id *)malloc(count*sizeof(id)); for (int i = 0; i < count; i++) { - objs[i] = RETAIN([NSObject new]); + objs[i] = RR_RETAIN([NSObject new]); } for (int i = 0; i < count; i++) { - RELEASE(objs[i]); - RELEASE(objs[i]); + RR_RELEASE(objs[i]); + RR_RELEASE(objs[i]); } free(objs); } @@ -315,8 +307,7 @@ int main() pthread_join(th, NULL); } - testwarn("rdar://9158789 leak slop due to false in-use from malloc"); - leak_check(8192 /* should be 0 */); + leak_check(0); // NSThread. diff --git a/test/rr-nsautorelease.m b/test/rr-nsautorelease.m index 38bbe57..095ec36 100644 --- a/test/rr-nsautorelease.m +++ b/test/rr-nsautorelease.m @@ -1,5 +1,5 @@ // TEST_CFLAGS -framework Foundation -// TEST_CONFIG GC=0 +// TEST_CONFIG MEM=mrc #define FOUNDATION 1 #define NAME "rr-nsautorelease" diff --git a/test/runtime.m b/test/runtime.m index 31f9875..22f8e92 100644 --- a/test/runtime.m +++ b/test/runtime.m @@ -2,26 +2,33 @@ TEST_RUN_OUTPUT objc\[\d+\]: class `DoesNotExist\' not linked into application OK: runtime.m +OR +confused by Foundation +OK: runtime.m END */ #include "test.h" - +#include "testroot.i" #include #include #include #include -@interface Super { id isa; } @end -@implementation Super -+(void)initialize { } -+class { return self; } -@end +#if __has_feature(objc_arc) -@interface Sub : Super { } @end -@implementation Sub @end +int main() +{ + testwarn("rdar://11368528 confused by Foundation"); + fprintf(stderr, "confused by Foundation\n"); + succeed(__FILE__); +} +#else + +@interface Sub : TestRoot @end +@implementation Sub @end int main() { @@ -29,12 +36,12 @@ int main() Class *list2; unsigned int count, count0, count2; unsigned int i; - int foundSuper; + int foundTestRoot; int foundSub; const char **names; Dl_info info; - [Super class]; + [TestRoot class]; // This shouldn't touch any classes. dladdr(&_mh_execute_header, &info); @@ -42,13 +49,13 @@ int main() testassert(names); testassert(count == 2); testassert(names[count] == NULL); - foundSuper = 0; + foundTestRoot = 0; foundSub = 0; for (i = 0; i < count; i++) { - if (0 == strcmp(names[i], "Super")) foundSuper++; + if (0 == strcmp(names[i], "TestRoot")) foundTestRoot++; if (0 == strcmp(names[i], "Sub")) foundSub++; } - testassert(foundSuper == 1); + testassert(foundTestRoot == 1); testassert(foundSub == 1); @@ -63,28 +70,28 @@ int main() count = objc_getClassList(list, count0); testassert(count == count0); - foundSuper = 0; + foundTestRoot = 0; foundSub = 0; for (i = 0; i < count; i++) { - if (0 == strcmp(class_getName(list[i]), "Super")) foundSuper++; + if (0 == strcmp(class_getName(list[i]), "TestRoot")) foundTestRoot++; if (0 == strcmp(class_getName(list[i]), "Sub")) foundSub++; // list should be non-meta classes only testassert(!class_isMetaClass(list[i])); } - testassert(foundSuper == 1); + testassert(foundTestRoot == 1); testassert(foundSub == 1); // fixme check class handler - testassert(objc_getClass("Super") == [Super class]); + testassert(objc_getClass("TestRoot") == [TestRoot class]); testassert(objc_getClass("DoesNotExist") == nil); testassert(objc_getClass(NULL) == nil); - testassert(objc_getMetaClass("Super") == [Super class]->isa); + testassert(objc_getMetaClass("TestRoot") == object_getClass([TestRoot class])); testassert(objc_getMetaClass("DoesNotExist") == nil); testassert(objc_getMetaClass(NULL) == nil); // fixme check class no handler - testassert(objc_lookUpClass("Super") == [Super class]); + testassert(objc_lookUpClass("TestRoot") == [TestRoot class]); testassert(objc_lookUpClass("DoesNotExist") == nil); testassert(objc_lookUpClass(NULL) == nil); @@ -101,3 +108,5 @@ int main() succeed(__FILE__); } + +#endif diff --git a/test/sel.m b/test/sel.m index ec4fc0d..bb79599 100644 --- a/test/sel.m +++ b/test/sel.m @@ -13,13 +13,20 @@ 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 + // sel_getName recognizes GC-ignored SELs -#if defined(__i386__) +# if defined(__i386__) if (objc_collectingEnabled()) { testassert(0 == strcmp("", sel_getName(@selector(retain)))); } else -#endif +# endif { testassert(0 == strcmp("retain", sel_getName(@selector(retain)))); @@ -33,5 +40,7 @@ int main() u.sel = @selector(retain); testassert(@selector(retain) == sel_registerName(u.ptr)); +#endif + succeed(__FILE__); } diff --git a/test/setSuper.m b/test/setSuper.m index 5f14f70..7ce6dee 100644 --- a/test/setSuper.m +++ b/test/setSuper.m @@ -1,27 +1,23 @@ // TEST_CFLAGS -Wno-deprecated-declarations #include "test.h" +#include "testroot.i" #include -@interface Super1 { id isa; } @end +@interface Super1 : TestRoot @end @implementation Super1 -+class { return self; } -+(void)initialize { } +(int)classMethod { return 1; } -(int)instanceMethod { return 10000; } @end -@interface Super2 { id isa; } @end +@interface Super2 : TestRoot @end @implementation Super2 -+class { return self; } -+(void)initialize { } +(int)classMethod { return 2; } -(int)instanceMethod { return 20000; } @end @interface Sub : Super1 @end @implementation Sub -+new { return class_createInstance(self, 0); } +(int)classMethod { return [super classMethod] + 100; } -(int)instanceMethod { return [super instanceMethod] + 1000000; @@ -39,7 +35,7 @@ int main() cls = class_setSuperclass([Sub class], [Super2 class]); testassert(cls == [Super1 class]); - testassert(cls->isa == [Super1 class]->isa); + testassert(object_getClass(cls) == object_getClass([Super1 class])); testassert(102 == [[Sub class] classMethod]); testassert(1020000 == [obj instanceMethod]); diff --git a/test/subscripting.m b/test/subscripting.m new file mode 100644 index 0000000..e05a370 --- /dev/null +++ b/test/subscripting.m @@ -0,0 +1,154 @@ +// 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 +#import +#include "test.h" + +@interface TestIndexed : NSObject { + NSMutableArray *indexedValues; +} +@property(readonly) NSUInteger count; +- (id)objectAtIndexedSubscript:(NSUInteger)index; +- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index; +@end + +@implementation TestIndexed + +- (id)init { + if ((self = [super init])) { + indexedValues = [NSMutableArray new]; + } + return self; +} + +#if !__has_feature(objc_arc) +- (void)dealloc { + [indexedValues release]; + [super dealloc]; +} +#endif + +- (NSUInteger)count { return [indexedValues count]; } +- (id)objectAtIndexedSubscript:(NSUInteger)index { return [indexedValues objectAtIndex:index]; } +- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index { + if (index == NSNotFound) + [indexedValues addObject:object]; + else + [indexedValues replaceObjectAtIndex:index withObject:object]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"indexedValues = %@", indexedValues]; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len { + return [indexedValues countByEnumeratingWithState:state objects:buffer count:len]; +} + + +@end + +@interface TestKeyed : NSObject { + NSMutableDictionary *keyedValues; +} +@property(readonly) NSUInteger count; +- (id)objectForKeyedSubscript:(id)key; +- (void)setObject:(id)object forKeyedSubscript:(id)key; +@end + +@implementation TestKeyed + +- (id)init { + if ((self = [super init])) { + keyedValues = [NSMutableDictionary new]; + } + return self; +} + +#if !__has_feature(objc_arc) +- (void)dealloc { + [keyedValues release]; + [super dealloc]; +} +#endif + +- (NSUInteger)count { return [keyedValues count]; } +- (id)objectForKeyedSubscript:(id)key { return [keyedValues objectForKey:key]; } +- (void)setObject:(id)object forKeyedSubscript:(id)key { + [keyedValues setObject:object forKey:key]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"keyedValues = %@", keyedValues]; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len { + return [keyedValues countByEnumeratingWithState:state objects:buffer count:len]; +} + +@end + +int main() { + PUSH_POOL { + +#if __has_feature(objc_bool) // placeholder until we get a more precise macro. + TestIndexed *testIndexed = [TestIndexed new]; + id objects[] = { @1, @2, @3, @4, @5 }; + size_t i, count = sizeof(objects) / sizeof(id); + for (i = 0; i < count; ++i) { + testIndexed[NSNotFound] = objects[i]; + } + for (i = 0; i < count; ++i) { + id object = testIndexed[i]; + testassert(object == objects[i]); + } + if (getenv("VERBOSE")) { + i = 0; + for (id object in testIndexed) { + NSString *message = [NSString stringWithFormat:@"testIndexed[%zu] = %@\n", i++, object]; + testprintf([message UTF8String]); + } + } + + TestKeyed *testKeyed = [TestKeyed new]; + id keys[] = { @"One", @"Two", @"Three", @"Four", @"Five" }; + for (i = 0; i < count; ++i) { + id key = keys[i]; + testKeyed[key] = objects[i]; + } + for (i = 0; i < count; ++i) { + id key = keys[i]; + id object = testKeyed[key]; + testassert(object == objects[i]); + } + if (getenv("VERBOSE")) { + for (id key in testKeyed) { + NSString *message = [NSString stringWithFormat:@"testKeyed[@\"%@\"] = %@\n", key, testKeyed[key]]; + testprintf([message UTF8String]); + } + } +#endif + + } POP_POOL; + + succeed(__FILE__); + + return 0; +} + +// __OBJC2__ +#endif diff --git a/test/super.m b/test/super.m index 16688c4..ff169f7 100644 --- a/test/super.m +++ b/test/super.m @@ -1,28 +1,20 @@ // TEST_CONFIG #include "test.h" +#include "testroot.i" #include -@interface Super { id isa; } @end -@implementation Super -+class { return self; } -+(void)initialize { } -@end - -@interface Sub : Super @end +@interface Sub : TestRoot @end @implementation Sub @end int main() { - id buf[10]; - buf[0] = [Sub class]; - // [super ...] messages are tested in msgSend.m - testassert(class_getSuperclass([Sub class]) == [Super class]); - testassert(class_getSuperclass([Sub class]->isa) == [Super class]->isa); - testassert(class_getSuperclass([Super class]) == Nil); - testassert(class_getSuperclass([Super class]->isa) == [Super class]); + testassert(class_getSuperclass([Sub class]) == [TestRoot class]); + testassert(class_getSuperclass(object_getClass([Sub class])) == object_getClass([TestRoot class])); + testassert(class_getSuperclass([TestRoot class]) == Nil); + testassert(class_getSuperclass(object_getClass([TestRoot class])) == [TestRoot class]); testassert(class_getSuperclass(Nil) == Nil); succeed(__FILE__); diff --git a/test/synchronized-counter.m b/test/synchronized-counter.m index 96654cf..6356f0d 100644 --- a/test/synchronized-counter.m +++ b/test/synchronized-counter.m @@ -1,4 +1,4 @@ -// TEST_CFLAGS -framework Foundation +// TEST_CONFIG #include "test.h" @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include // synchronized stress test diff --git a/test/synchronized-grid.m b/test/synchronized-grid.m index fdf31d8..64c99f4 100644 --- a/test/synchronized-grid.m +++ b/test/synchronized-grid.m @@ -1,4 +1,4 @@ -// TEST_CFLAGS -framework Foundation +// TEST_CONFIG #include "test.h" @@ -6,7 +6,7 @@ #include #include #include -#include +#include // synchronized stress test // 2-D grid of counters and locks. diff --git a/test/synchronized.m b/test/synchronized.m index 6840a3a..baa8c3f 100644 --- a/test/synchronized.m +++ b/test/synchronized.m @@ -1,8 +1,8 @@ -// TEST_CFLAGS -framework Foundation +// TEST_CONFIG #include "test.h" -#include +#include #include #include #include diff --git a/test/taggedNSPointers.m b/test/taggedNSPointers.m new file mode 100644 index 0000000..c7216f2 --- /dev/null +++ b/test/taggedNSPointers.m @@ -0,0 +1,78 @@ +// TEST_CFLAGS -framework Foundation + +#include "test.h" +#include +#import + +#if __OBJC2__ && __LP64__ + +void testTaggedNumber() +{ + NSNumber *taggedNS = [NSNumber numberWithInt: 1234]; + CFNumberRef taggedCF = (CFNumberRef)objc_unretainedPointer(taggedNS); + uintptr_t taggedAddress = (uintptr_t)taggedCF; + int result; + + testassert( CFGetTypeID(taggedCF) == CFNumberGetTypeID() ); + + CFNumberGetValue(taggedCF, kCFNumberIntType, &result); + testassert(result == 1234); + + testassert(taggedAddress & 0x1); // make sure it is really tagged + + // do some generic object-y things to the taggedPointer instance + CFRetain(taggedCF); + CFRelease(taggedCF); + + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + [dict setObject: taggedNS forKey: @"fred"]; + testassert(taggedNS == [dict objectForKey: @"fred"]); + [dict setObject: @"bob" forKey: taggedNS]; + testassert([@"bob" isEqualToString: [dict objectForKey: taggedNS]]); + + NSNumber *i12345 = [NSNumber numberWithInt: 12345]; + NSNumber *i12346 = [NSNumber numberWithInt: 12346]; + NSNumber *i12347 = [NSNumber numberWithInt: 12347]; + + NSArray *anArray = [NSArray arrayWithObjects: i12345, i12346, i12347, nil]; + testassert([anArray count] == 3); + testassert([anArray indexOfObject: i12346] == 1); + + NSSet *aSet = [NSSet setWithObjects: i12345, i12346, i12347, nil]; + testassert([aSet count] == 3); + testassert([aSet containsObject: i12346]); + + [taggedNS performSelector: @selector(intValue)]; + testassert(![taggedNS isProxy]); + testassert([taggedNS isKindOfClass: [NSNumber class]]); + testassert([taggedNS respondsToSelector: @selector(intValue)]); + + [taggedNS description]; +} + +int main() +{ + PUSH_POOL { + testTaggedNumber(); // should be tested by CF... our tests are wrong, wrong, wrong. + } POP_POOL; + + succeed(__FILE__); +} + +// OBJC2 && __LP64__ +#else +// not (OBJC2 && __LP64__) + + // Tagged pointers not supported. Crash if an NSNumber actually + // is a tagged pointer (which means this test is out of date). + +int main() +{ + PUSH_POOL { + testassert(*(void **)objc_unretainedPointer([NSNumber numberWithInt:1234])); + } POP_POOL; + + succeed(__FILE__); +} + +#endif diff --git a/test/taggedPointers.m b/test/taggedPointers.m index 72129b1..84efda3 100644 --- a/test/taggedPointers.m +++ b/test/taggedPointers.m @@ -1,47 +1,67 @@ -// TEST_CFLAGS -framework Foundation +// TEST_CONFIG #include "test.h" #include #include -#import +#import + +#if __has_feature(objc_arc) + +int main() +{ + testwarn("rdar://11368528 confused by Foundation"); + succeed(__FILE__); +} + +#else #if __OBJC2__ && __LP64__ -/* - gcc -o taggedPointers.out taggedPointers.m -L/tmp/bbum-products/Release/ -lobjc -undefined dynamic_lookup -framework Foundation -gdwarf-2 - env DYLD_LIBRARY_PATH=/tmp/bbum-products/Release/ DYLD_FRAMEWORK_PATH=/tmp/bbum-products/Release gdb ./taggedPointers.out - env DYLD_LIBRARY_PATH=/tmp/bbum-products/Debug/ DYLD_FRAMEWORK_PATH=/tmp/bbum-products/Debug gdb ./taggedPointers.out - */ static BOOL didIt; -#define TAG_VALUE(tagSlot, value) ((id)(1UL | (((uintptr_t)(tagSlot)) << 1) | (((uintptr_t)(value)) << 4))) +#define TAG_VALUE(tagSlot, value) (objc_unretainedObject((void*)(1UL | (((uintptr_t)(tagSlot)) << 1) | (((uintptr_t)(value)) << 4)))) + +@interface WeakContainer : NSObject +{ + @public + __weak id weaks[10000]; +} +@end +@implementation WeakContainer +-(void) dealloc { + for (unsigned int i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) { + testassert(weaks[i] == nil); + } + SUPER_DEALLOC(); +} +-(void) finalize { + for (unsigned int i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) { + testassert(weaks[i] == nil); + } + [super finalize]; +} +@end @interface TaggedBaseClass @end @implementation TaggedBaseClass -+ (void) initialize -{ - ; ++ (void) initialize { } -- (void) instanceMethod -{ +- (void) instanceMethod { didIt = YES; } -- (uintptr_t) taggedValue -{ - return (uintptr_t) self >> 4; +- (uintptr_t) taggedValue { + return (uintptr_t)objc_unretainedPointer(self) >> 4; } -- (NSRect) stret: (NSRect) aRect -{ - return aRect; +- (struct stret) stret: (struct stret) aStruct { + return aStruct; } -- (long double) fpret: (long double) aValue -{ +- (long double) fpret: (long double) aValue { return aValue; } @@ -50,49 +70,57 @@ static BOOL didIt; fail("TaggedBaseClass dealloc called!"); } --(id) retain { - return _objc_rootRetain(self); +static void * +retain_fn(void *self, SEL _cmd __unused) { + void * (*fn)(void *) = (typeof(fn))_objc_rootRetain; + return fn(self); +} + +static void +release_fn(void *self, SEL _cmd __unused) { + void (*fn)(void *) = (typeof(fn))_objc_rootRelease; + fn(self); } --(void) release { - return _objc_rootRelease(self); +static void * +autorelease_fn(void *self, SEL _cmd __unused) { + void * (*fn)(void *) = (typeof(fn))_objc_rootAutorelease; + return fn(self); } --(id) autorelease { - return _objc_rootAutorelease(self); +static unsigned long +retaincount_fn(void *self, SEL _cmd __unused) { + unsigned long (*fn)(void *) = (typeof(fn))_objc_rootRetainCount; + return fn(self); } --(uintptr_t) retainCount { - return _objc_rootRetainCount(self); ++(void) load { + 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, ""); + class_addMethod(self, sel_registerName("retainCount"), (IMP)retaincount_fn, ""); } + @end @interface TaggedSubclass: TaggedBaseClass @end @implementation TaggedSubclass -+ (void) initialize -{ - ; -} -- (void) instanceMethod -{ +- (void) instanceMethod { return [super instanceMethod]; } -- (uintptr_t) taggedValue -{ +- (uintptr_t) taggedValue { return [super taggedValue]; } -- (NSRect) stret: (NSRect) aRect -{ - return [super stret: aRect]; +- (struct stret) stret: (struct stret) aStruct { + return [super stret: aStruct]; } -- (long double) fpret: (long double) aValue -{ +- (long double) fpret: (long double) aValue { return [super fpret: aValue]; } @end @@ -101,122 +129,28 @@ static BOOL didIt; @end @implementation TaggedNSObjectSubclass -+ autorelease { - abort(); -} -- autorelease { - didIt = YES; - return self; -} -- retain { - didIt = YES; - return self; -} -- (oneway void) release { - didIt = YES; -} - (void) instanceMethod { didIt = YES; } -- (uintptr_t) taggedValue -{ - return (uintptr_t) self >> 4; +- (uintptr_t) taggedValue { + return (uintptr_t)objc_unretainedPointer(self) >> 4; } -- (NSRect) stret: (NSRect) aRect -{ - return aRect; +- (struct stret) stret: (struct stret) aStruct { + return aStruct; } -- (long double) fpret: (long double) aValue -{ +- (long double) fpret: (long double) aValue { return aValue; } @end -/* - -This class was used prior to integration of tagged numbers into CF. -Now that CF has tagged numbers, the test assumes their presence. - -@interface TestTaggedNumber:NSNumber -@end -@implementation TestTaggedNumber -+(void) load -{ - _objc_insert_tagged_isa(4, self); -} - -+ taggedNumberWithInt: (int) arg -{ - uint64_t value = (uint64_t) arg; - id returnValue = (id) (((uint64_t) 0x9) | (value << 4)); - return returnValue; -} - -- (void)getValue:(void *)value -{ - *(uint64_t *)value = ((uint64_t)self) >> 4; -} - -- (const char *)objCType -{ - return "i"; -} - -- (int)intValue -{ - return (int) (((uint64_t)self) >> 4); -} -@end -*/ - -void testTaggedNumber() -{ - NSNumber *taggedPointer = [NSNumber numberWithInt: 1234]; - int result; - - testassert( CFGetTypeID(taggedPointer) == CFNumberGetTypeID() ); - - CFNumberGetValue((CFNumberRef) taggedPointer, kCFNumberIntType, &result); - testassert(result == 1234); - - testassert(((uintptr_t)taggedPointer) & 0x1); // make sure it is really tagged - - // do some generic object-y things to the taggedPointer instance - CFRetain(taggedPointer); - CFRelease(taggedPointer); - - NSMutableDictionary *dict = [NSMutableDictionary dictionary]; - [dict setObject: taggedPointer forKey: @"fred"]; - testassert(taggedPointer == [dict objectForKey: @"fred"]); - [dict setObject: @"bob" forKey: taggedPointer]; - testassert([@"bob" isEqualToString: [dict objectForKey: taggedPointer]]); - - NSNumber *i12345 = [NSNumber numberWithInt: 12345]; - NSNumber *i12346 = [NSNumber numberWithInt: 12346]; - NSNumber *i12347 = [NSNumber numberWithInt: 12347]; - - NSArray *anArray = [NSArray arrayWithObjects: i12345, i12346, i12347, nil]; - testassert([anArray count] == 3); - testassert([anArray indexOfObject: i12346] == 1); - - NSSet *aSet = [NSSet setWithObjects: i12345, i12346, i12347, nil]; - testassert([aSet count] == 3); - testassert([aSet containsObject: i12346]); - - [taggedPointer performSelector: @selector(intValue)]; - testassert(![taggedPointer isProxy]); - testassert([taggedPointer isKindOfClass: [NSNumber class]]); - testassert([taggedPointer respondsToSelector: @selector(intValue)]); - - [taggedPointer description]; -} - void testGenericTaggedPointer(uint8_t tagSlot, const char *classname) { + testprintf("%s\n", classname); + Class cls = objc_getClass(classname); testassert(cls); @@ -228,50 +162,91 @@ void testGenericTaggedPointer(uint8_t tagSlot, const char *classname) [taggedPointer instanceMethod]; testassert(didIt); - NSRect originalRect = NSMakeRect(1.0, 2.0, 3.0, 4.0); - testassert(NSEqualRects(originalRect, [taggedPointer stret: originalRect])); + struct stret orig = STRET_RESULT; + testassert(stret_equal(orig, [taggedPointer stret: orig])); long double value = 3.14156789; testassert(value == [taggedPointer fpret: value]); - if (!objc_collectingEnabled()) { - // Tagged pointers should bypass refcount tables and autorelease pools - leak_mark(); - for (uintptr_t i = 0; i < 10000; i++) { - id o = TAG_VALUE(tagSlot, i); - testassert(object_getClass(o) == cls); - - [o release]; testassert([o retainCount] != 0); - [o release]; testassert([o retainCount] != 0); - CFRelease(o); testassert([o retainCount] != 0); - CFRelease(o); testassert([o retainCount] != 0); - [o retain]; - [o retain]; - [o retain]; - CFRetain(o); - CFRetain(o); - CFRetain(o); - [o autorelease]; + // Tagged pointers should bypass refcount tables and autorelease pools + // and weak reference tables + WeakContainer *w = [WeakContainer new]; + leak_mark(); + for (uintptr_t i = 0; i < sizeof(w->weaks)/sizeof(w->weaks[0]); i++) { + id o = TAG_VALUE(tagSlot, 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()) { + uintptr_t rc = _objc_rootRetainCount(o); + testassert(rc != 0); + _objc_rootRelease(o); testassert(_objc_rootRetainCount(o) == rc); + _objc_rootRelease(o); testassert(_objc_rootRetainCount(o) == rc); + _objc_rootRetain(o); testassert(_objc_rootRetainCount(o) == rc); + _objc_rootRetain(o); testassert(_objc_rootRetainCount(o) == rc); + _objc_rootRetain(o); testassert(_objc_rootRetainCount(o) == rc); +#if !__has_feature(objc_arc) + [o release]; testassert(_objc_rootRetainCount(o) == rc); + [o release]; testassert(_objc_rootRetainCount(o) == rc); + [o retain]; testassert(_objc_rootRetainCount(o) == rc); + [o retain]; testassert(_objc_rootRetainCount(o) == rc); + [o retain]; testassert(_objc_rootRetainCount(o) == rc); + objc_release(o); testassert(_objc_rootRetainCount(o) == rc); + objc_release(o); testassert(_objc_rootRetainCount(o) == rc); + objc_retain(o); testassert(_objc_rootRetainCount(o) == rc); + objc_retain(o); testassert(_objc_rootRetainCount(o) == rc); + objc_retain(o); testassert(_objc_rootRetainCount(o) == rc); +#endif + PUSH_POOL { + testassert(_objc_rootRetainCount(o) == rc); + _objc_rootAutorelease(o); + testassert(_objc_rootRetainCount(o) == rc); +#if !__has_feature(objc_arc) + [o autorelease]; + testassert(_objc_rootRetainCount(o) == rc); + objc_autorelease(o); + testassert(_objc_rootRetainCount(o) == rc); + objc_retainAutorelease(o); + testassert(_objc_rootRetainCount(o) == rc); + objc_autoreleaseReturnValue(o); + testassert(_objc_rootRetainCount(o) == rc); + objc_retainAutoreleaseReturnValue(o); + testassert(_objc_rootRetainCount(o) == rc); + objc_retainAutoreleasedReturnValue(o); + testassert(_objc_rootRetainCount(o) == rc); +#endif + } POP_POOL; + testassert(_objc_rootRetainCount(o) == rc); } - 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); + } + RELEASE_VAR(w); } int main() { - NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init]; - - _objc_insert_tagged_isa(5, objc_getClass("TaggedBaseClass")); - testGenericTaggedPointer(5, "TaggedBaseClass"); - - _objc_insert_tagged_isa(2, objc_getClass("TaggedSubclass")); - testGenericTaggedPointer(2, "TaggedSubclass"); - - _objc_insert_tagged_isa(3, objc_getClass("TaggedNSObjectSubclass")); - testGenericTaggedPointer(3, "TaggedNSObjectSubclass"); - - testTaggedNumber(); // should be tested by CF... our tests are wrong, wrong, wrong. - [p release]; + PUSH_POOL { + _objc_insert_tagged_isa(5, objc_getClass("TaggedBaseClass")); + testGenericTaggedPointer(5, "TaggedBaseClass"); + + _objc_insert_tagged_isa(2, objc_getClass("TaggedSubclass")); + testGenericTaggedPointer(2, "TaggedSubclass"); + + _objc_insert_tagged_isa(3, objc_getClass("TaggedNSObjectSubclass")); + testGenericTaggedPointer(3, "TaggedNSObjectSubclass"); + } POP_POOL; succeed(__FILE__); } @@ -280,15 +255,13 @@ int main() #else // not (OBJC2 && __LP64__) - // Tagged pointers not supported. Crash if an NSNumber actually - // is a tagged pointer (which means this test is out of date). + // Tagged pointers not supported. -int main() { - NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init]; - testassert(*(id *)[NSNumber numberWithInt:1234]); - [p release]; - +int main() +{ succeed(__FILE__); } #endif + +#endif diff --git a/test/test.h b/test/test.h index ed378aa..c9cce8b 100644 --- a/test/test.h +++ b/test/test.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -18,7 +19,9 @@ #include #include #include +#include #include +#include #include static inline void succeed(const char *name) __attribute__((noreturn)); @@ -37,13 +40,14 @@ static inline void succeed(const char *name) static inline void fail(const char *msg, ...) __attribute__((noreturn)); static inline void fail(const char *msg, ...) { - va_list v; if (msg) { - fprintf(stderr, "BAD: "); + char *msg2; + asprintf(&msg2, "BAD: %s\n", msg); + va_list v; va_start(v, msg); - vfprintf(stderr, msg, v); + vfprintf(stderr, msg2, v); va_end(v); - fprintf(stderr, "\n"); + free(msg2); } else { fprintf(stderr, "BAD\n"); } @@ -51,7 +55,7 @@ static inline void fail(const char *msg, ...) } #define testassert(cond) \ - ((void) ((cond) ? (void)0 : __testassert(#cond, __FILE__, __LINE__))) + ((void) (((cond) != 0) ? (void)0 : __testassert(#cond, __FILE__, __LINE__))) #define __testassert(cond, file, line) \ (fail("failed assertion '%s' at %s:%u", cond, __FILE__, __LINE__)) @@ -74,11 +78,13 @@ static inline void fail(const char *msg, ...) static inline void testprintf(const char *msg, ...) { if (msg && getenv("VERBOSE")) { + char *msg2; + asprintf(&msg2, "VERBOSE: %s", msg); va_list v; va_start(v, msg); - fprintf(stderr, "VERBOSE: "); - vfprintf(stderr, msg, v); + vfprintf(stderr, msg2, v); va_end(v); + free(msg2); } } @@ -88,12 +94,13 @@ static inline void testprintf(const char *msg, ...) static inline void testwarn(const char *msg, ...) { if (msg) { + char *msg2; + asprintf(&msg2, "WARN: %s\n", msg); va_list v; va_start(v, msg); - fprintf(stderr, "WARN: "); - vfprintf(stderr, msg, v); + vfprintf(stderr, msg2, v); va_end(v); - fprintf(stderr, "\n"); + free(msg2); } } @@ -143,6 +150,27 @@ static inline void testnoop() { } #endif +// 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 +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) +{ + pthread_t th; + testcodehack = code; // force GC not-thread-local, avoid ARC void* casts + pthread_create(&th, NULL, _testthread, NULL); + pthread_join(th, NULL); +} + /* Make sure libobjc does not call global operator new. Any test that DOES need to call global operator new must `#define TEST_CALLS_OPERATOR_NEW` before including test.h. @@ -246,4 +274,132 @@ static inline bool is_guardmalloc(void) return (env && 0 == strcmp(env, "YES")); } + +/* Memory management compatibility macros */ + +static id self_fn(id x) __attribute__((used)); +static id self_fn(id x) { return x; } + +#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] +# define AUTORELEASE(x) [x autorelease] +#endif + +/* gcc compatibility macros */ +/* @autoreleasepool should generate objc_autoreleasePoolPush/Pop on 10.7/5.0 */ +//#if !defined(__clang__) +# define PUSH_POOL { void *pool = objc_autoreleasePoolPush(); +# define POP_POOL objc_autoreleasePoolPop(pool); } +//#else +//# define PUSH_POOL @autoreleasepool +//# define POP_POOL +//#endif + +#if __OBJC__ + +/* General purpose root class */ + +@interface TestRoot { + @public + Class isa; +} + ++(void) load; ++(void) initialize; + +-(id) self; +-(Class) class; +-(Class) superclass; + ++(id) new; ++(id) alloc; ++(id) allocWithZone:(void*)zone; +-(id) copy; +-(id) mutableCopy; +-(id) init; +-(void) dealloc; +-(void) finalize; +@end +@interface TestRoot (RR) +-(id) retain; +-(oneway void) release; +-(id) autorelease; +-(unsigned long) retainCount; +-(id) copyWithZone:(void *)zone; +-(id) mutableCopyWithZone:(void*)zone; +@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; + +#endif + + +// Struct that does not return in registers on any architecture + +struct stret { + int a; + int b; + int c; + int d; + int e; +}; + +static inline BOOL stret_equal(struct stret a, struct stret b) +{ + return (a.a == b.a && + a.b == b.b && + a.c == b.c && + a.d == b.d && + a.e == b.e); +} + +static struct stret STRET_RESULT __attribute__((used)) = {1, 2, 3, 4, 5}; + #endif diff --git a/test/test.pl b/test/test.pl index 9635eff..d07ca21 100755 --- a/test/test.pl +++ b/test/test.pl @@ -39,12 +39,13 @@ testname: options: ARCH= - GC=0|1 SDK= ROOT=/path/to/project.roots/ - + CC= + MEM=mrc,arc,gc + STDLIB=libc++,libstdc++ GUARDMALLOC=0|1 BUILD=0|1 @@ -56,8 +57,8 @@ examples: test installed library, x86_64, no gc $0 - test buildit-built root, i386 and x86_64, gc and no gc, clang compiler - $0 ARCH=i386,x86_64 ROOT=/tmp/libclosure.roots GC=1,0 CC=clang + test buildit-built root, 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 with iOS simulator $0 ARCH=i386 ROOT=/tmp/libclosure.roots SDK=iphonesimulator @@ -84,7 +85,8 @@ my %ALL_TESTS; # SDK=system,macosx,iphoneos,iphonesimulator # LANGUAGE=c,c++,objective-c,objective-c++ # CC=clang,gcc-4.2,llvm-gcc-4.2 -# GC=0,1 +# MEM=mrc,arc,gc +# STDLIB=libc++,libstdc++ # GUARDMALLOC=0,1 # things you can set once on the command line @@ -104,7 +106,9 @@ my $crashcatch = <<'END'; #include #include #include -#include + +// from dyld-interposing.h +#define DYLD_INTERPOSE(_replacement,_replacee) __attribute__((used)) static struct{ const void* replacement; const void* replacee; } _interpose_##_replacee __attribute__ ((section ("__DATA,__interpose"))) = { (const void*)(unsigned long)&_replacement, (const void*)(unsigned long)&_replacee }; static void catchcrash(int sig) { @@ -244,12 +248,16 @@ sub cplusplus { } # Returns an array of all sdks from `xcodebuild -showsdks` +my @sdks_memo; sub getsdks { - return ("system", `xcodebuild -showsdks` =~ /-sdk (.+)$/mg); + if (!@sdks_memo) { + @sdks_memo = ("system", `xcodebuild -showsdks` =~ /-sdk (.+)$/mg); + } + return @sdks_memo; } -# Returns whether the given sdk supports GC -sub supportsgc { +# Returns whether the given sdk supports -lauto +sub supportslibauto { my ($sdk) = @_; return 1 if $sdk eq "system"; return 1 if $sdk =~ /^macosx/; @@ -362,12 +370,12 @@ sub filter_expected my $name = shift; my %T = %{$C{"TEST_$name"}}; - my $check = $T{TEST_RUN_OUTPUT} || return ""; + my $runerror = $T{TEST_RUN_OUTPUT} || return ""; my $bad = ""; my $output = join("\n", @$outputref) . "\n"; - if ($output !~ /$check/s) { + if ($output !~ /$runerror/) { $bad = "(run output does not match TEST_RUN_OUTPUT)"; @$outputref = ("FAIL: $name"); } else { @@ -478,18 +486,19 @@ sub filter_guardmalloc my $errors = 0; my @new_output; + my $count = 0; for my $line (@$outputref) { - if ($line =~ /^GuardMalloc: /) { - # guardmalloc prologue - next; - } if ($line !~ /^GuardMalloc\[[^\]]+\]: /) { # not guardmalloc output push @new_output, $line; next; } - $errors = 1; + # Ignore 4 lines of guardmalloc prologue. + # Anything further is a guardmalloc error. + if (++$count > 4) { + $errors = 1; + } } @$outputref = @new_output; @@ -533,7 +542,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 TEST_DISABLED)$def\n"; + print "${yellow}SKIP: $name (disabled by TEST_DISABLED)$def\n"; return 0; } @@ -591,7 +600,7 @@ sub gather_simple { if (!$ok) { my $plural = (@condvalues > 1) ? "one of: " : ""; - print "SKIP: $name ($condkey=$testvalue, but test requires $plural", join(' ', @condvalues), ")\n"; + print "SKIP: $name ($condkey=$testvalue, but test requires $plural", join(' ', @condvalues), ")\n"; return 0; } } @@ -644,7 +653,7 @@ sub build_simple { my $ok; if (my $builderror = $T{TEST_BUILD_OUTPUT}) { # check for expected output and ignore $? - if ($output =~ /$builderror/s) { + if ($output =~ /$builderror/) { $ok = 1; } else { print "${red}FAIL: /// test '$name' \\\\\\$def\n"; @@ -711,13 +720,13 @@ sub run_simple { $env =~ s/DYLD_LIBRARY_PATH=\S+//; $env =~ s/DYLD_ROOT_PATH=\S+//; - my $cmd = "ssh iphone 'cd $remotedir && $remotedyld $env ./$name.out'"; + my $cmd = "ssh iphone 'cd $remotedir && env $env $remotedyld ./$name.out'"; $output = make("$cmd"); } else { # run locally - my $cmd = "$env ./$name.out"; + my $cmd = "env $env ./$name.out"; $output = make("sh -c '$cmd 2>&1' 2>&1"); # need extra sh level to capture "sh: Illegal instruction" after crash # fixme fail if $? except tests that expect to crash @@ -727,11 +736,38 @@ sub run_simple { } +my %compiler_memo; +sub find_compiler { + my ($cc, $sdk, $sdk_path) = @_; + + # memoize + my $key = $cc . ':' . $sdk; + my $result = $compiler_memo{$key}; + return $result if defined $result; + + if (-e $cc) { + $result = $cc; + } elsif (-e "$sdk_path/$cc") { + $result = "$sdk_path/$cc"; + } elsif ($sdk eq "system" && -e "/usr/bin/$cc") { + $result = "/usr/bin/$cc"; + } elsif ($sdk eq "system") { + $result = `xcrun -find $cc 2>/dev/null`; + } else { + $result = `xcrun -sdk $sdk -find $cc 2>/dev/null`; + } + + chomp $result; + $compiler_memo{$key} = $result; + return $result; +} + sub make_one_config { my $configref = shift; my $root = shift; my %C = %{$configref}; + # Aliases $C{LANGUAGE} = "objective-c" if $C{LANGUAGE} eq "objc"; $C{LANGUAGE} = "objective-c++" if $C{LANGUAGE} eq "objc++"; @@ -762,10 +798,10 @@ sub make_one_config { # but before adding other settings my $configname = config_name(%C); die if ($configname =~ /'/); - die if ($configname =~ /\//); die if ($configname =~ / /); - $C{DIR} = "$BUILDDIR/$configname"; ($C{NAME} = $configname) =~ s/~/ /g; + (my $configdir = $configname) =~ s#/##g; + $C{DIR} = "$BUILDDIR/$configdir"; $C{SDK_PATH} = "/"; if ($C{SDK} ne "system") { @@ -785,47 +821,29 @@ sub make_one_config { } elsif (-e "$root/$TESTLIBNAME") { $C{TESTLIB} = "$root/$TESTLIBNAME"; } else { - die "No $TESTLIBNAME in root '$root' and sdk '$C{SDK_PATH}'\n"; + die "No $TESTLIBNAME in root '$root' for sdk '$C{SDK_PATH}'\n"; } - # Look up compilers - $C{CXX} = cplusplus($C{CC}); - if ($BUILD) { - my $oldcc = $C{CC}; - my $oldcxx = $C{CXX}; - - if (-e $C{CC}) { - # use it - } elsif (-e "$C{SDK_PATH}/$C{CC}") { - $C{CC} = "$C{SDK_PATH}/$C{CC}"; - } elsif ($C{SDK} eq "system" && -e "/usr/bin/$C{CC}") { - $C{CC} = "/usr/bin/$C{CC}"; - } elsif ($C{SDK} eq "system") { - $C{CC} = `xcrun -find $C{CC} 2>/dev/null`; - chomp $C{CC}; - } else { - $C{CC} = `xcrun -sdk $C{SDK} -find $C{CC} 2>/dev/null`; - chomp $C{CC}; + if ($VERBOSE) { + my @uuids = `/usr/bin/dwarfdump -u '$C{TESTLIB}'`; + while (my $uuid = shift @uuids) { + print "note: $uuid"; } + } - if (-e $C{CXX}) { - # use it - } elsif (-e "$C{SDK_PATH}/$C{CXX}") { - $C{CXX} = "$C{SDK_PATH}/$C{CXX}"; - } elsif ($C{SDK} eq "system" && -e "/usr/bin/$C{CXX}") { - $C{CXX} = "/usr/bin/$C{CXX}"; - } elsif ($C{SDK} eq "system") { - $C{CXX} = `xcrun -find $C{CXX} 2>/dev/null`; - chomp $C{CXX}; - } else { - $C{CXX} = `xcrun -sdk $C{SDK} -find $C{CXX} 2>/dev/null`; - chomp $C{CXX}; - } + # Look up compilers + my $cc = $C{CC}; + my $cxx = cplusplus($C{CC}); + if (! $BUILD) { + $C{CC} = $cc; + $C{CXX} = $cxx; + } else { + $C{CC} = find_compiler($cc, $C{SDK}, $C{SDK_PATH}); + $C{CXX} = find_compiler($cxx, $C{SDK}, $C{SDK_PATH}); - die "No compiler '$oldcc' in SDK '$C{SDK}'\n" if ! -e $C{CC}; - die "No compiler '$oldcxx' '$C{CXX}' in SDK '$C{SDK}'\n" if ! -e $C{CXX}; - } - + die "No compiler '$cc' ('$C{CC}') in SDK '$C{SDK}'\n" if !-e $C{CC}; + die "No compiler '$cxx' ('$C{CXX}') in SDK '$C{SDK}'\n" if !-e $C{CXX}; + } # Populate cflags @@ -865,17 +883,30 @@ sub make_one_config { if ($C{CC} =~ /clang/) { $cflags .= " -Qunused-arguments -fno-caret-diagnostics"; + $cflags .= " -stdlib=$C{STDLIB} -fno-objc-link-runtime"; } + # Populate objcflags $objcflags .= " -lobjc"; - if ($C{GC}) { + if ($C{MEM} eq "gc") { $objcflags .= " -fobjc-gc"; } - if (supportsgc($C{SDK})) { + elsif ($C{MEM} eq "arc") { + $objcflags .= " -fobjc-arc"; + } + elsif ($C{MEM} eq "mrc") { + # nothing + } + else { + die "unrecognized MEM '$C{MEM}'\n"; + } + + if (supportslibauto($C{SDK})) { + # do this even for non-GC tests $objcflags .= " -lauto"; - } + } # Populate ENV_PREFIX $C{ENV} = "LANG=C"; @@ -902,10 +933,10 @@ sub make_one_config { } # Populate compiler commands - $C{COMPILE_C} = "LANG=C '$C{CC}' $cflags -x c -std=gnu99"; - $C{COMPILE_CXX} = "LANG=C '$C{CXX}' $cflags -x c++"; - $C{COMPILE_M} = "LANG=C '$C{CC}' $cflags $objcflags -x objective-c -std=gnu99"; - $C{COMPILE_MM} = "LANG=C '$C{CXX}' $cflags $objcflags -x objective-c++"; + $C{COMPILE_C} = "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} = $C{COMPILE_C} if $C{LANGUAGE} eq "c"; $C{COMPILE} = $C{COMPILE_CXX} if $C{LANGUAGE} eq "c++"; @@ -913,7 +944,37 @@ sub make_one_config { $C{COMPILE} = $C{COMPILE_MM} if $C{LANGUAGE} eq "objective-c++"; die "unknown language '$C{LANGUAGE}'\n" if !defined $C{COMPILE}; - ($C{COMPILE_NOGC} = $C{COMPILE}) =~ s/-fobjc-gc\S*//; + ($C{COMPILE_NOMEM} = $C{COMPILE}) =~ s/ -fobjc-(?:gc|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"; + } + + if ($C{MEM} eq "gc" && $C{SDK} =~ /^iphone/) { + print "note: skipping configuration $C{NAME}\n"; + print "note: because SDK=$C{SDK} does not support MEM=$C{MEM}\n"; + return 0; + } + if ($C{MEM} eq "arc" && $C{SDK} !~ /^iphone/ && $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"; + return 0; + } %$configref = %C; } @@ -937,11 +998,14 @@ sub make_configs { @results = @newresults; } + my @newresults; for my $configref(@results) { - make_one_config($configref, $root); + if (make_one_config($configref, $root)) { + push @newresults, $configref; + } } - return @results; + return @newresults; } sub config_name { @@ -1090,10 +1154,11 @@ $args{ARCH} = getargs("ARCHS", $default_arch) if !@{$args{ARCH}}[0]; $args{SDK} = getargs("SDK", "system"); -$args{GC} = getbools("GC", 0); +$args{MEM} = getargs("MEM", "mrc"); $args{LANGUAGE} = [ map { lc($_) } @{getargs("LANGUAGE", "objective-c")} ]; +$args{STDLIB} = getargs("STDLIB", "libstdc++"); -$args{CC} = getargs("CC", "llvm-gcc-4.2"); +$args{CC} = getargs("CC", "clang"); $args{GUARDMALLOC} = getbools("GUARDMALLOC", 0); diff --git a/test/testroot.i b/test/testroot.i new file mode 100644 index 0000000..118c438 --- /dev/null +++ b/test/testroot.i @@ -0,0 +1,229 @@ +// testroot.i +// Implementation of class TestRoot +// Include this file into your main test file to use it. + +#include "test.h" +#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; + + +@implementation TestRoot + +// These all use void* pending rdar://9310005. + +static void * +retain_fn(void *self, SEL _cmd __unused) { + OSAtomicIncrement32(&TestRootRetain); + void * (*fn)(void *) = (typeof(fn))_objc_rootRetain; + return fn(self); +} + +static void +release_fn(void *self, SEL _cmd __unused) { + OSAtomicIncrement32(&TestRootRelease); + void (*fn)(void *) = (typeof(fn))_objc_rootRelease; + fn(self); +} + +static void * +autorelease_fn(void *self, SEL _cmd __unused) { + OSAtomicIncrement32(&TestRootAutorelease); + void * (*fn)(void *) = (typeof(fn))_objc_rootAutorelease; + return fn(self); +} + +static unsigned long +retaincount_fn(void *self, SEL _cmd __unused) { + OSAtomicIncrement32(&TestRootRetainCount); + 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"); + return fn(self, zone); +} + +static void * +plusretain_fn(void *self __unused, SEL _cmd __unused) { + OSAtomicIncrement32(&TestRootPlusRetain); + return self; +} + +static void +plusrelease_fn(void *self __unused, SEL _cmd __unused) { + OSAtomicIncrement32(&TestRootPlusRelease); +} + +static void * +plusautorelease_fn(void *self, SEL _cmd __unused) { + OSAtomicIncrement32(&TestRootPlusAutorelease); + return self; +} + +static unsigned long +plusretaincount_fn(void *self __unused, SEL _cmd __unused) { + OSAtomicIncrement32(&TestRootPlusRetainCount); + return ULONG_MAX; +} + ++(void) load { + OSAtomicIncrement32(&TestRootLoad); + + // install methods that ARR 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, ""); + class_addMethod(self, sel_registerName("retainCount"), (IMP)retaincount_fn, ""); + class_addMethod(self, sel_registerName("copyWithZone:"), (IMP)copywithzone_fn, ""); + + class_addMethod(object_getClass(self), sel_registerName("retain"), (IMP)plusretain_fn, ""); + class_addMethod(object_getClass(self), sel_registerName("release"), (IMP)plusrelease_fn, ""); + class_addMethod(object_getClass(self), sel_registerName("autorelease"), (IMP)plusautorelease_fn, ""); + class_addMethod(object_getClass(self), sel_registerName("retainCount"), (IMP)plusretaincount_fn, ""); +} + + ++(void) initialize { + OSAtomicIncrement32(&TestRootInitialize); +} + +-(id) self { + return self; +} + ++(Class) class { + return self; +} + +-(Class) class { + return object_getClass(self); +} + ++(Class) superclass { + return class_getSuperclass(self); +} + +-(Class) superclass { + return class_getSuperclass([self class]); +} + ++(id) new { + return [[self alloc] init]; +} + ++(id) alloc { + OSAtomicIncrement32(&TestRootAlloc); + void * (*fn)(id __unsafe_unretained) = (typeof(fn))_objc_rootAlloc; + return objc_retainedObject(fn(self)); +} + ++(id) allocWithZone:(void *)zone { + OSAtomicIncrement32(&TestRootAllocWithZone); + void * (*fn)(id __unsafe_unretained, void *) = (typeof(fn))_objc_rootAllocWithZone; + return objc_retainedObject(fn(self, zone)); +} + ++(id) copy { + return self; +} + ++(id) copyWithZone:(void *) __unused zone { + return self; +} + +-(id) copy { + OSAtomicIncrement32(&TestRootCopy); + return [self copyWithZone:NULL]; +} + ++(id) mutableCopyWithZone:(void *) __unused zone { + fail("+mutableCopyWithZone: called"); +} + +-(id) mutableCopy { + OSAtomicIncrement32(&TestRootMutableCopy); + return [self mutableCopyWithZone:NULL]; +} + +-(id) mutableCopyWithZone:(void *) __unused zone { + OSAtomicIncrement32(&TestRootMutableCopyWithZone); + void * (*fn)(id __unsafe_unretained) = (typeof(fn))_objc_rootAlloc; + return objc_retainedObject(fn(object_getClass(self))); +} + +-(id) init { + OSAtomicIncrement32(&TestRootInit); + return _objc_rootInit(self); +} + ++(void) dealloc { + fail("+dealloc called"); +} + +-(void) dealloc { + OSAtomicIncrement32(&TestRootDealloc); + _objc_rootDealloc(self); +} + ++(void) finalize { + fail("+finalize called"); +} + +-(void) finalize { + OSAtomicIncrement32(&TestRootFinalize); + _objc_rootFinalize(self); +} + ++(BOOL) _tryRetain { + return YES; +} + +-(BOOL) _tryRetain { + OSAtomicIncrement32(&TestRootTryRetain); + return _objc_rootTryRetain(self); +} + ++(BOOL) _isDeallocating { + return NO; +} + +-(BOOL) _isDeallocating { + OSAtomicIncrement32(&TestRootIsDeallocating); + return _objc_rootIsDeallocating(self); +} + +-(BOOL) allowsWeakReference { + return ! [self _isDeallocating]; +} + +-(BOOL) retainWeakReference { + return [self _tryRetain]; +} + + +@end diff --git a/test/unload.h b/test/unload.h index 6e932ea..5287fe9 100644 --- a/test/unload.h +++ b/test/unload.h @@ -1,10 +1,6 @@ -@interface SmallClass { id isa; } -+(id)new; --(void)free; -@end +#include "test.h" -@interface BigClass { id isa; } -+(id)new; --(void)free; -@end +@interface SmallClass : TestRoot @end + +@interface BigClass : TestRoot @end diff --git a/test/unload.m b/test/unload.m index cf3c97a..7379522 100644 --- a/test/unload.m +++ b/test/unload.m @@ -14,6 +14,16 @@ END #include "unload.h" +#if __has_feature(objc_arc) + +int main() +{ + testwarn("rdar://11368528 confused by Foundation"); + succeed(__FILE__); +} + +#else + static BOOL hasName(const char * const *names, const char *query) { const char *name; @@ -66,12 +76,12 @@ void cycle(void) for (i = 0; i < 10000; i++) { sprintf(buf, "method_%d", i); SEL sel = sel_registerName(buf); - objc_msgSend(o2, sel); - objc_msgSend(o2->isa, sel); + ((void(*)(id, SEL))objc_msgSend)(o2, sel); + ((void(*)(id, SEL))objc_msgSend)(object_getClass(o2), sel); } - [o1 free]; - [o2 free]; + RELEASE_VAR(o1); + RELEASE_VAR(o2); testcollect(); @@ -80,8 +90,8 @@ void cycle(void) err = dlclose(bundle); testassert(err == -1); // already closed - testassert(!objc_getClass("SmallClass")); - testassert(!objc_getClass("BigClass")); + testassert(objc_getClass("SmallClass") == NULL); + testassert(objc_getClass("BigClass") == NULL); names = objc_copyImageNames(&imageCount); testassert(names); @@ -115,7 +125,8 @@ int main() while (count--) { cycle(); } - leak_check(0); + // leak_check(0); + testwarn("rdar://11369189 can't check leaks because libxpc leaks"); // 5359412 Make sure dylibs with nothing other than image_info can close void *dylib = dlopen("unload3.dylib", RTLD_LAZY); @@ -137,3 +148,5 @@ int main() succeed(__FILE__); } + +#endif diff --git a/test/unload2.m b/test/unload2.m index 7a21d97..7869a0b 100644 --- a/test/unload2.m +++ b/test/unload2.m @@ -1,26 +1,15 @@ #include "unload.h" -#include +#include "testroot.i" -@implementation SmallClass -+(void)initialize { } -+(id)new { - return class_createInstance(self, 0); -} --(void)free { object_dispose(self); } +@implementation SmallClass : TestRoot -(void)unload2_instance_method { } --(void)finalize { } @end -@implementation BigClass -+(void)initialize { } -+(id)new { - return class_createInstance(self, 0); -} --(void)free { object_dispose(self); } --(void)finalize { } --(void)forward:(int)a1:(int)a2 { a1 = a2; } +@implementation BigClass : TestRoot ++(void) forward:(void *) __unused sel :(void*) __unused args { } +-(void) forward:(void *) __unused sel :(void*) __unused args { } @end diff --git a/test/unwind.m b/test/unwind.m index 30db745..3216880 100644 --- a/test/unwind.m +++ b/test/unwind.m @@ -1,8 +1,8 @@ -// TEST_CFLAGS -framework Foundation +// TEST_CONFIG #include "test.h" #include -#include +#include #if !defined(__OBJC2__) @@ -16,11 +16,13 @@ int main() static int state; @interface Foo : NSObject @end +@interface Bar : NSObject @end @interface Foo (Unimplemented) +(void)method; @end +@implementation Bar @end @implementation Foo @@ -62,32 +64,32 @@ int main() // unwind exception and alt handler through objc_msgSend() - NSAutoreleasePool *pool = [NSAutoreleasePool new]; - - state = 0; - for (i = 0; i < 100000; i++) { - @try { - testassert(state == 0); state++; - [Foo method]; - testassert(0); - } @catch (NSException *e) { - testassert(0); - } @catch (Foo *e) { - testassert(e == exc); - testassert(state == 4); state++; - testassert(state == 5); [e check]; // state++ - [e release]; - } @catch (id e) { - testassert(0); - } @catch (...) { - testassert(0); - } @finally { - testassert(state == 6); state++; + PUSH_POOL { + + state = 0; + for (i = 0; i < 100000; i++) { + @try { + testassert(state == 0); state++; + [Foo method]; + testassert(0); + } @catch (Bar *e) { + testassert(0); + } @catch (Foo *e) { + testassert(e == exc); + testassert(state == 4); state++; + testassert(state == 5); [e check]; // state++ + RELEASE_VAR(exc); + } @catch (id e) { + testassert(0); + } @catch (...) { + testassert(0); + } @finally { + testassert(state == 6); state++; + } + testassert(state == 7); state = 0; } - testassert(state == 7); state = 0; - } - - [pool drain]; + + } POP_POOL; succeed(__FILE__); #endif diff --git a/test/verify-exports.pl b/test/verify-exports.pl index b242a27..dc544ff 100755 --- a/test/verify-exports.pl +++ b/test/verify-exports.pl @@ -136,6 +136,8 @@ for my $cdecl (@cdecls) { ($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); diff --git a/test/weak.h b/test/weak.h index bacc39e..a5c9158 100644 --- a/test/weak.h +++ b/test/weak.h @@ -30,6 +30,11 @@ WEAK_IMPORT +(int) method; @end +@interface MissingRoot (RR) +-(id) retain; +-(void) release; +@end + WEAK_IMPORT @interface MissingSuper : MissingRoot { @public @@ -49,6 +54,11 @@ WEAK_IMPORT +(int) method; @end +@interface NotMissingRoot (RR) +-(id) retain; +-(void) release; +@end + @interface NotMissingSuper : NotMissingRoot { @public int unused[100]; diff --git a/test/weak.m b/test/weak.m index fd69485..3d1fa10 100644 --- a/test/weak.m +++ b/test/weak.m @@ -120,11 +120,10 @@ # define TESTIVAR(cond) /* rdar */ #endif -static BOOL classInList(Class *classes, const char *name) +static BOOL classInList(__unsafe_unretained Class classes[], const char *name) { - Class *cp; - for (cp = classes; *cp; cp++) { - if (0 == strcmp(class_getName(*cp), name)) return YES; + for (int i = 0; classes[i] != nil; i++) { + if (0 == strcmp(class_getName(classes[i]), name)) return YES; } return NO; } @@ -160,10 +159,10 @@ int main(int argc __unused, char **argv) testassert([MyNotMissingSuper class]); testassert([MyNotMissingSub class]); if (weakMissing) { - testassert(! [MissingRoot class]); - testassert(! [MissingSuper class]); - testassert(! [MyMissingSuper class]); - testassert(! [MyMissingSub class]); + testassert([MissingRoot class] == nil); + testassert([MissingSuper class] == nil); + testassert([MyMissingSuper class] == nil); + testassert([MyMissingSub class] == nil); } else { testassert([MissingRoot class]); testassert([MissingSuper class]); @@ -177,10 +176,10 @@ int main(int argc __unused, char **argv) testassert(objc_getClass("MyNotMissingSuper")); testassert(objc_getClass("MyNotMissingSub")); if (weakMissing) { - testassert(! objc_getClass("MissingRoot")); - testassert(! objc_getClass("MissingSuper")); - testassert(! objc_getClass("MyMissingSuper")); - testassert(! objc_getClass("MyMissingSub")); + testassert(objc_getClass("MissingRoot") == nil); + testassert(objc_getClass("MissingSuper") == nil); + testassert(objc_getClass("MyMissingSuper") == nil); + testassert(objc_getClass("MyMissingSub") == nil); } else { testassert(objc_getClass("MissingRoot")); testassert(objc_getClass("MissingSuper")); @@ -189,23 +188,27 @@ int main(int argc __unused, char **argv) } // class list - Class *classes = objc_copyClassList(NULL); - testassert(classInList(classes, "NotMissingRoot")); - testassert(classInList(classes, "NotMissingSuper")); - testassert(classInList(classes, "MyNotMissingSuper")); - testassert(classInList(classes, "MyNotMissingSub")); + union { + Class *c; + void *v; + } classes; + classes.c = objc_copyClassList(NULL); + testassert(classInList(classes.c, "NotMissingRoot")); + testassert(classInList(classes.c, "NotMissingSuper")); + testassert(classInList(classes.c, "MyNotMissingSuper")); + testassert(classInList(classes.c, "MyNotMissingSub")); if (weakMissing) { - testassert(! classInList(classes, "MissingRoot")); - testassert(! classInList(classes, "MissingSuper")); - testassert(! classInList(classes, "MyMissingSuper")); - testassert(! classInList(classes, "MyMissingSub")); + testassert(! classInList(classes.c, "MissingRoot")); + testassert(! classInList(classes.c, "MissingSuper")); + testassert(! classInList(classes.c, "MyMissingSuper")); + testassert(! classInList(classes.c, "MyMissingSub")); } else { - testassert(classInList(classes, "MissingRoot")); - testassert(classInList(classes, "MissingSuper")); - testassert(classInList(classes, "MyMissingSuper")); - testassert(classInList(classes, "MyMissingSub")); + testassert(classInList(classes.c, "MissingRoot")); + testassert(classInList(classes.c, "MissingSuper")); + testassert(classInList(classes.c, "MyMissingSuper")); + testassert(classInList(classes.c, "MyMissingSub")); } - free(classes); + free(classes.v); // class name list const char *image = class_getImageName(objc_getClass("NotMissingRoot")); @@ -277,33 +280,33 @@ int main(int argc __unused, char **argv) NotMissingSuper *obj2; MissingSuper *obj3; testassert((obj = [[NotMissingRoot alloc] init])); - [obj dealloc]; + RELEASE_VAR(obj); testassert((obj2 = [[NotMissingSuper alloc] init])); TESTIVAR(obj2->ivar == 200); - [obj2 dealloc]; + RELEASE_VAR(obj2); testassert((obj2 = [[MyNotMissingSuper alloc] init])); TESTIVAR(obj2->ivar == 200); - [obj2 dealloc]; + RELEASE_VAR(obj2); testassert((obj2 = [[MyNotMissingSub alloc] init])); TESTIVAR(obj2->ivar == 200); - [obj2 dealloc]; + RELEASE_VAR(obj2); if (weakMissing) { - testassert(! [[MissingRoot alloc] init]); - testassert(! [[MissingSuper alloc] init]); - testassert(! [[MyMissingSuper alloc] init]); - testassert(! [[MyMissingSub alloc] init]); + testassert([[MissingRoot alloc] init] == nil); + testassert([[MissingSuper alloc] init] == nil); + testassert([[MyMissingSuper alloc] init] == nil); + testassert([[MyMissingSub alloc] init] == nil); } else { testassert((obj = [[MissingRoot alloc] init])); - [obj dealloc]; + RELEASE_VAR(obj); testassert((obj3 = [[MissingSuper alloc] init])); TESTIVAR(obj3->ivar == 100); - [obj3 dealloc]; + RELEASE_VAR(obj3); testassert((obj3 = [[MyMissingSuper alloc] init])); TESTIVAR(obj3->ivar == 100); - [obj3 dealloc]; + RELEASE_VAR(obj3); testassert((obj3 = [[MyMissingSub alloc] init])); TESTIVAR(obj3->ivar == 100); - [obj3 dealloc]; + RELEASE_VAR(obj3); } *strrchr(argv[0], '.') = 0; diff --git a/test/weak2.m b/test/weak2.m index 260b662..f887e2d 100644 --- a/test/weak2.m +++ b/test/weak2.m @@ -2,19 +2,44 @@ #include "test.h" #include "weak.h" +#include int state = 0; +static void *noop_fn(void *self, SEL _cmd __unused) { + return self; +} +static id __unsafe_unretained retain_fn(id __unsafe_unretained self, SEL _cmd __unused) { + return _objc_rootRetain(self); +} +static void release_fn(id __unsafe_unretained self, SEL _cmd __unused) { + _objc_rootRelease(self); +} +static void autorelease_fn(id __unsafe_unretained self, SEL _cmd __unused) { + _objc_rootAutorelease(self); +} + #if !defined(EMPTY) @implementation MissingRoot +(void) initialize { } +(Class) class { return self; } -+(id) alloc { return class_createInstance(self, 0); } ++(id) alloc { return _objc_rootAlloc(self); } ++(id) allocWithZone:(void*)zone { return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone); } -(id) init { return self; } --(void) dealloc { object_dispose(self); } +-(void) dealloc { _objc_rootDealloc(self); } +(int) method { return 10; } -+(void) load { state++; } ++(void) load { + 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, ""); + + class_addMethod(object_getClass(self), sel_registerName("retain"), (IMP)noop_fn, ""); + class_addMethod(object_getClass(self), sel_registerName("release"), (IMP)noop_fn, ""); + class_addMethod(object_getClass(self), sel_registerName("autorelease"), (IMP)noop_fn, ""); + + state++; +} @end @implementation MissingSuper @@ -28,11 +53,22 @@ int state = 0; @implementation NotMissingRoot +(void) initialize { } +(Class) class { return self; } -+(id) alloc { return class_createInstance(self, 0); } ++(id) alloc { return _objc_rootAlloc(self); } ++(id) allocWithZone:(void*)zone { return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone); } -(id) init { return self; } --(void) dealloc { object_dispose(self); } +-(void) dealloc { _objc_rootDealloc(self); } +(int) method { return 20; } -+(void) load { state++; } ++(void) load { + 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, ""); + + class_addMethod(object_getClass(self), sel_registerName("retain"), (IMP)noop_fn, ""); + class_addMethod(object_getClass(self), sel_registerName("release"), (IMP)noop_fn, ""); + class_addMethod(object_getClass(self), sel_registerName("autorelease"), (IMP)noop_fn, ""); + + state++; +} @end @implementation NotMissingSuper diff --git a/test/weakcopy.m b/test/weakcopy.m index bbac806..266b455 100644 --- a/test/weakcopy.m +++ b/test/weakcopy.m @@ -1,21 +1,12 @@ -// TEST_CONFIG +// TEST_CONFIG #include "test.h" +#include "testroot.i" #include #include #include -@interface Base { - @public - id isa; -} -@end -@implementation Base -+(void)initialize { } -+class { return self; } -@end - -@interface Weak : Base { +@interface Weak : TestRoot { @public __weak id value; } @@ -23,14 +14,58 @@ @implementation Weak @end +Weak *oldObject; +Weak *newObject; + +void *fn(void *arg __unused) +{ + objc_registerThreadWithCollector(); + + return NULL; +} + int main() { - Base *value = class_createInstance([Base class], 0); - Weak *oldObject = class_createInstance([Weak class], 0); - oldObject->value = value; - Weak *newObject = object_copy(oldObject, 0); - testassert(newObject->value == oldObject->value); - newObject->value = nil; + testonthread(^{ + TestRoot *value; + + PUSH_POOL { + value = [TestRoot new]; + testassert(value); + oldObject = [Weak new]; + testassert(oldObject); + + oldObject->value = value; + testassert(oldObject->value == value); + + newObject = [oldObject copy]; + testassert(newObject); + testassert(newObject->value == oldObject->value); + + newObject->value = nil; + testassert(newObject->value == nil); + testassert(oldObject->value == value); + } POP_POOL; + + testcollect(); + TestRootDealloc = 0; + TestRootFinalize = 0; + RELEASE_VAR(value); + }); + + testcollect(); + testassert(TestRootDealloc || TestRootFinalize); + +#if defined(__OBJC_GC__) || __has_feature(objc_arc) + testassert(oldObject->value == nil); +#else + testassert(oldObject->value != nil); +#endif + testassert(newObject->value == nil); + + RELEASE_VAR(newObject); + RELEASE_VAR(oldObject); + succeed(__FILE__); return 0; } diff --git a/test/xref.m b/test/xref.m index 3843064..6418e3f 100644 --- a/test/xref.m +++ b/test/xref.m @@ -1,6 +1,6 @@ -// TEST_CFLAGS -framework Foundation +// TEST_CFLAGS -#include +#include #include #include @@ -26,7 +26,7 @@ int main() testassert(_object_readExternalReference(xref) == object); _object_removeExternalReference(xref); - [object release]; + RELEASE_VAR(object); succeed(__FILE__); } diff --git a/unexported_symbols b/unexported_symbols index 94edbfd..4a521d7 100644 --- a/unexported_symbols +++ b/unexported_symbols @@ -7,3 +7,6 @@ __ZdaPv __ZdaPvRKSt9nothrow_t __ZdlPv __ZdlPvRKSt9nothrow_t +__ZNSt3__113unordered_mapImPN23objc_references_support20ObjectAssociationMapENS1_20DisguisedPointerHashENS1_21DisguisedPointerEqualENS1_13ObjcAllocatorINS_4pairIKmS3_EEEEEixERS8_ +__ZNSt3__13mapIPvN23objc_references_support15ObjcAssociationENS2_17ObjectPointerLessENS2_13ObjcAllocatorINS_4pairIKS1_S3_EEEEE16__find_equal_keyERPNS_16__tree_node_baseIS1_EERS7_ +__ZNSt3__13mapIPvN23objc_references_support15ObjcAssociationENS2_17ObjectPointerLessENS2_13ObjcAllocatorINS_4pairIKS1_S3_EEEEEixERS7_ -- 2.45.2