#include <stdlib.h>
#include <unistd.h>
#include <string.h>
-#import <stdio.h>
+#include <stdio.h>
+#include <stdbool.h>
#include <fcntl.h>
-#import <sys/stat.h>
-#import <mach-o/fat.h>
-#import <mach-o/arch.h>
-#import <mach-o/loader.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <mach-o/fat.h>
+#include <mach-o/arch.h>
+#include <mach-o/loader.h>
// from "objc-private.h"
// masks for objc_image_info.flags
#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;
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.
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);
}
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);
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) {
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;
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, ); }; };
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, ); }; };
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 */; };
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 */;
};
/* End PBXContainerItemProxy section */
-/* Begin PBXCopyFilesBuildPhase section */
- 83E27999117F76DF00452546 /* CopyFiles */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 2147483647;
- dstPath = /usr/local/include/objc;
- dstSubfolderSpec = 0;
- files = (
- 83E2799D117F76EF00452546 /* objc-selopt.h in CopyFiles */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXCopyFilesBuildPhase section */
-
/* Begin PBXFileReference section */
- 39023FCE12F09D0400E1426D /* libobjc.A.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libobjc.A.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
393CEABF0DC69E3E000B69DE /* objc-references.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-references.mm"; path = "runtime/objc-references.mm"; sourceTree = "<group>"; };
393CEAC50DC69E67000B69DE /* objc-references.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-references.h"; path = "runtime/objc-references.h"; sourceTree = "<group>"; };
- 399BC72D1224831B007FBDF0 /* objc-externalref.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-externalref.m"; path = "runtime/objc-externalref.m"; sourceTree = "<group>"; };
+ 399BC72D1224831B007FBDF0 /* objc-externalref.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-externalref.mm"; path = "runtime/objc-externalref.mm"; sourceTree = "<group>"; };
39ABD71F12F0B61800D1054C /* objc-weak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-weak.h"; path = "runtime/objc-weak.h"; sourceTree = "<group>"; };
39ABD72012F0B61800D1054C /* objc-weak.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-weak.mm"; path = "runtime/objc-weak.mm"; sourceTree = "<group>"; };
- 552B2FB612F9D31E00650C0D /* objc-arr.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-arr.mm"; path = "runtime/objc-arr.mm"; sourceTree = "<group>"; };
830F2A690D737FB800392440 /* objc-msg-arm.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-arm.s"; path = "runtime/Messengers.subproj/objc-msg-arm.s"; sourceTree = "<group>"; };
830F2A6A0D737FB800392440 /* objc-msg-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-i386.s"; path = "runtime/Messengers.subproj/objc-msg-i386.s"; sourceTree = "<group>"; };
830F2A720D737FB800392440 /* objc-msg-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-x86_64.s"; path = "runtime/Messengers.subproj/objc-msg-x86_64.s"; sourceTree = "<group>"; tabWidth = 8; usesTabs = 1; };
830F2A850D73819A00392440 /* objc-auto-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-auto-i386.s"; path = "runtime/Auto.subproj/objc-auto-i386.s"; sourceTree = "<group>"; };
830F2A920D73876100392440 /* objc-accessors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-accessors.h"; path = "runtime/Accessors.subproj/objc-accessors.h"; sourceTree = "<group>"; };
- 830F2A930D73876100392440 /* objc-accessors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-accessors.m"; path = "runtime/Accessors.subproj/objc-accessors.m"; sourceTree = "<group>"; };
+ 830F2A930D73876100392440 /* objc-accessors.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-accessors.mm"; path = "runtime/Accessors.subproj/objc-accessors.mm"; sourceTree = "<group>"; };
830F2A970D738DC200392440 /* hashtable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hashtable.h; path = runtime/hashtable.h; sourceTree = "<group>"; };
830F2AA50D7394C200392440 /* markgc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = markgc.c; sourceTree = "<group>"; };
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 = "<group>"; };
831C85D30E10CF850066E64C /* objc-os.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-os.h"; path = "runtime/objc-os.h"; sourceTree = "<group>"; };
- 831C85D40E10CF850066E64C /* objc-os.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-os.m"; path = "runtime/objc-os.m"; sourceTree = "<group>"; };
+ 831C85D40E10CF850066E64C /* objc-os.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-os.mm"; path = "runtime/objc-os.mm"; sourceTree = "<group>"; };
834266D70E665A8B002E4DA2 /* objc-gdb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-gdb.h"; path = "runtime/objc-gdb.h"; sourceTree = "<group>"; };
834EC0A311614167009B2563 /* objc-abi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-abi.h"; path = "runtime/objc-abi.h"; sourceTree = "<group>"; };
+ 83725F4914CA5BFA0014370E /* objc-opt.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-opt.mm"; path = "runtime/objc-opt.mm"; sourceTree = "<group>"; };
8383A3A1122600E9009290B8 /* a1a2-blocktramps-arm.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a1a2-blocktramps-arm.s"; path = "runtime/a1a2-blocktramps-arm.s"; sourceTree = "<group>"; };
8383A3A2122600E9009290B8 /* a2a3-blocktramps-arm.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a2a3-blocktramps-arm.s"; path = "runtime/a2a3-blocktramps-arm.s"; sourceTree = "<group>"; };
838485B30D6D682B00CEA253 /* libobjc.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = libobjc.order; sourceTree = "<group>"; };
838485B40D6D683300CEA253 /* APPLE_LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = APPLE_LICENSE; sourceTree = "<group>"; };
838485B50D6D683300CEA253 /* ReleaseNotes.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = ReleaseNotes.rtf; sourceTree = "<group>"; };
838485B70D6D687300CEA253 /* hashtable2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hashtable2.h; path = runtime/hashtable2.h; sourceTree = "<group>"; };
- 838485B80D6D687300CEA253 /* hashtable2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = hashtable2.m; path = runtime/hashtable2.m; sourceTree = "<group>"; };
+ 838485B80D6D687300CEA253 /* hashtable2.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = hashtable2.mm; path = runtime/hashtable2.mm; sourceTree = "<group>"; };
838485BB0D6D687300CEA253 /* maptable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = maptable.h; path = runtime/maptable.h; sourceTree = "<group>"; };
- 838485BC0D6D687300CEA253 /* maptable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = maptable.m; path = runtime/maptable.m; sourceTree = "<group>"; };
+ 838485BC0D6D687300CEA253 /* maptable.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = maptable.mm; path = runtime/maptable.mm; sourceTree = "<group>"; };
838485BD0D6D687300CEA253 /* message.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = message.h; path = runtime/message.h; sourceTree = "<group>"; };
838485C80D6D68A200CEA253 /* objc-api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-api.h"; path = "runtime/objc-api.h"; sourceTree = "<group>"; };
838485C90D6D68A200CEA253 /* objc-auto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-auto.h"; path = "runtime/objc-auto.h"; sourceTree = "<group>"; };
838485CA0D6D68A200CEA253 /* objc-auto.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-auto.m"; path = "runtime/objc-auto.m"; sourceTree = "<group>"; };
- 838485CB0D6D68A200CEA253 /* objc-cache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-cache.m"; path = "runtime/objc-cache.m"; sourceTree = "<group>"; };
+ 838485CB0D6D68A200CEA253 /* objc-cache.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-cache.mm"; path = "runtime/objc-cache.mm"; sourceTree = "<group>"; };
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 = "<group>"; };
838485CD0D6D68A200CEA253 /* objc-class.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-class.h"; path = "runtime/objc-class.h"; sourceTree = "<group>"; };
- 838485CE0D6D68A200CEA253 /* objc-class.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-class.m"; path = "runtime/objc-class.m"; sourceTree = "<group>"; };
+ 838485CE0D6D68A200CEA253 /* objc-class.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-class.mm"; path = "runtime/objc-class.mm"; sourceTree = "<group>"; };
838485CF0D6D68A200CEA253 /* objc-config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-config.h"; path = "runtime/objc-config.h"; sourceTree = "<group>"; };
- 838485D00D6D68A200CEA253 /* objc-errors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-errors.m"; path = "runtime/objc-errors.m"; sourceTree = "<group>"; };
+ 838485D00D6D68A200CEA253 /* objc-errors.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-errors.mm"; path = "runtime/objc-errors.mm"; sourceTree = "<group>"; };
838485D10D6D68A200CEA253 /* objc-exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-exception.h"; path = "runtime/objc-exception.h"; sourceTree = "<group>"; };
- 838485D20D6D68A200CEA253 /* objc-exception.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-exception.m"; path = "runtime/objc-exception.m"; sourceTree = "<group>"; };
+ 838485D20D6D68A200CEA253 /* objc-exception.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-exception.mm"; path = "runtime/objc-exception.mm"; sourceTree = "<group>"; };
838485D30D6D68A200CEA253 /* objc-file.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-file.mm"; path = "runtime/objc-file.mm"; sourceTree = "<group>"; };
838485D40D6D68A200CEA253 /* objc-initialize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-initialize.h"; path = "runtime/objc-initialize.h"; sourceTree = "<group>"; };
- 838485D50D6D68A200CEA253 /* objc-initialize.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-initialize.m"; path = "runtime/objc-initialize.m"; sourceTree = "<group>"; };
- 838485D60D6D68A200CEA253 /* objc-layout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-layout.m"; path = "runtime/objc-layout.m"; sourceTree = "<group>"; };
+ 838485D50D6D68A200CEA253 /* objc-initialize.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-initialize.mm"; path = "runtime/objc-initialize.mm"; sourceTree = "<group>"; };
+ 838485D60D6D68A200CEA253 /* objc-layout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-layout.mm"; path = "runtime/objc-layout.mm"; sourceTree = "<group>"; };
838485D70D6D68A200CEA253 /* objc-load.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-load.h"; path = "runtime/objc-load.h"; sourceTree = "<group>"; };
- 838485D80D6D68A200CEA253 /* objc-load.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-load.m"; path = "runtime/objc-load.m"; sourceTree = "<group>"; };
+ 838485D80D6D68A200CEA253 /* objc-load.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-load.mm"; path = "runtime/objc-load.mm"; sourceTree = "<group>"; };
838485D90D6D68A200CEA253 /* objc-loadmethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-loadmethod.h"; path = "runtime/objc-loadmethod.h"; sourceTree = "<group>"; };
- 838485DA0D6D68A200CEA253 /* objc-loadmethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-loadmethod.m"; path = "runtime/objc-loadmethod.m"; sourceTree = "<group>"; };
- 838485DB0D6D68A200CEA253 /* objc-lockdebug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-lockdebug.m"; path = "runtime/objc-lockdebug.m"; sourceTree = "<group>"; };
+ 838485DA0D6D68A200CEA253 /* objc-loadmethod.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-loadmethod.mm"; path = "runtime/objc-loadmethod.mm"; sourceTree = "<group>"; };
+ 838485DB0D6D68A200CEA253 /* objc-lockdebug.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-lockdebug.mm"; path = "runtime/objc-lockdebug.mm"; sourceTree = "<group>"; };
838485DC0D6D68A200CEA253 /* objc-private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-private.h"; path = "runtime/objc-private.h"; sourceTree = "<group>"; };
- 838485DF0D6D68A200CEA253 /* objc-rtp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-rtp.m"; path = "runtime/objc-rtp.m"; sourceTree = "<group>"; };
+ 838485DF0D6D68A200CEA253 /* objc-rtp.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-rtp.mm"; path = "runtime/objc-rtp.mm"; sourceTree = "<group>"; };
838485E00D6D68A200CEA253 /* objc-runtime-new.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-runtime-new.h"; path = "runtime/objc-runtime-new.h"; sourceTree = "<group>"; };
838485E10D6D68A200CEA253 /* objc-runtime-new.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-runtime-new.mm"; path = "runtime/objc-runtime-new.mm"; sourceTree = "<group>"; };
838485E20D6D68A200CEA253 /* objc-runtime-old.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-runtime-old.m"; path = "runtime/objc-runtime-old.m"; sourceTree = "<group>"; };
838485E30D6D68A200CEA253 /* objc-runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-runtime.h"; path = "runtime/objc-runtime.h"; sourceTree = "<group>"; };
- 838485E40D6D68A200CEA253 /* objc-runtime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-runtime.m"; path = "runtime/objc-runtime.m"; sourceTree = "<group>"; };
+ 838485E40D6D68A200CEA253 /* objc-runtime.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-runtime.mm"; path = "runtime/objc-runtime.mm"; sourceTree = "<group>"; };
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 = "<group>"; };
- 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 = "<group>"; };
+ 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 = "<group>"; };
838485E80D6D68A200CEA253 /* objc-sel.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-sel.mm"; path = "runtime/objc-sel.mm"; sourceTree = "<group>"; };
838485E90D6D68A200CEA253 /* objc-sync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-sync.h"; path = "runtime/objc-sync.h"; sourceTree = "<group>"; };
- 838485EA0D6D68A200CEA253 /* objc-sync.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-sync.m"; path = "runtime/objc-sync.m"; sourceTree = "<group>"; };
- 838485EB0D6D68A200CEA253 /* objc-typeencoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-typeencoding.m"; path = "runtime/objc-typeencoding.m"; sourceTree = "<group>"; };
+ 838485EA0D6D68A200CEA253 /* objc-sync.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-sync.mm"; path = "runtime/objc-sync.mm"; sourceTree = "<group>"; };
+ 838485EB0D6D68A200CEA253 /* objc-typeencoding.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-typeencoding.mm"; path = "runtime/objc-typeencoding.mm"; sourceTree = "<group>"; };
838485EC0D6D68A200CEA253 /* objc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = objc.h; path = runtime/objc.h; sourceTree = "<group>"; };
838485ED0D6D68A200CEA253 /* Object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Object.h; path = runtime/Object.h; sourceTree = "<group>"; };
838485EE0D6D68A200CEA253 /* Object.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Object.m; path = runtime/Object.m; sourceTree = "<group>"; };
838486180D6D68A800CEA253 /* Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Protocol.h; path = runtime/Protocol.h; sourceTree = "<group>"; };
- 838486190D6D68A800CEA253 /* Protocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Protocol.m; path = runtime/Protocol.m; sourceTree = "<group>"; };
+ 838486190D6D68A800CEA253 /* Protocol.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Protocol.mm; path = runtime/Protocol.mm; sourceTree = "<group>"; };
8384861A0D6D68A800CEA253 /* runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = runtime.h; path = runtime/runtime.h; sourceTree = "<group>"; };
838486230D6D68F000CEA253 /* List.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = List.m; path = runtime/OldClasses.subproj/List.m; sourceTree = "<group>"; };
838486240D6D68F000CEA253 /* List.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = List.h; path = runtime/OldClasses.subproj/List.h; sourceTree = "<group>"; };
83BE02E50FCCB24D00661494 /* objc-file-old.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-file-old.h"; path = "runtime/objc-file-old.h"; sourceTree = "<group>"; };
83BE02E60FCCB24D00661494 /* objc-file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-file.h"; path = "runtime/objc-file.h"; sourceTree = "<group>"; };
83BE02E70FCCB24D00661494 /* objc-runtime-old.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-runtime-old.h"; path = "runtime/objc-runtime-old.h"; sourceTree = "<group>"; };
- 83CDE7520E2E0040003703C1 /* objc-selopt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-selopt.h"; path = "runtime/objc-selopt.h"; sourceTree = "<group>"; };
83E50D2A0FF19E8200D74C19 /* libobjc.A.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libobjc.A.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
83E50D2B0FF19E9E00D74C19 /* IndigoSDK.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = IndigoSDK.xcconfig; path = AppleInternal/XcodeConfig/IndigoSDK.xcconfig; sourceTree = DEVELOPER_DIR; };
83EB007A121C9EC200B92C16 /* objc-sel-table.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-sel-table.s"; path = "runtime/objc-sel-table.s"; sourceTree = "<group>"; };
87BB4E900EC39633005D08E1 /* objc-probes.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; name = "objc-probes.d"; path = "runtime/objc-probes.d"; sourceTree = "<group>"; };
+ 9672F7ED14D5F488007CEC96 /* NSObject.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = NSObject.mm; path = runtime/NSObject.mm; sourceTree = "<group>"; };
BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-auto-dump.h"; path = "runtime/objc-auto-dump.h"; sourceTree = "<group>"; };
BC07A0100EF72D9C0014EC61 /* objc-auto-dump.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-auto-dump.m"; path = "runtime/objc-auto-dump.m"; sourceTree = "<group>"; };
BC8B5D1212D3D48100C78A5B /* libauto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libauto.dylib; path = /usr/lib/libauto.dylib; sourceTree = "<absolute>"; };
E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a1a2-blocktramps-x86_64.s"; path = "runtime/a1a2-blocktramps-x86_64.s"; sourceTree = "<group>"; };
E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a2a3-blocktramps-i386.s"; path = "runtime/a2a3-blocktramps-i386.s"; sourceTree = "<group>"; };
E8923D9F116AB2820071B552 /* a2a3-blocktramps-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a2a3-blocktramps-x86_64.s"; path = "runtime/a2a3-blocktramps-x86_64.s"; sourceTree = "<group>"; };
- E8923DA0116AB2820071B552 /* objc-block-trampolines.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "objc-block-trampolines.m"; path = "runtime/objc-block-trampolines.m"; sourceTree = "<group>"; };
+ 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 = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
- 39023FC712F09D0400E1426D /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
830F2AA70D7394D000392440 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
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 */,
D2AAC0630554660B00DB518D /* libobjc.A.dylib */,
830F2AA90D7394D000392440 /* markgc */,
83E50D2A0FF19E8200D74C19 /* libobjc.A.dylib */,
- 39023FCE12F09D0400E1426D /* libobjc.A.dylib */,
);
name = Products;
sourceTree = "<group>";
838485BB0D6D687300CEA253 /* maptable.h */,
BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */,
834266D70E665A8B002E4DA2 /* objc-gdb.h */,
- 83CDE7520E2E0040003703C1 /* objc-selopt.h */,
);
name = "Private Headers";
sourceTree = "<group>";
children = (
838486230D6D68F000CEA253 /* List.m */,
838485EE0D6D68A200CEA253 /* Object.m */,
- 838486190D6D68A800CEA253 /* Protocol.m */,
);
name = "Obsolete Source";
sourceTree = "<group>";
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
- 39023F7612F09D0400E1426D /* Headers */ = {
- isa = PBXHeadersBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 39023F7712F09D0400E1426D /* hashtable.h in Headers */,
- 39023F7812F09D0400E1426D /* hashtable2.h in Headers */,
- 39023F7912F09D0400E1426D /* List.h in Headers */,
- 39023F7A12F09D0400E1426D /* maptable.h in Headers */,
- 39023F7B12F09D0400E1426D /* message.h in Headers */,
- 39023F7C12F09D0400E1426D /* objc-abi.h in Headers */,
- 39023F7D12F09D0400E1426D /* objc-accessors.h in Headers */,
- 39023F7E12F09D0400E1426D /* objc-api.h in Headers */,
- 39023F7F12F09D0400E1426D /* objc-auto-dump.h in Headers */,
- 39023F8012F09D0400E1426D /* objc-auto.h in Headers */,
- 39023F8112F09D0400E1426D /* objc-class.h in Headers */,
- 39023F8212F09D0400E1426D /* objc-config.h in Headers */,
- 39023F8312F09D0400E1426D /* objc-exception.h in Headers */,
- 39023F8412F09D0400E1426D /* objc-file-old.h in Headers */,
- 39023F8512F09D0400E1426D /* objc-file.h in Headers */,
- 39023F8612F09D0400E1426D /* objc-gdb.h in Headers */,
- 39023F8712F09D0400E1426D /* objc-initialize.h in Headers */,
- 39023F8812F09D0400E1426D /* objc-internal.h in Headers */,
- 39023F8912F09D0400E1426D /* objc-load.h in Headers */,
- 39023F8A12F09D0400E1426D /* objc-loadmethod.h in Headers */,
- 39023F8B12F09D0400E1426D /* objc-os.h in Headers */,
- 39023F8C12F09D0400E1426D /* objc-private.h in Headers */,
- 39023F8D12F09D0400E1426D /* objc-references.h in Headers */,
- 39023F8E12F09D0400E1426D /* objc-runtime-new.h in Headers */,
- 39023F8F12F09D0400E1426D /* objc-runtime-old.h in Headers */,
- 39023F9012F09D0400E1426D /* objc-runtime.h in Headers */,
- 39023F9112F09D0400E1426D /* objc-sel-set.h in Headers */,
- 39023F9212F09D0400E1426D /* objc-sync.h in Headers */,
- 39023F9312F09D0400E1426D /* objc.h in Headers */,
- 39023F9412F09D0400E1426D /* Object.h in Headers */,
- 39023F9512F09D0400E1426D /* Protocol.h in Headers */,
- 39023F9612F09D0400E1426D /* runtime.h in Headers */,
- 39ABD72112F0B61800D1054C /* objc-weak.h in Headers */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
83E50CDA0FF19E8200D74C19 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
/* 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" */;
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 = (
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;
/* 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;
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 */,
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;
};
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 */,
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 */;
);
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;
);
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;
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;
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 = {
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;
};
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;
};
"-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;
};
"-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;
};
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- 39023FCB12F09D0400E1426D /* Build configuration list for PBXNativeTarget "objc-device" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 39023FCC12F09D0400E1426D /* Debug */,
- 39023FCD12F09D0400E1426D /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
830F2AAE0D7394D600392440 /* Build configuration list for PBXNativeTarget "markgc" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- 83E2799E117F770D00452546 /* Build configuration list for PBXAggregateTarget "objc-selopt" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 83E2799B117F76E000452546 /* Debug */,
- 83E2799C117F76E000452546 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
83E50D270FF19E8200D74C19 /* Build configuration list for PBXNativeTarget "objc-simulator" */ = {
isa = XCConfigurationList;
buildConfigurations = (
#ifndef _OBJC_ACCESSORS_H_
#define _OBJC_ACCESSORS_H_
-#import <objc/objc.h>
-#import <stddef.h>
+#include <objc/objc.h>
+#include <stddef.h>
__BEGIN_DECLS
-// Called under non-GC for retain or copy attributed properties
-void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy);
-id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic);
-
// GC-specific accessors.
-void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy);
-id objc_getProperty_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic);
+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 <NSCopying> object_getProperty_bycopy(id object, SEL _cmd, ptrdiff_t offset);
-void object_setProperty_bycopy(id object, SEL _cmd, id <NSCopying> 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
+++ /dev/null
-/*
- * Copyright (c) 2006-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@
- */
-
-#import <string.h>
-#import <stddef.h>
-
-#import <libkern/OSAtomic.h>
-
-#import "objc-private.h"
-#import "objc-auto.h"
-#import "runtime.h"
-#import "objc-accessors.h"
-
-// stub interface declarations to make compiler happy.
-
-@interface __NSCopyable
-- (id)copyWithZone:(void *)zone;
-@end
-
-@interface __NSMutableCopyable
-- (id)mutableCopyWithZone:(void *)zone;
-@end
-
-
-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);
-
-/* need to consider cache line contention - space locks out XXX */
-
-#define GOODPOWER 7
-#define GOODMASK ((1<<GOODPOWER)-1)
-#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) {
- return *(id*) ((char*)self + offset);
-}
-
-PRIVATE_EXTERN id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
- // Retain release world
- id *slot = (id*) ((char*)self + offset);
- if (!atomic) return *slot;
-
- // Atomic retain release world
- spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
- _spin_lock(slotlock);
- id value = objc_retain(*slot);
- _spin_unlock(slotlock);
-
- // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
- return objc_autoreleaseReturnValue(value);
-}
-
-id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
- return
-#if SUPPORT_GC
- (UseGC ? objc_getProperty_gc : objc_getProperty_non_gc)
-#else
- objc_getProperty_non_gc
-#endif
- (self, _cmd, offset, atomic);
-}
-
-enum { OBJC_PROPERTY_RETAIN = 0, OBJC_PROPERTY_COPY = 1, OBJC_PROPERTY_MUTABLECOPY = 2 };
-
-#if SUPPORT_GC
-PRIVATE_EXTERN void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) {
- if (shouldCopy) {
- newValue = (shouldCopy == OBJC_PROPERTY_MUTABLECOPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]);
- }
- objc_assign_ivar_gc(newValue, self, offset);
-}
-#endif
-
-PRIVATE_EXTERN void objc_setProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) {
- // Retain release world
- id oldValue, *slot = (id*) ((char*)self + offset);
-
- // 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]);
- } else {
- newValue = objc_retain(newValue);
- }
-
- if (!atomic) {
- oldValue = *slot;
- *slot = newValue;
- } else {
- spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
- _spin_lock(slotlock);
- oldValue = *slot;
- *slot = newValue;
- _spin_unlock(slotlock);
- }
-
- objc_release(oldValue);
-}
-
-void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, BOOL shouldCopy) {
-#if SUPPORT_GC
- (UseGC ? objc_setProperty_gc : objc_setProperty_non_gc)
-#else
- objc_setProperty_non_gc
-#endif
- (self, _cmd, offset, newValue, atomic, shouldCopy);
-}
-
-
-// This entry point was designed wrong. When used as a getter, src needs to be locked so that
-// if simultaneously used for a setter then there would be contention on src.
-// So we need two locks - one of which will be contended.
-void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong) {
- static spin_lock_t StructLocks[1 << GOODPOWER] = { 0 };
- spin_lock_t *lockfirst = NULL;
- spin_lock_t *locksecond = NULL;
- if (atomic) {
- lockfirst = &StructLocks[GOODHASH(src)];
- locksecond = &StructLocks[GOODHASH(dest)];
- // order the locks by address so that we don't deadlock
- if (lockfirst > locksecond) {
- lockfirst = locksecond;
- locksecond = &StructLocks[GOODHASH(src)];
- }
- else if (lockfirst == locksecond) {
- // lucky - we only need one lock
- locksecond = NULL;
- }
- _spin_lock(lockfirst);
- if (locksecond) _spin_lock(locksecond);
- }
-#if SUPPORT_GC
- if (UseGC && hasStrong) {
- auto_zone_write_barrier_memmove(gc_zone, dest, src, size);
- } else
-#endif
- {
- memmove(dest, src, size);
- }
- if (atomic) {
- _spin_unlock(lockfirst);
- if (locksecond) _spin_unlock(locksecond);
- }
-}
-
--- /dev/null
+/*
+ * Copyright (c) 2006-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@
+ */
+
+#include <string.h>
+#include <stddef.h>
+
+#include <libkern/OSAtomic.h>
+
+#include "objc-private.h"
+#include "objc-auto.h"
+#include "runtime.h"
+#include "objc-accessors.h"
+
+// stub interface declarations to make compiler happy.
+
+@interface __NSCopyable
+- (id)copyWithZone:(void *)zone;
+@end
+
+@interface __NSMutableCopyable
+- (id)mutableCopyWithZone:(void *)zone;
+@end
+
+
+typedef uintptr_t spin_lock_t;
+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 */
+
+#define GOODPOWER 7
+#define GOODMASK ((1<<GOODPOWER)-1)
+#define GOODHASH(x) (((long)x >> 5) & GOODMASK)
+static spin_lock_t PropertyLocks[1 << GOODPOWER] = { 0 };
+
+#define MUTABLE_COPY 2
+
+id objc_getProperty_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
+ return *(id*) ((char*)self + offset);
+}
+
+id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
+ // Retain release world
+ id *slot = (id*) ((char*)self + offset);
+ if (!atomic) return *slot;
+
+ // Atomic retain release world
+ spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
+ _spin_lock(slotlock);
+ id value = objc_retain(*slot);
+ _spin_unlock(slotlock);
+
+ // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
+ return objc_autoreleaseReturnValue(value);
+}
+
+id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
+ return
+#if SUPPORT_GC
+ (UseGC ? objc_getProperty_gc : objc_getProperty_non_gc)
+#else
+ objc_getProperty_non_gc
+#endif
+ (self, _cmd, offset, atomic);
+}
+
+#if SUPPORT_GC
+void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) {
+ if (shouldCopy) {
+ newValue = (shouldCopy == MUTABLE_COPY ? [newValue mutableCopyWithZone:NULL] : [newValue copyWithZone:NULL]);
+ }
+ objc_assign_ivar_gc(newValue, self, offset);
+}
+#endif
+
+static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) __attribute__((always_inline));
+
+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);
+ }
+
+ if (!atomic) {
+ oldValue = *slot;
+ *slot = newValue;
+ } else {
+ spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
+ _spin_lock(slotlock);
+ oldValue = *slot;
+ *slot = newValue;
+ _spin_unlock(slotlock);
+ }
+
+ objc_release(oldValue);
+}
+
+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
+ objc_setProperty_non_gc
+#endif
+ (self, _cmd, offset, newValue, atomic, shouldCopy);
+}
+
+
+// This entry point was designed wrong. When used as a getter, src needs to be locked so that
+// if simultaneously used for a setter then there would be contention on src.
+// So we need two locks - one of which will be contended.
+void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong) {
+ static spin_lock_t StructLocks[1 << GOODPOWER] = { 0 };
+ spin_lock_t *lockfirst = NULL;
+ spin_lock_t *locksecond = NULL;
+ if (atomic) {
+ lockfirst = &StructLocks[GOODHASH(src)];
+ locksecond = &StructLocks[GOODHASH(dest)];
+ // order the locks by address so that we don't deadlock
+ if (lockfirst > locksecond) {
+ lockfirst = locksecond;
+ locksecond = &StructLocks[GOODHASH(src)];
+ }
+ else if (lockfirst == locksecond) {
+ // lucky - we only need one lock
+ locksecond = NULL;
+ }
+ _spin_lock(lockfirst);
+ if (locksecond) _spin_lock(locksecond);
+ }
+#if SUPPORT_GC
+ if (UseGC && hasStrong) {
+ auto_zone_write_barrier_memmove(gc_zone, dest, src, size);
+ } else
+#endif
+ {
+ memmove(dest, src, size);
+ }
+ if (atomic) {
+ _spin_unlock(lockfirst);
+ if (locksecond) _spin_unlock(locksecond);
+ }
+}
+
+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);
+}
#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__)
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
#endif
-MI_EXTERN(__class_lookupMethodAndLoadCache)
+MI_EXTERN(__class_lookupMethodAndLoadCache3)
MI_EXTERN(_FwdSel)
MI_EXTERN(___objc_error)
MI_EXTERN(__objc_forward_handler)
#ifdef THUMB
.thumb
#endif
- .align 2
+ .align 5
.globl _$0
#ifdef THUMB
.thumb_func
#ifdef THUMB
.thumb
#endif
- .align 2
+ .align 5
.private_extern _$0
#ifdef THUMB
.thumb_func
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}
ldmfd sp!, {a4,v1}
b _objc_msgSend_uncached
+LMsgSendNilReceiver:
+ mov a2, #0
+ bx lr
+
LMsgSendExit:
END_ENTRY objc_msgSend
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
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]
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
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
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
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
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
.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
.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
.endif
.endif
+ // edx = receiver
+ // ecx = selector
+ // eax = class
jmp $2 // go to callers handler
// eax points to matching cache entry
// 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
.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
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
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
.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
.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
.endif
.endif
+ // edx = receiver
+ // ecx = selector
+ // eax = class
jmp $2 // go to callers handler
// eax points to matching cache entry
// 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
MISS:\r
pop esi\r
pop edi\r
- mov eax, SELF\r
- mov eax, isa[eax]\r
+ mov edx, SELF\r
+ mov eax, isa[edx]\r
\r
// MethodTableLookup WORD_RETURN, MSG_SEND\r
- push ecx\r
push eax\r
- call _class_lookupMethodAndLoadCache\r
+ push ecx\r
+ push edx\r
+ call _class_lookupMethodAndLoadCache3\r
\r
mov edx, kFwdMsgSend\r
leave\r
MISS:\r
pop esi\r
pop edi\r
- mov eax, SELF\r
- mov eax, isa[eax]\r
+ mov edx, SELF\r
+ mov eax, isa[edx]\r
\r
// MethodTableLookup WORD_RETURN, MSG_SEND\r
- push ecx\r
push eax\r
- call _class_lookupMethodAndLoadCache\r
+ push ecx\r
+ push edx\r
+ call _class_lookupMethodAndLoadCache3\r
\r
mov edx, kFwdMsgSend\r
leave\r
\r
pop esi\r
pop edi\r
- mov edx, SUPER\r
- mov eax, super_receiver[edx]\r
- mov SUPER, eax\r
- mov eax, super_class[edx]\r
+ mov eax, SUPER\r
+ mov edx, super_receiver[eax]\r
+ mov SUPER, edx\r
+ mov eax, super_class[eax]\r
\r
// MethodTableLookup WORD_RETURN, MSG_SENDSUPER\r
- push ecx\r
push eax\r
- call _class_lookupMethodAndLoadCache\r
+ push ecx\r
+ push edx\r
+ call _class_lookupMethodAndLoadCache3\r
\r
mov edx, kFwdMsgSend\r
leave\r
MISS:\r
pop esi\r
pop edi\r
- mov eax, SELF_STRET\r
- mov eax, isa[eax]\r
+ mov edx, SELF_STRET\r
+ mov eax, isa[edx]\r
\r
// MethodTableLookup WORD_RETURN, MSG_SEND\r
- push ecx\r
push eax\r
- call _class_lookupMethodAndLoadCache\r
+ push ecx\r
+ push edx\r
+ call _class_lookupMethodAndLoadCache3\r
\r
mov edx, kFwdMsgSendStret\r
leave\r
\r
pop esi\r
pop edi\r
- mov edx, SUPER_STRET\r
- mov eax, super_receiver[edx]\r
- mov SUPER_STRET, eax\r
- mov eax, super_class[edx]\r
+ mov eax, SUPER_STRET\r
+ mov edx, super_receiver[eax]\r
+ mov SUPER_STRET, edx\r
+ mov eax, super_class[eax]\r
\r
// MethodTableLookup WORD_RETURN, MSG_SENDSUPER\r
- push ecx\r
push eax\r
- call _class_lookupMethodAndLoadCache\r
+ push ecx\r
+ push edx\r
+ call _class_lookupMethodAndLoadCache3\r
\r
mov edx, kFwdMsgSendStret\r
leave\r
.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
.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
.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
/////////////////////////////////////////////////////////////////////
.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)
movdqa %xmm6, -0x20(%rbp)
push %a6
movdqa %xmm7, -0x10(%rbp)
+
+ // These instructions must match the DWARF data in EMIT_FDE.
.endmacro
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
.macro RestoreRegisters
+ // These instructions must match the DWARF data in EMIT_FDE.
+
movdqa -0x80(%rbp), %xmm0
pop %a6
movdqa -0x70(%rbp), %xmm1
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
//
.macro CacheLookup
push %rax
+ DW_PUSH $1
+
movq cache(%r11), %r10 // cache = class->cache
.if $0 != STRET
mov %a2d, %eax // index = sel
// cache hit, r11 = method triplet
// restore saved registers
pop %rax
+ DW_POP $1
.if $0 != STRET
// eq (non-stret) flag already set above
/////////////////////////////////////////////////////////////////////
//
-// 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)
.macro MethodTableLookup
pop %rax // saved by CacheLookup
+ DW_MISS_POP $2
+
SaveRegisters $2
// _class_lookupMethodAndLoadCache3(receiver, selector, class)
// 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)
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
// 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
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
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
// 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__
NilTestSupport NORMAL
- DW_END _objc_msgSend_fixup
+ DW_END _objc_msgSend_fixup, 0, 1
END_ENTRY _objc_msgSend_fixup
// 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
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
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
// 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
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
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
// 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__
NilTestSupport FPRET
- DW_END _objc_msgSend_fpret_fixup
+ DW_END _objc_msgSend_fpret_fixup, 0, 1
END_ENTRY _objc_msgSend_fpret_fixup
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
// 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__
NilTestSupport FP2RET
- DW_END _objc_msgSend_fp2ret_fixup
+ DW_END _objc_msgSend_fp2ret_fixup, 0, 1
END_ENTRY _objc_msgSend_fp2ret_fixup
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
// 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__
NilTestSupport STRET
- DW_END _objc_msgSend_stret_fixup
+ DW_END _objc_msgSend_stret_fixup, 0, 1
END_ENTRY _objc_msgSend_stret_fixup
// 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
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
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
// 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
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
.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
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
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
--- /dev/null
+/*
+ * 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,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "objc-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"
+
+#include <malloc/malloc.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <mach/mach.h>
+#include <mach-o/dyld.h>
+#include <mach-o/nlist.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <libkern/OSAtomic.h>
+#include <Block.h>
+#include <map>
+#include <execinfo.h>
+
+@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 <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))
+static bool callerAcceptsFastAutorelease(const void * const ra0);
+#endif
+
+
+/***********************************************************************
+* Weak ivar support
+**********************************************************************/
+
+static bool seen_weak_refs;
+
+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
+
+#if ARR_LOGGING
+struct {
+ int retains;
+ int releases;
+ int autoreleases;
+ int blockCopies;
+} CompilerGenerated, ExplicitlyCoded;
+
+void (^objc_arr_log)(const char *, id param) =
+ ^(const char *str, id param) { printf("%s %p\n", str, param); };
+#endif
+
+
+namespace {
+
+#if TARGET_OS_EMBEDDED
+# define SIDE_TABLE_STRIPE 1
+#else
+# define SIDE_TABLE_STRIPE 8
+#endif
+
+// should be a multiple of cache line size (64)
+#define SIDE_TABLE_SIZE 64
+
+typedef objc::DenseMap<id,size_t,true> RefcountMap;
+
+class SideTable {
+private:
+ static uint8_t table_buf[SIDE_TABLE_STRIPE * SIDE_TABLE_SIZE];
+
+public:
+ OSSpinLock slock;
+ RefcountMap refcnts;
+ weak_table_t weak_table;
+
+ SideTable() : slock(OS_SPINLOCK_INIT)
+ {
+ memset(&weak_table, 0, sizeof(weak_table));
+ }
+
+ ~SideTable()
+ {
+ // never delete side_table in case other threads retain during exit
+ assert(0);
+ }
+
+ static SideTable *tableForPointer(const void *p)
+ {
+# if SIDE_TABLE_STRIPE == 1
+ return (SideTable *)table_buf;
+# else
+ uintptr_t a = (uintptr_t)p;
+ int index = ((a >> 4) ^ (a >> 9)) & (SIDE_TABLE_STRIPE - 1);
+ return (SideTable *)&table_buf[index * SIDE_TABLE_SIZE];
+# endif
+ }
+
+ static void init() {
+ // use placement new instead of static ctor to avoid dtor at exit
+ for (int i = 0; i < SIDE_TABLE_STRIPE; i++) {
+ new (&table_buf[i * SIDE_TABLE_SIZE]) SideTable;
+ }
+ }
+
+ static bool noLocksHeld(void) {
+ bool gotAll = true;
+ for (int i = 0; i < SIDE_TABLE_STRIPE && gotAll; i++) {
+ SideTable *s = (SideTable *)(&table_buf[i * SIDE_TABLE_SIZE]);
+ if (OSSpinLockTry(&s->slock)) {
+ OSSpinLockUnlock(&s->slock);
+ } else {
+ gotAll = false;
+ }
+ }
+ return gotAll;
+ }
+};
+
+STATIC_ASSERT(sizeof(SideTable) <= SIDE_TABLE_SIZE);
+__attribute__((aligned(SIDE_TABLE_SIZE))) uint8_t
+SideTable::table_buf[SIDE_TABLE_STRIPE * SIDE_TABLE_SIZE];
+
+// Avoid false-negative reports from tools like "leaks"
+#define DISGUISE(x) ((id)~(uintptr_t)(x))
+
+// anonymous namespace
+};
+
+bool noSideTableLocksHeld(void)
+{
+ return SideTable::noLocksHeld();
+}
+
+//
+// The -fobjc-arc flag causes the compiler to issue calls to objc_{retain/release/autorelease/retain_block}
+//
+
+id objc_retainBlock(id x) {
+#if ARR_LOGGING
+ objc_arr_log("objc_retain_block", x);
+ ++CompilerGenerated.blockCopies;
+#endif
+ return (id)_Block_copy(x);
+}
+
+//
+// The following SHOULD be called by the compiler directly, but the request hasn't been made yet :-)
+//
+
+BOOL objc_should_deallocate(id object) {
+ return YES;
+}
+
+id
+objc_retain_autorelease(id obj)
+{
+ return objc_autorelease(objc_retain(obj));
+}
+
+id
+objc_storeWeak(id *location, id newObj)
+{
+ id oldObj;
+ SideTable *oldTable;
+ SideTable *newTable;
+ OSSpinLock *lock1;
+#if SIDE_TABLE_STRIPE > 1
+ OSSpinLock *lock2;
+#endif
+
+ if (!seen_weak_refs) {
+ seen_weak_refs = true;
+ }
+
+ // Acquire locks for old and new values.
+ // Order by lock address to prevent lock ordering problems.
+ // Retry if the old value changes underneath us.
+ retry:
+ oldObj = *location;
+
+ oldTable = SideTable::tableForPointer(oldObj);
+ newTable = SideTable::tableForPointer(newObj);
+
+ lock1 = &newTable->slock;
+#if SIDE_TABLE_STRIPE > 1
+ lock2 = &oldTable->slock;
+ if (lock1 > lock2) {
+ OSSpinLock *temp = lock1;
+ lock1 = lock2;
+ lock2 = temp;
+ }
+ if (lock1 != lock2) OSSpinLockLock(lock2);
+#endif
+ OSSpinLockLock(lock1);
+
+ if (*location != oldObj) {
+ OSSpinLockUnlock(lock1);
+#if SIDE_TABLE_STRIPE > 1
+ if (lock1 != lock2) OSSpinLockUnlock(lock2);
+#endif
+ goto retry;
+ }
+
+ if (oldObj) {
+ weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
+ }
+ if (newObj) {
+ newObj = weak_register_no_lock(&newTable->weak_table, newObj,location);
+ // weak_register_no_lock returns NULL if weak store should be rejected
+ }
+ // Do not set *location anywhere else. That would introduce a race.
+ *location = newObj;
+
+ OSSpinLockUnlock(lock1);
+#if SIDE_TABLE_STRIPE > 1
+ if (lock1 != lock2) OSSpinLockUnlock(lock2);
+#endif
+
+ return newObj;
+}
+
+id
+objc_loadWeakRetained(id *location)
+{
+ id result;
+
+ SideTable *table;
+ OSSpinLock *lock;
+
+ retry:
+ result = *location;
+ if (!result) return NULL;
+
+ table = SideTable::tableForPointer(result);
+ lock = &table->slock;
+
+ OSSpinLockLock(lock);
+ if (*location != result) {
+ OSSpinLockUnlock(lock);
+ goto retry;
+ }
+
+ result = arr_read_weak_reference(&table->weak_table, location);
+
+ OSSpinLockUnlock(lock);
+ return result;
+}
+
+id
+objc_loadWeak(id *location)
+{
+ return objc_autorelease(objc_loadWeakRetained(location));
+}
+
+id
+objc_initWeak(id *addr, id val)
+{
+ *addr = 0;
+ return objc_storeWeak(addr, val);
+}
+
+void
+objc_destroyWeak(id *addr)
+{
+ objc_storeWeak(addr, 0);
+}
+
+void
+objc_copyWeak(id *to, id *from)
+{
+ id val = objc_loadWeakRetained(from);
+ objc_initWeak(to, val);
+ objc_release(val);
+}
+
+void
+objc_moveWeak(id *to, id *from)
+{
+ objc_copyWeak(to, from);
+ objc_destroyWeak(from);
+}
+
+
+/* Autorelease pool implementation
+ A thread's autorelease pool is a stack of pointers.
+ Each pointer is either an object to release, or POOL_SENTINEL which is
+ an autorelease pool boundary.
+ A pool token is a pointer to the POOL_SENTINEL for that pool. When
+ the pool is popped, every object hotter than the sentinel is released.
+ The stack is divided into a doubly-linked list of pages. Pages are added
+ and deleted as necessary.
+ Thread-local storage points to the hot page, where newly autoreleased
+ objects are stored.
+ */
+
+extern "C" BREAKPOINT_FUNCTION(void objc_autoreleaseNoPool(id obj));
+
+namespace {
+
+struct magic_t {
+ static const uint32_t M0 = 0xA1A1A1A1;
+# define M1 "AUTORELEASE!"
+ static const size_t M1_len = 12;
+ uint32_t m[4];
+
+ magic_t() {
+ assert(M1_len == strlen(M1));
+ assert(M1_len == 3 * sizeof(m[1]));
+
+ m[0] = M0;
+ strncpy((char *)&m[1], M1, M1_len);
+ }
+
+ ~magic_t() {
+ m[0] = m[1] = m[2] = m[3] = 0;
+ }
+
+ bool check() const {
+ return (m[0] == M0 && 0 == strncmp((char *)&m[1], M1, M1_len));
+ }
+
+ bool fastcheck() const {
+#ifdef NDEBUG
+ return (m[0] == M0);
+#else
+ return check();
+#endif
+ }
+
+# undef M1
+};
+
+
+// Set this to 1 to mprotect() autorelease pool contents
+#define PROTECT_AUTORELEASEPOOL 0
+
+class AutoreleasePoolPage
+{
+
+#define POOL_SENTINEL 0
+ static pthread_key_t const key = AUTORELEASE_POOL_KEY;
+ static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
+ static size_t const SIZE =
+#if PROTECT_AUTORELEASEPOOL
+ 4096; // must be multiple of vm page size
+#else
+ 4096; // size and alignment, power of 2
+#endif
+ static size_t const COUNT = SIZE / sizeof(id);
+
+ magic_t const magic;
+ id *next;
+ pthread_t const thread;
+ AutoreleasePoolPage * const parent;
+ AutoreleasePoolPage *child;
+ uint32_t const depth;
+ uint32_t hiwat;
+
+ // SIZE-sizeof(*this) bytes of contents follow
+
+ static void * operator new(size_t size) {
+ return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
+ }
+ static void operator delete(void * p) {
+ return free(p);
+ }
+
+ inline void protect() {
+#if PROTECT_AUTORELEASEPOOL
+ mprotect(this, SIZE, PROT_READ);
+ check();
+#endif
+ }
+
+ inline void unprotect() {
+#if PROTECT_AUTORELEASEPOOL
+ check();
+ mprotect(this, SIZE, PROT_READ | PROT_WRITE);
+#endif
+ }
+
+ AutoreleasePoolPage(AutoreleasePoolPage *newParent)
+ : magic(), next(begin()), thread(pthread_self()),
+ parent(newParent), child(NULL),
+ depth(parent ? 1+parent->depth : 0),
+ hiwat(parent ? parent->hiwat : 0)
+ {
+ if (parent) {
+ parent->check();
+ assert(!parent->child);
+ parent->unprotect();
+ parent->child = this;
+ parent->protect();
+ }
+ protect();
+ }
+
+ ~AutoreleasePoolPage()
+ {
+ check();
+ unprotect();
+ assert(empty());
+
+ // Not recursive: we don't want to blow out the stack
+ // if a thread accumulates a stupendous amount of garbage
+ assert(!child);
+ }
+
+
+ void busted(bool die = true)
+ {
+ (die ? _objc_fatal : _objc_inform)
+ ("autorelease pool page %p corrupted\n"
+ " magic 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);
+ }
+
+ void check(bool die = true)
+ {
+ if (!magic.check() || !pthread_equal(thread, pthread_self())) {
+ busted(die);
+ }
+ }
+
+ void fastcheck(bool die = true)
+ {
+ if (! magic.fastcheck()) {
+ busted(die);
+ }
+ }
+
+
+ id * begin() {
+ return (id *) ((uint8_t *)this+sizeof(*this));
+ }
+
+ id * end() {
+ return (id *) ((uint8_t *)this+SIZE);
+ }
+
+ bool empty() {
+ return next == begin();
+ }
+
+ bool full() {
+ return next == end();
+ }
+
+ bool lessThanHalfFull() {
+ return (next - begin() < (end() - begin()) / 2);
+ }
+
+ id *add(id obj)
+ {
+ assert(!full());
+ unprotect();
+ *next++ = obj;
+ protect();
+ return next-1;
+ }
+
+ void releaseAll()
+ {
+ releaseUntil(begin());
+ }
+
+ void releaseUntil(id *stop)
+ {
+ // Not recursive: we don't want to blow out the stack
+ // if a thread accumulates a stupendous amount of garbage
+
+ while (this->next != stop) {
+ // Restart from hotPage() every time, in case -release
+ // autoreleased more objects
+ AutoreleasePoolPage *page = hotPage();
+
+ // fixme I think this `while` can be `if`, but I can't prove it
+ while (page->empty()) {
+ page = page->parent;
+ setHotPage(page);
+ }
+
+ page->unprotect();
+ id obj = *--page->next;
+ memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
+ page->protect();
+
+ if (obj != POOL_SENTINEL) {
+ objc_release(obj);
+ }
+ }
+
+ setHotPage(this);
+
+#ifndef NDEBUG
+ // we expect any children to be completely empty
+ for (AutoreleasePoolPage *page = child; page; page = page->child) {
+ assert(page->empty());
+ }
+#endif
+ }
+
+ void kill()
+ {
+ // Not recursive: we don't want to blow out the stack
+ // if a thread accumulates a stupendous amount of garbage
+ AutoreleasePoolPage *page = this;
+ while (page->child) page = page->child;
+
+ AutoreleasePoolPage *deathptr;
+ do {
+ deathptr = page;
+ page = page->parent;
+ if (page) {
+ page->unprotect();
+ page->child = NULL;
+ page->protect();
+ }
+ delete deathptr;
+ } while (deathptr != this);
+ }
+
+ static void tls_dealloc(void *p)
+ {
+ // reinstate TLS value while we work
+ setHotPage((AutoreleasePoolPage *)p);
+ pop(0);
+ setHotPage(NULL);
+ }
+
+ static AutoreleasePoolPage *pageForPointer(const void *p)
+ {
+ return pageForPointer((uintptr_t)p);
+ }
+
+ static AutoreleasePoolPage *pageForPointer(uintptr_t p)
+ {
+ AutoreleasePoolPage *result;
+ uintptr_t offset = p % SIZE;
+
+ assert(offset >= sizeof(AutoreleasePoolPage));
+
+ result = (AutoreleasePoolPage *)(p - offset);
+ result->fastcheck();
+
+ return result;
+ }
+
+
+ static inline AutoreleasePoolPage *hotPage()
+ {
+ AutoreleasePoolPage *result = (AutoreleasePoolPage *)
+ tls_get_direct(key);
+ if (result) result->fastcheck();
+ return result;
+ }
+
+ static inline void setHotPage(AutoreleasePoolPage *page)
+ {
+ if (page) page->fastcheck();
+ tls_set_direct(key, (void *)page);
+ }
+
+ static inline AutoreleasePoolPage *coldPage()
+ {
+ AutoreleasePoolPage *result = hotPage();
+ if (result) {
+ while (result->parent) {
+ result = result->parent;
+ result->fastcheck();
+ }
+ }
+ return result;
+ }
+
+
+ static inline id *autoreleaseFast(id obj)
+ {
+ AutoreleasePoolPage *page = hotPage();
+ if (page && !page->full()) {
+ return page->add(obj);
+ } else {
+ return autoreleaseSlow(obj);
+ }
+ }
+
+ static __attribute__((noinline))
+ id *autoreleaseSlow(id obj)
+ {
+ AutoreleasePoolPage *page;
+ page = hotPage();
+
+ // The code below assumes some cases are handled by autoreleaseFast()
+ assert(!page || page->full());
+
+ if (!page) {
+ assert(obj != POOL_SENTINEL);
+ _objc_inform("Object %p of class %s autoreleased "
+ "with no pool in place - just leaking - "
+ "break on objc_autoreleaseNoPool() to debug",
+ obj, object_getClassName(obj));
+ objc_autoreleaseNoPool(obj);
+ return NULL;
+ }
+
+ do {
+ if (page->child) page = page->child;
+ else page = new AutoreleasePoolPage(page);
+ } while (page->full());
+
+ setHotPage(page);
+ return page->add(obj);
+ }
+
+public:
+ static inline id autorelease(id obj)
+ {
+ assert(obj);
+ assert(!OBJC_IS_TAGGED_PTR(obj));
+ id *dest __unused = autoreleaseFast(obj);
+ assert(!dest || *dest == obj);
+ return obj;
+ }
+
+
+ static inline void *push()
+ {
+ if (!hotPage()) {
+ setHotPage(new AutoreleasePoolPage(NULL));
+ }
+ id *dest = autoreleaseFast(POOL_SENTINEL);
+ assert(*dest == POOL_SENTINEL);
+ return dest;
+ }
+
+ static inline void pop(void *token)
+ {
+ AutoreleasePoolPage *page;
+ id *stop;
+
+ if (token) {
+ page = pageForPointer(token);
+ stop = (id *)token;
+ assert(*stop == POOL_SENTINEL);
+ } else {
+ // Token 0 is top-level pool
+ page = coldPage();
+ assert(page);
+ stop = page->begin();
+ }
+
+ if (PrintPoolHiwat) printHiwat();
+
+ page->releaseUntil(stop);
+
+ // memory: delete empty children
+ // hysteresis: keep one empty child if this page is more than half full
+ // special case: delete everything for pop(0)
+ if (!token) {
+ page->kill();
+ setHotPage(NULL);
+ } else if (page->child) {
+ if (page->lessThanHalfFull()) {
+ page->child->kill();
+ }
+ else if (page->child->child) {
+ page->child->child->kill();
+ }
+ }
+ }
+
+ static void init()
+ {
+ int r __unused = pthread_key_init_np(AutoreleasePoolPage::key,
+ AutoreleasePoolPage::tls_dealloc);
+ assert(r == 0);
+ }
+
+ void print()
+ {
+ _objc_inform("[%p] ................ PAGE %s %s %s", this,
+ full() ? "(full)" : "",
+ this == hotPage() ? "(hot)" : "",
+ this == coldPage() ? "(cold)" : "");
+ check(false);
+ for (id *p = begin(); p < next; p++) {
+ if (*p == POOL_SENTINEL) {
+ _objc_inform("[%p] ################ POOL %p", p, p);
+ } else {
+ _objc_inform("[%p] %#16lx %s",
+ p, (unsigned long)*p, object_getClassName(*p));
+ }
+ }
+ }
+
+ static void printAll()
+ {
+ _objc_inform("##############");
+ _objc_inform("AUTORELEASE POOLS for thread %p", pthread_self());
+
+ AutoreleasePoolPage *page;
+ ptrdiff_t objects = 0;
+ for (page = coldPage(); page; page = page->child) {
+ objects += page->next - page->begin();
+ }
+ _objc_inform("%llu releases pending.", (unsigned long long)objects);
+
+ for (page = coldPage(); page; page = page->child) {
+ page->print();
+ }
+
+ _objc_inform("##############");
+ }
+
+ static void printHiwat()
+ {
+ // Check and propagate high water mark
+ // Ignore high water marks under 256 to suppress noise.
+ AutoreleasePoolPage *p = hotPage();
+ uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin());
+ if (mark > p->hiwat && mark > 256) {
+ for( ; p; p = p->parent) {
+ p->unprotect();
+ p->hiwat = mark;
+ p->protect();
+ }
+
+ _objc_inform("POOL HIGHWATER: new high water mark of %u "
+ "pending autoreleases for thread %p:",
+ mark, pthread_self());
+
+ void *stack[128];
+ int count = backtrace(stack, sizeof(stack)/sizeof(stack[0]));
+ char **sym = backtrace_symbols(stack, count);
+ for (int i = 0; i < count; i++) {
+ _objc_inform("POOL HIGHWATER: %s", sym[i]);
+ }
+ free(sym);
+ }
+ }
+
+#undef POOL_SENTINEL
+};
+
+// anonymous namespace
+};
+
+// API to only be called by root classes like NSObject or NSProxy
+
+extern "C" {
+__attribute__((used,noinline,nothrow))
+static id _objc_rootRetain_slow(id obj);
+__attribute__((used,noinline,nothrow))
+static bool _objc_rootReleaseWasZero_slow(id obj);
+};
+
+id
+_objc_rootRetain_slow(id obj)
+{
+ SideTable *table = SideTable::tableForPointer(obj);
+ OSSpinLockLock(&table->slock);
+ table->refcnts[DISGUISE(obj)] += 2;
+ OSSpinLockUnlock(&table->slock);
+
+ return obj;
+}
+
+bool
+_objc_rootTryRetain(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ if (OBJC_IS_TAGGED_PTR(obj)) return true;
+
+ SideTable *table = SideTable::tableForPointer(obj);
+
+ // NO SPINLOCK HERE
+ // _objc_rootTryRetain() is called exclusively by _objc_loadWeak(),
+ // which already acquired the lock on our behalf.
+ if (table->slock == 0) {
+ _objc_fatal("Do not call -_tryRetain.");
+ }
+
+ bool result = true;
+ RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
+ if (it == table->refcnts.end()) {
+ table->refcnts[DISGUISE(obj)] = 2;
+ } else if (it->second & 1) {
+ result = false;
+ } else {
+ it->second += 2;
+ }
+
+ return result;
+}
+
+bool
+_objc_rootIsDeallocating(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ if (OBJC_IS_TAGGED_PTR(obj)) return false;
+
+ SideTable *table = SideTable::tableForPointer(obj);
+
+ // NO SPINLOCK HERE
+ // _objc_rootIsDeallocating() is called exclusively by _objc_storeWeak(),
+ // which already acquired the lock on our behalf.
+ if (table->slock == 0) {
+ _objc_fatal("Do not call -_isDeallocating.");
+ }
+
+ RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
+ return (it != table->refcnts.end()) && ((it->second & 1) == 1);
+}
+
+
+void
+objc_clear_deallocating(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ SideTable *table = SideTable::tableForPointer(obj);
+
+ // clear any weak table items
+ // clear extra retain count and deallocating bit
+ // (fixme warn or abort if extra retain count == 0 ?)
+ OSSpinLockLock(&table->slock);
+ if (seen_weak_refs) {
+ arr_clear_deallocating(&table->weak_table, obj);
+ }
+ table->refcnts.erase(DISGUISE(obj));
+ OSSpinLockUnlock(&table->slock);
+}
+
+
+bool
+_objc_rootReleaseWasZero_slow(id obj)
+{
+ SideTable *table = SideTable::tableForPointer(obj);
+
+ bool do_dealloc = false;
+
+ OSSpinLockLock(&table->slock);
+ RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
+ if (it == table->refcnts.end()) {
+ do_dealloc = true;
+ table->refcnts[DISGUISE(obj)] = 1;
+ } else if (it->second == 0) {
+ do_dealloc = true;
+ it->second = 1;
+ } else {
+ it->second -= 2;
+ }
+ OSSpinLockUnlock(&table->slock);
+ return do_dealloc;
+}
+
+bool
+_objc_rootReleaseWasZero(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ if (OBJC_IS_TAGGED_PTR(obj)) return false;
+
+ SideTable *table = SideTable::tableForPointer(obj);
+
+ bool do_dealloc = false;
+
+ if (OSSpinLockTry(&table->slock)) {
+ RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
+ if (it == table->refcnts.end()) {
+ do_dealloc = true;
+ table->refcnts[DISGUISE(obj)] = 1;
+ } else if (it->second == 0) {
+ do_dealloc = true;
+ it->second = 1;
+ } else {
+ it->second -= 2;
+ }
+ OSSpinLockUnlock(&table->slock);
+ return do_dealloc;
+ }
+ return _objc_rootReleaseWasZero_slow(obj);
+}
+
+__attribute__((noinline,used))
+static id _objc_rootAutorelease2(id obj)
+{
+ if (OBJC_IS_TAGGED_PTR(obj)) return obj;
+ return AutoreleasePoolPage::autorelease(obj);
+}
+
+uintptr_t
+_objc_rootRetainCount(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ // XXX -- There is no way that anybody can use this API race free in a
+ // threaded environment because the result is immediately stale by the
+ // time the caller receives it.
+
+ if (OBJC_IS_TAGGED_PTR(obj)) return (uintptr_t)obj;
+
+ SideTable *table = SideTable::tableForPointer(obj);
+
+ size_t refcnt_result = 1;
+
+ OSSpinLockLock(&table->slock);
+ RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
+ if (it != table->refcnts.end()) {
+ refcnt_result = (it->second >> 1) + 1;
+ }
+ OSSpinLockUnlock(&table->slock);
+ return refcnt_result;
+}
+
+id
+_objc_rootInit(id obj)
+{
+ // In practice, it will be hard to rely on this function.
+ // Many classes do not properly chain -init calls.
+ return obj;
+}
+
+id
+_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
+{
+ id obj;
+
+#if __OBJC2__
+ // allocWithZone under __OBJC2__ ignores the zone parameter
+ (void)zone;
+ obj = class_createInstance(cls, 0);
+#else
+ 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 && __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
+_objc_rootDealloc(id obj)
+{
+ assert(obj);
+ assert(!UseGC);
+
+ if (OBJC_IS_TAGGED_PTR(obj)) return;
+
+ object_dispose(obj);
+}
+
+void
+_objc_rootFinalize(id obj __unused)
+{
+ assert(obj);
+ assert(UseGC);
+
+ if (UseGC) {
+ return;
+ }
+ _objc_fatal("_objc_rootFinalize called with garbage collection off");
+}
+
+malloc_zone_t *
+_objc_rootZone(id obj)
+{
+ (void)obj;
+ if (gc_zone) {
+ return gc_zone;
+ }
+#if __OBJC2__
+ // allocWithZone under __OBJC2__ ignores the zone parameter
+ return malloc_default_zone();
+#else
+ malloc_zone_t *rval = malloc_zone_from_ptr(obj);
+ return rval ? rval : malloc_default_zone();
+#endif
+}
+
+uintptr_t
+_objc_rootHash(id obj)
+{
+ if (UseGC) {
+ return _object_getExternalHash(obj);
+ }
+ return (uintptr_t)obj;
+}
+
+// make CF link for now
+void *_objc_autoreleasePoolPush(void) { return objc_autoreleasePoolPush(); }
+void _objc_autoreleasePoolPop(void *ctxt) { objc_autoreleasePoolPop(ctxt); }
+
+void *
+objc_autoreleasePoolPush(void)
+{
+ if (UseGC) return NULL;
+ return AutoreleasePoolPage::push();
+}
+
+void
+objc_autoreleasePoolPop(void *ctxt)
+{
+ if (UseGC) return;
+
+ // fixme rdar://9167170
+ if (!ctxt) return;
+
+ AutoreleasePoolPage::pop(ctxt);
+}
+
+void
+_objc_autoreleasePoolPrint(void)
+{
+ if (UseGC) return;
+ AutoreleasePoolPage::printAll();
+}
+
+#if SUPPORT_RETURN_AUTORELEASE
+
+/*
+ Fast handling of returned autoreleased values.
+ The caller and callee cooperate to keep the returned object
+ out of the autorelease pool.
+
+ Caller:
+ ret = callee();
+ objc_retainAutoreleasedReturnValue(ret);
+ // use ret here
+
+ Callee:
+ // compute ret
+ [ret retain];
+ return objc_autoreleaseReturnValue(ret);
+
+ objc_autoreleaseReturnValue() examines the caller's instructions following
+ the return. If the caller's instructions immediately call
+ objc_autoreleaseReturnValue, then the callee omits the -autorelease and saves
+ the result in thread-local storage. If the caller does not look like it
+ cooperates, then the callee calls -autorelease as usual.
+
+ objc_autoreleaseReturnValue checks if the returned value is the same as the
+ one in thread-local storage. If it is, the value is used directly. If not,
+ the value is assumed to be truly autoreleased and is retained again. In
+ either case, the caller now has a retained reference to the value.
+
+ Tagged pointer objects do participate in the fast autorelease scheme,
+ because it saves message sends. They are not entered in the autorelease
+ pool in the slow case.
+*/
+
+# if __x86_64__
+
+static bool callerAcceptsFastAutorelease(const void * const ra0)
+{
+ const uint8_t *ra1 = (const uint8_t *)ra0;
+ const uint16_t *ra2;
+ const uint32_t *ra4 = (const uint32_t *)ra1;
+ const void **sym;
+
+#define PREFER_GOTPCREL 0
+#if PREFER_GOTPCREL
+ // 48 89 c7 movq %rax,%rdi
+ // ff 15 callq *symbol@GOTPCREL(%rip)
+ if (*ra4 != 0xffc78948) {
+ return false;
+ }
+ if (ra1[4] != 0x15) {
+ return false;
+ }
+ ra1 += 3;
+#else
+ // 48 89 c7 movq %rax,%rdi
+ // e8 callq symbol
+ if (*ra4 != 0xe8c78948) {
+ return false;
+ }
+ ra1 += (long)*(const int32_t *)(ra1 + 4) + 8l;
+ ra2 = (const uint16_t *)ra1;
+ // ff 25 jmpq *symbol@DYLDMAGIC(%rip)
+ if (*ra2 != 0x25ff) {
+ return false;
+ }
+#endif
+ ra1 += 6l + (long)*(const int32_t *)(ra1 + 2);
+ sym = (const void **)ra1;
+ if (*sym != objc_retainAutoreleasedReturnValue)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// __x86_64__
+# 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
+
+static bool callerAcceptsFastAutorelease(const void *ra)
+{
+ return false;
+}
+
+# endif
+
+// SUPPORT_RETURN_AUTORELEASE
+#endif
+
+
+id
+objc_autoreleaseReturnValue(id obj)
+{
+#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, obj);
+ return obj;
+ }
+#endif
+
+ return objc_autorelease(obj);
+}
+
+id
+objc_retainAutoreleaseReturnValue(id obj)
+{
+ return objc_autoreleaseReturnValue(objc_retain(obj));
+}
+
+id
+objc_retainAutoreleasedReturnValue(id obj)
+{
+#if SUPPORT_RETURN_AUTORELEASE
+ if (obj == tls_get_direct(AUTORELEASE_POOL_RECLAIM_KEY)) {
+ tls_set_direct(AUTORELEASE_POOL_RECLAIM_KEY, 0);
+ return obj;
+ }
+#endif
+ return objc_retain(obj);
+}
+
+void
+objc_storeStrong(id *location, id obj)
+{
+ // XXX FIXME -- GC support?
+ id prev = *location;
+ if (obj == prev) {
+ return;
+ }
+ objc_retain(obj);
+ *location = obj;
+ objc_release(prev);
+}
+
+id
+objc_retainAutorelease(id obj)
+{
+ return objc_autorelease(objc_retain(obj));
+}
+
+void
+_objc_deallocOnMainThreadHelper(void *context)
+{
+ id obj = (id)context;
+ [obj dealloc];
+}
+
+#undef objc_retainedObject
+#undef objc_unretainedObject
+#undef objc_unretainedPointer
+
+// convert objc_objectptr_t to id, callee must take ownership.
+id objc_retainedObject(objc_objectptr_t pointer) { return (id)pointer; }
+
+// convert objc_objectptr_t to id, without ownership transfer.
+id objc_unretainedObject(objc_objectptr_t pointer) { return (id)pointer; }
+
+// convert id to objc_objectptr_t, no ownership transfer.
+objc_objectptr_t objc_unretainedPointer(id object) { return object; }
+
+
+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);
+}
#define _OBJC_OBJECT_H_
#include <stdarg.h>
-#import <objc/objc-runtime.h>
+#include <objc/objc-runtime.h>
-#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
/* 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;
/* 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 */
/* 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
@interface Object (Archiving)
-- startArchiving: (void *)stream;
-- finishUnarchiving;
+- (id)startArchiving: (void *)stream;
+- (id)finishUnarchiving;
@end
@interface Object (DynamicLoading)
//+ finishLoading:(headerType *)header;
-+ finishLoading:(struct mach_header *)header;
-+ startUnloading;
+struct mach_header;
++ (id)finishLoading:(struct mach_header *)header;
++ (id)startUnloading;
@end
#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 <stdlib.h>
-#import <stdarg.h>
-#import <string.h>
-#import <malloc/malloc.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <malloc/malloc.h>
-#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);
_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;
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;
}
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;
}
return z ? z : malloc_default_zone();
}
-+ superclass
++ (id)superclass
{
return class_getSuperclass((Class)self);
}
-- superclass
+- (id)superclass
{
return class_getSuperclass(isa);
}
return class_getVersion((Class)self);
}
-+ setVersion: (int) aVersion
++ (id)setVersion: (int) aVersion
{
class_setVersion((Class)self, aVersion);
return self;
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);
{
}
-- 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;
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;
}
/* Obsolete methods (for binary compatibility only). */
-+ superClass
++ (id)superClass
{
return [self superclass];
}
-- superClass
+- (id)superClass
{
return [self superclass];
}
return [self isKindOfClassNamed: aClassName];
}
-- (BOOL)isMemberOfGivenName:(const char *)aClassName
+- (BOOL)isMemberOfGivenName:(const char *)aClassName
{
return [self isMemberOfClassNamed: aClassName];
}
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)];
}
#if !__OBJC2__
-#import <objc/Object.h>
-#import <Availability.h>
+#include <objc/Object.h>
+#include <Availability.h>
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED
@interface List : Object
/* 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 */
/* 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
* 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)
#ifndef __OBJC2__
-#import <stdlib.h>
-#import <stdio.h>
-#import <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
-#import <objc/List.h>
+#include <objc/List.h>
#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)
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]))
return self;
}
-- copyFromZone:(void *)z
+- (id)copyFromZone:(void *)z
{
List *new = [[[self class] alloc] initCount: numElements];
new->numElements = numElements;
return numElements;
}
-- objectAt:(unsigned)index
+- (id)objectAt:(unsigned)index
{
if (index >= numElements)
return nil;
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;
return self;
}
-- insertObject:anObject at:(unsigned)index
+- (id)insertObject:anObject at:(unsigned)index
{
register id *this, *last, *prev;
if (! anObject) return nil;
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;
}
-- removeObjectAt:(unsigned)index
+- (id)removeObjectAt:(unsigned)index
{
register id *this, *last, *next;
id retval;
return retval;
}
-- removeObject:anObject
+- (id)removeObject:anObject
{
register id *this, *last;
this = dataPtr;
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)
return nil;
}
-- replaceObjectAt:(unsigned)index with:newObject
+- (id)replaceObjectAt:(unsigned)index with:newObject
{
register id *this;
id retval;
return retval;
}
-- makeObjectsPerform:(SEL)aSelector
+- (id)makeObjectsPerform:(SEL)aSelector
{
unsigned count = numElements;
while (count--)
return self;
}
-- makeObjectsPerform:(SEL)aSelector with:anObject
+- (id)makeObjectsPerform:(SEL)aSelector with:anObject
{
unsigned count = numElements;
while (count--)
return self;
}
--appendList: (List *)otherList
+-(id)appendList: (List *)otherList
{
unsigned i, count;
#ifndef _OBJC_PROTOCOL_H_
#define _OBJC_PROTOCOL_H_
-#include <objc/Object.h>
+#if __OBJC2__
+
+#include <Foundation/NSObject.h>
-/* 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 <objc/Object.h>
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
@interface Protocol : Object
@end
+#endif
+
#endif /* _OBJC_PROTOCOL_H_ */
+++ /dev/null
-/*
- * Copyright (c) 1999-2001, 2005-2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-/*
- Protocol.h
- Copyright 1991-1996 NeXT Software, Inc.
-*/
-
-
-#include <stdlib.h>
-#include <string.h>
-#include <mach-o/dyld.h>
-#include <mach-o/ldsyms.h>
-
-#import "Protocol.h"
-#import "objc-private.h"
-#import "objc-runtime-old.h"
-
-PRIVATE_EXTERN
-@interface __IncompleteProtocol { id isa; } @end
-@implementation __IncompleteProtocol
-+(void) initialize { }
-@end
-
-@implementation Protocol
-
-#if __OBJC2__
-// fixme hack - make Protocol a non-lazy class
-+ (void) load { }
-#endif
-
-
-typedef struct {
- uintptr_t count;
- Protocol *list[0];
-} protocol_list_t;
-
-- (BOOL) conformsTo: (Protocol *)aProtocolObj
-{
- return protocol_conformsToProtocol(self, aProtocolObj);
-}
-
-- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel
-{
-#if !__OBJC2__
- return lookup_protocol_method((struct old_protocol *)self, aSel,
- YES/*required*/, YES/*instance*/);
-#else
- return method_getDescription(_protocol_getMethod(self, aSel, YES, YES));
-#endif
-}
-
-- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel
-{
-#if !__OBJC2__
- return lookup_protocol_method((struct old_protocol *)self, aSel,
- YES/*required*/, NO/*instance*/);
-#else
- return method_getDescription(_protocol_getMethod(self, aSel, YES, NO));
-#endif
-}
-
-- (const char *)name
-{
- return protocol_getName(self);
-}
-
-- (BOOL)isEqual:other
-{
-#if __OBJC2__
- // check isKindOf:
- Class cls;
- Class protoClass = objc_getClass("Protocol");
- for (cls = other->isa; cls; cls = class_getSuperclass(cls)) {
- if (cls == protoClass) break;
- }
- if (!cls) return NO;
- // check equality
- return protocol_isEqual(self, other);
-#else
- return [other isKindOf:[Protocol class]] && [self conformsTo: other] && [other conformsTo: self];
-#endif
-}
-
-- (unsigned int)hash
-{
- return 23;
-}
-
-@end
--- /dev/null
+/*
+ * Copyright (c) 1999-2001, 2005-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/*
+ Protocol.h
+ Copyright 1991-1996 NeXT Software, Inc.
+*/
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <mach-o/dyld.h>
+#include <mach-o/ldsyms.h>
+
+#include "Protocol.h"
+#include "objc-private.h"
+#include "objc-runtime-old.h"
+
+#if __OBJC2__
+@interface __IncompleteProtocol : NSObject @end
+@implementation __IncompleteProtocol
+// fixme hack - make __IncompleteProtocol a non-lazy class
++ (void) load { }
+@end
+#endif
+
+@implementation Protocol
+
+#if __OBJC2__
+// fixme hack - make Protocol a non-lazy class
++ (void) load { }
+#endif
+
+
+typedef struct {
+ uintptr_t count;
+ Protocol *list[0];
+} protocol_list_t;
+
+- (BOOL) conformsTo: (Protocol *)aProtocolObj
+{
+ return protocol_conformsToProtocol(self, aProtocolObj);
+}
+
+- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel
+{
+#if !__OBJC2__
+ return lookup_protocol_method((struct old_protocol *)self, aSel,
+ YES/*required*/, YES/*instance*/,
+ YES/*recursive*/);
+#else
+ return method_getDescription(_protocol_getMethod(self, aSel,
+ YES, YES, YES));
+#endif
+}
+
+- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel
+{
+#if !__OBJC2__
+ return lookup_protocol_method((struct old_protocol *)self, aSel,
+ YES/*required*/, NO/*instance*/,
+ YES/*recursive*/);
+#else
+ return method_getDescription(_protocol_getMethod(self, aSel,
+ YES, NO, YES));
+#endif
+}
+
+- (const char *)name
+{
+ return protocol_getName(self);
+}
+
+- (BOOL)isEqual:other
+{
+#if __OBJC2__
+ // check isKindOf:
+ Class cls;
+ Class protoClass = objc_getClass("Protocol");
+ for (cls = object_getClass(other); cls; cls = class_getSuperclass(cls)) {
+ if (cls == protoClass) break;
+ }
+ if (!cls) return NO;
+ // check equality
+ return protocol_isEqual(self, other);
+#else
+ return [other isKindOf:[Protocol class]] && [self conformsTo: other] && [other conformsTo: self];
+#endif
+}
+
+#if __OBJC2__
+- (NSUInteger)hash
+{
+ return 23;
+}
+#else
+- (unsigned)hash
+{
+ return 23;
+}
+#endif
+
+@end
.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
#endif
.align 12
+__a1a2_tramphead_nt:
__a1a2_tramphead:
/*
r0 == self
*/
// 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
#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
// 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
__a1a2_trampend:
-#endif
\ No newline at end of file
+#endif
.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
.align 12
.private_extern __a2a3_tramphead
+__a2a3_tramphead_nt:
__a2a3_tramphead:
/*
r0 == stret
*/
// 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
#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
// 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
__a2a3_trampend:
-#endif
\ No newline at end of file
+#endif
+++ /dev/null
-/*
- * Copyright (c) 1999-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@
- */
-/*
- hashtable2.m
- Copyright 1989-1996 NeXT Software, Inc.
- Created by Bertrand Serlet, Feb 89
- */
-
-#include "objc-private.h"
-#include "hashtable2.h"
-
-/* In order to improve efficiency, buckets contain a pointer to an array or directly the data when the array size is 1 */
-typedef union {
- const void *one;
- const void **many;
- } oneOrMany;
- /* an optimization consists of storing directly data when count = 1 */
-
-typedef struct {
- unsigned count;
- oneOrMany elements;
- } HashBucket;
- /* private data structure; may change */
-
-/*************************************************************************
- *
- * Macros and utilities
- *
- *************************************************************************/
-
-static unsigned log2u (unsigned x) { return (x<2) ? 0 : log2u (x>>1)+1; };
-
-#define PTRSIZE sizeof(void *)
-
-#if !SUPPORT_ZONES
-# define DEFAULT_ZONE NULL
-# 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 *)))
-#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 *)))
-#endif
-
-#if !SUPPORT_MOD
- /* nbBuckets must be a power of 2 */
-# define BUCKETOF(table, data) (((HashBucket *)table->buckets)+((*table->prototype->hash)(table->info, data) & (table->nbBuckets-1)))
-# define GOOD_CAPACITY(c) (c <= 1 ? 1 : 1 << (log2u (c-1)+1))
-# define MORE_CAPACITY(b) (b*2)
-#else
- /* iff necessary this modulo can be optimized since the nbBuckets is of the form 2**n-1 */
-# define BUCKETOF(table, data) (((HashBucket *)table->buckets)+((*table->prototype->hash)(table->info, data) % table->nbBuckets))
- static unsigned exp2m1 (unsigned x) { return (1 << x) - 1; };
-# define GOOD_CAPACITY(c) (exp2m1 (log2u (c)+1))
-# define MORE_CAPACITY(b) (b*2+1)
-#endif
-
-#define ISEQUAL(table, data1, data2) ((data1 == data2) || (*table->prototype->isEqual)(table->info, data1, data2))
- /* beware of double evaluation */
-
-/*************************************************************************
- *
- * Global data and bootstrap
- *
- *************************************************************************/
-
-static int isEqualPrototype (const void *info, const void *data1, const void *data2) {
- NXHashTablePrototype *proto1 = (NXHashTablePrototype *) data1;
- NXHashTablePrototype *proto2 = (NXHashTablePrototype *) data2;
-
- return (proto1->hash == proto2->hash) && (proto1->isEqual == proto2->isEqual) && (proto1->free == proto2->free) && (proto1->style == proto2->style);
- };
-
-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;
- };
-
-void NXNoEffectFree (const void *info, void *data) {};
-
-static NXHashTablePrototype protoPrototype = {
- hashPrototype, isEqualPrototype, NXNoEffectFree, 0
- };
-
-static NXHashTable *prototypes = NULL;
- /* table of all prototypes */
-
-static void bootstrap (void) {
- free(malloc(8));
- prototypes = ALLOCTABLE (DEFAULT_ZONE);
- prototypes->prototype = &protoPrototype;
- prototypes->count = 1;
- prototypes->nbBuckets = 1; /* has to be 1 so that the right bucket is 0 */
- prototypes->buckets = ALLOCBUCKETS(DEFAULT_ZONE, 1);
- prototypes->info = NULL;
- ((HashBucket *) prototypes->buckets)[0].count = 1;
- ((HashBucket *) prototypes->buckets)[0].elements.one = &protoPrototype;
- };
-
-int NXPtrIsEqual (const void *info, const void *data1, const void *data2) {
- return data1 == data2;
- };
-
-/*************************************************************************
- *
- * On z'y va
- *
- *************************************************************************/
-
-NXHashTable *NXCreateHashTable (NXHashTablePrototype prototype, unsigned capacity, const void *info) {
- return NXCreateHashTableFromZone(prototype, capacity, info, DEFAULT_ZONE);
-}
-
-NXHashTable *NXCreateHashTableFromZone (NXHashTablePrototype prototype, unsigned capacity, const void *info, void *z) {
- NXHashTable *table;
- NXHashTablePrototype *proto;
-
- table = ALLOCTABLE(z);
- if (! prototypes) bootstrap ();
- if (! prototype.hash) prototype.hash = NXPtrHash;
- if (! prototype.isEqual) prototype.isEqual = NXPtrIsEqual;
- if (! prototype.free) prototype.free = NXNoEffectFree;
- if (prototype.style) {
- _objc_inform ("*** NXCreateHashTable: invalid style\n");
- return NULL;
- };
- proto = 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);
- if (! proto) {
- _objc_inform ("*** NXCreateHashTable: bug\n");
- return NULL;
- };
- };
- table->prototype = proto; table->count = 0; table->info = info;
- table->nbBuckets = GOOD_CAPACITY(capacity);
- table->buckets = ALLOCBUCKETS(z, table->nbBuckets);
- return table;
- }
-
-static void freeBucketPairs (void (*freeProc)(const void *info, void *data), HashBucket bucket, const void *info) {
- unsigned j = bucket.count;
- const void **pairs;
-
- if (j == 1) {
- (*freeProc) (info, (void *) bucket.elements.one);
- return;
- };
- pairs = bucket.elements.many;
- while (j--) {
- (*freeProc) (info, (void *) *pairs);
- pairs ++;
- };
- free ((void*)bucket.elements.many);
- };
-
-static void freeBuckets (NXHashTable *table, int freeObjects) {
- unsigned i = table->nbBuckets;
- HashBucket *buckets = (HashBucket *) table->buckets;
-
- while (i--) {
- if (buckets->count) {
- freeBucketPairs ((freeObjects) ? table->prototype->free : NXNoEffectFree, *buckets, table->info);
- buckets->count = 0;
- buckets->elements.one = NULL;
- };
- buckets++;
- };
- };
-
-void NXFreeHashTable (NXHashTable *table) {
- freeBuckets (table, YES);
- free (table->buckets);
- free (table);
- };
-
-void NXEmptyHashTable (NXHashTable *table) {
- freeBuckets (table, NO);
- table->count = 0;
- }
-
-void NXResetHashTable (NXHashTable *table) {
- freeBuckets (table, YES);
- table->count = 0;
-}
-
-BOOL NXCompareHashTables (NXHashTable *table1, NXHashTable *table2) {
- if (table1 == table2) return YES;
- if (NXCountHashTable (table1) != NXCountHashTable (table2)) return NO;
- else {
- void *data;
- NXHashState state = NXInitHashState (table1);
- while (NXNextHashState (table1, &state, &data)) {
- if (! NXHashMember (table2, data)) return NO;
- }
- return YES;
- }
-}
-
-NXHashTable *NXCopyHashTable (NXHashTable *table) {
- NXHashTable *newt;
- NXHashState state = NXInitHashState (table);
- void *data;
- void *z = ZONE_FROM_PTR(table);
-
- newt = ALLOCTABLE(z);
- newt->prototype = table->prototype; newt->count = 0;
- newt->info = table->info;
- newt->nbBuckets = table->nbBuckets;
- newt->buckets = ALLOCBUCKETS(z, newt->nbBuckets);
- while (NXNextHashState (table, &state, &data))
- NXHashInsert (newt, data);
- return newt;
- }
-
-unsigned NXCountHashTable (NXHashTable *table) {
- return table->count;
- }
-
-int NXHashMember (NXHashTable *table, const void *data) {
- HashBucket *bucket = BUCKETOF(table, data);
- unsigned j = bucket->count;
- const void **pairs;
-
- if (! j) return 0;
- if (j == 1) {
- return ISEQUAL(table, data, bucket->elements.one);
- };
- pairs = bucket->elements.many;
- while (j--) {
- /* we don't cache isEqual because lists are short */
- if (ISEQUAL(table, data, *pairs)) return 1;
- pairs ++;
- };
- return 0;
- }
-
-void *NXHashGet (NXHashTable *table, const void *data) {
- HashBucket *bucket = BUCKETOF(table, data);
- unsigned j = bucket->count;
- const void **pairs;
-
- if (! j) return NULL;
- if (j == 1) {
- return ISEQUAL(table, data, bucket->elements.one)
- ? (void *) bucket->elements.one : NULL;
- };
- pairs = bucket->elements.many;
- while (j--) {
- /* we don't cache isEqual because lists are short */
- if (ISEQUAL(table, data, *pairs)) return (void *) *pairs;
- pairs ++;
- };
- return NULL;
- }
-
-PRIVATE_EXTERN unsigned _NXHashCapacity (NXHashTable *table) {
- return table->nbBuckets;
- }
-
-PRIVATE_EXTERN void _NXHashRehashToCapacity (NXHashTable *table, unsigned newCapacity) {
- /* Rehash: we create a pseudo table pointing really to the old guys,
- extend self, copy the old pairs, and free the pseudo table */
- NXHashTable *old;
- NXHashState state;
- void *aux;
- void *z = ZONE_FROM_PTR(table);
-
- old = ALLOCTABLE(z);
- old->prototype = table->prototype; old->count = table->count;
- old->nbBuckets = table->nbBuckets; old->buckets = table->buckets;
- table->nbBuckets = newCapacity;
- table->count = 0; table->buckets = ALLOCBUCKETS(z, table->nbBuckets);
- state = NXInitHashState (old);
- while (NXNextHashState (old, &state, &aux))
- (void) NXHashInsert (table, aux);
- freeBuckets (old, NO);
- if (old->count != table->count)
- _objc_inform("*** hashtable: 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 (old->buckets);
- free (old);
- }
-
-static void _NXHashRehash (NXHashTable *table) {
- _NXHashRehashToCapacity (table, MORE_CAPACITY(table->nbBuckets));
- }
-
-void *NXHashInsert (NXHashTable *table, const void *data) {
- HashBucket *bucket = BUCKETOF(table, data);
- unsigned j = bucket->count;
- const void **pairs;
- const void **newt;
- void *z = ZONE_FROM_PTR(table);
-
- if (! j) {
- bucket->count++; bucket->elements.one = data;
- table->count++;
- return NULL;
- };
- if (j == 1) {
- if (ISEQUAL(table, data, bucket->elements.one)) {
- const void *old = bucket->elements.one;
- bucket->elements.one = data;
- return (void *) old;
- };
- newt = ALLOCPAIRS(z, 2);
- newt[1] = bucket->elements.one;
- *newt = data;
- bucket->count++; bucket->elements.many = newt;
- table->count++;
- if (table->count > table->nbBuckets) _NXHashRehash (table);
- return NULL;
- };
- pairs = bucket->elements.many;
- while (j--) {
- /* we don't cache isEqual because lists are short */
- if (ISEQUAL(table, data, *pairs)) {
- const void *old = *pairs;
- *pairs = data;
- return (void *) old;
- };
- pairs ++;
- };
- /* we enlarge this bucket; and put new data in front */
- 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);
- bucket->count++; bucket->elements.many = newt;
- table->count++;
- if (table->count > table->nbBuckets) _NXHashRehash (table);
- return NULL;
- }
-
-void *NXHashInsertIfAbsent (NXHashTable *table, const void *data) {
- HashBucket *bucket = BUCKETOF(table, data);
- unsigned j = bucket->count;
- const void **pairs;
- const void **newt;
- void *z = ZONE_FROM_PTR(table);
-
- if (! j) {
- bucket->count++; bucket->elements.one = data;
- table->count++;
- return (void *) data;
- };
- if (j == 1) {
- if (ISEQUAL(table, data, bucket->elements.one))
- return (void *) bucket->elements.one;
- newt = ALLOCPAIRS(z, 2);
- newt[1] = bucket->elements.one;
- *newt = data;
- bucket->count++; bucket->elements.many = newt;
- table->count++;
- if (table->count > table->nbBuckets) _NXHashRehash (table);
- return (void *) data;
- };
- pairs = bucket->elements.many;
- while (j--) {
- /* we don't cache isEqual because lists are short */
- if (ISEQUAL(table, data, *pairs))
- return (void *) *pairs;
- pairs ++;
- };
- /* we enlarge this bucket; and put new data in front */
- 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);
- bucket->count++; bucket->elements.many = newt;
- table->count++;
- if (table->count > table->nbBuckets) _NXHashRehash (table);
- return (void *) data;
- }
-
-void *NXHashRemove (NXHashTable *table, const void *data) {
- HashBucket *bucket = BUCKETOF(table, data);
- unsigned j = bucket->count;
- const void **pairs;
- const void **newt;
- void *z = ZONE_FROM_PTR(table);
-
- if (! j) return NULL;
- if (j == 1) {
- if (! ISEQUAL(table, data, bucket->elements.one)) return NULL;
- data = bucket->elements.one;
- table->count--; bucket->count--; bucket->elements.one = NULL;
- return (void *) data;
- };
- pairs = bucket->elements.many;
- if (j == 2) {
- if (ISEQUAL(table, data, pairs[0])) {
- bucket->elements.one = pairs[1]; data = pairs[0];
- }
- else if (ISEQUAL(table, data, pairs[1])) {
- bucket->elements.one = pairs[0]; data = pairs[1];
- }
- else return NULL;
- free ((void*)pairs);
- table->count--; bucket->count--;
- return (void *) data;
- };
- while (j--) {
- if (ISEQUAL(table, data, *pairs)) {
- data = *pairs;
- /* we shrink this bucket */
- newt = (bucket->count-1)
- ? ALLOCPAIRS(z, bucket->count-1) : NULL;
- if (bucket->count-1 != j)
- 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);
- table->count--; bucket->count--; bucket->elements.many = newt;
- return (void *) data;
- };
- pairs ++;
- };
- return NULL;
- }
-
-NXHashState NXInitHashState (NXHashTable *table) {
- NXHashState state;
-
- state.i = table->nbBuckets;
- state.j = 0;
- return state;
- };
-
-int NXNextHashState (NXHashTable *table, NXHashState *state, void **data) {
- HashBucket *buckets = (HashBucket *) table->buckets;
-
- while (state->j == 0) {
- if (state->i == 0) return NO;
- state->i--; state->j = buckets[state->i].count;
- }
- state->j--;
- buckets += state->i;
- *data = (void *) ((buckets->count == 1)
- ? buckets->elements.one : buckets->elements.many[state->j]);
- return YES;
- };
-
-/*************************************************************************
- *
- * Conveniences
- *
- *************************************************************************/
-
-uintptr_t NXPtrHash (const void *info, const void *data) {
- return (((uintptr_t) data) >> 16) ^ ((uintptr_t) data);
- };
-
-uintptr_t NXStrHash (const void *info, const void *data) {
- register uintptr_t hash = 0;
- register unsigned char *s = (unsigned char *) data;
- /* unsigned to avoid a sign-extend */
- /* unroll the loop */
- if (s) for (; ; ) {
- if (*s == '\0') break;
- hash ^= (uintptr_t) *s++;
- if (*s == '\0') break;
- hash ^= (uintptr_t) *s++ << 8;
- if (*s == '\0') break;
- hash ^= (uintptr_t) *s++ << 16;
- if (*s == '\0') break;
- hash ^= (uintptr_t) *s++ << 24;
- }
- return hash;
- };
-
-int NXStrIsEqual (const void *info, const void *data1, const void *data2) {
- if (data1 == data2) return YES;
- if (! data1) return ! strlen ((char *) data2);
- if (! data2) return ! strlen ((char *) data1);
- if (((char *) data1)[0] != ((char *) data2)[0]) return NO;
- return (strcmp ((char *) data1, (char *) data2)) ? NO : YES;
- };
-
-void NXReallyFree (const void *info, void *data) {
- free (data);
- };
-
-/* All the following functions are really private, made non-static only for the benefit of shlibs */
-static uintptr_t hashPtrStructKey (const void *info, const void *data) {
- return NXPtrHash(info, *((void **) data));
- };
-
-static int isEqualPtrStructKey (const void *info, const void *data1, const void *data2) {
- return NXPtrIsEqual (info, *((void **) data1), *((void **) data2));
- };
-
-static uintptr_t hashStrStructKey (const void *info, const void *data) {
- return NXStrHash(info, *((char **) data));
- };
-
-static int isEqualStrStructKey (const void *info, const void *data1, const void *data2) {
- return NXStrIsEqual (info, *((char **) data1), *((char **) data2));
- };
-
-const NXHashTablePrototype NXPtrPrototype = {
- NXPtrHash, NXPtrIsEqual, NXNoEffectFree, 0
- };
-
-const NXHashTablePrototype NXStrPrototype = {
- NXStrHash, NXStrIsEqual, NXNoEffectFree, 0
- };
-
-const NXHashTablePrototype NXPtrStructKeyPrototype = {
- hashPtrStructKey, isEqualPtrStructKey, NXReallyFree, 0
- };
-
-const NXHashTablePrototype NXStrStructKeyPrototype = {
- hashStrStructKey, isEqualStrStructKey, NXReallyFree, 0
- };
-
-/*************************************************************************
- *
- * Unique strings
- *
- *************************************************************************/
-
-#if !__OBJC2__ && !TARGET_OS_WIN32
-
-/* the implementation could be made faster at the expense of memory if the size of the strings were kept around */
-static NXHashTable *uniqueStrings = NULL;
-
-/* this is based on most apps using a few K of strings, and an average string size of 15 using sqrt(2*dataAlloced*perChunkOverhead) */
-#define CHUNK_SIZE 360
-
-static int accessUniqueString = 0;
-
-static char *z = NULL;
-static size_t zSize = 0;
-static mutex_t lock = MUTEX_INITIALIZER;
-
-static const char *CopyIntoReadOnly (const char *str) {
- size_t len = strlen (str) + 1;
- char *new;
-
- if (len > CHUNK_SIZE/2) { /* dont let big strings waste space */
- new = malloc (len);
- bcopy (str, new, len);
- return new;
- }
-
- 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);
- };
-
- new = z;
- bcopy (str, new, len);
- z += len;
- zSize -= len;
- mutex_unlock (&lock);
- return new;
- };
-
-NXAtom NXUniqueString (const char *buffer) {
- const char *previous;
-
- if (! buffer) return buffer;
- accessUniqueString++;
- if (! uniqueStrings)
- uniqueStrings = NXCreateHashTable (NXStrPrototype, 0, NULL);
- previous = (const char *) NXHashGet (uniqueStrings, buffer);
- if (previous) return previous;
- previous = CopyIntoReadOnly (buffer);
- if (NXHashInsert (uniqueStrings, previous)) {
- _objc_inform ("*** NXUniqueString: invariant broken\n");
- return NULL;
- };
- return previous;
- };
-
-NXAtom NXUniqueStringNoCopy (const char *string) {
- accessUniqueString++;
- if (! uniqueStrings)
- uniqueStrings = NXCreateHashTable (NXStrPrototype, 0, NULL);
- return (const char *) NXHashInsertIfAbsent (uniqueStrings, string);
- };
-
-#define BUF_SIZE 256
-
-NXAtom NXUniqueStringWithLength (const char *buffer, int length) {
- NXAtom atom;
- char *nullTermStr;
- char stackBuf[BUF_SIZE];
-
- if (length+1 > BUF_SIZE)
- nullTermStr = malloc (length+1);
- else
- nullTermStr = stackBuf;
- bcopy (buffer, nullTermStr, length);
- nullTermStr[length] = '\0';
- atom = NXUniqueString (nullTermStr);
- if (length+1 > BUF_SIZE)
- free (nullTermStr);
- return atom;
- };
-
-char *NXCopyStringBufferFromZone (const char *str, void *z) {
-#if !SUPPORT_ZONES
- return strdup(str);
-#else
- return strcpy ((char *) malloc_zone_malloc(z, strlen (str) + 1), str);
-#endif
- };
-
-char *NXCopyStringBuffer (const char *str) {
- return strdup(str);
- };
-
-#endif
--- /dev/null
+/*
+ * Copyright (c) 1999-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@
+ */
+/*
+ hashtable2.m
+ Copyright 1989-1996 NeXT Software, Inc.
+ Created by Bertrand Serlet, Feb 89
+ */
+
+#include "objc-private.h"
+#include "hashtable2.h"
+
+/* In order to improve efficiency, buckets contain a pointer to an array or directly the data when the array size is 1 */
+typedef union {
+ const void *one;
+ const void **many;
+ } oneOrMany;
+ /* an optimization consists of storing directly data when count = 1 */
+
+typedef struct {
+ unsigned count;
+ oneOrMany elements;
+ } HashBucket;
+ /* private data structure; may change */
+
+/*************************************************************************
+ *
+ * Macros and utilities
+ *
+ *************************************************************************/
+
+static unsigned log2u (unsigned x) { return (x<2) ? 0 : log2u (x>>1)+1; };
+
+#define PTRSIZE sizeof(void *)
+
+#if !SUPPORT_ZONES
+# define DEFAULT_ZONE NULL
+# define ZONE_FROM_PTR(p) NULL
+# define ALLOCTABLE(z) ((NXHashTable *) malloc (sizeof (NXHashTable)))
+# define ALLOCBUCKETS(z,nb)((HashBucket *) calloc (nb, sizeof (HashBucket)))
+/* 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 ((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
+ /* nbBuckets must be a power of 2 */
+# define BUCKETOF(table, data) (((HashBucket *)table->buckets)+((*table->prototype->hash)(table->info, data) & (table->nbBuckets-1)))
+# define GOOD_CAPACITY(c) (c <= 1 ? 1 : 1 << (log2u (c-1)+1))
+# define MORE_CAPACITY(b) (b*2)
+#else
+ /* iff necessary this modulo can be optimized since the nbBuckets is of the form 2**n-1 */
+# define BUCKETOF(table, data) (((HashBucket *)table->buckets)+((*table->prototype->hash)(table->info, data) % table->nbBuckets))
+ static unsigned exp2m1 (unsigned x) { return (1 << x) - 1; };
+# define GOOD_CAPACITY(c) (exp2m1 (log2u (c)+1))
+# define MORE_CAPACITY(b) (b*2+1)
+#endif
+
+#define ISEQUAL(table, data1, data2) ((data1 == data2) || (*table->prototype->isEqual)(table->info, data1, data2))
+ /* beware of double evaluation */
+
+/*************************************************************************
+ *
+ * Global data and bootstrap
+ *
+ *************************************************************************/
+
+static int isEqualPrototype (const void *info, const void *data1, const void *data2) {
+ NXHashTablePrototype *proto1 = (NXHashTablePrototype *) data1;
+ NXHashTablePrototype *proto2 = (NXHashTablePrototype *) data2;
+
+ return (proto1->hash == proto2->hash) && (proto1->isEqual == proto2->isEqual) && (proto1->free == proto2->free) && (proto1->style == proto2->style);
+ };
+
+static uintptr_t hashPrototype (const void *info, const void *data) {
+ NXHashTablePrototype *proto = (NXHashTablePrototype *) data;
+
+ 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) {};
+
+static NXHashTablePrototype protoPrototype = {
+ hashPrototype, isEqualPrototype, NXNoEffectFree, 0
+ };
+
+static NXHashTable *prototypes = NULL;
+ /* table of all prototypes */
+
+static void bootstrap (void) {
+ free(malloc(8));
+ prototypes = ALLOCTABLE (DEFAULT_ZONE);
+ prototypes->prototype = &protoPrototype;
+ prototypes->count = 1;
+ prototypes->nbBuckets = 1; /* has to be 1 so that the right bucket is 0 */
+ prototypes->buckets = ALLOCBUCKETS(DEFAULT_ZONE, 1);
+ prototypes->info = NULL;
+ ((HashBucket *) prototypes->buckets)[0].count = 1;
+ ((HashBucket *) prototypes->buckets)[0].elements.one = &protoPrototype;
+ };
+
+int NXPtrIsEqual (const void *info, const void *data1, const void *data2) {
+ return data1 == data2;
+ };
+
+/*************************************************************************
+ *
+ * On z'y va
+ *
+ *************************************************************************/
+
+NXHashTable *NXCreateHashTable (NXHashTablePrototype prototype, unsigned capacity, const void *info) {
+ return NXCreateHashTableFromZone(prototype, capacity, info, DEFAULT_ZONE);
+}
+
+NXHashTable *NXCreateHashTableFromZone (NXHashTablePrototype prototype, unsigned capacity, const void *info, void *z) {
+ NXHashTable *table;
+ NXHashTablePrototype *proto;
+
+ table = ALLOCTABLE(z);
+ if (! prototypes) bootstrap ();
+ if (! prototype.hash) prototype.hash = NXPtrHash;
+ if (! prototype.isEqual) prototype.isEqual = NXPtrIsEqual;
+ if (! prototype.free) prototype.free = NXNoEffectFree;
+ if (prototype.style) {
+ _objc_inform ("*** NXCreateHashTable: invalid style\n");
+ return NULL;
+ };
+ 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 = (NXHashTablePrototype *)NXHashGet (prototypes, &prototype);
+ if (! proto) {
+ _objc_inform ("*** NXCreateHashTable: bug\n");
+ return NULL;
+ };
+ };
+ table->prototype = proto; table->count = 0; table->info = info;
+ table->nbBuckets = GOOD_CAPACITY(capacity);
+ table->buckets = ALLOCBUCKETS(z, table->nbBuckets);
+ return table;
+ }
+
+static void freeBucketPairs (void (*freeProc)(const void *info, void *data), HashBucket bucket, const void *info) {
+ unsigned j = bucket.count;
+ const void **pairs;
+
+ if (j == 1) {
+ (*freeProc) (info, (void *) bucket.elements.one);
+ return;
+ };
+ pairs = bucket.elements.many;
+ while (j--) {
+ (*freeProc) (info, (void *) *pairs);
+ pairs ++;
+ };
+ FREEPAIRS (bucket.elements.many);
+ };
+
+static void freeBuckets (NXHashTable *table, int freeObjects) {
+ unsigned i = table->nbBuckets;
+ HashBucket *buckets = (HashBucket *) table->buckets;
+
+ while (i--) {
+ if (buckets->count) {
+ freeBucketPairs ((freeObjects) ? table->prototype->free : NXNoEffectFree, *buckets, table->info);
+ buckets->count = 0;
+ buckets->elements.one = NULL;
+ };
+ buckets++;
+ };
+ };
+
+void NXFreeHashTable (NXHashTable *table) {
+ freeBuckets (table, YES);
+ free (table->buckets);
+ free (table);
+ };
+
+void NXEmptyHashTable (NXHashTable *table) {
+ freeBuckets (table, NO);
+ table->count = 0;
+ }
+
+void NXResetHashTable (NXHashTable *table) {
+ freeBuckets (table, YES);
+ table->count = 0;
+}
+
+BOOL NXCompareHashTables (NXHashTable *table1, NXHashTable *table2) {
+ if (table1 == table2) return YES;
+ if (NXCountHashTable (table1) != NXCountHashTable (table2)) return NO;
+ else {
+ void *data;
+ NXHashState state = NXInitHashState (table1);
+ while (NXNextHashState (table1, &state, &data)) {
+ if (! NXHashMember (table2, data)) return NO;
+ }
+ return YES;
+ }
+}
+
+NXHashTable *NXCopyHashTable (NXHashTable *table) {
+ NXHashTable *newt;
+ NXHashState state = NXInitHashState (table);
+ void *data;
+ void *z = ZONE_FROM_PTR(table);
+
+ newt = ALLOCTABLE(z);
+ newt->prototype = table->prototype; newt->count = 0;
+ newt->info = table->info;
+ newt->nbBuckets = table->nbBuckets;
+ newt->buckets = ALLOCBUCKETS(z, newt->nbBuckets);
+ while (NXNextHashState (table, &state, &data))
+ NXHashInsert (newt, data);
+ return newt;
+ }
+
+unsigned NXCountHashTable (NXHashTable *table) {
+ return table->count;
+ }
+
+int NXHashMember (NXHashTable *table, const void *data) {
+ HashBucket *bucket = BUCKETOF(table, data);
+ unsigned j = bucket->count;
+ const void **pairs;
+
+ if (! j) return 0;
+ if (j == 1) {
+ return ISEQUAL(table, data, bucket->elements.one);
+ };
+ pairs = bucket->elements.many;
+ while (j--) {
+ /* we don't cache isEqual because lists are short */
+ if (ISEQUAL(table, data, *pairs)) return 1;
+ pairs ++;
+ };
+ return 0;
+ }
+
+void *NXHashGet (NXHashTable *table, const void *data) {
+ HashBucket *bucket = BUCKETOF(table, data);
+ unsigned j = bucket->count;
+ const void **pairs;
+
+ if (! j) return NULL;
+ if (j == 1) {
+ return ISEQUAL(table, data, bucket->elements.one)
+ ? (void *) bucket->elements.one : NULL;
+ };
+ pairs = bucket->elements.many;
+ while (j--) {
+ /* we don't cache isEqual because lists are short */
+ if (ISEQUAL(table, data, *pairs)) return (void *) *pairs;
+ pairs ++;
+ };
+ return NULL;
+ }
+
+unsigned _NXHashCapacity (NXHashTable *table) {
+ return table->nbBuckets;
+ }
+
+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;
+ NXHashState state;
+ void *aux;
+ void *z = ZONE_FROM_PTR(table);
+
+ old = ALLOCTABLE(z);
+ old->prototype = table->prototype; old->count = table->count;
+ old->nbBuckets = table->nbBuckets; old->buckets = table->buckets;
+ table->nbBuckets = newCapacity;
+ table->count = 0; table->buckets = ALLOCBUCKETS(z, table->nbBuckets);
+ state = NXInitHashState (old);
+ while (NXNextHashState (old, &state, &aux))
+ (void) NXHashInsert (table, aux);
+ freeBuckets (old, NO);
+ if (old->count != table->count)
+ _objc_inform("*** hashtable: 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 (old->buckets);
+ free (old);
+ }
+
+static void _NXHashRehash (NXHashTable *table) {
+ _NXHashRehashToCapacity (table, MORE_CAPACITY(table->nbBuckets));
+ }
+
+void *NXHashInsert (NXHashTable *table, const void *data) {
+ HashBucket *bucket = BUCKETOF(table, data);
+ unsigned j = bucket->count;
+ const void **pairs;
+ const void **newt;
+ void *z = ZONE_FROM_PTR(table);
+
+ if (! j) {
+ bucket->count++; bucket->elements.one = data;
+ table->count++;
+ return NULL;
+ };
+ if (j == 1) {
+ if (ISEQUAL(table, data, bucket->elements.one)) {
+ const void *old = bucket->elements.one;
+ bucket->elements.one = data;
+ return (void *) old;
+ };
+ newt = ALLOCPAIRS(z, 2);
+ newt[1] = bucket->elements.one;
+ *newt = data;
+ bucket->count++; bucket->elements.many = newt;
+ table->count++;
+ if (table->count > table->nbBuckets) _NXHashRehash (table);
+ return NULL;
+ };
+ pairs = bucket->elements.many;
+ while (j--) {
+ /* we don't cache isEqual because lists are short */
+ if (ISEQUAL(table, data, *pairs)) {
+ const void *old = *pairs;
+ *pairs = data;
+ return (void *) old;
+ };
+ pairs ++;
+ };
+ /* we enlarge this bucket; and put new data in front */
+ newt = ALLOCPAIRS(z, bucket->count+1);
+ if (bucket->count) bcopy ((const char*)bucket->elements.many, (char*)(newt+1), bucket->count * PTRSIZE);
+ *newt = data;
+ FREEPAIRS (bucket->elements.many);
+ bucket->count++; bucket->elements.many = newt;
+ table->count++;
+ if (table->count > table->nbBuckets) _NXHashRehash (table);
+ return NULL;
+ }
+
+void *NXHashInsertIfAbsent (NXHashTable *table, const void *data) {
+ HashBucket *bucket = BUCKETOF(table, data);
+ unsigned j = bucket->count;
+ const void **pairs;
+ const void **newt;
+ void *z = ZONE_FROM_PTR(table);
+
+ if (! j) {
+ bucket->count++; bucket->elements.one = data;
+ table->count++;
+ return (void *) data;
+ };
+ if (j == 1) {
+ if (ISEQUAL(table, data, bucket->elements.one))
+ return (void *) bucket->elements.one;
+ newt = ALLOCPAIRS(z, 2);
+ newt[1] = bucket->elements.one;
+ *newt = data;
+ bucket->count++; bucket->elements.many = newt;
+ table->count++;
+ if (table->count > table->nbBuckets) _NXHashRehash (table);
+ return (void *) data;
+ };
+ pairs = bucket->elements.many;
+ while (j--) {
+ /* we don't cache isEqual because lists are short */
+ if (ISEQUAL(table, data, *pairs))
+ return (void *) *pairs;
+ pairs ++;
+ };
+ /* we enlarge this bucket; and put new data in front */
+ newt = ALLOCPAIRS(z, bucket->count+1);
+ if (bucket->count) bcopy ((const char*)bucket->elements.many, (char*)(newt+1), bucket->count * PTRSIZE);
+ *newt = data;
+ FREEPAIRS (bucket->elements.many);
+ bucket->count++; bucket->elements.many = newt;
+ table->count++;
+ if (table->count > table->nbBuckets) _NXHashRehash (table);
+ return (void *) data;
+ }
+
+void *NXHashRemove (NXHashTable *table, const void *data) {
+ HashBucket *bucket = BUCKETOF(table, data);
+ unsigned j = bucket->count;
+ const void **pairs;
+ const void **newt;
+ void *z = ZONE_FROM_PTR(table);
+
+ if (! j) return NULL;
+ if (j == 1) {
+ if (! ISEQUAL(table, data, bucket->elements.one)) return NULL;
+ data = bucket->elements.one;
+ table->count--; bucket->count--; bucket->elements.one = NULL;
+ return (void *) data;
+ };
+ pairs = bucket->elements.many;
+ if (j == 2) {
+ if (ISEQUAL(table, data, pairs[0])) {
+ bucket->elements.one = pairs[1]; data = pairs[0];
+ }
+ else if (ISEQUAL(table, data, pairs[1])) {
+ bucket->elements.one = pairs[0]; data = pairs[1];
+ }
+ else return NULL;
+ FREEPAIRS (pairs);
+ table->count--; bucket->count--;
+ return (void *) data;
+ };
+ while (j--) {
+ if (ISEQUAL(table, data, *pairs)) {
+ data = *pairs;
+ /* we shrink this bucket */
+ newt = (bucket->count-1)
+ ? ALLOCPAIRS(z, bucket->count-1) : NULL;
+ if (bucket->count-1 != j)
+ 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);
+ FREEPAIRS (bucket->elements.many);
+ table->count--; bucket->count--; bucket->elements.many = newt;
+ return (void *) data;
+ };
+ pairs ++;
+ };
+ return NULL;
+ }
+
+NXHashState NXInitHashState (NXHashTable *table) {
+ NXHashState state;
+
+ state.i = table->nbBuckets;
+ state.j = 0;
+ return state;
+ };
+
+int NXNextHashState (NXHashTable *table, NXHashState *state, void **data) {
+ HashBucket *buckets = (HashBucket *) table->buckets;
+
+ while (state->j == 0) {
+ if (state->i == 0) return NO;
+ state->i--; state->j = buckets[state->i].count;
+ }
+ state->j--;
+ buckets += state->i;
+ *data = (void *) ((buckets->count == 1)
+ ? buckets->elements.one : buckets->elements.many[state->j]);
+ return YES;
+ };
+
+/*************************************************************************
+ *
+ * Conveniences
+ *
+ *************************************************************************/
+
+uintptr_t NXPtrHash (const void *info, const void *data) {
+ return (((uintptr_t) data) >> 16) ^ ((uintptr_t) data);
+ };
+
+uintptr_t NXStrHash (const void *info, const void *data) {
+ register uintptr_t hash = 0;
+ register unsigned char *s = (unsigned char *) data;
+ /* unsigned to avoid a sign-extend */
+ /* unroll the loop */
+ if (s) for (; ; ) {
+ if (*s == '\0') break;
+ hash ^= (uintptr_t) *s++;
+ if (*s == '\0') break;
+ hash ^= (uintptr_t) *s++ << 8;
+ if (*s == '\0') break;
+ hash ^= (uintptr_t) *s++ << 16;
+ if (*s == '\0') break;
+ hash ^= (uintptr_t) *s++ << 24;
+ }
+ return hash;
+ };
+
+int NXStrIsEqual (const void *info, const void *data1, const void *data2) {
+ if (data1 == data2) return YES;
+ if (! data1) return ! strlen ((char *) data2);
+ if (! data2) return ! strlen ((char *) data1);
+ if (((char *) data1)[0] != ((char *) data2)[0]) return NO;
+ return (strcmp ((char *) data1, (char *) data2)) ? NO : YES;
+ };
+
+void NXReallyFree (const void *info, void *data) {
+ free (data);
+ };
+
+/* All the following functions are really private, made non-static only for the benefit of shlibs */
+static uintptr_t hashPtrStructKey (const void *info, const void *data) {
+ return NXPtrHash(info, *((void **) data));
+ };
+
+static int isEqualPtrStructKey (const void *info, const void *data1, const void *data2) {
+ return NXPtrIsEqual (info, *((void **) data1), *((void **) data2));
+ };
+
+static uintptr_t hashStrStructKey (const void *info, const void *data) {
+ return NXStrHash(info, *((char **) data));
+ };
+
+static int isEqualStrStructKey (const void *info, const void *data1, const void *data2) {
+ return NXStrIsEqual (info, *((char **) data1), *((char **) data2));
+ };
+
+const NXHashTablePrototype NXPtrPrototype = {
+ NXPtrHash, NXPtrIsEqual, NXNoEffectFree, 0
+ };
+
+const NXHashTablePrototype NXStrPrototype = {
+ NXStrHash, NXStrIsEqual, NXNoEffectFree, 0
+ };
+
+const NXHashTablePrototype NXPtrStructKeyPrototype = {
+ hashPtrStructKey, isEqualPtrStructKey, NXReallyFree, 0
+ };
+
+const NXHashTablePrototype NXStrStructKeyPrototype = {
+ hashStrStructKey, isEqualStrStructKey, NXReallyFree, 0
+ };
+
+/*************************************************************************
+ *
+ * Unique strings
+ *
+ *************************************************************************/
+
+#if !__OBJC2__ && !TARGET_OS_WIN32
+
+/* the implementation could be made faster at the expense of memory if the size of the strings were kept around */
+static NXHashTable *uniqueStrings = NULL;
+
+/* this is based on most apps using a few K of strings, and an average string size of 15 using sqrt(2*dataAlloced*perChunkOverhead) */
+#define CHUNK_SIZE 360
+
+static int accessUniqueString = 0;
+
+static char *z = NULL;
+static size_t zSize = 0;
+static mutex_t lock = MUTEX_INITIALIZER;
+
+static const char *CopyIntoReadOnly (const char *str) {
+ size_t len = strlen (str) + 1;
+ char *result;
+
+ if (len > CHUNK_SIZE/2) { /* dont let big strings waste space */
+ 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 = (char *)malloc (zSize);
+ };
+
+ result = z;
+ bcopy (str, result, len);
+ z += len;
+ zSize -= len;
+ mutex_unlock (&lock);
+ return result;
+ };
+
+NXAtom NXUniqueString (const char *buffer) {
+ const char *previous;
+
+ if (! buffer) return buffer;
+ accessUniqueString++;
+ if (! uniqueStrings)
+ uniqueStrings = NXCreateHashTable (NXStrPrototype, 0, NULL);
+ previous = (const char *) NXHashGet (uniqueStrings, buffer);
+ if (previous) return previous;
+ previous = CopyIntoReadOnly (buffer);
+ if (NXHashInsert (uniqueStrings, previous)) {
+ _objc_inform ("*** NXUniqueString: invariant broken\n");
+ return NULL;
+ };
+ return previous;
+ };
+
+NXAtom NXUniqueStringNoCopy (const char *string) {
+ accessUniqueString++;
+ if (! uniqueStrings)
+ uniqueStrings = NXCreateHashTable (NXStrPrototype, 0, NULL);
+ return (const char *) NXHashInsertIfAbsent (uniqueStrings, string);
+ };
+
+#define BUF_SIZE 256
+
+NXAtom NXUniqueStringWithLength (const char *buffer, int length) {
+ NXAtom atom;
+ char *nullTermStr;
+ char stackBuf[BUF_SIZE];
+
+ if (length+1 > BUF_SIZE)
+ nullTermStr = (char *)malloc (length+1);
+ else
+ nullTermStr = stackBuf;
+ bcopy (buffer, nullTermStr, length);
+ nullTermStr[length] = '\0';
+ atom = NXUniqueString (nullTermStr);
+ if (length+1 > BUF_SIZE)
+ free (nullTermStr);
+ return atom;
+ };
+
+char *NXCopyStringBufferFromZone (const char *str, void *zone) {
+#if !SUPPORT_ZONES
+ return strdup(str);
+#else
+ return strcpy ((char *) malloc_zone_malloc((malloc_zone_t *)zone, strlen (str) + 1), str);
+#endif
+ };
+
+char *NXCopyStringBuffer (const char *str) {
+ return strdup(str);
+ };
+
+#endif
#include <cstring>
#include <TargetConditionals.h>
-
namespace objc {
#if TARGET_OS_IPHONE
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.
!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)) {
// 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; i<NumBuckets; i++) {
+ if (KeyInfoT::isEqual(BucketsPtr->first, 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) {
+++ /dev/null
-/*
- * Copyright (c) 1999-2003, 2005-2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-/* maptable.m
- Copyright 1990-1996 NeXT Software, Inc.
- Created by Bertrand Serlet, August 1990
- */
-
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "objc-private.h"
-#include "maptable.h"
-#include "hashtable2.h"
-
-
-/****** Macros and utilities ****************************/
-
-#if defined(DEBUG)
- #define INLINE
-#else
- #define INLINE inline
-#endif
-
-typedef struct _MapPair {
- const void *key;
- const void *value;
-} MapPair;
-
-static unsigned log2u(unsigned x) { return (x<2) ? 0 : log2u(x>>1)+1; };
-
-static INLINE unsigned exp2u(unsigned x) { return (1 << x); };
-
-static INLINE unsigned xorHash(unsigned hash) {
- unsigned xored = (hash & 0xffff) ^ (hash >> 16);
- return ((xored * 65521) + hash);
-}
-
-static INLINE unsigned bucketOf(NXMapTable *table, const void *key) {
- unsigned hash = (table->prototype->hash)(table, key);
- return hash & table->nbBucketsMinusOne;
-}
-
-static INLINE int isEqual(NXMapTable *table, const void *key1, const void *key2) {
- return (key1 == key2) ? 1 : (table->prototype->isEqual)(table, key1, key2);
-}
-
-static INLINE unsigned nextIndex(NXMapTable *table, unsigned index) {
- return (index + 1) & table->nbBucketsMinusOne;
-}
-
-static INLINE void *allocBuckets(void *z, unsigned nb) {
- MapPair *pairs = malloc_zone_malloc(z, (nb * sizeof(MapPair)));
- MapPair *pair = pairs;
- while (nb--) { pair->key = NX_MAPNOTAKEY; pair->value = NULL; pair++; }
- return pairs;
-}
-
-/***** Global data and bootstrap **********************/
-
-static int isEqualPrototype (const void *info, const void *data1, const void *data2) {
- NXHashTablePrototype *proto1 = (NXHashTablePrototype *) data1;
- NXHashTablePrototype *proto2 = (NXHashTablePrototype *) data2;
-
- return (proto1->hash == proto2->hash) && (proto1->isEqual == proto2->isEqual) && (proto1->free == proto2->free) && (proto1->style == proto2->style);
- };
-
-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;
- };
-
-static NXHashTablePrototype protoPrototype = {
- hashPrototype, isEqualPrototype, NXNoEffectFree, 0
-};
-
-static NXHashTable *prototypes = NULL;
- /* table of all prototypes */
-
-/**** Fundamentals Operations **************/
-
-NXMapTable *NXCreateMapTableFromZone(NXMapTablePrototype prototype, unsigned capacity, void *z) {
- NXMapTable *table = malloc_zone_malloc(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);
- if (! proto) {
- proto = malloc(sizeof(NXMapTablePrototype));
- *proto = prototype;
- (void)NXHashInsert(prototypes, proto);
- }
- table->prototype = proto; table->count = 0;
- table->nbBucketsMinusOne = exp2u(log2u(capacity)+1) - 1;
- table->buckets = allocBuckets(z, table->nbBucketsMinusOne + 1);
- return table;
-}
-
-NXMapTable *NXCreateMapTable(NXMapTablePrototype prototype, unsigned capacity) {
- return NXCreateMapTableFromZone(prototype, capacity, malloc_default_zone());
-}
-
-void NXFreeMapTable(NXMapTable *table) {
- NXResetMapTable(table);
- free(table->buckets);
- free(table);
-}
-
-void NXResetMapTable(NXMapTable *table) {
- MapPair *pairs = table->buckets;
- void (*freeProc)(struct _NXMapTable *, void *, void *) = table->prototype->free;
- unsigned index = table->nbBucketsMinusOne + 1;
- while (index--) {
- if (pairs->key != NX_MAPNOTAKEY) {
- freeProc(table, (void *)pairs->key, (void *)pairs->value);
- pairs->key = NX_MAPNOTAKEY; pairs->value = NULL;
- }
- pairs++;
- }
- table->count = 0;
-}
-
-BOOL NXCompareMapTables(NXMapTable *table1, NXMapTable *table2) {
- if (table1 == table2) return YES;
- if (table1->count != table2->count) return NO;
- else {
- const void *key;
- const void *value;
- NXMapState state = NXInitMapState(table1);
- while (NXNextMapState(table1, &state, &key, &value)) {
- if (NXMapMember(table2, key, (void**)&value) == NX_MAPNOTAKEY) return NO;
- }
- return YES;
- }
-}
-
-unsigned NXCountMapTable(NXMapTable *table) { return table->count; }
-
-static INLINE void *_NXMapMember(NXMapTable *table, const void *key, void **value) {
- MapPair *pairs = table->buckets;
- unsigned index = bucketOf(table, key);
- MapPair *pair = pairs + index;
- if (pair->key == NX_MAPNOTAKEY) return NX_MAPNOTAKEY;
- if (isEqual(table, pair->key, key)) {
- *value = (void *)pair->value;
- return (void *)pair->key;
- } else {
- unsigned index2 = index;
- while ((index2 = nextIndex(table, index2)) != index) {
- pair = pairs + index2;
- if (pair->key == NX_MAPNOTAKEY) return NX_MAPNOTAKEY;
- if (isEqual(table, pair->key, key)) {
- *value = (void *)pair->value;
- return (void *)pair->key;
- }
- }
- return NX_MAPNOTAKEY;
- }
-}
-
-void *NXMapMember(NXMapTable *table, const void *key, void **value) {
- return _NXMapMember(table, key, value);
-}
-
-void *NXMapGet(NXMapTable *table, const void *key) {
- void *value;
- return (_NXMapMember(table, key, &value) != NX_MAPNOTAKEY) ? value : NULL;
-}
-
-static void _NXMapRehash(NXMapTable *table) {
- MapPair *pairs = table->buckets;
- MapPair *pair = pairs;
- unsigned numBuckets = table->nbBucketsMinusOne + 1;
- unsigned index = numBuckets;
- unsigned oldCount = table->count;
-
- table->nbBucketsMinusOne = 2 * numBuckets - 1;
- table->count = 0;
- table->buckets = allocBuckets(malloc_zone_from_ptr(table), table->nbBucketsMinusOne + 1);
- while (index--) {
- if (pair->key != NX_MAPNOTAKEY) {
- (void)NXMapInsert(table, pair->key, pair->value);
- }
- pair++;
- }
- 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);
-}
-
-void *NXMapInsert(NXMapTable *table, const void *key, const void *value) {
- MapPair *pairs = table->buckets;
- unsigned index = bucketOf(table, key);
- MapPair *pair = pairs + index;
- if (key == NX_MAPNOTAKEY) {
- _objc_inform("*** NXMapInsert: invalid key: -1\n");
- return NULL;
- }
-
- unsigned numBuckets = table->nbBucketsMinusOne + 1;
-
- if (pair->key == NX_MAPNOTAKEY) {
- pair->key = key; pair->value = value;
- table->count++;
- if (table->count * 4 > numBuckets * 3) _NXMapRehash(table);
- return NULL;
- }
-
- if (isEqual(table, pair->key, key)) {
- const void *old = pair->value;
- if (old != value) pair->value = value;/* avoid writing unless needed! */
- return (void *)old;
- } else if (table->count == numBuckets) {
- /* no room: rehash and retry */
- _NXMapRehash(table);
- return NXMapInsert(table, key, value);
- } else {
- unsigned index2 = index;
- while ((index2 = nextIndex(table, index2)) != index) {
- pair = pairs + index2;
- if (pair->key == NX_MAPNOTAKEY) {
- pair->key = key; pair->value = value;
- table->count++;
- if (table->count * 4 > numBuckets * 3) _NXMapRehash(table);
- return NULL;
- }
- if (isEqual(table, pair->key, key)) {
- const void *old = pair->value;
- if (old != value) pair->value = value;/* avoid writing unless needed! */
- return (void *)old;
- }
- }
- /* no room: can't happen! */
- _objc_inform("**** NXMapInsert: bug\n");
- return NULL;
- }
-}
-
-static int mapRemove = 0;
-
-void *NXMapRemove(NXMapTable *table, const void *key) {
- MapPair *pairs = table->buckets;
- unsigned index = bucketOf(table, key);
- MapPair *pair = pairs + index;
- unsigned chain = 1; /* number of non-nil pairs in a row */
- int found = 0;
- const void *old = NULL;
- if (pair->key == NX_MAPNOTAKEY) return NULL;
- mapRemove ++;
- /* compute chain */
- {
- unsigned index2 = index;
- if (isEqual(table, pair->key, key)) {found ++; old = pair->value; }
- while ((index2 = nextIndex(table, index2)) != index) {
- pair = pairs + index2;
- if (pair->key == NX_MAPNOTAKEY) break;
- if (isEqual(table, pair->key, key)) {found ++; old = pair->value; }
- chain++;
- }
- }
- if (! found) return NULL;
- if (found != 1) _objc_inform("**** NXMapRemove: incorrect table\n");
- /* remove then reinsert */
- {
- MapPair buffer[16];
- MapPair *aux = (chain > 16) ? malloc(sizeof(MapPair)*(chain-1)) : buffer;
- int auxnb = 0;
- int nb = chain;
- unsigned index2 = index;
- while (nb--) {
- pair = pairs + index2;
- if (! isEqual(table, pair->key, key)) aux[auxnb++] = *pair;
- pair->key = NX_MAPNOTAKEY; pair->value = NULL;
- index2 = nextIndex(table, index2);
- }
- table->count -= chain;
- if (auxnb != chain-1) _objc_inform("**** NXMapRemove: bug\n");
- while (auxnb--) NXMapInsert(table, aux[auxnb].key, aux[auxnb].value);
- if (chain > 16) free(aux);
- }
- return (void *)old;
-}
-
-NXMapState NXInitMapState(NXMapTable *table) {
- NXMapState state;
- state.index = table->nbBucketsMinusOne + 1;
- return state;
-}
-
-int NXNextMapState(NXMapTable *table, NXMapState *state, const void **key, const void **value) {
- MapPair *pairs = table->buckets;
- while (state->index--) {
- MapPair *pair = pairs + state->index;
- if (pair->key != NX_MAPNOTAKEY) {
- *key = pair->key; *value = pair->value;
- return YES;
- }
- }
- return NO;
-}
-
-
-/***********************************************************************
-* NXMapKeyCopyingInsert
-* 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 *realKey;
- void *realValue = NULL;
-
- if ((realKey = NXMapMember(table, key, &realValue)) != NX_MAPNOTAKEY) {
- // 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);
- }
- return NXMapInsert(table, realKey, value);
-}
-
-
-/***********************************************************************
-* NXMapKeyFreeingRemove
-* 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 *realKey;
- void *realValue = NULL;
-
- if ((realKey = NXMapMember(table, key, &realValue)) != NX_MAPNOTAKEY) {
- // key DOES exist in table - remove pair and free key
- realValue = NXMapRemove(table, realKey);
- _free_internal(realKey); // the key from the table, not necessarily the one given
- return realValue;
- } else {
- // key DOES NOT exist in table - nothing to do
- return NULL;
- }
-}
-
-
-/**** Conveniences *************************************/
-
-static unsigned _mapPtrHash(NXMapTable *table, const void *key) {
-#ifdef __LP64__
- return ((uintptr_t)key) >> 3;
-#else
- return ((uintptr_t)key) >> 2;
-#endif
-}
-
-static unsigned _mapStrHash(NXMapTable *table, const void *key) {
- unsigned hash = 0;
- unsigned char *s = (unsigned char *)key;
- /* unsigned to avoid a sign-extend */
- /* unroll the loop */
- if (s) for (; ; ) {
- if (*s == '\0') break;
- hash ^= *s++;
- if (*s == '\0') break;
- hash ^= *s++ << 8;
- if (*s == '\0') break;
- hash ^= *s++ << 16;
- if (*s == '\0') break;
- hash ^= *s++ << 24;
- }
- return xorHash(hash);
-}
-
-static int _mapPtrIsEqual(NXMapTable *table, const void *key1, const void *key2) {
- return key1 == key2;
-}
-
-static int _mapStrIsEqual(NXMapTable *table, const void *key1, const void *key2) {
- if (key1 == key2) return YES;
- if (! key1) return ! strlen ((char *) key2);
- if (! key2) return ! strlen ((char *) key1);
- if (((char *) key1)[0] != ((char *) key2)[0]) return NO;
- return (strcmp((char *) key1, (char *) key2)) ? NO : YES;
-}
-
-static void _mapNoFree(NXMapTable *table, void *key, void *value) {}
-
-const NXMapTablePrototype NXPtrValueMapPrototype = {
- _mapPtrHash, _mapPtrIsEqual, _mapNoFree, 0
-};
-
-const NXMapTablePrototype NXStrValueMapPrototype = {
- _mapStrHash, _mapStrIsEqual, _mapNoFree, 0
-};
-
-
-#if !__OBJC2__ && !TARGET_OS_WIN32
-
-/* This only works with class Object, which is unavailable. */
-
-/* Method prototypes */
-@interface DoesNotExist
-+ class;
-+ initialize;
-- (id)description;
-- (const char *)UTF8String;
-- (unsigned long)hash;
-- (BOOL)isEqual:(id)object;
-- free;
-@end
-
-static unsigned _mapObjectHash(NXMapTable *table, const void *key) {
- return [(id)key hash];
-}
-
-static int _mapObjectIsEqual(NXMapTable *table, const void *key1, const void *key2) {
- return [(id)key1 isEqual:(id)key2];
-}
-
-static void _mapObjectFree(NXMapTable *table, void *key, void *value) {
- [(id)key free];
-}
-
-const NXMapTablePrototype NXObjectMapPrototype = {
- _mapObjectHash, _mapObjectIsEqual, _mapObjectFree, 0
-};
-
-#endif
--- /dev/null
+/*
+ * Copyright (c) 1999-2003, 2005-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/* maptable.m
+ Copyright 1990-1996 NeXT Software, Inc.
+ Created by Bertrand Serlet, August 1990
+ */
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "objc-private.h"
+#include "maptable.h"
+#include "hashtable2.h"
+
+
+/****** Macros and utilities ****************************/
+
+#if defined(DEBUG)
+ #define INLINE
+#else
+ #define INLINE inline
+#endif
+
+typedef struct _MapPair {
+ const void *key;
+ const void *value;
+} MapPair;
+
+static unsigned log2u(unsigned x) { return (x<2) ? 0 : log2u(x>>1)+1; };
+
+static INLINE unsigned exp2u(unsigned x) { return (1 << x); };
+
+static INLINE unsigned xorHash(unsigned hash) {
+ unsigned xored = (hash & 0xffff) ^ (hash >> 16);
+ return ((xored * 65521) + hash);
+}
+
+static INLINE unsigned bucketOf(NXMapTable *table, const void *key) {
+ unsigned hash = (table->prototype->hash)(table, key);
+ return hash & table->nbBucketsMinusOne;
+}
+
+static INLINE int isEqual(NXMapTable *table, const void *key1, const void *key2) {
+ return (key1 == key2) ? 1 : (table->prototype->isEqual)(table, key1, key2);
+}
+
+static INLINE unsigned nextIndex(NXMapTable *table, unsigned index) {
+ return (index + 1) & table->nbBucketsMinusOne;
+}
+
+static INLINE void *allocBuckets(void *z, unsigned nb) {
+ 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) {
+ NXHashTablePrototype *proto1 = (NXHashTablePrototype *) data1;
+ NXHashTablePrototype *proto2 = (NXHashTablePrototype *) data2;
+
+ return (proto1->hash == proto2->hash) && (proto1->isEqual == proto2->isEqual) && (proto1->free == proto2->free) && (proto1->style == proto2->style);
+ };
+
+static uintptr_t hashPrototype (const void *info, const void *data) {
+ NXHashTablePrototype *proto = (NXHashTablePrototype *) data;
+
+ return NXPtrHash(info, (void*)proto->hash) ^ NXPtrHash(info, (void*)proto->isEqual) ^ NXPtrHash(info, (void*)proto->free) ^ (uintptr_t) proto->style;
+ };
+
+static NXHashTablePrototype protoPrototype = {
+ hashPrototype, isEqualPrototype, NXNoEffectFree, 0
+};
+
+static NXHashTable *prototypes = NULL;
+ /* table of all prototypes */
+
+/**** Fundamentals Operations **************/
+
+NXMapTable *NXCreateMapTableFromZone(NXMapTablePrototype prototype, unsigned capacity, void *z) {
+ 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 = (NXMapTablePrototype *)NXHashGet(prototypes, &prototype);
+ if (! proto) {
+ proto = (NXMapTablePrototype *)malloc(sizeof(NXMapTablePrototype));
+ *proto = prototype;
+ (void)NXHashInsert(prototypes, proto);
+ }
+ table->prototype = proto; table->count = 0;
+ table->nbBucketsMinusOne = exp2u(log2u(capacity)+1) - 1;
+ table->buckets = allocBuckets(z, table->nbBucketsMinusOne + 1);
+ return table;
+}
+
+NXMapTable *NXCreateMapTable(NXMapTablePrototype prototype, unsigned capacity) {
+ return NXCreateMapTableFromZone(prototype, capacity, malloc_default_zone());
+}
+
+void NXFreeMapTable(NXMapTable *table) {
+ NXResetMapTable(table);
+ freeBuckets(table->buckets);
+ free(table);
+}
+
+void NXResetMapTable(NXMapTable *table) {
+ MapPair *pairs = (MapPair *)table->buckets;
+ void (*freeProc)(struct _NXMapTable *, void *, void *) = table->prototype->free;
+ unsigned index = table->nbBucketsMinusOne + 1;
+ while (index--) {
+ if (pairs->key != NX_MAPNOTAKEY) {
+ freeProc(table, (void *)pairs->key, (void *)pairs->value);
+ pairs->key = NX_MAPNOTAKEY; pairs->value = NULL;
+ }
+ pairs++;
+ }
+ table->count = 0;
+}
+
+BOOL NXCompareMapTables(NXMapTable *table1, NXMapTable *table2) {
+ if (table1 == table2) return YES;
+ if (table1->count != table2->count) return NO;
+ else {
+ const void *key;
+ const void *value;
+ NXMapState state = NXInitMapState(table1);
+ while (NXNextMapState(table1, &state, &key, &value)) {
+ if (NXMapMember(table2, key, (void**)&value) == NX_MAPNOTAKEY) return NO;
+ }
+ return YES;
+ }
+}
+
+unsigned NXCountMapTable(NXMapTable *table) { return table->count; }
+
+static INLINE void *_NXMapMember(NXMapTable *table, const void *key, void **value) {
+ MapPair *pairs = (MapPair *)table->buckets;
+ unsigned index = bucketOf(table, key);
+ MapPair *pair = pairs + index;
+ if (pair->key == NX_MAPNOTAKEY) return NX_MAPNOTAKEY;
+ if (isEqual(table, pair->key, key)) {
+ *value = (void *)pair->value;
+ return (void *)pair->key;
+ } else {
+ unsigned index2 = index;
+ while ((index2 = nextIndex(table, index2)) != index) {
+ pair = pairs + index2;
+ if (pair->key == NX_MAPNOTAKEY) return NX_MAPNOTAKEY;
+ if (isEqual(table, pair->key, key)) {
+ *value = (void *)pair->value;
+ return (void *)pair->key;
+ }
+ }
+ return NX_MAPNOTAKEY;
+ }
+}
+
+void *NXMapMember(NXMapTable *table, const void *key, void **value) {
+ return _NXMapMember(table, key, value);
+}
+
+void *NXMapGet(NXMapTable *table, const void *key) {
+ void *value;
+ return (_NXMapMember(table, key, &value) != NX_MAPNOTAKEY) ? value : NULL;
+}
+
+static void _NXMapRehash(NXMapTable *table) {
+ MapPair *pairs = (MapPair *)table->buckets;
+ MapPair *pair = pairs;
+ unsigned numBuckets = table->nbBucketsMinusOne + 1;
+ unsigned index = numBuckets;
+ unsigned oldCount = table->count;
+
+ table->nbBucketsMinusOne = 2 * numBuckets - 1;
+ table->count = 0;
+ table->buckets = allocBuckets(malloc_zone_from_ptr(table), table->nbBucketsMinusOne + 1);
+ while (index--) {
+ if (pair->key != NX_MAPNOTAKEY) {
+ (void)NXMapInsert(table, pair->key, pair->value);
+ }
+ pair++;
+ }
+ 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");
+ freeBuckets(pairs);
+}
+
+void *NXMapInsert(NXMapTable *table, const void *key, const void *value) {
+ MapPair *pairs = (MapPair *)table->buckets;
+ unsigned index = bucketOf(table, key);
+ MapPair *pair = pairs + index;
+ if (key == NX_MAPNOTAKEY) {
+ _objc_inform("*** NXMapInsert: invalid key: -1\n");
+ return NULL;
+ }
+
+ unsigned numBuckets = table->nbBucketsMinusOne + 1;
+
+ if (pair->key == NX_MAPNOTAKEY) {
+ pair->key = key; pair->value = value;
+ table->count++;
+ if (table->count * 4 > numBuckets * 3) _NXMapRehash(table);
+ return NULL;
+ }
+
+ if (isEqual(table, pair->key, key)) {
+ const void *old = pair->value;
+ if (old != value) pair->value = value;/* avoid writing unless needed! */
+ return (void *)old;
+ } else if (table->count == numBuckets) {
+ /* no room: rehash and retry */
+ _NXMapRehash(table);
+ return NXMapInsert(table, key, value);
+ } else {
+ unsigned index2 = index;
+ while ((index2 = nextIndex(table, index2)) != index) {
+ pair = pairs + index2;
+ if (pair->key == NX_MAPNOTAKEY) {
+ pair->key = key; pair->value = value;
+ table->count++;
+ if (table->count * 4 > numBuckets * 3) _NXMapRehash(table);
+ return NULL;
+ }
+ if (isEqual(table, pair->key, key)) {
+ const void *old = pair->value;
+ if (old != value) pair->value = value;/* avoid writing unless needed! */
+ return (void *)old;
+ }
+ }
+ /* no room: can't happen! */
+ _objc_inform("**** NXMapInsert: bug\n");
+ return NULL;
+ }
+}
+
+static int mapRemove = 0;
+
+void *NXMapRemove(NXMapTable *table, const void *key) {
+ 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 */
+ int found = 0;
+ const void *old = NULL;
+ if (pair->key == NX_MAPNOTAKEY) return NULL;
+ mapRemove ++;
+ /* compute chain */
+ {
+ unsigned index2 = index;
+ if (isEqual(table, pair->key, key)) {found ++; old = pair->value; }
+ while ((index2 = nextIndex(table, index2)) != index) {
+ pair = pairs + index2;
+ if (pair->key == NX_MAPNOTAKEY) break;
+ if (isEqual(table, pair->key, key)) {found ++; old = pair->value; }
+ chain++;
+ }
+ }
+ if (! found) return NULL;
+ if (found != 1) _objc_inform("**** NXMapRemove: incorrect table\n");
+ /* remove then reinsert */
+ {
+ MapPair buffer[16];
+ MapPair *aux = (chain > 16) ? (MapPair *)malloc(sizeof(MapPair)*(chain-1)) : buffer;
+ unsigned auxnb = 0;
+ int nb = chain;
+ unsigned index2 = index;
+ while (nb--) {
+ pair = pairs + index2;
+ if (! isEqual(table, pair->key, key)) aux[auxnb++] = *pair;
+ pair->key = NX_MAPNOTAKEY; pair->value = NULL;
+ index2 = nextIndex(table, index2);
+ }
+ table->count -= chain;
+ if (auxnb != chain-1) _objc_inform("**** NXMapRemove: bug\n");
+ while (auxnb--) NXMapInsert(table, aux[auxnb].key, aux[auxnb].value);
+ if (chain > 16) free(aux);
+ }
+ return (void *)old;
+}
+
+NXMapState NXInitMapState(NXMapTable *table) {
+ NXMapState state;
+ state.index = table->nbBucketsMinusOne + 1;
+ return state;
+}
+
+int NXNextMapState(NXMapTable *table, NXMapState *state, const void **key, const void **value) {
+ MapPair *pairs = (MapPair *)table->buckets;
+ while (state->index--) {
+ MapPair *pair = pairs + state->index;
+ if (pair->key != NX_MAPNOTAKEY) {
+ *key = pair->key; *value = pair->value;
+ return YES;
+ }
+ }
+ return NO;
+}
+
+
+/***********************************************************************
+* NXMapKeyCopyingInsert
+* Like NXMapInsert, but strdups the key if necessary.
+* Used to prevent stale pointers when bundles are unloaded.
+**********************************************************************/
+void *NXMapKeyCopyingInsert(NXMapTable *table, const void *key, const void *value)
+{
+ void *realKey;
+ void *realValue = NULL;
+
+ if ((realKey = NXMapMember(table, key, &realValue)) != NX_MAPNOTAKEY) {
+ // 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 = (void *)_strdup_internal((char *)key);
+ }
+ return NXMapInsert(table, realKey, value);
+}
+
+
+/***********************************************************************
+* NXMapKeyFreeingRemove
+* Like NXMapRemove, but frees the existing key if necessary.
+* Used to prevent stale pointers when bundles are unloaded.
+**********************************************************************/
+void *NXMapKeyFreeingRemove(NXMapTable *table, const void *key)
+{
+ void *realKey;
+ void *realValue = NULL;
+
+ if ((realKey = NXMapMember(table, key, &realValue)) != NX_MAPNOTAKEY) {
+ // key DOES exist in table - remove pair and free key
+ realValue = NXMapRemove(table, realKey);
+ _free_internal(realKey); // the key from the table, not necessarily the one given
+ return realValue;
+ } else {
+ // key DOES NOT exist in table - nothing to do
+ return NULL;
+ }
+}
+
+
+/**** Conveniences *************************************/
+
+static unsigned _mapPtrHash(NXMapTable *table, const void *key) {
+#ifdef __LP64__
+ return ((uintptr_t)key) >> 3;
+#else
+ return ((uintptr_t)key) >> 2;
+#endif
+}
+
+static unsigned _mapStrHash(NXMapTable *table, const void *key) {
+ unsigned hash = 0;
+ unsigned char *s = (unsigned char *)key;
+ /* unsigned to avoid a sign-extend */
+ /* unroll the loop */
+ if (s) for (; ; ) {
+ if (*s == '\0') break;
+ hash ^= *s++;
+ if (*s == '\0') break;
+ hash ^= *s++ << 8;
+ if (*s == '\0') break;
+ hash ^= *s++ << 16;
+ if (*s == '\0') break;
+ hash ^= *s++ << 24;
+ }
+ return xorHash(hash);
+}
+
+static int _mapPtrIsEqual(NXMapTable *table, const void *key1, const void *key2) {
+ return key1 == key2;
+}
+
+static int _mapStrIsEqual(NXMapTable *table, const void *key1, const void *key2) {
+ if (key1 == key2) return YES;
+ if (! key1) return ! strlen ((char *) key2);
+ if (! key2) return ! strlen ((char *) key1);
+ if (((char *) key1)[0] != ((char *) key2)[0]) return NO;
+ return (strcmp((char *) key1, (char *) key2)) ? NO : YES;
+}
+
+static void _mapNoFree(NXMapTable *table, void *key, void *value) {}
+
+const NXMapTablePrototype NXPtrValueMapPrototype = {
+ _mapPtrHash, _mapPtrIsEqual, _mapNoFree, 0
+};
+
+const NXMapTablePrototype NXStrValueMapPrototype = {
+ _mapStrHash, _mapStrIsEqual, _mapNoFree, 0
+};
+
+
+#if !__OBJC2__ && !TARGET_OS_WIN32
+
+/* This only works with class Object, which is unavailable. */
+
+/* Method prototypes */
+@interface DoesNotExist
++ (id)class;
++ (id)initialize;
+- (id)description;
+- (const char *)UTF8String;
+- (unsigned long)hash;
+- (BOOL)isEqual:(id)object;
+- (void)free;
+@end
+
+static unsigned _mapObjectHash(NXMapTable *table, const void *key) {
+ return [(id)key hash];
+}
+
+static int _mapObjectIsEqual(NXMapTable *table, const void *key1, const void *key2) {
+ return [(id)key1 isEqual:(id)key2];
+}
+
+static void _mapObjectFree(NXMapTable *table, void *key, void *value) {
+ [(id)key free];
+}
+
+const NXMapTablePrototype NXObjectMapPrototype = {
+ _mapObjectHash, _mapObjectIsEqual, _mapObjectFree, 0
+};
+
+#endif
#ifndef _OBJC_MESSAGE_H
#define _OBJC_MESSAGE_H
+#pragma GCC system_header
+
#include <objc/objc.h>
#include <objc/runtime.h>
#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 */
};
* 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
* 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
* 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);
/* 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
* 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
* 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
// 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__
#ifndef _OBJC_OBJC_API_H_
#define _OBJC_OBJC_API_H_
+#include <Availability.h>
#include <AvailabilityMacros.h>
#include <TargetConditionals.h>
# 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__
/* 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"
+++ /dev/null
-/*
- * Copyright (c) 2010-2011 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include "llvm-DenseMap.h"
-
-#import "objc-weak.h"
-#import "objc-private.h"
-#import "objc-internal.h"
-#import "objc-os.h"
-#import "runtime.h"
-
-#include <stdint.h>
-#include <stdbool.h>
-//#include <fcntl.h>
-#include <mach/mach.h>
-#include <mach-o/dyld.h>
-#include <mach-o/nlist.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <libkern/OSAtomic.h>
-#include <Block.h>
-#include <map>
-#include <execinfo.h>
-
-#if SUPPORT_RETURN_AUTORELEASE
-// We cannot peek at where we are returning to unless we always inline this:
-__attribute__((always_inline))
-static bool callerAcceptsFastAutorelease(const void * const ra0);
-#endif
-
-
-/***********************************************************************
-* Weak ivar support
-**********************************************************************/
-
-static bool seen_weak_refs;
-
-@protocol ReferenceCounted
-+ (id)alloc;
-+ (id)allocWithZone:(malloc_zone_t *)zone;
-- (oneway void)dealloc;
-- (id)retain;
-- (oneway void)release;
-- (id)autorelease;
-- (uintptr_t)retainCount;
-@end
-
-#define ARR_LOGGING 0
-
-#if ARR_LOGGING
-struct {
- int retains;
- int releases;
- int autoreleases;
- int blockCopies;
-} CompilerGenerated, ExplicitlyCoded;
-
-PRIVATE_EXTERN void (^objc_arr_log)(const char *, id param) =
- ^(const char *str, id param) { printf("%s %p\n", str, param); };
-#endif
-
-
-namespace {
-
-#if TARGET_OS_EMBEDDED
-# define SIDE_TABLE_STRIPE 1
-#else
-# define SIDE_TABLE_STRIPE 8
-#endif
-
-// should be a multiple of cache line size (64)
-#define SIDE_TABLE_SIZE 64
-
-typedef objc::DenseMap<id,size_t,true> RefcountMap;
-
-class SideTable {
-private:
- static uint8_t table_buf[SIDE_TABLE_STRIPE * SIDE_TABLE_SIZE];
-
-public:
- OSSpinLock slock;
- RefcountMap refcnts;
- weak_table_t weak_table;
-
- SideTable() : slock(OS_SPINLOCK_INIT)
- {
- memset(&weak_table, 0, sizeof(weak_table));
- }
-
- ~SideTable()
- {
- // never delete side_table in case other threads retain during exit
- assert(0);
- }
-
- static SideTable *tableForPointer(const void *p)
- {
-# if SIDE_TABLE_STRIPE == 1
- return (SideTable *)table_buf;
-# else
- uintptr_t a = (uintptr_t)p;
- int index = ((a >> 4) ^ (a >> 9)) & (SIDE_TABLE_STRIPE - 1);
- return (SideTable *)&table_buf[index * SIDE_TABLE_SIZE];
-# endif
- }
-
- static void init() {
- // use placement new instead of static ctor to avoid dtor at exit
- for (int i = 0; i < SIDE_TABLE_STRIPE; i++) {
- new (&table_buf[i * SIDE_TABLE_SIZE]) SideTable;
- }
- }
-
- static bool noLocksHeld(void) {
- bool gotAll = true;
- for (int i = 0; i < SIDE_TABLE_STRIPE && gotAll; i++) {
- SideTable *s = (SideTable *)(&table_buf[i * SIDE_TABLE_SIZE]);
- if (OSSpinLockTry(&s->slock)) {
- OSSpinLockUnlock(&s->slock);
- } else {
- gotAll = false;
- }
- }
- return gotAll;
- }
-};
-
-STATIC_ASSERT(sizeof(SideTable) <= SIDE_TABLE_SIZE);
-__attribute__((aligned(SIDE_TABLE_SIZE))) uint8_t
-SideTable::table_buf[SIDE_TABLE_STRIPE * SIDE_TABLE_SIZE];
-
-// Avoid false-negative reports from tools like "leaks"
-#define DISGUISE(x) ((id)~(uintptr_t)(x))
-
-// anonymous namespace
-};
-
-bool noSideTableLocksHeld(void)
-{
- return SideTable::noLocksHeld();
-}
-
-//
-// The -fobjc-arr flag causes the compiler to issue calls to objc_{retain/release/autorelease/retain_block}
-//
-
-id objc_retainBlock(id x) {
-#if ARR_LOGGING
- objc_arr_log("objc_retain_block", x);
- ++CompilerGenerated.blockCopies;
-#endif
- return (id)_Block_copy(x);
-}
-
-//
-// The following SHOULD be called by the compiler directly, but the request hasn't been made yet :-)
-//
-
-BOOL objc_should_deallocate(id object) {
- return YES;
-}
-
-// WORKAROUND:
-// <rdar://problem/9038601> clang remembers variadic bit across function cast
-// <rdar://problem/9048030> Clang thinks that all ObjC vtable dispatches are variadic
-// <rdar://problem/8873428> vararg function defeats tail-call optimization
-id objc_msgSend_hack(id, SEL) asm("_objc_msgSend");
-
-// public API entry points that might be optimized later
-
-__attribute__((aligned(16)))
-id
-objc_retain(id obj)
-{
- return objc_msgSend_hack(obj, @selector(retain));
-}
-
-__attribute__((aligned(16)))
-void
-objc_release(id obj)
-{
- objc_msgSend_hack(obj, @selector(release));
-}
-
-__attribute__((aligned(16)))
-id
-objc_autorelease(id obj)
-{
- return objc_msgSend_hack(obj, @selector(autorelease));
-}
-
-id
-objc_retain_autorelease(id obj)
-{
- return objc_autorelease(objc_retain(obj));
-}
-
-id
-objc_storeWeak(id *location, id newObj)
-{
- id oldObj;
- SideTable *oldTable;
- SideTable *newTable;
- OSSpinLock *lock1;
-#if SIDE_TABLE_STRIPE > 1
- OSSpinLock *lock2;
-#endif
-
- if (!seen_weak_refs) {
- seen_weak_refs = true;
- }
-
- // Acquire locks for old and new values.
- // Order by lock address to prevent lock ordering problems.
- // Retry if the old value changes underneath us.
- retry:
- oldObj = *location;
-
- oldTable = SideTable::tableForPointer(oldObj);
- newTable = SideTable::tableForPointer(newObj);
-
- lock1 = &newTable->slock;
-#if SIDE_TABLE_STRIPE > 1
- lock2 = &oldTable->slock;
- if (lock1 > lock2) {
- OSSpinLock *temp = lock1;
- lock1 = lock2;
- lock2 = temp;
- }
- if (lock1 != lock2) OSSpinLockLock(lock2);
-#endif
- OSSpinLockLock(lock1);
-
- if (*location != oldObj) {
- OSSpinLockUnlock(lock1);
-#if SIDE_TABLE_STRIPE > 1
- if (lock1 != lock2) OSSpinLockUnlock(lock2);
-#endif
- goto retry;
- }
-
- if (oldObj) {
- weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
- }
- if (newObj) {
- newObj = weak_register_no_lock(&newTable->weak_table, newObj,location);
- // weak_register_no_lock returns NULL if weak store should be rejected
- }
- // Do not set *location anywhere else. That would introduce a race.
- *location = newObj;
-
- OSSpinLockUnlock(lock1);
-#if SIDE_TABLE_STRIPE > 1
- if (lock1 != lock2) OSSpinLockUnlock(lock2);
-#endif
-
- return newObj;
-}
-
-id
-objc_loadWeakRetained(id *location)
-{
- id result;
-
- SideTable *table;
- OSSpinLock *lock;
-
- retry:
- result = *location;
- if (!result) return NULL;
-
- table = SideTable::tableForPointer(result);
- lock = &table->slock;
-
- OSSpinLockLock(lock);
- if (*location != result) {
- OSSpinLockUnlock(lock);
- goto retry;
- }
-
- result = arr_read_weak_reference(&table->weak_table, location);
-
- OSSpinLockUnlock(lock);
- return result;
-}
-
-id
-objc_loadWeak(id *location)
-{
- return objc_autorelease(objc_loadWeakRetained(location));
-}
-
-id
-objc_initWeak(id *addr, id val)
-{
- *addr = 0;
- return objc_storeWeak(addr, val);
-}
-
-void
-objc_destroyWeak(id *addr)
-{
- objc_storeWeak(addr, 0);
-}
-
-void
-objc_copyWeak(id *to, id *from)
-{
- id val = objc_loadWeakRetained(from);
- objc_initWeak(to, val);
- objc_release(val);
-}
-
-void
-objc_moveWeak(id *to, id *from)
-{
- objc_copyWeak(to, from);
- objc_destroyWeak(from);
-}
-
-
-/* Autorelease pool implementation
- A thread's autorelease pool is a stack of pointers.
- Each pointer is either an object to release, or POOL_SENTINEL which is
- an autorelease pool boundary.
- A pool token is a pointer to the POOL_SENTINEL for that pool. When
- the pool is popped, every object hotter than the sentinel is released.
- The stack is divided into a doubly-linked list of pages. Pages are added
- and deleted as necessary.
- Thread-local storage points to the hot page, where newly autoreleased
- objects are stored.
- */
-
-extern "C" BREAKPOINT_FUNCTION(void objc_autoreleaseNoPool(id obj));
-
-namespace {
-
-struct magic_t {
- static const uint32_t M0 = 0xA1A1A1A1;
-# define M1 "AUTORELEASE!"
- static const size_t M1_len = 12;
- uint32_t m[4];
-
- magic_t() {
- assert(M1_len == strlen(M1));
- assert(M1_len == 3 * sizeof(m[1]));
-
- m[0] = M0;
- strncpy((char *)&m[1], M1, M1_len);
- }
-
- ~magic_t() {
- m[0] = m[1] = m[2] = m[3] = 0;
- }
-
- bool check() const {
- return (m[0] == M0 && 0 == strncmp((char *)&m[1], M1, M1_len));
- }
-
- bool fastcheck() const {
-#ifdef NDEBUG
- return (m[0] == M0);
-#else
- return check();
-#endif
- }
-
-# undef M1
-};
-
-
-// Set this to 1 to mprotect() autorelease pool contents
-#define PROTECT_AUTORELEASEPOOL 0
-
-class AutoreleasePoolPage
-{
-
-#define POOL_SENTINEL 0
- static pthread_key_t const key = AUTORELEASE_POOL_KEY;
- static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
- static size_t const SIZE =
-#if PROTECT_AUTORELEASEPOOL
- 4096; // must be multiple of vm page size
-#else
- 4096; // size and alignment, power of 2
-#endif
- static size_t const COUNT = SIZE / sizeof(id);
-
- magic_t const magic;
- id *next;
- pthread_t const thread;
- AutoreleasePoolPage * const parent;
- AutoreleasePoolPage *child;
- uint32_t const depth;
- uint32_t hiwat;
-
- // SIZE-sizeof(*this) bytes of contents follow
-
- static void * operator new(size_t size) {
- return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
- }
- static void operator delete(void * p) {
- return free(p);
- }
-
- inline void protect() {
-#if PROTECT_AUTORELEASEPOOL
- mprotect(this, SIZE, PROT_READ);
- check();
-#endif
- }
-
- inline void unprotect() {
-#if PROTECT_AUTORELEASEPOOL
- check();
- mprotect(this, SIZE, PROT_READ | PROT_WRITE);
-#endif
- }
-
- AutoreleasePoolPage(AutoreleasePoolPage *newParent)
- : magic(), next(begin()), thread(pthread_self()),
- parent(newParent), child(NULL),
- depth(parent ? 1+parent->depth : 0),
- hiwat(parent ? parent->hiwat : 0)
- {
- if (parent) {
- parent->check();
- assert(!parent->child);
- parent->unprotect();
- parent->child = this;
- parent->protect();
- }
- protect();
- }
-
- ~AutoreleasePoolPage()
- {
- check();
- unprotect();
- assert(empty());
-
- // Not recursive: we don't want to blow out the stack
- // if a thread accumulates a stupendous amount of garbage
- assert(!child);
- }
-
-
- void busted(bool die = true)
- {
- (die ? _objc_fatal : _objc_inform)
- ("autorelease pool page %p corrupted\n"
- " magic %x %x %x %x\n pthread %p\n",
- this, magic.m[0], magic.m[1], magic.m[2], magic.m[3],
- this->thread);
- }
-
- void check(bool die = true)
- {
- if (!magic.check() || !pthread_equal(thread, pthread_self())) {
- busted(die);
- }
- }
-
- void fastcheck(bool die = true)
- {
- if (! magic.fastcheck()) {
- busted(die);
- }
- }
-
-
- id * begin() {
- return (id *) ((uint8_t *)this+sizeof(*this));
- }
-
- id * end() {
- return (id *) ((uint8_t *)this+SIZE);
- }
-
- bool empty() {
- return next == begin();
- }
-
- bool full() {
- return next == end();
- }
-
- bool lessThanHalfFull() {
- return (next - begin() < (end() - begin()) / 2);
- }
-
- id *add(id obj)
- {
- assert(!full());
- unprotect();
- *next++ = obj;
- protect();
- return next-1;
- }
-
- void releaseAll()
- {
- releaseUntil(begin());
- }
-
- void releaseUntil(id *stop)
- {
- // Not recursive: we don't want to blow out the stack
- // if a thread accumulates a stupendous amount of garbage
-
- while (this->next != stop) {
- // Restart from hotPage() every time, in case -release
- // autoreleased more objects
- AutoreleasePoolPage *page = hotPage();
-
- // fixme I think this `while` can be `if`, but I can't prove it
- while (page->empty()) {
- page = page->parent;
- setHotPage(page);
- }
-
- page->unprotect();
- id obj = *--page->next;
- memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
- page->protect();
-
- if (obj != POOL_SENTINEL) {
- objc_release(obj);
- }
- }
-
- setHotPage(this);
-
-#ifndef NDEBUG
- // we expect any children to be completely empty
- for (AutoreleasePoolPage *page = child; page; page = page->child) {
- assert(page->empty());
- }
-#endif
- }
-
- void kill()
- {
- // Not recursive: we don't want to blow out the stack
- // if a thread accumulates a stupendous amount of garbage
- AutoreleasePoolPage *page = this;
- while (page->child) page = page->child;
-
- AutoreleasePoolPage *deathptr;
- do {
- deathptr = page;
- page = page->parent;
- if (page) {
- page->unprotect();
- page->child = NULL;
- page->protect();
- }
- delete deathptr;
- } while (deathptr != this);
- }
-
- static void tls_dealloc(void *p)
- {
- // reinstate TLS value while we work
- setHotPage((AutoreleasePoolPage *)p);
- pop(0);
- setHotPage(NULL);
- }
-
- static AutoreleasePoolPage *pageForPointer(const void *p)
- {
- return pageForPointer((uintptr_t)p);
- }
-
- static AutoreleasePoolPage *pageForPointer(uintptr_t p)
- {
- AutoreleasePoolPage *result;
- uintptr_t offset = p % SIZE;
-
- assert(offset >= sizeof(AutoreleasePoolPage));
-
- result = (AutoreleasePoolPage *)(p - offset);
- result->fastcheck();
-
- return result;
- }
-
-
- static inline AutoreleasePoolPage *hotPage()
- {
- AutoreleasePoolPage *result = (AutoreleasePoolPage *)
- _pthread_getspecific_direct(key);
- if (result) result->fastcheck();
- return result;
- }
-
- static inline void setHotPage(AutoreleasePoolPage *page)
- {
- if (page) page->fastcheck();
- _pthread_setspecific_direct(key, (void *)page);
- }
-
- static inline AutoreleasePoolPage *coldPage()
- {
- AutoreleasePoolPage *result = hotPage();
- if (result) {
- while (result->parent) {
- result = result->parent;
- result->fastcheck();
- }
- }
- return result;
- }
-
-
- static inline id *autoreleaseFast(id obj)
- {
- AutoreleasePoolPage *page = hotPage();
- if (page && !page->full()) {
- return page->add(obj);
- } else {
- return autoreleaseSlow(obj);
- }
- }
-
- static __attribute__((noinline))
- id *autoreleaseSlow(id obj)
- {
- AutoreleasePoolPage *page;
- page = hotPage();
-
- // The code below assumes some cases are handled by autoreleaseFast()
- assert(!page || page->full());
-
- if (!page) {
- assert(obj != POOL_SENTINEL);
- _objc_inform("Object %p of class %s autoreleased "
- "with no pool in place - just leaking - "
- "break on objc_autoreleaseNoPool() to debug",
- obj, object_getClassName(obj));
- objc_autoreleaseNoPool(obj);
- return NULL;
- }
-
- do {
- if (page->child) page = page->child;
- else page = new AutoreleasePoolPage(page);
- } while (page->full());
-
- setHotPage(page);
- return page->add(obj);
- }
-
-public:
- static inline id autorelease(id obj)
- {
- assert(obj);
- assert(!OBJC_IS_TAGGED_PTR(obj));
- id *dest __unused = autoreleaseFast(obj);
- assert(!dest || *dest == obj);
- return obj;
- }
-
-
- static inline void *push()
- {
- if (!hotPage()) {
- setHotPage(new AutoreleasePoolPage(NULL));
- }
- id *dest = autoreleaseFast(POOL_SENTINEL);
- assert(*dest == POOL_SENTINEL);
- return dest;
- }
-
- static inline void pop(void *token)
- {
- AutoreleasePoolPage *page;
- id *stop;
-
- if (token) {
- page = pageForPointer(token);
- stop = (id *)token;
- assert(*stop == POOL_SENTINEL);
- } else {
- // Token 0 is top-level pool
- page = coldPage();
- assert(page);
- stop = page->begin();
- }
-
- if (PrintPoolHiwat) printHiwat();
-
- page->releaseUntil(stop);
-
- // memory: delete empty children
- // hysteresis: keep one empty child if this page is more than half full
- // special case: delete everything for pop(0)
- if (!token) {
- page->kill();
- setHotPage(NULL);
- } else if (page->child) {
- if (page->lessThanHalfFull()) {
- page->child->kill();
- }
- else if (page->child->child) {
- page->child->child->kill();
- }
- }
- }
-
- static void init()
- {
- int r __unused = pthread_key_init_np(AutoreleasePoolPage::key,
- AutoreleasePoolPage::tls_dealloc);
- assert(r == 0);
- }
-
- void print()
- {
- _objc_inform("[%p] ................ PAGE %s %s %s", this,
- full() ? "(full)" : "",
- this == hotPage() ? "(hot)" : "",
- this == coldPage() ? "(cold)" : "");
- check(false);
- for (id *p = begin(); p < next; p++) {
- if (*p == POOL_SENTINEL) {
- _objc_inform("[%p] ################ POOL %p", p, p);
- } else {
- _objc_inform("[%p] %#16lx %s",
- p, (unsigned long)*p, object_getClassName(*p));
- }
- }
- }
-
- static void printAll()
- {
- _objc_inform("##############");
- _objc_inform("AUTORELEASE POOLS for thread %p", pthread_self());
-
- AutoreleasePoolPage *page;
- ptrdiff_t objects = 0;
- for (page = coldPage(); page; page = page->child) {
- objects += page->next - page->begin();
- }
- _objc_inform("%llu releases pending.", (unsigned long long)objects);
-
- for (page = coldPage(); page; page = page->child) {
- page->print();
- }
-
- _objc_inform("##############");
- }
-
- static void printHiwat()
- {
- // Check and propagate high water mark
- // Ignore high water marks under 256 to suppress noise.
- AutoreleasePoolPage *p = hotPage();
- uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin());
- if (mark > p->hiwat && mark > 256) {
- for( ; p; p = p->parent) {
- p->unprotect();
- p->hiwat = mark;
- p->protect();
- }
-
- _objc_inform("POOL HIGHWATER: new high water mark of %u "
- "pending autoreleases for thread %p:",
- mark, pthread_self());
-
- void *stack[128];
- int count = backtrace(stack, sizeof(stack)/sizeof(stack[0]));
- char **sym = backtrace_symbols(stack, count);
- for (int i = 0; i < count; i++) {
- _objc_inform("POOL HIGHWATER: %s", sym[i]);
- }
- free(sym);
- }
- }
-
-#undef POOL_SENTINEL
-};
-
-// anonymous namespace
-};
-
-// API to only be called by root classes like NSObject or NSProxy
-
-extern "C" {
-__attribute__((used,noinline,nothrow))
-static id _objc_rootRetain_slow(id obj);
-__attribute__((used,noinline,nothrow))
-static bool _objc_rootReleaseWasZero_slow(id obj);
-};
-
-id
-_objc_rootRetain_slow(id obj)
-{
- SideTable *table = SideTable::tableForPointer(obj);
- OSSpinLockLock(&table->slock);
- table->refcnts[DISGUISE(obj)] += 2;
- OSSpinLockUnlock(&table->slock);
-
- return obj;
-}
-
-id
-_objc_rootRetain(id obj)
-{
- assert(obj);
- assert(!UseGC);
-
- if (OBJC_IS_TAGGED_PTR(obj)) return obj;
-
- SideTable *table = SideTable::tableForPointer(obj);
-
- if (OSSpinLockTry(&table->slock)) {
- table->refcnts[DISGUISE(obj)] += 2;
- OSSpinLockUnlock(&table->slock);
- return obj;
- }
- return _objc_rootRetain_slow(obj);
-}
-
-bool
-_objc_rootTryRetain(id obj)
-{
- assert(obj);
- assert(!UseGC);
-
- if (OBJC_IS_TAGGED_PTR(obj)) return true;
-
- SideTable *table = SideTable::tableForPointer(obj);
-
- // NO SPINLOCK HERE
- // _objc_rootTryRetain() is called exclusively by _objc_loadWeak(),
- // which already acquired the lock on our behalf.
- if (table->slock == 0) {
- _objc_fatal("Do not call -_tryRetain.");
- }
-
- bool result = true;
- RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
- if (it == table->refcnts.end()) {
- table->refcnts[DISGUISE(obj)] = 2;
- } else if (it->second & 1) {
- result = false;
- } else {
- it->second += 2;
- }
-
- return result;
-}
-
-bool
-_objc_rootIsDeallocating(id obj)
-{
- assert(obj);
- assert(!UseGC);
-
- if (OBJC_IS_TAGGED_PTR(obj)) return false;
-
- SideTable *table = SideTable::tableForPointer(obj);
-
- // NO SPINLOCK HERE
- // _objc_rootIsDeallocating() is called exclusively by _objc_storeWeak(),
- // which already acquired the lock on our behalf.
- if (table->slock == 0) {
- _objc_fatal("Do not call -_isDeallocating.");
- }
-
- RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
- return (it != table->refcnts.end()) && ((it->second & 1) == 1);
-}
-
-
-void
-objc_clear_deallocating(id obj)
-{
- assert(obj);
- assert(!UseGC);
-
- SideTable *table = SideTable::tableForPointer(obj);
-
- // clear any weak table items
- // clear extra retain count and deallocating bit
- // (fixme warn or abort if extra retain count == 0 ?)
- OSSpinLockLock(&table->slock);
- if (seen_weak_refs) {
- arr_clear_deallocating(&table->weak_table, obj);
- }
- table->refcnts.erase(DISGUISE(obj));
- OSSpinLockUnlock(&table->slock);
-}
-
-
-bool
-_objc_rootReleaseWasZero_slow(id obj)
-{
- SideTable *table = SideTable::tableForPointer(obj);
-
- bool do_dealloc = false;
-
- OSSpinLockLock(&table->slock);
- RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
- if (it == table->refcnts.end()) {
- do_dealloc = true;
- table->refcnts[DISGUISE(obj)] = 1;
- } else if (it->second == 0) {
- do_dealloc = true;
- it->second = 1;
- } else {
- it->second -= 2;
- }
- OSSpinLockUnlock(&table->slock);
- return do_dealloc;
-}
-
-bool
-_objc_rootReleaseWasZero(id obj)
-{
- assert(obj);
- assert(!UseGC);
-
- if (OBJC_IS_TAGGED_PTR(obj)) return false;
-
- SideTable *table = SideTable::tableForPointer(obj);
-
- bool do_dealloc = false;
-
- if (OSSpinLockTry(&table->slock)) {
- RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
- if (it == table->refcnts.end()) {
- do_dealloc = true;
- table->refcnts[DISGUISE(obj)] = 1;
- } else if (it->second == 0) {
- do_dealloc = true;
- it->second = 1;
- } else {
- it->second -= 2;
- }
- OSSpinLockUnlock(&table->slock);
- return do_dealloc;
- }
- return _objc_rootReleaseWasZero_slow(obj);
-}
-
-void
-_objc_rootRelease(id obj)
-{
- assert(obj);
- assert(!UseGC);
-
- if (_objc_rootReleaseWasZero(obj) == false) {
- return;
- }
- objc_msgSend_hack(obj, @selector(dealloc));
-}
-
-__attribute__((noinline,used))
-static id _objc_rootAutorelease2(id obj)
-{
- if (OBJC_IS_TAGGED_PTR(obj)) return obj;
- return AutoreleasePoolPage::autorelease(obj);
-}
-
-__attribute__((aligned(16)))
-id
-_objc_rootAutorelease(id obj)
-{
- assert(obj); // root classes shouldn't get here, since objc_msgSend ignores nil
- assert(!UseGC);
-
- if (UseGC) {
- return obj;
- }
-
- // no tag check here: tagged pointers DO use fast autoreleasing
-
-#if SUPPORT_RETURN_AUTORELEASE
- assert(_pthread_getspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY) == NULL);
-
- if (callerAcceptsFastAutorelease(__builtin_return_address(0))) {
- _pthread_setspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY, obj);
- return obj;
- }
-#endif
- return _objc_rootAutorelease2(obj);
-}
-
-uintptr_t
-_objc_rootRetainCount(id obj)
-{
- assert(obj);
- assert(!UseGC);
-
- // XXX -- There is no way that anybody can use this API race free in a
- // threaded environment because the result is immediately stale by the
- // time the caller receives it.
-
- if (OBJC_IS_TAGGED_PTR(obj)) return (uintptr_t)obj;
-
- SideTable *table = SideTable::tableForPointer(obj);
-
- size_t refcnt_result = 1;
-
- OSSpinLockLock(&table->slock);
- RefcountMap::iterator it = table->refcnts.find(DISGUISE(obj));
- if (it != table->refcnts.end()) {
- refcnt_result = (it->second >> 1) + 1;
- }
- OSSpinLockUnlock(&table->slock);
- return refcnt_result;
-}
-
-id
-_objc_rootInit(id obj)
-{
- // In practice, it will be hard to rely on this function.
- // Many classes do not properly chain -init calls.
- return obj;
-}
-
-id
-_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
-{
-#if __OBJC2__
- // allocWithZone under __OBJC2__ ignores the zone parameter
- (void)zone;
- return class_createInstance(cls, 0);
-#else
- if (!zone || UseGC) {
- return class_createInstance(cls, 0);
- }
- return class_createInstanceFromZone(cls, 0, zone);
-#endif
-}
-
-id
-_objc_rootAlloc(Class cls)
-{
-#if 0
- // once we get a bit in the class, data structure, we can call this directly
- // because allocWithZone under __OBJC2__ ignores the zone parameter
- return class_createInstance(cls, 0);
-#else
- return [cls allocWithZone: nil];
-#endif
-}
-
-void
-_objc_rootDealloc(id obj)
-{
- assert(obj);
- assert(!UseGC);
-
- if (OBJC_IS_TAGGED_PTR(obj)) return;
-
- object_dispose(obj);
-}
-
-void
-_objc_rootFinalize(id obj __unused)
-{
- assert(obj);
- assert(UseGC);
-
- if (UseGC) {
- return;
- }
- _objc_fatal("_objc_rootFinalize called with garbage collection off");
-}
-
-malloc_zone_t *
-_objc_rootZone(id obj)
-{
- (void)obj;
- if (gc_zone) {
- return gc_zone;
- }
-#if __OBJC2__
- // allocWithZone under __OBJC2__ ignores the zone parameter
- return malloc_default_zone();
-#else
- malloc_zone_t *rval = malloc_zone_from_ptr(obj);
- return rval ? rval : malloc_default_zone();
-#endif
-}
-
-uintptr_t
-_objc_rootHash(id obj)
-{
- if (UseGC) {
- return _object_getExternalHash(obj);
- }
- return (uintptr_t)obj;
-}
-
-// make CF link for now
-void *_objc_autoreleasePoolPush(void) { return objc_autoreleasePoolPush(); }
-void _objc_autoreleasePoolPop(void *ctxt) { objc_autoreleasePoolPop(ctxt); }
-
-void *
-objc_autoreleasePoolPush(void)
-{
- if (UseGC) return NULL;
- return AutoreleasePoolPage::push();
-}
-
-void
-objc_autoreleasePoolPop(void *ctxt)
-{
- if (UseGC) return;
-
- // fixme rdar://9167170
- if (!ctxt) return;
-
- AutoreleasePoolPage::pop(ctxt);
-}
-
-void
-_objc_autoreleasePoolPrint(void)
-{
- if (UseGC) return;
- AutoreleasePoolPage::printAll();
-}
-
-#if SUPPORT_RETURN_AUTORELEASE
-
-/*
- Fast handling of returned autoreleased values.
- The caller and callee cooperate to keep the returned object
- out of the autorelease pool.
-
- Caller:
- ret = callee();
- objc_retainAutoreleasedReturnValue(ret);
- // use ret here
-
- Callee:
- // compute ret
- [ret retain];
- return objc_autoreleaseReturnValue(ret);
-
- objc_autoreleaseReturnValue() examines the caller's instructions following
- the return. If the caller's instructions immediately call
- objc_autoreleaseReturnValue, then the callee omits the -autorelease and saves
- the result in thread-local storage. If the caller does not look like it
- cooperates, then the callee calls -autorelease as usual.
-
- objc_autoreleaseReturnValue checks if the returned value is the same as the
- one in thread-local storage. If it is, the value is used directly. If not,
- the value is assumed to be truly autoreleased and is retained again. In
- either case, the caller now has a retained reference to the value.
-
- Tagged pointer objects do participate in the fast autorelease scheme,
- because it saves message sends. They are not entered in the autorelease
- pool in the slow case.
-*/
-
-# if __x86_64__
-
-static bool callerAcceptsFastAutorelease(const void * const ra0)
-{
- const uint8_t *ra1 = (const uint8_t *)ra0;
- const uint16_t *ra2;
- const uint32_t *ra4 = (const uint32_t *)ra1;
- const void **sym;
-
-#define PREFER_GOTPCREL 0
-#if PREFER_GOTPCREL
- // 48 89 c7 movq %rax,%rdi
- // ff 15 callq *symbol@GOTPCREL(%rip)
- if (*ra4 != 0xffc78948) {
- return false;
- }
- if (ra1[4] != 0x15) {
- return false;
- }
- ra1 += 3;
-#else
- // 48 89 c7 movq %rax,%rdi
- // e8 callq symbol
- if (*ra4 != 0xe8c78948) {
- return false;
- }
- ra1 += (long)*(const int32_t *)(ra1 + 4) + 8l;
- ra2 = (const uint16_t *)ra1;
- // ff 25 jmpq *symbol@DYLDMAGIC(%rip)
- if (*ra2 != 0x25ff) {
- return false;
- }
-#endif
- ra1 += 6l + (long)*(const int32_t *)(ra1 + 2);
- sym = (const void **)ra1;
- if (*sym != objc_retainAutoreleasedReturnValue)
- {
- return false;
- }
-
- return true;
-}
-
-// __x86_64__
-# else
-
-#warning unknown architecture
-
-static bool callerAcceptsFastAutorelease(const void *ra)
-{
- return false;
-}
-
-# endif
-
-// SUPPORT_RETURN_AUTORELEASE
-#endif
-
-
-id
-objc_autoreleaseReturnValue(id obj)
-{
-#if SUPPORT_RETURN_AUTORELEASE
- assert(_pthread_getspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY) == NULL);
-
- if (callerAcceptsFastAutorelease(__builtin_return_address(0))) {
- _pthread_setspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY, obj);
- return obj;
- }
-#endif
-
- return objc_autorelease(obj);
-}
-
-id
-objc_retainAutoreleaseReturnValue(id obj)
-{
- return objc_autoreleaseReturnValue(objc_retain(obj));
-}
-
-id
-objc_retainAutoreleasedReturnValue(id obj)
-{
-#if SUPPORT_RETURN_AUTORELEASE
- if (obj == _pthread_getspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY)) {
- _pthread_setspecific_direct(AUTORELEASE_POOL_RECLAIM_KEY, 0);
- return obj;
- }
-#endif
- return objc_retain(obj);
-}
-
-void
-objc_storeStrong(id *location, id obj)
-{
- // XXX FIXME -- GC support?
- id prev = *location;
- if (obj == prev) {
- return;
- }
- objc_retain(obj);
- *location = obj;
- objc_release(prev);
-}
-
-id
-objc_retainAutorelease(id obj)
-{
- return objc_autorelease(objc_retain(obj));
-}
-
-void
-_objc_deallocOnMainThreadHelper(void *context)
-{
- id obj = (id)context;
- objc_msgSend_hack(obj, @selector(dealloc));
-}
-
-// convert objc_objectptr_t to id, callee must take ownership.
-NS_RETURNS_RETAINED id objc_retainedObject(objc_objectptr_t CF_CONSUMED pointer) { return (id)pointer; }
-
-// convert objc_objectptr_t to id, without ownership transfer.
-NS_RETURNS_NOT_RETAINED id objc_unretainedObject(objc_objectptr_t pointer) { return (id)pointer; }
-
-// convert id to objc_objectptr_t, no ownership transfer.
-CF_RETURNS_NOT_RETAINED objc_objectptr_t objc_unretainedPointer(id object) { return object; }
-
-
-PRIVATE_EXTERN void arr_init(void)
-{
- AutoreleasePoolPage::init();
- SideTable::init();
-}
// 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
*/
+#endif
* @APPLE_LICENSE_HEADER_END@
*/
-#import "objc-auto.h"
+#include "objc-auto.h"
#ifndef OBJC_NO_GC
-#import <auto_zone.h>
-#import <objc/objc.h>
-#import <objc/runtime.h>
-#import "objc-auto-dump.h"
-#import "objc-private.h"
-#import <strings.h>
+#include <auto_zone.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+#include "objc-auto-dump.h"
+#include "objc-private.h"
+#include <strings.h>
/*
* Utilities
/*
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;
}
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...
#ifndef _OBJC_AUTO_H_
#define _OBJC_AUTO_H_
+#pragma GCC system_header
+
#include <objc/objc.h>
#include <malloc/malloc.h>
#include <stdint.h>
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); }
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) { }
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() { }
* @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 <stdint.h>
-#import <stdbool.h>
-#import <fcntl.h>
-#import <dlfcn.h>
-#import <mach/mach.h>
-#import <mach-o/dyld.h>
-#import <mach-o/nlist.h>
-#import <sys/types.h>
-#import <sys/mman.h>
-#import <libkern/OSAtomic.h>
-#import <auto_zone.h>
-
-#import <Block_private.h>
-#include <dispatch/dispatch.h>
-
-#import "objc-private.h"
-#import "objc-references.h"
-#import "maptable.h"
-#import "message.h"
-#import "objc-gdb.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <mach/mach.h>
+#include <mach-o/dyld.h>
+#include <mach-o/nlist.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <libkern/OSAtomic.h>
+#include <auto_zone.h>
+
+#include <Block_private.h>
+#include <dispatch/private.h>
+
+#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
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 */
* 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)) {
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);
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);
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);
}
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));
* 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);
return result;
}
-PRIVATE_EXTERN id objc_read_weak_non_gc(id *location) {
+id objc_read_weak_non_gc(id *location) {
return *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);
}
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) {
* Finalization support
**********************************************************************/
-static IMP _NSObject_finalize = NULL;
-
// Finalizer crash debugging
static void *finalizing_object;
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.
{
#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
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) {
/* Compaction support */
-PRIVATE_EXTERN void objc_disableCompaction() {
+void objc_disableCompaction() {
if (UseCompaction) {
UseCompaction = NO;
auto_zone_disable_compaction(gc_zone);
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);
}
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",
}
// 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;
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;
}
#define SHIFT 3
#define INITIALSIZE 512
-#define REMOVED -1
+#define REMOVED ~0ul
// Allocate the side table.
static void registeredClassTableInit() {
}
// lock held by callers
-PRIVATE_EXTERN
void objc_addRegisteredClass(Class candidate) {
if (!UseGC) return;
uintptr_t *table = (uintptr_t *)AllClasses;
}
// lock held by callers
-PRIVATE_EXTERN
void objc_removeRegisteredClass(Class candidate) {
if (!UseGC) return;
uintptr_t *table = (uintptr_t *)AllClasses;
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;
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
+++ /dev/null
-/*
- * Copyright (c) 2010 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-/***********************************************************************
- * objc-block-trampolines.m
- * Author: b.bum
- *
- **********************************************************************/
-
-/***********************************************************************
- * Imports.
- **********************************************************************/
-#include "objc-private.h"
-#include "runtime.h"
-
-#include <Block.h>
-#include <Block_private.h>
-#include <mach/mach.h>
-
-// symbols defined in assembly files
-// Don't use the symbols directly; they're thumb-biased on some ARM archs.
-#define TRAMP(tramp) \
- static inline uintptr_t tramp(void) { \
- extern void *_##tramp; \
- return ((uintptr_t)&_##tramp) & ~1UL; \
- }
-// Scalar return
-TRAMP(a1a2_tramphead); // trampoline header code
-TRAMP(a1a2_firsttramp); // first trampoline
-TRAMP(a1a2_nexttramp); // second trampoline
-TRAMP(a1a2_trampend); // after the last trampoline
-
-// Struct return
-TRAMP(a2a3_tramphead);
-TRAMP(a2a3_firsttramp);
-TRAMP(a2a3_nexttramp);
-TRAMP(a2a3_trampend);
-
-// argument mode identifier
-typedef enum {
- ReturnValueInRegisterArgumentMode,
- ReturnValueOnStackArgumentMode,
-
- ArgumentModeMax
-} ArgumentMode;
-
-// slot size is 8 bytes on both i386 and x86_64 (because of bytes-per-call instruction is > 4 for both)
-#define SLOT_SIZE 8
-
-// unsigned value, any value, larger thna # of blocks that fit in the page pair
-#define LAST_SLOT_MARKER 4241
-
-#define TRAMPOLINE_PAGE_PAIR_HEADER_SIZE (sizeof(uint32_t) + sizeof(struct _TrampolineBlockPagePair *) + sizeof(struct _TrampolineBlockPagePair *))
-typedef struct _TrampolineBlockPagePair {
- struct _TrampolineBlockPagePair *nextPagePair; // linked list of all page pairs
- struct _TrampolineBlockPagePair *nextAvailablePage; // linked list of pages with available slots
-
- uint32_t nextAvailable; // index of next available slot, 0 if no more available
-
- // Data: block pointers and free list.
- // Bytes parallel with trampoline header are the fields above, or unused.
- uint8_t blocks[ PAGE_SIZE - TRAMPOLINE_PAGE_PAIR_HEADER_SIZE ]
- __attribute__((unavailable)) /* always use _headerSize() */;
-
- // Code: trampoline header followed by trampolines.
- uint8_t trampolines[PAGE_SIZE];
-
- // Per-trampoline block data format:
- // initial value is 0 while page pair is filled sequentially (last slot is LAST_SLOT_MARKER to indicate end of page)
- // when filled, value is reference to Block_copy()d block
- // when empty, value is index of next available slot OR LAST_SLOT_MARKER
-
-} TrampolineBlockPagePair;
-
-// two sets of trampoline page pairs; one for stack returns and one for register returns
-static TrampolineBlockPagePair *headPagePairs[2];
-
-#pragma mark Utility Functions
-static inline uint32_t _headerSize() {
- uint32_t headerSize = (uint32_t) (a1a2_firsttramp() - a1a2_tramphead());
-
- // make sure stret and non-stret sizes match
- assert(a2a3_firsttramp() - a2a3_tramphead() == headerSize);
-
- return headerSize;
-}
-
-static inline uint32_t _slotSize() {
- uint32_t slotSize = (uint32_t) (a1a2_nexttramp() - a1a2_firsttramp());
-
- // make sure stret and non-stret sizes match
- assert(a2a3_nexttramp() - a2a3_firsttramp() == slotSize);
-
- return slotSize;
-}
-
-static inline bool trampolinesAreThumb(void) {
- extern void *_a1a2_firsttramp;
- extern void *_a1a2_nexttramp;
- extern void *_a2a3_firsttramp;
- extern void *_a2a3_nexttramp;
-
- // make sure thumb-edness of all trampolines match
- assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
- ((uintptr_t)&_a2a3_firsttramp) % 2);
- assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
- ((uintptr_t)&_a1a2_nexttramp) % 2);
- assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
- ((uintptr_t)&_a2a3_nexttramp) % 2);
-
- return ((uintptr_t)&_a1a2_firsttramp) % 2;
-}
-
-static inline uint32_t _slotsPerPagePair() {
- uint32_t slotSize = _slotSize();
- uint32_t slotsPerPagePair = PAGE_SIZE / slotSize;
- return slotsPerPagePair;
-}
-
-static inline uint32_t _paddingSlotCount() {
- uint32_t headerSize = _headerSize();
- uint32_t slotSize = _slotSize();
- uint32_t paddingSlots = headerSize / slotSize;
- return paddingSlots;
-}
-
-static inline void **_payloadAddressAtIndex(TrampolineBlockPagePair *pagePair, uint32_t index) {
- uint32_t slotSize = _slotSize();
- uintptr_t baseAddress = (uintptr_t) pagePair;
- uintptr_t payloadAddress = baseAddress + (slotSize * index);
- return (void **)payloadAddress;
-}
-
-static inline IMP _trampolineAddressAtIndex(TrampolineBlockPagePair *pagePair, uint32_t index) {
- uint32_t slotSize = _slotSize();
- uintptr_t baseAddress = (uintptr_t) &(pagePair->trampolines);
- uintptr_t trampolineAddress = baseAddress + (slotSize * index);
-
-#if defined(__arm__)
- if (trampolinesAreThumb()) trampolineAddress++;
-#endif
-
- return (IMP)trampolineAddress;
-}
-
-static inline void _lock() {
-#if __OBJC2__
- rwlock_write(&runtimeLock);
-#else
- mutex_lock(&classLock);
-#endif
-}
-
-static inline void _unlock() {
-#if __OBJC2__
- rwlock_unlock_write(&runtimeLock);
-#else
- mutex_unlock(&classLock);
-#endif
-}
-
-static inline void _assert_locked() {
-#if __OBJC2__
- rwlock_assert_writing(&runtimeLock);
-#else
- mutex_assert_locked(&classLock);
-#endif
-}
-
-#pragma mark Trampoline Management Functions
-static TrampolineBlockPagePair *_allocateTrampolinesAndData(ArgumentMode aMode) {
- _assert_locked();
-
- vm_address_t dataAddress;
-
- // make sure certain assumptions are met
- assert(PAGE_SIZE == 4096);
- assert(sizeof(TrampolineBlockPagePair) == 2*PAGE_SIZE);
- assert(_slotSize() == 8);
- assert(_headerSize() >= TRAMPOLINE_PAGE_PAIR_HEADER_SIZE);
- assert((_headerSize() % _slotSize()) == 0);
-
- assert(a1a2_tramphead() % PAGE_SIZE == 0);
- assert(a1a2_tramphead() + PAGE_SIZE == a1a2_trampend());
- assert(a2a3_tramphead() % PAGE_SIZE == 0);
- assert(a2a3_tramphead() + PAGE_SIZE == a2a3_trampend());
-
- TrampolineBlockPagePair *headPagePair = headPagePairs[aMode];
-
- if (headPagePair) {
- assert(headPagePair->nextAvailablePage == NULL);
- }
-
- int i;
- kern_return_t result = KERN_FAILURE;
- for(i = 0; i < 5; i++) {
- result = vm_allocate(mach_task_self(), &dataAddress, PAGE_SIZE * 2, TRUE);
- if (result != KERN_SUCCESS) {
- mach_error("vm_allocate failed", result);
- return NULL;
- }
-
- vm_address_t codeAddress = dataAddress + PAGE_SIZE;
- result = vm_deallocate(mach_task_self(), codeAddress, PAGE_SIZE);
- if (result != KERN_SUCCESS) {
- mach_error("vm_deallocate failed", result);
- return NULL;
- }
-
- uintptr_t codePage;
- switch(aMode) {
- case ReturnValueInRegisterArgumentMode:
- codePage = a1a2_firsttramp() & ~(PAGE_MASK);
- break;
- case ReturnValueOnStackArgumentMode:
- codePage = a2a3_firsttramp() & ~(PAGE_MASK);
- break;
- default:
- _objc_fatal("unknown return mode %d", (int)aMode);
- break;
- }
- vm_prot_t currentProtection, maxProtection;
- result = vm_remap(mach_task_self(), &codeAddress, PAGE_SIZE, 0, FALSE, mach_task_self(),
- codePage, TRUE, ¤tProtection, &maxProtection, VM_INHERIT_SHARE);
- if (result != KERN_SUCCESS) {
- result = vm_deallocate(mach_task_self(), dataAddress, PAGE_SIZE);
- if (result != KERN_SUCCESS) {
- mach_error("vm_deallocate for retry failed.", result);
- return NULL;
- }
- } else
- break;
- }
-
- if (result != KERN_SUCCESS)
- return NULL;
-
- TrampolineBlockPagePair *pagePair = (TrampolineBlockPagePair *) dataAddress;
- pagePair->nextAvailable = _paddingSlotCount();
- pagePair->nextPagePair = NULL;
- pagePair->nextAvailablePage = NULL;
- void **lastPageBlockPtr = _payloadAddressAtIndex(pagePair, _slotsPerPagePair() - 1);
- *lastPageBlockPtr = (void*)(uintptr_t) LAST_SLOT_MARKER;
-
- if (headPagePair) {
- TrampolineBlockPagePair *lastPage = headPagePair;
- while(lastPage->nextPagePair)
- lastPage = lastPage->nextPagePair;
-
- lastPage->nextPagePair = pagePair;
- headPagePairs[aMode]->nextAvailablePage = pagePair;
- } else {
- headPagePairs[aMode] = pagePair;
- }
-
- return pagePair;
-}
-
-static TrampolineBlockPagePair *_getOrAllocatePagePairWithNextAvailable(ArgumentMode aMode) {
- _assert_locked();
-
- TrampolineBlockPagePair *headPagePair = headPagePairs[aMode];
-
- if (!headPagePair)
- return _allocateTrampolinesAndData(aMode);
-
- if (headPagePair->nextAvailable) // make sure head page is filled first
- return headPagePair;
-
- if (headPagePair->nextAvailablePage) // check if there is a page w/a hole
- return headPagePair->nextAvailablePage;
-
- return _allocateTrampolinesAndData(aMode); // tack on a new one
-}
-
-static TrampolineBlockPagePair *_pagePairAndIndexContainingIMP(IMP anImp, uint32_t *outIndex, TrampolineBlockPagePair **outHeadPagePair) {
- _assert_locked();
-
- uintptr_t impValue = (uintptr_t) anImp;
- uint32_t i;
-
- for(i = 0; i < ArgumentModeMax; i++) {
- TrampolineBlockPagePair *pagePair = headPagePairs[i];
-
- while(pagePair) {
- uintptr_t startOfTrampolines = (uintptr_t) &(pagePair->trampolines);
- uintptr_t endOfTrampolines = ((uintptr_t) startOfTrampolines) + PAGE_SIZE;
-
- if ( (impValue >=startOfTrampolines) && (impValue <= endOfTrampolines) ) {
- if (outIndex) {
- *outIndex = (uint32_t) ((impValue - startOfTrampolines) / SLOT_SIZE);
- }
- if (outHeadPagePair) {
- *outHeadPagePair = headPagePairs[i];
- }
- return pagePair;
- }
-
- pagePair = pagePair->nextPagePair;
- }
- }
-
- return NULL;
-}
-
-// `block` must already have been copied
-static IMP _imp_implementationWithBlockNoCopy(ArgumentMode aMode, void *block)
-{
- _assert_locked();
-
- TrampolineBlockPagePair *pagePair = _getOrAllocatePagePairWithNextAvailable(aMode);
- if (!headPagePairs[aMode])
- headPagePairs[aMode] = pagePair;
-
- uint32_t index = pagePair->nextAvailable;
- void **payloadAddress = _payloadAddressAtIndex(pagePair, index);
- assert((index < 1024) || (index == LAST_SLOT_MARKER));
-
- uint32_t nextAvailableIndex = (uint32_t) *((uintptr_t *) payloadAddress);
- if (nextAvailableIndex == 0)
- // first time through, slots are filled with zeros, fill sequentially
- pagePair->nextAvailable = index + 1;
- else if (nextAvailableIndex == LAST_SLOT_MARKER) {
- // last slot is filled with this as marker
- // page now full, remove from available page linked list
- pagePair->nextAvailable = 0;
- TrampolineBlockPagePair *iteratorPair = headPagePairs[aMode];
- while(iteratorPair && (iteratorPair->nextAvailablePage != pagePair))
- iteratorPair = iteratorPair->nextAvailablePage;
- if (iteratorPair) {
- iteratorPair->nextAvailablePage = pagePair->nextAvailablePage;
- pagePair->nextAvailablePage = NULL;
- }
- } else {
- // empty slot at index contains pointer to next available index
- pagePair->nextAvailable = nextAvailableIndex;
- }
-
- *payloadAddress = block;
- IMP trampoline = _trampolineAddressAtIndex(pagePair, index);
-
- return trampoline;
-}
-
-static ArgumentMode _argumentModeForBlock(void *block) {
- ArgumentMode aMode = ReturnValueInRegisterArgumentMode;
-
- if (_Block_has_signature(block) && _Block_use_stret(block))
- aMode = ReturnValueOnStackArgumentMode;
-
- return aMode;
-}
-
-#pragma mark Public API
-IMP imp_implementationWithBlock(void *block)
-{
- block = Block_copy(block);
- _lock();
- IMP returnIMP = _imp_implementationWithBlockNoCopy(_argumentModeForBlock(block), block);
- _unlock();
- return returnIMP;
-}
-
-
-void *imp_getBlock(IMP anImp) {
- uint32_t index;
- TrampolineBlockPagePair *pagePair;
-
- if (!anImp) return NULL;
-
- _lock();
-
- pagePair = _pagePairAndIndexContainingIMP(anImp, &index, NULL);
-
- if (!pagePair) {
- _unlock();
- return NULL;
- }
-
- void *potentialBlock = *_payloadAddressAtIndex(pagePair, index);
-
- if ((uintptr_t) potentialBlock == (uintptr_t) LAST_SLOT_MARKER) {
- _unlock();
- return NULL;
- }
-
- if ((uintptr_t) potentialBlock < (uintptr_t) _slotsPerPagePair()) {
- _unlock();
- return NULL;
- }
-
- _unlock();
-
- return potentialBlock;
-}
-
-BOOL imp_removeBlock(IMP anImp) {
- TrampolineBlockPagePair *pagePair;
- TrampolineBlockPagePair *headPagePair;
- uint32_t index;
-
- if (!anImp) return NO;
-
- _lock();
- pagePair = _pagePairAndIndexContainingIMP(anImp, &index, &headPagePair);
-
- if (!pagePair) {
- _unlock();
- return NO;
- }
-
- void **payloadAddress = _payloadAddressAtIndex(pagePair, index);
- void *block = *payloadAddress;
- // block is released below
-
- if (pagePair->nextAvailable) {
- *payloadAddress = (void *) (uintptr_t) pagePair->nextAvailable;
- pagePair->nextAvailable = index;
- } else {
- *payloadAddress = (void *) (uintptr_t) LAST_SLOT_MARKER; // nada after this one is used
- pagePair->nextAvailable = index;
- }
-
- // make sure this page is on available linked list
- TrampolineBlockPagePair *pagePairIterator = headPagePair;
-
- // see if pagePair is the next available page for any existing pages
- while(pagePairIterator->nextAvailablePage && (pagePairIterator->nextAvailablePage != pagePair))
- pagePairIterator = pagePairIterator->nextAvailablePage;
-
- if (! pagePairIterator->nextAvailablePage) { // if iteration stopped because nextAvail was NULL
- // add to end of list.
- pagePairIterator->nextAvailablePage = pagePair;
- pagePair->nextAvailablePage = NULL;
- }
-
- _unlock();
- Block_release(block);
- return YES;
-}
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/***********************************************************************
+ * objc-block-trampolines.m
+ * Author: b.bum
+ *
+ **********************************************************************/
+
+/***********************************************************************
+ * Imports.
+ **********************************************************************/
+#include "objc-private.h"
+#include "runtime.h"
+
+#include <Block.h>
+#include <Block_private.h>
+#include <mach/mach.h>
+
+// symbols defined in assembly files
+// Don't use the symbols directly; they're thumb-biased on some ARM archs.
+#define TRAMP(tramp) \
+ static inline uintptr_t tramp(void) { \
+ extern void *_##tramp; \
+ return ((uintptr_t)&_##tramp) & ~1UL; \
+ }
+// Scalar return
+TRAMP(a1a2_tramphead); // trampoline header code
+TRAMP(a1a2_firsttramp); // first trampoline
+TRAMP(a1a2_nexttramp); // second trampoline
+TRAMP(a1a2_trampend); // after the last trampoline
+
+// Struct return
+TRAMP(a2a3_tramphead);
+TRAMP(a2a3_firsttramp);
+TRAMP(a2a3_nexttramp);
+TRAMP(a2a3_trampend);
+
+// argument mode identifier
+typedef enum {
+ ReturnValueInRegisterArgumentMode,
+ ReturnValueOnStackArgumentMode,
+
+ ArgumentModeMax
+} ArgumentMode;
+
+// slot size is 8 bytes on both i386 and x86_64 (because of bytes-per-call instruction is > 4 for both)
+#define SLOT_SIZE 8
+
+// unsigned value, any value, larger thna # of blocks that fit in the page pair
+#define LAST_SLOT_MARKER 4241
+
+#define TRAMPOLINE_PAGE_PAIR_HEADER_SIZE (sizeof(uint32_t) + sizeof(struct _TrampolineBlockPagePair *) + sizeof(struct _TrampolineBlockPagePair *))
+typedef struct _TrampolineBlockPagePair {
+ struct _TrampolineBlockPagePair *nextPagePair; // linked list of all page pairs
+ struct _TrampolineBlockPagePair *nextAvailablePage; // linked list of pages with available slots
+
+ uint32_t nextAvailable; // index of next available slot, 0 if no more available
+
+ // Data: block pointers and free list.
+ // Bytes parallel with trampoline header are the fields above, or unused.
+ uint8_t blocks[ PAGE_SIZE - TRAMPOLINE_PAGE_PAIR_HEADER_SIZE ]
+ __attribute__((unavailable)) /* always use _headerSize() */;
+
+ // Code: trampoline header followed by trampolines.
+ uint8_t trampolines[PAGE_SIZE];
+
+ // Per-trampoline block data format:
+ // initial value is 0 while page pair is filled sequentially (last slot is LAST_SLOT_MARKER to indicate end of page)
+ // when filled, value is reference to Block_copy()d block
+ // when empty, value is index of next available slot OR LAST_SLOT_MARKER
+
+} TrampolineBlockPagePair;
+
+// two sets of trampoline page pairs; one for stack returns and one for register returns
+static TrampolineBlockPagePair *headPagePairs[2];
+
+#pragma mark Utility Functions
+static inline uint32_t _headerSize() {
+ uint32_t headerSize = (uint32_t) (a1a2_firsttramp() - a1a2_tramphead());
+
+ // make sure stret and non-stret sizes match
+ assert(a2a3_firsttramp() - a2a3_tramphead() == headerSize);
+
+ return headerSize;
+}
+
+static inline uint32_t _slotSize() {
+ uint32_t slotSize = (uint32_t) (a1a2_nexttramp() - a1a2_firsttramp());
+
+ // make sure stret and non-stret sizes match
+ assert(a2a3_nexttramp() - a2a3_firsttramp() == slotSize);
+
+ return slotSize;
+}
+
+static inline bool trampolinesAreThumb(void) {
+ extern void *_a1a2_firsttramp;
+#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 ==
+ ((uintptr_t)&_a2a3_firsttramp) % 2);
+ assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
+ ((uintptr_t)&_a1a2_nexttramp) % 2);
+ assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
+ ((uintptr_t)&_a2a3_nexttramp) % 2);
+
+ return ((uintptr_t)&_a1a2_firsttramp) % 2;
+}
+
+static inline uint32_t _slotsPerPagePair() {
+ uint32_t slotSize = _slotSize();
+ uint32_t slotsPerPagePair = PAGE_SIZE / slotSize;
+ return slotsPerPagePair;
+}
+
+static inline uint32_t _paddingSlotCount() {
+ uint32_t headerSize = _headerSize();
+ uint32_t slotSize = _slotSize();
+ uint32_t paddingSlots = headerSize / slotSize;
+ return paddingSlots;
+}
+
+static inline id *_payloadAddressAtIndex(TrampolineBlockPagePair *pagePair, uint32_t index) {
+ uint32_t slotSize = _slotSize();
+ uintptr_t baseAddress = (uintptr_t) pagePair;
+ uintptr_t payloadAddress = baseAddress + (slotSize * index);
+ return (id *)payloadAddress;
+}
+
+static inline IMP _trampolineAddressAtIndex(TrampolineBlockPagePair *pagePair, uint32_t index) {
+ uint32_t slotSize = _slotSize();
+ uintptr_t baseAddress = (uintptr_t) &(pagePair->trampolines);
+ uintptr_t trampolineAddress = baseAddress + (slotSize * index);
+
+#if defined(__arm__)
+ if (trampolinesAreThumb()) trampolineAddress++;
+#endif
+
+ return (IMP)trampolineAddress;
+}
+
+static inline void _lock() {
+#if __OBJC2__
+ rwlock_write(&runtimeLock);
+#else
+ mutex_lock(&classLock);
+#endif
+}
+
+static inline void _unlock() {
+#if __OBJC2__
+ rwlock_unlock_write(&runtimeLock);
+#else
+ mutex_unlock(&classLock);
+#endif
+}
+
+static inline void _assert_locked() {
+#if __OBJC2__
+ rwlock_assert_writing(&runtimeLock);
+#else
+ mutex_assert_locked(&classLock);
+#endif
+}
+
+#pragma mark Trampoline Management Functions
+static TrampolineBlockPagePair *_allocateTrampolinesAndData(ArgumentMode aMode) {
+ _assert_locked();
+
+ vm_address_t dataAddress;
+
+ // make sure certain assumptions are met
+ assert(PAGE_SIZE == 4096);
+ assert(sizeof(TrampolineBlockPagePair) == 2*PAGE_SIZE);
+ assert(_slotSize() == 8);
+ assert(_headerSize() >= TRAMPOLINE_PAGE_PAIR_HEADER_SIZE);
+ assert((_headerSize() % _slotSize()) == 0);
+
+ assert(a1a2_tramphead() % PAGE_SIZE == 0);
+ assert(a1a2_tramphead() + PAGE_SIZE == a1a2_trampend());
+ assert(a2a3_tramphead() % PAGE_SIZE == 0);
+ assert(a2a3_tramphead() + PAGE_SIZE == a2a3_trampend());
+
+ TrampolineBlockPagePair *headPagePair = headPagePairs[aMode];
+
+ if (headPagePair) {
+ assert(headPagePair->nextAvailablePage == NULL);
+ }
+
+ int i;
+ kern_return_t result = KERN_FAILURE;
+ for(i = 0; i < 5; i++) {
+ result = vm_allocate(mach_task_self(), &dataAddress, PAGE_SIZE * 2, TRUE);
+ if (result != KERN_SUCCESS) {
+ mach_error("vm_allocate failed", result);
+ return NULL;
+ }
+
+ vm_address_t codeAddress = dataAddress + PAGE_SIZE;
+ result = vm_deallocate(mach_task_self(), codeAddress, PAGE_SIZE);
+ if (result != KERN_SUCCESS) {
+ mach_error("vm_deallocate failed", result);
+ return NULL;
+ }
+
+ uintptr_t codePage;
+ switch(aMode) {
+ case ReturnValueInRegisterArgumentMode:
+ codePage = a1a2_firsttramp() & ~(PAGE_MASK);
+ break;
+ case ReturnValueOnStackArgumentMode:
+ codePage = a2a3_firsttramp() & ~(PAGE_MASK);
+ break;
+ default:
+ _objc_fatal("unknown return mode %d", (int)aMode);
+ break;
+ }
+ vm_prot_t currentProtection, maxProtection;
+ result = vm_remap(mach_task_self(), &codeAddress, PAGE_SIZE, 0, FALSE, mach_task_self(),
+ codePage, TRUE, ¤tProtection, &maxProtection, VM_INHERIT_SHARE);
+ if (result != KERN_SUCCESS) {
+ result = vm_deallocate(mach_task_self(), dataAddress, PAGE_SIZE);
+ if (result != KERN_SUCCESS) {
+ mach_error("vm_deallocate for retry failed.", result);
+ return NULL;
+ }
+ } else
+ break;
+ }
+
+ if (result != KERN_SUCCESS)
+ return NULL;
+
+ TrampolineBlockPagePair *pagePair = (TrampolineBlockPagePair *) dataAddress;
+ pagePair->nextAvailable = _paddingSlotCount();
+ pagePair->nextPagePair = NULL;
+ pagePair->nextAvailablePage = NULL;
+ id *lastPageBlockPtr = _payloadAddressAtIndex(pagePair, _slotsPerPagePair() - 1);
+ *lastPageBlockPtr = (id)(uintptr_t) LAST_SLOT_MARKER;
+
+ if (headPagePair) {
+ TrampolineBlockPagePair *lastPage = headPagePair;
+ while(lastPage->nextPagePair)
+ lastPage = lastPage->nextPagePair;
+
+ lastPage->nextPagePair = pagePair;
+ headPagePairs[aMode]->nextAvailablePage = pagePair;
+ } else {
+ headPagePairs[aMode] = pagePair;
+ }
+
+ return pagePair;
+}
+
+static TrampolineBlockPagePair *_getOrAllocatePagePairWithNextAvailable(ArgumentMode aMode) {
+ _assert_locked();
+
+ TrampolineBlockPagePair *headPagePair = headPagePairs[aMode];
+
+ if (!headPagePair)
+ return _allocateTrampolinesAndData(aMode);
+
+ if (headPagePair->nextAvailable) // make sure head page is filled first
+ return headPagePair;
+
+ if (headPagePair->nextAvailablePage) // check if there is a page w/a hole
+ return headPagePair->nextAvailablePage;
+
+ return _allocateTrampolinesAndData(aMode); // tack on a new one
+}
+
+static TrampolineBlockPagePair *_pagePairAndIndexContainingIMP(IMP anImp, uint32_t *outIndex, TrampolineBlockPagePair **outHeadPagePair) {
+ _assert_locked();
+
+ uintptr_t impValue = (uintptr_t) anImp;
+ uint32_t i;
+
+ for(i = 0; i < ArgumentModeMax; i++) {
+ TrampolineBlockPagePair *pagePair = headPagePairs[i];
+
+ while(pagePair) {
+ uintptr_t startOfTrampolines = (uintptr_t) &(pagePair->trampolines);
+ uintptr_t endOfTrampolines = ((uintptr_t) startOfTrampolines) + PAGE_SIZE;
+
+ if ( (impValue >=startOfTrampolines) && (impValue <= endOfTrampolines) ) {
+ if (outIndex) {
+ *outIndex = (uint32_t) ((impValue - startOfTrampolines) / SLOT_SIZE);
+ }
+ if (outHeadPagePair) {
+ *outHeadPagePair = headPagePairs[i];
+ }
+ return pagePair;
+ }
+
+ pagePair = pagePair->nextPagePair;
+ }
+ }
+
+ return NULL;
+}
+
+// `block` must already have been copied
+static IMP _imp_implementationWithBlockNoCopy(ArgumentMode aMode, id block)
+{
+ _assert_locked();
+
+ TrampolineBlockPagePair *pagePair = _getOrAllocatePagePairWithNextAvailable(aMode);
+ if (!headPagePairs[aMode])
+ headPagePairs[aMode] = pagePair;
+
+ uint32_t index = pagePair->nextAvailable;
+ id *payloadAddress = _payloadAddressAtIndex(pagePair, index);
+ assert((index < 1024) || (index == LAST_SLOT_MARKER));
+
+ uint32_t nextAvailableIndex = (uint32_t) *((uintptr_t *) payloadAddress);
+ if (nextAvailableIndex == 0)
+ // first time through, slots are filled with zeros, fill sequentially
+ pagePair->nextAvailable = index + 1;
+ else if (nextAvailableIndex == LAST_SLOT_MARKER) {
+ // last slot is filled with this as marker
+ // page now full, remove from available page linked list
+ pagePair->nextAvailable = 0;
+ TrampolineBlockPagePair *iteratorPair = headPagePairs[aMode];
+ while(iteratorPair && (iteratorPair->nextAvailablePage != pagePair))
+ iteratorPair = iteratorPair->nextAvailablePage;
+ if (iteratorPair) {
+ iteratorPair->nextAvailablePage = pagePair->nextAvailablePage;
+ pagePair->nextAvailablePage = NULL;
+ }
+ } else {
+ // empty slot at index contains pointer to next available index
+ pagePair->nextAvailable = nextAvailableIndex;
+ }
+
+ *payloadAddress = block;
+ IMP trampoline = _trampolineAddressAtIndex(pagePair, index);
+
+ return trampoline;
+}
+
+static ArgumentMode _argumentModeForBlock(id block) {
+ ArgumentMode aMode = ReturnValueInRegisterArgumentMode;
+
+ if (_Block_has_signature(block) && _Block_use_stret(block))
+ aMode = ReturnValueOnStackArgumentMode;
+
+ return aMode;
+}
+
+#pragma mark Public API
+IMP imp_implementationWithBlock(id block)
+{
+ block = Block_copy(block);
+ _lock();
+ IMP returnIMP = _imp_implementationWithBlockNoCopy(_argumentModeForBlock(block), block);
+ _unlock();
+ return returnIMP;
+}
+
+
+id imp_getBlock(IMP anImp) {
+ uint32_t index;
+ TrampolineBlockPagePair *pagePair;
+
+ if (!anImp) return NULL;
+
+ _lock();
+
+ pagePair = _pagePairAndIndexContainingIMP(anImp, &index, NULL);
+
+ if (!pagePair) {
+ _unlock();
+ return NULL;
+ }
+
+ id potentialBlock = *_payloadAddressAtIndex(pagePair, index);
+
+ if ((uintptr_t) potentialBlock == (uintptr_t) LAST_SLOT_MARKER) {
+ _unlock();
+ return NULL;
+ }
+
+ if ((uintptr_t) potentialBlock < (uintptr_t) _slotsPerPagePair()) {
+ _unlock();
+ return NULL;
+ }
+
+ _unlock();
+
+ return potentialBlock;
+}
+
+BOOL imp_removeBlock(IMP anImp) {
+ TrampolineBlockPagePair *pagePair;
+ TrampolineBlockPagePair *headPagePair;
+ uint32_t index;
+
+ if (!anImp) return NO;
+
+ _lock();
+ pagePair = _pagePairAndIndexContainingIMP(anImp, &index, &headPagePair);
+
+ if (!pagePair) {
+ _unlock();
+ return NO;
+ }
+
+ id *payloadAddress = _payloadAddressAtIndex(pagePair, index);
+ id block = *payloadAddress;
+ // block is released below
+
+ if (pagePair->nextAvailable) {
+ *payloadAddress = (id) (uintptr_t) pagePair->nextAvailable;
+ pagePair->nextAvailable = index;
+ } else {
+ *payloadAddress = (id) (uintptr_t) LAST_SLOT_MARKER; // nada after this one is used
+ pagePair->nextAvailable = index;
+ }
+
+ // make sure this page is on available linked list
+ TrampolineBlockPagePair *pagePairIterator = headPagePair;
+
+ // see if pagePair is the next available page for any existing pages
+ while(pagePairIterator->nextAvailablePage && (pagePairIterator->nextAvailablePage != pagePair))
+ pagePairIterator = pagePairIterator->nextAvailablePage;
+
+ if (! pagePairIterator->nextAvailablePage) { // if iteration stopped because nextAvail was NULL
+ // add to end of list.
+ pagePairIterator->nextAvailablePage = pagePair;
+ pagePair->nextAvailablePage = NULL;
+ }
+
+ _unlock();
+ Block_release(block);
+ return YES;
+}
+++ /dev/null
-/*
- * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/***********************************************************************
-* objc-cache.m
-* Method cache management
-* Cache flushing
-* Cache garbage collection
-* Cache instrumentation
-* Dedicated allocator for large caches
-**********************************************************************/
-
-
-/***********************************************************************
- * Method cache locking (GrP 2001-1-14)
- *
- * For speed, objc_msgSend does not acquire any locks when it reads
- * method caches. Instead, all cache changes are performed so that any
- * objc_msgSend running concurrently with the cache mutator will not
- * crash or hang or get an incorrect result from the cache.
- *
- * When cache memory becomes unused (e.g. the old cache after cache
- * expansion), it is not immediately freed, because a concurrent
- * objc_msgSend could still be using it. Instead, the memory is
- * disconnected from the data structures and placed on a garbage list.
- * The memory is now only accessible to instances of objc_msgSend that
- * were running when the memory was disconnected; any further calls to
- * objc_msgSend will not see the garbage memory because the other data
- * structures don't point to it anymore. The collecting_in_critical
- * function checks the PC of all threads and returns FALSE when all threads
- * are found to be outside objc_msgSend. This means any call to objc_msgSend
- * that could have had access to the garbage has finished or moved past the
- * cache lookup stage, so it is safe to free the memory.
- *
- * All functions that modify cache data or structures must acquire the
- * cacheUpdateLock to prevent interference from concurrent modifications.
- * The function that frees cache garbage must acquire the cacheUpdateLock
- * and use collecting_in_critical() to flush out cache readers.
- * The cacheUpdateLock is also used to protect the custom allocator used
- * for large method cache blocks.
- *
- * Cache readers (PC-checked by collecting_in_critical())
- * objc_msgSend*
- * _cache_getImp
- * _cache_getMethod
- *
- * Cache writers (hold cacheUpdateLock while reading or writing; not PC-checked)
- * _cache_fill (acquires lock)
- * _cache_expand (only called from cache_fill)
- * _cache_create (only called from cache_expand)
- * bcopy (only called from instrumented cache_expand)
- * flush_caches (acquires lock)
- * _cache_flush (only called from cache_fill and flush_caches)
- * _cache_collect_free (only called from cache_expand and cache_flush)
- *
- * UNPROTECTED cache readers (NOT thread-safe; used for debug info only)
- * _cache_print
- * _class_printMethodCaches
- * _class_printDuplicateCacheEntries
- * _class_printMethodCacheStatistics
- *
- * _class_lookupMethodAndLoadCache is a special case. It may read a
- * method triplet out of one cache and store it in another cache. This
- * is unsafe if the method triplet is a forward:: entry, because the
- * triplet itself could be freed unless _class_lookupMethodAndLoadCache
- * were PC-checked or used a lock. Additionally, storing the method
- * triplet in both caches would result in double-freeing if both caches
- * were flushed or expanded. The solution is for _cache_getMethod to
- * ignore all entries whose implementation is _objc_msgForward_internal,
- * so _class_lookupMethodAndLoadCache cannot look at a forward:: entry
- * unsafely or place it in multiple caches.
- ***********************************************************************/
-
-#include "objc-private.h"
-#include "hashtable2.h"
-
-typedef struct {
- SEL name; // same layout as struct old_method
- void *unused;
- IMP imp; // same layout as struct old_method
-} cache_entry;
-
-#if __OBJC2__
-
-#ifndef __LP64__
-# define CACHE_HASH(sel, mask) (((uintptr_t)(sel)>>2) & (mask))
-#else
-# define CACHE_HASH(sel, mask) (((unsigned int)((uintptr_t)(sel)>>0)) & (mask))
-#endif
-
-struct objc_cache {
- uintptr_t mask; /* total = mask + 1 */
- uintptr_t occupied;
- cache_entry *buckets[1];
-};
-
-#define CACHE_BUCKET(e) ((cache_entry *)e)
-
-#else
-
-/* Most definitions are in runtime.h */
-#define CACHE_BUCKET(e) ((Method)e)
-
-#endif
-
-
-/* When _class_slow_grow is non-zero, any given cache is actually grown
- * only on the odd-numbered times it becomes full; on the even-numbered
- * times, it is simply emptied and re-used. When this flag is zero,
- * caches are grown every time. */
-static const int _class_slow_grow = 1;
-
-/* For min cache size: clear_cache=1, slow_grow=1
- For max cache size: clear_cache=0, slow_grow=0 */
-
-/* Initial cache bucket count. INIT_CACHE_SIZE must be a power of two. */
-enum {
- INIT_CACHE_SIZE_LOG2 = 2,
- INIT_CACHE_SIZE = (1 << INIT_CACHE_SIZE_LOG2)
-};
-
-
-/* Amount of space required for `count` hash table buckets, knowing that
- * one entry is embedded in the cache structure itself. */
-#define TABLE_SIZE(count) ((count - 1) * sizeof(cache_entry *))
-
-
-#if !TARGET_OS_WIN32
-# define CACHE_ALLOCATOR
-#endif
-
-/* Custom cache allocator parameters.
- * CACHE_REGION_SIZE must be a multiple of CACHE_QUANTUM. */
-#define CACHE_ALLOCATOR_MIN 512
-#define CACHE_QUANTUM (CACHE_ALLOCATOR_MIN+sizeof(struct objc_cache)-sizeof(cache_entry*))
-#define CACHE_REGION_SIZE ((128*1024 / CACHE_QUANTUM) * CACHE_QUANTUM)
-// #define CACHE_REGION_SIZE ((256*1024 / CACHE_QUANTUM) * CACHE_QUANTUM)
-
-static uintptr_t cache_allocator_mask_for_size(size_t size)
-{
- return (size - sizeof(struct objc_cache)) / sizeof(cache_entry *);
-}
-
-static size_t cache_allocator_size_for_mask(uintptr_t mask)
-{
- size_t requested = sizeof(struct objc_cache) + TABLE_SIZE(mask+1);
- size_t actual = CACHE_QUANTUM;
- while (actual < requested) actual += CACHE_QUANTUM;
- return actual;
-}
-
-
-/* Cache instrumentation data. Immediately follows the cache block itself. */
-#ifdef OBJC_INSTRUMENTED
-typedef struct
-{
- unsigned int hitCount; // cache lookup success tally
- unsigned int hitProbes; // sum entries checked to hit
- unsigned int maxHitProbes; // max entries checked to hit
- unsigned int missCount; // cache lookup no-find tally
- unsigned int missProbes; // sum entries checked to miss
- unsigned int maxMissProbes; // max entries checked to miss
- unsigned int flushCount; // cache flush tally
- unsigned int flushedEntries; // sum cache entries flushed
- unsigned int maxFlushedEntries; // max cache entries flushed
-} CacheInstrumentation;
-
-#define CACHE_INSTRUMENTATION(cache) (CacheInstrumentation *) &cache->buckets[cache->mask + 1];
-#endif
-
-/* Cache filling and flushing instrumentation */
-
-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;
-#endif
-
-
-/***********************************************************************
-* A static empty cache. All classes initially point at this cache.
-* When the first message is sent it misses in the cache, and when
-* the cache is grown it checks for this case and uses malloc rather
-* than realloc. This avoids the need to check for NULL caches in the
-* messenger.
-***********************************************************************/
-
-struct objc_cache _objc_empty_cache =
-{
- 0, // mask
- 0, // occupied
- { NULL } // buckets
-};
-#ifdef OBJC_INSTRUMENTED
-CacheInstrumentation emptyCacheInstrumentation = {0};
-#endif
-
-
-/* Local prototypes */
-
-static BOOL _cache_isEmpty(Cache cache);
-static Cache _cache_malloc(uintptr_t slotCount);
-static Cache _cache_create(Class cls);
-static Cache _cache_expand(Class cls);
-#if __OBJC2__
-static void _cache_flush(Class cls);
-#endif
-
-static int _collecting_in_critical(void);
-static void _garbage_make_room(void);
-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 void cache_allocator_free(void *block);
-#endif
-
-/***********************************************************************
-* Cache statistics for OBJC_PRINT_CACHE_SETUP
-**********************************************************************/
-static unsigned int cache_counts[16];
-static size_t cache_allocations;
-static size_t cache_collections;
-static size_t cache_allocator_regions;
-
-static size_t log2u(size_t x)
-{
- unsigned int log;
-
- log = 0;
- while (x >>= 1)
- log += 1;
-
- return log;
-}
-
-
-/***********************************************************************
-* _cache_isEmpty.
-* Returns YES if the given cache is some empty cache.
-* Empty caches should never be allocated on the heap.
-**********************************************************************/
-static BOOL _cache_isEmpty(Cache cache)
-{
- return (cache == NULL || cache == (Cache)&_objc_empty_cache || cache->mask == 0);
-}
-
-
-/***********************************************************************
-* _cache_malloc.
-*
-* Called from _cache_create() and cache_expand()
-* Cache locks: cacheUpdateLock must be held by the caller.
-**********************************************************************/
-static Cache _cache_malloc(uintptr_t slotCount)
-{
- Cache new_cache;
- size_t size;
-
- mutex_assert_locked(&cacheUpdateLock);
-
- // Allocate table (why not check for failure?)
- size = sizeof(struct objc_cache) + TABLE_SIZE(slotCount);
-#if defined(OBJC_INSTRUMENTED)
- // Custom cache allocator can't handle instrumentation.
- size += sizeof(CacheInstrumentation);
- new_cache = _calloc_internal(size, 1);
- new_cache->mask = slotCount - 1;
-#elif !defined(CACHE_ALLOCATOR)
- // fixme cache allocator implementation isn't 64-bit clean
- new_cache = _calloc_internal(size, 1);
- new_cache->mask = (unsigned int)(slotCount - 1);
-#else
- if (size < CACHE_ALLOCATOR_MIN || UseInternalZone) {
- new_cache = _calloc_internal(size, 1);
- new_cache->mask = slotCount - 1;
- // occupied and buckets and instrumentation are all zero
- } else {
- new_cache = cache_allocator_calloc(size);
- // mask is already set
- // occupied and buckets and instrumentation are all zero
- }
-#endif
-
- if (PrintCaches) {
- size_t bucket = log2u(slotCount);
- if (bucket < sizeof(cache_counts) / sizeof(cache_counts[0])) {
- cache_counts[bucket]++;
- }
- cache_allocations++;
- }
-
- return new_cache;
-}
-
-/***********************************************************************
-* _cache_free_block.
-*
-* Called from _cache_free() and _cache_collect_free().
-* block may be a cache or a forward:: entry.
-* If block is a cache, forward:: entries it points to will NOT be freed.
-* Cache locks: cacheUpdateLock must be held by the caller.
-**********************************************************************/
-static void _cache_free_block(void *block)
-{
- mutex_assert_locked(&cacheUpdateLock);
-
-#if !TARGET_OS_WIN32
- if (PrintCaches) {
- Cache cache = (Cache)block;
- size_t slotCount = cache->mask + 1;
- if (isPowerOf2(slotCount)) {
- size_t bucket = log2u(slotCount);
- if (bucket < sizeof(cache_counts) / sizeof(cache_counts[0])) {
- cache_counts[bucket]--;
- }
- }
- }
-#endif
-
-#if defined(CACHE_ALLOCATOR)
- if (cache_allocator_is_block(block)) {
- cache_allocator_free(block);
- } else
-#endif
- {
- free(block);
- }
-}
-
-
-/***********************************************************************
-* _cache_free.
-*
-* Called from _objc_remove_classes_in_image().
-* forward:: entries in the cache ARE freed.
-* Cache locks: cacheUpdateLock must NOT be held by the caller.
-**********************************************************************/
-PRIVATE_EXTERN void _cache_free(Cache cache)
-{
- unsigned int i;
-
- mutex_lock(&cacheUpdateLock);
-
- for (i = 0; i < cache->mask + 1; i++) {
- cache_entry *entry = (cache_entry *)cache->buckets[i];
- if (entry && entry->imp == &_objc_msgForward_internal) {
- _cache_free_block(entry);
- }
- }
-
- _cache_free_block(cache);
-
- mutex_unlock(&cacheUpdateLock);
-}
-
-
-/***********************************************************************
-* _cache_create.
-*
-* Called from _cache_expand().
-* Cache locks: cacheUpdateLock must be held by the caller.
-**********************************************************************/
-static Cache _cache_create(Class cls)
-{
- Cache new_cache;
-
- mutex_assert_locked(&cacheUpdateLock);
-
- // Allocate new cache block
- new_cache = _cache_malloc(INIT_CACHE_SIZE);
-
- // Install the cache
- _class_setCache(cls, new_cache);
-
- // Clear the grow flag so that we will re-use the current storage,
- // rather than actually grow the cache, when expanding the cache
- // for the first time
- if (_class_slow_grow) {
- _class_setGrowCache(cls, NO);
- }
-
- // Return our creation
- return new_cache;
-}
-
-
-/***********************************************************************
-* _cache_expand.
-*
-* Called from _cache_fill ()
-* Cache locks: cacheUpdateLock must be held by the caller.
-**********************************************************************/
-static Cache _cache_expand(Class cls)
-{
- Cache old_cache;
- Cache new_cache;
- uintptr_t slotCount;
- uintptr_t index;
-
- mutex_assert_locked(&cacheUpdateLock);
-
- // First growth goes from empty cache to a real one
- old_cache = _class_getCache(cls);
- if (_cache_isEmpty(old_cache))
- return _cache_create (cls);
-
- if (_class_slow_grow) {
- // Cache grows every other time only.
- if (_class_shouldGrowCache(cls)) {
- // Grow the cache this time. Don't grow next time.
- _class_setGrowCache(cls, NO);
- }
- else {
- // Reuse the current cache storage this time. Do grow next time.
- _class_setGrowCache(cls, YES);
-
- // Clear the valid-entry counter
- old_cache->occupied = 0;
-
- // Invalidate all the cache entries
- for (index = 0; index < old_cache->mask + 1; index += 1)
- {
- // Remember what this entry was, so we can possibly
- // deallocate it after the bucket has been invalidated
- cache_entry *oldEntry = (cache_entry *)old_cache->buckets[index];
-
- // Skip invalid entry
- if (!oldEntry)
- continue;
-
- // Invalidate this entry
- old_cache->buckets[index] = NULL;
-
- // Deallocate "forward::" entry
- if (oldEntry->imp == &_objc_msgForward_internal) {
- _cache_collect_free (oldEntry, sizeof(cache_entry));
- }
- }
-
- // Return the same old cache, freshly emptied
- return old_cache;
- }
- }
-
- // Double the cache size
- slotCount = (old_cache->mask + 1) << 1;
-
- new_cache = _cache_malloc(slotCount);
-
-#ifdef OBJC_INSTRUMENTED
- // Propagate the instrumentation data
- {
- CacheInstrumentation *oldCacheData;
- CacheInstrumentation *newCacheData;
-
- oldCacheData = CACHE_INSTRUMENTATION(old_cache);
- newCacheData = CACHE_INSTRUMENTATION(new_cache);
- bcopy ((const char *)oldCacheData, (char *)newCacheData, sizeof(CacheInstrumentation));
- }
-#endif
-
- // 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) {
- _cache_collect_free (entry, sizeof(cache_entry));
- }
- }
-
- // Install new cache
- _class_setCache(cls, new_cache);
-
- // Deallocate old cache, try freeing all the garbage
- _cache_collect_free (old_cache, old_cache->mask * sizeof(cache_entry *));
- _cache_collect(false);
-
- return new_cache;
-}
-
-
-/***********************************************************************
-* _cache_fill. Add the specified method to the specified class' cache.
-* Returns NO if the cache entry wasn't added: cache was busy,
-* class is still being initialized, new entry is a duplicate.
-*
-* Called only from _class_lookupMethodAndLoadCache and
-* class_respondsToMethod and _cache_addForwardEntry.
-*
-* Cache locks: cacheUpdateLock must not be held.
-**********************************************************************/
-PRIVATE_EXTERN BOOL _cache_fill(Class cls, Method smt, SEL sel)
-{
- uintptr_t newOccupied;
- uintptr_t index;
- cache_entry **buckets;
- cache_entry *entry;
- Cache cache;
-
- mutex_assert_unlocked(&cacheUpdateLock);
-
- // Never cache before +initialize is done
- if (!_class_isInitialized(cls)) {
- return NO;
- }
-
- // Keep tally of cache additions
- totalCacheFills += 1;
-
- mutex_lock(&cacheUpdateLock);
-
- entry = (cache_entry *)smt;
-
- cache = _class_getCache(cls);
-
- // Make sure the entry wasn't added to the cache by some other thread
- // before we grabbed the cacheUpdateLock.
- // Don't use _cache_getMethod() because _cache_getMethod() doesn't
- // return forward:: entries.
- if (_cache_getImp(cls, sel)) {
- mutex_unlock(&cacheUpdateLock);
- return NO; // entry is already cached, didn't add new one
- }
-
- // Use the cache as-is if it is less than 3/4 full
- newOccupied = cache->occupied + 1;
- if ((newOccupied * 4) <= (cache->mask + 1) * 3) {
- // Cache is less than 3/4 full.
- cache->occupied = (unsigned int)newOccupied;
- } else {
- // Cache is too full. Expand it.
- cache = _cache_expand (cls);
-
- // Account for the addition
- cache->occupied += 1;
- }
-
- // Scan for the first unused slot and insert there.
- // There is guaranteed to be an empty slot because the
- // minimum size is 4 and we resized at 3/4 full.
- buckets = (cache_entry **)cache->buckets;
- for (index = CACHE_HASH(sel, cache->mask);
- buckets[index] != NULL;
- index = (index+1) & cache->mask)
- {
- // empty
- }
- buckets[index] = entry;
-
- mutex_unlock(&cacheUpdateLock);
-
- return YES; // successfully added new cache entry
-}
-
-
-/***********************************************************************
-* _cache_addForwardEntry
-* Add a forward:: entry for the given selector to cls's method cache.
-* Does nothing if the cache addition fails for any reason.
-* Called from class_respondsToMethod and _class_lookupMethodAndLoadCache.
-* Cache locks: cacheUpdateLock must not be held.
-**********************************************************************/
-PRIVATE_EXTERN void _cache_addForwardEntry(Class cls, SEL sel)
-{
- cache_entry *smt;
-
- smt = _malloc_internal(sizeof(cache_entry));
- smt->name = sel;
- 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);
- }
-}
-
-
-/***********************************************************************
-* _cache_addIgnoredEntry
-* Add an entry for the ignored selector to cls's method cache.
-* Does nothing if the cache addition fails for any reason.
-* Returns the ignored IMP.
-* Cache locks: cacheUpdateLock must not be held.
-**********************************************************************/
-#if SUPPORT_GC && !SUPPORT_IGNORED_SELECTOR_CONSTANT
-static cache_entry *alloc_ignored_entries(void)
-{
- cache_entry *e = malloc(5 * sizeof(cache_entry));
- e[0] = (cache_entry){ @selector(retain), 0,(IMP)&_objc_ignored_method};
- e[1] = (cache_entry){ @selector(release), 0,(IMP)&_objc_ignored_method};
- e[2] = (cache_entry){ @selector(autorelease),0,(IMP)&_objc_ignored_method};
- e[3] = (cache_entry){ @selector(retainCount),0,(IMP)&_objc_ignored_method};
- e[4] = (cache_entry){ @selector(dealloc), 0,(IMP)&_objc_ignored_method};
- return e;
-}
-#endif
-
-PRIVATE_EXTERN IMP _cache_addIgnoredEntry(Class cls, SEL sel)
-{
- cache_entry *entryp = NULL;
-
-#if !SUPPORT_GC
- _objc_fatal("selector ignored with GC off");
-#elif SUPPORT_IGNORED_SELECTOR_CONSTANT
- static cache_entry entry = { (SEL)kIgnore, 0, (IMP)&_objc_ignored_method };
- entryp = &entry;
- assert(sel == (SEL)kIgnore);
-#else
- // hack
- int i;
- static cache_entry *entries;
- INIT_ONCE_PTR(entries, alloc_ignored_entries(), free(v));
-
- assert(ignoreSelector(sel));
- for (i = 0; i < 5; i++) {
- if (sel == entries[i].name) {
- entryp = &entries[i];
- break;
- }
- }
- if (!entryp) _objc_fatal("selector %s (%p) is not ignored",
- sel_getName(sel), sel);
-#endif
-
- _cache_fill(cls, (Method)entryp, sel);
- return entryp->imp;
-}
-
-
-/***********************************************************************
-* _cache_flush. Invalidate all valid entries in the given class' cache.
-*
-* Called from flush_caches() and _cache_fill()
-* Cache locks: cacheUpdateLock must be held by the caller.
-**********************************************************************/
-#if __OBJC2__
-static
-#else
-PRIVATE_EXTERN
-#endif
-void _cache_flush(Class cls)
-{
- Cache cache;
- unsigned int index;
-
- mutex_assert_locked(&cacheUpdateLock);
-
- // Locate cache. Ignore unused cache.
- cache = _class_getCache(cls);
- if (_cache_isEmpty(cache)) return;
-
-#ifdef OBJC_INSTRUMENTED
- {
- CacheInstrumentation *cacheData;
-
- // Tally this flush
- cacheData = CACHE_INSTRUMENTATION(cache);
- cacheData->flushCount += 1;
- cacheData->flushedEntries += cache->occupied;
- if (cache->occupied > cacheData->maxFlushedEntries)
- cacheData->maxFlushedEntries = cache->occupied;
- }
-#endif
-
- // Traverse the cache
- for (index = 0; index <= cache->mask; index += 1)
- {
- // Remember what this entry was, so we can possibly
- // deallocate it after the bucket has been invalidated
- cache_entry *oldEntry = (cache_entry *)cache->buckets[index];
-
- // Invalidate this entry
- cache->buckets[index] = NULL;
-
- // Deallocate "forward::" entry
- if (oldEntry && oldEntry->imp == &_objc_msgForward_internal)
- _cache_collect_free (oldEntry, sizeof(cache_entry));
- }
-
- // Clear the valid-entry counter
- cache->occupied = 0;
-}
-
-
-/***********************************************************************
-* 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)
-{
- if (cls) {
- mutex_lock(&cacheUpdateLock);
- _cache_flush(cls);
- mutex_unlock(&cacheUpdateLock);
- }
-}
-
-
-/***********************************************************************
-* cache collection.
-**********************************************************************/
-
-#if !TARGET_OS_WIN32
-
-// A sentinel (magic value) to report bad thread_get_state status.
-// Must not be a valid PC.
-// Must not be zero - thread_get_state() on a new thread returns PC == 0.
-#define PC_SENTINEL 1
-
-// UNIX03 compliance hack (4508809)
-#if !__DARWIN_UNIX03
-#define __srr0 srr0
-#define __eip eip
-#endif
-
-static uintptr_t _get_pc_for_thread(thread_t thread)
-#if defined(__i386__)
-{
- i386_thread_state_t state;
- unsigned int count = i386_THREAD_STATE_COUNT;
- kern_return_t okay = thread_get_state (thread, i386_THREAD_STATE, (thread_state_t)&state, &count);
- return (okay == KERN_SUCCESS) ? state.__eip : PC_SENTINEL;
-}
-#elif defined(__x86_64__)
-{
- x86_thread_state64_t state;
- unsigned int count = x86_THREAD_STATE64_COUNT;
- kern_return_t okay = thread_get_state (thread, x86_THREAD_STATE64, (thread_state_t)&state, &count);
- return (okay == KERN_SUCCESS) ? state.__rip : PC_SENTINEL;
-}
-#elif defined(__arm__)
-{
- arm_thread_state_t state;
- unsigned int count = ARM_THREAD_STATE_COUNT;
- kern_return_t okay = thread_get_state (thread, ARM_THREAD_STATE, (thread_state_t)&state, &count);
- return (okay == KERN_SUCCESS) ? state.__pc : PC_SENTINEL;
-}
-#else
-{
-#error _get_pc_for_thread () not implemented for this architecture
-}
-#endif
-
-#endif
-
-/***********************************************************************
-* _collecting_in_critical.
-* Returns TRUE if some thread is currently executing a cache-reading
-* function. Collection of cache garbage is not allowed when a cache-
-* reading function is in progress because it might still be using
-* the garbage memory.
-**********************************************************************/
-OBJC_EXPORT uintptr_t objc_entryPoints[];
-OBJC_EXPORT uintptr_t objc_exitPoints[];
-
-static int _collecting_in_critical(void)
-{
-#if TARGET_OS_WIN32
- return TRUE;
-#else
- thread_act_port_array_t threads;
- unsigned number;
- unsigned count;
- kern_return_t ret;
- int result;
-
- mach_port_t mythread = pthread_mach_thread_np(pthread_self());
-
- // Get a list of all the threads in the current task
- ret = task_threads (mach_task_self (), &threads, &number);
- if (ret != KERN_SUCCESS)
- {
- _objc_fatal("task_thread failed (result %d)\n", ret);
- }
-
- // Check whether any thread is in the cache lookup code
- result = FALSE;
- for (count = 0; count < number; count++)
- {
- int region;
- uintptr_t pc;
-
- // Don't bother checking ourselves
- if (threads[count] == mythread)
- continue;
-
- // Find out where thread is executing
- pc = _get_pc_for_thread (threads[count]);
-
- // Check for bad status, and if so, assume the worse (can't collect)
- if (pc == PC_SENTINEL)
- {
- result = TRUE;
- goto done;
- }
-
- // Check whether it is in the cache lookup code
- for (region = 0; objc_entryPoints[region] != 0; region++)
- {
- if ((pc >= objc_entryPoints[region]) &&
- (pc <= objc_exitPoints[region]))
- {
- result = TRUE;
- goto done;
- }
- }
- }
-
- done:
- // Deallocate the port rights for the threads
- for (count = 0; count < number; count++) {
- mach_port_deallocate(mach_task_self (), threads[count]);
- }
-
- // Deallocate the thread list
- vm_deallocate (mach_task_self (), (vm_address_t) threads, sizeof(threads[0]) * number);
-
- // Return our finding
- return result;
-#endif
-}
-
-
-/***********************************************************************
-* _garbage_make_room. Ensure that there is enough room for at least
-* one more ref in the garbage.
-**********************************************************************/
-
-// amount of memory represented by all refs in the garbage
-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;
-
-// table of refs to free
-static void **garbage_refs = 0;
-
-// current number of refs in garbage_refs
-static size_t garbage_count = 0;
-
-// capacity of current garbage_refs
-static size_t garbage_max = 0;
-
-// capacity of initial garbage_refs
-enum {
- INIT_GARBAGE_COUNT = 128
-};
-
-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_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_max *= 2;
- }
-}
-
-
-/***********************************************************************
-* _cache_collect_free. Add the specified malloc'd memory to the list
-* of them to free at some later point.
-* size is used for the collection threshold. It does not have to be
-* precisely the block's size.
-* Cache locks: cacheUpdateLock must be held by the caller.
-**********************************************************************/
-static void _cache_collect_free(void *data, size_t size)
-{
- mutex_assert_locked(&cacheUpdateLock);
-
- _garbage_make_room ();
- garbage_byte_size += size;
- garbage_refs[garbage_count++] = data;
-}
-
-
-/***********************************************************************
-* _cache_collect. Try to free accumulated dead caches.
-* collectALot tries harder to free memory.
-* Cache locks: cacheUpdateLock must be held by the caller.
-**********************************************************************/
-PRIVATE_EXTERN void _cache_collect(bool collectALot)
-{
- mutex_assert_locked(&cacheUpdateLock);
-
- // Done if the garbage is not full
- if (garbage_byte_size < garbage_threshold && !collectALot) {
- return;
- }
-
- // Synchronize collection with objc_msgSend and other cache readers
- if (!collectALot) {
- if (_collecting_in_critical ()) {
- // objc_msgSend (or other cache reader) is currently looking in
- // the cache and might still be using some garbage.
- if (PrintCaches) {
- _objc_inform ("CACHES: not collecting; "
- "objc_msgSend in progress");
- }
- return;
- }
- }
- else {
- // No excuses.
- while (_collecting_in_critical())
- ;
- }
-
- // No cache readers in progress - garbage is now deletable
-
- // Log our progress
- if (PrintCaches) {
- cache_collections++;
- _objc_inform ("CACHES: COLLECTING %zu bytes (%zu regions, %zu allocations, %zu collections)", garbage_byte_size, cache_allocator_regions, cache_allocations, cache_collections);
- }
-
- // Dispose all refs now in the garbage
- while (garbage_count--) {
- _cache_free_block(garbage_refs[garbage_count]);
- }
-
- // Clear the garbage count and total size indicator
- garbage_count = 0;
- garbage_byte_size = 0;
-
- if (PrintCaches) {
- int i;
- size_t total = 0;
- size_t ideal_total = 0;
- size_t malloc_total = 0;
- size_t local_total = 0;
-
- for (i = 0; i < sizeof(cache_counts) / sizeof(cache_counts[0]); i++) {
- int count = cache_counts[i];
- int slots = 1 << i;
- size_t size = sizeof(struct objc_cache) + TABLE_SIZE(slots);
- size_t ideal = size;
-#if TARGET_OS_WIN32
- size_t malloc = size;
-#else
- size_t malloc = malloc_good_size(size);
-#endif
- size_t local = size < CACHE_ALLOCATOR_MIN ? malloc : cache_allocator_size_for_mask(cache_allocator_mask_for_size(size));
-
- if (!count) continue;
-
- _objc_inform("CACHES: %4d slots: %4d caches, %6zu / %6zu / %6zu bytes ideal/malloc/local, %6zu / %6zu bytes wasted malloc/local", slots, count, ideal*count, malloc*count, local*count, malloc*count-ideal*count, local*count-ideal*count);
-
- total += count;
- ideal_total += ideal*count;
- malloc_total += malloc*count;
- local_total += local*count;
- }
-
- _objc_inform("CACHES: total: %4zu caches, %6zu / %6zu / %6zu bytes ideal/malloc/local, %6zu / %6zu bytes wasted malloc/local", total, ideal_total, malloc_total, local_total, malloc_total-ideal_total, local_total-ideal_total);
- }
-}
-
-
-
-
-
-#if defined(CACHE_ALLOCATOR)
-
-/***********************************************************************
-* Custom method cache allocator.
-* Method cache block sizes are 2^slots+2 words, which is a pessimal
-* case for the system allocator. It wastes 504 bytes per cache block
-* with 128 or more slots, which adds up to tens of KB for an AppKit process.
-* To save memory, the custom cache allocator below is used.
-*
-* The cache allocator uses 128 KB allocation regions. Few processes will
-* require a second region. Within a region, allocation is address-ordered
-* first fit.
-*
-* The cache allocator uses a quantum of 520.
-* Cache block ideal sizes: 520, 1032, 2056, 4104
-* Cache allocator sizes: 520, 1040, 2080, 4160
-*
-* Because all blocks are known to be genuine method caches, the ordinary
-* cache->mask and cache->occupied fields are used as block headers.
-* No out-of-band headers are maintained. The number of blocks will
-* almost always be fewer than 200, so for simplicity there is no free
-* list or other optimization.
-*
-* Block in use: mask != 0, occupied != -1 (mask indicates block size)
-* Block free: mask != 0, occupied == -1 (mask is precisely block size)
-*
-* No cache allocator functions take any locks. Instead, the caller
-* must hold the cacheUpdateLock.
-*
-* fixme with 128 KB regions and 520 B min block size, an allocation
-* bitmap would be only 32 bytes - better than free list?
-**********************************************************************/
-
-typedef struct cache_allocator_block {
- uintptr_t size;
- uintptr_t state;
- struct cache_allocator_block *nextFree;
-} cache_allocator_block;
-
-typedef struct cache_allocator_region {
- cache_allocator_block *start;
- cache_allocator_block *end; // first non-block address
- cache_allocator_block *freeList;
- struct cache_allocator_region *next;
-} cache_allocator_region;
-
-static cache_allocator_region *cacheRegion = NULL;
-
-
-/***********************************************************************
-* cache_allocator_add_region
-* Allocates and returns a new region that can hold at least size
-* bytes of large method caches.
-* The actual size will be rounded up to a CACHE_QUANTUM boundary,
-* with a minimum of CACHE_REGION_SIZE.
-* The new region is lowest-priority for new allocations. Callers that
-* know the other regions are already full should allocate directly
-* into the returned region.
-**********************************************************************/
-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 =
- _calloc_internal(1, sizeof(cache_allocator_region));
-
- // Round size up to quantum boundary, and apply the minimum size.
- size += CACHE_QUANTUM - (size % CACHE_QUANTUM);
- if (size < CACHE_REGION_SIZE) size = CACHE_REGION_SIZE;
-
- // Allocate the region
- addr = (vm_address_t)calloc(size, 1);
- newRegion->start = (cache_allocator_block *)addr;
- newRegion->end = (cache_allocator_block *)(addr + size);
-
- // Mark the first block: free and covers the entire region
- b = newRegion->start;
- b->size = size;
- b->state = (uintptr_t)-1;
- b->nextFree = NULL;
- newRegion->freeList = b;
-
- // Add to end of the linked list of regions.
- // Other regions should be re-used before this one is touched.
- newRegion->next = NULL;
- rgnP = &cacheRegion;
- while (*rgnP) {
- rgnP = &(**rgnP).next;
- }
- *rgnP = newRegion;
-
- cache_allocator_regions++;
-
- return newRegion;
-}
-
-
-/***********************************************************************
-* cache_allocator_coalesce
-* Attempts to coalesce a free block with the single free block following
-* it in the free list, if any.
-**********************************************************************/
-static void cache_allocator_coalesce(cache_allocator_block *block)
-{
- if (block->size + (uintptr_t)block == (uintptr_t)block->nextFree) {
- block->size += block->nextFree->size;
- block->nextFree = block->nextFree->nextFree;
- }
-}
-
-
-/***********************************************************************
-* cache_region_calloc
-* Attempt to allocate a size-byte block in the given region.
-* Allocation is first-fit. The free list is already fully coalesced.
-* Returns NULL if there is not enough room in the region for the block.
-**********************************************************************/
-static void *cache_region_calloc(cache_allocator_region *rgn, size_t size)
-{
- cache_allocator_block **blockP;
- uintptr_t mask;
-
- // Save mask for allocated block, then round size
- // up to CACHE_QUANTUM boundary
- mask = cache_allocator_mask_for_size(size);
- size = cache_allocator_size_for_mask(mask);
-
- // Search the free list for a sufficiently large free block.
-
- for (blockP = &rgn->freeList;
- *blockP != NULL;
- blockP = &(**blockP).nextFree)
- {
- cache_allocator_block *block = *blockP;
- if (block->size < size) continue; // not big enough
-
- // block is now big enough. Allocate from it.
-
- // Slice off unneeded fragment of block, if any,
- // and reconnect the free list around block.
- if (block->size - size >= CACHE_QUANTUM) {
- cache_allocator_block *leftover =
- (cache_allocator_block *)(size + (uintptr_t)block);
- leftover->size = block->size - size;
- leftover->state = (uintptr_t)-1;
- leftover->nextFree = block->nextFree;
- *blockP = leftover;
- } else {
- *blockP = block->nextFree;
- }
-
- // block is now exactly the right size.
-
- bzero(block, size);
- block->size = mask; // Cache->mask
- block->state = 0; // Cache->occupied
-
- return block;
- }
-
- // No room in this region.
- return NULL;
-}
-
-
-/***********************************************************************
-* cache_allocator_calloc
-* Custom allocator for large method caches (128+ slots)
-* The returned cache block already has cache->mask set.
-* 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)
-{
- cache_allocator_region *rgn;
-
- mutex_assert_locked(&cacheUpdateLock);
-
- for (rgn = cacheRegion; rgn != NULL; rgn = rgn->next) {
- void *p = cache_region_calloc(rgn, size);
- if (p) {
- return 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);
-}
-
-
-/***********************************************************************
-* cache_allocator_region_for_block
-* Returns the cache allocator region that ptr points into, or NULL.
-**********************************************************************/
-static cache_allocator_region *cache_allocator_region_for_block(cache_allocator_block *block)
-{
- cache_allocator_region *rgn;
- for (rgn = cacheRegion; rgn != NULL; rgn = rgn->next) {
- if (block >= rgn->start && block < rgn->end) return rgn;
- }
- return NULL;
-}
-
-
-/***********************************************************************
-* cache_allocator_is_block
-* If ptr is a live block from the cache allocator, return YES
-* If ptr is a block from some other allocator, return NO.
-* If ptr is a dead block from the cache allocator, result is undefined.
-* Cache locks: cacheUpdateLock must be held by the caller
-**********************************************************************/
-static BOOL cache_allocator_is_block(void *ptr)
-{
- mutex_assert_locked(&cacheUpdateLock);
- return (cache_allocator_region_for_block((cache_allocator_block *)ptr) != NULL);
-}
-
-/***********************************************************************
-* cache_allocator_free
-* Frees a block allocated by the cache allocator.
-* Cache locks: cacheUpdateLock must be held by the caller.
-**********************************************************************/
-static void cache_allocator_free(void *ptr)
-{
- cache_allocator_block *dead = (cache_allocator_block *)ptr;
- cache_allocator_block *cur;
- cache_allocator_region *rgn;
-
- mutex_assert_locked(&cacheUpdateLock);
-
- if (! (rgn = cache_allocator_region_for_block(ptr))) {
- // free of non-pointer
- _objc_inform("cache_allocator_free of non-pointer %p", ptr);
- return;
- }
-
- dead->size = cache_allocator_size_for_mask(dead->size);
- dead->state = (uintptr_t)-1;
-
- if (!rgn->freeList || rgn->freeList > dead) {
- // dead block belongs at front of free list
- dead->nextFree = rgn->freeList;
- rgn->freeList = dead;
- cache_allocator_coalesce(dead);
- return;
- }
-
- // dead block belongs in the middle or end of free list
- for (cur = rgn->freeList; cur != NULL; cur = cur->nextFree) {
- cache_allocator_block *ahead = cur->nextFree;
-
- if (!ahead || ahead > dead) {
- // cur and ahead straddle dead, OR dead belongs at end of free list
- cur->nextFree = dead;
- dead->nextFree = ahead;
-
- // coalesce into dead first in case both succeed
- cache_allocator_coalesce(dead);
- cache_allocator_coalesce(cur);
- return;
- }
- }
-
- // uh-oh
- _objc_inform("cache_allocator_free of non-pointer %p", ptr);
-}
-
-// defined(CACHE_ALLOCATOR)
-#endif
-
-/***********************************************************************
-* Cache instrumentation and debugging
-**********************************************************************/
-
-#ifdef OBJC_INSTRUMENTED
-enum {
- CACHE_HISTOGRAM_SIZE = 512
-};
-
-unsigned int CacheHitHistogram [CACHE_HISTOGRAM_SIZE];
-unsigned int CacheMissHistogram [CACHE_HISTOGRAM_SIZE];
-#endif
-
-
-/***********************************************************************
-* _cache_print.
-**********************************************************************/
-static void _cache_print(Cache cache)
-{
- uintptr_t index;
- uintptr_t count;
-
- count = cache->mask + 1;
- for (index = 0; index < count; index += 1) {
- cache_entry *entry = (cache_entry *)cache->buckets[index];
- if (entry) {
- if (entry->imp == &_objc_msgForward_internal)
- printf ("does not recognize: \n");
- printf ("%s\n", sel_getName(entry->name));
- }
- }
-}
-
-
-/***********************************************************************
-* _class_printMethodCaches.
-**********************************************************************/
-PRIVATE_EXTERN void _class_printMethodCaches(Class cls)
-{
- if (_cache_isEmpty(_class_getCache(cls))) {
- printf("no instance-method cache for class %s\n", _class_getName(cls));
- } else {
- printf("instance-method cache for class %s:\n", _class_getName(cls));
- _cache_print(_class_getCache(cls));
- }
-
- if (_cache_isEmpty(_class_getCache(((id)cls)->isa))) {
- printf("no class-method cache for class %s\n", _class_getName(cls));
- } else {
- printf ("class-method cache for class %s:\n", _class_getName(cls));
- _cache_print(_class_getCache(((id)cls)->isa));
- }
-}
-
-
-#if 0
-#warning fixme
-
-
-/***********************************************************************
-* _class_printDuplicateCacheEntries.
-**********************************************************************/
-void _class_printDuplicateCacheEntries(BOOL detail)
-{
- NXHashState state;
- Class cls;
- unsigned int duplicates;
- unsigned int index1;
- unsigned int index2;
- unsigned int mask;
- unsigned int count;
- unsigned int isMeta;
- Cache cache;
-
-
- printf ("Checking for duplicate cache entries \n");
-
- // Outermost loop - iterate over all classes
- state = NXInitHashState (class_hash);
- duplicates = 0;
- while (NXNextHashState (class_hash, &state, (void **) &cls))
- {
- // Control loop - do given class' cache, then its isa's cache
- for (isMeta = 0; isMeta <= 1; isMeta += 1)
- {
- // Select cache of interest and make sure it exists
- cache = _class_getCache(isMeta ? ((id)cls)->isa : cls);
- if (_cache_isEmpty(cache))
- continue;
-
- // Middle loop - check each entry in the given cache
- mask = cache->mask;
- count = mask + 1;
- for (index1 = 0; index1 < count; index1 += 1)
- {
- // Skip invalid entry
- if (!cache->buckets[index1])
- continue;
-
- // Inner loop - check that given entry matches no later entry
- for (index2 = index1 + 1; index2 < count; index2 += 1)
- {
- // Skip invalid entry
- if (!cache->buckets[index2])
- continue;
-
- // Check for duplication by method name comparison
- if (strcmp ((char *) cache->buckets[index1]->name),
- (char *) cache->buckets[index2]->name)) == 0)
- {
- if (detail)
- printf ("%s %s\n", _class_getName(cls), sel_getName(cache->buckets[index1]->name));
- duplicates += 1;
- break;
- }
- }
- }
- }
- }
-
- // Log the findings
- printf ("duplicates = %d\n", duplicates);
- printf ("total cache fills = %d\n", totalCacheFills);
-}
-
-
-/***********************************************************************
-* PrintCacheHeader.
-**********************************************************************/
-static void PrintCacheHeader(void)
-{
-#ifdef OBJC_INSTRUMENTED
- printf ("Cache Cache Slots Avg Max AvgS MaxS AvgS MaxS TotalD AvgD MaxD TotalD AvgD MaxD TotD AvgD MaxD\n");
- printf ("Size Count Used Used Used Hit Hit Miss Miss Hits Prbs Prbs Misses Prbs Prbs Flsh Flsh Flsh\n");
- printf ("----- ----- ----- ----- ---- ---- ---- ---- ---- ------- ---- ---- ------- ---- ---- ---- ---- ----\n");
-#else
- printf ("Cache Cache Slots Avg Max AvgS MaxS AvgS MaxS\n");
- printf ("Size Count Used Used Used Hit Hit Miss Miss\n");
- printf ("----- ----- ----- ----- ---- ---- ---- ---- ----\n");
-#endif
-}
-
-
-/***********************************************************************
-* PrintCacheInfo.
-**********************************************************************/
-static void PrintCacheInfo(unsigned int cacheSize,
- unsigned int cacheCount,
- unsigned int slotsUsed,
- float avgUsed, unsigned int maxUsed,
- float avgSHit, unsigned int maxSHit,
- float avgSMiss, unsigned int maxSMiss
-#ifdef OBJC_INSTRUMENTED
- , unsigned int totDHits,
- float avgDHit,
- unsigned int maxDHit,
- unsigned int totDMisses,
- float avgDMiss,
- unsigned int maxDMiss,
- unsigned int totDFlsh,
- float avgDFlsh,
- unsigned int maxDFlsh
-#endif
- )
-{
-#ifdef OBJC_INSTRUMENTED
- printf ("%5u %5u %5u %5.1f %4u %4.1f %4u %4.1f %4u %7u %4.1f %4u %7u %4.1f %4u %4u %4.1f %4u\n",
-#else
- printf ("%5u %5u %5u %5.1f %4u %4.1f %4u %4.1f %4u\n",
-#endif
- cacheSize, cacheCount, slotsUsed, avgUsed, maxUsed, avgSHit, maxSHit, avgSMiss, maxSMiss
-#ifdef OBJC_INSTRUMENTED
- , totDHits, avgDHit, maxDHit, totDMisses, avgDMiss, maxDMiss, totDFlsh, avgDFlsh, maxDFlsh
-#endif
- );
-
-}
-
-
-#ifdef OBJC_INSTRUMENTED
-/***********************************************************************
-* PrintCacheHistogram. Show the non-zero entries from the specified
-* cache histogram.
-**********************************************************************/
-static void PrintCacheHistogram(char *title,
- unsigned int *firstEntry,
- unsigned int entryCount)
-{
- unsigned int index;
- unsigned int *thisEntry;
-
- printf ("%s\n", title);
- printf (" Probes Tally\n");
- printf (" ------ -----\n");
- for (index = 0, thisEntry = firstEntry;
- index < entryCount;
- index += 1, thisEntry += 1)
- {
- if (*thisEntry == 0)
- continue;
-
- printf (" %6d %5d\n", index, *thisEntry);
- }
-}
-#endif
-
-
-/***********************************************************************
-* _class_printMethodCacheStatistics.
-**********************************************************************/
-
-#define MAX_LOG2_SIZE 32
-#define MAX_CHAIN_SIZE 100
-
-void _class_printMethodCacheStatistics(void)
-{
- unsigned int isMeta;
- unsigned int index;
- NXHashState state;
- Class cls;
- unsigned int totalChain;
- unsigned int totalMissChain;
- unsigned int maxChain;
- unsigned int maxMissChain;
- unsigned int classCount;
- unsigned int negativeEntryCount;
- unsigned int cacheExpandCount;
- unsigned int cacheCountBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int totalEntriesBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int maxEntriesBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int totalChainBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int totalMissChainBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int totalMaxChainBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int totalMaxMissChainBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int maxChainBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int maxMissChainBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int chainCount[MAX_CHAIN_SIZE] = {0};
- unsigned int missChainCount[MAX_CHAIN_SIZE] = {0};
-#ifdef OBJC_INSTRUMENTED
- unsigned int hitCountBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int hitProbesBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int maxHitProbesBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int missCountBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int missProbesBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int maxMissProbesBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int flushCountBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int flushedEntriesBySize[2][MAX_LOG2_SIZE] = {{0}};
- unsigned int maxFlushedEntriesBySize[2][MAX_LOG2_SIZE] = {{0}};
-#endif
-
- printf ("Printing cache statistics\n");
-
- // Outermost loop - iterate over all classes
- state = NXInitHashState (class_hash);
- classCount = 0;
- negativeEntryCount = 0;
- cacheExpandCount = 0;
- while (NXNextHashState (class_hash, &state, (void **) &cls))
- {
- // Tally classes
- classCount += 1;
-
- // Control loop - do given class' cache, then its isa's cache
- for (isMeta = 0; isMeta <= 1; isMeta += 1)
- {
- Cache cache;
- unsigned int mask;
- unsigned int log2Size;
- unsigned int entryCount;
-
- // Select cache of interest
- cache = _class_getCache(isMeta ? ((id)cls)->isa : cls);
-
- // Ignore empty cache... should we?
- if (_cache_isEmpty(cache))
- continue;
-
- // Middle loop - do each entry in the given cache
- mask = cache->mask;
- entryCount = 0;
- totalChain = 0;
- totalMissChain = 0;
- maxChain = 0;
- maxMissChain = 0;
- for (index = 0; index < mask + 1; index += 1)
- {
- cache_entry **buckets;
- cache_entry *entry;
- unsigned int hash;
- unsigned int methodChain;
- unsigned int methodMissChain;
- unsigned int index2;
-
- // If entry is invalid, the only item of
- // interest is that future insert hashes
- // to this entry can use it directly.
- buckets = (cache_entry **)cache->buckets;
- if (!buckets[index])
- {
- missChainCount[0] += 1;
- continue;
- }
-
- entry = buckets[index];
-
- // Tally valid entries
- entryCount += 1;
-
- // Tally "forward::" entries
- if (entry->imp == &_objc_msgForward_internal)
- negativeEntryCount += 1;
-
- // Calculate search distance (chain length) for this method
- // The chain may wrap around to the beginning of the table.
- hash = CACHE_HASH(entry->name, mask);
- if (index >= hash) methodChain = index - hash;
- else methodChain = (mask+1) + index - hash;
-
- // Tally chains of this length
- if (methodChain < MAX_CHAIN_SIZE)
- chainCount[methodChain] += 1;
-
- // Keep sum of all chain lengths
- totalChain += methodChain;
-
- // Record greatest chain length
- if (methodChain > maxChain)
- maxChain = methodChain;
-
- // Calculate search distance for miss that hashes here
- index2 = index;
- while (buckets[index2])
- {
- index2 += 1;
- index2 &= mask;
- }
- methodMissChain = ((index2 - index) & mask);
-
- // Tally miss chains of this length
- if (methodMissChain < MAX_CHAIN_SIZE)
- missChainCount[methodMissChain] += 1;
-
- // Keep sum of all miss chain lengths in this class
- totalMissChain += methodMissChain;
-
- // Record greatest miss chain length
- if (methodMissChain > maxMissChain)
- maxMissChain = methodMissChain;
- }
-
- // Factor this cache into statistics about caches of the same
- // type and size (all caches are a power of two in size)
- log2Size = log2u (mask + 1);
- cacheCountBySize[isMeta][log2Size] += 1;
- totalEntriesBySize[isMeta][log2Size] += entryCount;
- if (entryCount > maxEntriesBySize[isMeta][log2Size])
- maxEntriesBySize[isMeta][log2Size] = entryCount;
- totalChainBySize[isMeta][log2Size] += totalChain;
- totalMissChainBySize[isMeta][log2Size] += totalMissChain;
- totalMaxChainBySize[isMeta][log2Size] += maxChain;
- totalMaxMissChainBySize[isMeta][log2Size] += maxMissChain;
- if (maxChain > maxChainBySize[isMeta][log2Size])
- maxChainBySize[isMeta][log2Size] = maxChain;
- if (maxMissChain > maxMissChainBySize[isMeta][log2Size])
- maxMissChainBySize[isMeta][log2Size] = maxMissChain;
-#ifdef OBJC_INSTRUMENTED
- {
- CacheInstrumentation *cacheData;
-
- cacheData = CACHE_INSTRUMENTATION(cache);
- hitCountBySize[isMeta][log2Size] += cacheData->hitCount;
- hitProbesBySize[isMeta][log2Size] += cacheData->hitProbes;
- if (cacheData->maxHitProbes > maxHitProbesBySize[isMeta][log2Size])
- maxHitProbesBySize[isMeta][log2Size] = cacheData->maxHitProbes;
- missCountBySize[isMeta][log2Size] += cacheData->missCount;
- missProbesBySize[isMeta][log2Size] += cacheData->missProbes;
- if (cacheData->maxMissProbes > maxMissProbesBySize[isMeta][log2Size])
- maxMissProbesBySize[isMeta][log2Size] = cacheData->maxMissProbes;
- flushCountBySize[isMeta][log2Size] += cacheData->flushCount;
- flushedEntriesBySize[isMeta][log2Size] += cacheData->flushedEntries;
- if (cacheData->maxFlushedEntries > maxFlushedEntriesBySize[isMeta][log2Size])
- maxFlushedEntriesBySize[isMeta][log2Size] = cacheData->maxFlushedEntries;
- }
-#endif
- // Caches start with a power of two number of entries, and grow by doubling, so
- // we can calculate the number of times this cache has expanded
- cacheExpandCount += log2Size - INIT_CACHE_SIZE_LOG2;
- }
- }
-
- {
- unsigned int cacheCountByType[2] = {0};
- unsigned int totalCacheCount = 0;
- unsigned int totalEntries = 0;
- unsigned int maxEntries = 0;
- unsigned int totalSlots = 0;
-#ifdef OBJC_INSTRUMENTED
- unsigned int totalHitCount = 0;
- unsigned int totalHitProbes = 0;
- unsigned int maxHitProbes = 0;
- unsigned int totalMissCount = 0;
- unsigned int totalMissProbes = 0;
- unsigned int maxMissProbes = 0;
- unsigned int totalFlushCount = 0;
- unsigned int totalFlushedEntries = 0;
- unsigned int maxFlushedEntries = 0;
-#endif
-
- totalChain = 0;
- maxChain = 0;
- totalMissChain = 0;
- maxMissChain = 0;
-
- // Sum information over all caches
- for (isMeta = 0; isMeta <= 1; isMeta += 1)
- {
- for (index = 0; index < MAX_LOG2_SIZE; index += 1)
- {
- cacheCountByType[isMeta] += cacheCountBySize[isMeta][index];
- totalEntries += totalEntriesBySize[isMeta][index];
- totalSlots += cacheCountBySize[isMeta][index] * (1 << index);
- totalChain += totalChainBySize[isMeta][index];
- if (maxEntriesBySize[isMeta][index] > maxEntries)
- maxEntries = maxEntriesBySize[isMeta][index];
- if (maxChainBySize[isMeta][index] > maxChain)
- maxChain = maxChainBySize[isMeta][index];
- totalMissChain += totalMissChainBySize[isMeta][index];
- if (maxMissChainBySize[isMeta][index] > maxMissChain)
- maxMissChain = maxMissChainBySize[isMeta][index];
-#ifdef OBJC_INSTRUMENTED
- totalHitCount += hitCountBySize[isMeta][index];
- totalHitProbes += hitProbesBySize[isMeta][index];
- if (maxHitProbesBySize[isMeta][index] > maxHitProbes)
- maxHitProbes = maxHitProbesBySize[isMeta][index];
- totalMissCount += missCountBySize[isMeta][index];
- totalMissProbes += missProbesBySize[isMeta][index];
- if (maxMissProbesBySize[isMeta][index] > maxMissProbes)
- maxMissProbes = maxMissProbesBySize[isMeta][index];
- totalFlushCount += flushCountBySize[isMeta][index];
- totalFlushedEntries += flushedEntriesBySize[isMeta][index];
- if (maxFlushedEntriesBySize[isMeta][index] > maxFlushedEntries)
- maxFlushedEntries = maxFlushedEntriesBySize[isMeta][index];
-#endif
- }
-
- totalCacheCount += cacheCountByType[isMeta];
- }
-
- // Log our findings
- printf ("There are %u classes\n", classCount);
-
- for (isMeta = 0; isMeta <= 1; isMeta += 1)
- {
- // Number of this type of class
- printf ("\nThere are %u %s-method caches, broken down by size (slot count):\n",
- cacheCountByType[isMeta],
- isMeta ? "class" : "instance");
-
- // Print header
- PrintCacheHeader ();
-
- // Keep format consistent even if there are caches of this kind
- if (cacheCountByType[isMeta] == 0)
- {
- printf ("(none)\n");
- continue;
- }
-
- // Usage information by cache size
- for (index = 0; index < MAX_LOG2_SIZE; index += 1)
- {
- unsigned int cacheCount;
- unsigned int cacheSlotCount;
- unsigned int cacheEntryCount;
-
- // Get number of caches of this type and size
- cacheCount = cacheCountBySize[isMeta][index];
- if (cacheCount == 0)
- continue;
-
- // Get the cache slot count and the total number of valid entries
- cacheSlotCount = (1 << index);
- cacheEntryCount = totalEntriesBySize[isMeta][index];
-
- // Give the analysis
- PrintCacheInfo (cacheSlotCount,
- cacheCount,
- cacheEntryCount,
- (float) cacheEntryCount / (float) cacheCount,
- maxEntriesBySize[isMeta][index],
- (float) totalChainBySize[isMeta][index] / (float) cacheEntryCount,
- maxChainBySize[isMeta][index],
- (float) totalMissChainBySize[isMeta][index] / (float) (cacheCount * cacheSlotCount),
- maxMissChainBySize[isMeta][index]
-#ifdef OBJC_INSTRUMENTED
- , hitCountBySize[isMeta][index],
- hitCountBySize[isMeta][index] ?
- (float) hitProbesBySize[isMeta][index] / (float) hitCountBySize[isMeta][index] : 0.0,
- maxHitProbesBySize[isMeta][index],
- missCountBySize[isMeta][index],
- missCountBySize[isMeta][index] ?
- (float) missProbesBySize[isMeta][index] / (float) missCountBySize[isMeta][index] : 0.0,
- maxMissProbesBySize[isMeta][index],
- flushCountBySize[isMeta][index],
- flushCountBySize[isMeta][index] ?
- (float) flushedEntriesBySize[isMeta][index] / (float) flushCountBySize[isMeta][index] : 0.0,
- maxFlushedEntriesBySize[isMeta][index]
-#endif
- );
- }
- }
-
- // Give overall numbers
- printf ("\nCumulative:\n");
- PrintCacheHeader ();
- PrintCacheInfo (totalSlots,
- totalCacheCount,
- totalEntries,
- (float) totalEntries / (float) totalCacheCount,
- maxEntries,
- (float) totalChain / (float) totalEntries,
- maxChain,
- (float) totalMissChain / (float) totalSlots,
- maxMissChain
-#ifdef OBJC_INSTRUMENTED
- , totalHitCount,
- totalHitCount ?
- (float) totalHitProbes / (float) totalHitCount : 0.0,
- maxHitProbes,
- totalMissCount,
- totalMissCount ?
- (float) totalMissProbes / (float) totalMissCount : 0.0,
- maxMissProbes,
- totalFlushCount,
- totalFlushCount ?
- (float) totalFlushedEntries / (float) totalFlushCount : 0.0,
- maxFlushedEntries
-#endif
- );
-
- printf ("\nNumber of \"forward::\" entries: %d\n", negativeEntryCount);
- printf ("Number of cache expansions: %d\n", cacheExpandCount);
-#ifdef OBJC_INSTRUMENTED
- printf ("flush_caches: total calls total visits average visits max visits total classes visits/class\n");
- printf (" ----------- ------------ -------------- ---------- ------------- -------------\n");
- printf (" linear %11u %12u %14.1f %10u %13u %12.2f\n",
- LinearFlushCachesCount,
- LinearFlushCachesVisitedCount,
- LinearFlushCachesCount ?
- (float) LinearFlushCachesVisitedCount / (float) LinearFlushCachesCount : 0.0,
- MaxLinearFlushCachesVisitedCount,
- LinearFlushCachesVisitedCount,
- 1.0);
- printf (" nonlinear %11u %12u %14.1f %10u %13u %12.2f\n",
- NonlinearFlushCachesCount,
- NonlinearFlushCachesVisitedCount,
- NonlinearFlushCachesCount ?
- (float) NonlinearFlushCachesVisitedCount / (float) NonlinearFlushCachesCount : 0.0,
- MaxNonlinearFlushCachesVisitedCount,
- NonlinearFlushCachesClassCount,
- NonlinearFlushCachesClassCount ?
- (float) NonlinearFlushCachesVisitedCount / (float) NonlinearFlushCachesClassCount : 0.0);
- printf (" ideal %11u %12u %14.1f %10u %13u %12.2f\n",
- LinearFlushCachesCount + NonlinearFlushCachesCount,
- IdealFlushCachesCount,
- LinearFlushCachesCount + NonlinearFlushCachesCount ?
- (float) IdealFlushCachesCount / (float) (LinearFlushCachesCount + NonlinearFlushCachesCount) : 0.0,
- MaxIdealFlushCachesCount,
- LinearFlushCachesVisitedCount + NonlinearFlushCachesClassCount,
- LinearFlushCachesVisitedCount + NonlinearFlushCachesClassCount ?
- (float) IdealFlushCachesCount / (float) (LinearFlushCachesVisitedCount + NonlinearFlushCachesClassCount) : 0.0);
-
- PrintCacheHistogram ("\nCache hit histogram:", &CacheHitHistogram[0], CACHE_HISTOGRAM_SIZE);
- PrintCacheHistogram ("\nCache miss histogram:", &CacheMissHistogram[0], CACHE_HISTOGRAM_SIZE);
-#endif
-
-#if 0
- printf ("\nLookup chains:");
- for (index = 0; index < MAX_CHAIN_SIZE; index += 1)
- {
- if (chainCount[index] != 0)
- printf (" %u:%u", index, chainCount[index]);
- }
-
- printf ("\nMiss chains:");
- for (index = 0; index < MAX_CHAIN_SIZE; index += 1)
- {
- if (missChainCount[index] != 0)
- printf (" %u:%u", index, missChainCount[index]);
- }
-
- printf ("\nTotal memory usage for cache data structures: %lu bytes\n",
- totalCacheCount * (sizeof(struct objc_cache) - sizeof(cache_entry *)) +
- totalSlots * sizeof(cache_entry *) +
- negativeEntryCount * sizeof(cache_entry));
-#endif
- }
-}
-
-#endif
--- /dev/null
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/***********************************************************************
+* objc-cache.m
+* Method cache management
+* Cache flushing
+* Cache garbage collection
+* Cache instrumentation
+* Dedicated allocator for large caches
+**********************************************************************/
+
+
+/***********************************************************************
+ * Method cache locking (GrP 2001-1-14)
+ *
+ * For speed, objc_msgSend does not acquire any locks when it reads
+ * method caches. Instead, all cache changes are performed so that any
+ * objc_msgSend running concurrently with the cache mutator will not
+ * crash or hang or get an incorrect result from the cache.
+ *
+ * When cache memory becomes unused (e.g. the old cache after cache
+ * expansion), it is not immediately freed, because a concurrent
+ * objc_msgSend could still be using it. Instead, the memory is
+ * disconnected from the data structures and placed on a garbage list.
+ * The memory is now only accessible to instances of objc_msgSend that
+ * were running when the memory was disconnected; any further calls to
+ * objc_msgSend will not see the garbage memory because the other data
+ * structures don't point to it anymore. The collecting_in_critical
+ * function checks the PC of all threads and returns FALSE when all threads
+ * are found to be outside objc_msgSend. This means any call to objc_msgSend
+ * that could have had access to the garbage has finished or moved past the
+ * cache lookup stage, so it is safe to free the memory.
+ *
+ * All functions that modify cache data or structures must acquire the
+ * cacheUpdateLock to prevent interference from concurrent modifications.
+ * The function that frees cache garbage must acquire the cacheUpdateLock
+ * and use collecting_in_critical() to flush out cache readers.
+ * The cacheUpdateLock is also used to protect the custom allocator used
+ * for large method cache blocks.
+ *
+ * Cache readers (PC-checked by collecting_in_critical())
+ * objc_msgSend*
+ * _cache_getImp
+ * _cache_getMethod
+ *
+ * Cache writers (hold cacheUpdateLock while reading or writing; not PC-checked)
+ * _cache_fill (acquires lock)
+ * _cache_expand (only called from cache_fill)
+ * _cache_create (only called from cache_expand)
+ * bcopy (only called from instrumented cache_expand)
+ * flush_caches (acquires lock)
+ * _cache_flush (only called from cache_fill and flush_caches)
+ * _cache_collect_free (only called from cache_expand and cache_flush)
+ *
+ * UNPROTECTED cache readers (NOT thread-safe; used for debug info only)
+ * _cache_print
+ * _class_printMethodCaches
+ * _class_printDuplicateCacheEntries
+ * _class_printMethodCacheStatistics
+ *
+ * _class_lookupMethodAndLoadCache is a special case. It may read a
+ * method triplet out of one cache and store it in another cache. This
+ * is unsafe if the method triplet is a forward:: entry, because the
+ * triplet itself could be freed unless _class_lookupMethodAndLoadCache
+ * were PC-checked or used a lock. Additionally, storing the method
+ * triplet in both caches would result in double-freeing if both caches
+ * were flushed or expanded. The solution is for _cache_getMethod to
+ * ignore all entries whose implementation is _objc_msgForward_internal,
+ * so _class_lookupMethodAndLoadCache cannot look at a forward:: entry
+ * unsafely or place it in multiple caches.
+ ***********************************************************************/
+
+#include "objc-private.h"
+#include "hashtable2.h"
+
+typedef struct {
+ SEL name; // same layout as struct old_method
+ void *unused;
+ IMP imp; // same layout as struct old_method
+} cache_entry;
+
+#if __OBJC2__
+
+#ifndef __LP64__
+# define CACHE_HASH(sel, mask) (((uintptr_t)(sel)>>2) & (mask))
+#else
+# define CACHE_HASH(sel, mask) (((unsigned int)((uintptr_t)(sel)>>0)) & (mask))
+#endif
+
+struct objc_cache {
+ uintptr_t mask; /* total = mask + 1 */
+ uintptr_t occupied;
+ cache_entry *buckets[1];
+};
+
+#define CACHE_BUCKET(e) ((cache_entry *)e)
+
+#else
+
+/* Most definitions are in runtime.h */
+#define CACHE_BUCKET(e) ((Method)e)
+
+#endif
+
+
+/* When _class_slow_grow is non-zero, any given cache is actually grown
+ * only on the odd-numbered times it becomes full; on the even-numbered
+ * times, it is simply emptied and re-used. When this flag is zero,
+ * caches are grown every time. */
+static const int _class_slow_grow = 1;
+
+/* For min cache size: clear_cache=1, slow_grow=1
+ For max cache size: clear_cache=0, slow_grow=0 */
+
+/* Initial cache bucket count. INIT_CACHE_SIZE must be a power of two. */
+enum {
+ INIT_CACHE_SIZE_LOG2 = 2,
+ INIT_CACHE_SIZE = (1 << INIT_CACHE_SIZE_LOG2)
+};
+
+
+/* Amount of space required for `count` hash table buckets, knowing that
+ * one entry is embedded in the cache structure itself. */
+#define TABLE_SIZE(count) ((count - 1) * sizeof(cache_entry *))
+
+
+#if !TARGET_OS_WIN32
+# define CACHE_ALLOCATOR
+#endif
+
+/* Custom cache allocator parameters.
+ * CACHE_REGION_SIZE must be a multiple of CACHE_QUANTUM. */
+#define CACHE_ALLOCATOR_MIN 512
+#define CACHE_QUANTUM (CACHE_ALLOCATOR_MIN+sizeof(struct objc_cache)-sizeof(cache_entry*))
+#define CACHE_REGION_SIZE ((128*1024 / CACHE_QUANTUM) * CACHE_QUANTUM)
+// #define CACHE_REGION_SIZE ((256*1024 / CACHE_QUANTUM) * CACHE_QUANTUM)
+
+static uintptr_t cache_allocator_mask_for_size(size_t size)
+{
+ return (size - sizeof(struct objc_cache)) / sizeof(cache_entry *);
+}
+
+static size_t cache_allocator_size_for_mask(uintptr_t mask)
+{
+ size_t requested = sizeof(struct objc_cache) + TABLE_SIZE(mask+1);
+ size_t actual = CACHE_QUANTUM;
+ while (actual < requested) actual += CACHE_QUANTUM;
+ return actual;
+}
+
+
+/* Cache instrumentation data. Immediately follows the cache block itself. */
+#ifdef OBJC_INSTRUMENTED
+typedef struct
+{
+ unsigned int hitCount; // cache lookup success tally
+ unsigned int hitProbes; // sum entries checked to hit
+ unsigned int maxHitProbes; // max entries checked to hit
+ unsigned int missCount; // cache lookup no-find tally
+ unsigned int missProbes; // sum entries checked to miss
+ unsigned int maxMissProbes; // max entries checked to miss
+ unsigned int flushCount; // cache flush tally
+ unsigned int flushedEntries; // sum cache entries flushed
+ unsigned int maxFlushedEntries; // max cache entries flushed
+} CacheInstrumentation;
+
+#define CACHE_INSTRUMENTATION(cache) (CacheInstrumentation *) &cache->buckets[cache->mask + 1];
+#endif
+
+/* Cache filling and flushing instrumentation */
+
+static int totalCacheFills = 0;
+
+#ifdef OBJC_INSTRUMENTED
+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
+
+
+/***********************************************************************
+* A static empty cache. All classes initially point at this cache.
+* When the first message is sent it misses in the cache, and when
+* the cache is grown it checks for this case and uses malloc rather
+* than realloc. This avoids the need to check for NULL caches in the
+* messenger.
+***********************************************************************/
+
+struct objc_cache _objc_empty_cache =
+{
+ 0, // mask
+ 0, // occupied
+ { NULL } // buckets
+};
+#ifdef OBJC_INSTRUMENTED
+CacheInstrumentation emptyCacheInstrumentation = {0};
+#endif
+
+
+/* Local prototypes */
+
+static BOOL _cache_isEmpty(Cache cache);
+static Cache _cache_malloc(uintptr_t slotCount);
+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);
+static void _garbage_make_room(void);
+static void _cache_collect_free(void *data, size_t size);
+
+#if defined(CACHE_ALLOCATOR)
+static BOOL cache_allocator_is_block(void *block);
+static Cache cache_allocator_calloc(size_t size);
+static void cache_allocator_free(void *block);
+#endif
+
+/***********************************************************************
+* Cache statistics for OBJC_PRINT_CACHE_SETUP
+**********************************************************************/
+static unsigned int cache_counts[16];
+static size_t cache_allocations;
+static size_t cache_collections;
+static size_t cache_allocator_regions;
+
+static size_t log2u(size_t x)
+{
+ unsigned int log;
+
+ log = 0;
+ while (x >>= 1)
+ log += 1;
+
+ return log;
+}
+
+
+/***********************************************************************
+* _cache_isEmpty.
+* Returns YES if the given cache is some empty cache.
+* Empty caches should never be allocated on the heap.
+**********************************************************************/
+static BOOL _cache_isEmpty(Cache cache)
+{
+ return (cache == NULL || cache == (Cache)&_objc_empty_cache || cache->mask == 0);
+}
+
+
+/***********************************************************************
+* _cache_malloc.
+*
+* Called from _cache_create() and cache_expand()
+* Cache locks: cacheUpdateLock must be held by the caller.
+**********************************************************************/
+static Cache _cache_malloc(uintptr_t slotCount)
+{
+ Cache new_cache;
+ size_t size;
+
+ mutex_assert_locked(&cacheUpdateLock);
+
+ // Allocate table (why not check for failure?)
+ size = sizeof(struct objc_cache) + TABLE_SIZE(slotCount);
+#if defined(OBJC_INSTRUMENTED)
+ // Custom cache allocator can't handle instrumentation.
+ size += sizeof(CacheInstrumentation);
+ new_cache = _calloc_internal(size, 1);
+ new_cache->mask = slotCount - 1;
+#elif !defined(CACHE_ALLOCATOR)
+ // fixme cache allocator implementation isn't 64-bit clean
+ new_cache = _calloc_internal(size, 1);
+ new_cache->mask = (unsigned int)(slotCount - 1);
+#else
+ if (size < CACHE_ALLOCATOR_MIN || UseInternalZone) {
+ new_cache = (Cache)_calloc_internal(size, 1);
+ new_cache->mask = slotCount - 1;
+ // occupied and buckets and instrumentation are all zero
+ } else {
+ new_cache = cache_allocator_calloc(size);
+ // mask is already set
+ // occupied and buckets and instrumentation are all zero
+ }
+#endif
+
+ if (PrintCaches) {
+ size_t bucket = log2u(slotCount);
+ if (bucket < sizeof(cache_counts) / sizeof(cache_counts[0])) {
+ cache_counts[bucket]++;
+ }
+ cache_allocations++;
+ }
+
+ return new_cache;
+}
+
+/***********************************************************************
+* _cache_free_block.
+*
+* Called from _cache_free() and _cache_collect_free().
+* block may be a cache or a forward:: entry.
+* If block is a cache, forward:: entries it points to will NOT be freed.
+* Cache locks: cacheUpdateLock must be held by the caller.
+**********************************************************************/
+static void _cache_free_block(void *block)
+{
+ mutex_assert_locked(&cacheUpdateLock);
+
+#if !TARGET_OS_WIN32
+ if (PrintCaches) {
+ Cache cache = (Cache)block;
+ size_t slotCount = cache->mask + 1;
+ if (isPowerOf2(slotCount)) {
+ size_t bucket = log2u(slotCount);
+ if (bucket < sizeof(cache_counts) / sizeof(cache_counts[0])) {
+ cache_counts[bucket]--;
+ }
+ }
+ }
+#endif
+
+#if defined(CACHE_ALLOCATOR)
+ if (cache_allocator_is_block(block)) {
+ cache_allocator_free(block);
+ } else
+#endif
+ {
+ free(block);
+ }
+}
+
+
+/***********************************************************************
+* _cache_free.
+*
+* Called from _objc_remove_classes_in_image().
+* forward:: entries in the cache ARE freed.
+* Cache locks: cacheUpdateLock must NOT be held by the caller.
+**********************************************************************/
+void _cache_free(Cache cache)
+{
+ unsigned int i;
+
+ mutex_lock(&cacheUpdateLock);
+
+ for (i = 0; i < cache->mask + 1; i++) {
+ cache_entry *entry = (cache_entry *)cache->buckets[i];
+ if (entry && entry->imp == _objc_msgForward_internal) {
+ _cache_free_block(entry);
+ }
+ }
+
+ _cache_free_block(cache);
+
+ mutex_unlock(&cacheUpdateLock);
+}
+
+
+/***********************************************************************
+* _cache_create.
+*
+* Called from _cache_expand().
+* Cache locks: cacheUpdateLock must be held by the caller.
+**********************************************************************/
+static Cache _cache_create(Class cls)
+{
+ Cache new_cache;
+
+ mutex_assert_locked(&cacheUpdateLock);
+
+ // Allocate new cache block
+ new_cache = _cache_malloc(INIT_CACHE_SIZE);
+
+ // Install the cache
+ _class_setCache(cls, new_cache);
+
+ // Clear the grow flag so that we will re-use the current storage,
+ // rather than actually grow the cache, when expanding the cache
+ // for the first time
+ if (_class_slow_grow) {
+ _class_setGrowCache(cls, NO);
+ }
+
+ // Return our creation
+ return new_cache;
+}
+
+
+/***********************************************************************
+* _cache_expand.
+*
+* Called from _cache_fill ()
+* Cache locks: cacheUpdateLock must be held by the caller.
+**********************************************************************/
+static Cache _cache_expand(Class cls)
+{
+ Cache old_cache;
+ Cache new_cache;
+ uintptr_t slotCount;
+ uintptr_t index;
+
+ mutex_assert_locked(&cacheUpdateLock);
+
+ // First growth goes from empty cache to a real one
+ old_cache = _class_getCache(cls);
+ if (_cache_isEmpty(old_cache))
+ return _cache_create (cls);
+
+ if (_class_slow_grow) {
+ // Cache grows every other time only.
+ if (_class_shouldGrowCache(cls)) {
+ // Grow the cache this time. Don't grow next time.
+ _class_setGrowCache(cls, NO);
+ }
+ else {
+ // Reuse the current cache storage this time. Do grow next time.
+ _class_setGrowCache(cls, YES);
+
+ // Clear the valid-entry counter
+ old_cache->occupied = 0;
+
+ // Invalidate all the cache entries
+ for (index = 0; index < old_cache->mask + 1; index += 1)
+ {
+ // Remember what this entry was, so we can possibly
+ // deallocate it after the bucket has been invalidated
+ cache_entry *oldEntry = (cache_entry *)old_cache->buckets[index];
+
+ // Skip invalid entry
+ if (!oldEntry)
+ continue;
+
+ // Invalidate this entry
+ old_cache->buckets[index] = NULL;
+
+ // Deallocate "forward::" entry
+ if (oldEntry->imp == _objc_msgForward_internal) {
+ _cache_collect_free (oldEntry, sizeof(cache_entry));
+ }
+ }
+
+ // Return the same old cache, freshly emptied
+ return old_cache;
+ }
+ }
+
+ // Double the cache size
+ slotCount = (old_cache->mask + 1) << 1;
+
+ new_cache = _cache_malloc(slotCount);
+
+#ifdef OBJC_INSTRUMENTED
+ // Propagate the instrumentation data
+ {
+ CacheInstrumentation *oldCacheData;
+ CacheInstrumentation *newCacheData;
+
+ oldCacheData = CACHE_INSTRUMENTATION(old_cache);
+ newCacheData = CACHE_INSTRUMENTATION(new_cache);
+ bcopy ((const char *)oldCacheData, (char *)newCacheData, sizeof(CacheInstrumentation));
+ }
+#endif
+
+ // 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) {
+ _cache_collect_free (entry, sizeof(cache_entry));
+ }
+ }
+
+ // Install new cache
+ _class_setCache(cls, new_cache);
+
+ // Deallocate old cache, try freeing all the garbage
+ _cache_collect_free (old_cache, old_cache->mask * sizeof(cache_entry *));
+ _cache_collect(false);
+
+ return new_cache;
+}
+
+
+/***********************************************************************
+* _cache_fill. Add the specified method to the specified class' cache.
+* Returns NO if the cache entry wasn't added: cache was busy,
+* class is still being initialized, new entry is a duplicate.
+*
+* Called only from _class_lookupMethodAndLoadCache and
+* class_respondsToMethod and _cache_addForwardEntry.
+*
+* Cache locks: cacheUpdateLock must not be held.
+**********************************************************************/
+BOOL _cache_fill(Class cls, Method smt, SEL sel)
+{
+ uintptr_t newOccupied;
+ uintptr_t index;
+ cache_entry **buckets;
+ cache_entry *entry;
+ Cache cache;
+
+ mutex_assert_unlocked(&cacheUpdateLock);
+
+ // Never cache before +initialize is done
+ if (!_class_isInitialized(cls)) {
+ return NO;
+ }
+
+ // Keep tally of cache additions
+ totalCacheFills += 1;
+
+ mutex_lock(&cacheUpdateLock);
+
+ entry = (cache_entry *)smt;
+
+ cache = _class_getCache(cls);
+
+ // Make sure the entry wasn't added to the cache by some other thread
+ // before we grabbed the cacheUpdateLock.
+ // Don't use _cache_getMethod() because _cache_getMethod() doesn't
+ // return forward:: entries.
+ if (_cache_getImp(cls, sel)) {
+ mutex_unlock(&cacheUpdateLock);
+ return NO; // entry is already cached, didn't add new one
+ }
+
+ // Use the cache as-is if it is less than 3/4 full
+ newOccupied = cache->occupied + 1;
+ if ((newOccupied * 4) <= (cache->mask + 1) * 3) {
+ // Cache is less than 3/4 full.
+ cache->occupied = (unsigned int)newOccupied;
+ } else {
+ // Cache is too full. Expand it.
+ cache = _cache_expand (cls);
+
+ // Account for the addition
+ cache->occupied += 1;
+ }
+
+ // Scan for the first unused slot and insert there.
+ // There is guaranteed to be an empty slot because the
+ // minimum size is 4 and we resized at 3/4 full.
+ buckets = (cache_entry **)cache->buckets;
+ for (index = CACHE_HASH(sel, cache->mask);
+ buckets[index] != NULL;
+ index = (index+1) & cache->mask)
+ {
+ // empty
+ }
+ buckets[index] = entry;
+
+ mutex_unlock(&cacheUpdateLock);
+
+ return YES; // successfully added new cache entry
+}
+
+
+/***********************************************************************
+* _cache_addForwardEntry
+* Add a forward:: entry for the given selector to cls's method cache.
+* Does nothing if the cache addition fails for any reason.
+* Called from class_respondsToMethod and _class_lookupMethodAndLoadCache.
+* Cache locks: cacheUpdateLock must not be held.
+**********************************************************************/
+void _cache_addForwardEntry(Class cls, SEL sel)
+{
+ cache_entry *smt;
+
+ smt = (cache_entry *)_malloc_internal(sizeof(cache_entry));
+ smt->name = sel;
+ 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);
+ }
+}
+
+
+/***********************************************************************
+* _cache_addIgnoredEntry
+* Add an entry for the ignored selector to cls's method cache.
+* Does nothing if the cache addition fails for any reason.
+* Returns the ignored IMP.
+* Cache locks: cacheUpdateLock must not be held.
+**********************************************************************/
+#if SUPPORT_GC && !SUPPORT_IGNORED_SELECTOR_CONSTANT
+static cache_entry *alloc_ignored_entries(void)
+{
+ cache_entry *e = (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};
+ e[3] = (cache_entry){ @selector(retainCount),0,(IMP)&_objc_ignored_method};
+ e[4] = (cache_entry){ @selector(dealloc), 0,(IMP)&_objc_ignored_method};
+ return e;
+}
+#endif
+
+IMP _cache_addIgnoredEntry(Class cls, SEL sel)
+{
+ cache_entry *entryp = NULL;
+
+#if !SUPPORT_GC
+ _objc_fatal("selector ignored with GC off");
+#elif SUPPORT_IGNORED_SELECTOR_CONSTANT
+ static cache_entry entry = { (SEL)kIgnore, 0, (IMP)&_objc_ignored_method };
+ entryp = &entry;
+ assert(sel == (SEL)kIgnore);
+#else
+ // hack
+ int i;
+ static cache_entry *entries;
+ INIT_ONCE_PTR(entries, alloc_ignored_entries(), free(v));
+
+ assert(ignoreSelector(sel));
+ for (i = 0; i < 5; i++) {
+ if (sel == entries[i].name) {
+ entryp = &entries[i];
+ break;
+ }
+ }
+ if (!entryp) _objc_fatal("selector %s (%p) is not ignored",
+ sel_getName(sel), sel);
+#endif
+
+ _cache_fill(cls, (Method)entryp, sel);
+ return entryp->imp;
+}
+
+
+/***********************************************************************
+* _cache_flush. Invalidate all valid entries in the given class' cache.
+*
+* Called from flush_caches() and _cache_fill()
+* Cache locks: cacheUpdateLock must be held by the caller.
+**********************************************************************/
+#if __OBJC2__
+static
+#endif
+void _cache_flush(Class cls)
+{
+ Cache cache;
+ unsigned int index;
+
+ mutex_assert_locked(&cacheUpdateLock);
+
+ // Locate cache. Ignore unused cache.
+ cache = _class_getCache(cls);
+ if (_cache_isEmpty(cache)) return;
+
+#ifdef OBJC_INSTRUMENTED
+ {
+ CacheInstrumentation *cacheData;
+
+ // Tally this flush
+ cacheData = CACHE_INSTRUMENTATION(cache);
+ cacheData->flushCount += 1;
+ cacheData->flushedEntries += cache->occupied;
+ if (cache->occupied > cacheData->maxFlushedEntries)
+ cacheData->maxFlushedEntries = cache->occupied;
+ }
+#endif
+
+ // Traverse the cache
+ for (index = 0; index <= cache->mask; index += 1)
+ {
+ // Remember what this entry was, so we can possibly
+ // deallocate it after the bucket has been invalidated
+ cache_entry *oldEntry = (cache_entry *)cache->buckets[index];
+
+ // Invalidate this entry
+ cache->buckets[index] = NULL;
+
+ // Deallocate "forward::" entry
+ if (oldEntry && oldEntry->imp == _objc_msgForward_internal)
+ _cache_collect_free (oldEntry, sizeof(cache_entry));
+ }
+
+ // Clear the valid-entry counter
+ cache->occupied = 0;
+}
+
+
+/***********************************************************************
+* flush_cache. Flushes the instance method cache for class cls only.
+* Use flush_caches() if cls might have in-use subclasses.
+**********************************************************************/
+void flush_cache(Class cls)
+{
+ if (cls) {
+ mutex_lock(&cacheUpdateLock);
+ _cache_flush(cls);
+ mutex_unlock(&cacheUpdateLock);
+ }
+}
+
+
+/***********************************************************************
+* cache collection.
+**********************************************************************/
+
+#if !TARGET_OS_WIN32
+
+// A sentinel (magic value) to report bad thread_get_state status.
+// Must not be a valid PC.
+// Must not be zero - thread_get_state() on a new thread returns PC == 0.
+#define PC_SENTINEL 1
+
+// UNIX03 compliance hack (4508809)
+#if !__DARWIN_UNIX03
+#define __srr0 srr0
+#define __eip eip
+#endif
+
+static uintptr_t _get_pc_for_thread(thread_t thread)
+#if defined(__i386__)
+{
+ i386_thread_state_t state;
+ unsigned int count = i386_THREAD_STATE_COUNT;
+ kern_return_t okay = thread_get_state (thread, i386_THREAD_STATE, (thread_state_t)&state, &count);
+ return (okay == KERN_SUCCESS) ? state.__eip : PC_SENTINEL;
+}
+#elif defined(__x86_64__)
+{
+ x86_thread_state64_t state;
+ unsigned int count = x86_THREAD_STATE64_COUNT;
+ kern_return_t okay = thread_get_state (thread, x86_THREAD_STATE64, (thread_state_t)&state, &count);
+ return (okay == KERN_SUCCESS) ? state.__rip : PC_SENTINEL;
+}
+#elif defined(__arm__)
+{
+ arm_thread_state_t state;
+ unsigned int count = ARM_THREAD_STATE_COUNT;
+ kern_return_t okay = thread_get_state (thread, ARM_THREAD_STATE, (thread_state_t)&state, &count);
+ return (okay == KERN_SUCCESS) ? state.__pc : PC_SENTINEL;
+}
+#else
+{
+#error _get_pc_for_thread () not implemented for this architecture
+}
+#endif
+
+#endif
+
+/***********************************************************************
+* _collecting_in_critical.
+* Returns TRUE if some thread is currently executing a cache-reading
+* function. Collection of cache garbage is not allowed when a cache-
+* reading function is in progress because it might still be using
+* the garbage memory.
+**********************************************************************/
+OBJC_EXPORT uintptr_t objc_entryPoints[];
+OBJC_EXPORT uintptr_t objc_exitPoints[];
+
+static int _collecting_in_critical(void)
+{
+#if TARGET_OS_WIN32
+ return TRUE;
+#else
+ thread_act_port_array_t threads;
+ unsigned number;
+ unsigned count;
+ kern_return_t ret;
+ int result;
+
+ mach_port_t mythread = pthread_mach_thread_np(pthread_self());
+
+ // Get a list of all the threads in the current task
+ ret = task_threads (mach_task_self (), &threads, &number);
+ if (ret != KERN_SUCCESS)
+ {
+ _objc_fatal("task_thread failed (result %d)\n", ret);
+ }
+
+ // Check whether any thread is in the cache lookup code
+ result = FALSE;
+ for (count = 0; count < number; count++)
+ {
+ int region;
+ uintptr_t pc;
+
+ // Don't bother checking ourselves
+ if (threads[count] == mythread)
+ continue;
+
+ // Find out where thread is executing
+ pc = _get_pc_for_thread (threads[count]);
+
+ // Check for bad status, and if so, assume the worse (can't collect)
+ if (pc == PC_SENTINEL)
+ {
+ result = TRUE;
+ goto done;
+ }
+
+ // Check whether it is in the cache lookup code
+ for (region = 0; objc_entryPoints[region] != 0; region++)
+ {
+ if ((pc >= objc_entryPoints[region]) &&
+ (pc <= objc_exitPoints[region]))
+ {
+ result = TRUE;
+ goto done;
+ }
+ }
+ }
+
+ done:
+ // Deallocate the port rights for the threads
+ for (count = 0; count < number; count++) {
+ mach_port_deallocate(mach_task_self (), threads[count]);
+ }
+
+ // Deallocate the thread list
+ vm_deallocate (mach_task_self (), (vm_address_t) threads, sizeof(threads[0]) * number);
+
+ // Return our finding
+ return result;
+#endif
+}
+
+
+/***********************************************************************
+* _garbage_make_room. Ensure that there is enough room for at least
+* one more ref in the garbage.
+**********************************************************************/
+
+// amount of memory represented by all refs in the garbage
+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 = 32*1024;
+
+// table of refs to free
+static void **garbage_refs = 0;
+
+// current number of refs in garbage_refs
+static size_t garbage_count = 0;
+
+// capacity of current garbage_refs
+static size_t garbage_max = 0;
+
+// capacity of initial garbage_refs
+enum {
+ INIT_GARBAGE_COUNT = 128
+};
+
+static void _garbage_make_room(void)
+{
+ static int first = 1;
+
+ // Create the collection table the first time it is needed
+ if (first)
+ {
+ first = 0;
+ 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)
+ {
+ garbage_refs = (void**)
+ _realloc_internal(garbage_refs, garbage_max * 2 * sizeof(void *));
+ garbage_max *= 2;
+ }
+}
+
+
+/***********************************************************************
+* _cache_collect_free. Add the specified malloc'd memory to the list
+* of them to free at some later point.
+* size is used for the collection threshold. It does not have to be
+* precisely the block's size.
+* Cache locks: cacheUpdateLock must be held by the caller.
+**********************************************************************/
+static void _cache_collect_free(void *data, size_t size)
+{
+ mutex_assert_locked(&cacheUpdateLock);
+
+ _garbage_make_room ();
+ garbage_byte_size += size;
+ garbage_refs[garbage_count++] = data;
+}
+
+
+/***********************************************************************
+* _cache_collect. Try to free accumulated dead caches.
+* collectALot tries harder to free memory.
+* Cache locks: cacheUpdateLock must be held by the caller.
+**********************************************************************/
+void _cache_collect(bool collectALot)
+{
+ mutex_assert_locked(&cacheUpdateLock);
+
+ // Done if the garbage is not full
+ if (garbage_byte_size < garbage_threshold && !collectALot) {
+ return;
+ }
+
+ // Synchronize collection with objc_msgSend and other cache readers
+ if (!collectALot) {
+ if (_collecting_in_critical ()) {
+ // objc_msgSend (or other cache reader) is currently looking in
+ // the cache and might still be using some garbage.
+ if (PrintCaches) {
+ _objc_inform ("CACHES: not collecting; "
+ "objc_msgSend in progress");
+ }
+ return;
+ }
+ }
+ else {
+ // No excuses.
+ while (_collecting_in_critical())
+ ;
+ }
+
+ // No cache readers in progress - garbage is now deletable
+
+ // Log our progress
+ if (PrintCaches) {
+ cache_collections++;
+ _objc_inform ("CACHES: COLLECTING %zu bytes (%zu regions, %zu allocations, %zu collections)", garbage_byte_size, cache_allocator_regions, cache_allocations, cache_collections);
+ }
+
+ // Dispose all refs now in the garbage
+ while (garbage_count--) {
+ _cache_free_block(garbage_refs[garbage_count]);
+ }
+
+ // Clear the garbage count and total size indicator
+ garbage_count = 0;
+ garbage_byte_size = 0;
+
+ if (PrintCaches) {
+ size_t i;
+ size_t total = 0;
+ size_t ideal_total = 0;
+ size_t malloc_total = 0;
+ size_t local_total = 0;
+
+ for (i = 0; i < sizeof(cache_counts) / sizeof(cache_counts[0]); i++) {
+ int count = cache_counts[i];
+ int slots = 1 << i;
+ size_t size = sizeof(struct objc_cache) + TABLE_SIZE(slots);
+ size_t ideal = size;
+#if TARGET_OS_WIN32
+ size_t malloc = size;
+#else
+ size_t malloc = malloc_good_size(size);
+#endif
+ size_t local = size < CACHE_ALLOCATOR_MIN ? malloc : cache_allocator_size_for_mask(cache_allocator_mask_for_size(size));
+
+ if (!count) continue;
+
+ _objc_inform("CACHES: %4d slots: %4d caches, %6zu / %6zu / %6zu bytes ideal/malloc/local, %6zu / %6zu bytes wasted malloc/local", slots, count, ideal*count, malloc*count, local*count, malloc*count-ideal*count, local*count-ideal*count);
+
+ total += count;
+ ideal_total += ideal*count;
+ malloc_total += malloc*count;
+ local_total += local*count;
+ }
+
+ _objc_inform("CACHES: total: %4zu caches, %6zu / %6zu / %6zu bytes ideal/malloc/local, %6zu / %6zu bytes wasted malloc/local", total, ideal_total, malloc_total, local_total, malloc_total-ideal_total, local_total-ideal_total);
+ }
+}
+
+
+
+
+
+#if defined(CACHE_ALLOCATOR)
+
+/***********************************************************************
+* Custom method cache allocator.
+* Method cache block sizes are 2^slots+2 words, which is a pessimal
+* case for the system allocator. It wastes 504 bytes per cache block
+* with 128 or more slots, which adds up to tens of KB for an AppKit process.
+* To save memory, the custom cache allocator below is used.
+*
+* The cache allocator uses 128 KB allocation regions. Few processes will
+* require a second region. Within a region, allocation is address-ordered
+* first fit.
+*
+* The cache allocator uses a quantum of 520.
+* Cache block ideal sizes: 520, 1032, 2056, 4104
+* Cache allocator sizes: 520, 1040, 2080, 4160
+*
+* Because all blocks are known to be genuine method caches, the ordinary
+* cache->mask and cache->occupied fields are used as block headers.
+* No out-of-band headers are maintained. The number of blocks will
+* almost always be fewer than 200, so for simplicity there is no free
+* list or other optimization.
+*
+* Block in use: mask != 0, occupied != -1 (mask indicates block size)
+* Block free: mask != 0, occupied == -1 (mask is precisely block size)
+*
+* No cache allocator functions take any locks. Instead, the caller
+* must hold the cacheUpdateLock.
+*
+* fixme with 128 KB regions and 520 B min block size, an allocation
+* bitmap would be only 32 bytes - better than free list?
+**********************************************************************/
+
+typedef struct cache_allocator_block {
+ uintptr_t size;
+ uintptr_t state;
+ struct cache_allocator_block *nextFree;
+} cache_allocator_block;
+
+typedef struct cache_allocator_region {
+ cache_allocator_block *start;
+ cache_allocator_block *end; // first non-block address
+ cache_allocator_block *freeList;
+ struct cache_allocator_region *next;
+} cache_allocator_region;
+
+static cache_allocator_region *cacheRegion = NULL;
+
+
+/***********************************************************************
+* cache_allocator_add_region
+* Allocates and returns a new region that can hold at least size
+* bytes of large method caches.
+* The actual size will be rounded up to a CACHE_QUANTUM boundary,
+* with a minimum of CACHE_REGION_SIZE.
+* The new region is lowest-priority for new allocations. Callers that
+* know the other regions are already full should allocate directly
+* into the returned region.
+**********************************************************************/
+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 *)
+ _calloc_internal(1, sizeof(cache_allocator_region));
+
+ // Round size up to quantum boundary, and apply the minimum size.
+ size += CACHE_QUANTUM - (size % CACHE_QUANTUM);
+ if (size < CACHE_REGION_SIZE) size = CACHE_REGION_SIZE;
+
+ // Allocate the region
+ addr = (vm_address_t)calloc(size, 1);
+ newRegion->start = (cache_allocator_block *)addr;
+ newRegion->end = (cache_allocator_block *)(addr + size);
+
+ // Mark the first block: free and covers the entire region
+ b = newRegion->start;
+ b->size = size;
+ b->state = (uintptr_t)-1;
+ b->nextFree = NULL;
+ newRegion->freeList = b;
+
+ // Add to end of the linked list of regions.
+ // Other regions should be re-used before this one is touched.
+ newRegion->next = NULL;
+ rgnP = &cacheRegion;
+ while (*rgnP) {
+ rgnP = &(**rgnP).next;
+ }
+ *rgnP = newRegion;
+
+ cache_allocator_regions++;
+
+ return newRegion;
+}
+
+
+/***********************************************************************
+* cache_allocator_coalesce
+* Attempts to coalesce a free block with the single free block following
+* it in the free list, if any.
+**********************************************************************/
+static void cache_allocator_coalesce(cache_allocator_block *block)
+{
+ if (block->size + (uintptr_t)block == (uintptr_t)block->nextFree) {
+ block->size += block->nextFree->size;
+ block->nextFree = block->nextFree->nextFree;
+ }
+}
+
+
+/***********************************************************************
+* cache_region_calloc
+* Attempt to allocate a size-byte block in the given region.
+* Allocation is first-fit. The free list is already fully coalesced.
+* Returns NULL if there is not enough room in the region for the block.
+**********************************************************************/
+static void *cache_region_calloc(cache_allocator_region *rgn, size_t size)
+{
+ cache_allocator_block **blockP;
+ uintptr_t mask;
+
+ // Save mask for allocated block, then round size
+ // up to CACHE_QUANTUM boundary
+ mask = cache_allocator_mask_for_size(size);
+ size = cache_allocator_size_for_mask(mask);
+
+ // Search the free list for a sufficiently large free block.
+
+ for (blockP = &rgn->freeList;
+ *blockP != NULL;
+ blockP = &(**blockP).nextFree)
+ {
+ cache_allocator_block *block = *blockP;
+ if (block->size < size) continue; // not big enough
+
+ // block is now big enough. Allocate from it.
+
+ // Slice off unneeded fragment of block, if any,
+ // and reconnect the free list around block.
+ if (block->size - size >= CACHE_QUANTUM) {
+ cache_allocator_block *leftover =
+ (cache_allocator_block *)(size + (uintptr_t)block);
+ leftover->size = block->size - size;
+ leftover->state = (uintptr_t)-1;
+ leftover->nextFree = block->nextFree;
+ *blockP = leftover;
+ } else {
+ *blockP = block->nextFree;
+ }
+
+ // block is now exactly the right size.
+
+ bzero(block, size);
+ block->size = mask; // Cache->mask
+ block->state = 0; // Cache->occupied
+
+ return block;
+ }
+
+ // No room in this region.
+ return NULL;
+}
+
+
+/***********************************************************************
+* cache_allocator_calloc
+* Custom allocator for large method caches (128+ slots)
+* The returned cache block already has cache->mask set.
+* cache->occupied and the cache contents are zero.
+* Cache locks: cacheUpdateLock must be held by the caller
+**********************************************************************/
+static Cache cache_allocator_calloc(size_t size)
+{
+ cache_allocator_region *rgn;
+
+ mutex_assert_locked(&cacheUpdateLock);
+
+ for (rgn = cacheRegion; rgn != NULL; rgn = rgn->next) {
+ void *p = cache_region_calloc(rgn, size);
+ if (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)cache_region_calloc(cache_allocator_add_region(size), size);
+}
+
+
+/***********************************************************************
+* cache_allocator_region_for_block
+* Returns the cache allocator region that ptr points into, or NULL.
+**********************************************************************/
+static cache_allocator_region *cache_allocator_region_for_block(cache_allocator_block *block)
+{
+ cache_allocator_region *rgn;
+ for (rgn = cacheRegion; rgn != NULL; rgn = rgn->next) {
+ if (block >= rgn->start && block < rgn->end) return rgn;
+ }
+ return NULL;
+}
+
+
+/***********************************************************************
+* cache_allocator_is_block
+* If ptr is a live block from the cache allocator, return YES
+* If ptr is a block from some other allocator, return NO.
+* If ptr is a dead block from the cache allocator, result is undefined.
+* Cache locks: cacheUpdateLock must be held by the caller
+**********************************************************************/
+static BOOL cache_allocator_is_block(void *ptr)
+{
+ mutex_assert_locked(&cacheUpdateLock);
+ return (cache_allocator_region_for_block((cache_allocator_block *)ptr) != NULL);
+}
+
+/***********************************************************************
+* cache_allocator_free
+* Frees a block allocated by the cache allocator.
+* Cache locks: cacheUpdateLock must be held by the caller.
+**********************************************************************/
+static void cache_allocator_free(void *ptr)
+{
+ cache_allocator_block *dead = (cache_allocator_block *)ptr;
+ cache_allocator_block *cur;
+ cache_allocator_region *rgn;
+
+ mutex_assert_locked(&cacheUpdateLock);
+
+ if (! (rgn = cache_allocator_region_for_block(dead))) {
+ // free of non-pointer
+ _objc_inform("cache_allocator_free of non-pointer %p", dead);
+ return;
+ }
+
+ dead->size = cache_allocator_size_for_mask(dead->size);
+ dead->state = (uintptr_t)-1;
+
+ if (!rgn->freeList || rgn->freeList > dead) {
+ // dead block belongs at front of free list
+ dead->nextFree = rgn->freeList;
+ rgn->freeList = dead;
+ cache_allocator_coalesce(dead);
+ return;
+ }
+
+ // dead block belongs in the middle or end of free list
+ for (cur = rgn->freeList; cur != NULL; cur = cur->nextFree) {
+ cache_allocator_block *ahead = cur->nextFree;
+
+ if (!ahead || ahead > dead) {
+ // cur and ahead straddle dead, OR dead belongs at end of free list
+ cur->nextFree = dead;
+ dead->nextFree = ahead;
+
+ // coalesce into dead first in case both succeed
+ cache_allocator_coalesce(dead);
+ cache_allocator_coalesce(cur);
+ return;
+ }
+ }
+
+ // uh-oh
+ _objc_inform("cache_allocator_free of non-pointer %p", ptr);
+}
+
+// defined(CACHE_ALLOCATOR)
+#endif
+
+/***********************************************************************
+* Cache instrumentation and debugging
+**********************************************************************/
+
+#ifdef OBJC_INSTRUMENTED
+enum {
+ CACHE_HISTOGRAM_SIZE = 512
+};
+
+unsigned int CacheHitHistogram [CACHE_HISTOGRAM_SIZE];
+unsigned int CacheMissHistogram [CACHE_HISTOGRAM_SIZE];
+#endif
+
+
+/***********************************************************************
+* _cache_print.
+**********************************************************************/
+static void _cache_print(Cache cache)
+{
+ uintptr_t index;
+ uintptr_t count;
+
+ count = cache->mask + 1;
+ for (index = 0; index < count; index += 1) {
+ cache_entry *entry = (cache_entry *)cache->buckets[index];
+ if (entry) {
+ if (entry->imp == _objc_msgForward_internal)
+ printf ("does not recognize: \n");
+ printf ("%s\n", sel_getName(entry->name));
+ }
+ }
+}
+
+
+/***********************************************************************
+* _class_printMethodCaches.
+**********************************************************************/
+void _class_printMethodCaches(Class cls)
+{
+ if (_cache_isEmpty(_class_getCache(cls))) {
+ printf("no instance-method cache for class %s\n", _class_getName(cls));
+ } else {
+ printf("instance-method cache for class %s:\n", _class_getName(cls));
+ _cache_print(_class_getCache(cls));
+ }
+
+ if (_cache_isEmpty(_class_getCache(((id)cls)->isa))) {
+ printf("no class-method cache for class %s\n", _class_getName(cls));
+ } else {
+ printf ("class-method cache for class %s:\n", _class_getName(cls));
+ _cache_print(_class_getCache(((id)cls)->isa));
+ }
+}
+
+
+#if 0
+#warning fixme
+
+
+/***********************************************************************
+* _class_printDuplicateCacheEntries.
+**********************************************************************/
+void _class_printDuplicateCacheEntries(BOOL detail)
+{
+ NXHashState state;
+ Class cls;
+ unsigned int duplicates;
+ unsigned int index1;
+ unsigned int index2;
+ unsigned int mask;
+ unsigned int count;
+ unsigned int isMeta;
+ Cache cache;
+
+
+ printf ("Checking for duplicate cache entries \n");
+
+ // Outermost loop - iterate over all classes
+ state = NXInitHashState (class_hash);
+ duplicates = 0;
+ while (NXNextHashState (class_hash, &state, (void **) &cls))
+ {
+ // Control loop - do given class' cache, then its isa's cache
+ for (isMeta = 0; isMeta <= 1; isMeta += 1)
+ {
+ // Select cache of interest and make sure it exists
+ cache = _class_getCache(isMeta ? ((id)cls)->isa : cls);
+ if (_cache_isEmpty(cache))
+ continue;
+
+ // Middle loop - check each entry in the given cache
+ mask = cache->mask;
+ count = mask + 1;
+ for (index1 = 0; index1 < count; index1 += 1)
+ {
+ // Skip invalid entry
+ if (!cache->buckets[index1])
+ continue;
+
+ // Inner loop - check that given entry matches no later entry
+ for (index2 = index1 + 1; index2 < count; index2 += 1)
+ {
+ // Skip invalid entry
+ if (!cache->buckets[index2])
+ continue;
+
+ // Check for duplication by method name comparison
+ if (strcmp ((char *) cache->buckets[index1]->name),
+ (char *) cache->buckets[index2]->name)) == 0)
+ {
+ if (detail)
+ printf ("%s %s\n", _class_getName(cls), sel_getName(cache->buckets[index1]->name));
+ duplicates += 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Log the findings
+ printf ("duplicates = %d\n", duplicates);
+ printf ("total cache fills = %d\n", totalCacheFills);
+}
+
+
+/***********************************************************************
+* PrintCacheHeader.
+**********************************************************************/
+static void PrintCacheHeader(void)
+{
+#ifdef OBJC_INSTRUMENTED
+ printf ("Cache Cache Slots Avg Max AvgS MaxS AvgS MaxS TotalD AvgD MaxD TotalD AvgD MaxD TotD AvgD MaxD\n");
+ printf ("Size Count Used Used Used Hit Hit Miss Miss Hits Prbs Prbs Misses Prbs Prbs Flsh Flsh Flsh\n");
+ printf ("----- ----- ----- ----- ---- ---- ---- ---- ---- ------- ---- ---- ------- ---- ---- ---- ---- ----\n");
+#else
+ printf ("Cache Cache Slots Avg Max AvgS MaxS AvgS MaxS\n");
+ printf ("Size Count Used Used Used Hit Hit Miss Miss\n");
+ printf ("----- ----- ----- ----- ---- ---- ---- ---- ----\n");
+#endif
+}
+
+
+/***********************************************************************
+* PrintCacheInfo.
+**********************************************************************/
+static void PrintCacheInfo(unsigned int cacheSize,
+ unsigned int cacheCount,
+ unsigned int slotsUsed,
+ float avgUsed, unsigned int maxUsed,
+ float avgSHit, unsigned int maxSHit,
+ float avgSMiss, unsigned int maxSMiss
+#ifdef OBJC_INSTRUMENTED
+ , unsigned int totDHits,
+ float avgDHit,
+ unsigned int maxDHit,
+ unsigned int totDMisses,
+ float avgDMiss,
+ unsigned int maxDMiss,
+ unsigned int totDFlsh,
+ float avgDFlsh,
+ unsigned int maxDFlsh
+#endif
+ )
+{
+#ifdef OBJC_INSTRUMENTED
+ printf ("%5u %5u %5u %5.1f %4u %4.1f %4u %4.1f %4u %7u %4.1f %4u %7u %4.1f %4u %4u %4.1f %4u\n",
+#else
+ printf ("%5u %5u %5u %5.1f %4u %4.1f %4u %4.1f %4u\n",
+#endif
+ cacheSize, cacheCount, slotsUsed, avgUsed, maxUsed, avgSHit, maxSHit, avgSMiss, maxSMiss
+#ifdef OBJC_INSTRUMENTED
+ , totDHits, avgDHit, maxDHit, totDMisses, avgDMiss, maxDMiss, totDFlsh, avgDFlsh, maxDFlsh
+#endif
+ );
+
+}
+
+
+#ifdef OBJC_INSTRUMENTED
+/***********************************************************************
+* PrintCacheHistogram. Show the non-zero entries from the specified
+* cache histogram.
+**********************************************************************/
+static void PrintCacheHistogram(char *title,
+ unsigned int *firstEntry,
+ unsigned int entryCount)
+{
+ unsigned int index;
+ unsigned int *thisEntry;
+
+ printf ("%s\n", title);
+ printf (" Probes Tally\n");
+ printf (" ------ -----\n");
+ for (index = 0, thisEntry = firstEntry;
+ index < entryCount;
+ index += 1, thisEntry += 1)
+ {
+ if (*thisEntry == 0)
+ continue;
+
+ printf (" %6d %5d\n", index, *thisEntry);
+ }
+}
+#endif
+
+
+/***********************************************************************
+* _class_printMethodCacheStatistics.
+**********************************************************************/
+
+#define MAX_LOG2_SIZE 32
+#define MAX_CHAIN_SIZE 100
+
+void _class_printMethodCacheStatistics(void)
+{
+ unsigned int isMeta;
+ unsigned int index;
+ NXHashState state;
+ Class cls;
+ unsigned int totalChain;
+ unsigned int totalMissChain;
+ unsigned int maxChain;
+ unsigned int maxMissChain;
+ unsigned int classCount;
+ unsigned int negativeEntryCount;
+ unsigned int cacheExpandCount;
+ unsigned int cacheCountBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int totalEntriesBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int maxEntriesBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int totalChainBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int totalMissChainBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int totalMaxChainBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int totalMaxMissChainBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int maxChainBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int maxMissChainBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int chainCount[MAX_CHAIN_SIZE] = {0};
+ unsigned int missChainCount[MAX_CHAIN_SIZE] = {0};
+#ifdef OBJC_INSTRUMENTED
+ unsigned int hitCountBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int hitProbesBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int maxHitProbesBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int missCountBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int missProbesBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int maxMissProbesBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int flushCountBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int flushedEntriesBySize[2][MAX_LOG2_SIZE] = {{0}};
+ unsigned int maxFlushedEntriesBySize[2][MAX_LOG2_SIZE] = {{0}};
+#endif
+
+ printf ("Printing cache statistics\n");
+
+ // Outermost loop - iterate over all classes
+ state = NXInitHashState (class_hash);
+ classCount = 0;
+ negativeEntryCount = 0;
+ cacheExpandCount = 0;
+ while (NXNextHashState (class_hash, &state, (void **) &cls))
+ {
+ // Tally classes
+ classCount += 1;
+
+ // Control loop - do given class' cache, then its isa's cache
+ for (isMeta = 0; isMeta <= 1; isMeta += 1)
+ {
+ Cache cache;
+ unsigned int mask;
+ unsigned int log2Size;
+ unsigned int entryCount;
+
+ // Select cache of interest
+ cache = _class_getCache(isMeta ? ((id)cls)->isa : cls);
+
+ // Ignore empty cache... should we?
+ if (_cache_isEmpty(cache))
+ continue;
+
+ // Middle loop - do each entry in the given cache
+ mask = cache->mask;
+ entryCount = 0;
+ totalChain = 0;
+ totalMissChain = 0;
+ maxChain = 0;
+ maxMissChain = 0;
+ for (index = 0; index < mask + 1; index += 1)
+ {
+ cache_entry **buckets;
+ cache_entry *entry;
+ unsigned int hash;
+ unsigned int methodChain;
+ unsigned int methodMissChain;
+ unsigned int index2;
+
+ // If entry is invalid, the only item of
+ // interest is that future insert hashes
+ // to this entry can use it directly.
+ buckets = (cache_entry **)cache->buckets;
+ if (!buckets[index])
+ {
+ missChainCount[0] += 1;
+ continue;
+ }
+
+ entry = buckets[index];
+
+ // Tally valid entries
+ entryCount += 1;
+
+ // Tally "forward::" entries
+ if (entry->imp == _objc_msgForward_internal)
+ negativeEntryCount += 1;
+
+ // Calculate search distance (chain length) for this method
+ // The chain may wrap around to the beginning of the table.
+ hash = CACHE_HASH(entry->name, mask);
+ if (index >= hash) methodChain = index - hash;
+ else methodChain = (mask+1) + index - hash;
+
+ // Tally chains of this length
+ if (methodChain < MAX_CHAIN_SIZE)
+ chainCount[methodChain] += 1;
+
+ // Keep sum of all chain lengths
+ totalChain += methodChain;
+
+ // Record greatest chain length
+ if (methodChain > maxChain)
+ maxChain = methodChain;
+
+ // Calculate search distance for miss that hashes here
+ index2 = index;
+ while (buckets[index2])
+ {
+ index2 += 1;
+ index2 &= mask;
+ }
+ methodMissChain = ((index2 - index) & mask);
+
+ // Tally miss chains of this length
+ if (methodMissChain < MAX_CHAIN_SIZE)
+ missChainCount[methodMissChain] += 1;
+
+ // Keep sum of all miss chain lengths in this class
+ totalMissChain += methodMissChain;
+
+ // Record greatest miss chain length
+ if (methodMissChain > maxMissChain)
+ maxMissChain = methodMissChain;
+ }
+
+ // Factor this cache into statistics about caches of the same
+ // type and size (all caches are a power of two in size)
+ log2Size = log2u (mask + 1);
+ cacheCountBySize[isMeta][log2Size] += 1;
+ totalEntriesBySize[isMeta][log2Size] += entryCount;
+ if (entryCount > maxEntriesBySize[isMeta][log2Size])
+ maxEntriesBySize[isMeta][log2Size] = entryCount;
+ totalChainBySize[isMeta][log2Size] += totalChain;
+ totalMissChainBySize[isMeta][log2Size] += totalMissChain;
+ totalMaxChainBySize[isMeta][log2Size] += maxChain;
+ totalMaxMissChainBySize[isMeta][log2Size] += maxMissChain;
+ if (maxChain > maxChainBySize[isMeta][log2Size])
+ maxChainBySize[isMeta][log2Size] = maxChain;
+ if (maxMissChain > maxMissChainBySize[isMeta][log2Size])
+ maxMissChainBySize[isMeta][log2Size] = maxMissChain;
+#ifdef OBJC_INSTRUMENTED
+ {
+ CacheInstrumentation *cacheData;
+
+ cacheData = CACHE_INSTRUMENTATION(cache);
+ hitCountBySize[isMeta][log2Size] += cacheData->hitCount;
+ hitProbesBySize[isMeta][log2Size] += cacheData->hitProbes;
+ if (cacheData->maxHitProbes > maxHitProbesBySize[isMeta][log2Size])
+ maxHitProbesBySize[isMeta][log2Size] = cacheData->maxHitProbes;
+ missCountBySize[isMeta][log2Size] += cacheData->missCount;
+ missProbesBySize[isMeta][log2Size] += cacheData->missProbes;
+ if (cacheData->maxMissProbes > maxMissProbesBySize[isMeta][log2Size])
+ maxMissProbesBySize[isMeta][log2Size] = cacheData->maxMissProbes;
+ flushCountBySize[isMeta][log2Size] += cacheData->flushCount;
+ flushedEntriesBySize[isMeta][log2Size] += cacheData->flushedEntries;
+ if (cacheData->maxFlushedEntries > maxFlushedEntriesBySize[isMeta][log2Size])
+ maxFlushedEntriesBySize[isMeta][log2Size] = cacheData->maxFlushedEntries;
+ }
+#endif
+ // Caches start with a power of two number of entries, and grow by doubling, so
+ // we can calculate the number of times this cache has expanded
+ cacheExpandCount += log2Size - INIT_CACHE_SIZE_LOG2;
+ }
+ }
+
+ {
+ unsigned int cacheCountByType[2] = {0};
+ unsigned int totalCacheCount = 0;
+ unsigned int totalEntries = 0;
+ unsigned int maxEntries = 0;
+ unsigned int totalSlots = 0;
+#ifdef OBJC_INSTRUMENTED
+ unsigned int totalHitCount = 0;
+ unsigned int totalHitProbes = 0;
+ unsigned int maxHitProbes = 0;
+ unsigned int totalMissCount = 0;
+ unsigned int totalMissProbes = 0;
+ unsigned int maxMissProbes = 0;
+ unsigned int totalFlushCount = 0;
+ unsigned int totalFlushedEntries = 0;
+ unsigned int maxFlushedEntries = 0;
+#endif
+
+ totalChain = 0;
+ maxChain = 0;
+ totalMissChain = 0;
+ maxMissChain = 0;
+
+ // Sum information over all caches
+ for (isMeta = 0; isMeta <= 1; isMeta += 1)
+ {
+ for (index = 0; index < MAX_LOG2_SIZE; index += 1)
+ {
+ cacheCountByType[isMeta] += cacheCountBySize[isMeta][index];
+ totalEntries += totalEntriesBySize[isMeta][index];
+ totalSlots += cacheCountBySize[isMeta][index] * (1 << index);
+ totalChain += totalChainBySize[isMeta][index];
+ if (maxEntriesBySize[isMeta][index] > maxEntries)
+ maxEntries = maxEntriesBySize[isMeta][index];
+ if (maxChainBySize[isMeta][index] > maxChain)
+ maxChain = maxChainBySize[isMeta][index];
+ totalMissChain += totalMissChainBySize[isMeta][index];
+ if (maxMissChainBySize[isMeta][index] > maxMissChain)
+ maxMissChain = maxMissChainBySize[isMeta][index];
+#ifdef OBJC_INSTRUMENTED
+ totalHitCount += hitCountBySize[isMeta][index];
+ totalHitProbes += hitProbesBySize[isMeta][index];
+ if (maxHitProbesBySize[isMeta][index] > maxHitProbes)
+ maxHitProbes = maxHitProbesBySize[isMeta][index];
+ totalMissCount += missCountBySize[isMeta][index];
+ totalMissProbes += missProbesBySize[isMeta][index];
+ if (maxMissProbesBySize[isMeta][index] > maxMissProbes)
+ maxMissProbes = maxMissProbesBySize[isMeta][index];
+ totalFlushCount += flushCountBySize[isMeta][index];
+ totalFlushedEntries += flushedEntriesBySize[isMeta][index];
+ if (maxFlushedEntriesBySize[isMeta][index] > maxFlushedEntries)
+ maxFlushedEntries = maxFlushedEntriesBySize[isMeta][index];
+#endif
+ }
+
+ totalCacheCount += cacheCountByType[isMeta];
+ }
+
+ // Log our findings
+ printf ("There are %u classes\n", classCount);
+
+ for (isMeta = 0; isMeta <= 1; isMeta += 1)
+ {
+ // Number of this type of class
+ printf ("\nThere are %u %s-method caches, broken down by size (slot count):\n",
+ cacheCountByType[isMeta],
+ isMeta ? "class" : "instance");
+
+ // Print header
+ PrintCacheHeader ();
+
+ // Keep format consistent even if there are caches of this kind
+ if (cacheCountByType[isMeta] == 0)
+ {
+ printf ("(none)\n");
+ continue;
+ }
+
+ // Usage information by cache size
+ for (index = 0; index < MAX_LOG2_SIZE; index += 1)
+ {
+ unsigned int cacheCount;
+ unsigned int cacheSlotCount;
+ unsigned int cacheEntryCount;
+
+ // Get number of caches of this type and size
+ cacheCount = cacheCountBySize[isMeta][index];
+ if (cacheCount == 0)
+ continue;
+
+ // Get the cache slot count and the total number of valid entries
+ cacheSlotCount = (1 << index);
+ cacheEntryCount = totalEntriesBySize[isMeta][index];
+
+ // Give the analysis
+ PrintCacheInfo (cacheSlotCount,
+ cacheCount,
+ cacheEntryCount,
+ (float) cacheEntryCount / (float) cacheCount,
+ maxEntriesBySize[isMeta][index],
+ (float) totalChainBySize[isMeta][index] / (float) cacheEntryCount,
+ maxChainBySize[isMeta][index],
+ (float) totalMissChainBySize[isMeta][index] / (float) (cacheCount * cacheSlotCount),
+ maxMissChainBySize[isMeta][index]
+#ifdef OBJC_INSTRUMENTED
+ , hitCountBySize[isMeta][index],
+ hitCountBySize[isMeta][index] ?
+ (float) hitProbesBySize[isMeta][index] / (float) hitCountBySize[isMeta][index] : 0.0,
+ maxHitProbesBySize[isMeta][index],
+ missCountBySize[isMeta][index],
+ missCountBySize[isMeta][index] ?
+ (float) missProbesBySize[isMeta][index] / (float) missCountBySize[isMeta][index] : 0.0,
+ maxMissProbesBySize[isMeta][index],
+ flushCountBySize[isMeta][index],
+ flushCountBySize[isMeta][index] ?
+ (float) flushedEntriesBySize[isMeta][index] / (float) flushCountBySize[isMeta][index] : 0.0,
+ maxFlushedEntriesBySize[isMeta][index]
+#endif
+ );
+ }
+ }
+
+ // Give overall numbers
+ printf ("\nCumulative:\n");
+ PrintCacheHeader ();
+ PrintCacheInfo (totalSlots,
+ totalCacheCount,
+ totalEntries,
+ (float) totalEntries / (float) totalCacheCount,
+ maxEntries,
+ (float) totalChain / (float) totalEntries,
+ maxChain,
+ (float) totalMissChain / (float) totalSlots,
+ maxMissChain
+#ifdef OBJC_INSTRUMENTED
+ , totalHitCount,
+ totalHitCount ?
+ (float) totalHitProbes / (float) totalHitCount : 0.0,
+ maxHitProbes,
+ totalMissCount,
+ totalMissCount ?
+ (float) totalMissProbes / (float) totalMissCount : 0.0,
+ maxMissProbes,
+ totalFlushCount,
+ totalFlushCount ?
+ (float) totalFlushedEntries / (float) totalFlushCount : 0.0,
+ maxFlushedEntries
+#endif
+ );
+
+ printf ("\nNumber of \"forward::\" entries: %d\n", negativeEntryCount);
+ printf ("Number of cache expansions: %d\n", cacheExpandCount);
+#ifdef OBJC_INSTRUMENTED
+ printf ("flush_caches: total calls total visits average visits max visits total classes visits/class\n");
+ printf (" ----------- ------------ -------------- ---------- ------------- -------------\n");
+ printf (" linear %11u %12u %14.1f %10u %13u %12.2f\n",
+ LinearFlushCachesCount,
+ LinearFlushCachesVisitedCount,
+ LinearFlushCachesCount ?
+ (float) LinearFlushCachesVisitedCount / (float) LinearFlushCachesCount : 0.0,
+ MaxLinearFlushCachesVisitedCount,
+ LinearFlushCachesVisitedCount,
+ 1.0);
+ printf (" nonlinear %11u %12u %14.1f %10u %13u %12.2f\n",
+ NonlinearFlushCachesCount,
+ NonlinearFlushCachesVisitedCount,
+ NonlinearFlushCachesCount ?
+ (float) NonlinearFlushCachesVisitedCount / (float) NonlinearFlushCachesCount : 0.0,
+ MaxNonlinearFlushCachesVisitedCount,
+ NonlinearFlushCachesClassCount,
+ NonlinearFlushCachesClassCount ?
+ (float) NonlinearFlushCachesVisitedCount / (float) NonlinearFlushCachesClassCount : 0.0);
+ printf (" ideal %11u %12u %14.1f %10u %13u %12.2f\n",
+ LinearFlushCachesCount + NonlinearFlushCachesCount,
+ IdealFlushCachesCount,
+ LinearFlushCachesCount + NonlinearFlushCachesCount ?
+ (float) IdealFlushCachesCount / (float) (LinearFlushCachesCount + NonlinearFlushCachesCount) : 0.0,
+ MaxIdealFlushCachesCount,
+ LinearFlushCachesVisitedCount + NonlinearFlushCachesClassCount,
+ LinearFlushCachesVisitedCount + NonlinearFlushCachesClassCount ?
+ (float) IdealFlushCachesCount / (float) (LinearFlushCachesVisitedCount + NonlinearFlushCachesClassCount) : 0.0);
+
+ PrintCacheHistogram ("\nCache hit histogram:", &CacheHitHistogram[0], CACHE_HISTOGRAM_SIZE);
+ PrintCacheHistogram ("\nCache miss histogram:", &CacheMissHistogram[0], CACHE_HISTOGRAM_SIZE);
+#endif
+
+#if 0
+ printf ("\nLookup chains:");
+ for (index = 0; index < MAX_CHAIN_SIZE; index += 1)
+ {
+ if (chainCount[index] != 0)
+ printf (" %u:%u", index, chainCount[index]);
+ }
+
+ printf ("\nMiss chains:");
+ for (index = 0; index < MAX_CHAIN_SIZE; index += 1)
+ {
+ if (missChainCount[index] != 0)
+ printf (" %u:%u", index, missChainCount[index]);
+ }
+
+ printf ("\nTotal memory usage for cache data structures: %lu bytes\n",
+ totalCacheCount * (sizeof(struct objc_cache) - sizeof(cache_entry *)) +
+ totalSlots * sizeof(cache_entry *) +
+ negativeEntryCount * sizeof(cache_entry));
+#endif
+ }
+}
+
+#endif
NULL, // ivars
NULL, // methodLists
(Cache) &_objc_empty_cache, // cache
- NULL // protocols
+ NULL, // protocols
+ NULL, // ivar_layout;
+ NULL // ext
};
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;
}
// 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;
/***********************************************************************
* 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);
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
/***********************************************************************
* 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);
}
-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;
* 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;
* _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;
* _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);
}
* _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);
}
* 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);
}
* 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);
}
* 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);
}
* 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);
}
}
-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);
* 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)) {
}
-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;
-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) {
* 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;
return result;
}
-PRIVATE_EXTERN Method _class_getMethodNoSuper(Class cls, SEL sel)
+Method _class_getMethodNoSuper(Class cls, SEL sel)
{
Method result;
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);
/***********************************************************************
* objc_getOrigClass.
**********************************************************************/
-PRIVATE_EXTERN Class _objc_getOrigClass(const char *name)
+Class _objc_getOrigClass(const char *name)
{
Class ret;
* 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,
*
* 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);
* 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;
}
-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);
* _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) {
}
-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);
}
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.
* 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)
{
/***********************************************************************
* objc_allocateClass.
**********************************************************************/
-PRIVATE_EXTERN
+
void set_superclass(struct old_class *cls, struct old_class *supercls,
BOOL cls_is_new)
{
* 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;
-#import <objc/runtime.h>
-#import <objc/message.h>
+#include <objc/runtime.h>
+#include <objc/message.h>
+++ /dev/null
-/*
- * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-/***********************************************************************
-* objc-class.m
-* Copyright 1988-1997, Apple Computer, Inc.
-* Author: s. naroff
-**********************************************************************/
-
-
-/***********************************************************************
- * Lazy method list arrays and method list locking (2004-10-19)
- *
- * cls->methodLists may be in one of three forms:
- * 1. NULL: The class has no methods.
- * 2. non-NULL, with CLS_NO_METHOD_ARRAY set: cls->methodLists points
- * to a single method list, which is the class's only method list.
- * 3. non-NULL, with CLS_NO_METHOD_ARRAY clear: cls->methodLists points to
- * an array of method list pointers. The end of the array's block
- * is set to -1. If the actual number of method lists is smaller
- * than that, the rest of the array is NULL.
- *
- * Attaching categories and adding and removing classes may change
- * the form of the class list. In addition, individual method lists
- * may be reallocated when fixed up.
- *
- * Classes are initially read as #1 or #2. If a category is attached
- * or other methods added, the class is changed to #3. Once in form #3,
- * the class is never downgraded to #1 or #2, even if methods are removed.
- * Classes added with objc_addClass are initially either #1 or #3.
- *
- * Accessing and manipulating a class's method lists are synchronized,
- * to prevent races when one thread restructures the list. However,
- * if the class is not yet in use (i.e. not in class_hash), then the
- * thread loading the class may access its method lists without locking.
- *
- * The following functions acquire methodListLock:
- * class_getInstanceMethod
- * class_getClassMethod
- * class_nextMethodList
- * class_addMethods
- * class_removeMethods
- * class_respondsToMethod
- * _class_lookupMethodAndLoadCache
- * lookupMethodInClassAndLoadCache
- * _objc_add_category_flush_caches
- *
- * The following functions don't acquire methodListLock because they
- * only access method lists during class load and unload:
- * _objc_register_category
- * _resolve_categories_for_class (calls _objc_add_category)
- * add_class_to_loadable_list
- * _objc_addClass
- * _objc_remove_classes_in_image
- *
- * The following functions use method lists without holding methodListLock.
- * The caller must either hold methodListLock, or be loading the class.
- * _getMethod (called by class_getInstanceMethod, class_getClassMethod,
- * and class_respondsToMethod)
- * _findMethodInClass (called by _class_lookupMethodAndLoadCache,
- * lookupMethodInClassAndLoadCache, _getMethod)
- * _findMethodInList (called by _findMethodInClass)
- * nextMethodList (called by _findMethodInClass and class_nextMethodList
- * fixupSelectorsInMethodList (called by nextMethodList)
- * _objc_add_category (called by _objc_add_category_flush_caches,
- * resolve_categories_for_class and _objc_register_category)
- * _objc_insertMethods (called by class_addMethods and _objc_add_category)
- * _objc_removeMethods (called by class_removeMethods)
- * _objcTweakMethodListPointerForClass (called by _objc_insertMethods)
- * get_base_method_list (called by add_class_to_loadable_list)
- * lookupNamedMethodInMethodList (called by add_class_to_loadable_list)
- ***********************************************************************/
-
-/***********************************************************************
- * Thread-safety of class info bits (2004-10-19)
- *
- * Some class info bits are used to store mutable runtime state.
- * Modifications of the info bits at particular times need to be
- * synchronized to prevent races.
- *
- * Three thread-safe modification functions are provided:
- * _class_setInfo() // atomically sets some bits
- * _class_clearInfo() // atomically clears some bits
- * _class_changeInfo() // atomically sets some bits and clears others
- * These replace CLS_SETINFO() for the multithreaded cases.
- *
- * Three modification windows are defined:
- * - compile time
- * - class construction or image load (before +load) in one thread
- * - multi-threaded messaging and method caches
- *
- * Info bit modification at compile time and class construction do not
- * need to be locked, because only one thread is manipulating the class.
- * Info bit modification during messaging needs to be locked, because
- * there may be other threads simultaneously messaging or otherwise
- * manipulating the class.
- *
- * Modification windows for each flag:
- *
- * CLS_CLASS: compile-time and class load
- * CLS_META: compile-time and class load
- * CLS_INITIALIZED: +initialize
- * CLS_POSING: messaging
- * CLS_MAPPED: compile-time
- * CLS_FLUSH_CACHE: class load and messaging
- * CLS_GROW_CACHE: messaging
- * CLS_NEED_BIND: unused
- * CLS_METHOD_ARRAY: unused
- * CLS_JAVA_HYBRID: JavaBridge only
- * CLS_JAVA_CLASS: JavaBridge only
- * CLS_INITIALIZING: messaging
- * CLS_FROM_BUNDLE: class load
- * CLS_HAS_CXX_STRUCTORS: compile-time and class load
- * CLS_NO_METHOD_ARRAY: class load and messaging
- * CLS_HAS_LOAD_METHOD: class load
- *
- * CLS_INITIALIZED and CLS_INITIALIZING have additional thread-safety
- * constraints to support thread-safe +initialize. See "Thread safety
- * during class initialization" for details.
- *
- * CLS_JAVA_HYBRID and CLS_JAVA_CLASS are set immediately after JavaBridge
- * calls objc_addClass(). The JavaBridge does not use an atomic update,
- * but the modification counts as "class construction" unless some other
- * thread quickly finds the class via the class list. This race is
- * small and unlikely in well-behaved code.
- *
- * Most info bits that may be modified during messaging are also never
- * read without a lock. There is no general read lock for the info bits.
- * CLS_INITIALIZED: classInitLock
- * CLS_FLUSH_CACHE: cacheUpdateLock
- * CLS_GROW_CACHE: cacheUpdateLock
- * CLS_NO_METHOD_ARRAY: methodListLock
- * CLS_INITIALIZING: classInitLock
- ***********************************************************************/
-
-/***********************************************************************
-* Imports.
-**********************************************************************/
-
-#include "objc-private.h"
-#include "objc-abi.h"
-#include "objc-auto.h"
-#include <objc/message.h>
-
-
-/* overriding the default object allocation and error handling routines */
-
-OBJC_EXPORT id (*_alloc)(Class, size_t);
-OBJC_EXPORT id (*_copy)(id, size_t);
-OBJC_EXPORT id (*_realloc)(id, size_t);
-OBJC_EXPORT id (*_dealloc)(id);
-OBJC_EXPORT id (*_zoneAlloc)(Class, size_t, void *);
-OBJC_EXPORT id (*_zoneRealloc)(id, size_t, void *);
-OBJC_EXPORT id (*_zoneCopy)(id, size_t, void *);
-
-
-/***********************************************************************
-* Function prototypes internal to this module.
-**********************************************************************/
-
-static IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel);
-static Method look_up_method(Class cls, SEL sel, BOOL withCache, BOOL withResolver);
-
-
-/***********************************************************************
-* Static data internal to this module.
-**********************************************************************/
-
-#if !TARGET_OS_WIN32 && !defined(__arm__)
-# define MESSAGE_LOGGING
-#endif
-
-#if defined(MESSAGE_LOGGING)
-// Method call logging
-static int LogObjCMessageSend (BOOL isClassMethod, const char * objectsClass, const char * implementingClass, SEL selector);
-typedef int (*ObjCLogProc)(BOOL, const char *, const char *, SEL);
-
-static int objcMsgLogFD = (-1);
-static ObjCLogProc objcMsgLogProc = &LogObjCMessageSend;
-static int objcMsgLogEnabled = 0;
-#endif
-
-
-/***********************************************************************
-* Information about multi-thread support:
-*
-* Since we do not lock many operations which walk the superclass, method
-* and ivar chains, these chains must remain intact once a class is published
-* by inserting it into the class hashtable. All modifications must be
-* atomic so that someone walking these chains will always geta valid
-* result.
-***********************************************************************/
-
-
-
-/***********************************************************************
-* object_getClass.
-* Locking: None. If you add locking, tell gdb (rdar://7516456).
-**********************************************************************/
-Class object_getClass(id obj)
-{
- return _object_getClass(obj);
-}
-
-
-/***********************************************************************
-* object_setClass.
-**********************************************************************/
-Class object_setClass(id obj, Class cls)
-{
- if (obj) {
- Class old;
- do {
- old = obj->isa;
- } while (! OSAtomicCompareAndSwapPtrBarrier(old, cls, (void*)&obj->isa));
-
- if (old && _class_instancesHaveAssociatedObjects(old)) {
- _class_setInstancesHaveAssociatedObjects(cls);
- }
-
- return old;
- }
- else return Nil;
-}
-
-
-/***********************************************************************
-* object_getClassName.
-**********************************************************************/
-const char *object_getClassName(id obj)
-{
- Class isa = _object_getClass(obj);
- if (isa) return _class_getName(isa);
- else return "nil";
-}
-
-/***********************************************************************
-* object_getIndexedIvars.
-**********************************************************************/
-void *object_getIndexedIvars(id obj)
-{
- // ivars are tacked onto the end of the object
- if (obj) return ((char *) obj) + _class_getInstanceSize(_object_getClass(obj));
- else return NULL;
-}
-
-
-Ivar object_setInstanceVariable(id obj, const char *name, void *value)
-{
- Ivar ivar = NULL;
-
- if (obj && name) {
- if ((ivar = class_getInstanceVariable(_object_getClass(obj), name))) {
- object_setIvar(obj, ivar, (id)value);
- }
- }
- return ivar;
-}
-
-Ivar object_getInstanceVariable(id obj, const char *name, void **value)
-{
- if (obj && name) {
- Ivar ivar;
- if ((ivar = class_getInstanceVariable(_object_getClass(obj), name))) {
- if (value) *value = (void *)object_getIvar(obj, ivar);
- return ivar;
- }
- }
- if (value) *value = NULL;
- return NULL;
-}
-
-static BOOL is_scanned_offset(ptrdiff_t ivar_offset, const uint8_t *layout) {
- ptrdiff_t index = 0, ivar_index = ivar_offset / sizeof(void*);
- uint8_t byte;
- while ((byte = *layout++)) {
- unsigned skips = (byte >> 4);
- unsigned scans = (byte & 0x0F);
- index += skips;
- while (scans--) {
- if (index == ivar_index) return YES;
- if (index > ivar_index) return NO;
- ++index;
- }
- }
- return NO;
-}
-
-// FIXME: this could be optimized.
-
-static Class _ivar_getClass(Class cls, Ivar ivar) {
- Class ivar_class = NULL;
- const char *ivar_name = ivar_getName(ivar);
- Ivar named_ivar = _class_getVariable(cls, ivar_name, &ivar_class);
- if (named_ivar) {
- // the same ivar name can appear multiple times along the superclass chain.
- while (named_ivar != ivar && ivar_class != NULL) {
- ivar_class = class_getSuperclass(ivar_class);
- named_ivar = _class_getVariable(cls, ivar_getName(ivar), &ivar_class);
- }
- }
- return ivar_class;
-}
-
-void object_setIvar(id obj, Ivar ivar, id value)
-{
- if (obj && ivar) {
- Class cls = _ivar_getClass(object_getClass(obj), ivar);
- ptrdiff_t ivar_offset = ivar_getOffset(ivar);
- // if this ivar is a member of an ARR compiled class, then issue the correct barrier according to the layout.
- if (_class_usesAutomaticRetainRelease(cls)) {
- // for ARR, layout strings are relative to the instance start.
- uint32_t instanceStart = _class_getInstanceStart(cls);
- id *location = (id *)((char *)obj + ivar_offset);
- const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
- if (weak_layout && is_scanned_offset(ivar_offset - instanceStart, weak_layout)) {
- // use the weak system to write to this variable.
- objc_storeWeak(location, value);
- return;
- }
- const uint8_t *strong_layout = class_getIvarLayout(cls);
- if (strong_layout && is_scanned_offset(ivar_offset - instanceStart, strong_layout)) {
- objc_storeStrong(location, value);
- return;
- }
- }
-#if SUPPORT_GC
- if (UseGC) {
- // for GC, check for weak references.
- const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
- if (weak_layout && is_scanned_offset(ivar_offset, weak_layout)) {
- objc_assign_weak(value, (id *)((char *)obj + ivar_offset));
- }
- }
-#endif
- objc_assign_ivar_internal(value, obj, ivar_offset);
- }
-}
-
-
-id object_getIvar(id obj, Ivar ivar)
-{
- if (obj && ivar) {
- Class cls = _object_getClass(obj);
- ptrdiff_t ivar_offset = ivar_getOffset(ivar);
- if (_class_usesAutomaticRetainRelease(cls)) {
- // for ARR, layout strings are relative to the instance start.
- uint32_t instanceStart = _class_getInstanceStart(cls);
- const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
- if (weak_layout && is_scanned_offset(ivar_offset - instanceStart, weak_layout)) {
- // use the weak system to read this variable.
- id *location = (id *)((char *)obj + ivar_offset);
- return objc_loadWeak(location);
- }
- }
- id *idx = (id *)((char *)obj + ivar_offset);
-#if SUPPORT_GC
- if (UseGC) {
- const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
- if (weak_layout && is_scanned_offset(ivar_offset, weak_layout)) {
- return objc_read_weak(idx);
- }
- }
-#endif
- return *idx;
- }
- return NULL;
-}
-
-
-/***********************************************************************
-* object_cxxDestructFromClass.
-* Call C++ destructors on obj, starting with cls's
-* dtor method (if any) followed by superclasses' dtors (if any),
-* stopping at cls's dtor (if any).
-* Uses methodListLock and cacheUpdateLock. The caller must hold neither.
-**********************************************************************/
-static void object_cxxDestructFromClass(id obj, Class cls)
-{
- void (*dtor)(id);
-
- // Call cls's dtor first, then superclasses's dtors.
-
- for ( ; cls != NULL; cls = _class_getSuperclass(cls)) {
- if (!_class_hasCxxStructors(cls)) return;
- dtor = (void(*)(id))
- lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
- if (dtor != (void(*)(id))&_objc_msgForward_internal) {
- if (PrintCxxCtors) {
- _objc_inform("CXX: calling C++ destructors for class %s",
- _class_getName(cls));
- }
- (*dtor)(obj);
- }
- }
-}
-
-
-/***********************************************************************
-* object_cxxDestruct.
-* Call C++ destructors on obj, if any.
-* Uses methodListLock and cacheUpdateLock. The caller must hold neither.
-**********************************************************************/
-PRIVATE_EXTERN void object_cxxDestruct(id obj)
-{
- if (!obj) return;
- if (OBJC_IS_TAGGED_PTR(obj)) return;
- object_cxxDestructFromClass(obj, obj->isa); // need not be object_getClass
-}
-
-
-/***********************************************************************
-* object_cxxConstructFromClass.
-* Recursively call C++ constructors on obj, starting with base class's
-* ctor method (if any) followed by subclasses' ctors (if any), stopping
-* at cls's ctor (if any).
-* Returns YES if construction succeeded.
-* Returns NO if some constructor threw an exception. The exception is
-* caught and discarded. Any partial construction is destructed.
-* Uses methodListLock and cacheUpdateLock. The caller must hold neither.
-*
-* .cxx_construct returns id. This really means:
-* return self: construction succeeded
-* return nil: construction failed because a C++ constructor threw an exception
-**********************************************************************/
-static BOOL object_cxxConstructFromClass(id obj, Class cls)
-{
- id (*ctor)(id);
- Class supercls;
-
- // Stop if neither this class nor any superclass has ctors.
- if (!_class_hasCxxStructors(cls)) return YES; // no ctor - ok
-
- supercls = _class_getSuperclass(cls);
-
- // Call superclasses' ctors first, if any.
- if (supercls) {
- BOOL ok = object_cxxConstructFromClass(obj, supercls);
- if (!ok) return NO; // some superclass's ctor failed - give up
- }
-
- // 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
-
- // Call this class's ctor.
- if (PrintCxxCtors) {
- _objc_inform("CXX: calling C++ constructors for class %s", _class_getName(cls));
- }
- if ((*ctor)(obj)) return YES; // ctor called and succeeded - ok
-
- // This class's ctor was called and failed.
- // Call superclasses's dtors to clean up.
- if (supercls) object_cxxDestructFromClass(obj, supercls);
- return NO;
-}
-
-
-/***********************************************************************
-* object_cxxConstructFromClass.
-* Call C++ constructors on obj, if any.
-* Returns YES if construction succeeded.
-* Returns NO if some constructor threw an exception. The exception is
-* caught and discarded. Any partial construction is destructed.
-* Uses methodListLock and cacheUpdateLock. The caller must hold neither.
-**********************************************************************/
-PRIVATE_EXTERN BOOL object_cxxConstruct(id obj)
-{
- if (!obj) return YES;
- if (OBJC_IS_TAGGED_PTR(obj)) return YES;
- return object_cxxConstructFromClass(obj, obj->isa); // need not be object_getClass
-}
-
-
-/***********************************************************************
-* _class_resolveClassMethod
-* Call +resolveClassMethod and return the method added or NULL.
-* cls should be a metaclass.
-* Assumes the method doesn't exist already.
-**********************************************************************/
-static Method _class_resolveClassMethod(Class cls, SEL sel)
-{
- BOOL resolved;
- Method meth = NULL;
- Class clsInstance;
-
- if (!look_up_method(cls, SEL_resolveClassMethod,
- YES /*cache*/, NO /*resolver*/))
- {
- return NULL;
- }
-
- // GrP fixme same hack as +initialize
- if (strncmp(_class_getName(cls), "_%", 2) == 0) {
- // Posee's meta's name is smashed and isn't in the class_hash,
- // so objc_getClass doesn't work.
- const char *baseName = strchr(_class_getName(cls), '%'); // get posee's real name
- clsInstance = (Class)objc_getClass(baseName);
- } else {
- clsInstance = (Class)objc_getClass(_class_getName(cls));
- }
-
- resolved = ((BOOL(*)(id, SEL, SEL))objc_msgSend)((id)clsInstance, SEL_resolveClassMethod, sel);
-
- if (resolved) {
- // +resolveClassMethod adds to self->isa
- meth = look_up_method(cls, sel, YES/*cache*/, NO/*resolver*/);
-
- if (!meth) {
- // Method resolver didn't add anything?
- _objc_inform("+[%s resolveClassMethod:%s] returned YES, but "
- "no new implementation of +[%s %s] was found",
- class_getName(cls),
- sel_getName(sel),
- class_getName(cls),
- sel_getName(sel));
- return NULL;
- }
- }
-
- return meth;
-}
-
-
-/***********************************************************************
-* _class_resolveInstanceMethod
-* Call +resolveInstanceMethod and return the method added or NULL.
-* cls should be a non-meta class.
-* Assumes the method doesn't exist already.
-**********************************************************************/
-static Method _class_resolveInstanceMethod(Class cls, SEL sel)
-{
- BOOL resolved;
- Method meth = NULL;
-
- if (!look_up_method(((id)cls)->isa, SEL_resolveInstanceMethod,
- YES /*cache*/, NO /*resolver*/))
- {
- return NULL;
- }
-
- resolved = ((BOOL(*)(id, SEL, SEL))objc_msgSend)((id)cls, SEL_resolveInstanceMethod, sel);
-
- if (resolved) {
- // +resolveClassMethod adds to self
- meth = look_up_method(cls, sel, YES/*cache*/, NO/*resolver*/);
-
- if (!meth) {
- // Method resolver didn't add anything?
- _objc_inform("+[%s resolveInstanceMethod:%s] returned YES, but "
- "no new implementation of %c[%s %s] was found",
- class_getName(cls),
- sel_getName(sel),
- class_isMetaClass(cls) ? '+' : '-',
- class_getName(cls),
- sel_getName(sel));
- return NULL;
- }
- }
-
- return meth;
-}
-
-
-/***********************************************************************
-* _class_resolveMethod
-* Call +resolveClassMethod or +resolveInstanceMethod and return
-* the method added or NULL.
-* Assumes the method doesn't exist already.
-**********************************************************************/
-PRIVATE_EXTERN Method _class_resolveMethod(Class cls, SEL sel)
-{
- Method meth = NULL;
-
- if (_class_isMetaClass(cls)) {
- meth = _class_resolveClassMethod(cls, sel);
- }
- if (!meth) {
- meth = _class_resolveInstanceMethod(cls, sel);
- }
-
- if (PrintResolving && meth) {
- _objc_inform("RESOLVE: method %c[%s %s] dynamically resolved to %p",
- class_isMetaClass(cls) ? '+' : '-',
- class_getName(cls), sel_getName(sel),
- method_getImplementation(meth));
- }
-
- return meth;
-}
-
-
-/***********************************************************************
-* look_up_method
-* Look up a method in the given class and its superclasses.
-* If withCache==YES, look in the class's method cache too.
-* If withResolver==YES, call +resolveClass/InstanceMethod too.
-* Returns NULL if the method is not found.
-* +forward:: entries are not returned.
-**********************************************************************/
-static Method look_up_method(Class cls, SEL sel,
- BOOL withCache, BOOL withResolver)
-{
- Method meth = NULL;
-
- if (withCache) {
- meth = _cache_getMethod(cls, sel, &_objc_msgForward_internal);
- if (meth == (Method)1) {
- // Cache contains forward:: . Stop searching.
- return NULL;
- }
- }
-
- if (!meth) meth = _class_getMethod(cls, sel);
-
- if (!meth && withResolver) meth = _class_resolveMethod(cls, sel);
-
- return meth;
-}
-
-
-/***********************************************************************
-* class_getInstanceMethod. Return the instance method for the
-* specified class and selector.
-**********************************************************************/
-Method class_getInstanceMethod(Class cls, SEL sel)
-{
- if (!cls || !sel) return NULL;
-
- return look_up_method(cls, sel, YES/*cache*/, YES/*resolver*/);
-}
-
-/***********************************************************************
-* class_getClassMethod. Return the class method for the specified
-* class and selector.
-**********************************************************************/
-Method class_getClassMethod(Class cls, SEL sel)
-{
- if (!cls || !sel) return NULL;
-
- return class_getInstanceMethod(_class_getMeta(cls), sel);
-}
-
-
-/***********************************************************************
-* class_getInstanceVariable. Return the named instance variable.
-**********************************************************************/
-Ivar class_getInstanceVariable(Class cls, const char *name)
-{
- if (!cls || !name) return NULL;
-
- return _class_getVariable(cls, name, NULL);
-}
-
-
-/***********************************************************************
-* class_getClassVariable. Return the named class variable.
-**********************************************************************/
-Ivar class_getClassVariable(Class cls, const char *name)
-{
- if (!cls) return NULL;
-
- return class_getInstanceVariable(((id)cls)->isa, name);
-}
-
-
-/***********************************************************************
-* gdb_objc_class_changed
-* Tell gdb that a class changed. Currently used for OBJC2 ivar layouts only
-* Does nothing; gdb sets a breakpoint on it.
-**********************************************************************/
-BREAKPOINT_FUNCTION(
- void gdb_objc_class_changed(Class cls, unsigned long changes, const char *classname)
-);
-
-
-/***********************************************************************
-* _objc_flush_caches. Flush the caches of the specified class and any
-* of its subclasses. If cls is a meta-class, only meta-class (i.e.
-* class method) caches are flushed. If cls is an instance-class, both
-* instance-class and meta-class caches are flushed.
-**********************************************************************/
-void _objc_flush_caches(Class cls)
-{
- flush_caches (cls, YES);
-
- if (!cls) {
- // collectALot if cls==nil
- mutex_lock(&cacheUpdateLock);
- _cache_collect(true);
- mutex_unlock(&cacheUpdateLock);
- }
-}
-
-
-/***********************************************************************
-* class_respondsToSelector.
-**********************************************************************/
-BOOL class_respondsToMethod(Class cls, SEL sel)
-{
- OBJC_WARN_DEPRECATED;
-
- return class_respondsToSelector(cls, sel);
-}
-
-
-BOOL class_respondsToSelector(Class cls, SEL sel)
-{
- IMP imp;
-
- if (!sel || !cls) return NO;
-
- // Avoids +initialize because it historically did so.
- // We're not returning a callable IMP anyway.
- imp = lookUpMethod(cls, sel, NO/*initialize*/, YES/*cache*/);
- return (imp != (IMP)_objc_msgForward_internal) ? YES : NO;
-}
-
-
-/***********************************************************************
-* class_getMethodImplementation.
-* Returns the IMP that would be invoked if [obj sel] were sent,
-* where obj is an instance of class cls.
-**********************************************************************/
-IMP class_lookupMethod(Class cls, SEL sel)
-{
- OBJC_WARN_DEPRECATED;
-
- // No one responds to zero!
- if (!sel) {
- __objc_error((id)cls, "invalid selector (null)");
- }
-
- return class_getMethodImplementation(cls, sel);
-}
-
-IMP class_getMethodImplementation(Class cls, SEL sel)
-{
- IMP imp;
-
- if (!cls || !sel) return NULL;
-
- imp = lookUpMethod(cls, sel, YES/*initialize*/, YES/*cache*/);
-
- // Translate forwarding function to C-callable external version
- if (imp == (IMP)&_objc_msgForward_internal) {
- return (IMP)&_objc_msgForward;
- }
-
- return imp;
-}
-
-
-IMP class_getMethodImplementation_stret(Class cls, SEL sel)
-{
- IMP imp = class_getMethodImplementation(cls, sel);
-
- // Translate forwarding function to struct-returning version
- if (imp == (IMP)&_objc_msgForward /* not _internal! */) {
- return (IMP)&_objc_msgForward_stret;
- }
- return imp;
-}
-
-
-/***********************************************************************
-* instrumentObjcMessageSends/logObjcMessageSends.
-**********************************************************************/
-#if !defined(MESSAGE_LOGGING) && defined(__arm__)
-void instrumentObjcMessageSends (BOOL flag)
-{
-}
-#elif defined(MESSAGE_LOGGING)
-static int LogObjCMessageSend (BOOL isClassMethod,
- const char * objectsClass,
- const char * implementingClass,
- SEL selector)
-{
- char buf[ 1024 ];
-
- // Create/open the log file
- if (objcMsgLogFD == (-1))
- {
- snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ());
- objcMsgLogFD = secure_open (buf, O_WRONLY | O_CREAT, geteuid());
- if (objcMsgLogFD < 0) {
- // no log file - disable logging
- objcMsgLogEnabled = 0;
- objcMsgLogFD = -1;
- return 1;
- }
- }
-
- // Make the log entry
- snprintf(buf, sizeof(buf), "%c %s %s %s\n",
- isClassMethod ? '+' : '-',
- objectsClass,
- implementingClass,
- sel_getName(selector));
-
- static OSSpinLock lock = OS_SPINLOCK_INIT;
- OSSpinLockLock(&lock);
- write (objcMsgLogFD, buf, strlen(buf));
- OSSpinLockUnlock(&lock);
-
- // Tell caller to not cache the method
- return 0;
-}
-
-void instrumentObjcMessageSends (BOOL flag)
-{
- int enabledValue = (flag) ? 1 : 0;
-
- // Shortcut NOP
- if (objcMsgLogEnabled == enabledValue)
- return;
-
- // If enabling, flush all method caches so we get some traces
- if (flag)
- flush_caches (Nil, YES);
-
- // Sync our log file
- if (objcMsgLogFD != (-1))
- fsync (objcMsgLogFD);
-
- objcMsgLogEnabled = enabledValue;
-}
-
-PRIVATE_EXTERN void logObjcMessageSends (ObjCLogProc logProc)
-{
- if (logProc)
- {
- objcMsgLogProc = logProc;
- objcMsgLogEnabled = 1;
- }
- else
- {
- objcMsgLogProc = logProc;
- objcMsgLogEnabled = 0;
- }
-
- if (objcMsgLogFD != (-1))
- fsync (objcMsgLogFD);
-}
-#endif
-
-/***********************************************************************
-* log_and_fill_cache
-* Log this method call. If the logger permits it, fill the method cache.
-* cls is the method whose cache should be filled.
-* implementer is the class that owns the implementation in question.
-**********************************************************************/
-PRIVATE_EXTERN void
-log_and_fill_cache(Class cls, Class implementer, Method meth, SEL sel)
-{
-#if defined(MESSAGE_LOGGING)
- BOOL cacheIt = YES;
-
- if (objcMsgLogEnabled) {
- cacheIt = objcMsgLogProc (_class_isMetaClass(implementer) ? YES : NO,
- _class_getName(cls),
- _class_getName(implementer),
- sel);
- }
- if (cacheIt)
-#endif
- _cache_fill (cls, meth, sel);
-}
-
-
-/***********************************************************************
-* _class_lookupMethodAndLoadCache.
-* Method lookup for dispatchers ONLY. OTHER CODE SHOULD USE lookUpMethod().
-* 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)
-{
- return lookUpMethod(cls, sel, YES/*initialize*/, NO/*cache*/);
-}
-
-
-/***********************************************************************
-* lookUpMethod.
-* The standard method lookup.
-* 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.
-* 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)
-{
- Class curClass;
- IMP methodPC = NULL;
- Method meth;
- BOOL triedResolver = NO;
-
- // Optimistic cache lookup
- if (cache) {
- methodPC = _cache_getImp(cls, sel);
- if (methodPC) return methodPC;
- }
-
- // realize, +initialize, and any special early exit
- methodPC = prepareForMethodLookup(cls, sel, initialize);
- if (methodPC) return methodPC;
-
-
- // The lock is held to make method-lookup + cache-fill atomic
- // with respect to method addition. Otherwise, a category could
- // be added but ignored indefinitely because the cache was re-filled
- // with the old value after the cache flush on behalf of the category.
- retry:
- lockForMethodLookup();
-
- // Ignore GC selectors
- if (ignoreSelector(sel)) {
- methodPC = _cache_addIgnoredEntry(cls, sel);
- goto done;
- }
-
- // Try this class's cache.
-
- methodPC = _cache_getImp(cls, sel);
- if (methodPC) goto done;
-
- // Try this class's method lists.
-
- meth = _class_getMethodNoSuper_nolock(cls, sel);
- if (meth) {
- log_and_fill_cache(cls, cls, meth, sel);
- methodPC = method_getImplementation(meth);
- goto done;
- }
-
- // Try superclass caches and method lists.
-
- curClass = cls;
- while ((curClass = _class_getSuperclass(curClass))) {
- // Superclass cache.
- 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.
- log_and_fill_cache(cls, curClass, meth, sel);
- methodPC = method_getImplementation(meth);
- goto done;
- }
- else {
- // Found a forward:: entry in a superclass.
- // Stop searching, but don't cache yet; call method
- // resolver for this class first.
- break;
- }
- }
-
- // Superclass method list.
- meth = _class_getMethodNoSuper_nolock(curClass, sel);
- if (meth) {
- log_and_fill_cache(cls, curClass, meth, sel);
- methodPC = method_getImplementation(meth);
- goto done;
- }
- }
-
- // No implementation found. Try method resolver once.
-
- if (!triedResolver) {
- unlockForMethodLookup();
- _class_resolveMethod(cls, sel);
- // Don't cache the result; we don't hold the lock so it may have
- // changed already. Re-do the search from scratch instead.
- triedResolver = YES;
- goto retry;
- }
-
- // No implementation found, and method resolver didn't help.
- // Use forwarding.
-
- _cache_addForwardEntry(cls, sel);
- methodPC = &_objc_msgForward_internal;
-
- done:
- unlockForMethodLookup();
-
- // paranoia: look for ignored selectors with non-ignored implementations
- assert(!(ignoreSelector(sel) && methodPC != (IMP)&_objc_ignored_method));
-
- return methodPC;
-}
-
-
-/***********************************************************************
-* lookupMethodInClassAndLoadCache.
-* Like _class_lookupMethodAndLoadCache, but does not search superclasses.
-* Caches and returns objc_msgForward if the method is not found in the class.
-**********************************************************************/
-static IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel)
-{
- Method meth;
- IMP imp;
-
- // fixme this still has the method list vs method cache race
- // because it doesn't hold a lock across lookup+cache_fill,
- // but it's only used for .cxx_construct/destruct and we assume
- // categories don't change them.
-
- // Search cache first.
- imp = _cache_getImp(cls, sel);
- if (imp) return imp;
-
- // Cache miss. Search method list.
-
- meth = _class_getMethodNoSuper(cls, sel);
-
- if (meth) {
- // Hit in method list. Cache it.
- _cache_fill(cls, meth, sel);
- return method_getImplementation(meth);
- } else {
- // Miss in method list. Cache objc_msgForward.
- _cache_addForwardEntry(cls, sel);
- return &_objc_msgForward_internal;
- }
-}
-
-
-/***********************************************************************
-* _malloc_internal
-* _calloc_internal
-* _realloc_internal
-* _strdup_internal
-* _strdupcat_internal
-* _memdup_internal
-* _free_internal
-* Convenience functions for the internal malloc zone.
-**********************************************************************/
-PRIVATE_EXTERN void *_malloc_internal(size_t size)
-{
- return malloc_zone_malloc(_objc_internal_zone(), size);
-}
-
-PRIVATE_EXTERN void *_calloc_internal(size_t count, size_t size)
-{
- return malloc_zone_calloc(_objc_internal_zone(), count, size);
-}
-
-PRIVATE_EXTERN void *_realloc_internal(void *ptr, size_t size)
-{
- return malloc_zone_realloc(_objc_internal_zone(), ptr, size);
-}
-
-PRIVATE_EXTERN char *_strdup_internal(const char *str)
-{
- size_t len;
- char *dup;
- if (!str) return NULL;
- len = strlen(str);
- dup = 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)
-{
- 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)
-{
- size_t len1 = strlen(s1);
- size_t len2 = strlen(s2);
- char *dup = 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 *dup = malloc_zone_malloc(_objc_internal_zone(), len);
- memcpy(dup, mem, len);
- return dup;
-}
-
-PRIVATE_EXTERN void _free_internal(void *ptr)
-{
- malloc_zone_free(_objc_internal_zone(), ptr);
-}
-
-PRIVATE_EXTERN size_t _malloc_size_internal(void *ptr)
-{
- malloc_zone_t *zone = _objc_internal_zone();
- return zone->size(zone, ptr);
-}
-
-PRIVATE_EXTERN Class _calloc_class(size_t size)
-{
-#if SUPPORT_GC
- if (UseGC) return (Class) malloc_zone_calloc(gc_zone, 1, size);
-#endif
- return (Class) _calloc_internal(1, size);
-}
-
-
-const char *class_getName(Class cls)
-{
- return _class_getName(cls);
-}
-
-Class class_getSuperclass(Class cls)
-{
- return _class_getSuperclass(cls);
-}
-
-BOOL class_isMetaClass(Class cls)
-{
- return _class_isMetaClass(cls);
-}
-
-
-size_t class_getInstanceSize(Class cls)
-{
- return _class_getInstanceSize(cls);
-}
-
-
-/***********************************************************************
-* method_getNumberOfArguments.
-**********************************************************************/
-unsigned int method_getNumberOfArguments(Method m)
-{
- if (!m) return 0;
- return encoding_getNumberOfArguments(method_getTypeEncoding(m));
-}
-
-
-void method_getReturnType(Method m, char *dst, size_t dst_len)
-{
- encoding_getReturnType(method_getTypeEncoding(m), dst, dst_len);
-}
-
-
-char * method_copyReturnType(Method m)
-{
- return encoding_copyReturnType(method_getTypeEncoding(m));
-}
-
-
-void method_getArgumentType(Method m, unsigned int index,
- char *dst, size_t dst_len)
-{
- encoding_getArgumentType(method_getTypeEncoding(m),
- index, dst, dst_len);
-}
-
-
-char * method_copyArgumentType(Method m, unsigned int index)
-{
- return encoding_copyArgumentType(method_getTypeEncoding(m), index);
-}
-
-
-/***********************************************************************
-* objc_constructInstance
-* Creates an instance of `cls` at the location pointed to by `bytes`.
-* `bytes` must point to at least class_getInstanceSize(cls) bytes of
-* well-aligned zero-filled memory.
-* The new object's isa is set. Any C++ constructors are called.
-* Returns `bytes` if successful. Returns nil if `cls` or `bytes` is
-* NULL, or if C++ constructors fail.
-* Note: class_createInstance() and class_createInstances() preflight this.
-**********************************************************************/
-static id
-_objc_constructInstance(Class cls, void *bytes)
-{
- id obj = (id)bytes;
-
- // Set the isa pointer
- obj->isa = cls; // need not be object_setClass
-
- // Call C++ constructors, if any.
- if (!object_cxxConstruct(obj)) {
- // Some C++ constructor threw an exception.
- return nil;
- }
-
- return obj;
-}
-
-
-id
-objc_constructInstance(Class cls, void *bytes)
-{
- if (!cls || !bytes) return nil;
- return _objc_constructInstance(cls, bytes);
-}
-
-
-PRIVATE_EXTERN id
-_objc_constructOrFree(Class cls, void *bytes)
-{
- id obj = _objc_constructInstance(cls, bytes);
- if (!obj) {
-#if SUPPORT_GC
- if (UseGC) {
- auto_zone_retain(gc_zone, bytes); // gc free expects rc==1
- }
-#endif
- free(bytes);
- }
-
- return obj;
-}
-
-
-/***********************************************************************
-* _class_createInstancesFromZone
-* Batch-allocating version of _class_createInstanceFromZone.
-* Attempts to allocate num_requested objects, each with extraBytes.
-* Returns the number of allocated objects (possibly zero), with
-* the allocated pointers in *results.
-**********************************************************************/
-PRIVATE_EXTERN unsigned
-_class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone,
- id *results, unsigned num_requested)
-{
- unsigned num_allocated;
- if (!cls) return 0;
-
- size_t size = _class_getInstanceSize(cls) + extraBytes;
- // CF requires all objects be at least 16 bytes.
- if (size < 16) size = 16;
-
-#if SUPPORT_GC
- if (UseGC) {
- num_allocated =
- auto_zone_batch_allocate(gc_zone, size, AUTO_OBJECT_SCANNED, 0, 1,
- (void**)results, num_requested);
- } else
-#endif
- {
- unsigned i;
- num_allocated =
- malloc_zone_batch_malloc(zone ? zone : malloc_default_zone(),
- size, (void**)results, num_requested);
- for (i = 0; i < num_allocated; i++) {
- bzero(results[i], size);
- }
- }
-
- // Construct each object, and delete any that fail construction.
-
- unsigned shift = 0;
- unsigned i;
- BOOL ctor = _class_hasCxxStructors(cls);
- for (i = 0; i < num_allocated; i++) {
- id obj = results[i];
- if (ctor) obj = _objc_constructOrFree(cls, obj);
- else if (obj) obj->isa = cls; // need not be object_setClass
-
- if (obj) {
- results[i-shift] = obj;
- } else {
- shift++;
- }
- }
-
- return num_allocated - shift;
-}
-
-
-/***********************************************************************
-* inform_duplicate. Complain about duplicate class implementations.
-**********************************************************************/
-PRIVATE_EXTERN void
-inform_duplicate(const char *name, Class oldCls, Class cls)
-{
-#if TARGET_OS_WIN32
- _objc_inform ("Class %s is implemented in two different images.", name);
-#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) : "??";
-
- _objc_inform ("Class %s is implemented in both %s and %s. "
- "One of the two will be used. "
- "Which one is undefined.",
- name, oldName, newName);
-#endif
-}
-
-#if SUPPORT_TAGGED_POINTERS
-/***********************************************************************
- * _objc_insert_tagged_isa
- * Insert an isa into a particular slot in the tagged isa table.
- * Will error & abort if slot already has an isa that is different.
- **********************************************************************/
-void _objc_insert_tagged_isa(unsigned char slotNumber, Class isa) {
- unsigned char actualSlotNumber = (slotNumber << 1) + 1;
- Class previousIsa = _objc_tagged_isa_table[actualSlotNumber];
-
- if (actualSlotNumber & 0xF0) {
- _objc_fatal("%s -- Slot number %uc is too large. Aborting.", __FUNCTION__, slotNumber);
- }
-
- if (actualSlotNumber == 0) {
- _objc_fatal("%s -- Slot number 0 doesn't make sense. Aborting.", __FUNCTION__);
- }
-
- if (isa && previousIsa && (previousIsa != isa)) {
- _objc_fatal("%s -- Tagged pointer table already had an item in that slot (%s). "
- "Not putting (%s) in table. Aborting instead",
- __FUNCTION__, class_getName(previousIsa), class_getName(isa));
- }
- _objc_tagged_isa_table[actualSlotNumber] = isa;
-}
-#endif
-
-
-PRIVATE_EXTERN const char *
-copyPropertyAttributeString(const objc_property_attribute_t *attrs,
- unsigned int count)
-{
- char *result;
- unsigned int i;
- if (count == 0) return strdup("");
-
-#ifndef NDEBUG
- // debug build: sanitize input
- for (i = 0; i < count; i++) {
- assert(attrs[i].name);
- assert(strlen(attrs[i].name) > 0);
- assert(! strchr(attrs[i].name, ','));
- assert(! strchr(attrs[i].name, '"'));
- if (attrs[i].value) assert(! strchr(attrs[i].value, ','));
- }
-#endif
-
- size_t len = 0;
- for (i = 0; i < count; i++) {
- if (attrs[i].value) {
- size_t namelen = strlen(attrs[i].name);
- if (namelen > 1) namelen += 2; // long names get quoted
- len += namelen + strlen(attrs[i].value) + 1;
- }
- }
-
- result = malloc(len + 1);
- char *s = result;
- for (i = 0; i < count; i++) {
- if (attrs[i].value) {
- size_t namelen = strlen(attrs[i].name);
- if (namelen > 1) {
- s += sprintf(s, "\"%s\"%s,", attrs[i].name, attrs[i].value);
- } else {
- s += sprintf(s, "%s%s,", attrs[i].name, attrs[i].value);
- }
- }
- }
-
- // remove trailing ',' if any
- if (s > result) s[-1] = '\0';
-
- return result;
-}
-
-/*
- Property attribute string format:
- - Comma-separated name-value pairs.
- - Name and value may not contain ,
- - Name may not contain "
- - Value may be empty
- - Name is single char, value follows
- - OR Name is double-quoted string of 2+ chars, value follows
-*/
-static unsigned int
-iteratePropertyAttributes(const char *attrs,
- BOOL (*fn)(unsigned int index,
- void *ctx1, void *ctx2,
- const char *name, size_t nlen,
- const char *value, size_t vlen),
- void *ctx1, void *ctx2)
-{
- if (!attrs) return 0;
-
-#ifndef NDEBUG
- const char *attrsend = attrs + strlen(attrs);
-#endif
- unsigned int attrcount = 0;
-
- while (*attrs) {
- // Find the next comma-separated attribute
- const char *start = attrs;
- const char *end = start + strcspn(attrs, ",");
-
- // Move attrs past this attribute and the comma (if any)
- attrs = *end ? end+1 : end;
-
- assert(attrs <= attrsend);
- assert(start <= attrsend);
- assert(end <= attrsend);
-
- // Skip empty attribute
- if (start == end) continue;
-
- // Process one non-empty comma-free attribute [start,end)
- const char *nameStart;
- const char *nameEnd;
-
- assert(start < end);
- assert(*start);
- if (*start != '\"') {
- // single-char short name
- nameStart = start;
- nameEnd = start+1;
- start++;
- }
- else {
- // double-quoted long name
- nameStart = start+1;
- nameEnd = nameStart + strcspn(nameStart, "\",");
- start++; // leading quote
- start += nameEnd - nameStart; // name
- if (*start == '\"') start++; // trailing quote, if any
- }
-
- // Process one possibly-empty comma-free attribute value [start,end)
- const char *valueStart;
- const char *valueEnd;
-
- assert(start <= end);
-
- valueStart = start;
- valueEnd = end;
-
- BOOL more = (*fn)(attrcount, ctx1, ctx2,
- nameStart, nameEnd-nameStart,
- valueStart, valueEnd-valueStart);
- attrcount++;
- if (!more) break;
- }
-
- return attrcount;
-}
-
-
-static BOOL
-copyOneAttribute(unsigned int index, void *ctxa, void *ctxs,
- const char *name, size_t nlen, const char *value, size_t vlen)
-{
- objc_property_attribute_t **ap = (objc_property_attribute_t**)ctxa;
- char **sp = (char **)ctxs;
-
- objc_property_attribute_t *a = *ap;
- char *s = *sp;
-
- a->name = s;
- memcpy(s, name, nlen);
- s += nlen;
- *s++ = '\0';
-
- a->value = s;
- memcpy(s, value, vlen);
- s += vlen;
- *s++ = '\0';
-
- a++;
-
- *ap = a;
- *sp = s;
-
- return YES;
-}
-
-
-PRIVATE_EXTERN objc_property_attribute_t *
-copyPropertyAttributeList(const char *attrs, unsigned int *outCount)
-{
- if (!attrs) {
- if (outCount) *outCount = 0;
- return NULL;
- }
-
- // Result size:
- // number of commas plus 1 for the attributes (upper bound)
- // plus another attribute for the attribute array terminator
- // plus strlen(attrs) for name/value string data (upper bound)
- // plus count*2 for the name/value string terminators (upper bound)
- unsigned int attrcount = 1;
- const char *s;
- for (s = attrs; s && *s; s++) {
- if (*s == ',') attrcount++;
- }
-
- size_t size =
- attrcount * sizeof(objc_property_attribute_t) +
- sizeof(objc_property_attribute_t) +
- strlen(attrs) +
- attrcount * 2;
- objc_property_attribute_t *result = calloc(size, 1);
-
- objc_property_attribute_t *ra = result;
- char *rs = (char *)(ra+attrcount+1);
-
- attrcount = iteratePropertyAttributes(attrs, copyOneAttribute, &ra, &rs);
-
- assert((uint8_t *)(ra+1) <= (uint8_t *)result+size);
- assert((uint8_t *)rs <= (uint8_t *)result+size);
-
- if (attrcount == 0) {
- free(result);
- result = NULL;
- }
-
- if (outCount) *outCount = attrcount;
- return result;
-}
-
-
-static BOOL
-findOneAttribute(unsigned int index, void *ctxa, void *ctxs,
- const char *name, size_t nlen, const char *value, size_t vlen)
-{
- const char *query = (char *)ctxa;
- char **resultp = (char **)ctxs;
-
- if (strlen(query) == nlen && 0 == strncmp(name, query, nlen)) {
- char *result = calloc(vlen+1, 1);
- memcpy(result, value, vlen);
- result[vlen] = '\0';
- *resultp = result;
- return NO;
- }
-
- return YES;
-}
-
-PRIVATE_EXTERN
-char *copyPropertyAttributeValue(const char *attrs, const char *name)
-{
- char *result = NULL;
-
- iteratePropertyAttributes(attrs, findOneAttribute, (void*)name, &result);
-
- return result;
-}
--- /dev/null
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/***********************************************************************
+* objc-class.m
+* Copyright 1988-1997, Apple Computer, Inc.
+* Author: s. naroff
+**********************************************************************/
+
+
+/***********************************************************************
+ * Lazy method list arrays and method list locking (2004-10-19)
+ *
+ * cls->methodLists may be in one of three forms:
+ * 1. NULL: The class has no methods.
+ * 2. non-NULL, with CLS_NO_METHOD_ARRAY set: cls->methodLists points
+ * to a single method list, which is the class's only method list.
+ * 3. non-NULL, with CLS_NO_METHOD_ARRAY clear: cls->methodLists points to
+ * an array of method list pointers. The end of the array's block
+ * is set to -1. If the actual number of method lists is smaller
+ * than that, the rest of the array is NULL.
+ *
+ * Attaching categories and adding and removing classes may change
+ * the form of the class list. In addition, individual method lists
+ * may be reallocated when fixed up.
+ *
+ * Classes are initially read as #1 or #2. If a category is attached
+ * or other methods added, the class is changed to #3. Once in form #3,
+ * the class is never downgraded to #1 or #2, even if methods are removed.
+ * Classes added with objc_addClass are initially either #1 or #3.
+ *
+ * Accessing and manipulating a class's method lists are synchronized,
+ * to prevent races when one thread restructures the list. However,
+ * if the class is not yet in use (i.e. not in class_hash), then the
+ * thread loading the class may access its method lists without locking.
+ *
+ * The following functions acquire methodListLock:
+ * class_getInstanceMethod
+ * class_getClassMethod
+ * class_nextMethodList
+ * class_addMethods
+ * class_removeMethods
+ * class_respondsToMethod
+ * _class_lookupMethodAndLoadCache
+ * lookupMethodInClassAndLoadCache
+ * _objc_add_category_flush_caches
+ *
+ * The following functions don't acquire methodListLock because they
+ * only access method lists during class load and unload:
+ * _objc_register_category
+ * _resolve_categories_for_class (calls _objc_add_category)
+ * add_class_to_loadable_list
+ * _objc_addClass
+ * _objc_remove_classes_in_image
+ *
+ * The following functions use method lists without holding methodListLock.
+ * The caller must either hold methodListLock, or be loading the class.
+ * _getMethod (called by class_getInstanceMethod, class_getClassMethod,
+ * and class_respondsToMethod)
+ * _findMethodInClass (called by _class_lookupMethodAndLoadCache,
+ * lookupMethodInClassAndLoadCache, _getMethod)
+ * _findMethodInList (called by _findMethodInClass)
+ * nextMethodList (called by _findMethodInClass and class_nextMethodList
+ * fixupSelectorsInMethodList (called by nextMethodList)
+ * _objc_add_category (called by _objc_add_category_flush_caches,
+ * resolve_categories_for_class and _objc_register_category)
+ * _objc_insertMethods (called by class_addMethods and _objc_add_category)
+ * _objc_removeMethods (called by class_removeMethods)
+ * _objcTweakMethodListPointerForClass (called by _objc_insertMethods)
+ * get_base_method_list (called by add_class_to_loadable_list)
+ * lookupNamedMethodInMethodList (called by add_class_to_loadable_list)
+ ***********************************************************************/
+
+/***********************************************************************
+ * Thread-safety of class info bits (2004-10-19)
+ *
+ * Some class info bits are used to store mutable runtime state.
+ * Modifications of the info bits at particular times need to be
+ * synchronized to prevent races.
+ *
+ * Three thread-safe modification functions are provided:
+ * _class_setInfo() // atomically sets some bits
+ * _class_clearInfo() // atomically clears some bits
+ * _class_changeInfo() // atomically sets some bits and clears others
+ * These replace CLS_SETINFO() for the multithreaded cases.
+ *
+ * Three modification windows are defined:
+ * - compile time
+ * - class construction or image load (before +load) in one thread
+ * - multi-threaded messaging and method caches
+ *
+ * Info bit modification at compile time and class construction do not
+ * need to be locked, because only one thread is manipulating the class.
+ * Info bit modification during messaging needs to be locked, because
+ * there may be other threads simultaneously messaging or otherwise
+ * manipulating the class.
+ *
+ * Modification windows for each flag:
+ *
+ * CLS_CLASS: compile-time and class load
+ * CLS_META: compile-time and class load
+ * CLS_INITIALIZED: +initialize
+ * CLS_POSING: messaging
+ * CLS_MAPPED: compile-time
+ * CLS_FLUSH_CACHE: class load and messaging
+ * CLS_GROW_CACHE: messaging
+ * CLS_NEED_BIND: unused
+ * CLS_METHOD_ARRAY: unused
+ * CLS_JAVA_HYBRID: JavaBridge only
+ * CLS_JAVA_CLASS: JavaBridge only
+ * CLS_INITIALIZING: messaging
+ * CLS_FROM_BUNDLE: class load
+ * CLS_HAS_CXX_STRUCTORS: compile-time and class load
+ * CLS_NO_METHOD_ARRAY: class load and messaging
+ * CLS_HAS_LOAD_METHOD: class load
+ *
+ * CLS_INITIALIZED and CLS_INITIALIZING have additional thread-safety
+ * constraints to support thread-safe +initialize. See "Thread safety
+ * during class initialization" for details.
+ *
+ * CLS_JAVA_HYBRID and CLS_JAVA_CLASS are set immediately after JavaBridge
+ * calls objc_addClass(). The JavaBridge does not use an atomic update,
+ * but the modification counts as "class construction" unless some other
+ * thread quickly finds the class via the class list. This race is
+ * small and unlikely in well-behaved code.
+ *
+ * Most info bits that may be modified during messaging are also never
+ * read without a lock. There is no general read lock for the info bits.
+ * CLS_INITIALIZED: classInitLock
+ * CLS_FLUSH_CACHE: cacheUpdateLock
+ * CLS_GROW_CACHE: cacheUpdateLock
+ * CLS_NO_METHOD_ARRAY: methodListLock
+ * CLS_INITIALIZING: classInitLock
+ ***********************************************************************/
+
+/***********************************************************************
+* Imports.
+**********************************************************************/
+
+#include "objc-private.h"
+#include "objc-abi.h"
+#include "objc-auto.h"
+#include <objc/message.h>
+
+
+/* overriding the default object allocation and error handling routines */
+
+OBJC_EXPORT id (*_alloc)(Class, size_t);
+OBJC_EXPORT id (*_copy)(id, size_t);
+OBJC_EXPORT id (*_realloc)(id, size_t);
+OBJC_EXPORT id (*_dealloc)(id);
+OBJC_EXPORT id (*_zoneAlloc)(Class, size_t, void *);
+OBJC_EXPORT id (*_zoneRealloc)(id, size_t, void *);
+OBJC_EXPORT id (*_zoneCopy)(id, size_t, void *);
+
+
+/***********************************************************************
+* Function prototypes internal to this module.
+**********************************************************************/
+
+static IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel);
+static Method look_up_method(Class cls, SEL sel, BOOL withCache, BOOL withResolver);
+
+
+/***********************************************************************
+* Static data internal to this module.
+**********************************************************************/
+
+#if !TARGET_OS_WIN32 && !defined(__arm__)
+# define MESSAGE_LOGGING
+#endif
+
+#if defined(MESSAGE_LOGGING)
+// Method call logging
+static int LogObjCMessageSend (BOOL isClassMethod, const char * objectsClass, const char * implementingClass, SEL selector);
+typedef int (*ObjCLogProc)(BOOL, const char *, const char *, SEL);
+
+static int objcMsgLogFD = (-1);
+static ObjCLogProc objcMsgLogProc = &LogObjCMessageSend;
+static int objcMsgLogEnabled = 0;
+#endif
+
+
+/***********************************************************************
+* Information about multi-thread support:
+*
+* Since we do not lock many operations which walk the superclass, method
+* and ivar chains, these chains must remain intact once a class is published
+* by inserting it into the class hashtable. All modifications must be
+* atomic so that someone walking these chains will always geta valid
+* result.
+***********************************************************************/
+
+
+
+/***********************************************************************
+* object_getClass.
+* Locking: None. If you add locking, tell gdb (rdar://7516456).
+**********************************************************************/
+Class object_getClass(id obj)
+{
+ return _object_getClass(obj);
+}
+
+
+/***********************************************************************
+* object_setClass.
+**********************************************************************/
+Class object_setClass(id obj, Class cls)
+{
+ if (obj) {
+ Class old;
+ do {
+ old = obj->isa;
+ } while (! OSAtomicCompareAndSwapPtrBarrier(old, cls, (void * volatile *)&obj->isa));
+
+ if (old && _class_instancesHaveAssociatedObjects(old)) {
+ _class_setInstancesHaveAssociatedObjects(cls);
+ }
+
+ return old;
+ }
+ else return Nil;
+}
+
+
+/***********************************************************************
+* object_getClassName.
+**********************************************************************/
+const char *object_getClassName(id obj)
+{
+ Class isa = _object_getClass(obj);
+ if (isa) return _class_getName(isa);
+ else return "nil";
+}
+
+/***********************************************************************
+* object_getIndexedIvars.
+**********************************************************************/
+void *object_getIndexedIvars(id obj)
+{
+ // ivars are tacked onto the end of the object
+ if (obj) return ((char *) obj) + _class_getInstanceSize(_object_getClass(obj));
+ else return NULL;
+}
+
+
+Ivar object_setInstanceVariable(id obj, const char *name, void *value)
+{
+ Ivar ivar = NULL;
+
+ if (obj && name) {
+ if ((ivar = class_getInstanceVariable(_object_getClass(obj), name))) {
+ object_setIvar(obj, ivar, (id)value);
+ }
+ }
+ return ivar;
+}
+
+Ivar object_getInstanceVariable(id obj, const char *name, void **value)
+{
+ if (obj && name) {
+ Ivar ivar;
+ if ((ivar = class_getInstanceVariable(_object_getClass(obj), name))) {
+ if (value) *value = (void *)object_getIvar(obj, ivar);
+ return ivar;
+ }
+ }
+ if (value) *value = NULL;
+ return NULL;
+}
+
+static BOOL is_scanned_offset(ptrdiff_t ivar_offset, const uint8_t *layout) {
+ ptrdiff_t index = 0, ivar_index = ivar_offset / sizeof(void*);
+ uint8_t byte;
+ while ((byte = *layout++)) {
+ unsigned skips = (byte >> 4);
+ unsigned scans = (byte & 0x0F);
+ index += skips;
+ while (scans--) {
+ if (index == ivar_index) return YES;
+ if (index > ivar_index) return NO;
+ ++index;
+ }
+ }
+ return NO;
+}
+
+// FIXME: this could be optimized.
+
+static Class _ivar_getClass(Class cls, Ivar ivar) {
+ Class ivar_class = NULL;
+ const char *ivar_name = ivar_getName(ivar);
+ Ivar named_ivar = _class_getVariable(cls, ivar_name, &ivar_class);
+ if (named_ivar) {
+ // the same ivar name can appear multiple times along the superclass chain.
+ while (named_ivar != ivar && ivar_class != NULL) {
+ ivar_class = class_getSuperclass(ivar_class);
+ named_ivar = _class_getVariable(cls, ivar_getName(ivar), &ivar_class);
+ }
+ }
+ return ivar_class;
+}
+
+void object_setIvar(id obj, Ivar ivar, id value)
+{
+ if (obj && ivar) {
+ 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);
+ const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
+ if (weak_layout && is_scanned_offset(ivar_offset - instanceStart, weak_layout)) {
+ // use the weak system to write to this variable.
+ objc_storeWeak(location, value);
+ return;
+ }
+ const uint8_t *strong_layout = class_getIvarLayout(cls);
+ if (strong_layout && is_scanned_offset(ivar_offset - instanceStart, strong_layout)) {
+ objc_storeStrong(location, value);
+ return;
+ }
+ }
+#if SUPPORT_GC
+ if (UseGC) {
+ // for GC, check for weak references.
+ const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
+ if (weak_layout && is_scanned_offset(ivar_offset, weak_layout)) {
+ objc_assign_weak(value, location);
+ }
+ }
+ objc_assign_ivar_internal(value, obj, ivar_offset);
+#else
+ *location = value;
+#endif
+ }
+}
+
+
+id object_getIvar(id obj, Ivar ivar)
+{
+ if (obj && ivar) {
+ Class cls = _object_getClass(obj);
+ ptrdiff_t ivar_offset = ivar_getOffset(ivar);
+ if (_class_usesAutomaticRetainRelease(cls)) {
+ // for ARR, layout strings are relative to the instance start.
+ uint32_t instanceStart = _class_getInstanceStart(cls);
+ const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
+ if (weak_layout && is_scanned_offset(ivar_offset - instanceStart, weak_layout)) {
+ // use the weak system to read this variable.
+ id *location = (id *)((char *)obj + ivar_offset);
+ return objc_loadWeak(location);
+ }
+ }
+ id *idx = (id *)((char *)obj + ivar_offset);
+#if SUPPORT_GC
+ if (UseGC) {
+ const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
+ if (weak_layout && is_scanned_offset(ivar_offset, weak_layout)) {
+ return objc_read_weak(idx);
+ }
+ }
+#endif
+ return *idx;
+ }
+ return NULL;
+}
+
+
+/***********************************************************************
+* object_cxxDestructFromClass.
+* Call C++ destructors on obj, starting with cls's
+* dtor method (if any) followed by superclasses' dtors (if any),
+* stopping at cls's dtor (if any).
+* Uses methodListLock and cacheUpdateLock. The caller must hold neither.
+**********************************************************************/
+static void object_cxxDestructFromClass(id obj, Class cls)
+{
+ void (*dtor)(id);
+
+ // Call cls's dtor first, then superclasses's dtors.
+
+ for ( ; cls != NULL; cls = _class_getSuperclass(cls)) {
+ if (!_class_hasCxxStructors(cls)) return;
+ dtor = (void(*)(id))
+ lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
+ if (dtor != (void(*)(id))_objc_msgForward_internal) {
+ if (PrintCxxCtors) {
+ _objc_inform("CXX: calling C++ destructors for class %s",
+ _class_getName(cls));
+ }
+ (*dtor)(obj);
+ }
+ }
+}
+
+
+/***********************************************************************
+* object_cxxDestruct.
+* Call C++ destructors on obj, if any.
+* Uses methodListLock and cacheUpdateLock. The caller must hold neither.
+**********************************************************************/
+void object_cxxDestruct(id obj)
+{
+ if (!obj) return;
+ if (OBJC_IS_TAGGED_PTR(obj)) return;
+ object_cxxDestructFromClass(obj, obj->isa); // need not be object_getClass
+}
+
+
+/***********************************************************************
+* object_cxxConstructFromClass.
+* Recursively call C++ constructors on obj, starting with base class's
+* ctor method (if any) followed by subclasses' ctors (if any), stopping
+* at cls's ctor (if any).
+* Returns YES if construction succeeded.
+* Returns NO if some constructor threw an exception. The exception is
+* caught and discarded. Any partial construction is destructed.
+* Uses methodListLock and cacheUpdateLock. The caller must hold neither.
+*
+* .cxx_construct returns id. This really means:
+* return self: construction succeeded
+* return nil: construction failed because a C++ constructor threw an exception
+**********************************************************************/
+static BOOL object_cxxConstructFromClass(id obj, Class cls)
+{
+ id (*ctor)(id);
+ Class supercls;
+
+ // Stop if neither this class nor any superclass has ctors.
+ if (!_class_hasCxxStructors(cls)) return YES; // no ctor - ok
+
+ supercls = _class_getSuperclass(cls);
+
+ // Call superclasses' ctors first, if any.
+ if (supercls) {
+ BOOL ok = object_cxxConstructFromClass(obj, supercls);
+ if (!ok) return NO; // some superclass's ctor failed - give up
+ }
+
+ // 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
+
+ // Call this class's ctor.
+ if (PrintCxxCtors) {
+ _objc_inform("CXX: calling C++ constructors for class %s", _class_getName(cls));
+ }
+ if ((*ctor)(obj)) return YES; // ctor called and succeeded - ok
+
+ // This class's ctor was called and failed.
+ // Call superclasses's dtors to clean up.
+ if (supercls) object_cxxDestructFromClass(obj, supercls);
+ return NO;
+}
+
+
+/***********************************************************************
+* object_cxxConstructFromClass.
+* Call C++ constructors on obj, if any.
+* Returns YES if construction succeeded.
+* Returns NO if some constructor threw an exception. The exception is
+* caught and discarded. Any partial construction is destructed.
+* Uses methodListLock and cacheUpdateLock. The caller must hold neither.
+**********************************************************************/
+BOOL object_cxxConstruct(id obj)
+{
+ if (!obj) return YES;
+ if (OBJC_IS_TAGGED_PTR(obj)) return YES;
+ return object_cxxConstructFromClass(obj, obj->isa); // need not be object_getClass
+}
+
+
+/***********************************************************************
+* _class_resolveClassMethod
+* Call +resolveClassMethod and return the method added or NULL.
+* cls should be a metaclass.
+* Assumes the method doesn't exist already.
+**********************************************************************/
+static Method _class_resolveClassMethod(Class cls, SEL sel)
+{
+ BOOL resolved;
+ Method meth = NULL;
+ Class clsInstance;
+
+ if (!look_up_method(cls, SEL_resolveClassMethod,
+ YES /*cache*/, NO /*resolver*/))
+ {
+ return NULL;
+ }
+
+ // GrP fixme same hack as +initialize
+ if (strncmp(_class_getName(cls), "_%", 2) == 0) {
+ // Posee's meta's name is smashed and isn't in the class_hash,
+ // so objc_getClass doesn't work.
+ const char *baseName = strchr(_class_getName(cls), '%'); // get posee's real name
+ clsInstance = (Class)objc_getClass(baseName);
+ } else {
+ clsInstance = (Class)objc_getClass(_class_getName(cls));
+ }
+
+ resolved = ((BOOL(*)(id, SEL, SEL))objc_msgSend)((id)clsInstance, SEL_resolveClassMethod, sel);
+
+ if (resolved) {
+ // +resolveClassMethod adds to self->isa
+ meth = look_up_method(cls, sel, YES/*cache*/, NO/*resolver*/);
+
+ if (!meth) {
+ // Method resolver didn't add anything?
+ _objc_inform("+[%s resolveClassMethod:%s] returned YES, but "
+ "no new implementation of +[%s %s] was found",
+ class_getName(cls),
+ sel_getName(sel),
+ class_getName(cls),
+ sel_getName(sel));
+ return NULL;
+ }
+ }
+
+ return meth;
+}
+
+
+/***********************************************************************
+* _class_resolveInstanceMethod
+* Call +resolveInstanceMethod and return the method added or NULL.
+* cls should be a non-meta class.
+* Assumes the method doesn't exist already.
+**********************************************************************/
+static Method _class_resolveInstanceMethod(Class cls, SEL sel)
+{
+ BOOL resolved;
+ Method meth = NULL;
+
+ if (!look_up_method(((id)cls)->isa, SEL_resolveInstanceMethod,
+ YES /*cache*/, NO /*resolver*/))
+ {
+ return NULL;
+ }
+
+ resolved = ((BOOL(*)(id, SEL, SEL))objc_msgSend)((id)cls, SEL_resolveInstanceMethod, sel);
+
+ if (resolved) {
+ // +resolveClassMethod adds to self
+ meth = look_up_method(cls, sel, YES/*cache*/, NO/*resolver*/);
+
+ if (!meth) {
+ // Method resolver didn't add anything?
+ _objc_inform("+[%s resolveInstanceMethod:%s] returned YES, but "
+ "no new implementation of %c[%s %s] was found",
+ class_getName(cls),
+ sel_getName(sel),
+ class_isMetaClass(cls) ? '+' : '-',
+ class_getName(cls),
+ sel_getName(sel));
+ return NULL;
+ }
+ }
+
+ return meth;
+}
+
+
+/***********************************************************************
+* _class_resolveMethod
+* Call +resolveClassMethod or +resolveInstanceMethod and return
+* the method added or NULL.
+* Assumes the method doesn't exist already.
+**********************************************************************/
+Method _class_resolveMethod(Class cls, SEL sel)
+{
+ Method meth = NULL;
+
+ if (_class_isMetaClass(cls)) {
+ meth = _class_resolveClassMethod(cls, sel);
+ }
+ if (!meth) {
+ meth = _class_resolveInstanceMethod(cls, sel);
+ }
+
+ if (PrintResolving && meth) {
+ _objc_inform("RESOLVE: method %c[%s %s] dynamically resolved to %p",
+ class_isMetaClass(cls) ? '+' : '-',
+ class_getName(cls), sel_getName(sel),
+ method_getImplementation(meth));
+ }
+
+ return meth;
+}
+
+
+/***********************************************************************
+* look_up_method
+* Look up a method in the given class and its superclasses.
+* If withCache==YES, look in the class's method cache too.
+* If withResolver==YES, call +resolveClass/InstanceMethod too.
+* Returns NULL if the method is not found.
+* +forward:: entries are not returned.
+**********************************************************************/
+static Method look_up_method(Class cls, SEL sel,
+ BOOL withCache, BOOL withResolver)
+{
+ Method meth = NULL;
+
+ if (withCache) {
+ meth = _cache_getMethod(cls, sel, _objc_msgForward_internal);
+ if (meth == (Method)1) {
+ // Cache contains forward:: . Stop searching.
+ return NULL;
+ }
+ }
+
+ if (!meth) meth = _class_getMethod(cls, sel);
+
+ if (!meth && withResolver) meth = _class_resolveMethod(cls, sel);
+
+ return meth;
+}
+
+
+/***********************************************************************
+* class_getInstanceMethod. Return the instance method for the
+* specified class and selector.
+**********************************************************************/
+Method class_getInstanceMethod(Class cls, SEL sel)
+{
+ if (!cls || !sel) return NULL;
+
+ return look_up_method(cls, sel, YES/*cache*/, YES/*resolver*/);
+}
+
+/***********************************************************************
+* class_getClassMethod. Return the class method for the specified
+* class and selector.
+**********************************************************************/
+Method class_getClassMethod(Class cls, SEL sel)
+{
+ if (!cls || !sel) return NULL;
+
+ return class_getInstanceMethod(_class_getMeta(cls), sel);
+}
+
+
+/***********************************************************************
+* class_getInstanceVariable. Return the named instance variable.
+**********************************************************************/
+Ivar class_getInstanceVariable(Class cls, const char *name)
+{
+ if (!cls || !name) return NULL;
+
+ return _class_getVariable(cls, name, NULL);
+}
+
+
+/***********************************************************************
+* class_getClassVariable. Return the named class variable.
+**********************************************************************/
+Ivar class_getClassVariable(Class cls, const char *name)
+{
+ if (!cls) return NULL;
+
+ return class_getInstanceVariable(((id)cls)->isa, name);
+}
+
+
+/***********************************************************************
+* gdb_objc_class_changed
+* Tell gdb that a class changed. Currently used for OBJC2 ivar layouts only
+* Does nothing; gdb sets a breakpoint on it.
+**********************************************************************/
+BREAKPOINT_FUNCTION(
+ void gdb_objc_class_changed(Class cls, unsigned long changes, const char *classname)
+);
+
+
+/***********************************************************************
+* _objc_flush_caches. Flush the caches of the specified class and any
+* of its subclasses. If cls is a meta-class, only meta-class (i.e.
+* class method) caches are flushed. If cls is an instance-class, both
+* instance-class and meta-class caches are flushed.
+**********************************************************************/
+void _objc_flush_caches(Class cls)
+{
+ flush_caches (cls, YES);
+
+ if (!cls) {
+ // collectALot if cls==nil
+ mutex_lock(&cacheUpdateLock);
+ _cache_collect(true);
+ mutex_unlock(&cacheUpdateLock);
+ }
+}
+
+
+/***********************************************************************
+* class_respondsToSelector.
+**********************************************************************/
+BOOL class_respondsToMethod(Class cls, SEL sel)
+{
+ OBJC_WARN_DEPRECATED;
+
+ return class_respondsToSelector(cls, sel);
+}
+
+
+BOOL class_respondsToSelector(Class cls, SEL sel)
+{
+ IMP imp;
+
+ if (!sel || !cls) return NO;
+
+ // Avoids +initialize because it historically did so.
+ // We're not returning a callable IMP anyway.
+ imp = lookUpMethod(cls, sel, NO/*initialize*/, YES/*cache*/, nil);
+ return (imp != (IMP)_objc_msgForward_internal) ? YES : NO;
+}
+
+
+/***********************************************************************
+* class_getMethodImplementation.
+* Returns the IMP that would be invoked if [obj sel] were sent,
+* where obj is an instance of class cls.
+**********************************************************************/
+IMP class_lookupMethod(Class cls, SEL sel)
+{
+ OBJC_WARN_DEPRECATED;
+
+ // No one responds to zero!
+ if (!sel) {
+ __objc_error((id)cls, "invalid selector (null)");
+ }
+
+ return class_getMethodImplementation(cls, sel);
+}
+
+IMP class_getMethodImplementation(Class cls, SEL sel)
+{
+ IMP imp;
+
+ if (!cls || !sel) return NULL;
+
+ imp = lookUpMethod(cls, sel, YES/*initialize*/, YES/*cache*/, nil);
+
+ // Translate forwarding function to C-callable external version
+ if (imp == _objc_msgForward_internal) {
+ return _objc_msgForward;
+ }
+
+ return imp;
+}
+
+
+IMP class_getMethodImplementation_stret(Class cls, SEL sel)
+{
+ IMP imp = class_getMethodImplementation(cls, sel);
+
+ // Translate forwarding function to struct-returning version
+ if (imp == (IMP)&_objc_msgForward /* not _internal! */) {
+ return (IMP)&_objc_msgForward_stret;
+ }
+ return imp;
+}
+
+
+/***********************************************************************
+* instrumentObjcMessageSends/logObjcMessageSends.
+**********************************************************************/
+#if !defined(MESSAGE_LOGGING) && defined(__arm__)
+void instrumentObjcMessageSends (BOOL flag)
+{
+}
+#elif defined(MESSAGE_LOGGING)
+static int LogObjCMessageSend (BOOL isClassMethod,
+ const char * objectsClass,
+ const char * implementingClass,
+ SEL selector)
+{
+ char buf[ 1024 ];
+
+ // Create/open the log file
+ if (objcMsgLogFD == (-1))
+ {
+ snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ());
+ objcMsgLogFD = secure_open (buf, O_WRONLY | O_CREAT, geteuid());
+ if (objcMsgLogFD < 0) {
+ // no log file - disable logging
+ objcMsgLogEnabled = 0;
+ objcMsgLogFD = -1;
+ return 1;
+ }
+ }
+
+ // Make the log entry
+ snprintf(buf, sizeof(buf), "%c %s %s %s\n",
+ isClassMethod ? '+' : '-',
+ objectsClass,
+ implementingClass,
+ sel_getName(selector));
+
+ static OSSpinLock lock = OS_SPINLOCK_INIT;
+ OSSpinLockLock(&lock);
+ write (objcMsgLogFD, buf, strlen(buf));
+ OSSpinLockUnlock(&lock);
+
+ // Tell caller to not cache the method
+ return 0;
+}
+
+void instrumentObjcMessageSends (BOOL flag)
+{
+ int enabledValue = (flag) ? 1 : 0;
+
+ // Shortcut NOP
+ if (objcMsgLogEnabled == enabledValue)
+ return;
+
+ // If enabling, flush all method caches so we get some traces
+ if (flag)
+ flush_caches (Nil, YES);
+
+ // Sync our log file
+ if (objcMsgLogFD != (-1))
+ fsync (objcMsgLogFD);
+
+ objcMsgLogEnabled = enabledValue;
+}
+
+void logObjcMessageSends (ObjCLogProc logProc)
+{
+ if (logProc)
+ {
+ objcMsgLogProc = logProc;
+ objcMsgLogEnabled = 1;
+ }
+ else
+ {
+ objcMsgLogProc = logProc;
+ objcMsgLogEnabled = 0;
+ }
+
+ if (objcMsgLogFD != (-1))
+ fsync (objcMsgLogFD);
+}
+#endif
+
+/***********************************************************************
+* log_and_fill_cache
+* Log this method call. If the logger permits it, fill the method cache.
+* cls is the method whose cache should be filled.
+* implementer is the class that owns the implementation in question.
+**********************************************************************/
+void
+log_and_fill_cache(Class cls, Class implementer, Method meth, SEL sel)
+{
+#if defined(MESSAGE_LOGGING)
+ BOOL cacheIt = YES;
+
+ if (objcMsgLogEnabled) {
+ cacheIt = objcMsgLogProc (_class_isMetaClass(implementer) ? YES : NO,
+ _class_getName(cls),
+ _class_getName(implementer),
+ sel);
+ }
+ if (cacheIt)
+#endif
+ _cache_fill (cls, meth, sel);
+}
+
+
+/***********************************************************************
+* _class_lookupMethodAndLoadCache.
+* Method lookup for dispatchers ONLY. OTHER CODE SHOULD USE lookUpMethod().
+* This lookup avoids optimistic cache scan because the dispatcher
+* already tried that.
+**********************************************************************/
+IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
+{
+ return lookUpMethod(cls, sel, YES/*initialize*/, NO/*cache*/, obj);
+}
+
+
+/***********************************************************************
+* lookUpMethod.
+* The standard method lookup.
+* 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.
+**********************************************************************/
+IMP lookUpMethod(Class cls, SEL sel, BOOL initialize, BOOL cache, id inst)
+{
+ Class curClass;
+ IMP methodPC = NULL;
+ Method meth;
+ BOOL triedResolver = NO;
+
+ // Optimistic cache lookup
+ if (cache) {
+ methodPC = _cache_getImp(cls, sel);
+ if (methodPC) return methodPC;
+ }
+
+ // realize, +initialize, and any special early exit
+ methodPC = prepareForMethodLookup(cls, sel, initialize, inst);
+ if (methodPC) return methodPC;
+
+
+ // The lock is held to make method-lookup + cache-fill atomic
+ // with respect to method addition. Otherwise, a category could
+ // be added but ignored indefinitely because the cache was re-filled
+ // with the old value after the cache flush on behalf of the category.
+ retry:
+ lockForMethodLookup();
+
+ // Ignore GC selectors
+ if (ignoreSelector(sel)) {
+ methodPC = _cache_addIgnoredEntry(cls, sel);
+ goto done;
+ }
+
+ // Try this class's cache.
+
+ methodPC = _cache_getImp(cls, sel);
+ if (methodPC) goto done;
+
+ // Try this class's method lists.
+
+ meth = _class_getMethodNoSuper_nolock(cls, sel);
+ if (meth) {
+ log_and_fill_cache(cls, cls, meth, sel);
+ methodPC = method_getImplementation(meth);
+ goto done;
+ }
+
+ // Try superclass caches and method lists.
+
+ curClass = cls;
+ while ((curClass = _class_getSuperclass(curClass))) {
+ // Superclass cache.
+ 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.
+ log_and_fill_cache(cls, curClass, meth, sel);
+ methodPC = method_getImplementation(meth);
+ goto done;
+ }
+ else {
+ // Found a forward:: entry in a superclass.
+ // Stop searching, but don't cache yet; call method
+ // resolver for this class first.
+ break;
+ }
+ }
+
+ // Superclass method list.
+ meth = _class_getMethodNoSuper_nolock(curClass, sel);
+ if (meth) {
+ log_and_fill_cache(cls, curClass, meth, sel);
+ methodPC = method_getImplementation(meth);
+ goto done;
+ }
+ }
+
+ // No implementation found. Try method resolver once.
+
+ if (!triedResolver) {
+ unlockForMethodLookup();
+ _class_resolveMethod(cls, sel);
+ // Don't cache the result; we don't hold the lock so it may have
+ // changed already. Re-do the search from scratch instead.
+ triedResolver = YES;
+ goto retry;
+ }
+
+ // No implementation found, and method resolver didn't help.
+ // Use forwarding.
+
+ _cache_addForwardEntry(cls, sel);
+ methodPC = _objc_msgForward_internal;
+
+ done:
+ unlockForMethodLookup();
+
+ // paranoia: look for ignored selectors with non-ignored implementations
+ assert(!(ignoreSelector(sel) && methodPC != (IMP)&_objc_ignored_method));
+
+ return methodPC;
+}
+
+
+/***********************************************************************
+* lookupMethodInClassAndLoadCache.
+* Like _class_lookupMethodAndLoadCache, but does not search superclasses.
+* Caches and returns objc_msgForward if the method is not found in the class.
+**********************************************************************/
+static IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel)
+{
+ Method meth;
+ IMP imp;
+
+ // fixme this still has the method list vs method cache race
+ // because it doesn't hold a lock across lookup+cache_fill,
+ // but it's only used for .cxx_construct/destruct and we assume
+ // categories don't change them.
+
+ // Search cache first.
+ imp = _cache_getImp(cls, sel);
+ if (imp) return imp;
+
+ // Cache miss. Search method list.
+
+ meth = _class_getMethodNoSuper(cls, sel);
+
+ if (meth) {
+ // Hit in method list. Cache it.
+ _cache_fill(cls, meth, sel);
+ return method_getImplementation(meth);
+ } else {
+ // Miss in method list. Cache objc_msgForward.
+ _cache_addForwardEntry(cls, sel);
+ return _objc_msgForward_internal;
+ }
+}
+
+
+/***********************************************************************
+* _malloc_internal
+* _calloc_internal
+* _realloc_internal
+* _strdup_internal
+* _strdupcat_internal
+* _memdup_internal
+* _free_internal
+* Convenience functions for the internal malloc zone.
+**********************************************************************/
+void *_malloc_internal(size_t size)
+{
+ return malloc_zone_malloc(_objc_internal_zone(), size);
+}
+
+void *_calloc_internal(size_t count, size_t size)
+{
+ return malloc_zone_calloc(_objc_internal_zone(), count, size);
+}
+
+void *_realloc_internal(void *ptr, size_t size)
+{
+ return malloc_zone_realloc(_objc_internal_zone(), ptr, size);
+}
+
+char *_strdup_internal(const char *str)
+{
+ size_t len;
+ char *dup;
+ if (!str) return NULL;
+ len = strlen(str);
+ dup = (char *)malloc_zone_malloc(_objc_internal_zone(), len + 1);
+ memcpy(dup, str, len + 1);
+ return dup;
+}
+
+uint8_t *_ustrdup_internal(const uint8_t *str)
+{
+ return (uint8_t *)_strdup_internal((char *)str);
+}
+
+// allocate a new string that concatenates s1+s2.
+char *_strdupcat_internal(const char *s1, const char *s2)
+{
+ size_t len1 = strlen(s1);
+ size_t len2 = strlen(s2);
+ char *dup = (char *)
+ malloc_zone_malloc(_objc_internal_zone(), len1 + len2 + 1);
+ memcpy(dup, s1, len1);
+ memcpy(dup + len1, s2, len2 + 1);
+ return dup;
+}
+
+void *_memdup_internal(const void *mem, size_t len)
+{
+ void *dup = malloc_zone_malloc(_objc_internal_zone(), len);
+ memcpy(dup, mem, len);
+ return dup;
+}
+
+void _free_internal(void *ptr)
+{
+ malloc_zone_free(_objc_internal_zone(), ptr);
+}
+
+size_t _malloc_size_internal(void *ptr)
+{
+ malloc_zone_t *zone = _objc_internal_zone();
+ return zone->size(zone, ptr);
+}
+
+Class _calloc_class(size_t size)
+{
+#if SUPPORT_GC
+ if (UseGC) return (Class) malloc_zone_calloc(gc_zone, 1, size);
+#endif
+ return (Class) _calloc_internal(1, size);
+}
+
+
+const char *class_getName(Class cls)
+{
+ return _class_getName(cls);
+}
+
+Class class_getSuperclass(Class cls)
+{
+ return _class_getSuperclass(cls);
+}
+
+BOOL class_isMetaClass(Class cls)
+{
+ return _class_isMetaClass(cls);
+}
+
+
+size_t class_getInstanceSize(Class cls)
+{
+ return _class_getInstanceSize(cls);
+}
+
+
+/***********************************************************************
+* method_getNumberOfArguments.
+**********************************************************************/
+unsigned int method_getNumberOfArguments(Method m)
+{
+ if (!m) return 0;
+ return encoding_getNumberOfArguments(method_getTypeEncoding(m));
+}
+
+
+void method_getReturnType(Method m, char *dst, size_t dst_len)
+{
+ encoding_getReturnType(method_getTypeEncoding(m), dst, dst_len);
+}
+
+
+char * method_copyReturnType(Method m)
+{
+ return encoding_copyReturnType(method_getTypeEncoding(m));
+}
+
+
+void method_getArgumentType(Method m, unsigned int index,
+ char *dst, size_t dst_len)
+{
+ encoding_getArgumentType(method_getTypeEncoding(m),
+ index, dst, dst_len);
+}
+
+
+char * method_copyArgumentType(Method m, unsigned int index)
+{
+ return encoding_copyArgumentType(method_getTypeEncoding(m), index);
+}
+
+
+/***********************************************************************
+* objc_constructInstance
+* Creates an instance of `cls` at the location pointed to by `bytes`.
+* `bytes` must point to at least class_getInstanceSize(cls) bytes of
+* well-aligned zero-filled memory.
+* The new object's isa is set. Any C++ constructors are called.
+* Returns `bytes` if successful. Returns nil if `cls` or `bytes` is
+* NULL, or if C++ constructors fail.
+* Note: class_createInstance() and class_createInstances() preflight this.
+**********************************************************************/
+static id
+_objc_constructInstance(Class cls, void *bytes)
+{
+ id obj = (id)bytes;
+
+ // Set the isa pointer
+ obj->isa = cls; // need not be object_setClass
+
+ // Call C++ constructors, if any.
+ if (!object_cxxConstruct(obj)) {
+ // Some C++ constructor threw an exception.
+ return nil;
+ }
+
+ return obj;
+}
+
+
+id
+objc_constructInstance(Class cls, void *bytes)
+{
+ if (!cls || !bytes) return nil;
+ return _objc_constructInstance(cls, bytes);
+}
+
+
+id
+_objc_constructOrFree(Class cls, void *bytes)
+{
+ id obj = _objc_constructInstance(cls, bytes);
+ if (!obj) {
+#if SUPPORT_GC
+ if (UseGC) {
+ auto_zone_retain(gc_zone, bytes); // gc free expects rc==1
+ }
+#endif
+ free(bytes);
+ }
+
+ return obj;
+}
+
+
+/***********************************************************************
+* _class_createInstancesFromZone
+* Batch-allocating version of _class_createInstanceFromZone.
+* Attempts to allocate num_requested objects, each with extraBytes.
+* Returns the number of allocated objects (possibly zero), with
+* the allocated pointers in *results.
+**********************************************************************/
+unsigned
+_class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone,
+ id *results, unsigned num_requested)
+{
+ unsigned num_allocated;
+ if (!cls) return 0;
+
+ size_t size = _class_getInstanceSize(cls) + extraBytes;
+ // CF requires all objects be at least 16 bytes.
+ if (size < 16) size = 16;
+
+#if SUPPORT_GC
+ if (UseGC) {
+ num_allocated =
+ auto_zone_batch_allocate(gc_zone, size, AUTO_OBJECT_SCANNED, 0, 1,
+ (void**)results, num_requested);
+ } else
+#endif
+ {
+ unsigned i;
+ num_allocated =
+ 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);
+ }
+ }
+
+ // Construct each object, and delete any that fail construction.
+
+ unsigned shift = 0;
+ unsigned i;
+ BOOL ctor = _class_hasCxxStructors(cls);
+ for (i = 0; i < num_allocated; i++) {
+ id obj = results[i];
+ if (ctor) obj = _objc_constructOrFree(cls, obj);
+ else if (obj) obj->isa = cls; // need not be object_setClass
+
+ if (obj) {
+ results[i-shift] = obj;
+ } else {
+ shift++;
+ }
+ }
+
+ return num_allocated - shift;
+}
+
+
+/***********************************************************************
+* inform_duplicate. Complain about duplicate class implementations.
+**********************************************************************/
+void
+inform_duplicate(const char *name, Class oldCls, Class cls)
+{
+#if TARGET_OS_WIN32
+ _objc_inform ("Class %s is implemented in two different images.", name);
+#else
+ const header_info *oldHeader = _headerForClass(oldCls);
+ const header_info *newHeader = _headerForClass(cls);
+ 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. "
+ "Which one is undefined.",
+ name, oldName, newName);
+#endif
+}
+
+#if SUPPORT_TAGGED_POINTERS
+/***********************************************************************
+ * _objc_insert_tagged_isa
+ * Insert an isa into a particular slot in the tagged isa table.
+ * Will error & abort if slot already has an isa that is different.
+ **********************************************************************/
+void _objc_insert_tagged_isa(unsigned char slotNumber, Class isa) {
+ unsigned char actualSlotNumber = (slotNumber << 1) + 1;
+ Class previousIsa = _objc_tagged_isa_table[actualSlotNumber];
+
+ if (actualSlotNumber & 0xF0) {
+ _objc_fatal("%s -- Slot number %uc is too large. Aborting.", __FUNCTION__, slotNumber);
+ }
+
+ if (actualSlotNumber == 0) {
+ _objc_fatal("%s -- Slot number 0 doesn't make sense. Aborting.", __FUNCTION__);
+ }
+
+ if (isa && previousIsa && (previousIsa != isa)) {
+ _objc_fatal("%s -- Tagged pointer table already had an item in that slot (%s). "
+ "Not putting (%s) in table. Aborting instead",
+ __FUNCTION__, class_getName(previousIsa), class_getName(isa));
+ }
+ _objc_tagged_isa_table[actualSlotNumber] = isa;
+}
+#endif
+
+
+const char *
+copyPropertyAttributeString(const objc_property_attribute_t *attrs,
+ unsigned int count)
+{
+ char *result;
+ unsigned int i;
+ if (count == 0) return strdup("");
+
+#ifndef NDEBUG
+ // debug build: sanitize input
+ for (i = 0; i < count; i++) {
+ assert(attrs[i].name);
+ assert(strlen(attrs[i].name) > 0);
+ assert(! strchr(attrs[i].name, ','));
+ assert(! strchr(attrs[i].name, '"'));
+ if (attrs[i].value) assert(! strchr(attrs[i].value, ','));
+ }
+#endif
+
+ size_t len = 0;
+ for (i = 0; i < count; i++) {
+ if (attrs[i].value) {
+ size_t namelen = strlen(attrs[i].name);
+ if (namelen > 1) namelen += 2; // long names get quoted
+ len += namelen + strlen(attrs[i].value) + 1;
+ }
+ }
+
+ result = (char *)malloc(len + 1);
+ char *s = result;
+ for (i = 0; i < count; i++) {
+ if (attrs[i].value) {
+ size_t namelen = strlen(attrs[i].name);
+ if (namelen > 1) {
+ s += sprintf(s, "\"%s\"%s,", attrs[i].name, attrs[i].value);
+ } else {
+ s += sprintf(s, "%s%s,", attrs[i].name, attrs[i].value);
+ }
+ }
+ }
+
+ // remove trailing ',' if any
+ if (s > result) s[-1] = '\0';
+
+ return result;
+}
+
+/*
+ Property attribute string format:
+
+ - Comma-separated name-value pairs.
+ - Name and value may not contain ,
+ - Name may not contain "
+ - Value may be empty
+ - Name is single char, value follows
+ - OR Name is double-quoted string of 2+ chars, value follows
+
+ 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,
+ BOOL (*fn)(unsigned int index,
+ void *ctx1, void *ctx2,
+ const char *name, size_t nlen,
+ const char *value, size_t vlen),
+ void *ctx1, void *ctx2)
+{
+ if (!attrs) return 0;
+
+#ifndef NDEBUG
+ const char *attrsend = attrs + strlen(attrs);
+#endif
+ unsigned int attrcount = 0;
+
+ while (*attrs) {
+ // Find the next comma-separated attribute
+ const char *start = attrs;
+ const char *end = start + strcspn(attrs, ",");
+
+ // Move attrs past this attribute and the comma (if any)
+ attrs = *end ? end+1 : end;
+
+ assert(attrs <= attrsend);
+ assert(start <= attrsend);
+ assert(end <= attrsend);
+
+ // Skip empty attribute
+ if (start == end) continue;
+
+ // Process one non-empty comma-free attribute [start,end)
+ const char *nameStart;
+ const char *nameEnd;
+
+ assert(start < end);
+ assert(*start);
+ if (*start != '\"') {
+ // single-char short name
+ nameStart = start;
+ nameEnd = start+1;
+ start++;
+ }
+ else {
+ // double-quoted long name
+ nameStart = start+1;
+ nameEnd = nameStart + strcspn(nameStart, "\",");
+ start++; // leading quote
+ start += nameEnd - nameStart; // name
+ if (*start == '\"') start++; // trailing quote, if any
+ }
+
+ // Process one possibly-empty comma-free attribute value [start,end)
+ const char *valueStart;
+ const char *valueEnd;
+
+ assert(start <= end);
+
+ valueStart = start;
+ valueEnd = end;
+
+ BOOL more = (*fn)(attrcount, ctx1, ctx2,
+ nameStart, nameEnd-nameStart,
+ valueStart, valueEnd-valueStart);
+ attrcount++;
+ if (!more) break;
+ }
+
+ return attrcount;
+}
+
+
+static BOOL
+copyOneAttribute(unsigned int index, void *ctxa, void *ctxs,
+ const char *name, size_t nlen, const char *value, size_t vlen)
+{
+ objc_property_attribute_t **ap = (objc_property_attribute_t**)ctxa;
+ char **sp = (char **)ctxs;
+
+ objc_property_attribute_t *a = *ap;
+ char *s = *sp;
+
+ a->name = s;
+ memcpy(s, name, nlen);
+ s += nlen;
+ *s++ = '\0';
+
+ a->value = s;
+ memcpy(s, value, vlen);
+ s += vlen;
+ *s++ = '\0';
+
+ a++;
+
+ *ap = a;
+ *sp = s;
+
+ return YES;
+}
+
+
+objc_property_attribute_t *
+copyPropertyAttributeList(const char *attrs, unsigned int *outCount)
+{
+ if (!attrs) {
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ // Result size:
+ // number of commas plus 1 for the attributes (upper bound)
+ // plus another attribute for the attribute array terminator
+ // plus strlen(attrs) for name/value string data (upper bound)
+ // plus count*2 for the name/value string terminators (upper bound)
+ unsigned int attrcount = 1;
+ const char *s;
+ for (s = attrs; s && *s; s++) {
+ if (*s == ',') attrcount++;
+ }
+
+ size_t size =
+ attrcount * sizeof(objc_property_attribute_t) +
+ sizeof(objc_property_attribute_t) +
+ strlen(attrs) +
+ attrcount * 2;
+ objc_property_attribute_t *result = (objc_property_attribute_t *)
+ calloc(size, 1);
+
+ objc_property_attribute_t *ra = result;
+ char *rs = (char *)(ra+attrcount+1);
+
+ attrcount = iteratePropertyAttributes(attrs, copyOneAttribute, &ra, &rs);
+
+ assert((uint8_t *)(ra+1) <= (uint8_t *)result+size);
+ assert((uint8_t *)rs <= (uint8_t *)result+size);
+
+ if (attrcount == 0) {
+ free(result);
+ result = NULL;
+ }
+
+ if (outCount) *outCount = attrcount;
+ return result;
+}
+
+
+static BOOL
+findOneAttribute(unsigned int index, void *ctxa, void *ctxs,
+ const char *name, size_t nlen, const char *value, size_t vlen)
+{
+ const char *query = (char *)ctxa;
+ char **resultp = (char **)ctxs;
+
+ if (strlen(query) == nlen && 0 == strncmp(name, query, nlen)) {
+ char *result = (char *)calloc(vlen+1, 1);
+ memcpy(result, value, vlen);
+ result[vlen] = '\0';
+ *resultp = result;
+ return NO;
+ }
+
+ return YES;
+}
+
+char *copyPropertyAttributeValue(const char *attrs, const char *name)
+{
+ char *result = NULL;
+
+ iteratePropertyAttributes(attrs, findOneAttribute, (void*)name, &result);
+
+ return result;
+}
* @APPLE_LICENSE_HEADER_END@
*/
+#ifndef _OBJC_CONFIG_H_
+#define _OBJC_CONFIG_H_
+
#include <TargetConditionals.h>
// Define SUPPORT_GC=1 to enable garbage collection.
# 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
// 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 <architecture/i386/asm_help.h>
#endif
+++ /dev/null
-/*
- * Copyright (c) 1999-2003, 2005-2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-/*
- * objc-errors.m
- * Copyright 1988-2001, NeXT Software, Inc., Apple Computer, Inc.
- */
-
-#include "objc-private.h"
-
-#if TARGET_OS_WIN32
-
-#include <conio.h>
-
-PRIVATE_EXTERN void _objc_inform_on_crash(const char *fmt, ...)
-{
-}
-
-PRIVATE_EXTERN void _objc_inform(const char *fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
- _vcprintf(fmt, args);
- va_end(args);
- _cprintf("\n");
-}
-
-PRIVATE_EXTERN void _objc_fatal(const char *fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
- _vcprintf(fmt, args);
- va_end(args);
- _cprintf("\n");
-
- abort();
-}
-
-PRIVATE_EXTERN void __objc_error(id rcv, const char *fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
- _vcprintf(fmt, args);
- va_end(args);
-
- abort();
-}
-
-PRIVATE_EXTERN void _objc_error(id rcv, const char *fmt, va_list args)
-{
- _vcprintf(fmt, args);
-
- abort();
-}
-
-#else
-
-
-OBJC_EXPORT void (*_error)(id, const char *, va_list);
-
-static void _objc_trap(void) __attribute__((noreturn));
-
-// Add "message" to any forthcoming crash log.
-static void _objc_crashlog(const char *message)
-{
- char *newmsg;
-
-#if 0
- {
- // for debugging at BOOT time.
- extern char **_NSGetProgname(void);
- FILE *crashlog = fopen("/_objc_crash.log", "a");
- setbuf(crashlog, NULL);
- fprintf(crashlog, "[%s] %s\n", *_NSGetProgname(), message);
- fclose(crashlog);
- sync();
- }
-#endif
-
- static mutex_t crashlog_lock = MUTEX_INITIALIZER;
- mutex_lock(&crashlog_lock);
-
- char *oldmsg = (char *)CRGetCrashLogMessage();
-
- if (!oldmsg) {
- newmsg = strdup(message);
- } else {
- asprintf(&newmsg, "%s\n%s", oldmsg, message);
- }
-
- if (newmsg) {
- // Strip trailing newline
- char *c = &newmsg[strlen(newmsg)-1];
- if (*c == '\n') *c = '\0';
-
- if (oldmsg) free(oldmsg);
- CRSetCrashLogMessage(newmsg);
- }
-
- mutex_unlock(&crashlog_lock);
-}
-
-// 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
- write(STDERR_FILENO, message, strlen(message));
- if (message[strlen(message)-1] != '\n') {
- write(STDERR_FILENO, "\n", 1);
- }
- } else {
- syslog(LOG_ERR, "%s", message);
- }
-}
-
-/*
- * _objc_error is the default *_error handler.
- */
-#if __OBJC2__
-PRIVATE_EXTERN __attribute__((noreturn))
-#else
-// used by ExceptionHandling.framework
-#endif
-void _objc_error(id self, const char *fmt, va_list ap)
-{
- char *buf1;
- char *buf2;
-
- vasprintf(&buf1, fmt, ap);
- asprintf(&buf2, "objc[%d]: %s: %s\n",
- getpid(), object_getClassName(self), buf1);
- _objc_syslog(buf2);
- _objc_crashlog(buf2);
-
- _objc_trap();
-}
-
-/*
- * this routine handles errors that involve an object (or class).
- */
-PRIVATE_EXTERN void __objc_error(id rcv, const char *fmt, ...)
-{
- va_list vp;
-
- va_start(vp,fmt);
-#if !__OBJC2__
- (*_error)(rcv, fmt, vp);
-#endif
- _objc_error (rcv, fmt, vp); /* In case (*_error)() returns. */
- va_end(vp);
-}
-
-/*
- * this routine handles severe runtime errors...like not being able
- * to read the mach headers, allocate space, etc...very uncommon.
- */
-PRIVATE_EXTERN void _objc_fatal(const char *fmt, ...)
-{
- va_list ap;
- char *buf1;
- char *buf2;
-
- va_start(ap,fmt);
- vasprintf(&buf1, fmt, ap);
- va_end (ap);
-
- asprintf(&buf2, "objc[%d]: %s\n", getpid(), buf1);
- _objc_syslog(buf2);
- _objc_crashlog(buf2);
-
- _objc_trap();
-}
-
-/*
- * 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, ...)
-{
- va_list ap;
- char *buf1;
- char *buf2;
-
- va_start (ap,fmt);
- vasprintf(&buf1, fmt, ap);
- va_end (ap);
-
- asprintf(&buf2, "objc[%d]: %s\n", getpid(), buf1);
- _objc_syslog(buf2);
-
- free(buf2);
- free(buf1);
-}
-
-
-/*
- * Like _objc_inform(), but prints the message only in any
- * forthcoming crash log, not to the console.
- */
-PRIVATE_EXTERN void _objc_inform_on_crash(const char *fmt, ...)
-{
- va_list ap;
- char *buf1;
- char *buf2;
-
- va_start (ap,fmt);
- vasprintf(&buf1, fmt, ap);
- va_end (ap);
-
- asprintf(&buf2, "objc[%d]: %s\n", getpid(), buf1);
- _objc_crashlog(buf2);
-
- free(buf2);
- free(buf1);
-}
-
-
-/*
- * Like calling both _objc_inform and _objc_inform_on_crash.
- */
-PRIVATE_EXTERN void _objc_inform_now_and_on_crash(const char *fmt, ...)
-{
- va_list ap;
- char *buf1;
- char *buf2;
-
- va_start (ap,fmt);
- vasprintf(&buf1, fmt, ap);
- va_end (ap);
-
- asprintf(&buf2, "objc[%d]: %s\n", getpid(), buf1);
- _objc_crashlog(buf2);
- _objc_syslog(buf2);
-
- free(buf2);
- free(buf1);
-}
-
-
-/* Kill the process in a way that generates a crash log.
- * This is better than calling exit(). */
-static void _objc_trap(void)
-{
- __builtin_trap();
-}
-
-/* Try to keep _objc_warn_deprecated out of crash logs
- * caused by _objc_trap(). rdar://4546883 */
-__attribute__((used))
-static void _objc_trap2(void)
-{
- __builtin_trap();
-}
-
-#endif
-
-
-BREAKPOINT_FUNCTION(
- void _objc_warn_deprecated(void)
-);
-
-PRIVATE_EXTERN void _objc_inform_deprecated(const char *oldf, const char *newf)
-{
- if (PrintDeprecation) {
- if (newf) {
- _objc_inform("The function %s is obsolete. Use %s instead. Set a breakpoint on _objc_warn_deprecated to find the culprit.", oldf, newf);
- } else {
- _objc_inform("The function %s is obsolete. Do not use it. Set a breakpoint on _objc_warn_deprecated to find the culprit.", oldf);
- }
- }
- _objc_warn_deprecated();
-}
--- /dev/null
+/*
+ * Copyright (c) 1999-2003, 2005-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/*
+ * objc-errors.m
+ * Copyright 1988-2001, NeXT Software, Inc., Apple Computer, Inc.
+ */
+
+#include "objc-private.h"
+
+#if TARGET_OS_WIN32
+
+#include <conio.h>
+
+void _objc_inform_on_crash(const char *fmt, ...)
+{
+}
+
+void _objc_inform(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ _vcprintf(fmt, args);
+ va_end(args);
+ _cprintf("\n");
+}
+
+void _objc_fatal(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ _vcprintf(fmt, args);
+ va_end(args);
+ _cprintf("\n");
+
+ abort();
+}
+
+void __objc_error(id rcv, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ _vcprintf(fmt, args);
+ va_end(args);
+
+ abort();
+}
+
+void _objc_error(id rcv, const char *fmt, va_list args)
+{
+ _vcprintf(fmt, args);
+
+ abort();
+}
+
+#else
+
+#include <vproc_priv.h>
+
+OBJC_EXPORT void (*_error)(id, const char *, va_list);
+
+static void _objc_trap(void) __attribute__((noreturn));
+
+// Add "message" to any forthcoming crash log.
+static void _objc_crashlog(const char *message)
+{
+ char *newmsg;
+
+#if 0
+ {
+ // for debugging at BOOT time.
+ extern char **_NSGetProgname(void);
+ FILE *crashlog = fopen("/_objc_crash.log", "a");
+ setbuf(crashlog, NULL);
+ fprintf(crashlog, "[%s] %s\n", *_NSGetProgname(), message);
+ fclose(crashlog);
+ sync();
+ }
+#endif
+
+ static mutex_t crashlog_lock = MUTEX_INITIALIZER;
+ mutex_lock(&crashlog_lock);
+
+ char *oldmsg = (char *)CRGetCrashLogMessage();
+
+ if (!oldmsg) {
+ newmsg = strdup(message);
+ } else {
+ asprintf(&newmsg, "%s\n%s", oldmsg, message);
+ }
+
+ if (newmsg) {
+ // Strip trailing newline
+ char *c = &newmsg[strlen(newmsg)-1];
+ if (*c == '\n') *c = '\0';
+
+ if (oldmsg) free(oldmsg);
+ CRSetCrashLogMessage(newmsg);
+ }
+
+ 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)
+{
+ syslog(LOG_ERR, "%s", message);
+
+ if (also_do_stderr()) {
+ write(STDERR_FILENO, message, strlen(message));
+ }
+}
+
+/*
+ * _objc_error is the default *_error handler.
+ */
+#if __OBJC2__
+__attribute__((noreturn))
+#else
+// used by ExceptionHandling.framework
+#endif
+void _objc_error(id self, const char *fmt, va_list ap)
+{
+ char *buf1;
+ char *buf2;
+
+ vasprintf(&buf1, fmt, ap);
+ asprintf(&buf2, "objc[%d]: %s: %s\n",
+ getpid(), object_getClassName(self), buf1);
+ _objc_syslog(buf2);
+ _objc_crashlog(buf2);
+
+ _objc_trap();
+}
+
+/*
+ * this routine handles errors that involve an object (or class).
+ */
+void __objc_error(id rcv, const char *fmt, ...)
+{
+ va_list vp;
+
+ va_start(vp,fmt);
+#if !__OBJC2__
+ (*_error)(rcv, fmt, vp);
+#endif
+ _objc_error (rcv, fmt, vp); /* In case (*_error)() returns. */
+ va_end(vp);
+}
+
+/*
+ * this routine handles severe runtime errors...like not being able
+ * to read the mach headers, allocate space, etc...very uncommon.
+ */
+void _objc_fatal(const char *fmt, ...)
+{
+ va_list ap;
+ char *buf1;
+ char *buf2;
+
+ va_start(ap,fmt);
+ vasprintf(&buf1, fmt, ap);
+ va_end (ap);
+
+ asprintf(&buf2, "objc[%d]: %s\n", getpid(), buf1);
+ _objc_syslog(buf2);
+ _objc_crashlog(buf2);
+
+ _objc_trap();
+}
+
+/*
+ * this routine handles soft runtime errors...like not being able
+ * add a category to a class (because it wasn't linked in).
+ */
+void _objc_inform(const char *fmt, ...)
+{
+ va_list ap;
+ char *buf1;
+ char *buf2;
+
+ va_start (ap,fmt);
+ vasprintf(&buf1, fmt, ap);
+ va_end (ap);
+
+ asprintf(&buf2, "objc[%d]: %s\n", getpid(), buf1);
+ _objc_syslog(buf2);
+
+ free(buf2);
+ free(buf1);
+}
+
+
+/*
+ * Like _objc_inform(), but prints the message only in any
+ * forthcoming crash log, not to the console.
+ */
+void _objc_inform_on_crash(const char *fmt, ...)
+{
+ va_list ap;
+ char *buf1;
+ char *buf2;
+
+ va_start (ap,fmt);
+ vasprintf(&buf1, fmt, ap);
+ va_end (ap);
+
+ asprintf(&buf2, "objc[%d]: %s\n", getpid(), buf1);
+ _objc_crashlog(buf2);
+
+ free(buf2);
+ free(buf1);
+}
+
+
+/*
+ * Like calling both _objc_inform and _objc_inform_on_crash.
+ */
+void _objc_inform_now_and_on_crash(const char *fmt, ...)
+{
+ va_list ap;
+ char *buf1;
+ char *buf2;
+
+ va_start (ap,fmt);
+ vasprintf(&buf1, fmt, ap);
+ va_end (ap);
+
+ asprintf(&buf2, "objc[%d]: %s\n", getpid(), buf1);
+ _objc_crashlog(buf2);
+ _objc_syslog(buf2);
+
+ free(buf2);
+ free(buf1);
+}
+
+
+/* Kill the process in a way that generates a crash log.
+ * This is better than calling exit(). */
+static void _objc_trap(void)
+{
+ __builtin_trap();
+}
+
+/* Try to keep _objc_warn_deprecated out of crash logs
+ * caused by _objc_trap(). rdar://4546883 */
+__attribute__((used))
+static void _objc_trap2(void)
+{
+ __builtin_trap();
+}
+
+#endif
+
+
+BREAKPOINT_FUNCTION(
+ void _objc_warn_deprecated(void)
+);
+
+void _objc_inform_deprecated(const char *oldf, const char *newf)
+{
+ if (PrintDeprecation) {
+ if (newf) {
+ _objc_inform("The function %s is obsolete. Use %s instead. Set a breakpoint on _objc_warn_deprecated to find the culprit.", oldf, newf);
+ } else {
+ _objc_inform("The function %s is obsolete. Do not use it. Set a breakpoint on _objc_warn_deprecated to find the culprit.", oldf);
+ }
+ }
+ _objc_warn_deprecated();
+}
__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);
+++ /dev/null
-/*
- * Copyright (c) 2002-2007 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#if !__OBJC2__
-
-/***********************************************************************
-* 32-bit implementation
-**********************************************************************/
-
-#include "objc-private.h"
-#include <stdlib.h>
-#include <setjmp.h>
-#include <execinfo.h>
-
-#include "objc-exception.h"
-
-static objc_exception_functions_t xtab;
-
-// forward declaration
-static void set_default_handlers();
-
-
-/*
- * Exported functions
- */
-
-// get table; version tells how many
-void objc_exception_get_functions(objc_exception_functions_t *table) {
- // only version 0 supported at this point
- if (table && table->version == 0)
- *table = xtab;
-}
-
-// set table
-void objc_exception_set_functions(objc_exception_functions_t *table) {
- // only version 0 supported at this point
- if (table && table->version == 0)
- xtab = *table;
-}
-
-/*
- * The following functions are
- * synthesized by the compiler upon encountering language constructs
- */
-
-void objc_exception_throw(id exception) {
- if (!xtab.throw_exc) {
- set_default_handlers();
- }
-
- if (PrintExceptionThrow) {
- _objc_inform("EXCEPTIONS: throwing %p (%s)",
- exception, object_getClassName(exception));
- void* callstack[500];
- int frameCount = backtrace(callstack, 500);
- backtrace_symbols_fd(callstack, frameCount, fileno(stderr));
- }
-
- OBJC_RUNTIME_OBJC_EXCEPTION_THROW(exception); // dtrace probe to log throw activity.
- xtab.throw_exc(exception);
- _objc_fatal("objc_exception_throw failed");
-}
-
-void objc_exception_try_enter(void *localExceptionData) {
- if (!xtab.throw_exc) {
- set_default_handlers();
- }
- xtab.try_enter(localExceptionData);
-}
-
-
-void objc_exception_try_exit(void *localExceptionData) {
- if (!xtab.throw_exc) {
- set_default_handlers();
- }
- xtab.try_exit(localExceptionData);
-}
-
-
-id objc_exception_extract(void *localExceptionData) {
- if (!xtab.throw_exc) {
- set_default_handlers();
- }
- return xtab.extract(localExceptionData);
-}
-
-
-int objc_exception_match(Class exceptionClass, id exception) {
- if (!xtab.throw_exc) {
- set_default_handlers();
- }
- return xtab.match(exceptionClass, exception);
-}
-
-
-// quick and dirty exception handling code
-// default implementation - mostly a toy for use outside/before Foundation
-// provides its implementation
-// Perhaps the default implementation should just complain loudly and quit
-
-
-extern void _objc_inform(const char *fmt, ...);
-
-typedef struct { jmp_buf buf; void *pointers[4]; } LocalData_t;
-
-typedef struct _threadChain {
- LocalData_t *topHandler;
- objc_thread_t perThreadID;
- struct _threadChain *next;
-}
- ThreadChainLink_t;
-
-static ThreadChainLink_t ThreadChainLink;
-
-static ThreadChainLink_t *getChainLink() {
- // follow links until thread_self() found (someday) XXX
- objc_thread_t self = thread_self();
- ThreadChainLink_t *walker = &ThreadChainLink;
- while (walker->perThreadID != self) {
- if (walker->next != NULL) {
- walker = walker->next;
- continue;
- }
- // create a new one
- // XXX not thread safe (!)
- // XXX Also, we don't register to deallocate on thread death
- walker->next = (ThreadChainLink_t *)malloc(sizeof(ThreadChainLink_t));
- walker = walker->next;
- walker->next = NULL;
- walker->topHandler = NULL;
- walker->perThreadID = self;
- }
- return walker;
-}
-
-static void default_try_enter(void *localExceptionData) {
- LocalData_t *data = (LocalData_t *)localExceptionData;
- ThreadChainLink_t *chainLink = getChainLink();
- data->pointers[1] = chainLink->topHandler;
- chainLink->topHandler = data;
- if (PrintExceptions) _objc_inform("EXCEPTIONS: entered try block %p\n", chainLink->topHandler);
-}
-
-static void default_throw(id value) {
- ThreadChainLink_t *chainLink = getChainLink();
- LocalData_t *led;
- if (value == nil) {
- if (PrintExceptions) _objc_inform("EXCEPTIONS: objc_exception_throw with nil value\n");
- return;
- }
- if (chainLink == NULL) {
- if (PrintExceptions) _objc_inform("EXCEPTIONS: No handler in place!\n");
- return;
- }
- if (PrintExceptions) _objc_inform("EXCEPTIONS: exception thrown, going to handler block %p\n", chainLink->topHandler);
- led = chainLink->topHandler;
- chainLink->topHandler = (LocalData_t *)
- led->pointers[1]; // pop top handler
- led->pointers[0] = value; // store exception that is thrown
-#if TARGET_OS_WIN32
- longjmp(led->buf, 1);
-#else
- _longjmp(led->buf, 1);
-#endif
-}
-
-static void default_try_exit(void *led) {
- ThreadChainLink_t *chainLink = getChainLink();
- if (!chainLink || led != chainLink->topHandler) {
- if (PrintExceptions) _objc_inform("EXCEPTIONS: *** mismatched try block exit handlers\n");
- return;
- }
- if (PrintExceptions) _objc_inform("EXCEPTIONS: removing try block handler %p\n", chainLink->topHandler);
- chainLink->topHandler = (LocalData_t *)
- chainLink->topHandler->pointers[1]; // pop top handler
-}
-
-static id default_extract(void *localExceptionData) {
- LocalData_t *led = (LocalData_t *)localExceptionData;
- return (id)led->pointers[0];
-}
-
-static int default_match(Class exceptionClass, id exception) {
- //return [exception isKindOfClass:exceptionClass];
- Class cls;
- for (cls = _object_getClass(exception); nil != cls; cls = _class_getSuperclass(cls))
- if (cls == exceptionClass) return 1;
- return 0;
-}
-
-static void set_default_handlers() {
- objc_exception_functions_t default_functions = {
- 0, default_throw, default_try_enter, default_try_exit, default_extract, default_match };
-
- // should this always print?
- if (PrintExceptions) _objc_inform("EXCEPTIONS: *** Setting default (non-Foundation) exception mechanism\n");
- objc_exception_set_functions(&default_functions);
-}
-
-
-PRIVATE_EXTERN void exception_init(void)
-{
- // nothing to do
-}
-
-PRIVATE_EXTERN void _destroyAltHandlerList(struct alt_handler_list *list)
-{
- // nothing to do
-}
-
-
-// !__OBJC2__
-#else
-// __OBJC2__
-
-/***********************************************************************
-* 64-bit implementation.
-**********************************************************************/
-
-#include "objc-private.h"
-#include <objc/objc-exception.h>
-#include <execinfo.h>
-
-// unwind library types and functions
-// Mostly adapted from Itanium C++ ABI: Exception Handling
-// http://www.codesourcery.com/cxx-abi/abi-eh.html
-
-struct _Unwind_Exception;
-struct _Unwind_Context;
-
-typedef int _Unwind_Action;
-static const _Unwind_Action _UA_SEARCH_PHASE = 1;
-static const _Unwind_Action _UA_CLEANUP_PHASE = 2;
-static const _Unwind_Action _UA_HANDLER_FRAME = 4;
-static const _Unwind_Action _UA_FORCE_UNWIND = 8;
-
-typedef int _Unwind_Reason_Code;
-static const _Unwind_Reason_Code _URC_NO_REASON = 0;
-static const _Unwind_Reason_Code _URC_FOREIGN_EXCEPTION_CAUGHT = 1;
-static const _Unwind_Reason_Code _URC_FATAL_PHASE2_ERROR = 2;
-static const _Unwind_Reason_Code _URC_FATAL_PHASE1_ERROR = 3;
-static const _Unwind_Reason_Code _URC_NORMAL_STOP = 4;
-static const _Unwind_Reason_Code _URC_END_OF_STACK = 5;
-static const _Unwind_Reason_Code _URC_HANDLER_FOUND = 6;
-static const _Unwind_Reason_Code _URC_INSTALL_CONTEXT = 7;
-static const _Unwind_Reason_Code _URC_CONTINUE_UNWIND = 8;
-
-struct dwarf_eh_bases
-{
- uintptr_t tbase;
- uintptr_t dbase;
- 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 *);
-
-
-// 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);
-
-#if SUPPORT_ZEROCOST_EXCEPTIONS
-# define CXX_PERSONALITY __gxx_personality_v0
-#else
-# define CXX_PERSONALITY __gxx_personality_sj0
-#endif
-
-extern _Unwind_Reason_Code
-CXX_PERSONALITY(int version,
- _Unwind_Action actions,
- uint64_t exceptionClass,
- struct _Unwind_Exception *exceptionObject,
- struct _Unwind_Context *context);
-
-
-// objc's internal exception types and data
-
-struct objc_typeinfo {
- // Position of vtable and name fields must match C++ typeinfo object
- const void **vtable; // always objc_ehtype_vtable+2
- const char *name; // c++ typeinfo string
-
- Class cls;
-};
-
-struct objc_exception {
- id obj;
- struct objc_typeinfo tinfo;
-};
-
-
-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,
- struct objc_typeinfo *throw_tinfo,
- void **throw_obj_p,
- unsigned outer);
-
-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
-};
-
-struct objc_typeinfo OBJC_EHTYPE_id = {
- objc_ehtype_vtable+2,
- "id",
- NULL
-};
-
-
-
-/***********************************************************************
-* Foundation customization
-**********************************************************************/
-
-/***********************************************************************
-* _objc_default_exception_preprocessor
-* Default exception preprocessor. Expected to be overridden by Foundation.
-**********************************************************************/
-static id _objc_default_exception_preprocessor(id exception)
-{
- return exception;
-}
-static objc_exception_preprocessor exception_preprocessor = _objc_default_exception_preprocessor;
-
-
-/***********************************************************************
-* _objc_default_exception_matcher
-* Default exception matcher. Expected to be overridden by Foundation.
-**********************************************************************/
-static int _objc_default_exception_matcher(Class catch_cls, id exception)
-{
- Class cls;
- for (cls = _object_getClass(exception);
- cls != NULL;
- cls = class_getSuperclass(cls))
- {
- if (cls == catch_cls) return 1;
- }
-
- return 0;
-}
-static objc_exception_matcher exception_matcher = _objc_default_exception_matcher;
-
-
-/***********************************************************************
-* _objc_default_uncaught_exception_handler
-* Default uncaught exception handler. Expected to be overridden by Foundation.
-**********************************************************************/
-static void _objc_default_uncaught_exception_handler(id exception)
-{
-}
-static objc_uncaught_exception_handler uncaught_handler = _objc_default_uncaught_exception_handler;
-
-
-/***********************************************************************
-* objc_setExceptionPreprocessor
-* Set a handler for preprocessing Objective-C exceptions.
-* Returns the previous handler.
-**********************************************************************/
-objc_exception_preprocessor
-objc_setExceptionPreprocessor(objc_exception_preprocessor fn)
-{
- objc_exception_preprocessor result = exception_preprocessor;
- exception_preprocessor = fn;
- return result;
-}
-
-
-/***********************************************************************
-* objc_setExceptionMatcher
-* Set a handler for matching Objective-C exceptions.
-* Returns the previous handler.
-**********************************************************************/
-objc_exception_matcher
-objc_setExceptionMatcher(objc_exception_matcher fn)
-{
- objc_exception_matcher result = exception_matcher;
- exception_matcher = fn;
- return result;
-}
-
-
-/***********************************************************************
-* objc_setUncaughtExceptionHandler
-* Set a handler for uncaught Objective-C exceptions.
-* Returns the previous handler.
-**********************************************************************/
-objc_uncaught_exception_handler
-objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
-{
- objc_uncaught_exception_handler result = uncaught_handler;
- uncaught_handler = fn;
- return result;
-}
-
-
-/***********************************************************************
-* Exception personality
-**********************************************************************/
-
-static void call_alt_handlers(struct _Unwind_Context *ctx);
-
-_Unwind_Reason_Code
-__objc_personality_v0(int version,
- _Unwind_Action actions,
- uint64_t exceptionClass,
- struct _Unwind_Exception *exceptionObject,
- struct _Unwind_Context *context)
-{
- BOOL unwinding = ((actions & _UA_CLEANUP_PHASE) ||
- (actions & _UA_FORCE_UNWIND));
-
- if (PrintExceptions) {
- _objc_inform("EXCEPTIONS: %s through frame [ip=%p sp=%p] "
- "for exception %p",
- unwinding ? "unwinding" : "searching",
- (void*)(_Unwind_GetIP(context)-1),
- (void*)_Unwind_GetCFA(context), exceptionObject);
- }
-
- // If we're executing the unwind, call this frame's alt handlers, if any.
- if (unwinding) {
- call_alt_handlers(context);
- }
-
- // Let C++ handle the unwind itself.
- return CXX_PERSONALITY(version, actions, exceptionClass,
- exceptionObject, context);
-}
-
-
-/***********************************************************************
-* Compiler ABI
-**********************************************************************/
-
-static void _objc_exception_destructor(void *exc_gen) {
- 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);
- }
-}
-
-
-void objc_exception_throw(id obj)
-{
- struct objc_exception *exc =
- __cxa_allocate_exception(sizeof(struct objc_exception));
-
- exc->obj = (*exception_preprocessor)(obj);
- 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);
- }
-
- exc->tinfo.vtable = objc_ehtype_vtable+2;
- exc->tinfo.name = object_getClassName(obj);
- exc->tinfo.cls = obj ? _object_getClass(obj) : Nil;
-
- if (PrintExceptions) {
- _objc_inform("EXCEPTIONS: throwing %p (object %p, a %s)",
- exc, obj, object_getClassName(obj));
- }
-
- if (PrintExceptionThrow) {
- if (!PrintExceptions)
- _objc_inform("EXCEPTIONS: throwing %p (object %p, a %s)",
- exc, obj, object_getClassName(obj));
- void* callstack[500];
- int frameCount = backtrace(callstack, 500);
- backtrace_symbols_fd(callstack, frameCount, fileno(stderr));
- }
-
- OBJC_RUNTIME_OBJC_EXCEPTION_THROW(obj); // dtrace probe to log throw activity
- __cxa_throw(exc, &exc->tinfo, &_objc_exception_destructor);
- __builtin_trap();
-}
-
-
-void objc_exception_rethrow(void)
-{
- // exception_preprocessor doesn't get another bite of the apple
- if (PrintExceptions) {
- _objc_inform("EXCEPTIONS: rethrowing current exception");
- }
-
- OBJC_RUNTIME_OBJC_EXCEPTION_RETHROW(); // dtrace probe to log throw activity.
- __cxa_rethrow();
- __builtin_trap();
-}
-
-
-id objc_begin_catch(void *exc_gen)
-{
- if (PrintExceptions) {
- _objc_inform("EXCEPTIONS: handling exception %p at %p",
- exc_gen, __builtin_return_address(0));
- }
- // NOT actually an id in the catch(...) case!
- return (id)__cxa_begin_catch(exc_gen);
-}
-
-
-void objc_end_catch(void)
-{
- if (PrintExceptions) {
- _objc_inform("EXCEPTIONS: finishing handler");
- }
- __cxa_end_catch();
-}
-
-
-static char _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo,
- struct objc_typeinfo *throw_tinfo,
- void **throw_obj_p,
- unsigned outer)
-{
- 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;
- }
-
- // `catch (id)` always catches objc types.
- if (catch_tinfo == &OBJC_EHTYPE_id) {
- if (PrintExceptions) _objc_inform("EXCEPTIONS: catch(id)");
- return 1;
- }
-
- exception = *(id *)throw_obj_p;
- // fixme remapped catch_tinfo->cls
- if ((*exception_matcher)(catch_tinfo->cls, exception)) {
- if (PrintExceptions) _objc_inform("EXCEPTIONS: catch(%s)",
- class_getName(catch_tinfo->cls));
- return 1;
- }
-
- if (PrintExceptions) _objc_inform("EXCEPTIONS: skipping catch(%s)",
- class_getName(catch_tinfo->cls));
-
- return 0;
-}
-
-
-/***********************************************************************
-* _objc_terminate
-* Custom std::terminate handler.
-*
-* The uncaught exception callback is implemented as a std::terminate handler.
-* 1. Check if there's an active exception
-* 2. If so, check if it's an Objective-C exception
-* 3. If so, call our registered callback with the object.
-* 4. Finally, call the previous terminate handler.
-**********************************************************************/
-static void (*old_terminate)(void) = NULL;
-static void _objc_terminate(void)
-{
- if (PrintExceptions) {
- _objc_inform("EXCEPTIONS: terminating");
- }
-
- if (! __cxa_current_exception_type()) {
- // No current exception.
- (*old_terminate)();
- }
- else {
- // There is a current exception. Check if it's an objc exception.
- @try {
- __cxa_rethrow();
- } @catch (id e) {
- // It's an objc object. Call Foundation's handler, if any.
- (*uncaught_handler)(e);
- (*old_terminate)();
- } @catch (...) {
- // It's not an objc object. Continue to C++ terminate.
- (*old_terminate)();
- }
- }
-}
-
-
-/***********************************************************************
-* alt handler support - zerocost implementation only
-**********************************************************************/
-
-#if !SUPPORT_ALT_HANDLERS
-
-PRIVATE_EXTERN void _destroyAltHandlerList(struct alt_handler_list *list)
-{
-}
-
-static void call_alt_handlers(struct _Unwind_Context *ctx)
-{
- // unsupported in sjlj environments
-}
-
-#else
-
-#include <libunwind.h>
-#include <execinfo.h>
-#include <dispatch/dispatch.h>
-
-// Dwarf eh data encodings
-#define DW_EH_PE_omit 0xff // no data follows
-
-#define DW_EH_PE_absptr 0x00
-#define DW_EH_PE_uleb128 0x01
-#define DW_EH_PE_udata2 0x02
-#define DW_EH_PE_udata4 0x03
-#define DW_EH_PE_udata8 0x04
-#define DW_EH_PE_sleb128 0x09
-#define DW_EH_PE_sdata2 0x0A
-#define DW_EH_PE_sdata4 0x0B
-#define DW_EH_PE_sdata8 0x0C
-
-#define DW_EH_PE_pcrel 0x10
-#define DW_EH_PE_textrel 0x20
-#define DW_EH_PE_datarel 0x30
-#define DW_EH_PE_funcrel 0x40
-#define DW_EH_PE_aligned 0x50 // fixme
-
-#define DW_EH_PE_indirect 0x80 // gcc extension
-
-
-/***********************************************************************
-* read_uleb
-* Read a LEB-encoded unsigned integer from the address stored in *pp.
-* Increments *pp past the bytes read.
-* Adapted from DWARF Debugging Information Format 1.1, appendix 4
-**********************************************************************/
-static uintptr_t read_uleb(uintptr_t *pp)
-{
- uintptr_t result = 0;
- uintptr_t shift = 0;
- unsigned char byte;
- do {
- byte = *(const unsigned char *)(*pp)++;
- result |= (byte & 0x7f) << shift;
- shift += 7;
- } while (byte & 0x80);
- return result;
-}
-
-
-/***********************************************************************
-* read_sleb
-* Read a LEB-encoded signed integer from the address stored in *pp.
-* Increments *pp past the bytes read.
-* Adapted from DWARF Debugging Information Format 1.1, appendix 4
-**********************************************************************/
-static intptr_t read_sleb(uintptr_t *pp)
-{
- uintptr_t result = 0;
- uintptr_t shift = 0;
- unsigned char byte;
- do {
- byte = *(const unsigned char *)(*pp)++;
- result |= (byte & 0x7f) << shift;
- shift += 7;
- } while (byte & 0x80);
- if ((shift < 8*sizeof(intptr_t)) && (byte & 0x40)) {
- result |= ((intptr_t)-1) << shift;
- }
- return result;
-}
-
-
-/***********************************************************************
-* read_address
-* Reads an encoded address from the address stored in *pp.
-* Increments *pp past the bytes read.
-* The data is interpreted according to the given dwarf encoding
-* and base addresses.
-**********************************************************************/
-static uintptr_t read_address(uintptr_t *pp,
- const struct dwarf_eh_bases *bases,
- unsigned char encoding)
-{
- uintptr_t result = 0;
- uintptr_t oldp = *pp;
-
- // fixme need DW_EH_PE_aligned?
-
-#define READ(type) \
- result = *(type *)(*pp); \
- *pp += sizeof(type);
-
- if (encoding == DW_EH_PE_omit) return 0;
-
- switch (encoding & 0x0f) {
- case DW_EH_PE_absptr:
- READ(uintptr_t);
- break;
- case DW_EH_PE_uleb128:
- result = read_uleb(pp);
- break;
- case DW_EH_PE_udata2:
- READ(uint16_t);
- break;
- case DW_EH_PE_udata4:
- READ(uint32_t);
- break;
-#if __LP64__
- case DW_EH_PE_udata8:
- READ(uint64_t);
- break;
-#endif
- case DW_EH_PE_sleb128:
- result = read_sleb(pp);
- break;
- case DW_EH_PE_sdata2:
- READ(int16_t);
- break;
- case DW_EH_PE_sdata4:
- READ(int32_t);
- break;
-#if __LP64__
- case DW_EH_PE_sdata8:
- READ(int64_t);
- break;
-#endif
- default:
- _objc_inform("unknown DWARF EH encoding 0x%x at %p",
- encoding, (void *)*pp);
- break;
- }
-
-#undef READ
-
- if (result) {
- switch (encoding & 0x70) {
- case DW_EH_PE_pcrel:
- // fixme correct?
- result += (uintptr_t)oldp;
- break;
- case DW_EH_PE_textrel:
- result += bases->tbase;
- break;
- case DW_EH_PE_datarel:
- result += bases->dbase;
- break;
- case DW_EH_PE_funcrel:
- result += bases->func;
- break;
- case DW_EH_PE_aligned:
- _objc_inform("unknown DWARF EH encoding 0x%x at %p",
- encoding, (void *)*pp);
- break;
- default:
- // no adjustment
- break;
- }
-
- if (encoding & DW_EH_PE_indirect) {
- result = *(uintptr_t *)result;
- }
- }
-
- return (uintptr_t)result;
-}
-
-
-static bool isObjCExceptionCatcher(uintptr_t lsda, uintptr_t ip,
- const struct dwarf_eh_bases* bases,
- uintptr_t* try_start, uintptr_t* try_end)
-{
- unsigned char LPStart_enc = *(const unsigned char *)lsda++;
-
- if (LPStart_enc != DW_EH_PE_omit) {
- read_address(&lsda, bases, LPStart_enc); // LPStart
- }
-
- unsigned char TType_enc = *(const unsigned char *)lsda++;
- if (TType_enc != DW_EH_PE_omit) {
- read_uleb(&lsda); // TType
- }
-
- unsigned char call_site_enc = *(const unsigned char *)lsda++;
- uintptr_t length = read_uleb(&lsda);
- uintptr_t call_site_table = lsda;
- uintptr_t call_site_table_end = call_site_table + length;
- uintptr_t action_record_table = call_site_table_end;
-
- uintptr_t action_record = 0;
- uintptr_t p = call_site_table;
-
- 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);
-
- if (ip < bases->func + start) {
- // no more source ranges
- return false;
- }
- else if (ip < bases->func + 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;
- break;
- }
- }
-
- if (!action_record) return false; // no catch handlers
-
- // has handlers, destructors, and/or throws specifications
- // Use this frame if it has any handlers
- bool has_handler = false;
- p = action_record;
- intptr_t offset;
- do {
- intptr_t filter = read_sleb(&p);
- uintptr_t temp = p;
- offset = read_sleb(&temp);
- p += offset;
-
- if (filter < 0) {
- // throws specification - ignore
- } else if (filter == 0) {
- // destructor - ignore
- } else /* filter >= 0 */ {
- // catch handler - use this frame
- has_handler = true;
- break;
- }
- } while (offset);
-
- return has_handler;
-}
-
-
-struct frame_range {
- uintptr_t ip_start;
- uintptr_t ip_end;
- uintptr_t cfa;
-};
-
-static struct frame_range findHandler(void)
-{
- // walk stack looking for frame with objc catch handler
- unw_context_t uc;
- unw_cursor_t cursor;
- unw_proc_info_t info;
- unw_getcontext(&uc);
- unw_init_local(&cursor, &uc);
- while ( (unw_step(&cursor) > 0) && (unw_get_proc_info(&cursor, &info) == UNW_ESUCCESS) ) {
- // must use objc personality handler
- if ( info.handler != (uintptr_t)__objc_personality_v0 )
- continue;
- // must have landing pad
- if ( info.lsda == 0 )
- continue;
- // must have landing pad that catches objc exceptions
- struct dwarf_eh_bases bases;
- bases.tbase = 0; // from unwind-dw2-fde-darwin.c:examine_objects()
- bases.dbase = 0; // from unwind-dw2-fde-darwin.c:examine_objects()
- bases.func = info.start_ip;
- 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) ) {
- unw_word_t cfa;
- unw_get_reg(&cursor, UNW_REG_SP, &cfa);
- return (struct frame_range){try_start, try_end, cfa};
- }
- }
-
- return (struct frame_range){0, 0, 0};
-}
-
-
-// This data structure assumes the number of
-// active alt handlers per frame is small.
-
-// for OBJC_DEBUG_ALT_HANDLERS, record the call to objc_addExceptionHandler.
-#define BACKTRACE_COUNT 46
-#define THREADNAME_COUNT 64
-struct alt_handler_debug {
- uintptr_t token;
- int backtraceSize;
- void *backtrace[BACKTRACE_COUNT];
- char thread[THREADNAME_COUNT];
- char queue[THREADNAME_COUNT];
-};
-
-struct alt_handler_data {
- uintptr_t ip_start;
- uintptr_t ip_end;
- uintptr_t cfa;
- objc_exception_handler fn;
- void *context;
- struct alt_handler_debug *debug;
-};
-
-struct alt_handler_list {
- unsigned int allocated;
- unsigned int used;
- struct alt_handler_data *handlers;
- struct alt_handler_list *next_DEBUGONLY;
-};
-
-static pthread_mutex_t DebugLock = PTHREAD_MUTEX_INITIALIZER;
-static struct alt_handler_list *DebugLists;
-static uintptr_t DebugCounter;
-
-PRIVATE_EXTERN void alt_handler_error(uintptr_t token) __attribute__((noinline));
-
-static struct alt_handler_list *
-fetch_handler_list(BOOL create)
-{
- _objc_pthread_data *data = _objc_fetch_pthread_data(create);
- if (!data) return NULL;
-
- struct alt_handler_list *list = data->handlerList;
- if (!list) {
- if (!create) return NULL;
- list = _calloc_internal(1, sizeof(*list));
- data->handlerList = list;
-
- if (DebugAltHandlers) {
- // Save this list so the debug code can find it from other threads
- pthread_mutex_lock(&DebugLock);
- list->next_DEBUGONLY = DebugLists;
- DebugLists = list;
- pthread_mutex_unlock(&DebugLock);
- }
- }
-
- return list;
-}
-
-
-PRIVATE_EXTERN void _destroyAltHandlerList(struct alt_handler_list *list)
-{
- if (list) {
- if (DebugAltHandlers) {
- // Detach from the list-of-lists.
- pthread_mutex_lock(&DebugLock);
- struct alt_handler_list **listp = &DebugLists;
- while (*listp && *listp != list) listp = &(*listp)->next_DEBUGONLY;
- if (*listp) *listp = (*listp)->next_DEBUGONLY;
- pthread_mutex_unlock(&DebugLock);
- }
-
- if (list->handlers) {
- _free_internal(list->handlers);
- }
- _free_internal(list);
- }
-}
-
-
-uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context)
-{
- // Find the closest enclosing frame with objc catch handlers
- struct frame_range target_frame = findHandler();
- if (!target_frame.ip_start) {
- // No suitable enclosing handler found.
- return 0;
- }
-
- // Record this alt handler for the discovered frame.
- struct alt_handler_list *list = fetch_handler_list(YES);
- unsigned int i = 0;
-
- if (list->used == list->allocated) {
- list->allocated = list->allocated*2 ?: 4;
- list->handlers = _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)
- {
- break;
- }
- }
- if (i == list->allocated) {
- _objc_fatal("alt handlers in objc runtime are buggy!");
- }
- }
-
- 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->fn = fn;
- data->context = context;
- list->used++;
-
- uintptr_t token = i+1;
-
- if (DebugAltHandlers) {
- // Record backtrace in case this handler is misused later.
- pthread_mutex_lock(&DebugLock);
-
- token = DebugCounter++;
- if (token == 0) token = DebugCounter++;
-
- if (!data->debug) {
- data->debug = _calloc_internal(sizeof(*data->debug), 1);
- } else {
- bzero(data->debug, sizeof(*data->debug));
- }
-
- pthread_getname_np(pthread_self(), data->debug->thread, THREADNAME_COUNT);
- strlcpy(data->debug->queue,
- dispatch_queue_get_label(dispatch_get_current_queue()),
- THREADNAME_COUNT);
- data->debug->backtraceSize =
- backtrace(data->debug->backtrace, BACKTRACE_COUNT);
- data->debug->token = token;
-
- pthread_mutex_unlock(&DebugLock);
- }
-
- if (PrintAltHandlers) {
- _objc_inform("ALT HANDLERS: installing alt handler #%lu %p(%p) on "
- "frame [ip=%p..%p sp=%p]", (unsigned long)token,
- data->fn, data->context, (void *)data->ip_start,
- (void *)data->ip_end, (void *)data->cfa);
- }
-
- if (list->used > 1000) {
- static int warned = 0;
- if (!warned) {
- _objc_inform("ALT HANDLERS: *** over 1000 alt handlers installed; "
- "this is probably a bug");
- warned = 1;
- }
- }
-
- return token;
-}
-
-
-void objc_removeExceptionHandler(uintptr_t token)
-{
- if (!token) {
- // objc_addExceptionHandler failed
- return;
- }
-
- struct alt_handler_list *list = fetch_handler_list(NO);
- if (!list || !list->handlers) {
- // no alt handlers active
- alt_handler_error(token);
- __builtin_trap();
- }
-
- uintptr_t i = token-1;
-
- if (DebugAltHandlers) {
- // search for the token instead of using token-1
- for (i = 0; i < list->allocated; i++) {
- struct alt_handler_data *data = &list->handlers[i];
- if (data->debug && data->debug->token == token) break;
- }
- }
-
- if (i >= list->allocated) {
- // token out of range
- alt_handler_error(token);
- __builtin_trap();
- }
-
- struct alt_handler_data *data = &list->handlers[i];
-
- if (data->ip_start == 0 && data->ip_end == 0 && data->cfa == 0) {
- // token in range, but invalid
- alt_handler_error(token);
- __builtin_trap();
- }
-
- if (PrintAltHandlers) {
- _objc_inform("ALT HANDLERS: removing alt handler #%lu %p(%p) on "
- "frame [ip=%p..%p sp=%p]", (unsigned long)token,
- data->fn, data->context, (void *)data->ip_start,
- (void *)data->ip_end, (void *)data->cfa);
- }
-
- if (data->debug) _free_internal(data->debug);
- bzero(data, sizeof(*data));
- list->used--;
-}
-
-PRIVATE_EXTERN void objc_alt_handler_error(void) __attribute__((noinline));
-
-PRIVATE_EXTERN void alt_handler_error(uintptr_t token)
-{
- if (!DebugAltHandlers) {
- _objc_inform_now_and_on_crash
- ("objc_removeExceptionHandler() called with unknown alt handler; "
- "this is probably a bug in multithreaded AppKit use. "
- "Set environment variable OBJC_DEBUG_ALT_HANDLERS=YES "
- "or break in objc_alt_handler_error() to debug.");
- objc_alt_handler_error();
- }
-
- pthread_mutex_lock(&DebugLock);
-
- // Search other threads' alt handler lists for this handler.
- struct alt_handler_list *list;
- for (list = DebugLists; list; list = list->next_DEBUGONLY) {
- int h;
- for (h = 0; h < list->allocated; h++) {
- struct alt_handler_data *data = &list->handlers[h];
- if (data->debug && data->debug->token == token) {
- // found it
- int i;
-
- // Build a string from the recorded backtrace
- char *symbolString;
- char **symbols =
- backtrace_symbols(data->debug->backtrace,
- data->debug->backtraceSize);
- size_t len = 1;
- for (i = 0; i < data->debug->backtraceSize; i++){
- len += 4 + strlen(symbols[i]) + 1;
- }
- symbolString = _calloc_internal(len, 1);
- for (i = 0; i < data->debug->backtraceSize; i++){
- strcat(symbolString, " ");
- strcat(symbolString, symbols[i]);
- strcat(symbolString, "\n");
- }
-
- free(symbols);
-
- _objc_inform_now_and_on_crash
- ("objc_removeExceptionHandler() called with "
- "unknown alt handler; this is probably a bug in "
- "multithreaded AppKit use. \n"
- "The matching objc_addExceptionHandler() was called by:\n"
- "Thread '%s': Dispatch queue: '%s': \n%s",
- data->debug->thread, data->debug->queue, symbolString);
-
- pthread_mutex_unlock(&DebugLock);
- _free_internal(symbolString);
-
- objc_alt_handler_error();
- }
- }
- }
-
- pthread_mutex_lock(&DebugLock);
-
- // not found
- _objc_inform_now_and_on_crash
- ("objc_removeExceptionHandler() called with unknown alt handler; "
- "this is probably a bug in multithreaded AppKit use");
- objc_alt_handler_error();
-}
-
-PRIVATE_EXTERN void objc_alt_handler_error(void)
-{
- __builtin_trap();
-}
-
-// called in order registered, to match 32-bit _NSAddAltHandler2
-// fixme reverse registration order matches c++ destructors better
-static void call_alt_handlers(struct _Unwind_Context *ctx)
-{
- uintptr_t ip = _Unwind_GetIP(ctx) - 1;
- uintptr_t cfa = _Unwind_GetCFA(ctx);
- unsigned int i;
-
- struct alt_handler_list *list = fetch_handler_list(NO);
- if (!list || list->used == 0) return;
-
- 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)
- {
- // Copy and clear before the callback, in case the
- // callback manipulates the alt handler list.
- struct alt_handler_data copy = *data;
- bzero(data, sizeof(*data));
- list->used--;
- 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);
- }
- if (copy.fn) (*copy.fn)(nil, copy.context);
- }
- }
-}
-
-// SUPPORT_ALT_HANDLERS
-#endif
-
-
-/***********************************************************************
-* exception_init
-* Initialize libobjc's exception handling system.
-* Called by map_images().
-**********************************************************************/
-PRIVATE_EXTERN 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
-}
-
-
-// __OBJC2__
-#endif
--- /dev/null
+/*
+ * Copyright (c) 2002-2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#if !__OBJC2__
+
+/***********************************************************************
+* 32-bit implementation
+**********************************************************************/
+
+#include "objc-private.h"
+#include <stdlib.h>
+#include <setjmp.h>
+#include <execinfo.h>
+
+#include "objc-exception.h"
+
+static objc_exception_functions_t xtab;
+
+// forward declaration
+static void set_default_handlers();
+
+
+/*
+ * Exported functions
+ */
+
+// get table; version tells how many
+void objc_exception_get_functions(objc_exception_functions_t *table) {
+ // only version 0 supported at this point
+ if (table && table->version == 0)
+ *table = xtab;
+}
+
+// set table
+void objc_exception_set_functions(objc_exception_functions_t *table) {
+ // only version 0 supported at this point
+ if (table && table->version == 0)
+ xtab = *table;
+}
+
+/*
+ * The following functions are
+ * synthesized by the compiler upon encountering language constructs
+ */
+
+void objc_exception_throw(id exception) {
+ if (!xtab.throw_exc) {
+ set_default_handlers();
+ }
+
+ if (PrintExceptionThrow) {
+ _objc_inform("EXCEPTIONS: throwing %p (%s)",
+ exception, object_getClassName(exception));
+ void* callstack[500];
+ int frameCount = backtrace(callstack, 500);
+ backtrace_symbols_fd(callstack, frameCount, fileno(stderr));
+ }
+
+ OBJC_RUNTIME_OBJC_EXCEPTION_THROW(exception); // dtrace probe to log throw activity.
+ xtab.throw_exc(exception);
+ _objc_fatal("objc_exception_throw failed");
+}
+
+void objc_exception_try_enter(void *localExceptionData) {
+ if (!xtab.throw_exc) {
+ set_default_handlers();
+ }
+ xtab.try_enter(localExceptionData);
+}
+
+
+void objc_exception_try_exit(void *localExceptionData) {
+ if (!xtab.throw_exc) {
+ set_default_handlers();
+ }
+ xtab.try_exit(localExceptionData);
+}
+
+
+id objc_exception_extract(void *localExceptionData) {
+ if (!xtab.throw_exc) {
+ set_default_handlers();
+ }
+ return xtab.extract(localExceptionData);
+}
+
+
+int objc_exception_match(Class exceptionClass, id exception) {
+ if (!xtab.throw_exc) {
+ set_default_handlers();
+ }
+ return xtab.match(exceptionClass, exception);
+}
+
+
+// quick and dirty exception handling code
+// default implementation - mostly a toy for use outside/before Foundation
+// provides its implementation
+// Perhaps the default implementation should just complain loudly and quit
+
+
+extern void _objc_inform(const char *fmt, ...);
+
+typedef struct { jmp_buf buf; void *pointers[4]; } LocalData_t;
+
+typedef struct _threadChain {
+ LocalData_t *topHandler;
+ objc_thread_t perThreadID;
+ struct _threadChain *next;
+}
+ ThreadChainLink_t;
+
+static ThreadChainLink_t ThreadChainLink;
+
+static ThreadChainLink_t *getChainLink() {
+ // follow links until thread_self() found (someday) XXX
+ objc_thread_t self = thread_self();
+ ThreadChainLink_t *walker = &ThreadChainLink;
+ while (walker->perThreadID != self) {
+ if (walker->next != NULL) {
+ walker = walker->next;
+ continue;
+ }
+ // create a new one
+ // XXX not thread safe (!)
+ // XXX Also, we don't register to deallocate on thread death
+ walker->next = (ThreadChainLink_t *)malloc(sizeof(ThreadChainLink_t));
+ walker = walker->next;
+ walker->next = NULL;
+ walker->topHandler = NULL;
+ walker->perThreadID = self;
+ }
+ return walker;
+}
+
+static void default_try_enter(void *localExceptionData) {
+ LocalData_t *data = (LocalData_t *)localExceptionData;
+ ThreadChainLink_t *chainLink = getChainLink();
+ data->pointers[1] = chainLink->topHandler;
+ chainLink->topHandler = data;
+ if (PrintExceptions) _objc_inform("EXCEPTIONS: entered try block %p\n", chainLink->topHandler);
+}
+
+static void default_throw(id value) {
+ ThreadChainLink_t *chainLink = getChainLink();
+ LocalData_t *led;
+ if (value == nil) {
+ if (PrintExceptions) _objc_inform("EXCEPTIONS: objc_exception_throw with nil value\n");
+ return;
+ }
+ if (chainLink == NULL) {
+ if (PrintExceptions) _objc_inform("EXCEPTIONS: No handler in place!\n");
+ return;
+ }
+ if (PrintExceptions) _objc_inform("EXCEPTIONS: exception thrown, going to handler block %p\n", chainLink->topHandler);
+ led = chainLink->topHandler;
+ chainLink->topHandler = (LocalData_t *)
+ led->pointers[1]; // pop top handler
+ led->pointers[0] = value; // store exception that is thrown
+#if TARGET_OS_WIN32
+ longjmp(led->buf, 1);
+#else
+ _longjmp(led->buf, 1);
+#endif
+}
+
+static void default_try_exit(void *led) {
+ ThreadChainLink_t *chainLink = getChainLink();
+ if (!chainLink || led != chainLink->topHandler) {
+ if (PrintExceptions) _objc_inform("EXCEPTIONS: *** mismatched try block exit handlers\n");
+ return;
+ }
+ if (PrintExceptions) _objc_inform("EXCEPTIONS: removing try block handler %p\n", chainLink->topHandler);
+ chainLink->topHandler = (LocalData_t *)
+ chainLink->topHandler->pointers[1]; // pop top handler
+}
+
+static id default_extract(void *localExceptionData) {
+ LocalData_t *led = (LocalData_t *)localExceptionData;
+ return (id)led->pointers[0];
+}
+
+static int default_match(Class exceptionClass, id exception) {
+ //return [exception isKindOfClass:exceptionClass];
+ Class cls;
+ for (cls = _object_getClass(exception); nil != cls; cls = _class_getSuperclass(cls))
+ if (cls == exceptionClass) return 1;
+ return 0;
+}
+
+static void set_default_handlers() {
+ objc_exception_functions_t default_functions = {
+ 0, default_throw, default_try_enter, default_try_exit, default_extract, default_match };
+
+ // should this always print?
+ if (PrintExceptions) _objc_inform("EXCEPTIONS: *** Setting default (non-Foundation) exception mechanism\n");
+ objc_exception_set_functions(&default_functions);
+}
+
+
+void exception_init(void)
+{
+ // nothing to do
+}
+
+void _destroyAltHandlerList(struct alt_handler_list *list)
+{
+ // nothing to do
+}
+
+
+// !__OBJC2__
+#else
+// __OBJC2__
+
+/***********************************************************************
+* 64-bit implementation.
+**********************************************************************/
+
+#include "objc-private.h"
+#include <objc/objc-exception.h>
+#include <execinfo.h>
+
+// unwind library types and functions
+// Mostly adapted from Itanium C++ ABI: Exception Handling
+// http://www.codesourcery.com/cxx-abi/abi-eh.html
+
+struct _Unwind_Exception;
+struct _Unwind_Context;
+
+typedef int _Unwind_Action;
+static const _Unwind_Action _UA_SEARCH_PHASE = 1;
+static const _Unwind_Action _UA_CLEANUP_PHASE = 2;
+static const _Unwind_Action _UA_HANDLER_FRAME = 4;
+static const _Unwind_Action _UA_FORCE_UNWIND = 8;
+
+typedef int _Unwind_Reason_Code;
+static const _Unwind_Reason_Code _URC_NO_REASON = 0;
+static const _Unwind_Reason_Code _URC_FOREIGN_EXCEPTION_CAUGHT = 1;
+static const _Unwind_Reason_Code _URC_FATAL_PHASE2_ERROR = 2;
+static const _Unwind_Reason_Code _URC_FATAL_PHASE1_ERROR = 3;
+static const _Unwind_Reason_Code _URC_NORMAL_STOP = 4;
+static const _Unwind_Reason_Code _URC_END_OF_STACK = 5;
+static const _Unwind_Reason_Code _URC_HANDLER_FOUND = 6;
+static const _Unwind_Reason_Code _URC_INSTALL_CONTEXT = 7;
+static const _Unwind_Reason_Code _URC_CONTINUE_UNWIND = 8;
+
+struct dwarf_eh_bases
+{
+ uintptr_t tbase;
+ uintptr_t dbase;
+ uintptr_t func;
+};
+
+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
+
+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
+#else
+# define CXX_PERSONALITY __gxx_personality_sj0
+#endif
+
+OBJC_EXTERN _Unwind_Reason_Code
+CXX_PERSONALITY(int version,
+ _Unwind_Action actions,
+ uint64_t exceptionClass,
+ struct _Unwind_Exception *exceptionObject,
+ struct _Unwind_Context *context);
+
+
+// objc's internal exception types and data
+
+struct objc_typeinfo {
+ // Position of vtable and name fields must match C++ typeinfo object
+ const void **vtable; // always objc_ehtype_vtable+2
+ const char *name; // c++ typeinfo string
+
+ Class cls_unremapped;
+};
+
+struct objc_exception {
+ id obj;
+ struct objc_typeinfo tinfo;
+};
+
+
+static void _objc_exception_noop(void) { }
+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
+ (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",
+ NULL
+};
+
+
+
+/***********************************************************************
+* Foundation customization
+**********************************************************************/
+
+/***********************************************************************
+* _objc_default_exception_preprocessor
+* Default exception preprocessor. Expected to be overridden by Foundation.
+**********************************************************************/
+static id _objc_default_exception_preprocessor(id exception)
+{
+ return exception;
+}
+static objc_exception_preprocessor exception_preprocessor = _objc_default_exception_preprocessor;
+
+
+/***********************************************************************
+* _objc_default_exception_matcher
+* Default exception matcher. Expected to be overridden by Foundation.
+**********************************************************************/
+static int _objc_default_exception_matcher(Class catch_cls, id exception)
+{
+ Class cls;
+ for (cls = _object_getClass(exception);
+ cls != NULL;
+ cls = class_getSuperclass(cls))
+ {
+ if (cls == catch_cls) return 1;
+ }
+
+ return 0;
+}
+static objc_exception_matcher exception_matcher = _objc_default_exception_matcher;
+
+
+/***********************************************************************
+* _objc_default_uncaught_exception_handler
+* Default uncaught exception handler. Expected to be overridden by Foundation.
+**********************************************************************/
+static void _objc_default_uncaught_exception_handler(id exception)
+{
+}
+static objc_uncaught_exception_handler uncaught_handler = _objc_default_uncaught_exception_handler;
+
+
+/***********************************************************************
+* objc_setExceptionPreprocessor
+* Set a handler for preprocessing Objective-C exceptions.
+* Returns the previous handler.
+**********************************************************************/
+objc_exception_preprocessor
+objc_setExceptionPreprocessor(objc_exception_preprocessor fn)
+{
+ objc_exception_preprocessor result = exception_preprocessor;
+ exception_preprocessor = fn;
+ return result;
+}
+
+
+/***********************************************************************
+* objc_setExceptionMatcher
+* Set a handler for matching Objective-C exceptions.
+* Returns the previous handler.
+**********************************************************************/
+objc_exception_matcher
+objc_setExceptionMatcher(objc_exception_matcher fn)
+{
+ objc_exception_matcher result = exception_matcher;
+ exception_matcher = fn;
+ return result;
+}
+
+
+/***********************************************************************
+* objc_setUncaughtExceptionHandler
+* Set a handler for uncaught Objective-C exceptions.
+* Returns the previous handler.
+**********************************************************************/
+objc_uncaught_exception_handler
+objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
+{
+ objc_uncaught_exception_handler result = uncaught_handler;
+ uncaught_handler = fn;
+ return result;
+}
+
+
+/***********************************************************************
+* Exception personality
+**********************************************************************/
+
+static void call_alt_handlers(struct _Unwind_Context *ctx);
+
+_Unwind_Reason_Code
+__objc_personality_v0(int version,
+ _Unwind_Action actions,
+ uint64_t exceptionClass,
+ struct _Unwind_Exception *exceptionObject,
+ struct _Unwind_Context *context)
+{
+ BOOL unwinding = ((actions & _UA_CLEANUP_PHASE) ||
+ (actions & _UA_FORCE_UNWIND));
+
+ if (PrintExceptions) {
+ _objc_inform("EXCEPTIONS: %s through frame [ip=%p sp=%p] "
+ "for exception %p",
+ unwinding ? "unwinding" : "searching",
+ (void*)(_Unwind_GetIP(context)-1),
+ (void*)_Unwind_GetCFA(context), exceptionObject);
+ }
+
+ // If we're executing the unwind, call this frame's alt handlers, if any.
+ if (unwinding) {
+ call_alt_handlers(context);
+ }
+
+ // Let C++ handle the unwind itself.
+ return CXX_PERSONALITY(version, actions, exceptionClass,
+ exceptionObject, context);
+}
+
+
+/***********************************************************************
+* Compiler ABI
+**********************************************************************/
+
+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 *)
+ __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_unremapped = obj ? _object_getClass(obj) : Nil;
+
+ if (PrintExceptions) {
+ _objc_inform("EXCEPTIONS: throwing %p (object %p, a %s)",
+ exc, obj, object_getClassName(obj));
+ }
+
+ if (PrintExceptionThrow) {
+ if (!PrintExceptions)
+ _objc_inform("EXCEPTIONS: throwing %p (object %p, a %s)",
+ exc, obj, object_getClassName(obj));
+ void* callstack[500];
+ int frameCount = backtrace(callstack, 500);
+ backtrace_symbols_fd(callstack, frameCount, fileno(stderr));
+ }
+
+ OBJC_RUNTIME_OBJC_EXCEPTION_THROW(obj); // dtrace probe to log throw activity
+ __cxa_throw(exc, &exc->tinfo, &_objc_exception_destructor);
+ __builtin_trap();
+}
+
+
+void objc_exception_rethrow(void)
+{
+ // exception_preprocessor doesn't get another bite of the apple
+ if (PrintExceptions) {
+ _objc_inform("EXCEPTIONS: rethrowing current exception");
+ }
+
+ OBJC_RUNTIME_OBJC_EXCEPTION_RETHROW(); // dtrace probe to log throw activity.
+ __cxa_rethrow();
+ __builtin_trap();
+}
+
+
+id objc_begin_catch(void *exc_gen)
+{
+ if (PrintExceptions) {
+ _objc_inform("EXCEPTIONS: handling exception %p at %p",
+ exc_gen, __builtin_return_address(0));
+ }
+ // NOT actually an id in the catch(...) case!
+ return (id)__cxa_begin_catch(exc_gen);
+}
+
+
+void objc_end_catch(void)
+{
+ if (PrintExceptions) {
+ _objc_inform("EXCEPTIONS: finishing handler");
+ }
+ __cxa_end_catch();
+}
+
+
+// `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 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 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 true;
+ }
+
+ exception = *(id *)throw_obj_p;
+
+ 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(handler_cls));
+ return true;
+ }
+
+ if (PrintExceptions) _objc_inform("EXCEPTIONS: skipping catch(%s)",
+ class_getName(handler_cls));
+
+ return false;
+}
+
+
+/***********************************************************************
+* _objc_terminate
+* Custom std::terminate handler.
+*
+* The uncaught exception callback is implemented as a std::terminate handler.
+* 1. Check if there's an active exception
+* 2. If so, check if it's an Objective-C exception
+* 3. If so, call our registered callback with the object.
+* 4. Finally, call the previous terminate handler.
+**********************************************************************/
+static void (*old_terminate)(void) = NULL;
+static void _objc_terminate(void)
+{
+ if (PrintExceptions) {
+ _objc_inform("EXCEPTIONS: terminating");
+ }
+
+ if (! __cxa_current_exception_type()) {
+ // No current exception.
+ (*old_terminate)();
+ }
+ else {
+ // There is a current exception. Check if it's an objc exception.
+ @try {
+ __cxa_rethrow();
+ } @catch (id e) {
+ // It's an objc object. Call Foundation's handler, if any.
+ (*uncaught_handler)(e);
+ (*old_terminate)();
+ } @catch (...) {
+ // It's not an objc object. Continue to C++ terminate.
+ (*old_terminate)();
+ }
+ }
+}
+
+
+/***********************************************************************
+* 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
+
+void _destroyAltHandlerList(struct alt_handler_list *list)
+{
+}
+
+static void call_alt_handlers(struct _Unwind_Context *ctx)
+{
+ // unsupported in sjlj environments
+}
+
+#else
+
+#include <libunwind.h>
+#include <execinfo.h>
+#include <dispatch/dispatch.h>
+
+// Dwarf eh data encodings
+#define DW_EH_PE_omit 0xff // no data follows
+
+#define DW_EH_PE_absptr 0x00
+#define DW_EH_PE_uleb128 0x01
+#define DW_EH_PE_udata2 0x02
+#define DW_EH_PE_udata4 0x03
+#define DW_EH_PE_udata8 0x04
+#define DW_EH_PE_sleb128 0x09
+#define DW_EH_PE_sdata2 0x0A
+#define DW_EH_PE_sdata4 0x0B
+#define DW_EH_PE_sdata8 0x0C
+
+#define DW_EH_PE_pcrel 0x10
+#define DW_EH_PE_textrel 0x20
+#define DW_EH_PE_datarel 0x30
+#define DW_EH_PE_funcrel 0x40
+#define DW_EH_PE_aligned 0x50 // fixme
+
+#define DW_EH_PE_indirect 0x80 // gcc extension
+
+
+/***********************************************************************
+* read_uleb
+* Read a LEB-encoded unsigned integer from the address stored in *pp.
+* Increments *pp past the bytes read.
+* Adapted from DWARF Debugging Information Format 1.1, appendix 4
+**********************************************************************/
+static uintptr_t read_uleb(uintptr_t *pp)
+{
+ uintptr_t result = 0;
+ uintptr_t shift = 0;
+ unsigned char byte;
+ do {
+ byte = *(const unsigned char *)(*pp)++;
+ result |= (byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+ return result;
+}
+
+
+/***********************************************************************
+* read_sleb
+* Read a LEB-encoded signed integer from the address stored in *pp.
+* Increments *pp past the bytes read.
+* Adapted from DWARF Debugging Information Format 1.1, appendix 4
+**********************************************************************/
+static intptr_t read_sleb(uintptr_t *pp)
+{
+ uintptr_t result = 0;
+ uintptr_t shift = 0;
+ unsigned char byte;
+ do {
+ byte = *(const unsigned char *)(*pp)++;
+ result |= (byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+ if ((shift < 8*sizeof(intptr_t)) && (byte & 0x40)) {
+ result |= ((intptr_t)-1) << shift;
+ }
+ return result;
+}
+
+
+/***********************************************************************
+* read_address
+* Reads an encoded address from the address stored in *pp.
+* Increments *pp past the bytes read.
+* The data is interpreted according to the given dwarf encoding
+* and base addresses.
+**********************************************************************/
+static uintptr_t read_address(uintptr_t *pp,
+ const struct dwarf_eh_bases *bases,
+ unsigned char encoding)
+{
+ uintptr_t result = 0;
+ uintptr_t oldp = *pp;
+
+ // fixme need DW_EH_PE_aligned?
+
+#define READ(type) \
+ result = *(type *)(*pp); \
+ *pp += sizeof(type);
+
+ if (encoding == DW_EH_PE_omit) return 0;
+
+ switch (encoding & 0x0f) {
+ case DW_EH_PE_absptr:
+ READ(uintptr_t);
+ break;
+ case DW_EH_PE_uleb128:
+ result = read_uleb(pp);
+ break;
+ case DW_EH_PE_udata2:
+ READ(uint16_t);
+ break;
+ case DW_EH_PE_udata4:
+ READ(uint32_t);
+ break;
+#if __LP64__
+ case DW_EH_PE_udata8:
+ READ(uint64_t);
+ break;
+#endif
+ case DW_EH_PE_sleb128:
+ result = read_sleb(pp);
+ break;
+ case DW_EH_PE_sdata2:
+ READ(int16_t);
+ break;
+ case DW_EH_PE_sdata4:
+ READ(int32_t);
+ break;
+#if __LP64__
+ case DW_EH_PE_sdata8:
+ READ(int64_t);
+ break;
+#endif
+ default:
+ _objc_inform("unknown DWARF EH encoding 0x%x at %p",
+ encoding, (void *)*pp);
+ break;
+ }
+
+#undef READ
+
+ if (result) {
+ switch (encoding & 0x70) {
+ case DW_EH_PE_pcrel:
+ // fixme correct?
+ result += (uintptr_t)oldp;
+ break;
+ case DW_EH_PE_textrel:
+ result += bases->tbase;
+ break;
+ case DW_EH_PE_datarel:
+ result += bases->dbase;
+ break;
+ case DW_EH_PE_funcrel:
+ result += bases->func;
+ break;
+ case DW_EH_PE_aligned:
+ _objc_inform("unknown DWARF EH encoding 0x%x at %p",
+ encoding, (void *)*pp);
+ break;
+ default:
+ // no adjustment
+ break;
+ }
+
+ if (encoding & DW_EH_PE_indirect) {
+ result = *(uintptr_t *)result;
+ }
+ }
+
+ return (uintptr_t)result;
+}
+
+
+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,
+ struct frame_range *frame)
+{
+ unsigned char LPStart_enc = *(const unsigned char *)lsda++;
+
+ if (LPStart_enc != DW_EH_PE_omit) {
+ read_address(&lsda, bases, LPStart_enc); // LPStart
+ }
+
+ unsigned char TType_enc = *(const unsigned char *)lsda++;
+ if (TType_enc != DW_EH_PE_omit) {
+ read_uleb(&lsda); // TType
+ }
+
+ unsigned char call_site_enc = *(const unsigned char *)lsda++;
+ uintptr_t length = read_uleb(&lsda);
+ uintptr_t call_site_table = lsda;
+ uintptr_t call_site_table_end = call_site_table + length;
+ uintptr_t action_record_table = call_site_table_end;
+
+ 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)+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 < start) {
+ // no more source ranges
+ return false;
+ }
+ 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 = start;
+ try_end = start + len;
+ try_landing_pad = pad;
+ break;
+ }
+ }
+
+ if (!action_record) return false; // no catch handlers
+
+ // has handlers, destructors, and/or throws specifications
+ // Use this frame if it has any handlers
+ bool has_handler = false;
+ p = action_record;
+ intptr_t offset;
+ do {
+ intptr_t filter = read_sleb(&p);
+ uintptr_t temp = p;
+ offset = read_sleb(&temp);
+ p += offset;
+
+ if (filter < 0) {
+ // throws specification - ignore
+ } else if (filter == 0) {
+ // destructor - ignore
+ } else /* filter >= 0 */ {
+ // catch handler - use this frame
+ has_handler = true;
+ break;
+ }
+ } while (offset);
+
+ if (!has_handler) return false;
+
+ // 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;
+}
+
+
+static struct frame_range findHandler(void)
+{
+ // walk stack looking for frame with objc catch handler
+ unw_context_t uc;
+ unw_cursor_t cursor;
+ unw_proc_info_t info;
+ unw_getcontext(&uc);
+ unw_init_local(&cursor, &uc);
+ while ( (unw_step(&cursor) > 0) && (unw_get_proc_info(&cursor, &info) == UNW_ESUCCESS) ) {
+ // must use objc personality handler
+ if ( info.handler != (uintptr_t)__objc_personality_v0 )
+ continue;
+ // must have landing pad
+ if ( info.lsda == 0 )
+ continue;
+ // must have landing pad that catches objc exceptions
+ struct dwarf_eh_bases bases;
+ bases.tbase = 0; // from unwind-dw2-fde-darwin.c:examine_objects()
+ bases.dbase = 0; // from unwind-dw2-fde-darwin.c:examine_objects()
+ bases.func = info.start_ip;
+ unw_word_t ip;
+ unw_get_reg(&cursor, UNW_REG_IP, &ip);
+ ip -= 1;
+ 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);
+ try_range.cfa = cfa;
+ return try_range;
+ }
+ }
+
+ return (struct frame_range){0, 0, 0, 0};
+}
+
+
+// This data structure assumes the number of
+// active alt handlers per frame is small.
+
+// for OBJC_DEBUG_ALT_HANDLERS, record the call to objc_addExceptionHandler.
+#define BACKTRACE_COUNT 46
+#define THREADNAME_COUNT 64
+struct alt_handler_debug {
+ uintptr_t token;
+ int backtraceSize;
+ void *backtrace[BACKTRACE_COUNT];
+ char thread[THREADNAME_COUNT];
+ char queue[THREADNAME_COUNT];
+};
+
+struct alt_handler_data {
+ struct frame_range frame;
+ objc_exception_handler fn;
+ void *context;
+ struct alt_handler_debug *debug;
+};
+
+struct alt_handler_list {
+ unsigned int allocated;
+ unsigned int used;
+ struct alt_handler_data *handlers;
+ struct alt_handler_list *next_DEBUGONLY;
+};
+
+static pthread_mutex_t DebugLock = PTHREAD_MUTEX_INITIALIZER;
+static struct alt_handler_list *DebugLists;
+static uintptr_t DebugCounter;
+
+void alt_handler_error(uintptr_t token) __attribute__((noinline));
+
+static struct alt_handler_list *
+fetch_handler_list(BOOL create)
+{
+ _objc_pthread_data *data = _objc_fetch_pthread_data(create);
+ if (!data) return NULL;
+
+ struct alt_handler_list *list = data->handlerList;
+ if (!list) {
+ if (!create) return NULL;
+ list = (struct alt_handler_list *)_calloc_internal(1, sizeof(*list));
+ data->handlerList = list;
+
+ if (DebugAltHandlers) {
+ // Save this list so the debug code can find it from other threads
+ pthread_mutex_lock(&DebugLock);
+ list->next_DEBUGONLY = DebugLists;
+ DebugLists = list;
+ pthread_mutex_unlock(&DebugLock);
+ }
+ }
+
+ return list;
+}
+
+
+void _destroyAltHandlerList(struct alt_handler_list *list)
+{
+ if (list) {
+ if (DebugAltHandlers) {
+ // Detach from the list-of-lists.
+ pthread_mutex_lock(&DebugLock);
+ struct alt_handler_list **listp = &DebugLists;
+ while (*listp && *listp != list) listp = &(*listp)->next_DEBUGONLY;
+ if (*listp) *listp = (*listp)->next_DEBUGONLY;
+ pthread_mutex_unlock(&DebugLock);
+ }
+
+ if (list->handlers) {
+ 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);
+ }
+}
+
+
+uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context)
+{
+ // Find the closest enclosing frame with objc catch handlers
+ struct frame_range target_frame = findHandler();
+ if (!target_frame.ip_start) {
+ // No suitable enclosing handler found.
+ return 0;
+ }
+
+ // Record this alt handler for the discovered frame.
+ struct alt_handler_list *list = fetch_handler_list(YES);
+ unsigned int i = 0;
+
+ if (list->used == list->allocated) {
+ list->allocated = list->allocated*2 ?: 4;
+ 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].frame.ip_start == 0 &&
+ list->handlers[i].frame.ip_end == 0 &&
+ list->handlers[i].frame.cfa == 0)
+ {
+ break;
+ }
+ }
+ if (i == list->allocated) {
+ _objc_fatal("alt handlers in objc runtime are buggy!");
+ }
+ }
+
+ struct alt_handler_data *data = &list->handlers[i];
+
+ data->frame = target_frame;
+ data->fn = fn;
+ data->context = context;
+ list->used++;
+
+ uintptr_t token = i+1;
+
+ if (DebugAltHandlers) {
+ // Record backtrace in case this handler is misused later.
+ pthread_mutex_lock(&DebugLock);
+
+ token = DebugCounter++;
+ if (token == 0) token = DebugCounter++;
+
+ if (!data->debug) {
+ data->debug = (struct alt_handler_debug *)
+ _calloc_internal(sizeof(*data->debug), 1);
+ } else {
+ bzero(data->debug, sizeof(*data->debug));
+ }
+
+ pthread_getname_np(pthread_self(), data->debug->thread, THREADNAME_COUNT);
+ strlcpy(data->debug->queue,
+ dispatch_queue_get_label(dispatch_get_current_queue()),
+ THREADNAME_COUNT);
+ data->debug->backtraceSize =
+ backtrace(data->debug->backtrace, BACKTRACE_COUNT);
+ data->debug->token = token;
+
+ pthread_mutex_unlock(&DebugLock);
+ }
+
+ if (PrintAltHandlers) {
+ _objc_inform("ALT HANDLERS: installing alt handler #%lu %p(%p) on "
+ "frame [ip=%p..%p sp=%p]", (unsigned long)token,
+ 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) {
+ static int warned = 0;
+ if (!warned) {
+ _objc_inform("ALT HANDLERS: *** over 1000 alt handlers installed; "
+ "this is probably a bug");
+ warned = 1;
+ }
+ }
+
+ return token;
+}
+
+
+void objc_removeExceptionHandler(uintptr_t token)
+{
+ if (!token) {
+ // objc_addExceptionHandler failed
+ return;
+ }
+
+ struct alt_handler_list *list = fetch_handler_list(NO);
+ if (!list || !list->handlers) {
+ // no alt handlers active
+ alt_handler_error(token);
+ __builtin_trap();
+ }
+
+ uintptr_t i = token-1;
+
+ if (DebugAltHandlers) {
+ // search for the token instead of using token-1
+ for (i = 0; i < list->allocated; i++) {
+ struct alt_handler_data *data = &list->handlers[i];
+ if (data->debug && data->debug->token == token) break;
+ }
+ }
+
+ if (i >= list->allocated) {
+ // token out of range
+ alt_handler_error(token);
+ __builtin_trap();
+ }
+
+ struct alt_handler_data *data = &list->handlers[i];
+
+ 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();
+ }
+
+ 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->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--;
+}
+
+void objc_alt_handler_error(void) __attribute__((noinline));
+
+void alt_handler_error(uintptr_t token)
+{
+ if (!DebugAltHandlers) {
+ _objc_inform_now_and_on_crash
+ ("objc_removeExceptionHandler() called with unknown alt handler; "
+ "this is probably a bug in multithreaded AppKit use. "
+ "Set environment variable OBJC_DEBUG_ALT_HANDLERS=YES "
+ "or break in objc_alt_handler_error() to debug.");
+ objc_alt_handler_error();
+ }
+
+ pthread_mutex_lock(&DebugLock);
+
+ // Search other threads' alt handler lists for this handler.
+ struct alt_handler_list *list;
+ for (list = DebugLists; list; list = list->next_DEBUGONLY) {
+ unsigned h;
+ for (h = 0; h < list->allocated; h++) {
+ struct alt_handler_data *data = &list->handlers[h];
+ if (data->debug && data->debug->token == token) {
+ // found it
+ int i;
+
+ // Build a string from the recorded backtrace
+ char *symbolString;
+ char **symbols =
+ backtrace_symbols(data->debug->backtrace,
+ data->debug->backtraceSize);
+ size_t len = 1;
+ for (i = 0; i < data->debug->backtraceSize; i++){
+ len += 4 + strlen(symbols[i]) + 1;
+ }
+ symbolString = (char *)_calloc_internal(len, 1);
+ for (i = 0; i < data->debug->backtraceSize; i++){
+ strcat(symbolString, " ");
+ strcat(symbolString, symbols[i]);
+ strcat(symbolString, "\n");
+ }
+
+ free(symbols);
+
+ _objc_inform_now_and_on_crash
+ ("objc_removeExceptionHandler() called with "
+ "unknown alt handler; this is probably a bug in "
+ "multithreaded AppKit use. \n"
+ "The matching objc_addExceptionHandler() was called by:\n"
+ "Thread '%s': Dispatch queue: '%s': \n%s",
+ data->debug->thread, data->debug->queue, symbolString);
+
+ pthread_mutex_unlock(&DebugLock);
+ _free_internal(symbolString);
+
+ objc_alt_handler_error();
+ }
+ }
+ }
+
+ pthread_mutex_lock(&DebugLock);
+
+ // not found
+ _objc_inform_now_and_on_crash
+ ("objc_removeExceptionHandler() called with unknown alt handler; "
+ "this is probably a bug in multithreaded AppKit use");
+ objc_alt_handler_error();
+}
+
+void objc_alt_handler_error(void)
+{
+ __builtin_trap();
+}
+
+// called in order registered, to match 32-bit _NSAddAltHandler2
+// fixme reverse registration order matches c++ destructors better
+static void call_alt_handlers(struct _Unwind_Context *ctx)
+{
+ uintptr_t ip = _Unwind_GetIP(ctx) - 1;
+ uintptr_t cfa = _Unwind_GetCFA(ctx);
+ unsigned int i;
+
+ struct alt_handler_list *list = fetch_handler_list(NO);
+ if (!list || list->used == 0) return;
+
+ for (i = 0; i < list->allocated; i++) {
+ struct alt_handler_data *data = &list->handlers[i];
+ 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;
+ bzero(data, sizeof(*data));
+ list->used--;
+ if (PrintExceptions || PrintAltHandlers) {
+ _objc_inform("EXCEPTIONS: calling alt handler %p(%p) from "
+ "frame [ip=%p..%p sp=%p]", copy.fn, copy.context,
+ (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);
+ }
+ }
+}
+
+// SUPPORT_ALT_HANDLERS
+#endif
+
+
+/***********************************************************************
+* exception_init
+* Initialize libobjc's exception handling system.
+* Called by map_images().
+**********************************************************************/
+void exception_init(void)
+{
+ old_terminate = std::set_terminate(&_objc_terminate);
+}
+
+
+// __OBJC2__
+#endif
+++ /dev/null
-/*
- * Copyright (c) 2010 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <malloc/malloc.h>
-#include <assert.h>
-#include "runtime.h"
-#include "objc-os.h"
-#include "objc-private.h"
-#include "message.h"
-#if SUPPORT_GC
-#include "auto_zone.h"
-#endif
-
-enum {
- // external references to data segment objects all use this type
- OBJC_XREF_TYPE_STATIC = 3,
-
- OBJC_XREF_TYPE_MASK = 3
-};
-
-// Macros to encode/decode reference values and types.
-#define encode_pointer_and_type(pointer, type) (~((uintptr_t)(pointer) | type))
-#define decode_pointer(encoded) ((id)((~(encoded)) & (~OBJC_XREF_TYPE_MASK)))
-#define decode_type(encoded) ((~(encoded)) & OBJC_XREF_TYPE_MASK)
-#define encode_index_and_type(index, type) (~((index<<3) | type))
-#define decode_index(encoded) ((~encoded)>>3)
-
-#if SUPPORT_GC
-
-typedef struct {
- objc_xref_type_t _type; // type of list.
- dispatch_queue_t _synchronizer; // a reader/write lock
- __strong void **_buffer; // a retained all pointers block
- size_t _size; // number of pointers that fit in _list (buffer size)
- size_t _count; // count of pointers in _list (in use count)
- size_t _search; // lowest index in list which *might* be unused
-} external_ref_list;
-
-static external_ref_list _xref_lists[2];
-
-#define is_strong(list) (list->_type == OBJC_XREF_STRONG)
-#define is_weak(list) (list->_type == OBJC_XREF_WEAK)
-
-inline static size_t _index_for_type(objc_xref_type_t ref_type) {
- assert(ref_type == OBJC_XREF_STRONG || ref_type == OBJC_XREF_WEAK);
- return (ref_type - 1);
-}
-
-static void _initialize_gc() {
- static dispatch_once_t init_guard;
- dispatch_once(&init_guard, ^{
- external_ref_list *_strong_list = &_xref_lists[_index_for_type(OBJC_XREF_STRONG)];
- _strong_list->_type = OBJC_XREF_STRONG;
- _strong_list->_synchronizer = dispatch_queue_create("OBJC_XREF_STRONG synchronizer", DISPATCH_QUEUE_CONCURRENT);
-
- external_ref_list *_weak_list = &_xref_lists[_index_for_type(OBJC_XREF_WEAK)];
- _weak_list->_type = OBJC_XREF_WEAK;
- _weak_list->_synchronizer = dispatch_queue_create("OBJC_XREF_WEAK synchronizer", DISPATCH_QUEUE_CONCURRENT);
- });
-}
-
-#define EMPTY_SLOT ((void*)0x1)
-
-// grow the buffer by one page
-static bool _grow_list(external_ref_list *list) {
- auto_memory_type_t memory_type = (is_strong(list) ? AUTO_MEMORY_ALL_POINTERS : AUTO_MEMORY_ALL_WEAK_POINTERS);
- size_t new_size = list->_size + PAGE_SIZE / sizeof(void *);
- // auto_realloc() has been enhanced to handle strong and weak memory.
- void **new_list = (void **)(list->_buffer ? malloc_zone_realloc(gc_zone, list->_buffer, new_size * sizeof(void *)) : auto_zone_allocate_object(gc_zone, new_size * sizeof(void *), memory_type, false, false));
- if (!new_list) _objc_fatal("unable to allocate, size = %ld\n", new_size);
-
- list->_search = list->_size;
- // Fill the newly allocated space with empty slot tokens.
- for (size_t index = list->_size; index < new_size; ++index)
- new_list[index] = EMPTY_SLOT;
- list->_size = new_size;
- auto_zone_root_write_barrier(gc_zone, &list->_buffer, new_list);
- return true;
-}
-
-
-// find an unused slot in the list, growing the list if necessary
-static size_t _find_unused_index(external_ref_list *list) {
- size_t index;
- if (list->_size == list->_count) {
- _grow_list(list);
- }
- // find the lowest unused index in _list
- index = list->_search;
- while (list->_buffer[index] != EMPTY_SLOT)
- index++;
- // mark the slot as no longer empty, good form for weak slots.
- list->_buffer[index] = NULL;
- return index;
-}
-
-
-// return the strong or weak list
-inline static external_ref_list *_list_for_type(objc_xref_type_t ref_type) {
- return &_xref_lists[_index_for_type(ref_type)];
-}
-
-
-// create a GC external reference
-PRIVATE_EXTERN objc_xref_t _object_addExternalReference_gc(id obj, objc_xref_type_t ref_type) {
- _initialize_gc();
- __block size_t index;
- objc_xref_t xref;
-
- if (auto_zone_is_valid_pointer(gc_zone, obj)) {
- external_ref_list *list = _list_for_type(ref_type);
-
- // writer lock
- dispatch_barrier_sync(list->_synchronizer, (dispatch_block_t)^{
- index = _find_unused_index(list);
- if (ref_type == OBJC_XREF_STRONG) {
- auto_zone_set_write_barrier(gc_zone, &list->_buffer[index], obj);
- } else {
- auto_assign_weak_reference(gc_zone, obj, (const void **)&list->_buffer[index], NULL);
- }
- list->_count++;
- });
- xref = encode_index_and_type(index, ref_type);
- } else {
- // data segment object
- xref = encode_pointer_and_type(obj, OBJC_XREF_TYPE_STATIC);
- }
- return xref;
-}
-
-PRIVATE_EXTERN id _object_readExternalReference_gc(objc_xref_t ref) {
- _initialize_gc();
- __block id result;
- objc_xref_type_t ref_type = decode_type(ref);
- if (ref_type != OBJC_XREF_TYPE_STATIC) {
- size_t index = decode_index(ref);
- external_ref_list *list = _list_for_type(ref_type);
-
- dispatch_sync(list->_synchronizer, ^{
- if (index >= list->_size) {
- _objc_fatal("attempted to resolve invalid external reference\n");
- }
- if (ref_type == OBJC_XREF_STRONG)
- result = (id)list->_buffer[index];
- else
- result = (id)auto_read_weak_reference(gc_zone, &list->_buffer[index]);
- if (result == (id)EMPTY_SLOT)
- _objc_fatal("attempted to resolve unallocated external reference\n");
- });
- } else {
- // data segment object
- result = decode_pointer(ref);
- }
- return result;
-}
-
-PRIVATE_EXTERN void _object_removeExternalReference_gc(objc_xref_t ref) {
- _initialize_gc();
- objc_xref_type_t ref_type = decode_type(ref);
- if (ref_type != OBJC_XREF_TYPE_STATIC) {
- size_t index = decode_index(ref);
- external_ref_list *list = _list_for_type(ref_type);
-
- dispatch_barrier_sync(list->_synchronizer, ^{
- if (index >= list->_size) {
- _objc_fatal("attempted to destroy invalid external reference\n");
- }
- id old_value;
- if (ref_type == OBJC_XREF_STRONG) {
- old_value = (id)list->_buffer[index];
- } else {
- old_value = (id)auto_read_weak_reference(gc_zone, &list->_buffer[index]);
- auto_assign_weak_reference(gc_zone, NULL, (const void **)&list->_buffer[index], NULL);
- }
- list->_buffer[index] = EMPTY_SLOT;
- if (old_value == (id)EMPTY_SLOT)
- _objc_fatal("attempted to destroy unallocated external reference\n");
- list->_count--;
- if (list->_search > index)
- list->_search = index;
- });
- } else {
- // nothing for data segment object
- }
-}
-
-// SUPPORT_GC
-#endif
-
-PRIVATE_EXTERN objc_xref_t _object_addExternalReference_rr(id obj, objc_xref_type_t ref_type) {
- switch (ref_type) {
- case OBJC_XREF_STRONG:
- objc_msgSend(obj, SEL_retain);
- break;
- case OBJC_XREF_WEAK:
- break;
- default:
- _objc_fatal("invalid external reference type: %d", (int)ref_type);
- break;
- }
- return encode_pointer_and_type(obj, ref_type);
-}
-
-PRIVATE_EXTERN id _object_readExternalReference_rr(objc_xref_t ref) {
- id obj = decode_pointer(ref);
- return obj;
-}
-
-PRIVATE_EXTERN void _object_removeExternalReference_rr(objc_xref_t ref) {
- id obj = decode_pointer(ref);
- objc_xref_type_t ref_type = decode_type(ref);
- switch (ref_type) {
- case OBJC_XREF_STRONG:
- objc_msgSend(obj, SEL_release);
- break;
- case OBJC_XREF_WEAK:
- break;
- default:
- _objc_fatal("invalid external reference type: %d", (int)ref_type);
- break;
- }
-}
-
-objc_xref_t _object_addExternalReference(id obj, objc_xref_t type) {
-#if SUPPORT_GC
- if (UseGC)
- return _object_addExternalReference_gc(obj, type);
- else
-#endif
- return _object_addExternalReference_rr(obj, type);
-}
-
-id _object_readExternalReference(objc_xref_t ref) {
-#if SUPPORT_GC
- if (UseGC)
- return _object_readExternalReference_gc(ref);
- else
-#endif
- return _object_readExternalReference_rr(ref);
-}
-
-void _object_removeExternalReference(objc_xref_t ref) {
-#if SUPPORT_GC
- if (UseGC)
- _object_removeExternalReference_gc(ref);
- else
-#endif
- _object_removeExternalReference_rr(ref);
-}
-
-uintptr_t _object_getExternalHash(id object) {
-#if SUPPORT_GC
- if (UseCompaction)
- return auto_zone_get_associative_hash(gc_zone, object);
- else
-#endif
- return (uintptr_t)object;
-}
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <malloc/malloc.h>
+#include <assert.h>
+#include "runtime.h"
+#include "objc-os.h"
+#include "objc-private.h"
+#include "message.h"
+#if SUPPORT_GC
+#include "auto_zone.h"
+#endif
+
+enum {
+ // external references to data segment objects all use this type
+ OBJC_XREF_TYPE_STATIC = 3,
+
+ OBJC_XREF_TYPE_MASK = 3
+};
+
+// Macros to encode/decode reference values and types.
+#define encode_pointer_and_type(pointer, type) (~((uintptr_t)(pointer) | type))
+#define decode_pointer(encoded) ((id)((~(encoded)) & (~OBJC_XREF_TYPE_MASK)))
+#define decode_type(encoded) ((~(encoded)) & OBJC_XREF_TYPE_MASK)
+#define encode_index_and_type(index, type) (~((index<<3) | type))
+#define decode_index(encoded) ((~encoded)>>3)
+
+#if SUPPORT_GC
+
+typedef struct {
+ objc_xref_type_t _type; // type of list.
+ dispatch_queue_t _synchronizer; // a reader/write lock
+ __strong void **_buffer; // a retained all pointers block
+ size_t _size; // number of pointers that fit in _list (buffer size)
+ size_t _count; // count of pointers in _list (in use count)
+ size_t _search; // lowest index in list which *might* be unused
+} external_ref_list;
+
+static external_ref_list _xref_lists[2];
+
+#define is_strong(list) (list->_type == OBJC_XREF_STRONG)
+#define is_weak(list) (list->_type == OBJC_XREF_WEAK)
+
+inline static size_t _index_for_type(objc_xref_type_t ref_type) {
+ assert(ref_type == OBJC_XREF_STRONG || ref_type == OBJC_XREF_WEAK);
+ return (ref_type - 1);
+}
+
+static void _initialize_gc() {
+ static dispatch_once_t init_guard;
+ dispatch_once(&init_guard, ^{
+ external_ref_list *_strong_list = &_xref_lists[_index_for_type(OBJC_XREF_STRONG)];
+ _strong_list->_type = OBJC_XREF_STRONG;
+ _strong_list->_synchronizer = dispatch_queue_create("OBJC_XREF_STRONG synchronizer", DISPATCH_QUEUE_CONCURRENT);
+
+ external_ref_list *_weak_list = &_xref_lists[_index_for_type(OBJC_XREF_WEAK)];
+ _weak_list->_type = OBJC_XREF_WEAK;
+ _weak_list->_synchronizer = dispatch_queue_create("OBJC_XREF_WEAK synchronizer", DISPATCH_QUEUE_CONCURRENT);
+ });
+}
+
+#define EMPTY_SLOT ((void*)0x1)
+
+// grow the buffer by one page
+static bool _grow_list(external_ref_list *list) {
+ auto_memory_type_t memory_type = (is_strong(list) ? AUTO_MEMORY_ALL_POINTERS : AUTO_MEMORY_ALL_WEAK_POINTERS);
+ size_t new_size = list->_size + PAGE_SIZE / sizeof(void *);
+ // auto_realloc() has been enhanced to handle strong and weak memory.
+ void **new_list = (void **)(list->_buffer ? malloc_zone_realloc(gc_zone, list->_buffer, new_size * sizeof(void *)) : auto_zone_allocate_object(gc_zone, new_size * sizeof(void *), memory_type, false, false));
+ if (!new_list) _objc_fatal("unable to allocate, size = %ld\n", new_size);
+
+ list->_search = list->_size;
+ // Fill the newly allocated space with empty slot tokens.
+ for (size_t index = list->_size; index < new_size; ++index)
+ new_list[index] = EMPTY_SLOT;
+ list->_size = new_size;
+ auto_zone_root_write_barrier(gc_zone, &list->_buffer, new_list);
+ return true;
+}
+
+
+// find an unused slot in the list, growing the list if necessary
+static size_t _find_unused_index(external_ref_list *list) {
+ size_t index;
+ if (list->_size == list->_count) {
+ _grow_list(list);
+ }
+ // find the lowest unused index in _list
+ index = list->_search;
+ while (list->_buffer[index] != EMPTY_SLOT)
+ index++;
+ // mark the slot as no longer empty, good form for weak slots.
+ list->_buffer[index] = NULL;
+ return index;
+}
+
+
+// return the strong or weak list
+inline static external_ref_list *_list_for_type(objc_xref_type_t ref_type) {
+ return &_xref_lists[_index_for_type(ref_type)];
+}
+
+
+// create a GC external reference
+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;
+
+ if (auto_zone_is_valid_pointer(gc_zone, obj)) {
+ external_ref_list *list = _list_for_type(ref_type);
+
+ // writer lock
+ dispatch_barrier_sync(list->_synchronizer, (dispatch_block_t)^{
+ index = _find_unused_index(list);
+ if (ref_type == OBJC_XREF_STRONG) {
+ auto_zone_set_write_barrier(gc_zone, &list->_buffer[index], obj);
+ } else {
+ auto_assign_weak_reference(gc_zone, obj, (const void **)&list->_buffer[index], NULL);
+ }
+ list->_count++;
+ });
+ xref = encode_index_and_type(index, ref_type);
+ } else {
+ // data segment object
+ xref = encode_pointer_and_type(obj, OBJC_XREF_TYPE_STATIC);
+ }
+ return xref;
+}
+
+OBJC_EXTERN
+id _object_readExternalReference_gc(objc_xref_t ref) {
+ _initialize_gc();
+ __block id result;
+ objc_xref_type_t ref_type = decode_type(ref);
+ if (ref_type != OBJC_XREF_TYPE_STATIC) {
+ size_t index = decode_index(ref);
+ external_ref_list *list = _list_for_type(ref_type);
+
+ dispatch_sync(list->_synchronizer, ^{
+ if (index >= list->_size) {
+ _objc_fatal("attempted to resolve invalid external reference\n");
+ }
+ if (ref_type == OBJC_XREF_STRONG)
+ result = (id)list->_buffer[index];
+ else
+ result = (id)auto_read_weak_reference(gc_zone, &list->_buffer[index]);
+ if (result == (id)EMPTY_SLOT)
+ _objc_fatal("attempted to resolve unallocated external reference\n");
+ });
+ } else {
+ // data segment object
+ result = decode_pointer(ref);
+ }
+ return result;
+}
+
+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) {
+ size_t index = decode_index(ref);
+ external_ref_list *list = _list_for_type(ref_type);
+
+ dispatch_barrier_sync(list->_synchronizer, ^{
+ if (index >= list->_size) {
+ _objc_fatal("attempted to destroy invalid external reference\n");
+ }
+ id old_value;
+ if (ref_type == OBJC_XREF_STRONG) {
+ old_value = (id)list->_buffer[index];
+ } else {
+ old_value = (id)auto_read_weak_reference(gc_zone, &list->_buffer[index]);
+ auto_assign_weak_reference(gc_zone, NULL, (const void **)&list->_buffer[index], NULL);
+ }
+ list->_buffer[index] = EMPTY_SLOT;
+ if (old_value == (id)EMPTY_SLOT)
+ _objc_fatal("attempted to destroy unallocated external reference\n");
+ list->_count--;
+ if (list->_search > index)
+ list->_search = index;
+ });
+ } else {
+ // nothing for data segment object
+ }
+}
+
+// SUPPORT_GC
+#endif
+
+OBJC_EXTERN
+objc_xref_t _object_addExternalReference_rr(id obj, objc_xref_type_t ref_type) {
+ switch (ref_type) {
+ case OBJC_XREF_STRONG:
+ ((id(*)(id, SEL))objc_msgSend)(obj, SEL_retain);
+ break;
+ case OBJC_XREF_WEAK:
+ break;
+ default:
+ _objc_fatal("invalid external reference type: %d", (int)ref_type);
+ break;
+ }
+ return encode_pointer_and_type(obj, ref_type);
+}
+
+OBJC_EXTERN
+id _object_readExternalReference_rr(objc_xref_t ref) {
+ id obj = decode_pointer(ref);
+ return obj;
+}
+
+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:
+ ((void(*)(id, SEL))objc_msgSend)(obj, SEL_release);
+ break;
+ case OBJC_XREF_WEAK:
+ break;
+ default:
+ _objc_fatal("invalid external reference type: %d", (int)ref_type);
+ break;
+ }
+}
+
+objc_xref_t _object_addExternalReference(id obj, objc_xref_t type) {
+#if SUPPORT_GC
+ if (UseGC)
+ return _object_addExternalReference_gc(obj, type);
+ else
+#endif
+ return _object_addExternalReference_rr(obj, type);
+}
+
+id _object_readExternalReference(objc_xref_t ref) {
+#if SUPPORT_GC
+ if (UseGC)
+ return _object_readExternalReference_gc(ref);
+ else
+#endif
+ return _object_readExternalReference_rr(ref);
+}
+
+void _object_removeExternalReference(objc_xref_t ref) {
+#if SUPPORT_GC
+ if (UseGC)
+ _object_removeExternalReference_gc(ref);
+ else
+#endif
+ _object_removeExternalReference_rr(ref);
+}
+
+uintptr_t _object_getExternalHash(id object) {
+#if SUPPORT_GC
+ if (UseCompaction)
+ return auto_zone_get_associative_hash(gc_zone, object);
+ else
+#endif
+ return (uintptr_t)object;
+}
#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;
#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 *) \
// __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;
}
-PRIVATE_EXTERN struct old_protocol **
+struct old_protocol **
_getObjcProtocols(const header_info *hi, size_t *nprotos)
{
unsigned long size = 0;
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;
}
return NULL;
}
-PRIVATE_EXTERN BOOL
+BOOL
_hasObjcContents(const header_info *hi)
{
// Look for an __OBJC,* section other than __OBJC,__image_info
__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);
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;
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;
#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 *) \
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;
return NULL;
}
-PRIVATE_EXTERN BOOL
+BOOL
_hasObjcContents(const header_info *hi)
{
// Look for a __DATA,__objc* section other than __DATA,__objc_imageinfo
+++ /dev/null
-/*
- * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/***********************************************************************
-* objc-initialize.m
-* +initialize support
-**********************************************************************/
-
-/***********************************************************************
- * Thread-safety during class initialization (GrP 2001-9-24)
- *
- * Initial state: CLS_INITIALIZING and CLS_INITIALIZED both clear.
- * During initialization: CLS_INITIALIZING is set
- * After initialization: CLS_INITIALIZING clear and CLS_INITIALIZED set.
- * CLS_INITIALIZING and CLS_INITIALIZED are never set at the same time.
- * CLS_INITIALIZED is never cleared once set.
- *
- * Only one thread is allowed to actually initialize a class and send
- * +initialize. Enforced by allowing only one thread to set CLS_INITIALIZING.
- *
- * Additionally, threads trying to send messages to a class must wait for
- * +initialize to finish. During initialization of a class, that class's
- * method cache is kept empty. objc_msgSend will revert to
- * class_lookupMethodAndLoadCache, which checks CLS_INITIALIZED before
- * messaging. If CLS_INITIALIZED is clear but CLS_INITIALIZING is set,
- * the thread must block, unless it is the thread that started
- * initializing the class in the first place.
- *
- * Each thread keeps a list of classes it's initializing.
- * The global classInitLock is used to synchronize changes to CLS_INITIALIZED
- * and CLS_INITIALIZING: the transition to CLS_INITIALIZING must be
- * an atomic test-and-set with respect to itself and the transition
- * to CLS_INITIALIZED.
- * The global classInitWaitCond is used to block threads waiting for an
- * initialization to complete. The classInitLock synchronizes
- * condition checking and the condition variable.
- **********************************************************************/
-
-/***********************************************************************
- * +initialize deadlock case when a class is marked initializing while
- * its superclass is initialized. Solved by completely initializing
- * superclasses before beginning to initialize a class.
- *
- * OmniWeb class hierarchy:
- * OBObject
- * | ` OBPostLoader
- * OFObject
- * / \
- * OWAddressEntry OWController
- * |
- * OWConsoleController
- *
- * Thread 1 (evil testing thread):
- * initialize OWAddressEntry
- * super init OFObject
- * super init OBObject
- * [OBObject initialize] runs OBPostLoader, which inits lots of classes...
- * initialize OWConsoleController
- * super init OWController - wait for Thread 2 to finish OWController init
- *
- * Thread 2 (normal OmniWeb thread):
- * initialize OWController
- * super init OFObject - wait for Thread 1 to finish OFObject init
- *
- * deadlock!
- *
- * Solution: fully initialize super classes before beginning to initialize
- * a subclass. Then the initializing+initialized part of the class hierarchy
- * will be a contiguous subtree starting at the root, so other threads
- * can't jump into the middle between two initializing classes, and we won't
- * get stuck while a superclass waits for its subclass which waits for the
- * superclass.
- **********************************************************************/
-
-#include "objc-private.h"
-#include "message.h"
-#include "objc-initialize.h"
-
-/* classInitLock protects CLS_INITIALIZED and CLS_INITIALIZING, and
- * is signalled when any class is done initializing.
- * Threads that are waiting for a class to finish initializing wait on this. */
-static monitor_t classInitLock = MONITOR_INITIALIZER;
-
-
-/***********************************************************************
-* struct _objc_initializing_classes
-* Per-thread list of classes currently being initialized by that thread.
-* During initialization, that thread is allowed to send messages to that
-* class, but other threads have to wait.
-* The list is a simple array of metaclasses (the metaclass stores
-* the initialization state).
-**********************************************************************/
-typedef struct _objc_initializing_classes {
- int classesAllocated;
- Class *metaclasses;
-} _objc_initializing_classes;
-
-
-/***********************************************************************
-* _fetchInitializingClassList
-* Return the list of classes being initialized by this thread.
-* If create == YES, create the list when no classes are being initialized by this thread.
-* If create == NO, return NULL when no classes are being initialized by this thread.
-**********************************************************************/
-static _objc_initializing_classes *_fetchInitializingClassList(BOOL create)
-{
- _objc_pthread_data *data;
- _objc_initializing_classes *list;
- Class *classes;
-
- data = _objc_fetch_pthread_data(create);
- if (data == NULL) return NULL;
-
- list = data->initializingClasses;
- if (list == NULL) {
- if (!create) {
- return NULL;
- } else {
- list = _calloc_internal(1, sizeof(_objc_initializing_classes));
- data->initializingClasses = list;
- }
- }
-
- classes = list->metaclasses;
- if (classes == NULL) {
- // If _objc_initializing_classes exists, allocate metaclass array,
- // even if create == NO.
- // Allow 4 simultaneous class inits on this thread before realloc.
- list->classesAllocated = 4;
- classes = _calloc_internal(list->classesAllocated, sizeof(Class));
- list->metaclasses = classes;
- }
- return list;
-}
-
-
-/***********************************************************************
-* _destroyInitializingClassList
-* Deallocate memory used by the given initialization list.
-* 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) {
- if (list->metaclasses != NULL) {
- _free_internal(list->metaclasses);
- }
- _free_internal(list);
- }
-}
-
-
-/***********************************************************************
-* _thisThreadIsInitializingClass
-* Return TRUE if this thread is currently initializing the given class.
-**********************************************************************/
-static BOOL _thisThreadIsInitializingClass(Class cls)
-{
- int i;
-
- _objc_initializing_classes *list = _fetchInitializingClassList(NO);
- if (list) {
- cls = _class_getMeta(cls);
- for (i = 0; i < list->classesAllocated; i++) {
- if (cls == list->metaclasses[i]) return YES;
- }
- }
-
- // no list or not found in list
- return NO;
-}
-
-
-/***********************************************************************
-* _setThisThreadIsInitializingClass
-* Record that this thread is currently initializing the given class.
-* This thread will be allowed to send messages to the class, but
-* other threads will have to wait.
-**********************************************************************/
-static void _setThisThreadIsInitializingClass(Class cls)
-{
- int i;
- _objc_initializing_classes *list = _fetchInitializingClassList(YES);
- cls = _class_getMeta(cls);
-
- // paranoia: explicitly disallow duplicates
- for (i = 0; i < list->classesAllocated; i++) {
- if (cls == list->metaclasses[i]) {
- _objc_fatal("thread is already initializing this class!");
- return; // already the initializer
- }
- }
-
- for (i = 0; i < list->classesAllocated; i++) {
- if (0 == list->metaclasses[i]) {
- list->metaclasses[i] = cls;
- return;
- }
- }
-
- // class list is full - reallocate
- list->classesAllocated = list->classesAllocated * 2 + 1;
- list->metaclasses = _realloc_internal(list->metaclasses, list->classesAllocated * sizeof(Class));
- // zero out the new entries
- list->metaclasses[i++] = cls;
- for ( ; i < list->classesAllocated; i++) {
- list->metaclasses[i] = NULL;
- }
-}
-
-
-/***********************************************************************
-* _setThisThreadIsNotInitializingClass
-* Record that this thread is no longer initializing the given class.
-**********************************************************************/
-static void _setThisThreadIsNotInitializingClass(Class cls)
-{
- int i;
-
- _objc_initializing_classes *list = _fetchInitializingClassList(NO);
- if (list) {
- cls = _class_getMeta(cls);
- for (i = 0; i < list->classesAllocated; i++) {
- if (cls == list->metaclasses[i]) {
- list->metaclasses[i] = NULL;
- return;
- }
- }
- }
-
- // no list or not found in list
- _objc_fatal("thread is not initializing this class!");
-}
-
-
-typedef struct PendingInitialize {
- Class subclass;
- struct PendingInitialize *next;
-} PendingInitialize;
-
-static NXMapTable *pendingInitializeMap;
-
-/***********************************************************************
-* _finishInitializing
-* cls has completed its +initialize method, and so has its superclass.
-* Mark cls as initialized as well, then mark any of cls's subclasses
-* that have already finished their own +initialize methods.
-**********************************************************************/
-static void _finishInitializing(Class cls, Class supercls)
-{
- PendingInitialize *pending;
-
- monitor_assert_locked(&classInitLock);
- assert(!supercls || _class_isInitialized(supercls));
-
- if (PrintInitializing) {
- _objc_inform("INITIALIZE: %s is fully +initialized",
- _class_getName(cls));
- }
-
- // propagate finalization affinity.
- if (UseGC && supercls && _class_shouldFinalizeOnMainThread(supercls)) {
- _class_setFinalizeOnMainThread(cls);
- }
-
- // mark this class as fully +initialized
- _class_setInitialized(cls);
- monitor_notifyAll(&classInitLock);
- _setThisThreadIsNotInitializingClass(cls);
-
- // mark any subclasses that were merely waiting for this class
- if (!pendingInitializeMap) return;
- pending = NXMapGet(pendingInitializeMap, cls);
- if (!pending) return;
-
- NXMapRemove(pendingInitializeMap, cls);
-
- // Destroy the pending table if it's now empty, to save memory.
- if (NXCountMapTable(pendingInitializeMap) == 0) {
- NXFreeMapTable(pendingInitializeMap);
- pendingInitializeMap = NULL;
- }
-
- while (pending) {
- PendingInitialize *next = pending->next;
- if (pending->subclass) _finishInitializing(pending->subclass, cls);
- _free_internal(pending);
- pending = next;
- }
-}
-
-
-/***********************************************************************
-* _finishInitializingAfter
-* cls has completed its +initialize method, but its superclass has not.
-* Wait until supercls finishes before marking cls as initialized.
-**********************************************************************/
-static void _finishInitializingAfter(Class cls, Class supercls)
-{
- PendingInitialize *pending;
-
- monitor_assert_locked(&classInitLock);
-
- if (PrintInitializing) {
- _objc_inform("INITIALIZE: %s waiting for superclass +[%s initialize]",
- _class_getName(cls), _class_getName(supercls));
- }
-
- if (!pendingInitializeMap) {
- pendingInitializeMap =
- NXCreateMapTableFromZone(NXPtrValueMapPrototype,
- 10, _objc_internal_zone());
- // fixme pre-size this table for CF/NSObject +initialize
- }
-
- pending = _malloc_internal(sizeof(*pending));
- pending->subclass = cls;
- pending->next = NXMapGet(pendingInitializeMap, supercls);
- NXMapInsert(pendingInitializeMap, supercls, pending);
-}
-
-
-/***********************************************************************
-* class_initialize. Send the '+initialize' message on demand to any
-* uninitialized class. Force initialization of superclasses first.
-*
-* Called only from _class_lookupMethodAndLoadCache (or itself).
-**********************************************************************/
-PRIVATE_EXTERN void _class_initialize(Class 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);
- if (supercls && !_class_isInitialized(supercls)) {
- _class_initialize(supercls);
- }
-
- // Try to atomically set CLS_INITIALIZING.
- monitor_enter(&classInitLock);
- if (!_class_isInitialized(cls) && !_class_isInitializing(cls)) {
- _class_setInitializing(cls);
- reallyInitialize = YES;
- }
- monitor_exit(&classInitLock);
-
- if (reallyInitialize) {
- // We successfully set the CLS_INITIALIZING bit. Initialize the class.
-
- // Record that we're initializing this class so we can message it.
- _setThisThreadIsInitializingClass(cls);
-
- // Send the +initialize message.
- // Note that +initialize is sent to the superclass (again) if
- // this class doesn't implement +initialize. 2157218
- if (PrintInitializing) {
- _objc_inform("INITIALIZE: calling +[%s initialize]",
- _class_getName(cls));
- }
-
- ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
-
- if (PrintInitializing) {
- _objc_inform("INITIALIZE: finished +[%s initialize]",
- _class_getName(cls));
- }
-
- // Done initializing.
- // If the superclass is also done initializing, then update
- // the info bits and notify waiting threads.
- // If not, update them later. (This can happen if this +initialize
- // was itself triggered from inside a superclass +initialize.)
-
- monitor_enter(&classInitLock);
- if (!supercls || _class_isInitialized(supercls)) {
- _finishInitializing(cls, supercls);
- } else {
- _finishInitializingAfter(cls, supercls);
- }
- monitor_exit(&classInitLock);
- return;
- }
-
- else if (_class_isInitializing(cls)) {
- // We couldn't set INITIALIZING because INITIALIZING was already set.
- // If this thread set it earlier, continue normally.
- // If some other thread set it, block until initialize is done.
- // It's ok if INITIALIZING changes to INITIALIZED while we're here,
- // because we safely check for INITIALIZED inside the lock
- // before blocking.
- if (_thisThreadIsInitializingClass(cls)) {
- return;
- } else {
- monitor_enter(&classInitLock);
- while (!_class_isInitialized(cls)) {
- monitor_wait(&classInitLock);
- }
- monitor_exit(&classInitLock);
- return;
- }
- }
-
- else if (_class_isInitialized(cls)) {
- // Set CLS_INITIALIZING failed because someone else already
- // initialized the class. Continue normally.
- // NOTE this check must come AFTER the ISINITIALIZING case.
- // Otherwise: Another thread is initializing this class. ISINITIALIZED
- // is false. Skip this clause. Then the other thread finishes
- // initialization and sets INITIALIZING=no and INITIALIZED=yes.
- // Skip the ISINITIALIZING clause. Die horribly.
- return;
- }
-
- else {
- // We shouldn't be here.
- _objc_fatal("thread-safe class init in objc runtime is buggy!");
- }
-}
--- /dev/null
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/***********************************************************************
+* objc-initialize.m
+* +initialize support
+**********************************************************************/
+
+/***********************************************************************
+ * Thread-safety during class initialization (GrP 2001-9-24)
+ *
+ * Initial state: CLS_INITIALIZING and CLS_INITIALIZED both clear.
+ * During initialization: CLS_INITIALIZING is set
+ * After initialization: CLS_INITIALIZING clear and CLS_INITIALIZED set.
+ * CLS_INITIALIZING and CLS_INITIALIZED are never set at the same time.
+ * CLS_INITIALIZED is never cleared once set.
+ *
+ * Only one thread is allowed to actually initialize a class and send
+ * +initialize. Enforced by allowing only one thread to set CLS_INITIALIZING.
+ *
+ * Additionally, threads trying to send messages to a class must wait for
+ * +initialize to finish. During initialization of a class, that class's
+ * method cache is kept empty. objc_msgSend will revert to
+ * class_lookupMethodAndLoadCache, which checks CLS_INITIALIZED before
+ * messaging. If CLS_INITIALIZED is clear but CLS_INITIALIZING is set,
+ * the thread must block, unless it is the thread that started
+ * initializing the class in the first place.
+ *
+ * Each thread keeps a list of classes it's initializing.
+ * The global classInitLock is used to synchronize changes to CLS_INITIALIZED
+ * and CLS_INITIALIZING: the transition to CLS_INITIALIZING must be
+ * an atomic test-and-set with respect to itself and the transition
+ * to CLS_INITIALIZED.
+ * The global classInitWaitCond is used to block threads waiting for an
+ * initialization to complete. The classInitLock synchronizes
+ * condition checking and the condition variable.
+ **********************************************************************/
+
+/***********************************************************************
+ * +initialize deadlock case when a class is marked initializing while
+ * its superclass is initialized. Solved by completely initializing
+ * superclasses before beginning to initialize a class.
+ *
+ * OmniWeb class hierarchy:
+ * OBObject
+ * | ` OBPostLoader
+ * OFObject
+ * / \
+ * OWAddressEntry OWController
+ * |
+ * OWConsoleController
+ *
+ * Thread 1 (evil testing thread):
+ * initialize OWAddressEntry
+ * super init OFObject
+ * super init OBObject
+ * [OBObject initialize] runs OBPostLoader, which inits lots of classes...
+ * initialize OWConsoleController
+ * super init OWController - wait for Thread 2 to finish OWController init
+ *
+ * Thread 2 (normal OmniWeb thread):
+ * initialize OWController
+ * super init OFObject - wait for Thread 1 to finish OFObject init
+ *
+ * deadlock!
+ *
+ * Solution: fully initialize super classes before beginning to initialize
+ * a subclass. Then the initializing+initialized part of the class hierarchy
+ * will be a contiguous subtree starting at the root, so other threads
+ * can't jump into the middle between two initializing classes, and we won't
+ * get stuck while a superclass waits for its subclass which waits for the
+ * superclass.
+ **********************************************************************/
+
+#include "objc-private.h"
+#include "message.h"
+#include "objc-initialize.h"
+
+/* classInitLock protects CLS_INITIALIZED and CLS_INITIALIZING, and
+ * is signalled when any class is done initializing.
+ * Threads that are waiting for a class to finish initializing wait on this. */
+static monitor_t classInitLock = MONITOR_INITIALIZER;
+
+
+/***********************************************************************
+* struct _objc_initializing_classes
+* Per-thread list of classes currently being initialized by that thread.
+* During initialization, that thread is allowed to send messages to that
+* class, but other threads have to wait.
+* The list is a simple array of metaclasses (the metaclass stores
+* the initialization state).
+**********************************************************************/
+typedef struct _objc_initializing_classes {
+ int classesAllocated;
+ Class *metaclasses;
+} _objc_initializing_classes;
+
+
+/***********************************************************************
+* _fetchInitializingClassList
+* Return the list of classes being initialized by this thread.
+* If create == YES, create the list when no classes are being initialized by this thread.
+* If create == NO, return NULL when no classes are being initialized by this thread.
+**********************************************************************/
+static _objc_initializing_classes *_fetchInitializingClassList(BOOL create)
+{
+ _objc_pthread_data *data;
+ _objc_initializing_classes *list;
+ Class *classes;
+
+ data = _objc_fetch_pthread_data(create);
+ if (data == NULL) return NULL;
+
+ list = data->initializingClasses;
+ if (list == NULL) {
+ if (!create) {
+ return NULL;
+ } else {
+ list = (_objc_initializing_classes *)
+ _calloc_internal(1, sizeof(_objc_initializing_classes));
+ data->initializingClasses = list;
+ }
+ }
+
+ classes = list->metaclasses;
+ if (classes == NULL) {
+ // If _objc_initializing_classes exists, allocate metaclass array,
+ // even if create == NO.
+ // Allow 4 simultaneous class inits on this thread before realloc.
+ list->classesAllocated = 4;
+ classes = (Class *)
+ _calloc_internal(list->classesAllocated, sizeof(Class));
+ list->metaclasses = classes;
+ }
+ return list;
+}
+
+
+/***********************************************************************
+* _destroyInitializingClassList
+* Deallocate memory used by the given initialization list.
+* Any part of the list may be NULL.
+* Called from _objc_pthread_destroyspecific().
+**********************************************************************/
+
+void _destroyInitializingClassList(struct _objc_initializing_classes *list)
+{
+ if (list != NULL) {
+ if (list->metaclasses != NULL) {
+ _free_internal(list->metaclasses);
+ }
+ _free_internal(list);
+ }
+}
+
+
+/***********************************************************************
+* _thisThreadIsInitializingClass
+* Return TRUE if this thread is currently initializing the given class.
+**********************************************************************/
+static BOOL _thisThreadIsInitializingClass(Class cls)
+{
+ int i;
+
+ _objc_initializing_classes *list = _fetchInitializingClassList(NO);
+ if (list) {
+ cls = _class_getMeta(cls);
+ for (i = 0; i < list->classesAllocated; i++) {
+ if (cls == list->metaclasses[i]) return YES;
+ }
+ }
+
+ // no list or not found in list
+ return NO;
+}
+
+
+/***********************************************************************
+* _setThisThreadIsInitializingClass
+* Record that this thread is currently initializing the given class.
+* This thread will be allowed to send messages to the class, but
+* other threads will have to wait.
+**********************************************************************/
+static void _setThisThreadIsInitializingClass(Class cls)
+{
+ int i;
+ _objc_initializing_classes *list = _fetchInitializingClassList(YES);
+ cls = _class_getMeta(cls);
+
+ // paranoia: explicitly disallow duplicates
+ for (i = 0; i < list->classesAllocated; i++) {
+ if (cls == list->metaclasses[i]) {
+ _objc_fatal("thread is already initializing this class!");
+ return; // already the initializer
+ }
+ }
+
+ for (i = 0; i < list->classesAllocated; i++) {
+ if (0 == list->metaclasses[i]) {
+ list->metaclasses[i] = cls;
+ return;
+ }
+ }
+
+ // class list is full - reallocate
+ list->classesAllocated = list->classesAllocated * 2 + 1;
+ 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++) {
+ list->metaclasses[i] = NULL;
+ }
+}
+
+
+/***********************************************************************
+* _setThisThreadIsNotInitializingClass
+* Record that this thread is no longer initializing the given class.
+**********************************************************************/
+static void _setThisThreadIsNotInitializingClass(Class cls)
+{
+ int i;
+
+ _objc_initializing_classes *list = _fetchInitializingClassList(NO);
+ if (list) {
+ cls = _class_getMeta(cls);
+ for (i = 0; i < list->classesAllocated; i++) {
+ if (cls == list->metaclasses[i]) {
+ list->metaclasses[i] = NULL;
+ return;
+ }
+ }
+ }
+
+ // no list or not found in list
+ _objc_fatal("thread is not initializing this class!");
+}
+
+
+typedef struct PendingInitialize {
+ Class subclass;
+ struct PendingInitialize *next;
+} PendingInitialize;
+
+static NXMapTable *pendingInitializeMap;
+
+/***********************************************************************
+* _finishInitializing
+* cls has completed its +initialize method, and so has its superclass.
+* Mark cls as initialized as well, then mark any of cls's subclasses
+* that have already finished their own +initialize methods.
+**********************************************************************/
+static void _finishInitializing(Class cls, Class supercls)
+{
+ PendingInitialize *pending;
+
+ monitor_assert_locked(&classInitLock);
+ assert(!supercls || _class_isInitialized(supercls));
+
+ if (PrintInitializing) {
+ _objc_inform("INITIALIZE: %s is fully +initialized",
+ _class_getName(cls));
+ }
+
+ // propagate finalization affinity.
+ if (UseGC && supercls && _class_shouldFinalizeOnMainThread(supercls)) {
+ _class_setFinalizeOnMainThread(cls);
+ }
+
+ // mark this class as fully +initialized
+ _class_setInitialized(cls);
+ monitor_notifyAll(&classInitLock);
+ _setThisThreadIsNotInitializingClass(cls);
+
+ // mark any subclasses that were merely waiting for this class
+ if (!pendingInitializeMap) return;
+ pending = (PendingInitialize *)NXMapGet(pendingInitializeMap, cls);
+ if (!pending) return;
+
+ NXMapRemove(pendingInitializeMap, cls);
+
+ // Destroy the pending table if it's now empty, to save memory.
+ if (NXCountMapTable(pendingInitializeMap) == 0) {
+ NXFreeMapTable(pendingInitializeMap);
+ pendingInitializeMap = NULL;
+ }
+
+ while (pending) {
+ PendingInitialize *next = pending->next;
+ if (pending->subclass) _finishInitializing(pending->subclass, cls);
+ _free_internal(pending);
+ pending = next;
+ }
+}
+
+
+/***********************************************************************
+* _finishInitializingAfter
+* cls has completed its +initialize method, but its superclass has not.
+* Wait until supercls finishes before marking cls as initialized.
+**********************************************************************/
+static void _finishInitializingAfter(Class cls, Class supercls)
+{
+ PendingInitialize *pending;
+
+ monitor_assert_locked(&classInitLock);
+
+ if (PrintInitializing) {
+ _objc_inform("INITIALIZE: %s waiting for superclass +[%s initialize]",
+ _class_getName(cls), _class_getName(supercls));
+ }
+
+ if (!pendingInitializeMap) {
+ pendingInitializeMap =
+ NXCreateMapTableFromZone(NXPtrValueMapPrototype,
+ 10, _objc_internal_zone());
+ // fixme pre-size this table for CF/NSObject +initialize
+ }
+
+ pending = (PendingInitialize *)_malloc_internal(sizeof(*pending));
+ pending->subclass = cls;
+ pending->next = (PendingInitialize *)
+ NXMapGet(pendingInitializeMap, supercls);
+ NXMapInsert(pendingInitializeMap, supercls, pending);
+}
+
+
+/***********************************************************************
+* class_initialize. Send the '+initialize' message on demand to any
+* uninitialized class. Force initialization of superclasses first.
+*
+* Called only from _class_lookupMethodAndLoadCache (or itself).
+**********************************************************************/
+void _class_initialize(Class cls)
+{
+ assert(!_class_isMetaClass(cls));
+
+ Class supercls;
+ BOOL reallyInitialize = NO;
+
+ // Make sure super is done initializing BEFORE beginning to initialize cls.
+ // See note about deadlock above.
+ supercls = _class_getSuperclass(cls);
+ if (supercls && !_class_isInitialized(supercls)) {
+ _class_initialize(supercls);
+ }
+
+ // Try to atomically set CLS_INITIALIZING.
+ monitor_enter(&classInitLock);
+ if (!_class_isInitialized(cls) && !_class_isInitializing(cls)) {
+ _class_setInitializing(cls);
+ reallyInitialize = YES;
+ }
+ monitor_exit(&classInitLock);
+
+ if (reallyInitialize) {
+ // We successfully set the CLS_INITIALIZING bit. Initialize the class.
+
+ // Record that we're initializing this class so we can message it.
+ _setThisThreadIsInitializingClass(cls);
+
+ // Send the +initialize message.
+ // Note that +initialize is sent to the superclass (again) if
+ // this class doesn't implement +initialize. 2157218
+ if (PrintInitializing) {
+ _objc_inform("INITIALIZE: calling +[%s initialize]",
+ _class_getName(cls));
+ }
+
+ ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
+
+ if (PrintInitializing) {
+ _objc_inform("INITIALIZE: finished +[%s initialize]",
+ _class_getName(cls));
+ }
+
+ // Done initializing.
+ // If the superclass is also done initializing, then update
+ // the info bits and notify waiting threads.
+ // If not, update them later. (This can happen if this +initialize
+ // was itself triggered from inside a superclass +initialize.)
+
+ monitor_enter(&classInitLock);
+ if (!supercls || _class_isInitialized(supercls)) {
+ _finishInitializing(cls, supercls);
+ } else {
+ _finishInitializingAfter(cls, supercls);
+ }
+ monitor_exit(&classInitLock);
+ return;
+ }
+
+ else if (_class_isInitializing(cls)) {
+ // We couldn't set INITIALIZING because INITIALIZING was already set.
+ // If this thread set it earlier, continue normally.
+ // If some other thread set it, block until initialize is done.
+ // It's ok if INITIALIZING changes to INITIALIZED while we're here,
+ // because we safely check for INITIALIZED inside the lock
+ // before blocking.
+ if (_thisThreadIsInitializingClass(cls)) {
+ return;
+ } else {
+ monitor_enter(&classInitLock);
+ while (!_class_isInitialized(cls)) {
+ monitor_wait(&classInitLock);
+ }
+ monitor_exit(&classInitLock);
+ return;
+ }
+ }
+
+ else if (_class_isInitialized(cls)) {
+ // Set CLS_INITIALIZING failed because someone else already
+ // initialized the class. Continue normally.
+ // NOTE this check must come AFTER the ISINITIALIZING case.
+ // Otherwise: Another thread is initializing this class. ISINITIALIZED
+ // is false. Skip this clause. Then the other thread finishes
+ // initialization and sets INITIALIZING=no and INITIALIZED=yes.
+ // Skip the ISINITIALIZING clause. Die horribly.
+ return;
+ }
+
+ else {
+ // We shouldn't be here.
+ _objc_fatal("thread-safe class init in objc runtime is buggy!");
+ }
+}
__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);
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);
// 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)
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
__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
_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)
+++ /dev/null
-/*
- * Copyright (c) 2004-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@
- */
-
-#include <stdlib.h>
-#include <assert.h>
-
-#include "objc-private.h"
-
-/**********************************************************************
-* Object Layouts.
-*
-* Layouts are used by the garbage collector to identify references from
-* the object to other objects.
-*
-* Layout information is in the form of a '\0' terminated byte string.
-* Each byte contains a word skip count in the high nibble and a
-* consecutive references count in the low nibble. Counts that exceed 15 are
-* continued in the succeeding byte with a zero in the opposite nibble.
-* Objects that should be scanned conservatively will have a NULL layout.
-* Objects that have no references have a empty byte string.
-*
-* Example;
-*
-* For a class with pointers at offsets 4,12, 16, 32-128
-* the layout is { 0x11, 0x12, 0x3f, 0x0a, 0x00 } or
-* skip 1 - 1 reference (4)
-* skip 1 - 2 references (12, 16)
-* skip 3 - 15 references (32-88)
-* no skip - 10 references (92-128)
-* end
-*
-**********************************************************************/
-
-
-/**********************************************************************
-* compress_layout
-* Allocates and returns a compressed string matching the given layout bitmap.
-**********************************************************************/
-static unsigned char *
-compress_layout(const uint8_t *bits, size_t bitmap_bits, BOOL weak)
-{
- BOOL all_set = YES;
- BOOL none_set = YES;
- unsigned char *result;
-
- // overallocate a lot; reallocate at correct size later
- unsigned char * const layout = _calloc_internal(bitmap_bits + 1, 1);
- unsigned char *l = layout;
-
- size_t i = 0;
- while (i < bitmap_bits) {
- size_t skip = 0;
- size_t scan = 0;
-
- // Count one range each of skip and scan.
- while (i < bitmap_bits) {
- uint8_t bit = (uint8_t)((bits[i/8] >> (i % 8)) & 1);
- if (bit) break;
- i++;
- skip++;
- }
- while (i < bitmap_bits) {
- uint8_t bit = (uint8_t)((bits[i/8] >> (i % 8)) & 1);
- if (!bit) break;
- i++;
- scan++;
- none_set = NO;
- }
-
- // Record skip and scan
- if (skip) all_set = NO;
- if (scan) none_set = NO;
- while (skip > 0xf) {
- *l++ = 0xf0;
- skip -= 0xf;
- }
- if (skip || scan) {
- *l = (uint8_t)(skip << 4); // NOT incremented - merges with scan
- while (scan > 0xf) {
- *l++ |= 0x0f; // May merge with short skip; must calloc
- scan -= 0xf;
- }
- *l++ |= scan; // NOT checked for zero - always increments
- // May merge with short skip; must calloc
- }
- }
-
- // insert terminating byte
- *l++ = '\0';
-
- // return result
- if (none_set && weak) {
- result = NULL; // NULL weak layout means none-weak
- } else if (all_set && !weak) {
- result = NULL; // NULL ivar layout means all-scanned
- } else {
- result = (unsigned char *)_strdup_internal((char *)layout);
- }
- _free_internal(layout);
- return result;
-}
-
-
-static void set_bits(layout_bitmap bits, size_t which, size_t count)
-{
- // fixme optimize for byte/word at a time
- size_t bit;
- for (bit = which; bit < which + count && bit < bits.bitCount; bit++) {
- bits.bits[bit/8] |= 1 << (bit % 8);
- }
- if (bit == bits.bitCount && bit < which + count) {
- // couldn't fit full type in bitmap
- _objc_fatal("layout bitmap too short");
- }
-}
-
-static void clear_bits(layout_bitmap bits, size_t which, size_t count)
-{
- // fixme optimize for byte/word at a time
- size_t bit;
- for (bit = which; bit < which + count && bit < bits.bitCount; bit++) {
- bits.bits[bit/8] &= ~(1 << (bit % 8));
- }
- if (bit == bits.bitCount && bit < which + count) {
- // couldn't fit full type in bitmap
- _objc_fatal("layout bitmap too short");
- }
-}
-
-static void move_bits(layout_bitmap bits, size_t src, size_t dst,
- size_t count)
-{
- // fixme optimize for byte/word at a time
-
- if (dst == src) {
- return;
- }
- else if (dst > src) {
- // Copy backwards in case of overlap
- size_t pos = count;
- while (pos--) {
- size_t srcbit = src + pos;
- size_t dstbit = dst + pos;
- if (bits.bits[srcbit/8] & (1 << (srcbit % 8))) {
- bits.bits[dstbit/8] |= 1 << (dstbit % 8);
- } else {
- bits.bits[dstbit/8] &= ~(1 << (dstbit % 8));
- }
- }
- }
- else {
- // Copy forwards in case of overlap
- size_t pos;
- for (pos = 0; pos < count; pos++) {
- size_t srcbit = src + pos;
- size_t dstbit = dst + pos;
- if (bits.bits[srcbit/8] & (1 << (srcbit % 8))) {
- bits.bits[dstbit/8] |= 1 << (dstbit % 8);
- } else {
- bits.bits[dstbit/8] &= ~(1 << (dstbit % 8));
- }
- }
- }
-}
-
-// emacs autoindent hack - it doesn't like the loop in set_bits/clear_bits
-#if 0
-} }
-#endif
-
-
-static void decompress_layout(const unsigned char *layout_string, layout_bitmap bits)
-{
- unsigned char c;
- size_t bit = 0;
- while ((c = *layout_string++)) {
- unsigned char skip = (c & 0xf0) >> 4;
- unsigned char scan = (c & 0x0f);
- bit += skip;
- set_bits(bits, bit, scan);
- bit += scan;
- }
-}
-
-
-/***********************************************************************
-* layout_bitmap_create
-* Allocate a layout bitmap.
-* The new bitmap spans the given instance size bytes.
-* The start of the bitmap is filled from the given layout string (which
-* 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_create(const unsigned char *layout_string,
- size_t layoutStringInstanceSize,
- size_t instanceSize, BOOL weak)
-{
- layout_bitmap result;
- size_t words = instanceSize / sizeof(id);
-
- result.weak = weak;
- result.bitCount = words;
- result.bitsAllocated = words;
- result.bits = _calloc_internal((words+7)/8, 1);
-
- if (!layout_string) {
- if (!weak) {
- // NULL ivar layout means all-scanned
- // (but only up to layoutStringSize instance size)
- set_bits(result, 0, layoutStringInstanceSize/sizeof(id));
- } else {
- // NULL weak layout means none-weak.
- }
- } else {
- decompress_layout(layout_string, result);
- }
-
- return result;
-}
-
-
-/***********************************************************************
- * layout_bitmap_create_empty
- * Allocate a layout bitmap.
- * The new bitmap spans the given instance size bytes.
- * The bitmap is empty, to represent an object whose ivars are completely unscanned.
- * The returned bitmap must be freed with layout_bitmap_free().
- **********************************************************************/
-PRIVATE_EXTERN layout_bitmap
-layout_bitmap_create_empty(size_t instanceSize, BOOL weak)
-{
- layout_bitmap result;
- size_t words = instanceSize / sizeof(id);
-
- result.weak = weak;
- result.bitCount = words;
- result.bitsAllocated = words;
- result.bits = _calloc_internal((words+7)/8, 1);
-
- return result;
-}
-
-PRIVATE_EXTERN void
-layout_bitmap_free(layout_bitmap bits)
-{
- if (bits.bits) _free_internal(bits.bits);
-}
-
-PRIVATE_EXTERN const unsigned char *
-layout_string_create(layout_bitmap bits)
-{
- const unsigned char *result =
- compress_layout(bits.bits, bits.bitCount, bits.weak);
-
-#ifndef NDEBUG
- // paranoia: cycle to bitmap and back to string again, and compare
- layout_bitmap check = layout_bitmap_create(result, bits.bitCount*sizeof(id),
- bits.bitCount*sizeof(id), bits.weak);
- unsigned char *result2 =
- compress_layout(check.bits, check.bitCount, check.weak);
- if (result != result2 && 0 != strcmp((char*)result, (char *)result2)) {
- layout_bitmap_print(bits);
- layout_bitmap_print(check);
- _objc_fatal("libobjc bug: mishandled layout bitmap");
- }
- free(result2);
- layout_bitmap_free(check);
-#endif
-
- return result;
-}
-
-
-PRIVATE_EXTERN void
-layout_bitmap_set_ivar(layout_bitmap bits, const char *type, size_t offset)
-{
- // fixme only handles some types
- size_t bit = offset / sizeof(id);
-
- if (!type) return;
- if (type[0] == '@' || 0 == strcmp(type, "^@")) {
- // id
- // id *
- // Block ("@?")
- set_bits(bits, bit, 1);
- }
- else if (type[0] == '[') {
- // id[]
- char *t;
- unsigned long count = strtoul(type+1, &t, 10);
- if (t && t[0] == '@') {
- set_bits(bits, bit, count);
- }
- }
- else if (strchr(type, '@')) {
- _objc_inform("warning: failing to set GC layout for '%s'\n", type);
- }
-}
-
-
-
-/***********************************************************************
-* layout_bitmap_grow
-* Expand a layout bitmap to span newCount bits.
-* The new bits are undefined.
-**********************************************************************/
-PRIVATE_EXTERN void
-layout_bitmap_grow(layout_bitmap *bits, size_t newCount)
-{
- if (bits->bitCount >= newCount) return;
- bits->bitCount = 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->bitsAllocated = newAllocated;
- }
- assert(bits->bitsAllocated >= bits->bitCount);
- assert(bits->bitsAllocated >= newCount);
-}
-
-
-/***********************************************************************
-* layout_bitmap_slide
-* Slide the end of a layout bitmap farther from the start.
-* Slides bits [oldPos, bits.bitCount) to [newPos, bits.bitCount+newPos-oldPos)
-* Bits [oldPos, newPos) are zero-filled.
-* The bitmap is expanded and bitCount updated if necessary.
-* newPos >= oldPos.
-**********************************************************************/
-PRIVATE_EXTERN void
-layout_bitmap_slide(layout_bitmap *bits, size_t oldPos, size_t newPos)
-{
- size_t shift;
- size_t count;
-
- if (oldPos == newPos) return;
- if (oldPos > newPos) _objc_fatal("layout bitmap sliding backwards");
-
- shift = newPos - oldPos;
- count = bits->bitCount - oldPos;
- layout_bitmap_grow(bits, bits->bitCount + shift);
- move_bits(*bits, oldPos, newPos, count); // slide
- clear_bits(*bits, oldPos, shift); // zero-fill
-}
-
-
-/***********************************************************************
-* layout_bitmap_slide_anywhere
-* Slide the end of a layout bitmap relative to the start.
-* Like layout_bitmap_slide, but can slide backwards too.
-* The end of the bitmap is truncated.
-**********************************************************************/
-PRIVATE_EXTERN void
-layout_bitmap_slide_anywhere(layout_bitmap *bits, size_t oldPos, size_t newPos)
-{
- size_t shift;
- size_t count;
-
- if (oldPos == newPos) return;
-
- if (oldPos < newPos) {
- layout_bitmap_slide(bits, oldPos, newPos);
- return;
- }
-
- shift = oldPos - newPos;
- count = bits->bitCount - oldPos;
- move_bits(*bits, oldPos, newPos, count); // slide
- bits->bitCount -= shift;
-}
-
-
-/***********************************************************************
-* layout_bitmap_splat
-* Pastes the contents of bitmap src to the start of bitmap dst.
-* dst bits between the end of src and oldSrcInstanceSize are zeroed.
-* dst must be at least as long as src.
-* Returns YES if any of dst's bits were changed.
-**********************************************************************/
-PRIVATE_EXTERN BOOL
-layout_bitmap_splat(layout_bitmap dst, layout_bitmap src,
- size_t oldSrcInstanceSize)
-{
- BOOL changed;
- size_t oldSrcBitCount;
- size_t bit;
-
- if (dst.bitCount < src.bitCount) _objc_fatal("layout bitmap too short");
-
- changed = NO;
- oldSrcBitCount = oldSrcInstanceSize / sizeof(id);
-
- // fixme optimize for byte/word at a time
- for (bit = 0; bit < oldSrcBitCount; bit++) {
- int dstset = dst.bits[bit/8] & (1 << (bit % 8));
- int srcset = (bit < src.bitCount)
- ? src.bits[bit/8] & (1 << (bit % 8))
- : 0;
- if (dstset != srcset) {
- changed = YES;
- if (srcset) {
- dst.bits[bit/8] |= 1 << (bit % 8);
- } else {
- dst.bits[bit/8] &= ~(1 << (bit % 8));
- }
- }
- }
-
- return changed;
-}
-
-
-/***********************************************************************
-* layout_bitmap_or
-* Set dst=dst|src.
-* dst must be at least as long as src.
-* Returns YES if any of dst's bits were changed.
-**********************************************************************/
-PRIVATE_EXTERN BOOL
-layout_bitmap_or(layout_bitmap dst, layout_bitmap src, const char *msg)
-{
- BOOL changed = NO;
- size_t bit;
-
- if (dst.bitCount < src.bitCount) {
- _objc_fatal("layout_bitmap_or: layout bitmap too short%s%s",
- msg ? ": " : "", msg ? msg : "");
- }
-
- // fixme optimize for byte/word at a time
- for (bit = 0; bit < src.bitCount; bit++) {
- int dstset = dst.bits[bit/8] & (1 << (bit % 8));
- int srcset = src.bits[bit/8] & (1 << (bit % 8));
- if (srcset && !dstset) {
- changed = YES;
- dst.bits[bit/8] |= 1 << (bit % 8);
- }
- }
-
- return changed;
-}
-
-
-/***********************************************************************
-* layout_bitmap_clear
-* Set dst=dst&~src.
-* dst must be at least as long as src.
-* Returns YES if any of dst's bits were changed.
-**********************************************************************/
-PRIVATE_EXTERN BOOL
-layout_bitmap_clear(layout_bitmap dst, layout_bitmap src, const char *msg)
-{
- BOOL changed = NO;
- size_t bit;
-
- if (dst.bitCount < src.bitCount) {
- _objc_fatal("layout_bitmap_clear: layout bitmap too short%s%s",
- msg ? ": " : "", msg ? msg : "");
- }
-
- // fixme optimize for byte/word at a time
- for (bit = 0; bit < src.bitCount; bit++) {
- int dstset = dst.bits[bit/8] & (1 << (bit % 8));
- int srcset = src.bits[bit/8] & (1 << (bit % 8));
- if (srcset && dstset) {
- changed = YES;
- dst.bits[bit/8] &= ~(1 << (bit % 8));
- }
- }
-
- return changed;
-}
-
-
-PRIVATE_EXTERN void
-layout_bitmap_print(layout_bitmap bits)
-{
- size_t i;
- printf("%zu: ", bits.bitCount);
- for (i = 0; i < bits.bitCount; i++) {
- int set = bits.bits[i/8] & (1 << (i % 8));
- printf("%c", set ? '#' : '.');
- }
- printf("\n");
-}
-
-#if 0
-// The code below may be useful when interpreting ivar types more precisely.
-
-/**********************************************************************
-* mark_offset_for_layout
-*
-* Marks the appropriate bit in the bits array cooresponding to a the
-* offset of a reference. If we are scanning a nested pointer structure
-* then the bits array will be NULL then this function does nothing.
-*
-**********************************************************************/
-static void mark_offset_for_layout(long offset, long bits_size, unsigned char *bits) {
- // references are ignored if bits is NULL
- if (bits) {
- long slot = offset / sizeof(long);
-
- // determine byte index using (offset / 8 bits per byte)
- long i_byte = slot >> 3;
-
- // if the byte index is valid
- if (i_byte < bits_size) {
- // set the (offset / 8 bits per byte)th bit
- bits[i_byte] |= 1 << (slot & 7);
- } else {
- // offset not within instance size
- _objc_inform ("layout - offset exceeds instance size");
- }
- }
-}
-
-/**********************************************************************
-* skip_ivar_type_name
-*
-* Skip over the name of a field/class in an ivar type string. Names
-* are in the form of a double-quoted string. Returns the remaining
-* string.
-*
-**********************************************************************/
-static char *skip_ivar_type_name(char *type) {
- // current character
- char ch;
-
- // if there is an open quote
- if (*type == '\"') {
- // skip quote
- type++;
-
- // while no closing quote
- while ((ch = *type) != '\"') {
- // if end of string return end of string
- if (!ch) return type;
-
- // skip character
- type++;
- }
-
- // skip closing quote
- type++;
- }
-
- // return remaining string
- return type;
-}
-
-
-/**********************************************************************
-* skip_ivar_struct_name
-*
-* Skip over the name of a struct in an ivar type string. Names
-* may be followed by an equals sign. Returns the remaining string.
-*
-**********************************************************************/
-static char *skip_ivar_struct_name(char *type) {
- // get first character
- char ch = *type;
-
- if (ch == _C_UNDEF) {
- // skip undefined name
- type++;
- } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_') {
- // if alphabetic
-
- // scan alphanumerics
- do {
- // next character
- ch = *++type;
- } while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' || (ch >= '0' && ch <= '9'));
- } else {
- // no struct name present
- return type;
- }
-
- // skip equals sign
- if (*type == '=') type++;
-
- return type;
-}
-
-
-/**********************************************************************
-* scan_basic_ivar_type
-*
-* Determines the size and alignment of a basic ivar type. If the basic
-* type is a possible reference to another garbage collected type the
-* is_reference is set to true (false otherwise.) Returns the remaining
-* string.
-*
-**********************************************************************/
-static char *scan_ivar_type_for_layout(char *type, long offset, long bits_size, unsigned char *bits, long *next_offset);
-static char *scan_basic_ivar_type(char *type, long *size, long *alignment, BOOL *is_reference) {
- // assume it is a non-reference type
- *is_reference = NO;
-
- // get the first character (advancing string)
- const char *full_type = type;
- char ch = *type++;
-
- // GCC 4 uses for const type*.
- if (ch == _C_CONST) ch = *type++;
-
- // act on first character
- switch (ch) {
- case _C_ID: {
- // ID type
-
- // skip over optional class name
- type = skip_ivar_type_name(type);
-
- // size and alignment of an id type
- *size = sizeof(id);
- *alignment = __alignof(id);
-
- // is a reference type
- *is_reference = YES;
- break;
- }
- case _C_PTR: {
- // C pointer type
-
- // skip underlying type
- long ignored_offset;
- type = scan_ivar_type_for_layout(type, 0, 0, NULL, &ignored_offset);
-
- // size and alignment of a generic pointer type
- *size = sizeof(void *);
- *alignment = __alignof(void *);
-
- // is a reference type
- *is_reference = YES;
- break;
- }
- case _C_CHARPTR: {
- // C string
-
- // size and alignment of a char pointer type
- *size = sizeof(char *);
- *alignment = __alignof(char *);
-
- // is a reference type
- *is_reference = YES;
- break;
- }
- case _C_CLASS:
- case _C_SEL: {
- // classes and selectors are ignored for now
- *size = sizeof(void *);
- *alignment = __alignof(void *);
- break;
- }
- case _C_CHR:
- case _C_UCHR: {
- // char and unsigned char
- *size = sizeof(char);
- *alignment = __alignof(char);
- break;
- }
- case _C_SHT:
- case _C_USHT: {
- // short and unsigned short
- *size = sizeof(short);
- *alignment = __alignof(short);
- break;
- }
- case _C_ATOM:
- case _C_INT:
- case _C_UINT: {
- // int and unsigned int
- *size = sizeof(int);
- *alignment = __alignof(int);
- break;
- }
- case _C_LNG:
- case _C_ULNG: {
- // long and unsigned long
- *size = sizeof(long);
- *alignment = __alignof(long);
- break;
- }
- case _C_LNG_LNG:
- case _C_ULNG_LNG: {
- // long long and unsigned long long
- *size = sizeof(long long);
- *alignment = __alignof(long long);
- break;
- }
- case _C_VECTOR: {
- // vector
- *size = 16;
- *alignment = 16;
- break;
- }
- case _C_FLT: {
- // float
- *size = sizeof(float);
- *alignment = __alignof(float);
- break;
- }
- case _C_DBL: {
- // double
- *size = sizeof(double);
- *alignment = __alignof(double);
- break;
- }
- case _C_BFLD: {
- // bit field
-
- // get number of bits in bit field (advance type string)
- long lng = strtol(type, &type, 10);
-
- // while next type is a bit field
- while (*type == _C_BFLD) {
- // skip over _C_BFLD
- type++;
-
- // get next bit field length
- long next_lng = strtol(type, &type, 10);
-
- // if spans next word then align to next word
- if ((lng & ~31) != ((lng + next_lng) & ~31)) lng = (lng + 31) & ~31;
-
- // increment running length
- lng += next_lng;
-
- // skip over potential field name
- type = skip_ivar_type_name(type);
- }
-
- // determine number of bytes bits represent
- *size = (lng + 7) / 8;
-
- // byte alignment
- *alignment = __alignof(char);
- break;
- }
- case _C_BOOL: {
- // double
- *size = sizeof(BOOL);
- *alignment = __alignof(BOOL);
- break;
- }
- case _C_VOID: {
- // skip void types
- *size = 0;
- *alignment = __alignof(char);
- break;
- }
- case _C_UNDEF: {
- *size = 0;
- *alignment = __alignof(char);
- break;
- }
- default: {
- // unhandled type
- _objc_fatal("unrecognized character \'%c\' in ivar type: \"%s\"", ch, full_type);
- }
- }
-
- return type;
-}
-
-
-/**********************************************************************
-* scan_ivar_type_for_layout
-*
-* Scan an ivar type string looking for references. The offset indicates
-* where the ivar begins. bits is a byte array of size bits_size used to
-* contain the references bit map. next_offset is the offset beyond the
-* ivar. Returns the remaining string.
-*
-**********************************************************************/
-static char *scan_ivar_type_for_layout(char *type, long offset, long bits_size, unsigned char *bits, long *next_offset) {
- long size; // size of a basic type
- long alignment; // alignment of the basic type
- BOOL is_reference; // true if the type indicates a reference to a garbage collected object
-
- // get the first character
- char ch = *type;
-
- // GCC 4 uses for const type*.
- if (ch == _C_CONST) ch = *++type;
-
- // act on first character
- switch (ch) {
- case _C_ARY_B: {
- // array type
-
- // get the array length
- long lng = strtol(type + 1, &type, 10);
-
- // next type will be where to advance the type string once the array is processed
- char *next_type = type;
-
- // repeat the next type x lng
- if (!lng) {
- next_type = scan_ivar_type_for_layout(type, 0, 0, NULL, &offset);
- } else {
- while (lng--) {
- // repeatedly scan the same type
- next_type = scan_ivar_type_for_layout(type, offset, bits_size, bits, &offset);
- }
- }
-
- // advance the type now
- type = next_type;
-
- // after the end of the array
- *next_offset = offset;
-
- // advance over closing bracket
- if (*type == _C_ARY_E) type++;
- else _objc_inform("missing \'%c\' in ivar type.", _C_ARY_E);
-
- break;
- }
- case _C_UNION_B: {
- // union type
-
- // skip over possible union name
- type = skip_ivar_struct_name(type + 1);
-
- // need to accumulate the maximum element offset
- long max_offset = 0;
-
- // while not closing paren
- while ((ch = *type) && ch != _C_UNION_E) {
- // skip over potential field name
- type = skip_ivar_type_name(type);
-
- // scan type
- long union_offset;
- type = scan_ivar_type_for_layout(type, offset, bits_size, bits, &union_offset);
-
- // adjust the maximum element offset
- if (max_offset < union_offset) max_offset = union_offset;
- }
-
- // after the largest element
- *next_offset = max_offset;
-
- // advance over closing paren
- if (ch == _C_UNION_E) {
- type++;
- } else {
- _objc_inform("missing \'%c\' in ivar type", _C_UNION_E);
- }
-
- break;
- }
- case _C_STRUCT_B: {
- // struct type
-
- // skip over possible struct name
- type = skip_ivar_struct_name(type + 1);
-
- // while not closing brace
- while ((ch = *type) && ch != _C_STRUCT_E) {
- // skip over potential field name
- type = skip_ivar_type_name(type);
-
- // scan type
- type = scan_ivar_type_for_layout(type, offset, bits_size, bits, &offset);
- }
-
- // after the end of the struct
- *next_offset = offset;
-
- // advance over closing brace
- if (ch == _C_STRUCT_E) type++;
- else _objc_inform("missing \'%c\' in ivar type", _C_STRUCT_E);
-
- break;
- }
- default: {
- // basic type
-
- // scan type
- type = scan_basic_ivar_type(type, &size, &alignment, &is_reference);
-
- // create alignment mask
- alignment--;
-
- // align offset
- offset = (offset + alignment) & ~alignment;
-
- // if is a reference then mark in the bit map
- if (is_reference) mark_offset_for_layout(offset, bits_size, bits);
-
- // after the basic type
- *next_offset = offset + size;
- break;
- }
- }
-
- // return remainder of type string
- return type;
-}
-
-#endif
--- /dev/null
+/*
+ * Copyright (c) 2004-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@
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "objc-private.h"
+
+/**********************************************************************
+* Object Layouts.
+*
+* Layouts are used by the garbage collector to identify references from
+* the object to other objects.
+*
+* Layout information is in the form of a '\0' terminated byte string.
+* Each byte contains a word skip count in the high nibble and a
+* consecutive references count in the low nibble. Counts that exceed 15 are
+* continued in the succeeding byte with a zero in the opposite nibble.
+* Objects that should be scanned conservatively will have a NULL layout.
+* Objects that have no references have a empty byte string.
+*
+* Example;
+*
+* For a class with pointers at offsets 4,12, 16, 32-128
+* the layout is { 0x11, 0x12, 0x3f, 0x0a, 0x00 } or
+* skip 1 - 1 reference (4)
+* skip 1 - 2 references (12, 16)
+* skip 3 - 15 references (32-88)
+* no skip - 10 references (92-128)
+* end
+*
+**********************************************************************/
+
+
+/**********************************************************************
+* compress_layout
+* Allocates and returns a compressed string matching the given layout bitmap.
+**********************************************************************/
+static unsigned char *
+compress_layout(const uint8_t *bits, size_t bitmap_bits, BOOL weak)
+{
+ BOOL all_set = YES;
+ BOOL none_set = YES;
+ unsigned char *result;
+
+ // overallocate a lot; reallocate at correct size later
+ unsigned char * const layout = (unsigned char *)
+ _calloc_internal(bitmap_bits + 1, 1);
+ unsigned char *l = layout;
+
+ size_t i = 0;
+ while (i < bitmap_bits) {
+ size_t skip = 0;
+ size_t scan = 0;
+
+ // Count one range each of skip and scan.
+ while (i < bitmap_bits) {
+ uint8_t bit = (uint8_t)((bits[i/8] >> (i % 8)) & 1);
+ if (bit) break;
+ i++;
+ skip++;
+ }
+ while (i < bitmap_bits) {
+ uint8_t bit = (uint8_t)((bits[i/8] >> (i % 8)) & 1);
+ if (!bit) break;
+ i++;
+ scan++;
+ none_set = NO;
+ }
+
+ // Record skip and scan
+ if (skip) all_set = NO;
+ if (scan) none_set = NO;
+ while (skip > 0xf) {
+ *l++ = 0xf0;
+ skip -= 0xf;
+ }
+ if (skip || scan) {
+ *l = (uint8_t)(skip << 4); // NOT incremented - merges with scan
+ while (scan > 0xf) {
+ *l++ |= 0x0f; // May merge with short skip; must calloc
+ scan -= 0xf;
+ }
+ *l++ |= scan; // NOT checked for zero - always increments
+ // May merge with short skip; must calloc
+ }
+ }
+
+ // insert terminating byte
+ *l++ = '\0';
+
+ // return result
+ if (none_set && weak) {
+ result = NULL; // NULL weak layout means none-weak
+ } else if (all_set && !weak) {
+ result = NULL; // NULL ivar layout means all-scanned
+ } else {
+ result = (unsigned char *)_strdup_internal((char *)layout);
+ }
+ _free_internal(layout);
+ return result;
+}
+
+
+static void set_bits(layout_bitmap bits, size_t which, size_t count)
+{
+ // fixme optimize for byte/word at a time
+ size_t bit;
+ for (bit = which; bit < which + count && bit < bits.bitCount; bit++) {
+ bits.bits[bit/8] |= 1 << (bit % 8);
+ }
+ if (bit == bits.bitCount && bit < which + count) {
+ // couldn't fit full type in bitmap
+ _objc_fatal("layout bitmap too short");
+ }
+}
+
+static void clear_bits(layout_bitmap bits, size_t which, size_t count)
+{
+ // fixme optimize for byte/word at a time
+ size_t bit;
+ for (bit = which; bit < which + count && bit < bits.bitCount; bit++) {
+ bits.bits[bit/8] &= ~(1 << (bit % 8));
+ }
+ if (bit == bits.bitCount && bit < which + count) {
+ // couldn't fit full type in bitmap
+ _objc_fatal("layout bitmap too short");
+ }
+}
+
+static void move_bits(layout_bitmap bits, size_t src, size_t dst,
+ size_t count)
+{
+ // fixme optimize for byte/word at a time
+
+ if (dst == src) {
+ return;
+ }
+ else if (dst > src) {
+ // Copy backwards in case of overlap
+ size_t pos = count;
+ while (pos--) {
+ size_t srcbit = src + pos;
+ size_t dstbit = dst + pos;
+ if (bits.bits[srcbit/8] & (1 << (srcbit % 8))) {
+ bits.bits[dstbit/8] |= 1 << (dstbit % 8);
+ } else {
+ bits.bits[dstbit/8] &= ~(1 << (dstbit % 8));
+ }
+ }
+ }
+ else {
+ // Copy forwards in case of overlap
+ size_t pos;
+ for (pos = 0; pos < count; pos++) {
+ size_t srcbit = src + pos;
+ size_t dstbit = dst + pos;
+ if (bits.bits[srcbit/8] & (1 << (srcbit % 8))) {
+ bits.bits[dstbit/8] |= 1 << (dstbit % 8);
+ } else {
+ bits.bits[dstbit/8] &= ~(1 << (dstbit % 8));
+ }
+ }
+ }
+}
+
+// emacs autoindent hack - it doesn't like the loop in set_bits/clear_bits
+#if 0
+} }
+#endif
+
+
+static void decompress_layout(const unsigned char *layout_string, layout_bitmap bits)
+{
+ unsigned char c;
+ size_t bit = 0;
+ while ((c = *layout_string++)) {
+ unsigned char skip = (c & 0xf0) >> 4;
+ unsigned char scan = (c & 0x0f);
+ bit += skip;
+ set_bits(bits, bit, scan);
+ bit += scan;
+ }
+}
+
+
+/***********************************************************************
+* layout_bitmap_create
+* Allocate a layout bitmap.
+* The new bitmap spans the given instance size bytes.
+* The start of the bitmap is filled from the given layout string (which
+* spans an instance size of layoutStringSize); the rest is zero-filled.
+* The returned bitmap must be freed with layout_bitmap_free().
+**********************************************************************/
+layout_bitmap
+layout_bitmap_create(const unsigned char *layout_string,
+ size_t layoutStringInstanceSize,
+ size_t instanceSize, BOOL weak)
+{
+ layout_bitmap result;
+ size_t words = instanceSize / sizeof(id);
+
+ result.weak = weak;
+ result.bitCount = words;
+ result.bitsAllocated = words;
+ result.bits = (uint8_t *)_calloc_internal((words+7)/8, 1);
+
+ if (!layout_string) {
+ if (!weak) {
+ // NULL ivar layout means all-scanned
+ // (but only up to layoutStringSize instance size)
+ set_bits(result, 0, layoutStringInstanceSize/sizeof(id));
+ } else {
+ // NULL weak layout means none-weak.
+ }
+ } else {
+ decompress_layout(layout_string, result);
+ }
+
+ return result;
+}
+
+
+/***********************************************************************
+ * layout_bitmap_create_empty
+ * Allocate a layout bitmap.
+ * The new bitmap spans the given instance size bytes.
+ * The bitmap is empty, to represent an object whose ivars are completely unscanned.
+ * The returned bitmap must be freed with layout_bitmap_free().
+ **********************************************************************/
+layout_bitmap
+layout_bitmap_create_empty(size_t instanceSize, BOOL weak)
+{
+ layout_bitmap result;
+ size_t words = instanceSize / sizeof(id);
+
+ result.weak = weak;
+ result.bitCount = words;
+ result.bitsAllocated = words;
+ result.bits = (uint8_t *)_calloc_internal((words+7)/8, 1);
+
+ return result;
+}
+
+void
+layout_bitmap_free(layout_bitmap bits)
+{
+ if (bits.bits) _free_internal(bits.bits);
+}
+
+const unsigned char *
+layout_string_create(layout_bitmap bits)
+{
+ const unsigned char *result =
+ compress_layout(bits.bits, bits.bitCount, bits.weak);
+
+#ifndef NDEBUG
+ // paranoia: cycle to bitmap and back to string again, and compare
+ layout_bitmap check = layout_bitmap_create(result, bits.bitCount*sizeof(id),
+ bits.bitCount*sizeof(id), bits.weak);
+ unsigned char *result2 =
+ compress_layout(check.bits, check.bitCount, check.weak);
+ if (result != result2 && 0 != strcmp((char*)result, (char *)result2)) {
+ layout_bitmap_print(bits);
+ layout_bitmap_print(check);
+ _objc_fatal("libobjc bug: mishandled layout bitmap");
+ }
+ free(result2);
+ layout_bitmap_free(check);
+#endif
+
+ return result;
+}
+
+
+void
+layout_bitmap_set_ivar(layout_bitmap bits, const char *type, size_t offset)
+{
+ // fixme only handles some types
+ size_t bit = offset / sizeof(id);
+
+ if (!type) return;
+ if (type[0] == '@' || 0 == strcmp(type, "^@")) {
+ // id
+ // id *
+ // Block ("@?")
+ set_bits(bits, bit, 1);
+ }
+ else if (type[0] == '[') {
+ // id[]
+ char *t;
+ unsigned long count = strtoul(type+1, &t, 10);
+ if (t && t[0] == '@') {
+ set_bits(bits, bit, count);
+ }
+ }
+ else if (strchr(type, '@')) {
+ _objc_inform("warning: failing to set GC layout for '%s'\n", type);
+ }
+}
+
+
+
+/***********************************************************************
+* layout_bitmap_grow
+* Expand a layout bitmap to span newCount bits.
+* The new bits are undefined.
+**********************************************************************/
+void
+layout_bitmap_grow(layout_bitmap *bits, size_t newCount)
+{
+ if (bits->bitCount >= newCount) return;
+ bits->bitCount = newCount;
+ if (bits->bitsAllocated < newCount) {
+ size_t newAllocated = bits->bitsAllocated * 2;
+ if (newAllocated < newCount) newAllocated = newCount;
+ bits->bits = (uint8_t *)
+ _realloc_internal(bits->bits, (newAllocated+7) / 8);
+ bits->bitsAllocated = newAllocated;
+ }
+ assert(bits->bitsAllocated >= bits->bitCount);
+ assert(bits->bitsAllocated >= newCount);
+}
+
+
+/***********************************************************************
+* layout_bitmap_slide
+* Slide the end of a layout bitmap farther from the start.
+* Slides bits [oldPos, bits.bitCount) to [newPos, bits.bitCount+newPos-oldPos)
+* Bits [oldPos, newPos) are zero-filled.
+* The bitmap is expanded and bitCount updated if necessary.
+* newPos >= oldPos.
+**********************************************************************/
+void
+layout_bitmap_slide(layout_bitmap *bits, size_t oldPos, size_t newPos)
+{
+ size_t shift;
+ size_t count;
+
+ if (oldPos == newPos) return;
+ if (oldPos > newPos) _objc_fatal("layout bitmap sliding backwards");
+
+ shift = newPos - oldPos;
+ count = bits->bitCount - oldPos;
+ layout_bitmap_grow(bits, bits->bitCount + shift);
+ move_bits(*bits, oldPos, newPos, count); // slide
+ clear_bits(*bits, oldPos, shift); // zero-fill
+}
+
+
+/***********************************************************************
+* layout_bitmap_slide_anywhere
+* Slide the end of a layout bitmap relative to the start.
+* Like layout_bitmap_slide, but can slide backwards too.
+* The end of the bitmap is truncated.
+**********************************************************************/
+void
+layout_bitmap_slide_anywhere(layout_bitmap *bits, size_t oldPos, size_t newPos)
+{
+ size_t shift;
+ size_t count;
+
+ if (oldPos == newPos) return;
+
+ if (oldPos < newPos) {
+ layout_bitmap_slide(bits, oldPos, newPos);
+ return;
+ }
+
+ shift = oldPos - newPos;
+ count = bits->bitCount - oldPos;
+ move_bits(*bits, oldPos, newPos, count); // slide
+ bits->bitCount -= shift;
+}
+
+
+/***********************************************************************
+* layout_bitmap_splat
+* Pastes the contents of bitmap src to the start of bitmap dst.
+* dst bits between the end of src and oldSrcInstanceSize are zeroed.
+* dst must be at least as long as src.
+* Returns YES if any of dst's bits were changed.
+**********************************************************************/
+BOOL
+layout_bitmap_splat(layout_bitmap dst, layout_bitmap src,
+ size_t oldSrcInstanceSize)
+{
+ BOOL changed;
+ size_t oldSrcBitCount;
+ size_t bit;
+
+ if (dst.bitCount < src.bitCount) _objc_fatal("layout bitmap too short");
+
+ changed = NO;
+ oldSrcBitCount = oldSrcInstanceSize / sizeof(id);
+
+ // fixme optimize for byte/word at a time
+ for (bit = 0; bit < oldSrcBitCount; bit++) {
+ int dstset = dst.bits[bit/8] & (1 << (bit % 8));
+ int srcset = (bit < src.bitCount)
+ ? src.bits[bit/8] & (1 << (bit % 8))
+ : 0;
+ if (dstset != srcset) {
+ changed = YES;
+ if (srcset) {
+ dst.bits[bit/8] |= 1 << (bit % 8);
+ } else {
+ dst.bits[bit/8] &= ~(1 << (bit % 8));
+ }
+ }
+ }
+
+ return changed;
+}
+
+
+/***********************************************************************
+* layout_bitmap_or
+* Set dst=dst|src.
+* dst must be at least as long as src.
+* Returns YES if any of dst's bits were changed.
+**********************************************************************/
+BOOL
+layout_bitmap_or(layout_bitmap dst, layout_bitmap src, const char *msg)
+{
+ BOOL changed = NO;
+ size_t bit;
+
+ if (dst.bitCount < src.bitCount) {
+ _objc_fatal("layout_bitmap_or: layout bitmap too short%s%s",
+ msg ? ": " : "", msg ? msg : "");
+ }
+
+ // fixme optimize for byte/word at a time
+ for (bit = 0; bit < src.bitCount; bit++) {
+ int dstset = dst.bits[bit/8] & (1 << (bit % 8));
+ int srcset = src.bits[bit/8] & (1 << (bit % 8));
+ if (srcset && !dstset) {
+ changed = YES;
+ dst.bits[bit/8] |= 1 << (bit % 8);
+ }
+ }
+
+ return changed;
+}
+
+
+/***********************************************************************
+* layout_bitmap_clear
+* Set dst=dst&~src.
+* dst must be at least as long as src.
+* Returns YES if any of dst's bits were changed.
+**********************************************************************/
+BOOL
+layout_bitmap_clear(layout_bitmap dst, layout_bitmap src, const char *msg)
+{
+ BOOL changed = NO;
+ size_t bit;
+
+ if (dst.bitCount < src.bitCount) {
+ _objc_fatal("layout_bitmap_clear: layout bitmap too short%s%s",
+ msg ? ": " : "", msg ? msg : "");
+ }
+
+ // fixme optimize for byte/word at a time
+ for (bit = 0; bit < src.bitCount; bit++) {
+ int dstset = dst.bits[bit/8] & (1 << (bit % 8));
+ int srcset = src.bits[bit/8] & (1 << (bit % 8));
+ if (srcset && dstset) {
+ changed = YES;
+ dst.bits[bit/8] &= ~(1 << (bit % 8));
+ }
+ }
+
+ return changed;
+}
+
+
+void
+layout_bitmap_print(layout_bitmap bits)
+{
+ size_t i;
+ printf("%zu: ", bits.bitCount);
+ for (i = 0; i < bits.bitCount; i++) {
+ int set = bits.bits[i/8] & (1 << (i % 8));
+ printf("%c", set ? '#' : '.');
+ }
+ printf("\n");
+}
+
+#if 0
+// The code below may be useful when interpreting ivar types more precisely.
+
+/**********************************************************************
+* mark_offset_for_layout
+*
+* Marks the appropriate bit in the bits array cooresponding to a the
+* offset of a reference. If we are scanning a nested pointer structure
+* then the bits array will be NULL then this function does nothing.
+*
+**********************************************************************/
+static void mark_offset_for_layout(long offset, long bits_size, unsigned char *bits) {
+ // references are ignored if bits is NULL
+ if (bits) {
+ long slot = offset / sizeof(long);
+
+ // determine byte index using (offset / 8 bits per byte)
+ long i_byte = slot >> 3;
+
+ // if the byte index is valid
+ if (i_byte < bits_size) {
+ // set the (offset / 8 bits per byte)th bit
+ bits[i_byte] |= 1 << (slot & 7);
+ } else {
+ // offset not within instance size
+ _objc_inform ("layout - offset exceeds instance size");
+ }
+ }
+}
+
+/**********************************************************************
+* skip_ivar_type_name
+*
+* Skip over the name of a field/class in an ivar type string. Names
+* are in the form of a double-quoted string. Returns the remaining
+* string.
+*
+**********************************************************************/
+static char *skip_ivar_type_name(char *type) {
+ // current character
+ char ch;
+
+ // if there is an open quote
+ if (*type == '\"') {
+ // skip quote
+ type++;
+
+ // while no closing quote
+ while ((ch = *type) != '\"') {
+ // if end of string return end of string
+ if (!ch) return type;
+
+ // skip character
+ type++;
+ }
+
+ // skip closing quote
+ type++;
+ }
+
+ // return remaining string
+ return type;
+}
+
+
+/**********************************************************************
+* skip_ivar_struct_name
+*
+* Skip over the name of a struct in an ivar type string. Names
+* may be followed by an equals sign. Returns the remaining string.
+*
+**********************************************************************/
+static char *skip_ivar_struct_name(char *type) {
+ // get first character
+ char ch = *type;
+
+ if (ch == _C_UNDEF) {
+ // skip undefined name
+ type++;
+ } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_') {
+ // if alphabetic
+
+ // scan alphanumerics
+ do {
+ // next character
+ ch = *++type;
+ } while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' || (ch >= '0' && ch <= '9'));
+ } else {
+ // no struct name present
+ return type;
+ }
+
+ // skip equals sign
+ if (*type == '=') type++;
+
+ return type;
+}
+
+
+/**********************************************************************
+* scan_basic_ivar_type
+*
+* Determines the size and alignment of a basic ivar type. If the basic
+* type is a possible reference to another garbage collected type the
+* is_reference is set to true (false otherwise.) Returns the remaining
+* string.
+*
+**********************************************************************/
+static char *scan_ivar_type_for_layout(char *type, long offset, long bits_size, unsigned char *bits, long *next_offset);
+static char *scan_basic_ivar_type(char *type, long *size, long *alignment, BOOL *is_reference) {
+ // assume it is a non-reference type
+ *is_reference = NO;
+
+ // get the first character (advancing string)
+ const char *full_type = type;
+ char ch = *type++;
+
+ // GCC 4 uses for const type*.
+ if (ch == _C_CONST) ch = *type++;
+
+ // act on first character
+ switch (ch) {
+ case _C_ID: {
+ // ID type
+
+ // skip over optional class name
+ type = skip_ivar_type_name(type);
+
+ // size and alignment of an id type
+ *size = sizeof(id);
+ *alignment = __alignof(id);
+
+ // is a reference type
+ *is_reference = YES;
+ break;
+ }
+ case _C_PTR: {
+ // C pointer type
+
+ // skip underlying type
+ long ignored_offset;
+ type = scan_ivar_type_for_layout(type, 0, 0, NULL, &ignored_offset);
+
+ // size and alignment of a generic pointer type
+ *size = sizeof(void *);
+ *alignment = __alignof(void *);
+
+ // is a reference type
+ *is_reference = YES;
+ break;
+ }
+ case _C_CHARPTR: {
+ // C string
+
+ // size and alignment of a char pointer type
+ *size = sizeof(char *);
+ *alignment = __alignof(char *);
+
+ // is a reference type
+ *is_reference = YES;
+ break;
+ }
+ case _C_CLASS:
+ case _C_SEL: {
+ // classes and selectors are ignored for now
+ *size = sizeof(void *);
+ *alignment = __alignof(void *);
+ break;
+ }
+ case _C_CHR:
+ case _C_UCHR: {
+ // char and unsigned char
+ *size = sizeof(char);
+ *alignment = __alignof(char);
+ break;
+ }
+ case _C_SHT:
+ case _C_USHT: {
+ // short and unsigned short
+ *size = sizeof(short);
+ *alignment = __alignof(short);
+ break;
+ }
+ case _C_ATOM:
+ case _C_INT:
+ case _C_UINT: {
+ // int and unsigned int
+ *size = sizeof(int);
+ *alignment = __alignof(int);
+ break;
+ }
+ case _C_LNG:
+ case _C_ULNG: {
+ // long and unsigned long
+ *size = sizeof(long);
+ *alignment = __alignof(long);
+ break;
+ }
+ case _C_LNG_LNG:
+ case _C_ULNG_LNG: {
+ // long long and unsigned long long
+ *size = sizeof(long long);
+ *alignment = __alignof(long long);
+ break;
+ }
+ case _C_VECTOR: {
+ // vector
+ *size = 16;
+ *alignment = 16;
+ break;
+ }
+ case _C_FLT: {
+ // float
+ *size = sizeof(float);
+ *alignment = __alignof(float);
+ break;
+ }
+ case _C_DBL: {
+ // double
+ *size = sizeof(double);
+ *alignment = __alignof(double);
+ break;
+ }
+ case _C_BFLD: {
+ // bit field
+
+ // get number of bits in bit field (advance type string)
+ long lng = strtol(type, &type, 10);
+
+ // while next type is a bit field
+ while (*type == _C_BFLD) {
+ // skip over _C_BFLD
+ type++;
+
+ // get next bit field length
+ long next_lng = strtol(type, &type, 10);
+
+ // if spans next word then align to next word
+ if ((lng & ~31) != ((lng + next_lng) & ~31)) lng = (lng + 31) & ~31;
+
+ // increment running length
+ lng += next_lng;
+
+ // skip over potential field name
+ type = skip_ivar_type_name(type);
+ }
+
+ // determine number of bytes bits represent
+ *size = (lng + 7) / 8;
+
+ // byte alignment
+ *alignment = __alignof(char);
+ break;
+ }
+ case _C_BOOL: {
+ // double
+ *size = sizeof(BOOL);
+ *alignment = __alignof(BOOL);
+ break;
+ }
+ case _C_VOID: {
+ // skip void types
+ *size = 0;
+ *alignment = __alignof(char);
+ break;
+ }
+ case _C_UNDEF: {
+ *size = 0;
+ *alignment = __alignof(char);
+ break;
+ }
+ default: {
+ // unhandled type
+ _objc_fatal("unrecognized character \'%c\' in ivar type: \"%s\"", ch, full_type);
+ }
+ }
+
+ return type;
+}
+
+
+/**********************************************************************
+* scan_ivar_type_for_layout
+*
+* Scan an ivar type string looking for references. The offset indicates
+* where the ivar begins. bits is a byte array of size bits_size used to
+* contain the references bit map. next_offset is the offset beyond the
+* ivar. Returns the remaining string.
+*
+**********************************************************************/
+static char *scan_ivar_type_for_layout(char *type, long offset, long bits_size, unsigned char *bits, long *next_offset) {
+ long size; // size of a basic type
+ long alignment; // alignment of the basic type
+ BOOL is_reference; // true if the type indicates a reference to a garbage collected object
+
+ // get the first character
+ char ch = *type;
+
+ // GCC 4 uses for const type*.
+ if (ch == _C_CONST) ch = *++type;
+
+ // act on first character
+ switch (ch) {
+ case _C_ARY_B: {
+ // array type
+
+ // get the array length
+ long lng = strtol(type + 1, &type, 10);
+
+ // next type will be where to advance the type string once the array is processed
+ char *next_type = type;
+
+ // repeat the next type x lng
+ if (!lng) {
+ next_type = scan_ivar_type_for_layout(type, 0, 0, NULL, &offset);
+ } else {
+ while (lng--) {
+ // repeatedly scan the same type
+ next_type = scan_ivar_type_for_layout(type, offset, bits_size, bits, &offset);
+ }
+ }
+
+ // advance the type now
+ type = next_type;
+
+ // after the end of the array
+ *next_offset = offset;
+
+ // advance over closing bracket
+ if (*type == _C_ARY_E) type++;
+ else _objc_inform("missing \'%c\' in ivar type.", _C_ARY_E);
+
+ break;
+ }
+ case _C_UNION_B: {
+ // union type
+
+ // skip over possible union name
+ type = skip_ivar_struct_name(type + 1);
+
+ // need to accumulate the maximum element offset
+ long max_offset = 0;
+
+ // while not closing paren
+ while ((ch = *type) && ch != _C_UNION_E) {
+ // skip over potential field name
+ type = skip_ivar_type_name(type);
+
+ // scan type
+ long union_offset;
+ type = scan_ivar_type_for_layout(type, offset, bits_size, bits, &union_offset);
+
+ // adjust the maximum element offset
+ if (max_offset < union_offset) max_offset = union_offset;
+ }
+
+ // after the largest element
+ *next_offset = max_offset;
+
+ // advance over closing paren
+ if (ch == _C_UNION_E) {
+ type++;
+ } else {
+ _objc_inform("missing \'%c\' in ivar type", _C_UNION_E);
+ }
+
+ break;
+ }
+ case _C_STRUCT_B: {
+ // struct type
+
+ // skip over possible struct name
+ type = skip_ivar_struct_name(type + 1);
+
+ // while not closing brace
+ while ((ch = *type) && ch != _C_STRUCT_E) {
+ // skip over potential field name
+ type = skip_ivar_type_name(type);
+
+ // scan type
+ type = scan_ivar_type_for_layout(type, offset, bits_size, bits, &offset);
+ }
+
+ // after the end of the struct
+ *next_offset = offset;
+
+ // advance over closing brace
+ if (ch == _C_STRUCT_E) type++;
+ else _objc_inform("missing \'%c\' in ivar type", _C_STRUCT_E);
+
+ break;
+ }
+ default: {
+ // basic type
+
+ // scan type
+ type = scan_basic_ivar_type(type, &size, &alignment, &is_reference);
+
+ // create alignment mask
+ alignment--;
+
+ // align offset
+ offset = (offset + alignment) & ~alignment;
+
+ // if is a reference then mark in the bit map
+ if (is_reference) mark_offset_for_layout(offset, bits_size, bits);
+
+ // after the basic type
+ *next_offset = offset + size;
+ break;
+ }
+ }
+
+ // return remainder of type string
+ return type;
+}
+
+#endif
+++ /dev/null
-/*
- * Copyright (c) 1999-2001, 2004-2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/*
- * objc-load.m
- * Copyright 1988-1996, NeXT Software, Inc.
- * Author: s. naroff
- *
- */
-
-#include "objc-private.h"
-#include "objc-load.h"
-
-#if !__OBJC2__ && !TARGET_OS_WIN32
-
-extern void (*callbackFunction)( Class, Category );
-
-
-/**********************************************************************************
-* objc_loadModule.
-*
-* NOTE: Loading isn't really thread safe. If a load message recursively calls
-* objc_loadModules() both sets will be loaded correctly, but if the original
-* caller calls objc_unloadModules() it will probably unload the wrong modules.
-* If a load message calls objc_unloadModules(), then it will unload
-* the modules currently being loaded, which will probably cause a crash.
-*
-* Error handling is still somewhat crude. If we encounter errors while
-* linking up classes or categories, we will not recover correctly.
-*
-* I removed attempts to lock the class hashtable, since this introduced
-* deadlock which was hard to remove. The only way you can get into trouble
-* is if one thread loads a module while another thread tries to access the
-* loaded classes (using objc_lookUpClass) before the load is complete.
-**********************************************************************************/
-int objc_loadModule(char *moduleName, void (*class_callback) (Class, Category), int *errorCode)
-{
- int successFlag = 1;
- int locErrorCode;
- NSObjectFileImage objectFileImage;
- NSObjectFileImageReturnCode code;
-
- // So we don't have to check this everywhere
- if (errorCode == NULL)
- errorCode = &locErrorCode;
-
- if (moduleName == NULL)
- {
- *errorCode = NSObjectFileImageInappropriateFile;
- return 0;
- }
-
- if (_dyld_present () == 0)
- {
- *errorCode = NSObjectFileImageFailure;
- return 0;
- }
-
- callbackFunction = class_callback;
- code = NSCreateObjectFileImageFromFile (moduleName, &objectFileImage);
- if (code != NSObjectFileImageSuccess)
- {
- *errorCode = code;
- return 0;
- }
-
- if (NSLinkModule(objectFileImage, moduleName, NSLINKMODULE_OPTION_RETURN_ON_ERROR) == NULL) {
- NSLinkEditErrors error;
- int errorNum;
- const char *fileName, *errorString;
- NSLinkEditError(&error, &errorNum, &fileName, &errorString);
- // These errors may overlap with other errors that objc_loadModule returns in other failure cases.
- *errorCode = error;
- return 0;
- }
- callbackFunction = NULL;
-
-
- return successFlag;
-}
-
-/**********************************************************************************
-* objc_loadModules.
-**********************************************************************************/
-/* Lock for dynamic loading and unloading. */
-// static OBJC_DECLARE_LOCK (loadLock);
-
-
-long objc_loadModules (char * modlist[],
- void * errStream,
- void (*class_callback) (Class, Category),
- headerType ** hdr_addr,
- char * debug_file)
-{
- char ** modules;
- int code;
- int itWorked;
-
- if (modlist == 0)
- return 0;
-
- for (modules = &modlist[0]; *modules != 0; modules++)
- {
- itWorked = objc_loadModule (*modules, class_callback, &code);
- if (itWorked == 0)
- {
- //if (errStream)
- // NXPrintf ((NXStream *) errStream, "objc_loadModules(%s) code = %d\n", *modules, code);
- return 1;
- }
-
- if (hdr_addr)
- *(hdr_addr++) = 0;
- }
-
- return 0;
-}
-
-/**********************************************************************************
-* objc_unloadModules.
-*
-* NOTE: Unloading isn't really thread safe. If an unload message calls
-* objc_loadModules() or objc_unloadModules(), then the current call
-* to objc_unloadModules() will probably unload the wrong stuff.
-**********************************************************************************/
-
-long objc_unloadModules (void * errStream,
- void (*unload_callback) (Class, Category))
-{
- headerType * header_addr = 0;
- int errflag = 0;
-
- // TODO: to make unloading work, should get the current header
-
- if (header_addr)
- {
- ; // TODO: unload the current header
- }
- else
- {
- errflag = 1;
- }
-
- return errflag;
-}
-
-#endif
--- /dev/null
+/*
+ * Copyright (c) 1999-2001, 2004-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * objc-load.m
+ * Copyright 1988-1996, NeXT Software, Inc.
+ * Author: s. naroff
+ *
+ */
+
+#include "objc-private.h"
+#include "objc-load.h"
+
+#if !__OBJC2__ && !TARGET_OS_WIN32
+
+extern void (*callbackFunction)( Class, Category );
+
+
+/**********************************************************************************
+* objc_loadModule.
+*
+* NOTE: Loading isn't really thread safe. If a load message recursively calls
+* objc_loadModules() both sets will be loaded correctly, but if the original
+* caller calls objc_unloadModules() it will probably unload the wrong modules.
+* If a load message calls objc_unloadModules(), then it will unload
+* the modules currently being loaded, which will probably cause a crash.
+*
+* Error handling is still somewhat crude. If we encounter errors while
+* linking up classes or categories, we will not recover correctly.
+*
+* I removed attempts to lock the class hashtable, since this introduced
+* deadlock which was hard to remove. The only way you can get into trouble
+* is if one thread loads a module while another thread tries to access the
+* loaded classes (using objc_lookUpClass) before the load is complete.
+**********************************************************************************/
+int objc_loadModule(char *moduleName, void (*class_callback) (Class, Category), int *errorCode)
+{
+ int successFlag = 1;
+ int locErrorCode;
+ NSObjectFileImage objectFileImage;
+ NSObjectFileImageReturnCode code;
+
+ // So we don't have to check this everywhere
+ if (errorCode == NULL)
+ errorCode = &locErrorCode;
+
+ if (moduleName == NULL)
+ {
+ *errorCode = NSObjectFileImageInappropriateFile;
+ return 0;
+ }
+
+ if (_dyld_present () == 0)
+ {
+ *errorCode = NSObjectFileImageFailure;
+ return 0;
+ }
+
+ callbackFunction = class_callback;
+ code = NSCreateObjectFileImageFromFile (moduleName, &objectFileImage);
+ if (code != NSObjectFileImageSuccess)
+ {
+ *errorCode = code;
+ return 0;
+ }
+
+ if (NSLinkModule(objectFileImage, moduleName, NSLINKMODULE_OPTION_RETURN_ON_ERROR) == NULL) {
+ NSLinkEditErrors error;
+ int errorNum;
+ const char *fileName, *errorString;
+ NSLinkEditError(&error, &errorNum, &fileName, &errorString);
+ // These errors may overlap with other errors that objc_loadModule returns in other failure cases.
+ *errorCode = error;
+ return 0;
+ }
+ callbackFunction = NULL;
+
+
+ return successFlag;
+}
+
+/**********************************************************************************
+* objc_loadModules.
+**********************************************************************************/
+/* Lock for dynamic loading and unloading. */
+// static OBJC_DECLARE_LOCK (loadLock);
+
+
+long objc_loadModules (char * modlist[],
+ void * errStream,
+ void (*class_callback) (Class, Category),
+ headerType ** hdr_addr,
+ char * debug_file)
+{
+ char ** modules;
+ int code;
+ int itWorked;
+
+ if (modlist == 0)
+ return 0;
+
+ for (modules = &modlist[0]; *modules != 0; modules++)
+ {
+ itWorked = objc_loadModule (*modules, class_callback, &code);
+ if (itWorked == 0)
+ {
+ //if (errStream)
+ // NXPrintf ((NXStream *) errStream, "objc_loadModules(%s) code = %d\n", *modules, code);
+ return 1;
+ }
+
+ if (hdr_addr)
+ *(hdr_addr++) = 0;
+ }
+
+ return 0;
+}
+
+/**********************************************************************************
+* objc_unloadModules.
+*
+* NOTE: Unloading isn't really thread safe. If an unload message calls
+* objc_loadModules() or objc_unloadModules(), then the current call
+* to objc_unloadModules() will probably unload the wrong stuff.
+**********************************************************************************/
+
+long objc_unloadModules (void * errStream,
+ void (*unload_callback) (Class, Category))
+{
+ headerType * header_addr = 0;
+ int errflag = 0;
+
+ // TODO: to make unloading work, should get the current header
+
+ if (header_addr)
+ {
+ ; // TODO: unload the current header
+ }
+ else
+ {
+ errflag = 1;
+ }
+
+ return errflag;
+}
+
+#endif
+++ /dev/null
-/*
- * Copyright (c) 2004-2006 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/***********************************************************************
-* objc-loadmethod.m
-* Support for +load methods.
-**********************************************************************/
-
-#include "objc-loadmethod.h"
-#include "objc-private.h"
-
-struct loadable_class {
- Class cls; // may be NULL
- IMP method;
-};
-
-struct loadable_category {
- Category cat; // may be NULL
- IMP method;
-};
-
-
-// List of classes that need +load called (pending superclass +load)
-// This list always has superclasses first because of the way it is constructed
-static struct loadable_class *loadable_classes = NULL;
-static int loadable_classes_used = 0;
-static int loadable_classes_allocated = 0;
-
-// List of categories that need +load called (pending parent class +load)
-static struct loadable_category *loadable_categories = NULL;
-static int loadable_categories_used = 0;
-static int loadable_categories_allocated = 0;
-
-
-/***********************************************************************
-* add_class_to_loadable_list
-* 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)
-{
- IMP method;
-
- recursive_mutex_assert_locked(&loadMethodLock);
-
- method = _class_getLoadMethod(cls);
- if (!method) return; // Don't bother if cls has no +load method
-
- if (PrintLoading) {
- _objc_inform("LOAD: class '%s' scheduled for +load", _class_getName(cls));
- }
-
- if (loadable_classes_used == loadable_classes_allocated) {
- loadable_classes_allocated = loadable_classes_allocated*2 + 16;
- loadable_classes =
- _realloc_internal(loadable_classes,
- loadable_classes_allocated *
- sizeof(struct loadable_class));
- }
-
- loadable_classes[loadable_classes_used].cls = cls;
- loadable_classes[loadable_classes_used].method = method;
- loadable_classes_used++;
-}
-
-
-/***********************************************************************
-* add_category_to_loadable_list
-* Category cat's parent class exists and the category has been attached
-* 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)
-{
- IMP method;
-
- recursive_mutex_assert_locked(&loadMethodLock);
-
- method = _category_getLoadMethod(cat);
-
- // Don't bother if cat has no +load method
- if (!method) return;
-
- if (PrintLoading) {
- _objc_inform("LOAD: category '%s(%s)' scheduled for +load",
- _category_getClassName(cat), _category_getName(cat));
- }
-
- if (loadable_categories_used == loadable_categories_allocated) {
- loadable_categories_allocated = loadable_categories_allocated*2 + 16;
- loadable_categories =
- _realloc_internal(loadable_categories,
- loadable_categories_allocated *
- sizeof(struct loadable_category));
- }
-
- loadable_categories[loadable_categories_used].cat = cat;
- loadable_categories[loadable_categories_used].method = method;
- loadable_categories_used++;
-}
-
-
-/***********************************************************************
-* remove_class_from_loadable_list
-* 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)
-{
- recursive_mutex_assert_locked(&loadMethodLock);
-
- if (loadable_classes) {
- int i;
- for (i = 0; i < loadable_classes_used; i++) {
- if (loadable_classes[i].cls == cls) {
- loadable_classes[i].cls = NULL;
- if (PrintLoading) {
- _objc_inform("LOAD: class '%s' unscheduled for +load", _class_getName(cls));
- }
- return;
- }
- }
- }
-}
-
-
-/***********************************************************************
-* remove_category_from_loadable_list
-* 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)
-{
- recursive_mutex_assert_locked(&loadMethodLock);
-
- if (loadable_categories) {
- int i;
- for (i = 0; i < loadable_categories_used; i++) {
- if (loadable_categories[i].cat == cat) {
- loadable_categories[i].cat = NULL;
- if (PrintLoading) {
- _objc_inform("LOAD: category '%s(%s)' unscheduled for +load",
- _category_getClassName(cat),
- _category_getName(cat));
- }
- return;
- }
- }
- }
-}
-
-
-/***********************************************************************
-* call_class_loads
-* Call all pending class +load methods.
-* If new classes become loadable, +load is NOT called for them.
-*
-* Called only by call_load_methods().
-**********************************************************************/
-static void call_class_loads(void)
-{
- int i;
-
- // Detach current loadable list.
- struct loadable_class *classes = loadable_classes;
- int used = loadable_classes_used;
- loadable_classes = NULL;
- loadable_classes_allocated = 0;
- loadable_classes_used = 0;
-
- // Call all +loads for the detached list.
- for (i = 0; i < used; i++) {
- Class cls = classes[i].cls;
- IMP load_method = classes[i].method;
- if (!cls) continue;
-
- if (PrintLoading) {
- _objc_inform("LOAD: +[%s load]\n", _class_getName(cls));
- }
- (*load_method) ((id) cls, SEL_load);
- }
-
- // Destroy the detached list.
- if (classes) _free_internal(classes);
-}
-
-
-/***********************************************************************
-* call_category_loads
-* Call some pending category +load methods.
-* The parent class of the +load-implementing categories has all of
-* its categories attached, in case some are lazily waiting for +initalize.
-* Don't call +load unless the parent class is connected.
-* If new categories become loadable, +load is NOT called, and they
-* are added to the end of the loadable list, and we return TRUE.
-* Return FALSE if no new categories became loadable.
-*
-* Called only by call_load_methods().
-**********************************************************************/
-static BOOL call_category_loads(void)
-{
- int i, shift;
- BOOL new_categories_added = NO;
-
- // Detach current loadable list.
- struct loadable_category *cats = loadable_categories;
- int used = loadable_categories_used;
- int allocated = loadable_categories_allocated;
- loadable_categories = NULL;
- loadable_categories_allocated = 0;
- loadable_categories_used = 0;
-
- // Call all +loads for the detached list.
- for (i = 0; i < used; i++) {
- Category cat = cats[i].cat;
- IMP load_method = cats[i].method;
- Class cls;
- if (!cat) continue;
-
- cls = _category_getClass(cat);
- if (cls && _class_isLoadable(cls)) {
- if (PrintLoading) {
- _objc_inform("LOAD: +[%s(%s) load]\n",
- _class_getName(cls),
- _category_getName(cat));
- }
- (*load_method) ((id) cls, SEL_load);
- cats[i].cat = NULL;
- }
- }
-
- // Compact detached list (order-preserving)
- shift = 0;
- for (i = 0; i < used; i++) {
- if (cats[i].cat) {
- cats[i-shift] = cats[i];
- } else {
- shift++;
- }
- }
- used -= shift;
-
- // Copy any new +load candidates from the new list to the detached list.
- new_categories_added = (loadable_categories_used > 0);
- 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[used++] = loadable_categories[i];
- }
-
- // Destroy the new list.
- if (loadable_categories) _free_internal(loadable_categories);
-
- // Reattach the (now augmented) detached list.
- // But if there's nothing left to load, destroy the list.
- if (used) {
- loadable_categories = cats;
- loadable_categories_used = used;
- loadable_categories_allocated = allocated;
- } else {
- if (cats) _free_internal(cats);
- loadable_categories = NULL;
- loadable_categories_used = 0;
- loadable_categories_allocated = 0;
- }
-
- if (PrintLoading) {
- if (loadable_categories_used != 0) {
- _objc_inform("LOAD: %d categories still waiting for +load\n",
- loadable_categories_used);
- }
- }
-
- return new_categories_added;
-}
-
-
-/***********************************************************************
-* call_load_methods
-* Call all pending class and category +load methods.
-* Class +load methods are called superclass-first.
-* Category +load methods are not called until after the parent class's +load.
-*
-* This method must be RE-ENTRANT, because a +load could trigger
-* more image mapping. In addition, the superclass-first ordering
-* must be preserved in the face of re-entrant calls. Therefore,
-* only the OUTERMOST call of this function will do anything, and
-* that call will handle all loadable classes, even those generated
-* while it was running.
-*
-* The sequence below preserves +load ordering in the face of
-* image loading during a +load, and make sure that no
-* +load method is forgotten because it was added during
-* a +load call.
-* Sequence:
-* 1. Repeatedly call class +loads until there aren't any more
-* 2. Call category +loads ONCE.
-* 3. Run more +loads if:
-* (a) there are more classes to load, OR
-* (b) there are some potential category +loads that have
-* still never been attempted.
-* Category +loads are only run once to ensure "parent class first"
-* ordering, even if a category +load triggers a new loadable class
-* and a new loadable category attached to that class.
-*
-* Locking: loadMethodLock must be held by the caller
-* All other locks must not be held.
-**********************************************************************/
-PRIVATE_EXTERN void call_load_methods(void)
-{
- static BOOL loading = NO;
- BOOL more_categories;
-
- recursive_mutex_assert_locked(&loadMethodLock);
-
- // Re-entrant calls do nothing; the outermost call will finish the job.
- if (loading) return;
- loading = YES;
-
- do {
- // 1. Repeatedly call class +loads until there aren't any more
- while (loadable_classes_used > 0) {
- call_class_loads();
- }
-
- // 2. Call category +loads ONCE
- more_categories = call_category_loads();
-
- // 3. Run more +loads if there are classes OR more untried categories
- } while (loadable_classes_used > 0 || more_categories);
-
- loading = NO;
-}
-
-
--- /dev/null
+/*
+ * Copyright (c) 2004-2006 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/***********************************************************************
+* objc-loadmethod.m
+* Support for +load methods.
+**********************************************************************/
+
+#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;
+};
+
+struct loadable_category {
+ Category cat; // may be NULL
+ IMP method;
+};
+
+
+// List of classes that need +load called (pending superclass +load)
+// This list always has superclasses first because of the way it is constructed
+static struct loadable_class *loadable_classes = NULL;
+static int loadable_classes_used = 0;
+static int loadable_classes_allocated = 0;
+
+// List of categories that need +load called (pending parent class +load)
+static struct loadable_category *loadable_categories = NULL;
+static int loadable_categories_used = 0;
+static int loadable_categories_allocated = 0;
+
+
+/***********************************************************************
+* add_class_to_loadable_list
+* Class cls has just become connected. Schedule it for +load if
+* it implements a +load method.
+**********************************************************************/
+void add_class_to_loadable_list(Class cls)
+{
+ IMP method;
+
+ recursive_mutex_assert_locked(&loadMethodLock);
+
+ method = _class_getLoadMethod(cls);
+ if (!method) return; // Don't bother if cls has no +load method
+
+ if (PrintLoading) {
+ _objc_inform("LOAD: class '%s' scheduled for +load", _class_getName(cls));
+ }
+
+ if (loadable_classes_used == loadable_classes_allocated) {
+ loadable_classes_allocated = loadable_classes_allocated*2 + 16;
+ loadable_classes = (struct loadable_class *)
+ _realloc_internal(loadable_classes,
+ loadable_classes_allocated *
+ sizeof(struct loadable_class));
+ }
+
+ loadable_classes[loadable_classes_used].cls = cls;
+ loadable_classes[loadable_classes_used].method = method;
+ loadable_classes_used++;
+}
+
+
+/***********************************************************************
+* add_category_to_loadable_list
+* Category cat's parent class exists and the category has been attached
+* to its class. Schedule this category for +load after its parent class
+* becomes connected and has its own +load method called.
+**********************************************************************/
+void add_category_to_loadable_list(Category cat)
+{
+ IMP method;
+
+ recursive_mutex_assert_locked(&loadMethodLock);
+
+ method = _category_getLoadMethod(cat);
+
+ // Don't bother if cat has no +load method
+ if (!method) return;
+
+ if (PrintLoading) {
+ _objc_inform("LOAD: category '%s(%s)' scheduled for +load",
+ _category_getClassName(cat), _category_getName(cat));
+ }
+
+ if (loadable_categories_used == loadable_categories_allocated) {
+ loadable_categories_allocated = loadable_categories_allocated*2 + 16;
+ loadable_categories = (struct loadable_category *)
+ _realloc_internal(loadable_categories,
+ loadable_categories_allocated *
+ sizeof(struct loadable_category));
+ }
+
+ loadable_categories[loadable_categories_used].cat = cat;
+ loadable_categories[loadable_categories_used].method = method;
+ loadable_categories_used++;
+}
+
+
+/***********************************************************************
+* remove_class_from_loadable_list
+* Class cls may have been loadable before, but it is now no longer
+* loadable (because its image is being unmapped).
+**********************************************************************/
+void remove_class_from_loadable_list(Class cls)
+{
+ recursive_mutex_assert_locked(&loadMethodLock);
+
+ if (loadable_classes) {
+ int i;
+ for (i = 0; i < loadable_classes_used; i++) {
+ if (loadable_classes[i].cls == cls) {
+ loadable_classes[i].cls = NULL;
+ if (PrintLoading) {
+ _objc_inform("LOAD: class '%s' unscheduled for +load", _class_getName(cls));
+ }
+ return;
+ }
+ }
+ }
+}
+
+
+/***********************************************************************
+* remove_category_from_loadable_list
+* Category cat may have been loadable before, but it is now no longer
+* loadable (because its image is being unmapped).
+**********************************************************************/
+void remove_category_from_loadable_list(Category cat)
+{
+ recursive_mutex_assert_locked(&loadMethodLock);
+
+ if (loadable_categories) {
+ int i;
+ for (i = 0; i < loadable_categories_used; i++) {
+ if (loadable_categories[i].cat == cat) {
+ loadable_categories[i].cat = NULL;
+ if (PrintLoading) {
+ _objc_inform("LOAD: category '%s(%s)' unscheduled for +load",
+ _category_getClassName(cat),
+ _category_getName(cat));
+ }
+ return;
+ }
+ }
+ }
+}
+
+
+/***********************************************************************
+* call_class_loads
+* Call all pending class +load methods.
+* If new classes become loadable, +load is NOT called for them.
+*
+* Called only by call_load_methods().
+**********************************************************************/
+static void call_class_loads(void)
+{
+ int i;
+
+ // Detach current loadable list.
+ struct loadable_class *classes = loadable_classes;
+ int used = loadable_classes_used;
+ loadable_classes = NULL;
+ loadable_classes_allocated = 0;
+ loadable_classes_used = 0;
+
+ // Call all +loads for the detached list.
+ for (i = 0; i < used; i++) {
+ Class cls = classes[i].cls;
+ 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)(cls, SEL_load);
+ }
+
+ // Destroy the detached list.
+ if (classes) _free_internal(classes);
+}
+
+
+/***********************************************************************
+* call_category_loads
+* Call some pending category +load methods.
+* The parent class of the +load-implementing categories has all of
+* its categories attached, in case some are lazily waiting for +initalize.
+* Don't call +load unless the parent class is connected.
+* If new categories become loadable, +load is NOT called, and they
+* are added to the end of the loadable list, and we return TRUE.
+* Return FALSE if no new categories became loadable.
+*
+* Called only by call_load_methods().
+**********************************************************************/
+static BOOL call_category_loads(void)
+{
+ int i, shift;
+ BOOL new_categories_added = NO;
+
+ // Detach current loadable list.
+ struct loadable_category *cats = loadable_categories;
+ int used = loadable_categories_used;
+ int allocated = loadable_categories_allocated;
+ loadable_categories = NULL;
+ loadable_categories_allocated = 0;
+ loadable_categories_used = 0;
+
+ // Call all +loads for the detached list.
+ for (i = 0; i < used; i++) {
+ Category cat = cats[i].cat;
+ load_method_t load_method = (load_method_t)cats[i].method;
+ Class cls;
+ if (!cat) continue;
+
+ cls = _category_getClass(cat);
+ if (cls && _class_isLoadable(cls)) {
+ if (PrintLoading) {
+ _objc_inform("LOAD: +[%s(%s) load]\n",
+ _class_getName(cls),
+ _category_getName(cat));
+ }
+ (*load_method)(cls, SEL_load);
+ cats[i].cat = NULL;
+ }
+ }
+
+ // Compact detached list (order-preserving)
+ shift = 0;
+ for (i = 0; i < used; i++) {
+ if (cats[i].cat) {
+ cats[i-shift] = cats[i];
+ } else {
+ shift++;
+ }
+ }
+ used -= shift;
+
+ // Copy any new +load candidates from the new list to the detached list.
+ new_categories_added = (loadable_categories_used > 0);
+ for (i = 0; i < loadable_categories_used; i++) {
+ if (used == allocated) {
+ allocated = allocated*2 + 16;
+ cats = (struct loadable_category *)
+ _realloc_internal(cats, allocated *
+ sizeof(struct loadable_category));
+ }
+ cats[used++] = loadable_categories[i];
+ }
+
+ // Destroy the new list.
+ if (loadable_categories) _free_internal(loadable_categories);
+
+ // Reattach the (now augmented) detached list.
+ // But if there's nothing left to load, destroy the list.
+ if (used) {
+ loadable_categories = cats;
+ loadable_categories_used = used;
+ loadable_categories_allocated = allocated;
+ } else {
+ if (cats) _free_internal(cats);
+ loadable_categories = NULL;
+ loadable_categories_used = 0;
+ loadable_categories_allocated = 0;
+ }
+
+ if (PrintLoading) {
+ if (loadable_categories_used != 0) {
+ _objc_inform("LOAD: %d categories still waiting for +load\n",
+ loadable_categories_used);
+ }
+ }
+
+ return new_categories_added;
+}
+
+
+/***********************************************************************
+* call_load_methods
+* Call all pending class and category +load methods.
+* Class +load methods are called superclass-first.
+* Category +load methods are not called until after the parent class's +load.
+*
+* This method must be RE-ENTRANT, because a +load could trigger
+* more image mapping. In addition, the superclass-first ordering
+* must be preserved in the face of re-entrant calls. Therefore,
+* only the OUTERMOST call of this function will do anything, and
+* that call will handle all loadable classes, even those generated
+* while it was running.
+*
+* The sequence below preserves +load ordering in the face of
+* image loading during a +load, and make sure that no
+* +load method is forgotten because it was added during
+* a +load call.
+* Sequence:
+* 1. Repeatedly call class +loads until there aren't any more
+* 2. Call category +loads ONCE.
+* 3. Run more +loads if:
+* (a) there are more classes to load, OR
+* (b) there are some potential category +loads that have
+* still never been attempted.
+* Category +loads are only run once to ensure "parent class first"
+* ordering, even if a category +load triggers a new loadable class
+* and a new loadable category attached to that class.
+*
+* Locking: loadMethodLock must be held by the caller
+* All other locks must not be held.
+**********************************************************************/
+void call_load_methods(void)
+{
+ static BOOL loading = NO;
+ BOOL more_categories;
+
+ recursive_mutex_assert_locked(&loadMethodLock);
+
+ // Re-entrant calls do nothing; the outermost call will finish the job.
+ 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) {
+ call_class_loads();
+ }
+
+ // 2. Call category +loads ONCE
+ more_categories = call_category_loads();
+
+ // 3. Run more +loads if there are classes OR more untried categories
+ } while (loadable_classes_used > 0 || more_categories);
+
+ objc_autoreleasePoolPop(pool);
+
+ loading = NO;
+}
+
+
+++ /dev/null
-/*
- * Copyright (c) 2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/***********************************************************************
-* objc-lock.m
-* Error-checking locks for debugging.
-**********************************************************************/
-
-#include "objc-private.h"
-
-#if !defined(NDEBUG) && !TARGET_OS_WIN32
-
-/***********************************************************************
-* Recording - per-thread list of mutexes and monitors held
-**********************************************************************/
-
-typedef struct {
- void *l; // the lock itself
- int k; // the kind of lock it is (MUTEX, MONITOR, etc)
- int i; // the lock's nest count
-} lockcount;
-
-#define MUTEX 1
-#define MONITOR 2
-#define RDLOCK 3
-#define WRLOCK 4
-#define RECURSIVE 5
-
-typedef struct _objc_lock_list {
- int allocated;
- int used;
- lockcount list[0];
-} _objc_lock_list;
-
-static tls_key_t lock_tls;
-
-static void
-destroyLocks(void *value)
-{
- _objc_lock_list *locks = (_objc_lock_list *)value;
- // fixme complain about any still-held locks?
- if (locks) _free_internal(locks);
-}
-
-static struct _objc_lock_list *
-getLocks(BOOL create)
-{
- _objc_lock_list *locks;
-
- // Use a dedicated tls key to prevent differences vs non-debug in
- // usage of objc's other tls keys (required for some unit tests).
- INIT_ONCE_PTR(lock_tls, tls_create(&destroyLocks), (void)0);
-
- locks = (_objc_lock_list *)tls_get(lock_tls);
- if (!locks) {
- if (!create) {
- return NULL;
- } else {
- locks = _calloc_internal(1, sizeof(_objc_lock_list) + sizeof(lockcount) * 16);
- locks->allocated = 16;
- locks->used = 0;
- tls_set(lock_tls, locks);
- }
- }
-
- if (locks->allocated == locks->used) {
- if (!create) {
- return locks;
- } else {
- _objc_lock_list *oldlocks = locks;
- locks = _calloc_internal(1, sizeof(_objc_lock_list) + 2 * oldlocks->used * sizeof(lockcount));
- locks->used = oldlocks->used;
- locks->allocated = oldlocks->used * 2;
- memcpy(locks->list, oldlocks->list, locks->used * sizeof(lockcount));
- tls_set(lock_tls, locks);
- _free_internal(oldlocks);
- }
- }
-
- return locks;
-}
-
-static BOOL
-hasLock(_objc_lock_list *locks, void *lock, int kind)
-{
- int i;
- if (!locks) return NO;
-
- for (i = 0; i < locks->used; i++) {
- if (locks->list[i].l == lock && locks->list[i].k == kind) return YES;
- }
- return NO;
-}
-
-
-static void
-setLock(_objc_lock_list *locks, void *lock, int kind)
-{
- int i;
- for (i = 0; i < locks->used; i++) {
- if (locks->list[i].l == lock && locks->list[i].k == kind) {
- locks->list[i].i++;
- return;
- }
- }
-
- locks->list[locks->used].l = lock;
- locks->list[locks->used].i = 1;
- locks->list[locks->used].k = kind;
- locks->used++;
-}
-
-static void
-clearLock(_objc_lock_list *locks, void *lock, int kind)
-{
- int i;
- for (i = 0; i < locks->used; i++) {
- if (locks->list[i].l == lock && locks->list[i].k == kind) {
- if (--locks->list[i].i == 0) {
- locks->list[i].l = NULL;
- locks->list[i] = locks->list[--locks->used];
- }
- return;
- }
- }
-
- _objc_fatal("lock not found!");
-}
-
-
-/***********************************************************************
-* Mutex checking
-**********************************************************************/
-
-PRIVATE_EXTERN int
-_mutex_lock_debug(mutex_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(YES);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (hasLock(locks, lock, MUTEX)) {
- _objc_fatal("deadlock: relocking mutex %s\n", name+1);
- }
- setLock(locks, lock, MUTEX);
- }
-
- return _mutex_lock_nodebug(lock);
-}
-
-PRIVATE_EXTERN int
-_mutex_try_lock_debug(mutex_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(YES);
-
- // attempting to relock in try_lock is OK
- int result = _mutex_try_lock_nodebug(lock);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (result) {
- setLock(locks, lock, MUTEX);
- }
- }
- return result;
-}
-
-PRIVATE_EXTERN int
-_mutex_unlock_debug(mutex_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (!hasLock(locks, lock, MUTEX)) {
- _objc_fatal("unlocking unowned mutex %s\n", name+1);
- }
- clearLock(locks, lock, MUTEX);
- }
-
- return _mutex_unlock_nodebug(lock);
-}
-
-PRIVATE_EXTERN void
-_mutex_assert_locked_debug(mutex_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (!hasLock(locks, lock, MUTEX)) {
- _objc_fatal("mutex %s incorrectly not held\n",name+1);
- }
- }
-}
-
-
-PRIVATE_EXTERN void
-_mutex_assert_unlocked_debug(mutex_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (hasLock(locks, lock, MUTEX)) {
- _objc_fatal("mutex %s incorrectly held\n", name+1);
- }
- }
-}
-
-
-/***********************************************************************
-* Recursive mutex checking
-**********************************************************************/
-
-PRIVATE_EXTERN int
-_recursive_mutex_lock_debug(recursive_mutex_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(YES);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- setLock(locks, lock, RECURSIVE);
- }
-
- return _recursive_mutex_lock_nodebug(lock);
-}
-
-PRIVATE_EXTERN int
-_recursive_mutex_try_lock_debug(recursive_mutex_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(YES);
-
- int result = _recursive_mutex_try_lock_nodebug(lock);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (result) {
- setLock(locks, lock, RECURSIVE);
- }
- }
- return result;
-}
-
-PRIVATE_EXTERN int
-_recursive_mutex_unlock_debug(recursive_mutex_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (!hasLock(locks, lock, RECURSIVE)) {
- _objc_fatal("unlocking unowned recursive mutex %s\n", name+1);
- }
- clearLock(locks, lock, RECURSIVE);
- }
-
- return _recursive_mutex_unlock_nodebug(lock);
-}
-
-PRIVATE_EXTERN void
-_recursive_mutex_assert_locked_debug(recursive_mutex_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (!hasLock(locks, lock, RECURSIVE)) {
- _objc_fatal("recursive mutex %s incorrectly not held\n",name+1);
- }
- }
-}
-
-
-PRIVATE_EXTERN void
-_recursive_mutex_assert_unlocked_debug(recursive_mutex_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (hasLock(locks, lock, RECURSIVE)) {
- _objc_fatal("recursive mutex %s incorrectly held\n", name+1);
- }
- }
-}
-
-
-/***********************************************************************
-* Monitor checking
-**********************************************************************/
-
-PRIVATE_EXTERN int
-_monitor_enter_debug(monitor_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(YES);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (hasLock(locks, lock, MONITOR)) {
- _objc_fatal("deadlock: relocking monitor %s\n", name+1);
- }
- setLock(locks, lock, MONITOR);
- }
-
- return _monitor_enter_nodebug(lock);
-}
-
-PRIVATE_EXTERN int
-_monitor_exit_debug(monitor_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (!hasLock(locks, lock, MONITOR)) {
- _objc_fatal("unlocking unowned monitor%s\n", name+1);
- }
- clearLock(locks, lock, MONITOR);
- }
-
- return _monitor_exit_nodebug(lock);
-}
-
-PRIVATE_EXTERN int
-_monitor_wait_debug(monitor_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (!hasLock(locks, lock, MONITOR)) {
- _objc_fatal("waiting in unowned monitor%s\n", name+1);
- }
- }
-
- return _monitor_wait_nodebug(lock);
-}
-
-PRIVATE_EXTERN void
-_monitor_assert_locked_debug(monitor_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (!hasLock(locks, lock, MONITOR)) {
- _objc_fatal("monitor %s incorrectly not held\n",name+1);
- }
- }
-}
-
-PRIVATE_EXTERN void
-_monitor_assert_unlocked_debug(monitor_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (hasLock(locks, lock, MONITOR)) {
- _objc_fatal("monitor %s incorrectly held\n", name+1);
- }
- }
-}
-
-
-/***********************************************************************
-* rwlock checking
-**********************************************************************/
-
-PRIVATE_EXTERN void
-_rwlock_read_debug(rwlock_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(YES);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (hasLock(locks, lock, RDLOCK)) {
- // Recursive rwlock read is bad (may deadlock vs pending writer)
- _objc_fatal("recursive rwlock read %s\n", name+1);
- }
- if (hasLock(locks, lock, WRLOCK)) {
- _objc_fatal("deadlock: read after write for rwlock %s\n", name+1);
- }
- setLock(locks, lock, RDLOCK);
- }
-
- _rwlock_read_nodebug(lock);
-}
-
-PRIVATE_EXTERN int
-_rwlock_try_read_debug(rwlock_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(YES);
-
- // try-read when already reading is OK (won't deadlock against writer)
- // try-read when already writing is OK (will fail)
- int result = _rwlock_try_read_nodebug(lock);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (result) {
- setLock(locks, lock, RDLOCK);
- }
- }
- return result;
-}
-
-PRIVATE_EXTERN void
-_rwlock_unlock_read_debug(rwlock_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (!hasLock(locks, lock, RDLOCK)) {
- _objc_fatal("un-reading unowned rwlock %s\n", name+1);
- }
- clearLock(locks, lock, RDLOCK);
- }
-
- _rwlock_unlock_read_nodebug(lock);
-}
-
-PRIVATE_EXTERN void
-_rwlock_write_debug(rwlock_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(YES);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (hasLock(locks, lock, RDLOCK)) {
- // Lock promotion not allowed (may deadlock)
- _objc_fatal("deadlock: write after read for rwlock %s\n", name+1);
- }
- if (hasLock(locks, lock, WRLOCK)) {
- _objc_fatal("recursive rwlock write %s\n", name+1);
- }
- setLock(locks, lock, WRLOCK);
- }
-
- _rwlock_write_nodebug(lock);
-}
-
-
-PRIVATE_EXTERN int
-_rwlock_try_write_debug(rwlock_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(YES);
-
- // try-write when already reading is OK (will fail)
- // try-write when already writing is OK (will fail)
- int result = _rwlock_try_write_nodebug(lock);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (result) {
- setLock(locks, lock, WRLOCK);
- }
- }
- return result;
-}
-
-PRIVATE_EXTERN void
-_rwlock_unlock_write_debug(rwlock_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (!hasLock(locks, lock, WRLOCK)) {
- _objc_fatal("un-writing unowned rwlock %s\n", name+1);
- }
- clearLock(locks, lock, WRLOCK);
- }
-
- _rwlock_unlock_write_nodebug(lock);
-}
-
-
-PRIVATE_EXTERN void
-_rwlock_assert_reading_debug(rwlock_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (!hasLock(locks, lock, RDLOCK)) {
- _objc_fatal("rwlock %s incorrectly not reading\n", name+1);
- }
- }
-}
-
-PRIVATE_EXTERN void
-_rwlock_assert_writing_debug(rwlock_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (!hasLock(locks, lock, WRLOCK)) {
- _objc_fatal("rwlock %s incorrectly not writing\n", name+1);
- }
- }
-}
-
-PRIVATE_EXTERN void
-_rwlock_assert_locked_debug(rwlock_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (!hasLock(locks, lock, RDLOCK) && !hasLock(locks, lock, WRLOCK)) {
- _objc_fatal("rwlock %s incorrectly neither reading nor writing\n",
- name+1);
- }
- }
-}
-
-PRIVATE_EXTERN void
-_rwlock_assert_unlocked_debug(rwlock_t *lock, const char *name)
-{
- _objc_lock_list *locks = getLocks(NO);
-
- if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
- if (hasLock(locks, lock, RDLOCK) || hasLock(locks, lock, WRLOCK)) {
- _objc_fatal("rwlock %s incorrectly not unlocked\n", name+1);
- }
- }
-}
-
-
-#endif
--- /dev/null
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/***********************************************************************
+* objc-lock.m
+* Error-checking locks for debugging.
+**********************************************************************/
+
+#include "objc-private.h"
+
+#if !defined(NDEBUG) && !TARGET_OS_WIN32
+
+/***********************************************************************
+* Recording - per-thread list of mutexes and monitors held
+**********************************************************************/
+
+typedef struct {
+ void *l; // the lock itself
+ int k; // the kind of lock it is (MUTEX, MONITOR, etc)
+ int i; // the lock's nest count
+} lockcount;
+
+#define MUTEX 1
+#define MONITOR 2
+#define RDLOCK 3
+#define WRLOCK 4
+#define RECURSIVE 5
+
+typedef struct _objc_lock_list {
+ int allocated;
+ int used;
+ lockcount list[0];
+} _objc_lock_list;
+
+static tls_key_t lock_tls;
+
+static void
+destroyLocks(void *value)
+{
+ _objc_lock_list *locks = (_objc_lock_list *)value;
+ // fixme complain about any still-held locks?
+ if (locks) _free_internal(locks);
+}
+
+static struct _objc_lock_list *
+getLocks(BOOL create)
+{
+ _objc_lock_list *locks;
+
+ // Use a dedicated tls key to prevent differences vs non-debug in
+ // usage of objc's other tls keys (required for some unit tests).
+ INIT_ONCE_PTR(lock_tls, tls_create(&destroyLocks), (void)0);
+
+ locks = (_objc_lock_list *)tls_get(lock_tls);
+ if (!locks) {
+ if (!create) {
+ return NULL;
+ } else {
+ 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);
+ }
+ }
+
+ if (locks->allocated == locks->used) {
+ if (!create) {
+ return locks;
+ } else {
+ _objc_lock_list *oldlocks = locks;
+ 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));
+ tls_set(lock_tls, locks);
+ _free_internal(oldlocks);
+ }
+ }
+
+ return locks;
+}
+
+static BOOL
+hasLock(_objc_lock_list *locks, void *lock, int kind)
+{
+ int i;
+ if (!locks) return NO;
+
+ for (i = 0; i < locks->used; i++) {
+ if (locks->list[i].l == lock && locks->list[i].k == kind) return YES;
+ }
+ return NO;
+}
+
+
+static void
+setLock(_objc_lock_list *locks, void *lock, int kind)
+{
+ int i;
+ for (i = 0; i < locks->used; i++) {
+ if (locks->list[i].l == lock && locks->list[i].k == kind) {
+ locks->list[i].i++;
+ return;
+ }
+ }
+
+ locks->list[locks->used].l = lock;
+ locks->list[locks->used].i = 1;
+ locks->list[locks->used].k = kind;
+ locks->used++;
+}
+
+static void
+clearLock(_objc_lock_list *locks, void *lock, int kind)
+{
+ int i;
+ for (i = 0; i < locks->used; i++) {
+ if (locks->list[i].l == lock && locks->list[i].k == kind) {
+ if (--locks->list[i].i == 0) {
+ locks->list[i].l = NULL;
+ locks->list[i] = locks->list[--locks->used];
+ }
+ return;
+ }
+ }
+
+ _objc_fatal("lock not found!");
+}
+
+
+/***********************************************************************
+* Mutex checking
+**********************************************************************/
+
+int
+_mutex_lock_debug(mutex_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(YES);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (hasLock(locks, lock, MUTEX)) {
+ _objc_fatal("deadlock: relocking mutex %s\n", name+1);
+ }
+ setLock(locks, lock, MUTEX);
+ }
+
+ return _mutex_lock_nodebug(lock);
+}
+
+int
+_mutex_try_lock_debug(mutex_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(YES);
+
+ // attempting to relock in try_lock is OK
+ int result = _mutex_try_lock_nodebug(lock);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (result) {
+ setLock(locks, lock, MUTEX);
+ }
+ }
+ return result;
+}
+
+int
+_mutex_unlock_debug(mutex_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (!hasLock(locks, lock, MUTEX)) {
+ _objc_fatal("unlocking unowned mutex %s\n", name+1);
+ }
+ clearLock(locks, lock, MUTEX);
+ }
+
+ return _mutex_unlock_nodebug(lock);
+}
+
+void
+_mutex_assert_locked_debug(mutex_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (!hasLock(locks, lock, MUTEX)) {
+ _objc_fatal("mutex %s incorrectly not held\n",name+1);
+ }
+ }
+}
+
+
+void
+_mutex_assert_unlocked_debug(mutex_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (hasLock(locks, lock, MUTEX)) {
+ _objc_fatal("mutex %s incorrectly held\n", name+1);
+ }
+ }
+}
+
+
+/***********************************************************************
+* Recursive mutex checking
+**********************************************************************/
+
+int
+_recursive_mutex_lock_debug(recursive_mutex_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(YES);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ setLock(locks, lock, RECURSIVE);
+ }
+
+ return _recursive_mutex_lock_nodebug(lock);
+}
+
+int
+_recursive_mutex_try_lock_debug(recursive_mutex_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(YES);
+
+ int result = _recursive_mutex_try_lock_nodebug(lock);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (result) {
+ setLock(locks, lock, RECURSIVE);
+ }
+ }
+ return result;
+}
+
+int
+_recursive_mutex_unlock_debug(recursive_mutex_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (!hasLock(locks, lock, RECURSIVE)) {
+ _objc_fatal("unlocking unowned recursive mutex %s\n", name+1);
+ }
+ clearLock(locks, lock, RECURSIVE);
+ }
+
+ return _recursive_mutex_unlock_nodebug(lock);
+}
+
+void
+_recursive_mutex_assert_locked_debug(recursive_mutex_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (!hasLock(locks, lock, RECURSIVE)) {
+ _objc_fatal("recursive mutex %s incorrectly not held\n",name+1);
+ }
+ }
+}
+
+
+void
+_recursive_mutex_assert_unlocked_debug(recursive_mutex_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (hasLock(locks, lock, RECURSIVE)) {
+ _objc_fatal("recursive mutex %s incorrectly held\n", name+1);
+ }
+ }
+}
+
+
+/***********************************************************************
+* Monitor checking
+**********************************************************************/
+
+int
+_monitor_enter_debug(monitor_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(YES);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (hasLock(locks, lock, MONITOR)) {
+ _objc_fatal("deadlock: relocking monitor %s\n", name+1);
+ }
+ setLock(locks, lock, MONITOR);
+ }
+
+ return _monitor_enter_nodebug(lock);
+}
+
+int
+_monitor_exit_debug(monitor_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (!hasLock(locks, lock, MONITOR)) {
+ _objc_fatal("unlocking unowned monitor%s\n", name+1);
+ }
+ clearLock(locks, lock, MONITOR);
+ }
+
+ return _monitor_exit_nodebug(lock);
+}
+
+int
+_monitor_wait_debug(monitor_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (!hasLock(locks, lock, MONITOR)) {
+ _objc_fatal("waiting in unowned monitor%s\n", name+1);
+ }
+ }
+
+ return _monitor_wait_nodebug(lock);
+}
+
+void
+_monitor_assert_locked_debug(monitor_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (!hasLock(locks, lock, MONITOR)) {
+ _objc_fatal("monitor %s incorrectly not held\n",name+1);
+ }
+ }
+}
+
+void
+_monitor_assert_unlocked_debug(monitor_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (hasLock(locks, lock, MONITOR)) {
+ _objc_fatal("monitor %s incorrectly held\n", name+1);
+ }
+ }
+}
+
+
+/***********************************************************************
+* rwlock checking
+**********************************************************************/
+
+void
+_rwlock_read_debug(rwlock_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(YES);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (hasLock(locks, lock, RDLOCK)) {
+ // Recursive rwlock read is bad (may deadlock vs pending writer)
+ _objc_fatal("recursive rwlock read %s\n", name+1);
+ }
+ if (hasLock(locks, lock, WRLOCK)) {
+ _objc_fatal("deadlock: read after write for rwlock %s\n", name+1);
+ }
+ setLock(locks, lock, RDLOCK);
+ }
+
+ _rwlock_read_nodebug(lock);
+}
+
+int
+_rwlock_try_read_debug(rwlock_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(YES);
+
+ // try-read when already reading is OK (won't deadlock against writer)
+ // try-read when already writing is OK (will fail)
+ int result = _rwlock_try_read_nodebug(lock);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (result) {
+ setLock(locks, lock, RDLOCK);
+ }
+ }
+ return result;
+}
+
+void
+_rwlock_unlock_read_debug(rwlock_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (!hasLock(locks, lock, RDLOCK)) {
+ _objc_fatal("un-reading unowned rwlock %s\n", name+1);
+ }
+ clearLock(locks, lock, RDLOCK);
+ }
+
+ _rwlock_unlock_read_nodebug(lock);
+}
+
+void
+_rwlock_write_debug(rwlock_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(YES);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (hasLock(locks, lock, RDLOCK)) {
+ // Lock promotion not allowed (may deadlock)
+ _objc_fatal("deadlock: write after read for rwlock %s\n", name+1);
+ }
+ if (hasLock(locks, lock, WRLOCK)) {
+ _objc_fatal("recursive rwlock write %s\n", name+1);
+ }
+ setLock(locks, lock, WRLOCK);
+ }
+
+ _rwlock_write_nodebug(lock);
+}
+
+
+int
+_rwlock_try_write_debug(rwlock_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(YES);
+
+ // try-write when already reading is OK (will fail)
+ // try-write when already writing is OK (will fail)
+ int result = _rwlock_try_write_nodebug(lock);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (result) {
+ setLock(locks, lock, WRLOCK);
+ }
+ }
+ return result;
+}
+
+void
+_rwlock_unlock_write_debug(rwlock_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (!hasLock(locks, lock, WRLOCK)) {
+ _objc_fatal("un-writing unowned rwlock %s\n", name+1);
+ }
+ clearLock(locks, lock, WRLOCK);
+ }
+
+ _rwlock_unlock_write_nodebug(lock);
+}
+
+
+void
+_rwlock_assert_reading_debug(rwlock_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (!hasLock(locks, lock, RDLOCK)) {
+ _objc_fatal("rwlock %s incorrectly not reading\n", name+1);
+ }
+ }
+}
+
+void
+_rwlock_assert_writing_debug(rwlock_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (!hasLock(locks, lock, WRLOCK)) {
+ _objc_fatal("rwlock %s incorrectly not writing\n", name+1);
+ }
+ }
+}
+
+void
+_rwlock_assert_locked_debug(rwlock_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (!hasLock(locks, lock, RDLOCK) && !hasLock(locks, lock, WRLOCK)) {
+ _objc_fatal("rwlock %s incorrectly neither reading nor writing\n",
+ name+1);
+ }
+ }
+}
+
+void
+_rwlock_assert_unlocked_debug(rwlock_t *lock, const char *name)
+{
+ _objc_lock_list *locks = getLocks(NO);
+
+ if (! (DebuggerMode && isManagedDuringDebugger(lock))) {
+ if (hasLock(locks, lock, RDLOCK) || hasLock(locks, lock, WRLOCK)) {
+ _objc_fatal("rwlock %s incorrectly not unlocked\n", name+1);
+ }
+ }
+}
+
+
+#endif
--- /dev/null
+/*
+ * 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 <objc-shared-cache.h>
+
+__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
# include <System/pthread_machdep.h>
# 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;
{
__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 <CrashReporterClient.h>
#else
# if __cplusplus
# include <vector>
# include <algorithm>
+# include <functional>
using namespace std;
-# include <ext/hash_map>
- using namespace __gnu_cxx;
# endif
# define PRIVATE_EXTERN __attribute__((visibility("hidden")))
/* 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
# if __cplusplus
# include <vector>
# include <algorithm>
+# include <functional>
using namespace std;
-# include <hash_map>
- using namespace stdext;
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
# else
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);
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));
}
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); }
*/
#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
}
#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);
}
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);
pthread_setspecific(k, value);
}
}
+
+// not arm
+#endif
+
+// SUPPORT_DIRECT_THREAD_KEYS
#endif
}
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);
}
}
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);
}
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);
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)
}
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)
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);
}
}
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)
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);
}
#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 */
+++ /dev/null
-/*
- * Copyright (c) 2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/***********************************************************************
-* objc-os.m
-* OS portability layer.
-**********************************************************************/
-
-#include "objc-os.h"
-#include "objc-private.h"
-#include "objc-loadmethod.h"
-
-#if TARGET_OS_WIN32
-
-#include "objc-runtime-old.h"
-#include "objcrt.h"
-
-malloc_zone_t *_objc_internal_zone(void)
-{
- return NULL;
-}
-
-int monitor_init(monitor_t *c)
-{
- // fixme error checking
- HANDLE mutex = CreateMutex(NULL, TRUE, NULL);
- while (!c->mutex) {
- // fixme memory barrier here?
- if (0 == InterlockedCompareExchangePointer(&c->mutex, mutex, 0)) {
- // we win - finish construction
- c->waiters = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
- c->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL);
- InitializeCriticalSection(&c->waitCountLock);
- c->waitCount = 0;
- c->didBroadcast = 0;
- ReleaseMutex(c->mutex);
- return 0;
- }
- }
-
- // someone else allocated the mutex and constructed the monitor
- ReleaseMutex(mutex);
- CloseHandle(mutex);
- return 0;
-}
-
-void mutex_init(mutex_t *m)
-{
- while (!m->lock) {
- CRITICAL_SECTION *newlock = malloc(sizeof(CRITICAL_SECTION));
- InitializeCriticalSection(newlock);
- // fixme memory barrier here?
- if (0 == InterlockedCompareExchangePointer(&m->lock, newlock, 0)) {
- return;
- }
- // someone else installed their lock first
- DeleteCriticalSection(newlock);
- free(newlock);
- }
-}
-
-
-void recursive_mutex_init(recursive_mutex_t *m)
-{
- // fixme error checking
- HANDLE newmutex = CreateMutex(NULL, FALSE, NULL);
- while (!m->mutex) {
- // fixme memory barrier here?
- if (0 == InterlockedCompareExchangePointer(&m->mutex, newmutex, 0)) {
- // we win
- return;
- }
- }
-
- // someone else installed their lock first
- CloseHandle(newmutex);
-}
-
-
-WINBOOL APIENTRY DllMain( HMODULE hModule,
- DWORD ul_reason_for_call,
- LPVOID lpReserved
- )
-{
- switch (ul_reason_for_call) {
- case DLL_PROCESS_ATTACH:
- environ_init();
- tls_init();
- lock_init();
- sel_init(NO);
- exception_init();
- break;
-
- case DLL_THREAD_ATTACH:
- break;
-
- case DLL_THREAD_DETACH:
- case DLL_PROCESS_DETACH:
- break;
- }
- return TRUE;
-}
-
-OBJC_EXPORT void *_objc_init_image(HMODULE image, const objc_sections *sects)
-{
- header_info *hi = _malloc_internal(sizeof(header_info));
- size_t count, i;
-
- 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;
-
- count = 0;
- for (i = 0; i < hi->os.moduleCount; i++) {
- if (hi->os.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));
- }
- }
-
- hi->os.moduleName = malloc(MAX_PATH * sizeof(TCHAR));
- GetModuleFileName((HMODULE)(hi->mhdr), hi->os.moduleName, MAX_PATH * sizeof(TCHAR));
-
- _objc_appendHeader(hi);
-
- if (PrintImages) {
- _objc_inform("IMAGES: loading image for %s%s%s\n",
- _nameForHeader(hi->mhdr),
- headerIsBundle(hi) ? " (bundle)" : "",
- _objcHeaderIsReplacement(hi) ? " (replacement)":"");
- }
-
- _read_images(&hi, 1);
-
- return hi;
-}
-
-OBJC_EXPORT void _objc_load_image(HMODULE image, header_info *hinfo)
-{
- prepare_load_methods(hinfo);
- call_load_methods();
-}
-
-OBJC_EXPORT void _objc_unload_image(HMODULE image, header_info *hinfo)
-{
- _objc_fatal("image unload not supported");
-}
-
-
-PRIVATE_EXTERN bool crashlog_header_name(header_info *hi)
-{
- return true;
-}
-
-
-// TARGET_OS_WIN32
-#elif TARGET_OS_MAC
-
-#if !__OBJC2__
-#include "objc-file-old.h"
-#endif
-
-PRIVATE_EXTERN void mutex_init(mutex_t *m)
-{
- pthread_mutex_init(m, NULL);
-}
-
-
-PRIVATE_EXTERN void recursive_mutex_init(recursive_mutex_t *m)
-{
- // fixme error checking
- pthread_mutex_t *newmutex;
-
- // Build recursive mutex attributes, if needed
- static pthread_mutexattr_t *attr;
- if (!attr) {
- pthread_mutexattr_t *newattr =
- _malloc_internal(sizeof(pthread_mutexattr_t));
- pthread_mutexattr_init(newattr);
- pthread_mutexattr_settype(newattr, PTHREAD_MUTEX_RECURSIVE);
- while (!attr) {
- if (OSAtomicCompareAndSwapPtrBarrier(0, newattr, (void**)&attr)) {
- // we win
- goto attr_done;
- }
- }
- // someone else built the attr first
- _free_internal(newattr);
- }
- attr_done:
-
- // Build the mutex itself
- newmutex = _malloc_internal(sizeof(pthread_mutex_t));
- pthread_mutex_init(newmutex, attr);
- while (!m->mutex) {
- if (OSAtomicCompareAndSwapPtrBarrier(0, newmutex, (void**)&m->mutex)) {
- // we win
- return;
- }
- }
-
- // someone else installed their mutex first
- pthread_mutex_destroy(newmutex);
-}
-
-
-/***********************************************************************
-* bad_magic.
-* Return YES if the header has invalid Mach-o magic.
-**********************************************************************/
-PRIVATE_EXTERN BOOL bad_magic(const headerType *mhdr)
-{
- return (mhdr->magic != MH_MAGIC && mhdr->magic != MH_MAGIC_64 &&
- mhdr->magic != MH_CIGAM && mhdr->magic != MH_CIGAM_64);
-}
-
-
-static header_info * _objc_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;
-
- if (bad_magic(mhdr)) return NULL;
-
- // Weed out duplicates
- for (result = FirstHeader; result; result = result->next) {
- if (mhdr == result->mhdr) return NULL;
- }
-
- // 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;
-
- // Allocate a header_info entry.
- result = _calloc_internal(sizeof(header_info), 1);
-
- // Set up the new header_info entry.
- result->mhdr = mhdr;
-#if !__OBJC2__
- // mhdr must already be set
- result->mod_count = 0;
- result->mod_ptr = _getObjcModules(result, &result->mod_count);
-#endif
- result->info = image_info;
- dladdr(result->mhdr, &result->os.dl_info);
- result->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);
- }
- }
-
- _objc_appendHeader(result);
-
- return result;
-}
-
-
-#if !SUPPORT_GC
-
-PRIVATE_EXTERN const char *_gcForHInfo(const header_info *hinfo)
-{
- return "";
-}
-PRIVATE_EXTERN const char *_gcForHInfo2(const header_info *hinfo)
-{
- return "";
-}
-
-#else
-
-/***********************************************************************
-* _gcForHInfo.
-**********************************************************************/
-PRIVATE_EXTERN const char *_gcForHInfo(const header_info *hinfo)
-{
- if (_objcHeaderRequiresGC(hinfo)) {
- if (_objcHeaderSupportsCompaction(hinfo))
- return "requires GC, supports compaction";
- else
- return "requires GC";
- } else if (_objcHeaderSupportsGC(hinfo)) {
- if (_objcHeaderSupportsCompaction(hinfo))
- return "supports GC, supports compaction";
- else
- return "supports GC";
- } else {
- return "does not support GC";
- }
-}
-PRIVATE_EXTERN const char *_gcForHInfo2(const header_info *hinfo)
-{
- if (_objcHeaderRequiresGC(hinfo)) {
- if (_objcHeaderSupportsCompaction(hinfo))
- return "(requires GC) (supports compaction)";
- else
- return "(requires GC)";
- } else if (_objcHeaderSupportsGC(hinfo)) {
- if (_objcHeaderSupportsCompaction(hinfo))
- return "(supports GC) (supports compaction)";
- else
- return "(supports GC)";
- }
- return "";
-}
-
-
-/***********************************************************************
-* check_gc
-* Check whether the executable supports or requires GC, and make sure
-* all already-loaded libraries support the executable's GC mode.
-* Returns TRUE if the executable wants GC on.
-**********************************************************************/
-static void check_wants_gc(BOOL *appWantsGC, BOOL *appSupportsCompaction)
-{
- const header_info *hi;
-
- // Environment variables can override the following.
- if (DisableGC) {
- _objc_inform("GC: forcing GC OFF because OBJC_DISABLE_GC is set");
- *appWantsGC = NO;
- *appSupportsCompaction = NO;
- }
- else {
- // Find the executable and check its GC bits.
- // If the executable cannot be found, default to NO.
- // (The executable will not be found if the executable contains
- // no Objective-C code.)
- *appWantsGC = NO;
- *appSupportsCompaction = NO;
- for (hi = FirstHeader; hi != NULL; hi = hi->next) {
- if (hi->mhdr->filetype == MH_EXECUTE) {
- *appWantsGC = _objcHeaderSupportsGC(hi) ? YES : NO;
- *appSupportsCompaction = (*appWantsGC && _objcHeaderSupportsCompaction(hi)) ? YES : NO;
- if (PrintGC) {
- _objc_inform("GC: executable '%s' %s",
- _nameForHeader(hi->mhdr), _gcForHInfo(hi));
- }
- }
- }
- }
-}
-
-
-/***********************************************************************
-* verify_gc_readiness
-* if we want gc, verify that every header describes files compiled
-* and presumably ready for gc.
-************************************************************************/
-static void verify_gc_readiness(BOOL wantsGC, BOOL *wantsCompaction,
- header_info **hList, uint32_t hCount)
-{
- BOOL busted = NO;
- uint32_t i;
-
- // Find the libraries and check their GC bits against the app's request
- for (i = 0; i < hCount; i++) {
- header_info *hi = hList[i];
- if (hi->mhdr->filetype == MH_EXECUTE) {
- continue;
- }
- else if (hi->mhdr == &_mh_dylib_header) {
- // libobjc itself works with anything even though it is not
- // compiled with -fobjc-gc (fixme should it be?)
- }
- else if (wantsGC && ! _objcHeaderSupportsGC(hi)) {
- // App wants GC but library does not support it - bad
- _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));
- busted = YES;
- }
- else if (!wantsGC && _objcHeaderRequiresGC(hi)) {
- // App doesn't want GC but library requires it - bad
- _objc_inform_now_and_on_crash
- ("'%s' was compiled with -fobjc-gc-only, "
- "but the application does not support GC",
- _nameForHeader(hi->mhdr));
- busted = YES;
- }
-
- if (*wantsCompaction && !_objcHeaderSupportsCompaction(hi)) {
- // App supports compaction, but library doesn't.
- _objc_inform_now_and_on_crash
- ("'%s' was not linked with -Xlinker -objc_gc_compaction, "
- "but the application wants compaction.",
- _nameForHeader(hi->mhdr));
- // Simply warn for now until radars are filed. Eventually,
- // objc_disableCompaction() will block until any current compaction completes.
- objc_disableCompaction();
- *wantsCompaction = NO;
- }
-
- if (PrintGC) {
- _objc_inform("GC: library '%s' %s",
- _nameForHeader(hi->mhdr), _gcForHInfo(hi));
- }
- }
-
- if (busted) {
- // GC state is not consistent.
- // Kill the process unless one of the forcing flags is set.
- if (!DisableGC) {
- _objc_fatal("*** GC capability of application and some libraries did not match");
- }
- }
-}
-
-
-/***********************************************************************
-* gc_enforcer
-* Make sure that images about to be loaded by dyld are GC-acceptable.
-* Images linked to the executable are always permitted; they are
-* enforced inside map_images() itself.
-**********************************************************************/
-static BOOL InitialDyldRegistration = NO;
-static const char *gc_enforcer(enum dyld_image_states state,
- uint32_t infoCount,
- const struct dyld_image_info info[])
-{
- uint32_t i;
-
- // Linked images get a free pass
- if (InitialDyldRegistration) return NULL;
-
- if (PrintImages) {
- _objc_inform("IMAGES: checking %d images for compatibility...",
- infoCount);
- }
-
- for (i = 0; i < infoCount; i++) {
- crashlog_header_name_string(info[i].imageFilePath);
-
- const headerType *mhdr = (const headerType *)info[i].imageLoadAddress;
- if (bad_magic(mhdr)) continue;
-
- objc_image_info *image_info;
- size_t size;
-
- if (mhdr == &_mh_dylib_header) {
- // libobjc itself - OK
- continue;
- }
-
-#if !__OBJC2__
- unsigned long seg_size;
- // 32-bit: __OBJC seg but no image_info means no GC support
- if (!getsegmentdata(mhdr, "__OBJC", &seg_size)) {
- // not objc - assume OK
- continue;
- }
- image_info = _getObjcImageInfo(mhdr, &size);
- if (!image_info) {
- // No image_info - assume GC unsupported
- if (!UseGC) {
- // GC is OFF - ok
- continue;
- } else {
- // GC is ON - bad
- if (PrintImages || PrintGC) {
- _objc_inform("IMAGES: rejecting %d images because %s doesn't support GC (no image_info)", infoCount, info[i].imageFilePath);
- }
- goto reject;
- }
- }
-#else
- // 64-bit: no image_info means no objc at all
- image_info = _getObjcImageInfo(mhdr, &size);
- if (!image_info) {
- // not objc - assume OK
- continue;
- }
-#endif
-
- if (UseGC && !_objcInfoSupportsGC(image_info)) {
- // GC is ON, but image does not support GC
- if (PrintImages || PrintGC) {
- _objc_inform("IMAGES: rejecting %d images because %s doesn't support GC", infoCount, info[i].imageFilePath);
- }
- goto reject;
- }
- if (!UseGC && _objcInfoRequiresGC(image_info)) {
- // GC is OFF, but image requires GC
- if (PrintImages || PrintGC) {
- _objc_inform("IMAGES: rejecting %d images because %s requires GC", infoCount, info[i].imageFilePath);
- }
- goto reject;
- }
- }
-
- crashlog_header_name_string(NULL);
- return NULL;
-
- reject:
- crashlog_header_name_string(NULL);
- return "GC capability mismatch";
-}
-
-// SUPPORT_GC
-#endif
-
-
-/***********************************************************************
-* map_images_nolock
-* Process the given images which are being mapped in by dyld.
-* All class registration and fixups are performed (or deferred pending
-* discovery of missing superclasses etc), and +load methods are called.
-*
-* info[] is in bottom-up order i.e. libobjc will be earlier in the
-* array than any library that links to libobjc.
-*
-* Locking: loadMethodLock(old) or runtimeLock(new) acquired by map_images.
-**********************************************************************/
-PRIVATE_EXTERN const char *
-map_images_nolock(enum dyld_image_states state, uint32_t infoCount,
- const struct dyld_image_info infoList[])
-{
- static BOOL firstTime = YES;
- static BOOL wantsGC = NO;
- static BOOL wantsCompaction = NO;
- uint32_t i;
- header_info *hi;
- header_info *hList[infoCount];
- uint32_t hCount;
-
- // 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) {
-#if SUPPORT_GC
- InitialDyldRegistration = YES;
- dyld_register_image_state_change_handler(dyld_image_state_mapped, 0 /* batch */, &gc_enforcer);
- InitialDyldRegistration = NO;
-#endif
- }
-
- if (PrintImages) {
- _objc_inform("IMAGES: processing %u newly-mapped images...\n", infoCount);
- }
-
-
- // Find all images with Objective-C metadata.
- hCount = 0;
- i = infoCount;
- while (i--) {
- const headerType *mhdr = (headerType *)infoList[i].imageLoadAddress;
-
- hi = _objc_addHeader(mhdr);
- if (!hi) {
- // no objc data in this entry
- continue;
- }
-
- hList[hCount++] = hi;
-
-
- if (PrintImages) {
- _objc_inform("IMAGES: loading image for %s%s%s%s%s\n",
- _nameForHeader(mhdr),
- mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
- _objcHeaderIsReplacement(hi) ? " (replacement)" : "",
- _objcHeaderOptimizedByDyld(hi)?" (preoptimized)" : "",
- _gcForHInfo2(hi));
- }
- }
-
- // Perform one-time runtime initialization that must be deferred until
- // the executable itself is found. This needs to be done before
- // further initialization.
- // (The executable may not be present in this infoList if the
- // executable does not contain Objective-C code but Objective-C
- // is dynamically loaded later. In that case, check_wants_gc()
- // will do the right thing.)
-#if SUPPORT_GC
- if (firstTime) {
- check_wants_gc(&wantsGC, &wantsCompaction);
-
- verify_gc_readiness(wantsGC, &wantsCompaction, hList, hCount);
-
- gc_init(wantsGC, wantsCompaction); // needs executable for GC decision
- rtp_init(); // needs GC decision first
- } else {
- verify_gc_readiness(wantsGC, &wantsCompaction, hList, hCount);
- }
-
- if (wantsGC) {
- // tell the collector about the data segment ranges.
- for (i = 0; i < hCount; ++i) {
- uint8_t *seg;
- unsigned long seg_size;
- hi = hList[i];
-
- seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size);
- if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
-
- seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
- if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
- // __OBJC contains no GC data, but pointers to it are
- // used as associated reference values (rdar://6953570)
- }
- }
-
- // Need to fixup barriers in all libraries that call into libobjc, whether GC is on or not.
- for (i = 0; i < infoCount; ++i) {
- gc_fixup_barrier_stubs(&infoList[i]);
- }
-#endif
-
- if (firstTime) {
- extern SEL FwdSel; // in objc-msg-*.s
- sel_init(wantsGC);
- FwdSel = sel_registerName("forward::");
-
- arr_init();
- }
-
- _read_images(hList, hCount);
-
- firstTime = NO;
-
- return NULL;
-}
-
-
-/***********************************************************************
-* load_images_nolock
-* Prepares +load in the given images which are being mapped in by dyld.
-* Returns YES if there are now +load methods to be called by call_load_methods.
-*
-* Locking: loadMethodLock(both) and runtimeLock(new) acquired by load_images
-**********************************************************************/
-PRIVATE_EXTERN BOOL
-load_images_nolock(enum dyld_image_states state,uint32_t infoCount,
- const struct dyld_image_info infoList[])
-{
- BOOL found = NO;
- uint32_t i;
-
- i = infoCount;
- while (i--) {
- header_info *hi;
- for (hi = FirstHeader; hi != NULL; hi = hi->next) {
- const headerType *mhdr = (headerType*)infoList[i].imageLoadAddress;
- if (hi->mhdr == mhdr) {
- prepare_load_methods(hi);
- found = YES;
- }
- }
- }
-
- return found;
-}
-
-
-/***********************************************************************
-* unmap_image_nolock
-* Process the given image which is about to be unmapped by dyld.
-* mh is mach_header instead of headerType because that's what
-* dyld_priv.h says even for 64-bit.
-*
-* Locking: loadMethodLock(both) and runtimeLock(new) acquired by unmap_image.
-**********************************************************************/
-PRIVATE_EXTERN void
-unmap_image_nolock(const struct mach_header *mh)
-{
- if (PrintImages) {
- _objc_inform("IMAGES: processing 1 newly-unmapped image...\n");
- }
-
- header_info *hi;
-
- // Find the runtime's header_info struct for the image
- for (hi = FirstHeader; hi != NULL; hi = hi->next) {
- if (hi->mhdr == (const headerType *)mh) {
- break;
- }
- }
-
- if (!hi) return;
-
- if (PrintImages) {
- _objc_inform("IMAGES: unloading image for %s%s%s%s\n",
- _nameForHeader(hi->mhdr),
- hi->mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
- _objcHeaderIsReplacement(hi) ? " (replacement)" : "",
- _gcForHInfo2(hi));
- }
-
-#if SUPPORT_GC
- if (UseGC) {
- uint8_t *seg;
- unsigned long seg_size;
-
- seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size);
- if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
-
- seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
- if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
- }
-#endif
-
- _unload_image(hi);
-
- // Remove header_info from header list
- _objc_removeHeader(hi);
- _free_internal(hi);
-}
-
-
-/***********************************************************************
-* _objc_init
-* Static initializer. Registers our image notifier with dyld.
-**********************************************************************/
-static __attribute__((constructor))
-void _objc_init(void)
-{
- // fixme defer initialization until an objc-using image is found?
- environ_init();
- tls_init();
- lock_init();
- exception_init();
-
- // Register for unmap first, in case some +load unmaps something
- _dyld_register_func_for_remove_image(&unmap_image);
- dyld_register_image_state_change_handler(dyld_image_state_bound,
- 1/*batch*/, &map_images);
- dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images);
-}
-
-
-/***********************************************************************
-* _headerForAddress.
-* addr can be a class or a category
-**********************************************************************/
-static const header_info *_headerForAddress(void *addr)
-{
-#if __OBJC2__
- const char *segname = "__DATA";
-#else
- const char *segname = "__OBJC";
-#endif
- header_info *hi;
-
- // Check all headers in the vector
- for (hi = FirstHeader; hi != NULL; hi = hi->next)
- {
- uint8_t *seg;
- unsigned long seg_size;
-
- seg = getsegmentdata(hi->mhdr, segname, &seg_size);
- if (!seg) continue;
-
- // Is the class in this header?
- if ((uint8_t *)addr >= seg && (uint8_t *)addr < seg + seg_size)
- return hi;
- }
-
- // Not found
- return 0;
-}
-
-
-/***********************************************************************
-* _headerForClass
-* 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)
-{
- return _headerForAddress(cls);
-}
-
-
-/**********************************************************************
-* secure_open
-* Securely open a file from a world-writable directory (like /tmp)
-* If the file does not exist, it will be atomically created with mode 0600
-* If the file exists, it must be, and remain after opening:
-* 1. a regular file (in particular, not a symlink)
-* 2. owned by euid
-* 3. permissions 0600
-* 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)
-{
- struct stat fs, ls;
- int fd = -1;
- BOOL truncate = NO;
- BOOL create = NO;
-
- if (flags & O_TRUNC) {
- // Don't truncate the file until after it is open and verified.
- truncate = YES;
- flags &= ~O_TRUNC;
- }
- if (flags & O_CREAT) {
- // Don't create except when we're ready for it
- create = YES;
- flags &= ~O_CREAT;
- flags &= ~O_EXCL;
- }
-
- if (lstat(filename, &ls) < 0) {
- if (errno == ENOENT && create) {
- // No such file - create it
- fd = open(filename, flags | O_CREAT | O_EXCL, 0600);
- if (fd >= 0) {
- // File was created successfully.
- // New file does not need to be truncated.
- return fd;
- } else {
- // File creation failed.
- return -1;
- }
- } else {
- // lstat failed, or user doesn't want to create the file
- return -1;
- }
- } else {
- // lstat succeeded - verify attributes and open
- if (S_ISREG(ls.st_mode) && // regular file?
- ls.st_nlink == 1 && // link count == 1?
- ls.st_uid == euid && // owned by euid?
- (ls.st_mode & ALLPERMS) == (S_IRUSR | S_IWUSR)) // mode 0600?
- {
- // Attributes look ok - open it and check attributes again
- fd = open(filename, flags, 0000);
- if (fd >= 0) {
- // File is open - double-check attributes
- if (0 == fstat(fd, &fs) &&
- fs.st_nlink == ls.st_nlink && // link count == 1?
- fs.st_uid == ls.st_uid && // owned by euid?
- fs.st_mode == ls.st_mode && // regular file, 0600?
- fs.st_ino == ls.st_ino && // same inode as before?
- fs.st_dev == ls.st_dev) // same device as before?
- {
- // File is open and OK
- if (truncate) ftruncate(fd, 0);
- return fd;
- } else {
- // Opened file looks funny - close it
- close(fd);
- return -1;
- }
- } else {
- // File didn't open
- return -1;
- }
- } else {
- // Unopened file looks funny - don't open it
- return -1;
- }
- }
-}
-
-
-/***********************************************************************
-* _objc_internal_zone.
-* Malloc zone for internal runtime data.
-* 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)
-{
- static malloc_zone_t *z = (malloc_zone_t *)-1;
- if (z == (malloc_zone_t *)-1) {
- if (UseInternalZone) {
- z = malloc_create_zone(vm_page_size, 0);
- malloc_set_zone_name(z, "ObjC");
- } else {
- z = malloc_default_zone();
- }
- }
- return z;
-}
-
-
-PRIVATE_EXTERN const char *
-_getObjcHeaderName(const headerType *header)
-{
- Dl_info info;
-
- if (dladdr(header, &info)) {
- return info.dli_fname;
- }
- else {
- return (*_NSGetArgv())[0];
- }
-}
-
-
-PRIVATE_EXTERN bool crashlog_header_name(header_info *hi)
-{
- return crashlog_header_name_string(hi ? hi->os.dl_info.dli_fname : NULL);
-}
-
-PRIVATE_EXTERN bool crashlog_header_name_string(const char *name)
-{
- CRSetCrashLogMessage2(name);
- return true;
-}
-
-
-#if TARGET_OS_IPHONE
-
-PRIVATE_EXTERN const char *__crashreporter_info__ = NULL;
-
-PRIVATE_EXTERN const char *CRSetCrashLogMessage(const char *msg)
-{
- __crashreporter_info__ = msg;
- return msg;
-}
-PRIVATE_EXTERN const char *CRGetCrashLogMessage(void)
-{
- return __crashreporter_info__;
-}
-
-PRIVATE_EXTERN const char *CRSetCrashLogMessage2(const char *msg)
-{
- // sorry
- return msg;
-}
-
-#endif
-
-// TARGET_OS_MAC
-#else
-
-
-#error unknown OS
-
-
-#endif
--- /dev/null
+/*
+ * Copyright (c) 2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/***********************************************************************
+* objc-os.m
+* OS portability layer.
+**********************************************************************/
+
+#include "objc-os.h"
+#include "objc-private.h"
+#include "objc-loadmethod.h"
+
+#if TARGET_OS_WIN32
+
+#include "objc-runtime-old.h"
+#include "objcrt.h"
+
+malloc_zone_t *_objc_internal_zone(void)
+{
+ return NULL;
+}
+
+int monitor_init(monitor_t *c)
+{
+ // fixme error checking
+ HANDLE mutex = CreateMutex(NULL, TRUE, NULL);
+ while (!c->mutex) {
+ // fixme memory barrier here?
+ if (0 == InterlockedCompareExchangePointer(&c->mutex, mutex, 0)) {
+ // we win - finish construction
+ c->waiters = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
+ c->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL);
+ InitializeCriticalSection(&c->waitCountLock);
+ c->waitCount = 0;
+ c->didBroadcast = 0;
+ ReleaseMutex(c->mutex);
+ return 0;
+ }
+ }
+
+ // someone else allocated the mutex and constructed the monitor
+ ReleaseMutex(mutex);
+ CloseHandle(mutex);
+ return 0;
+}
+
+void mutex_init(mutex_t *m)
+{
+ while (!m->lock) {
+ CRITICAL_SECTION *newlock = malloc(sizeof(CRITICAL_SECTION));
+ InitializeCriticalSection(newlock);
+ // fixme memory barrier here?
+ if (0 == InterlockedCompareExchangePointer(&m->lock, newlock, 0)) {
+ return;
+ }
+ // someone else installed their lock first
+ DeleteCriticalSection(newlock);
+ free(newlock);
+ }
+}
+
+
+void recursive_mutex_init(recursive_mutex_t *m)
+{
+ // fixme error checking
+ HANDLE newmutex = CreateMutex(NULL, FALSE, NULL);
+ while (!m->mutex) {
+ // fixme memory barrier here?
+ if (0 == InterlockedCompareExchangePointer(&m->mutex, newmutex, 0)) {
+ // we win
+ return;
+ }
+ }
+
+ // someone else installed their lock first
+ CloseHandle(newmutex);
+}
+
+
+WINBOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call) {
+ case DLL_PROCESS_ATTACH:
+ environ_init();
+ tls_init();
+ lock_init();
+ sel_init(NO, 3500); // old selector heuristic
+ exception_init();
+ break;
+
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+OBJC_EXPORT void *_objc_init_image(HMODULE image, const objc_sections *sects)
+{
+ header_info *hi = _malloc_internal(sizeof(header_info));
+ size_t count, i;
+
+ hi->mhdr = (const headerType *)image;
+ hi->info = sects->iiStart;
+ hi->allClassesRealized = NO;
+ 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->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->moduleCount; i++) {
+ if (hi->modules[i]) memcpy(&hi->mod_ptr[hi->mod_count++], hi->modules[i], sizeof(struct objc_module));
+ }
+ }
+
+ hi->moduleName = malloc(MAX_PATH * sizeof(TCHAR));
+ GetModuleFileName((HMODULE)(hi->mhdr), hi->moduleName, MAX_PATH * sizeof(TCHAR));
+
+ appendHeader(hi);
+
+ if (PrintImages) {
+ _objc_inform("IMAGES: loading image for %s%s%s\n",
+ hi->fname,
+ headerIsBundle(hi) ? " (bundle)" : "",
+ _objcHeaderIsReplacement(hi) ? " (replacement)":"");
+ }
+
+ _read_images(&hi, 1);
+
+ return hi;
+}
+
+OBJC_EXPORT void _objc_load_image(HMODULE image, header_info *hinfo)
+{
+ prepare_load_methods(hinfo);
+ call_load_methods();
+}
+
+OBJC_EXPORT void _objc_unload_image(HMODULE image, header_info *hinfo)
+{
+ _objc_fatal("image unload not supported");
+}
+
+
+bool crashlog_header_name(header_info *hi)
+{
+ return true;
+}
+
+
+// TARGET_OS_WIN32
+#elif TARGET_OS_MAC
+
+#if !__OBJC2__
+#include "objc-file-old.h"
+#endif
+
+void mutex_init(mutex_t *m)
+{
+ pthread_mutex_init(m, NULL);
+}
+
+
+void recursive_mutex_init(recursive_mutex_t *m)
+{
+ // fixme error checking
+ pthread_mutex_t *newmutex;
+
+ // Build recursive mutex attributes, if needed
+ static pthread_mutexattr_t *attr;
+ if (!attr) {
+ pthread_mutexattr_t *newattr = (pthread_mutexattr_t *)
+ _malloc_internal(sizeof(pthread_mutexattr_t));
+ pthread_mutexattr_init(newattr);
+ pthread_mutexattr_settype(newattr, PTHREAD_MUTEX_RECURSIVE);
+ while (!attr) {
+ if (OSAtomicCompareAndSwapPtrBarrier(0, newattr, (void**)&attr)) {
+ // we win
+ goto attr_done;
+ }
+ }
+ // someone else built the attr first
+ _free_internal(newattr);
+ }
+ attr_done:
+
+ // Build the mutex itself
+ 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)) {
+ // we win
+ return;
+ }
+ }
+
+ // someone else installed their mutex first
+ pthread_mutex_destroy(newmutex);
+}
+
+
+/***********************************************************************
+* bad_magic.
+* Return YES if the header has invalid Mach-o magic.
+**********************************************************************/
+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 * addHeader(const headerType *mhdr)
+{
+ header_info *hi;
+
+ if (bad_magic(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.
+
+ // Weed out duplicates
+ for (hi = FirstHeader; hi; hi = hi->next) {
+ if (mhdr == hi->mhdr) return NULL;
+ }
+
+ // 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;
+
+ // 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
+ hi->mod_count = 0;
+ hi->mod_ptr = _getObjcModules(hi, &hi->mod_count);
+#endif
+ 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 (hi->mhdr->filetype == MH_DYLIB && _hasObjcContents(hi)) {
+ dlopen(hi->fname, RTLD_NOLOAD);
+ }
+
+ appendHeader(hi);
+
+ return hi;
+}
+
+
+#if !SUPPORT_GC
+
+const char *_gcForHInfo(const header_info *hinfo)
+{
+ return "";
+}
+const char *_gcForHInfo2(const header_info *hinfo)
+{
+ return "";
+}
+
+#else
+
+/***********************************************************************
+* _gcForHInfo.
+**********************************************************************/
+const char *_gcForHInfo(const header_info *hinfo)
+{
+ if (_objcHeaderRequiresGC(hinfo)) {
+ if (_objcHeaderSupportsCompaction(hinfo))
+ return "requires GC, supports compaction";
+ else
+ return "requires GC";
+ } else if (_objcHeaderSupportsGC(hinfo)) {
+ if (_objcHeaderSupportsCompaction(hinfo))
+ return "supports GC, supports compaction";
+ else
+ return "supports GC";
+ } else {
+ return "does not support GC";
+ }
+}
+const char *_gcForHInfo2(const header_info *hinfo)
+{
+ if (_objcHeaderRequiresGC(hinfo)) {
+ if (_objcHeaderSupportsCompaction(hinfo))
+ return "(requires GC) (supports compaction)";
+ else
+ return "(requires GC)";
+ } else if (_objcHeaderSupportsGC(hinfo)) {
+ if (_objcHeaderSupportsCompaction(hinfo))
+ return "(supports GC) (supports compaction)";
+ else
+ return "(supports GC)";
+ }
+ return "";
+}
+
+
+/***********************************************************************
+* check_gc
+* Check whether the executable supports or requires GC, and make sure
+* all already-loaded libraries support the executable's GC mode.
+* Returns TRUE if the executable wants GC on.
+**********************************************************************/
+static void check_wants_gc(BOOL *appWantsGC, BOOL *appSupportsCompaction)
+{
+ const header_info *hi;
+
+ // Environment variables can override the following.
+ if (DisableGC) {
+ _objc_inform_on_crash("GC: forcing GC OFF because OBJC_DISABLE_GC is set");
+ *appWantsGC = NO;
+ *appSupportsCompaction = NO;
+ }
+ else {
+ // Find the executable and check its GC bits.
+ // If the executable cannot be found, default to NO.
+ // (The executable will not be found if the executable contains
+ // no Objective-C code.)
+ *appWantsGC = NO;
+ *appSupportsCompaction = NO;
+ for (hi = FirstHeader; hi != NULL; hi = hi->next) {
+ if (hi->mhdr->filetype == MH_EXECUTE) {
+ *appWantsGC = _objcHeaderSupportsGC(hi) ? YES : NO;
+ *appSupportsCompaction = (*appWantsGC && _objcHeaderSupportsCompaction(hi)) ? YES : NO;
+ if (PrintGC) {
+ _objc_inform("GC: executable '%s' %s",
+ hi->fname, _gcForHInfo(hi));
+ }
+ }
+ }
+ }
+}
+
+
+/***********************************************************************
+* verify_gc_readiness
+* if we want gc, verify that every header describes files compiled
+* and presumably ready for gc.
+************************************************************************/
+static void verify_gc_readiness(BOOL wantsGC, BOOL *wantsCompaction,
+ header_info **hList, uint32_t hCount)
+{
+ BOOL busted = NO;
+ uint32_t i;
+
+ // Find the libraries and check their GC bits against the app's request
+ for (i = 0; i < hCount; i++) {
+ header_info *hi = hList[i];
+ if (hi->mhdr->filetype == MH_EXECUTE) {
+ continue;
+ }
+ else if (hi->mhdr == &_mh_dylib_header) {
+ // libobjc itself works with anything even though it is not
+ // compiled with -fobjc-gc (fixme should it be?)
+ }
+ else if (wantsGC && ! _objcHeaderSupportsGC(hi)) {
+ // App wants GC but library does not support it - bad
+ _objc_inform_now_and_on_crash
+ ("'%s' was not compiled with -fobjc-gc or -fobjc-gc-only, "
+ "but the application requires GC",
+ hi->fname);
+ busted = YES;
+ }
+ else if (!wantsGC && _objcHeaderRequiresGC(hi)) {
+ // App doesn't want GC but library requires it - bad
+ _objc_inform_now_and_on_crash
+ ("'%s' was compiled with -fobjc-gc-only, "
+ "but the application does not support GC",
+ hi->fname);
+ busted = YES;
+ }
+
+ if (*wantsCompaction && !_objcHeaderSupportsCompaction(hi)) {
+ // App supports compaction, but library doesn't.
+ _objc_inform_now_and_on_crash
+ ("'%s' was not linked with -Xlinker -objc_gc_compaction, "
+ "but the application wants compaction.",
+ hi->fname);
+ // Simply warn for now until radars are filed. Eventually,
+ // objc_disableCompaction() will block until any current compaction completes.
+ objc_disableCompaction();
+ *wantsCompaction = NO;
+ }
+
+ if (PrintGC) {
+ _objc_inform("GC: library '%s' %s",
+ hi->fname, _gcForHInfo(hi));
+ }
+ }
+
+ if (busted) {
+ // GC state is not consistent.
+ // Kill the process unless one of the forcing flags is set.
+ if (!DisableGC) {
+ _objc_fatal("*** GC capability of application and some libraries did not match");
+ }
+ }
+}
+
+
+/***********************************************************************
+* gc_enforcer
+* Make sure that images about to be loaded by dyld are GC-acceptable.
+* Images linked to the executable are always permitted; they are
+* enforced inside map_images() itself.
+**********************************************************************/
+static BOOL InitialDyldRegistration = NO;
+static const char *gc_enforcer(enum dyld_image_states state,
+ uint32_t infoCount,
+ const struct dyld_image_info info[])
+{
+ uint32_t i;
+
+ // Linked images get a free pass
+ if (InitialDyldRegistration) return NULL;
+
+ if (PrintImages) {
+ _objc_inform("IMAGES: checking %d images for compatibility...",
+ infoCount);
+ }
+
+ for (i = 0; i < infoCount; i++) {
+ crashlog_header_name_string(info[i].imageFilePath);
+
+ const headerType *mhdr = (const headerType *)info[i].imageLoadAddress;
+ if (bad_magic(mhdr)) continue;
+
+ objc_image_info *image_info;
+ size_t size;
+
+ if (mhdr == &_mh_dylib_header) {
+ // libobjc itself - OK
+ continue;
+ }
+
+#if !__OBJC2__
+ unsigned long seg_size;
+ // 32-bit: __OBJC seg but no image_info means no GC support
+ if (!getsegmentdata(mhdr, "__OBJC", &seg_size)) {
+ // not objc - assume OK
+ continue;
+ }
+ image_info = _getObjcImageInfo(mhdr, &size);
+ if (!image_info) {
+ // No image_info - assume GC unsupported
+ if (!UseGC) {
+ // GC is OFF - ok
+ continue;
+ } else {
+ // GC is ON - bad
+ if (PrintImages || PrintGC) {
+ _objc_inform("IMAGES: rejecting %d images because %s doesn't support GC (no image_info)", infoCount, info[i].imageFilePath);
+ }
+ goto reject;
+ }
+ }
+#else
+ // 64-bit: no image_info means no objc at all
+ image_info = _getObjcImageInfo(mhdr, &size);
+ if (!image_info) {
+ // not objc - assume OK
+ continue;
+ }
+#endif
+
+ if (UseGC && !_objcInfoSupportsGC(image_info)) {
+ // GC is ON, but image does not support GC
+ if (PrintImages || PrintGC) {
+ _objc_inform("IMAGES: rejecting %d images because %s doesn't support GC", infoCount, info[i].imageFilePath);
+ }
+ goto reject;
+ }
+ if (!UseGC && _objcInfoRequiresGC(image_info)) {
+ // GC is OFF, but image requires GC
+ if (PrintImages || PrintGC) {
+ _objc_inform("IMAGES: rejecting %d images because %s requires GC", infoCount, info[i].imageFilePath);
+ }
+ goto reject;
+ }
+ }
+
+ crashlog_header_name_string(NULL);
+ return NULL;
+
+ reject:
+ crashlog_header_name_string(NULL);
+ return "GC capability mismatch";
+}
+
+// SUPPORT_GC
+#endif
+
+
+/***********************************************************************
+* map_images_nolock
+* Process the given images which are being mapped in by dyld.
+* All class registration and fixups are performed (or deferred pending
+* discovery of missing superclasses etc), and +load methods are called.
+*
+* info[] is in bottom-up order i.e. libobjc will be earlier in the
+* array than any library that links to libobjc.
+*
+* Locking: loadMethodLock(old) or runtimeLock(new) acquired by map_images.
+**********************************************************************/
+#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[])
+{
+ static BOOL firstTime = YES;
+ static BOOL wantsGC = NO;
+ static BOOL wantsCompaction = NO;
+ uint32_t i;
+ 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);
+ InitialDyldRegistration = NO;
+#endif
+ }
+
+ if (PrintImages) {
+ _objc_inform("IMAGES: processing %u newly-mapped images...\n", infoCount);
+ }
+
+
+ // Find all images with Objective-C metadata.
+ hCount = 0;
+ i = infoCount;
+ while (i--) {
+ const headerType *mhdr = (headerType *)infoList[i].imageLoadAddress;
+
+ 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",
+ hi->fname,
+ mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
+ _objcHeaderIsReplacement(hi) ? " (replacement)" : "",
+ _objcHeaderOptimizedByDyld(hi)?" (preoptimized)" : "",
+ _gcForHInfo2(hi));
+ }
+ }
+
+ // Perform one-time runtime initialization that must be deferred until
+ // the executable itself is found. This needs to be done before
+ // further initialization.
+ // (The executable may not be present in this infoList if the
+ // executable does not contain Objective-C code but Objective-C
+ // is dynamically loaded later. In that case, check_wants_gc()
+ // will do the right thing.)
+#if SUPPORT_GC
+ if (firstTime) {
+ check_wants_gc(&wantsGC, &wantsCompaction);
+
+ verify_gc_readiness(wantsGC, &wantsCompaction, hList, hCount);
+
+ gc_init(wantsGC, wantsCompaction); // needs executable for GC decision
+ rtp_init(); // needs GC decision first
+ } else {
+ verify_gc_readiness(wantsGC, &wantsCompaction, hList, hCount);
+ }
+
+ if (wantsGC) {
+ // tell the collector about the data segment ranges.
+ for (i = 0; i < hCount; ++i) {
+ uint8_t *seg;
+ unsigned long seg_size;
+ hi = hList[i];
+
+ seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size);
+ if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
+
+ seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
+ if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
+ // __OBJC contains no GC data, but pointers to it are
+ // used as associated reference values (rdar://6953570)
+ }
+ }
+
+ // Need to fixup barriers in all libraries that call into libobjc, whether GC is on or not.
+ for (i = 0; i < infoCount; ++i) {
+ gc_fixup_barrier_stubs(&infoList[i]);
+ }
+#endif
+
+ if (firstTime) {
+ extern SEL FwdSel; // in objc-msg-*.s
+ sel_init(wantsGC, selrefCount);
+ FwdSel = sel_registerName("forward::");
+
+ arr_init();
+ }
+
+ _read_images(hList, hCount);
+
+ firstTime = NO;
+
+ return NULL;
+}
+
+
+/***********************************************************************
+* load_images_nolock
+* Prepares +load in the given images which are being mapped in by dyld.
+* Returns YES if there are now +load methods to be called by call_load_methods.
+*
+* Locking: loadMethodLock(both) and runtimeLock(new) acquired by load_images
+**********************************************************************/
+BOOL
+load_images_nolock(enum dyld_image_states state,uint32_t infoCount,
+ const struct dyld_image_info infoList[])
+{
+ BOOL found = NO;
+ uint32_t i;
+
+ i = infoCount;
+ while (i--) {
+ header_info *hi;
+ for (hi = FirstHeader; hi != NULL; hi = hi->next) {
+ const headerType *mhdr = (headerType*)infoList[i].imageLoadAddress;
+ if (hi->mhdr == mhdr) {
+ prepare_load_methods(hi);
+ found = YES;
+ }
+ }
+ }
+
+ return found;
+}
+
+
+/***********************************************************************
+* unmap_image_nolock
+* Process the given image which is about to be unmapped by dyld.
+* mh is mach_header instead of headerType because that's what
+* dyld_priv.h says even for 64-bit.
+*
+* Locking: loadMethodLock(both) and runtimeLock(new) acquired by unmap_image.
+**********************************************************************/
+void
+unmap_image_nolock(const struct mach_header *mh)
+{
+ if (PrintImages) {
+ _objc_inform("IMAGES: processing 1 newly-unmapped image...\n");
+ }
+
+ header_info *hi;
+
+ // Find the runtime's header_info struct for the image
+ for (hi = FirstHeader; hi != NULL; hi = hi->next) {
+ if (hi->mhdr == (const headerType *)mh) {
+ break;
+ }
+ }
+
+ if (!hi) return;
+
+ if (PrintImages) {
+ _objc_inform("IMAGES: unloading image for %s%s%s%s\n",
+ hi->fname,
+ hi->mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
+ _objcHeaderIsReplacement(hi) ? " (replacement)" : "",
+ _gcForHInfo2(hi));
+ }
+
+#if SUPPORT_GC
+ if (UseGC) {
+ uint8_t *seg;
+ unsigned long seg_size;
+
+ seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size);
+ if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
+
+ seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
+ if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
+ }
+#endif
+
+ _unload_image(hi);
+
+ // Remove header_info from header list
+ removeHeader(hi);
+ _free_internal(hi);
+}
+
+
+/***********************************************************************
+* _objc_init
+* 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();
+ lock_init();
+ exception_init();
+
+ // Register for unmap first, in case some +load unmaps something
+ _dyld_register_func_for_remove_image(&unmap_image);
+ dyld_register_image_state_change_handler(dyld_image_state_bound,
+ 1/*batch*/, &map_images);
+ dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images);
+}
+
+
+/***********************************************************************
+* _headerForAddress.
+* addr can be a class or a category
+**********************************************************************/
+static const header_info *_headerForAddress(void *addr)
+{
+#if __OBJC2__
+ const char *segname = "__DATA";
+#else
+ const char *segname = "__OBJC";
+#endif
+ header_info *hi;
+
+ // Check all headers in the vector
+ for (hi = FirstHeader; hi != NULL; hi = hi->next)
+ {
+ uint8_t *seg;
+ unsigned long seg_size;
+
+ seg = getsegmentdata(hi->mhdr, segname, &seg_size);
+ if (!seg) continue;
+
+ // Is the class in this header?
+ if ((uint8_t *)addr >= seg && (uint8_t *)addr < seg + seg_size)
+ return hi;
+ }
+
+ // Not found
+ return 0;
+}
+
+
+/***********************************************************************
+* _headerForClass
+* Return the image header containing this class, or NULL.
+* Returns NULL on runtime-constructed classes, and the NSCF classes.
+**********************************************************************/
+const header_info *_headerForClass(Class cls)
+{
+ return _headerForAddress(cls);
+}
+
+
+/**********************************************************************
+* secure_open
+* Securely open a file from a world-writable directory (like /tmp)
+* If the file does not exist, it will be atomically created with mode 0600
+* If the file exists, it must be, and remain after opening:
+* 1. a regular file (in particular, not a symlink)
+* 2. owned by euid
+* 3. permissions 0600
+* 4. link count == 1
+* Returns a file descriptor or -1. Errno may or may not be set on error.
+**********************************************************************/
+int secure_open(const char *filename, int flags, uid_t euid)
+{
+ struct stat fs, ls;
+ int fd = -1;
+ BOOL truncate = NO;
+ BOOL create = NO;
+
+ if (flags & O_TRUNC) {
+ // Don't truncate the file until after it is open and verified.
+ truncate = YES;
+ flags &= ~O_TRUNC;
+ }
+ if (flags & O_CREAT) {
+ // Don't create except when we're ready for it
+ create = YES;
+ flags &= ~O_CREAT;
+ flags &= ~O_EXCL;
+ }
+
+ if (lstat(filename, &ls) < 0) {
+ if (errno == ENOENT && create) {
+ // No such file - create it
+ fd = open(filename, flags | O_CREAT | O_EXCL, 0600);
+ if (fd >= 0) {
+ // File was created successfully.
+ // New file does not need to be truncated.
+ return fd;
+ } else {
+ // File creation failed.
+ return -1;
+ }
+ } else {
+ // lstat failed, or user doesn't want to create the file
+ return -1;
+ }
+ } else {
+ // lstat succeeded - verify attributes and open
+ if (S_ISREG(ls.st_mode) && // regular file?
+ ls.st_nlink == 1 && // link count == 1?
+ ls.st_uid == euid && // owned by euid?
+ (ls.st_mode & ALLPERMS) == (S_IRUSR | S_IWUSR)) // mode 0600?
+ {
+ // Attributes look ok - open it and check attributes again
+ fd = open(filename, flags, 0000);
+ if (fd >= 0) {
+ // File is open - double-check attributes
+ if (0 == fstat(fd, &fs) &&
+ fs.st_nlink == ls.st_nlink && // link count == 1?
+ fs.st_uid == ls.st_uid && // owned by euid?
+ fs.st_mode == ls.st_mode && // regular file, 0600?
+ fs.st_ino == ls.st_ino && // same inode as before?
+ fs.st_dev == ls.st_dev) // same device as before?
+ {
+ // File is open and OK
+ if (truncate) ftruncate(fd, 0);
+ return fd;
+ } else {
+ // Opened file looks funny - close it
+ close(fd);
+ return -1;
+ }
+ } else {
+ // File didn't open
+ return -1;
+ }
+ } else {
+ // Unopened file looks funny - don't open it
+ return -1;
+ }
+ }
+}
+
+
+/***********************************************************************
+* _objc_internal_zone.
+* Malloc zone for internal runtime data.
+* By default this is the default malloc zone, but a dedicated zone is
+* used if environment variable OBJC_USE_INTERNAL_ZONE is set.
+**********************************************************************/
+malloc_zone_t *_objc_internal_zone(void)
+{
+ static malloc_zone_t *z = (malloc_zone_t *)-1;
+ if (z == (malloc_zone_t *)-1) {
+ if (UseInternalZone) {
+ z = malloc_create_zone(vm_page_size, 0);
+ malloc_set_zone_name(z, "ObjC");
+ } else {
+ z = malloc_default_zone();
+ }
+ }
+ return z;
+}
+
+
+bool crashlog_header_name(header_info *hi)
+{
+ return crashlog_header_name_string(hi ? hi->fname : NULL);
+}
+
+bool crashlog_header_name_string(const char *name)
+{
+ CRSetCrashLogMessage2(name);
+ return true;
+}
+
+
+#if TARGET_OS_IPHONE
+
+const char *__crashreporter_info__ = NULL;
+
+const char *CRSetCrashLogMessage(const char *msg)
+{
+ __crashreporter_info__ = msg;
+ return msg;
+}
+const char *CRGetCrashLogMessage(void)
+{
+ return __crashreporter_info__;
+}
+
+const char *CRSetCrashLogMessage2(const char *msg)
+{
+ // sorry
+ return msg;
+}
+
+#endif
+
+// TARGET_OS_MAC
+#else
+
+
+#error unknown OS
+
+
+#endif
#if SUPPORT_GC
# include <auto_zone.h>
- 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();
# 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. */
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;
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);
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));
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);
/* 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 */
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
// 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);
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);
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);
// Global operator new and delete. We must not use any app overrides.
// This ALSO REQUIRES each of these be in libobjc's unexported symbol list.
#if __cplusplus
-#import <new>
+#include <new>
inline void* operator new(std::size_t size) throw (std::bad_alloc) { return _malloc_internal(size); }
inline void* operator new[](std::size_t size) throw (std::bad_alloc) { return _malloc_internal(size); }
inline void* operator new(std::size_t size, const std::nothrow_t&) throw() { return _malloc_internal(size); }
* objc-references.h
*/
-#if !defined(_OBJC_REFERENCES_H_)
+#ifndef _OBJC_REFERENCES_H_
#define _OBJC_REFERENCES_H_
#include "objc-api.h"
#include <objc/message.h>
#include <map>
+#if _LIBCPP_VERSION
+# include <unordered_map>
+#else
+# include <tr1/unordered_map>
+ 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);
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);
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.
typedef const void *const_pointer;
template <typename U> struct rebind { typedef ObjcAllocator<U> 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<void *, ObjcAssociation> ObjectAssociationMap;
- typedef hash_map<void *, ObjectAssociationMap *> AssociationsHashMap;
+ typedef hash_map<disguised_ptr_t, ObjectAssociationMap *> AssociationsHashMap;
#else
- class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjcAllocator<std::pair<void * const, ObjcAssociation> > > {
+ typedef ObjcAllocator<std::pair<void * const, ObjcAssociation> > ObjectAssociationMapAllocator;
+ class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
+ public:
+ void *operator new(size_t n) { return ::_malloc_internal(n); }
+ void operator delete(void *ptr) { ::_free_internal(ptr); }
+ };
+ typedef ObjcAllocator<std::pair<const disguised_ptr_t, ObjectAssociationMap*> > AssociationsHashMapAllocator;
+ class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
public:
void *operator new(size_t n) { return ::_malloc_internal(n); }
void operator delete(void *ptr) { ::_free_internal(ptr); }
};
- typedef hash_map<void *, ObjectAssociationMap *, ObjcPointerHash, ObjcPointerEqual, ObjcAllocator<void *> > AssociationsHashMap;
#endif
}
AssociationsHashMap &associations() {
if (_map == NULL)
- _map = new(::_malloc_internal(sizeof(AssociationsHashMap))) AssociationsHashMap();
+ _map = new AssociationsHashMap();
return *_map;
}
};
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;
}
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<ObjcAssociation> > 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;
+++ /dev/null
-/*
- * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#import "objc-private.h"
-#import <objc/message.h>
-
-
-#if defined(__i386__)
-
-
-/**********************************************************************
-* rtp_swap_imp
-*
-* Swap a function's current implementation with a new one.
-* The routine at 'address' is assumed to be at least as large as the
-* jump instruction required to reach the new implementation.
-**********************************************************************/
-static void rtp_swap_imp(unsigned *address, void *code, const char *name)
-{
- if (vm_protect(mach_task_self(), (vm_address_t)address, 1,
- FALSE, VM_PROT_READ | VM_PROT_WRITE) != KERN_SUCCESS)
- _objc_fatal("Could not get write access to %s.", name);
- else
- {
- objc_write_branch(address, (unsigned*)code);
-
- if (vm_protect(mach_task_self(), (vm_address_t)address, 1,
- FALSE, VM_PROT_READ | VM_PROT_EXECUTE) != KERN_SUCCESS)
- _objc_fatal("Could not reprotect %s.", name);
- }
-}
-
-
-PRIVATE_EXTERN void rtp_init(void)
-{
- // At load time, the page on which the objc_assign_* routines live is not
- // marked as executable. We fix that here, regardless of the GC choice.
-#if SUPPORT_GC
- if (UseGC)
- {
- rtp_swap_imp((unsigned*)objc_assign_ivar,
- objc_assign_ivar_gc, "objc_assign_ivar");
- rtp_swap_imp((unsigned*)objc_assign_global,
- objc_assign_global_gc, "objc_assign_global");
- rtp_swap_imp((unsigned*)objc_assign_threadlocal,
- objc_assign_threadlocal_gc, "objc_assign_threadlocal");
- rtp_swap_imp((unsigned*)objc_assign_strongCast,
- objc_assign_strongCast_gc, "objc_assign_strongCast");
- }
- else
-#endif
- { // Not GC, just make the page executable.
- if (vm_protect(mach_task_self(), (vm_address_t)objc_assign_ivar, 1,
- FALSE, VM_PROT_READ | VM_PROT_EXECUTE) != KERN_SUCCESS)
- _objc_fatal("Could not reprotect objc_assign_*.");
- }
-}
-
-
-#else
-
-
-PRIVATE_EXTERN void rtp_init(void)
-{
- if (PrintRTP) {
- _objc_inform("RTP: no rtp implementation for this platform");
- }
-}
-
-
-#endif
-
--- /dev/null
+/*
+ * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "objc-private.h"
+#include <objc/message.h>
+
+
+#if defined(__i386__)
+
+
+/**********************************************************************
+* rtp_swap_imp
+*
+* Swap a function's current implementation with a new one.
+* The routine at 'address' is assumed to be at least as large as the
+* jump instruction required to reach the new implementation.
+**********************************************************************/
+static void rtp_swap_imp(unsigned *address, void *code, const char *name)
+{
+ if (vm_protect(mach_task_self(), (vm_address_t)address, 1,
+ FALSE, VM_PROT_READ | VM_PROT_WRITE) != KERN_SUCCESS)
+ _objc_fatal("Could not get write access to %s.", name);
+ else
+ {
+ objc_write_branch(address, (unsigned*)code);
+
+ if (vm_protect(mach_task_self(), (vm_address_t)address, 1,
+ FALSE, VM_PROT_READ | VM_PROT_EXECUTE) != KERN_SUCCESS)
+ _objc_fatal("Could not reprotect %s.", name);
+ }
+}
+
+
+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.
+#if SUPPORT_GC
+ if (UseGC)
+ {
+ rtp_swap_imp((unsigned*)objc_assign_ivar,
+ (void*)objc_assign_ivar_gc, "objc_assign_ivar");
+ rtp_swap_imp((unsigned*)objc_assign_global,
+ (void*)objc_assign_global_gc, "objc_assign_global");
+ rtp_swap_imp((unsigned*)objc_assign_threadlocal,
+ (void*)objc_assign_threadlocal_gc, "objc_assign_threadlocal");
+ rtp_swap_imp((unsigned*)objc_assign_strongCast,
+ (void*)objc_assign_strongCast_gc, "objc_assign_strongCast");
+ }
+ else
+#endif
+ { // Not GC, just make the page executable.
+ if (vm_protect(mach_task_self(), (vm_address_t)objc_assign_ivar, 1,
+ FALSE, VM_PROT_READ | VM_PROT_EXECUTE) != KERN_SUCCESS)
+ _objc_fatal("Could not reprotect objc_assign_*.");
+ }
+}
+
+
+#else
+
+
+void rtp_init(void)
+{
+ if (PrintRTP) {
+ _objc_inform("RTP: no rtp implementation for this platform");
+ }
+}
+
+
+#endif
+
__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
#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
#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;
, 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; }
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 {
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;
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;
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;
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);
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;
}
* 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;
#define RDONLY 1
#define RDWR 2
-PRIVATE_EXTERN void lock_init(void)
+void lock_init(void)
{
rwlock_init(&selLock);
rwlock_init(&runtimeLock);
* 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;
* endDebuggerMode
* Relinquish locks acquired in startDebuggerMode().
**********************************************************************/
-PRIVATE_EXTERN void endDebuggerMode(void)
+void endDebuggerMode(void)
{
assert(debugger_runtimeLock != 0);
* 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;
* 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;
}
* 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);
* 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);
"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;
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);
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
#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 \
} \
} \
static uint32_t fixed_up_method_list = 3;
-PRIVATE_EXTERN void
+void
disableSharedCacheOptimizations(void)
{
fixed_up_method_list = 1;
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)
{
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;
{
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;
#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.
static method_list_t *
-fixupMethodList(method_list_t *mlist, BOOL bundleCopy)
+fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
assert(!isMethodListFixedUp(mlist));
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);
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.
// Fixup selectors if necessary
if (!isMethodListFixedUp(mlist)) {
- mlist = fixupMethodList(mlist, methodsFromBundle);
+ mlist = fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
}
// Scan for vtable updates
}
// 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
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
}
}
- attachMethodLists(cls, mlists, mcount, fromBundle, inoutVtablesAffected);
+ attachMethodLists(cls, mlists, mcount, NO, fromBundle, inoutVtablesAffected);
_free_internal(mlists);
static chained_property_list *
buildPropertyList(const property_list_t *plist, category_list *cats, BOOL isMeta)
{
- // Do NOT use cat->cls! It may have been remapped.
chained_property_list *newlist;
uint32_t count = 0;
uint32_t p, c;
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;
// 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);
/***********************************************************************
-* 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);
}
{
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));
{
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
}
/***********************************************************************
-* 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;
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);
}
if (PrintFuture) {
_objc_inform("FUTURE: using %p instead of %p for %s",
- oldcls, newcls, getName(newcls));
+ oldcls, newcls, getName(oldcls));
}
void *old;
}
}
+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
}
+/***********************************************************************
+* 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.
}
if (supercls->hasCustomRR()) {
- subcls->setHasCustomRR();
+ subcls->setHasCustomRR(true);
+ }
+
+ if (supercls->hasCustomAWZ()) {
+ subcls->setHasCustomAWZ(true);
}
}
}
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;
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) {
}
-/***********************************************************************
-* 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.
} 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);
rwlock_assert_writing(&runtimeLock);
size_t count, i;
- class_t **classlist;
+ classref_t *classlist;
if (hi->allClassesRealized) return;
* 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;
* 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);
*
* 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[])
{
*
* 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[])
{
*
* 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);
*
* 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;
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);
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
// 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) {
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);
}
}
// 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]));
_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) {
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]));
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);
* 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;
// 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);
}
}
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
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))
{
flushVtables(NULL);
}
- // fixme catch NSObject changing to custom RR
- // cls->setCustomRR();
+ updateCustomRR_AWZ(nil, m1);
+ updateCustomRR_AWZ(nil, m2);
// fixme update monomorphism if necessary
}
+/***********************************************************************
+* 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;
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;
}
}
* 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
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};
}
* 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);
* Returns a category's name.
* Locking: none
**********************************************************************/
-PRIVATE_EXTERN const char *
+const char *
_category_getName(Category cat)
{
return newcategory(cat)->name;
* 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));
}
* 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);
* 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);
* 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);
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
}
* fixme
* Locking: none
**********************************************************************/
-PRIVATE_EXTERN Cache
+Cache
_class_getCache(Class cls)
{
return newcls(cls)->cache;
* obj + class_getInstanceSize(obj->isa) == object_getIndexedIvars(obj)
* Locking: none
**********************************************************************/
-PRIVATE_EXTERN size_t
+size_t
_class_getInstanceSize(Class cls)
{
if (!cls) return 0;
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);
* fixme
* Locking: none
**********************************************************************/
-PRIVATE_EXTERN void
+void
_class_setCache(Class cls, Cache cache)
{
newcls(cls)->cache = cache;
* 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);
* fixme
* Locking: read-locks runtimeLock
**********************************************************************/
-PRIVATE_EXTERN Method
+Method
_class_getMethodNoSuper(Class cls, SEL sel)
{
rwlock_read(&runtimeLock);
* 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);
* 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);
* 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);
}
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
/***********************************************************************
* Locking: fixme
**********************************************************************/
-PRIVATE_EXTERN BOOL _class_isMetaClass(Class cls)
+BOOL _class_isMetaClass(Class cls)
{
if (!cls) return NO;
return isMetaClass(newcls(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)
/***********************************************************************
* Locking: fixme
**********************************************************************/
-PRIVATE_EXTERN BOOL
+BOOL
_class_isInitializing(Class cls_gen)
{
class_t *cls = newcls(_class_getMeta(cls_gen));
/***********************************************************************
* Locking: fixme
**********************************************************************/
-PRIVATE_EXTERN BOOL
+BOOL
_class_isInitialized(Class cls_gen)
{
class_t *cls = newcls(_class_getMeta(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);
}
/***********************************************************************
* 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
/***********************************************************************
* Locking: fixme
**********************************************************************/
-PRIVATE_EXTERN BOOL
+BOOL
_class_shouldGrowCache(Class cls)
{
return YES; // fixme good or bad for memory use?
/***********************************************************************
* Locking: fixme
**********************************************************************/
-PRIVATE_EXTERN void
+void
_class_setGrowCache(Class cls, BOOL grow)
{
// fixme good or bad for memory use?
* fixme
* Locking: none
**********************************************************************/
-PRIVATE_EXTERN BOOL
+BOOL
_class_isLoadable(Class cls)
{
assert(isRealized(newcls(cls)));
return (cls->data()->flags & RW_HAS_CXX_STRUCTORS) ? YES : NO;
}
-PRIVATE_EXTERN BOOL
+BOOL
_class_hasCxxStructors(Class cls)
{
return hasCxxStructors(newcls(cls));
/***********************************************************************
* Locking: fixme
**********************************************************************/
-PRIVATE_EXTERN BOOL
+BOOL
_class_shouldFinalizeOnMainThread(Class cls)
{
assert(isRealized(newcls(cls)));
/***********************************************************************
* Locking: fixme
**********************************************************************/
-PRIVATE_EXTERN void
+void
_class_setFinalizeOnMainThread(Class cls)
{
assert(isRealized(newcls(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);
* _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);
/***********************************************************************
* _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)
{
/***********************************************************************
* 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);
}
* 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));
* fixme
* Locking: read-locks runtimeLock
**********************************************************************/
-PRIVATE_EXTERN Ivar
+Ivar
_class_getVariable(Class cls, const char *name, Class *memberOf)
{
rwlock_read(&runtimeLock);
}
BOOL vtablesAffected = NO;
- attachMethodLists(cls, &newlist, 1, NO, &vtablesAffected);
+ attachMethodLists(cls, &newlist, 1, NO, NO, &vtablesAffected);
flushCaches(cls);
if (vtablesAffected) flushVtables(cls);
* 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)))
_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
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);
// 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);
}
* 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;
#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;
rwlock_assert_writing(&runtimeLock);
+ assert(isRealized(cls));
+ assert(isRealized(newSuper));
+
oldSuper = cls->superclass;
removeSubclass(oldSuper, cls);
removeSubclass(oldSuper->isa, cls->isa);
addSubclass(newSuper, cls);
addSubclass(newSuper->isa, cls->isa);
- flushCaches(cls);
flushCaches(cls->isa);
- flushVtables(cls);
flushVtables(cls->isa);
-
+ flushCaches(cls);
+ flushVtables(cls);
+
return oldSuper;
}
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;
};
#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
{
// 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,
/***********************************************************************
* 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;
* _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)
* 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;
* 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;
* 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));
}
layout_bitmap_free(superBitmap);
if (layoutChanged) {
- layout_bitmap weakBitmap = {0};
+ layout_bitmap weakBitmap = {};
BOOL weakLayoutChanged = NO;
if (cls->ext && cls->ext->weak_ivar_layout) {
/***********************************************************************
* 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;
}
}
- 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);
}
}
if (!proto) return empty;
desc = lookup_protocol_method(proto, aSel,
- isRequiredMethod, isInstanceMethod);
+ isRequiredMethod, isInstanceMethod, true);
if (desc) return *desc;
else return empty;
}
{
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;
}
}
- 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;
}
}
+/***********************************************************************
+* _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
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);
}
}
* 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);
* 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[])
{
*
* 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[])
{
* _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;
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;
#if TARGET_OS_WIN32
-PRIVATE_EXTERN void unload_class(struct old_class *cls)
+void unload_class(struct old_class *cls)
{
}
}
-PRIVATE_EXTERN void try_free(const void *p)
+void try_free(const void *p)
{
if (p && malloc_size(p)) free((void *)p);
}
// 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
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);
* 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);
* 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)
{
* 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;
}
-PRIVATE_EXTERN const char **
+const char **
_objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount)
{
Module mods;
* 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);
* 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;
* endDebuggerMode
* Relinquish locks acquired in startDebuggerMode().
**********************************************************************/
-PRIVATE_EXTERN void endDebuggerMode(void)
+void endDebuggerMode(void)
{
if (debugger_loadMethodLock) {
recursive_mutex_unlock(&loadMethodLock);
* Returns YES if the given lock is handled specially during debugger
* mode (i.e. debugger mode tries to acquire it).
**********************************************************************/
-PRIVATE_EXTERN BOOL isManagedDuringDebugger(void *lock)
+BOOL isManagedDuringDebugger(void *lock)
{
if (lock == &selLock) return YES;
if (lock == &classLock) return YES;
* Locking a managed mutex during debugger mode causes a trap unless
* this returns YES.
**********************************************************************/
-PRIVATE_EXTERN BOOL isLockedDuringDebugger(void *lock)
+BOOL isLockedDuringDebugger(void *lock)
{
assert(DebuggerMode);
if (lock == &methodListLock) return YES;
if (lock == &cacheUpdateLock) return YES;
if (lock == (mutex_t *)&loadMethodLock) return YES;
-
return NO;
}
* 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);
* 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);
-#import <objc/runtime.h>
-#import <objc/message.h>
+#include <objc/runtime.h>
+#include <objc/message.h>
+++ /dev/null
-/*
- * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-/***********************************************************************
-* objc-runtime.m
-* Copyright 1988-1996, NeXT Software, Inc.
-* Author: s. naroff
-*
-**********************************************************************/
-
-
-
-/***********************************************************************
-* Imports.
-**********************************************************************/
-
-#include "objc-private.h"
-#include "objc-loadmethod.h"
-#include "message.h"
-
-OBJC_EXPORT Class getOriginalClassForPosingClass(Class);
-
-
-/***********************************************************************
-* Exports.
-**********************************************************************/
-
-// 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
-#endif
-
-
-// objc's key for pthread_getspecific
-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;
-
-
-
-/***********************************************************************
-* objc_getClass. Return the id of the named class. If the class does
-* not exist, call _objc_classLoader and then objc_classHandler, either of
-* which may create a new class.
-* Warning: doesn't work if aClassName is the name of a posed-for class's isa!
-**********************************************************************/
-id objc_getClass(const char *aClassName)
-{
- if (!aClassName) return Nil;
-
- // NO unconnected, YES class handler
- return look_up_class(aClassName, NO, YES);
-}
-
-
-/***********************************************************************
-* objc_getRequiredClass.
-* Same as objc_getClass, but kills the process if the class is not found.
-* This is used by ZeroLink, where failing to find a class would be a
-* compile-time link error without ZeroLink.
-**********************************************************************/
-id objc_getRequiredClass(const char *aClassName)
-{
- id cls = objc_getClass(aClassName);
- if (!cls) _objc_fatal("link error: class '%s' not found.", aClassName);
- return cls;
-}
-
-
-/***********************************************************************
-* objc_lookUpClass. Return the id of the named class.
-* If the class does not exist, call _objc_classLoader, which may create
-* a new class.
-*
-* Formerly objc_getClassWithoutWarning ()
-**********************************************************************/
-id objc_lookUpClass(const char *aClassName)
-{
- if (!aClassName) return Nil;
-
- // NO unconnected, NO class handler
- return look_up_class(aClassName, NO, NO);
-}
-
-/***********************************************************************
-* objc_getFutureClass. Return the id of the named class.
-* If the class does not exist, return an uninitialized class
-* structure that will be used for the class when and if it
-* does get loaded.
-* Not thread safe.
-**********************************************************************/
-Class objc_getFutureClass(const char *name)
-{
- Class cls;
-
- // YES unconnected, NO class handler
- // (unconnected is OK because it will someday be the real class)
- cls = (Class)look_up_class(name, YES, NO);
- if (cls) {
- if (PrintFuture) {
- _objc_inform("FUTURE: found %p already in use for %s", cls, name);
- }
- return cls;
- }
-
- // No class or future class with that name yet. Make one.
- // fixme not thread-safe with respect to
- // simultaneous library load or getFutureClass.
- return _objc_allocateFutureClass(name);
-}
-
-
-/***********************************************************************
-* objc_getMetaClass. Return the id of the meta class the named class.
-* Warning: doesn't work if aClassName is the name of a posed-for class's isa!
-**********************************************************************/
-id objc_getMetaClass(const char *aClassName)
-{
- Class cls;
-
- if (!aClassName) return Nil;
-
- cls = (Class)objc_getClass (aClassName);
- if (!cls)
- {
- _objc_inform ("class `%s' not linked into application", aClassName);
- return Nil;
- }
-
- return (id)cls->isa;
-}
-
-
-/***********************************************************************
-* _nameForHeader.
-**********************************************************************/
-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)
-{
- // Add the header to the header list.
- // The header is appended to the list, to preserve the bottom-up order.
- HeaderCount++;
- hi->next = NULL;
- if (!FirstHeader) {
- // list is empty
- FirstHeader = LastHeader = hi;
- } else {
- if (!LastHeader) {
- // list is not empty, but LastHeader is invalid - recompute it
- LastHeader = FirstHeader;
- while (LastHeader->next) LastHeader = LastHeader->next;
- }
- // LastHeader is now valid
- LastHeader->next = hi;
- LastHeader = hi;
- }
-}
-
-
-/***********************************************************************
-* _objc_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)
-{
- header_info **hiP;
-
- for (hiP = &FirstHeader; *hiP != NULL; hiP = &(**hiP).next) {
- if (*hiP == hi) {
- header_info *deadHead = *hiP;
-
- // Remove from the linked list (updating FirstHeader if necessary).
- *hiP = (**hiP).next;
-
- // Update LastHeader if necessary.
- if (LastHeader == deadHead) {
- LastHeader = NULL; // will be recomputed next time it's used
- }
-
- HeaderCount--;
- break;
- }
- }
-}
-
-
-/***********************************************************************
-* environ_init
-* Read environment variables that affect the runtime.
-* Also print environment variable help, if requested.
-**********************************************************************/
-PRIVATE_EXTERN void environ_init(void)
-{
-#if SUPPORT_ENVIRON
- int PrintHelp = (getenv("OBJC_HELP") != NULL);
- int PrintOptions = (getenv("OBJC_PRINT_OPTIONS") != NULL);
- int secure = issetugid();
-
- if (secure) {
- // All environment variables are ignored when setuid or setgid.
- // This includes OBJC_HELP and OBJC_PRINT_OPTIONS themselves.
- }
- else {
- if (PrintHelp) {
- _objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
- _objc_inform("OBJC_HELP: describe available environment variables");
- if (PrintOptions) {
- _objc_inform("OBJC_HELP is set");
- }
- _objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
- }
- if (PrintOptions) {
- _objc_inform("OBJC_PRINT_OPTIONS is set");
- }
- }
-
-#define OPTION(var, env, help) \
- if ( var == -1 ) { \
- char *value = getenv(#env); \
- var = value != NULL && !strcmp("YES", value); \
- if (secure) { \
- if (var) _objc_inform(#env " ignored when running setuid or setgid"); \
- var = 0; \
- } else { \
- if (PrintHelp) _objc_inform(#env ": " help); \
- if (PrintOptions && var) _objc_inform(#env " is set"); \
- } \
- }
-
- OPTION(PrintImages, OBJC_PRINT_IMAGES,
- "log image and library names as they are loaded");
- OPTION(PrintLoading, OBJC_PRINT_LOAD_METHODS,
- "log calls to class and category +load methods");
- OPTION(PrintInitializing, OBJC_PRINT_INITIALIZE_METHODS,
- "log calls to class +initialize methods");
- OPTION(PrintResolving, OBJC_PRINT_RESOLVED_METHODS,
- "log methods created by +resolveClassMethod: and +resolveInstanceMethod:");
- OPTION(PrintConnecting, OBJC_PRINT_CLASS_SETUP,
- "log progress of class and category setup");
- OPTION(PrintProtocols, OBJC_PRINT_PROTOCOL_SETUP,
- "log progress of protocol setup");
- OPTION(PrintIvars, OBJC_PRINT_IVAR_SETUP,
- "log processing of non-fragile ivars");
- OPTION(PrintVtables, OBJC_PRINT_VTABLE_SETUP,
- "log processing of class vtables");
- OPTION(PrintVtableImages, OBJC_PRINT_VTABLE_IMAGES,
- "print vtable images showing overridden methods");
- OPTION(PrintCaches, OBJC_PRINT_CACHE_SETUP,
- "log processing of method caches");
- OPTION(PrintFuture, OBJC_PRINT_FUTURE_CLASSES,
- "log use of future classes for toll-free bridging");
- OPTION(PrintRTP, OBJC_PRINT_RTP,
- "log initialization of the Objective-C runtime pages");
- OPTION(PrintGC, OBJC_PRINT_GC,
- "log some GC operations");
- OPTION(PrintPreopt, OBJC_PRINT_PREOPTIMIZATION,
- "log preoptimization courtesy of dyld shared cache");
- OPTION(PrintCxxCtors, OBJC_PRINT_CXX_CTORS,
- "log calls to C++ ctors and dtors for instance variables");
- OPTION(PrintExceptions, OBJC_PRINT_EXCEPTIONS,
- "log exception handling");
- OPTION(PrintExceptionThrow, OBJC_PRINT_EXCEPTION_THROW,
- "log backtrace of every objc_exception_throw()");
- OPTION(PrintAltHandlers, OBJC_PRINT_ALT_HANDLERS,
- "log processing of exception alt handlers");
- OPTION(PrintReplacedMethods, OBJC_PRINT_REPLACED_METHODS,
- "log methods replaced by category implementations");
- OPTION(PrintDeprecation, OBJC_PRINT_DEPRECATION_WARNINGS,
- "warn about calls to deprecated runtime functions");
- OPTION(PrintPoolHiwat, OBJC_PRINT_POOL_HIGHWATER,
- "print high-water marks for autorelease pools");
-
- OPTION(DebugUnload, OBJC_DEBUG_UNLOAD,
- "warn about poorly-behaving bundles when unloaded");
- OPTION(DebugFragileSuperclasses, OBJC_DEBUG_FRAGILE_SUPERCLASSES,
- "warn about subclasses that may have been broken by subsequent changes to superclasses");
- OPTION(DebugFinalizers, OBJC_DEBUG_FINALIZERS,
- "warn about classes that implement -dealloc but not -finalize");
- OPTION(DebugNilSync, OBJC_DEBUG_NIL_SYNC,
- "warn about @synchronized(nil), which does no synchronization");
- OPTION(DebugNonFragileIvars, OBJC_DEBUG_NONFRAGILE_IVARS,
- "capriciously rearrange non-fragile ivars");
- OPTION(DebugAltHandlers, OBJC_DEBUG_ALT_HANDLERS,
- "record more info about bad alt handler use");
-
- OPTION(UseInternalZone, OBJC_USE_INTERNAL_ZONE,
- "allocate runtime data in a dedicated malloc zone");
-
- OPTION(DisableGC, OBJC_DISABLE_GC,
- "force GC OFF, even if the executable wants it on");
- OPTION(DisableVtables, OBJC_DISABLE_VTABLES,
- "disable vtable dispatch");
- OPTION(DisablePreopt, OBJC_DISABLE_PREOPTIMIZATION,
- "disable preoptimization courtesy of dyld shared cache");
-
-#undef OPTION
-#endif
-}
-
-
-/***********************************************************************
-* logReplacedMethod
-* OBJC_PRINT_REPLACED_METHODS implementation
-**********************************************************************/
-PRIVATE_EXTERN void
-logReplacedMethod(const char *className, SEL s,
- BOOL isMeta, const char *catName,
- IMP oldImp, IMP newImp)
-{
- const char *oldImage = "??";
- const char *newImage = "??";
-
- // Silently ignore +load replacement because category +load is special
- if (s == SEL_load) return;
-
-#if TARGET_OS_WIN32
- // don't know dladdr()/dli_fname equivalent
-#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;
-#endif
-
- _objc_inform("REPLACED: %c[%s %s] %s%s (IMP was %p (%s), now %p (%s))",
- isMeta ? '+' : '-', className, sel_getName(s),
- catName ? "by category " : "", catName ? catName : "",
- oldImp, oldImage, newImp, newImage);
-}
-
-
-
-/***********************************************************************
-* objc_setMultithreaded.
-**********************************************************************/
-void objc_setMultithreaded (BOOL flag)
-{
- OBJC_WARN_DEPRECATED;
-
- // Nothing here. Thread synchronization in the runtime is always active.
-}
-
-
-/***********************************************************************
-* _objc_fetch_pthread_data
-* Fetch objc's pthread data for this thread.
-* 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 *data;
-
- data = tls_get(_objc_pthread_key);
- if (!data && create) {
- data = _calloc_internal(1, sizeof(_objc_pthread_data));
- tls_set(_objc_pthread_key, data);
- }
-
- return data;
-}
-
-
-/***********************************************************************
-* _objc_pthread_destroyspecific
-* Destructor for objc's per-thread data.
-* arg shouldn't be NULL, but we check anyway.
-**********************************************************************/
-extern void _destroyInitializingClassList(struct _objc_initializing_classes *list);
-PRIVATE_EXTERN void _objc_pthread_destroyspecific(void *arg)
-{
- _objc_pthread_data *data = (_objc_pthread_data *)arg;
- if (data != NULL) {
- _destroyInitializingClassList(data->initializingClasses);
- _destroySyncCache(data->syncCache);
- _destroyAltHandlerList(data->handlerList);
-
- // add further cleanup here...
-
- _free_internal(data);
- }
-}
-
-
-PRIVATE_EXTERN void tls_init(void)
-{
-#if SUPPORT_DIRECT_THREAD_KEYS
- _objc_pthread_key = TLS_DIRECT_KEY;
- pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
-#else
- _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
-#endif
-}
-
-
-/***********************************************************************
-* _objcInit
-* Former library initializer. This function is now merely a placeholder
-* for external callers. All runtime initialization has now been moved
-* to map_images() and _objc_init.
-**********************************************************************/
-void _objcInit(void)
-{
- // do nothing
-}
-
-
-#if !(TARGET_OS_WIN32 || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
-/***********************************************************************
-* _objc_setNilReceiver
-**********************************************************************/
-id _objc_setNilReceiver(id newNilReceiver)
-{
- id oldNilReceiver;
-
- oldNilReceiver = _objc_nilReceiver;
- _objc_nilReceiver = newNilReceiver;
-
- return oldNilReceiver;
-}
-
-/***********************************************************************
-* _objc_getNilReceiver
-**********************************************************************/
-id _objc_getNilReceiver(void)
-{
- return _objc_nilReceiver;
-}
-#endif
-
-
-/***********************************************************************
-* objc_setForwardHandler
-**********************************************************************/
-void objc_setForwardHandler(void *fwd, void *fwd_stret)
-{
- _objc_forward_handler = fwd;
- _objc_forward_stret_handler = fwd_stret;
-}
-
-
-#if defined(__i386__) || defined(__x86_64__)
-
-/**********************************************************************
-* objc_branch_size
-* Returns the number of BYTES needed
-* for a branch from entry to target.
-**********************************************************************/
-PRIVATE_EXTERN size_t objc_branch_size(void *entry, void *target)
-{
- return objc_cond_branch_size(entry, target, COND_ALWAYS);
-}
-
-PRIVATE_EXTERN size_t
-objc_cond_branch_size(void *entry, void *target, unsigned cond)
-{
- // For simplicity, always use 32-bit relative jumps.
- if (cond == COND_ALWAYS) return 5;
- else return 6;
-}
-
-/**********************************************************************
-* objc_write_branch
-* Writes at entry an i386 branch instruction sequence that branches to target.
-* 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)
-{
- return objc_write_cond_branch(entry, target, COND_ALWAYS);
-}
-
-PRIVATE_EXTERN size_t
-objc_write_cond_branch(void *entry, void *target, unsigned cond)
-{
- uint8_t *address = (uint8_t *)entry; // instructions written to here
- intptr_t destination = (intptr_t)target; // branch dest as absolute address
- intptr_t displacement = (intptr_t)destination - ((intptr_t)address + objc_cond_branch_size(entry, target, cond)); // branch dest as relative offset
-
- // For simplicity, always use 32-bit relative jumps
- if (cond != COND_ALWAYS) {
- *address++ = 0x0f; // Jcc prefix
- }
- *address++ = cond;
- *address++ = displacement & 0xff;
- *address++ = (displacement >> 8) & 0xff;
- *address++ = (displacement >> 16) & 0xff;
- *address++ = (displacement >> 24) & 0xff;
-
- return address - (uint8_t *)entry;
-}
-
-// defined __i386__
-#endif
-
-
-
-
-#if !__OBJC2__
-// GrP fixme
-extern Class _objc_getOrigClass(const char *name);
-#endif
-const char *class_getImageName(Class cls)
-{
-#if TARGET_OS_WIN32
- TCHAR *szFileName;
- DWORD charactersCopied;
- Class origCls;
- HMODULE classModule;
- BOOL res;
-#endif
- if (!cls) return NULL;
-
-#if !__OBJC2__
- cls = _objc_getOrigClass(_class_getName(cls));
-#endif
-#if TARGET_OS_WIN32
- charactersCopied = 0;
- szFileName = malloc(MAX_PATH * sizeof(TCHAR));
-
- origCls = objc_getOrigClass(class_getName(cls));
- classModule = NULL;
- res = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)origCls, &classModule);
- if (res && classModule) {
- charactersCopied = GetModuleFileName(classModule, szFileName, MAX_PATH * sizeof(TCHAR));
- }
- if (classModule) FreeLibrary(classModule);
- if (charactersCopied) {
- return (const char *)szFileName;
- } else
- free(szFileName);
- return NULL;
-#else
- return dyld_image_path_containing_address(cls);
-#endif
-}
-
-
-const char **objc_copyImageNames(unsigned int *outCount)
-{
- header_info *hi;
- int count = 0;
- int max = HeaderCount;
-#if TARGET_OS_WIN32
- const TCHAR **names = calloc(max+1, sizeof(TCHAR *));
-#else
- const char **names = calloc(max+1, sizeof(char *));
-#endif
-
- for (hi = FirstHeader; hi != NULL && count < max; hi = hi->next) {
-#if TARGET_OS_WIN32
- if (hi->os.moduleName) {
- names[count++] = hi->os.moduleName;
- }
-#else
- if (hi->os.dl_info.dli_fname) {
- names[count++] = hi->os.dl_info.dli_fname;
- }
-#endif
- }
- names[count] = NULL;
-
- if (count == 0) {
- // Return NULL instead of empty list if there are no images
- free((void *)names);
- names = NULL;
- }
-
- if (outCount) *outCount = count;
- return names;
-}
-
-
-/**********************************************************************
-*
-**********************************************************************/
-const char **
-objc_copyClassNamesForImage(const char *image, unsigned int *outCount)
-{
- header_info *hi;
-
- if (!image) {
- if (outCount) *outCount = 0;
- return NULL;
- }
-
- // Find the image.
- for (hi = FirstHeader; hi != NULL; hi = hi->next) {
-#if TARGET_OS_WIN32
- if (0 == wcscmp((TCHAR *)image, hi->os.moduleName)) break;
-#else
- if (0 == strcmp(image, hi->os.dl_info.dli_fname)) break;
-#endif
- }
-
- if (!hi) {
- if (outCount) *outCount = 0;
- return NULL;
- }
-
- return _objc_copyClassNamesForImage(hi, outCount);
-}
-
-
-/**********************************************************************
-* Fast Enumeration Support
-**********************************************************************/
-
-static void (*enumerationMutationHandler)(id);
-
-/**********************************************************************
-* objc_enumerationMutation
-* called by compiler when a mutation is detected during foreach iteration
-**********************************************************************/
-void objc_enumerationMutation(id object) {
- if (enumerationMutationHandler == nil) {
- _objc_fatal("mutation detected during 'for(... in ...)' enumeration of object %p.", object);
- }
- (*enumerationMutationHandler)(object);
-}
-
-
-/**********************************************************************
-* objc_setEnumerationMutationHandler
-* an entry point to customize mutation error handing
-**********************************************************************/
-void objc_setEnumerationMutationHandler(void (*handler)(id)) {
- enumerationMutationHandler = handler;
-}
-
-
-/**********************************************************************
-* Associative Reference Support
-**********************************************************************/
-
-#if SUPPORT_GC
-PRIVATE_EXTERN id objc_getAssociatedObject_gc(id object, const void *key) {
- return auto_zone_get_associative_ref(gc_zone, object, (void *)key);
-}
-#endif
-
-PRIVATE_EXTERN id objc_getAssociatedObject_non_gc(id object, const void *key) {
- return _object_get_associative_reference(object, (void *)key);
-}
-
-id objc_getAssociatedObject(id object, const void *key) {
-#if SUPPORT_GC
- if (UseGC) {
- return auto_zone_get_associative_ref(gc_zone, object, (void *)key);
- } else
-#endif
- {
- return _object_get_associative_reference(object, (void *)key);
- }
-}
-
-#if SUPPORT_GC
-PRIVATE_EXTERN void objc_setAssociatedObject_gc(id object, const void *key, id value, objc_AssociationPolicy policy) {
- if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) {
- value = objc_msgSend(value, SEL_copy);
- }
- auto_zone_set_associative_ref(gc_zone, object, (void *)key, value);
-}
-#endif
-
-PRIVATE_EXTERN void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy) {
- _object_set_associative_reference(object, (void *)key, value, policy);
-}
-
-void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
-#if SUPPORT_GC
- if (UseGC) {
- if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) {
- value = objc_msgSend(value, SEL_copy);
- }
- auto_zone_set_associative_ref(gc_zone, object, (void *)key, value);
- } else
-#endif
- {
- // Note, creates a retained reference in non-GC.
- _object_set_associative_reference(object, (void *)key, value, policy);
- }
-}
-
-void objc_removeAssociatedObjects(id object) {
-#if SUPPORT_GC
- if (UseGC) {
- auto_zone_erase_associative_refs(gc_zone, object);
- } else
-#endif
- {
- if (_class_instancesHaveAssociatedObjects(_object_getClass(object))) _object_remove_assocations(object);
- }
-}
-
-BOOL class_instancesHaveAssociatedObjects(Class cls) {
- return _class_instancesHaveAssociatedObjects(cls);
-}
-
-
-/**********************************************************************
-* Debugger mode
-*
-* Debugger mode is used when gdb wants to call runtime functions
-* and other methods while other threads are stopped. The runtime
-* provides best-effort functionality while avoiding deadlocks
-* with the stopped threads. gdb is responsible for ensuring that all
-* threads but one stay stopped.
-*
-* When debugger mode starts, the runtime acquires as many locks as
-* it can. Any locks that can't be acquired are off-limits until
-* debugger mode ends. The locking functions in objc-os.h check each
-* operation and halt if a disallowed lock is used; gdb catches that
-* trap and cleans up.
-*
-* Each ABI is responsible for tracking its locks. Any lock not
-* handled there is a potential gdb deadlock.
-**********************************************************************/
-
-#if SUPPORT_DEBUGGER_MODE
-
-PRIVATE_EXTERN int DebuggerMode = DEBUGGER_OFF;
-PRIVATE_EXTERN objc_thread_t DebuggerModeThread = 0;
-static int DebuggerModeCount;
-
-/**********************************************************************
-* gdb_objc_startDebuggerMode
-* Start debugger mode by taking locks. Return 0 if not enough locks
-* could be acquired.
-**********************************************************************/
-int gdb_objc_startDebuggerMode(uint32_t flags)
-{
- BOOL wantFull = flags & OBJC_DEBUGMODE_FULL;
- if (! DebuggerMode) {
- // Start debugger mode
- int mode = startDebuggerMode(); // Do this FIRST
- if (mode == DEBUGGER_OFF) {
- // sorry
- return 0;
- }
- else if (mode == DEBUGGER_PARTIAL && wantFull) {
- // not good enough
- endDebuggerMode();
- return 0;
- }
- else {
- // w00t
- DebuggerMode = mode;
- DebuggerModeCount = 1;
- DebuggerModeThread = thread_self();
- return 1;
- }
- }
- else if (DebuggerMode == DEBUGGER_PARTIAL && wantFull) {
- // Debugger mode already active, but not as requested - sorry
- return 0;
- }
- else {
- // Debugger mode already active as requested
- if (thread_self() == DebuggerModeThread) {
- DebuggerModeCount++;
- return 1;
- } else {
- _objc_inform("DEBUGGER MODE: debugger is buggy: can't run "
- "debugger mode from two threads!");
- return 0;
- }
- }
-}
-
-
-/**********************************************************************
-* gdb_objc_endDebuggerMode
-* Relinquish locks and end debugger mode.
-**********************************************************************/
-void gdb_objc_endDebuggerMode(void)
-{
- if (DebuggerMode && thread_self() == DebuggerModeThread) {
- if (--DebuggerModeCount == 0) {
- DebuggerMode = NO;
- DebuggerModeThread = 0;
- endDebuggerMode(); // Do this LAST
- }
- } else {
- _objc_inform("DEBUGGER MODE: debugger is buggy: debugger mode "
- "not active for this thread!");
- }
-}
-
-
-/**********************************************************************
-* gdb_objc_debuggerModeFailure
-* Breakpoint hook for gdb when debugger mode can't finish something
-**********************************************************************/
-void gdb_objc_debuggerModeFailure(void)
-{
- _objc_fatal("DEBUGGER MODE: failed");
-}
-
-// SUPPORT_DEBUGGER_MODE
-#endif
--- /dev/null
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/***********************************************************************
+* objc-runtime.m
+* Copyright 1988-1996, NeXT Software, Inc.
+* Author: s. naroff
+*
+**********************************************************************/
+
+
+
+/***********************************************************************
+* Imports.
+**********************************************************************/
+
+#include "objc-private.h"
+#include "objc-loadmethod.h"
+#include "message.h"
+
+OBJC_EXPORT Class getOriginalClassForPosingClass(Class);
+
+
+/***********************************************************************
+* Exports.
+**********************************************************************/
+
+// Settings from environment variables
+#if SUPPORT_ENVIRON
+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
+
+
+// objc's key for pthread_getspecific
+static tls_key_t _objc_pthread_key;
+
+// Selectors
+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;
+
+
+
+/***********************************************************************
+* objc_getClass. Return the id of the named class. If the class does
+* not exist, call _objc_classLoader and then objc_classHandler, either of
+* which may create a new class.
+* Warning: doesn't work if aClassName is the name of a posed-for class's isa!
+**********************************************************************/
+id objc_getClass(const char *aClassName)
+{
+ if (!aClassName) return Nil;
+
+ // NO unconnected, YES class handler
+ return look_up_class(aClassName, NO, YES);
+}
+
+
+/***********************************************************************
+* objc_getRequiredClass.
+* Same as objc_getClass, but kills the process if the class is not found.
+* This is used by ZeroLink, where failing to find a class would be a
+* compile-time link error without ZeroLink.
+**********************************************************************/
+id objc_getRequiredClass(const char *aClassName)
+{
+ id cls = objc_getClass(aClassName);
+ if (!cls) _objc_fatal("link error: class '%s' not found.", aClassName);
+ return cls;
+}
+
+
+/***********************************************************************
+* objc_lookUpClass. Return the id of the named class.
+* If the class does not exist, call _objc_classLoader, which may create
+* a new class.
+*
+* Formerly objc_getClassWithoutWarning ()
+**********************************************************************/
+id objc_lookUpClass(const char *aClassName)
+{
+ if (!aClassName) return Nil;
+
+ // NO unconnected, NO class handler
+ return look_up_class(aClassName, NO, NO);
+}
+
+/***********************************************************************
+* objc_getFutureClass. Return the id of the named class.
+* If the class does not exist, return an uninitialized class
+* structure that will be used for the class when and if it
+* does get loaded.
+* Not thread safe.
+**********************************************************************/
+Class objc_getFutureClass(const char *name)
+{
+ Class cls;
+
+ // YES unconnected, NO class handler
+ // (unconnected is OK because it will someday be the real class)
+ cls = (Class)look_up_class(name, YES, NO);
+ if (cls) {
+ if (PrintFuture) {
+ _objc_inform("FUTURE: found %p already in use for %s", cls, name);
+ }
+ return cls;
+ }
+
+ // No class or future class with that name yet. Make one.
+ // fixme not thread-safe with respect to
+ // simultaneous library load or getFutureClass.
+ return _objc_allocateFutureClass(name);
+}
+
+
+/***********************************************************************
+* objc_getMetaClass. Return the id of the meta class the named class.
+* Warning: doesn't work if aClassName is the name of a posed-for class's isa!
+**********************************************************************/
+id objc_getMetaClass(const char *aClassName)
+{
+ Class cls;
+
+ if (!aClassName) return Nil;
+
+ cls = (Class)objc_getClass (aClassName);
+ if (!cls)
+ {
+ _objc_inform ("class `%s' not linked into application", aClassName);
+ return Nil;
+ }
+
+ return (id)cls->isa;
+}
+
+
+/***********************************************************************
+* appendHeader. Add a newly-constructed header_info to the list.
+**********************************************************************/
+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.
+ HeaderCount++;
+ hi->next = NULL;
+ if (!FirstHeader) {
+ // list is empty
+ FirstHeader = LastHeader = hi;
+ } else {
+ if (!LastHeader) {
+ // list is not empty, but LastHeader is invalid - recompute it
+ LastHeader = FirstHeader;
+ while (LastHeader->next) LastHeader = LastHeader->next;
+ }
+ // LastHeader is now valid
+ LastHeader->next = hi;
+ LastHeader = hi;
+ }
+}
+
+
+/***********************************************************************
+* 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.
+**********************************************************************/
+void removeHeader(header_info *hi)
+{
+ header_info **hiP;
+
+ for (hiP = &FirstHeader; *hiP != NULL; hiP = &(**hiP).next) {
+ if (*hiP == hi) {
+ header_info *deadHead = *hiP;
+
+ // Remove from the linked list (updating FirstHeader if necessary).
+ *hiP = (**hiP).next;
+
+ // Update LastHeader if necessary.
+ if (LastHeader == deadHead) {
+ LastHeader = NULL; // will be recomputed next time it's used
+ }
+
+ HeaderCount--;
+ break;
+ }
+ }
+}
+
+
+/***********************************************************************
+* environ_init
+* Read environment variables that affect the runtime.
+* Also print environment variable help, if requested.
+**********************************************************************/
+void environ_init(void)
+{
+#if SUPPORT_ENVIRON
+ int PrintHelp = (getenv("OBJC_HELP") != NULL);
+ int PrintOptions = (getenv("OBJC_PRINT_OPTIONS") != NULL);
+ int secure = issetugid();
+
+ if (secure) {
+ // All environment variables are ignored when setuid or setgid.
+ // This includes OBJC_HELP and OBJC_PRINT_OPTIONS themselves.
+ }
+ else {
+ if (PrintHelp) {
+ _objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
+ _objc_inform("OBJC_HELP: describe available environment variables");
+ if (PrintOptions) {
+ _objc_inform("OBJC_HELP is set");
+ }
+ _objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
+ }
+ if (PrintOptions) {
+ _objc_inform("OBJC_PRINT_OPTIONS is set");
+ }
+ }
+
+#define OPTION(var, env, help) \
+ if ( var == -1 ) { \
+ char *value = getenv(#env); \
+ var = value != NULL && !strcmp("YES", value); \
+ if (secure) { \
+ if (var) _objc_inform(#env " ignored when running setuid or setgid"); \
+ var = 0; \
+ } else { \
+ if (PrintHelp) _objc_inform(#env ": " help); \
+ if (PrintOptions && var) _objc_inform(#env " is set"); \
+ } \
+ }
+
+ OPTION(PrintImages, OBJC_PRINT_IMAGES,
+ "log image and library names as they are loaded");
+ OPTION(PrintLoading, OBJC_PRINT_LOAD_METHODS,
+ "log calls to class and category +load methods");
+ OPTION(PrintInitializing, OBJC_PRINT_INITIALIZE_METHODS,
+ "log calls to class +initialize methods");
+ OPTION(PrintResolving, OBJC_PRINT_RESOLVED_METHODS,
+ "log methods created by +resolveClassMethod: and +resolveInstanceMethod:");
+ OPTION(PrintConnecting, OBJC_PRINT_CLASS_SETUP,
+ "log progress of class and category setup");
+ OPTION(PrintProtocols, OBJC_PRINT_PROTOCOL_SETUP,
+ "log progress of protocol setup");
+ OPTION(PrintIvars, OBJC_PRINT_IVAR_SETUP,
+ "log processing of non-fragile ivars");
+ OPTION(PrintVtables, OBJC_PRINT_VTABLE_SETUP,
+ "log processing of class vtables");
+ OPTION(PrintVtableImages, OBJC_PRINT_VTABLE_IMAGES,
+ "print vtable images showing overridden methods");
+ OPTION(PrintCaches, OBJC_PRINT_CACHE_SETUP,
+ "log processing of method caches");
+ OPTION(PrintFuture, OBJC_PRINT_FUTURE_CLASSES,
+ "log use of future classes for toll-free bridging");
+ OPTION(PrintRTP, OBJC_PRINT_RTP,
+ "log initialization of the Objective-C runtime pages");
+ OPTION(PrintGC, OBJC_PRINT_GC,
+ "log some GC operations");
+ OPTION(PrintPreopt, OBJC_PRINT_PREOPTIMIZATION,
+ "log preoptimization courtesy of dyld shared cache");
+ OPTION(PrintCxxCtors, OBJC_PRINT_CXX_CTORS,
+ "log calls to C++ ctors and dtors for instance variables");
+ OPTION(PrintExceptions, OBJC_PRINT_EXCEPTIONS,
+ "log exception handling");
+ OPTION(PrintExceptionThrow, OBJC_PRINT_EXCEPTION_THROW,
+ "log backtrace of every objc_exception_throw()");
+ OPTION(PrintAltHandlers, OBJC_PRINT_ALT_HANDLERS,
+ "log processing of exception alt handlers");
+ OPTION(PrintReplacedMethods, OBJC_PRINT_REPLACED_METHODS,
+ "log methods replaced by category implementations");
+ OPTION(PrintDeprecation, OBJC_PRINT_DEPRECATION_WARNINGS,
+ "warn about calls to deprecated runtime functions");
+ OPTION(PrintPoolHiwat, OBJC_PRINT_POOL_HIGHWATER,
+ "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");
+ OPTION(DebugFragileSuperclasses, OBJC_DEBUG_FRAGILE_SUPERCLASSES,
+ "warn about subclasses that may have been broken by subsequent changes to superclasses");
+ OPTION(DebugFinalizers, OBJC_DEBUG_FINALIZERS,
+ "warn about classes that implement -dealloc but not -finalize");
+ OPTION(DebugNilSync, OBJC_DEBUG_NIL_SYNC,
+ "warn about @synchronized(nil), which does no synchronization");
+ OPTION(DebugNonFragileIvars, OBJC_DEBUG_NONFRAGILE_IVARS,
+ "capriciously rearrange non-fragile ivars");
+ OPTION(DebugAltHandlers, OBJC_DEBUG_ALT_HANDLERS,
+ "record more info about bad alt handler use");
+
+ OPTION(UseInternalZone, OBJC_USE_INTERNAL_ZONE,
+ "allocate runtime data in a dedicated malloc zone");
+
+ OPTION(DisableGC, OBJC_DISABLE_GC,
+ "force GC OFF, even if the executable wants it on");
+ OPTION(DisableVtables, OBJC_DISABLE_VTABLES,
+ "disable vtable dispatch");
+ OPTION(DisablePreopt, OBJC_DISABLE_PREOPTIMIZATION,
+ "disable preoptimization courtesy of dyld shared cache");
+
+#undef OPTION
+#endif
+}
+
+
+/***********************************************************************
+* logReplacedMethod
+* OBJC_PRINT_REPLACED_METHODS implementation
+**********************************************************************/
+void
+logReplacedMethod(const char *className, SEL s,
+ BOOL isMeta, const char *catName,
+ IMP oldImp, IMP newImp)
+{
+ const char *oldImage = "??";
+ const char *newImage = "??";
+
+ // Silently ignore +load replacement because category +load is special
+ if (s == SEL_load) return;
+
+#if TARGET_OS_WIN32
+ // don't know dladdr()/dli_fname equivalent
+#else
+ Dl_info dl;
+
+ 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))",
+ isMeta ? '+' : '-', className, sel_getName(s),
+ catName ? "by category " : "", catName ? catName : "",
+ oldImp, oldImage, newImp, newImage);
+}
+
+
+
+/***********************************************************************
+* objc_setMultithreaded.
+**********************************************************************/
+void objc_setMultithreaded (BOOL flag)
+{
+ OBJC_WARN_DEPRECATED;
+
+ // Nothing here. Thread synchronization in the runtime is always active.
+}
+
+
+/***********************************************************************
+* _objc_fetch_pthread_data
+* Fetch objc's pthread data for this thread.
+* 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.
+**********************************************************************/
+_objc_pthread_data *_objc_fetch_pthread_data(BOOL create)
+{
+ _objc_pthread_data *data;
+
+ data = (_objc_pthread_data *)tls_get(_objc_pthread_key);
+ if (!data && create) {
+ data = (_objc_pthread_data *)
+ _calloc_internal(1, sizeof(_objc_pthread_data));
+ tls_set(_objc_pthread_key, data);
+ }
+
+ return data;
+}
+
+
+/***********************************************************************
+* _objc_pthread_destroyspecific
+* Destructor for objc's per-thread data.
+* arg shouldn't be NULL, but we check anyway.
+**********************************************************************/
+extern void _destroyInitializingClassList(struct _objc_initializing_classes *list);
+void _objc_pthread_destroyspecific(void *arg)
+{
+ _objc_pthread_data *data = (_objc_pthread_data *)arg;
+ if (data != NULL) {
+ _destroyInitializingClassList(data->initializingClasses);
+ _destroySyncCache(data->syncCache);
+ _destroyAltHandlerList(data->handlerList);
+
+ // add further cleanup here...
+
+ _free_internal(data);
+ }
+}
+
+
+void tls_init(void)
+{
+#if SUPPORT_DIRECT_THREAD_KEYS
+ _objc_pthread_key = TLS_DIRECT_KEY;
+ pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
+#else
+ _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
+#endif
+}
+
+
+/***********************************************************************
+* _objcInit
+* Former library initializer. This function is now merely a placeholder
+* for external callers. All runtime initialization has now been moved
+* to map_images() and _objc_init.
+**********************************************************************/
+void _objcInit(void)
+{
+ // do nothing
+}
+
+
+#if !(TARGET_OS_WIN32 || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
+/***********************************************************************
+* _objc_setNilReceiver
+**********************************************************************/
+id _objc_setNilReceiver(id newNilReceiver)
+{
+ id oldNilReceiver;
+
+ oldNilReceiver = _objc_nilReceiver;
+ _objc_nilReceiver = newNilReceiver;
+
+ return oldNilReceiver;
+}
+
+/***********************************************************************
+* _objc_getNilReceiver
+**********************************************************************/
+id _objc_getNilReceiver(void)
+{
+ return _objc_nilReceiver;
+}
+#endif
+
+
+/***********************************************************************
+* objc_setForwardHandler
+**********************************************************************/
+void objc_setForwardHandler(void *fwd, void *fwd_stret)
+{
+ _objc_forward_handler = fwd;
+ _objc_forward_stret_handler = fwd_stret;
+}
+
+
+#if defined(__i386__) || defined(__x86_64__)
+
+/**********************************************************************
+* objc_branch_size
+* Returns the number of BYTES needed
+* for a branch from entry to target.
+**********************************************************************/
+size_t objc_branch_size(void *entry, void *target)
+{
+ return objc_cond_branch_size(entry, target, COND_ALWAYS);
+}
+
+size_t
+objc_cond_branch_size(void *entry, void *target, unsigned cond)
+{
+ // For simplicity, always use 32-bit relative jumps.
+ if (cond == COND_ALWAYS) return 5;
+ else return 6;
+}
+
+/**********************************************************************
+* objc_write_branch
+* Writes at entry an i386 branch instruction sequence that branches to target.
+* The sequence written will be objc_branch_size(entry, target) BYTES.
+* Returns the number of BYTES written.
+**********************************************************************/
+size_t objc_write_branch(void *entry, void *target)
+{
+ return objc_write_cond_branch(entry, target, COND_ALWAYS);
+}
+
+size_t
+objc_write_cond_branch(void *entry, void *target, unsigned cond)
+{
+ uint8_t *address = (uint8_t *)entry; // instructions written to here
+ intptr_t destination = (intptr_t)target; // branch dest as absolute address
+ intptr_t displacement = (intptr_t)destination - ((intptr_t)address + objc_cond_branch_size(entry, target, cond)); // branch dest as relative offset
+
+ // For simplicity, always use 32-bit relative jumps
+ if (cond != COND_ALWAYS) {
+ *address++ = 0x0f; // Jcc prefix
+ }
+ *address++ = cond;
+ *address++ = displacement & 0xff;
+ *address++ = (displacement >> 8) & 0xff;
+ *address++ = (displacement >> 16) & 0xff;
+ *address++ = (displacement >> 24) & 0xff;
+
+ return address - (uint8_t *)entry;
+}
+
+// defined __i386__
+#endif
+
+
+
+
+#if !__OBJC2__
+// GrP fixme
+OBJC_EXTERN Class _objc_getOrigClass(const char *name);
+#endif
+const char *class_getImageName(Class cls)
+{
+#if TARGET_OS_WIN32
+ TCHAR *szFileName;
+ DWORD charactersCopied;
+ Class origCls;
+ HMODULE classModule;
+ BOOL res;
+#endif
+ if (!cls) return NULL;
+
+#if !__OBJC2__
+ cls = _objc_getOrigClass(_class_getName(cls));
+#endif
+#if TARGET_OS_WIN32
+ charactersCopied = 0;
+ szFileName = malloc(MAX_PATH * sizeof(TCHAR));
+
+ origCls = objc_getOrigClass(class_getName(cls));
+ classModule = NULL;
+ res = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)origCls, &classModule);
+ if (res && classModule) {
+ charactersCopied = GetModuleFileName(classModule, szFileName, MAX_PATH * sizeof(TCHAR));
+ }
+ if (classModule) FreeLibrary(classModule);
+ if (charactersCopied) {
+ return (const char *)szFileName;
+ } else
+ free(szFileName);
+ return NULL;
+#else
+ return dyld_image_path_containing_address(cls);
+#endif
+}
+
+
+const char **objc_copyImageNames(unsigned int *outCount)
+{
+ header_info *hi;
+ int count = 0;
+ int max = HeaderCount;
+#if TARGET_OS_WIN32
+ const TCHAR **names = (const TCHAR **)calloc(max+1, sizeof(TCHAR *));
+#else
+ 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->moduleName) {
+ names[count++] = hi->moduleName;
+ }
+#else
+ if (hi->fname) {
+ names[count++] = hi->fname;
+ }
+#endif
+ }
+ names[count] = NULL;
+
+ if (count == 0) {
+ // Return NULL instead of empty list if there are no images
+ free((void *)names);
+ names = NULL;
+ }
+
+ if (outCount) *outCount = count;
+ return names;
+}
+
+
+/**********************************************************************
+*
+**********************************************************************/
+const char **
+objc_copyClassNamesForImage(const char *image, unsigned int *outCount)
+{
+ header_info *hi;
+
+ if (!image) {
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ // Find the image.
+ for (hi = FirstHeader; hi != NULL; hi = hi->next) {
+#if TARGET_OS_WIN32
+ if (0 == wcscmp((TCHAR *)image, hi->moduleName)) break;
+#else
+ if (0 == strcmp(image, hi->fname)) break;
+#endif
+ }
+
+ if (!hi) {
+ if (outCount) *outCount = 0;
+ return NULL;
+ }
+
+ return _objc_copyClassNamesForImage(hi, outCount);
+}
+
+
+/**********************************************************************
+* Fast Enumeration Support
+**********************************************************************/
+
+static void (*enumerationMutationHandler)(id);
+
+/**********************************************************************
+* objc_enumerationMutation
+* called by compiler when a mutation is detected during foreach iteration
+**********************************************************************/
+void objc_enumerationMutation(id object) {
+ if (enumerationMutationHandler == nil) {
+ _objc_fatal("mutation detected during 'for(... in ...)' enumeration of object %p.", object);
+ }
+ (*enumerationMutationHandler)(object);
+}
+
+
+/**********************************************************************
+* objc_setEnumerationMutationHandler
+* an entry point to customize mutation error handing
+**********************************************************************/
+void objc_setEnumerationMutationHandler(void (*handler)(id)) {
+ enumerationMutationHandler = handler;
+}
+
+
+/**********************************************************************
+* Associative Reference Support
+**********************************************************************/
+
+#if SUPPORT_GC
+id objc_getAssociatedObject_gc(id object, const void *key) {
+ return (id)auto_zone_get_associative_ref(gc_zone, object, (void *)key);
+}
+#endif
+
+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 (id)auto_zone_get_associative_ref(gc_zone, object, (void *)key);
+ } else
+#endif
+ {
+ return _object_get_associative_reference(object, (void *)key);
+ }
+}
+
+#if SUPPORT_GC
+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 = ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
+ }
+ auto_zone_set_associative_ref(gc_zone, object, (void *)key, value);
+}
+#endif
+
+void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy) {
+ _object_set_associative_reference(object, (void *)key, value, policy);
+}
+
+void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
+#if SUPPORT_GC
+ if (UseGC) {
+ if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) {
+ value = ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
+ }
+ auto_zone_set_associative_ref(gc_zone, object, (void *)key, value);
+ } else
+#endif
+ {
+ // Note, creates a retained reference in non-GC.
+ _object_set_associative_reference(object, (void *)key, value, policy);
+ }
+}
+
+void objc_removeAssociatedObjects(id object) {
+#if SUPPORT_GC
+ if (UseGC) {
+ auto_zone_erase_associative_refs(gc_zone, object);
+ } else
+#endif
+ {
+ if (_class_instancesHaveAssociatedObjects(_object_getClass(object))) _object_remove_assocations(object);
+ }
+}
+
+BOOL class_instancesHaveAssociatedObjects(Class cls) {
+ return _class_instancesHaveAssociatedObjects(cls);
+}
+
+
+/**********************************************************************
+* Debugger mode
+*
+* Debugger mode is used when gdb wants to call runtime functions
+* and other methods while other threads are stopped. The runtime
+* provides best-effort functionality while avoiding deadlocks
+* with the stopped threads. gdb is responsible for ensuring that all
+* threads but one stay stopped.
+*
+* When debugger mode starts, the runtime acquires as many locks as
+* it can. Any locks that can't be acquired are off-limits until
+* debugger mode ends. The locking functions in objc-os.h check each
+* operation and halt if a disallowed lock is used; gdb catches that
+* trap and cleans up.
+*
+* Each ABI is responsible for tracking its locks. Any lock not
+* handled there is a potential gdb deadlock.
+**********************************************************************/
+
+#if SUPPORT_DEBUGGER_MODE
+
+int DebuggerMode = DEBUGGER_OFF;
+objc_thread_t DebuggerModeThread = 0;
+static int DebuggerModeCount;
+
+/**********************************************************************
+* gdb_objc_startDebuggerMode
+* Start debugger mode by taking locks. Return 0 if not enough locks
+* could be acquired.
+**********************************************************************/
+int gdb_objc_startDebuggerMode(uint32_t flags)
+{
+ BOOL wantFull = flags & OBJC_DEBUGMODE_FULL;
+ if (! DebuggerMode) {
+ // Start debugger mode
+ int mode = startDebuggerMode(); // Do this FIRST
+ if (mode == DEBUGGER_OFF) {
+ // sorry
+ return 0;
+ }
+ else if (mode == DEBUGGER_PARTIAL && wantFull) {
+ // not good enough
+ endDebuggerMode();
+ return 0;
+ }
+ else {
+ // w00t
+ DebuggerMode = mode;
+ DebuggerModeCount = 1;
+ DebuggerModeThread = thread_self();
+ return 1;
+ }
+ }
+ else if (DebuggerMode == DEBUGGER_PARTIAL && wantFull) {
+ // Debugger mode already active, but not as requested - sorry
+ return 0;
+ }
+ else {
+ // Debugger mode already active as requested
+ if (thread_self() == DebuggerModeThread) {
+ DebuggerModeCount++;
+ return 1;
+ } else {
+ _objc_inform("DEBUGGER MODE: debugger is buggy: can't run "
+ "debugger mode from two threads!");
+ return 0;
+ }
+ }
+}
+
+
+/**********************************************************************
+* gdb_objc_endDebuggerMode
+* Relinquish locks and end debugger mode.
+**********************************************************************/
+void gdb_objc_endDebuggerMode(void)
+{
+ if (DebuggerMode && thread_self() == DebuggerModeThread) {
+ if (--DebuggerModeCount == 0) {
+ DebuggerMode = NO;
+ DebuggerModeThread = 0;
+ endDebuggerMode(); // Do this LAST
+ }
+ } else {
+ _objc_inform("DEBUGGER MODE: debugger is buggy: debugger mode "
+ "not active for this thread!");
+ }
+}
+
+
+/**********************************************************************
+* gdb_objc_debuggerModeFailure
+* Breakpoint hook for gdb when debugger mode can't finish something
+**********************************************************************/
+void gdb_objc_debuggerModeFailure(void)
+{
+ _objc_fatal("DEBUGGER MODE: failed");
+}
+
+// SUPPORT_DEBUGGER_MODE
+#endif
* A set of SELs used for SEL uniquing.
*/
+#ifndef _OBJC_SEL_SET_H_
+#define _OBJC_SEL_SET_H_
+
#include <stdint.h>
#include "objc-os.h"
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
+++ /dev/null
-/*
- * Copyright (c) 1999-2004,2008 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/*
- * objc-sel-set.h
- * A cut-down copy of CFSet used for SEL uniquing.
- */
-
-
-// NOTE: even on a 64-bit system, the implementation is still limited
-// to 32-bit integers (like, the count), but SEL can be any size.
-
-#include <stdint.h>
-#include "objc-private.h"
-#include "objc-sel-set.h"
-
-#if !SUPPORT_MOD
-// mod-free power of 2 version
-
-#define CONSTRAIN(val, range) ((val) & ((range)-1))
-#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
-};
-
-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
-};
-
-#else
-// prime version
-
-#define CONSTRAIN(val, range) ((val) % (range))
-#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
-};
-
-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
-};
-
-#endif
-
-struct __objc_sel_set {
- uint32_t _count; /* number of slots used */
- uint32_t _capacity; /* maximum number of used slots */
- uint32_t _bucketsNum; /* number of slots */
- SEL *_buckets; /* can be NULL if not allocated yet */
-};
-
-struct __objc_sel_set_finds {
- SEL match;
- uint32_t nomatch;
-};
-
-// candidate may not be 0; match is 0 if not present
-static struct __objc_sel_set_finds __objc_sel_set_findBuckets(struct __objc_sel_set *sset, SEL candidate) {
- struct __objc_sel_set_finds ret = {0, 0xffffffff};
- uint32_t probe = CONSTRAIN((uint32_t)_objc_strhash((const char *)candidate), sset->_bucketsNum);
- for (;;) {
- SEL currentSel = sset->_buckets[probe];
- if (!currentSel) {
- ret.nomatch = probe;
- return ret;
- } else if (!ret.match && 0 == strcmp((const char *)currentSel, (const char *)candidate)) {
- ret.match = currentSel;
- }
- probe++;
- if (sset->_bucketsNum <= probe) {
- probe -= sset->_bucketsNum;
- }
- }
-}
-
-// create a set with given starting capacity, will resize as needed
-PRIVATE_EXTERN struct __objc_sel_set *__objc_sel_set_create(uint32_t capacity) {
- uint32_t idx;
-
- struct __objc_sel_set *sset = _malloc_internal(sizeof(struct __objc_sel_set));
- if (!sset) _objc_fatal("objc_sel_set failure");
- sset->_count = 0;
-
- for (idx = 0; __objc_sel_set_capacities[idx] < capacity; idx++);
- 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));
- 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) {
- 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) {
- if (sset->_count == sset->_capacity) {
- SEL *oldbuckets = sset->_buckets;
- uint32_t oldnbuckets = sset->_bucketsNum;
- uint32_t idx, capacity = sset->_count + 1;
- for (idx = 0; __objc_sel_set_capacities[idx] < capacity; idx++);
- 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));
- if (!sset->_buckets) _objc_fatal("objc_sel_set failure");
- for (idx = 0; idx < oldnbuckets; idx++) {
- SEL currentSel = oldbuckets[idx];
- if (currentSel) {
- uint32_t nomatch = __objc_sel_set_findBuckets(sset, currentSel).nomatch;
- sset->_buckets[nomatch] = currentSel;
- }
- }
- _free_internal(oldbuckets);
- }
- {
- uint32_t nomatch = __objc_sel_set_findBuckets(sset, value).nomatch;
- sset->_buckets[nomatch] = value;
- sset->_count++;
- }
-}
--- /dev/null
+/*
+ * Copyright (c) 1999-2004,2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * objc-sel-set.h
+ * A cut-down copy of CFSet used for SEL uniquing.
+ */
+
+
+// NOTE: even on a 64-bit system, the implementation is still limited
+// to 32-bit integers (like, the count), but SEL can be any size.
+
+#include <stdint.h>
+#include "objc-private.h"
+#include "objc-sel-set.h"
+
+#if !SUPPORT_MOD
+// mod-free power of 2 version
+
+#define CONSTRAIN(val, range) ((val) & ((range)-1))
+#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
+};
+
+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
+};
+
+#else
+// prime version
+
+#define CONSTRAIN(val, range) ((val) % (range))
+#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
+};
+
+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
+};
+
+#endif
+
+struct __objc_sel_set {
+ uint32_t _count; /* number of slots used */
+ uint32_t _capacity; /* maximum number of used slots */
+ uint32_t _bucketsNum; /* number of slots */
+ SEL *_buckets; /* can be NULL if not allocated yet */
+};
+
+struct __objc_sel_set_finds {
+ SEL match;
+ uint32_t nomatch;
+};
+
+// candidate may not be 0; match is 0 if not present
+static struct __objc_sel_set_finds __objc_sel_set_findBuckets(struct __objc_sel_set *sset, SEL candidate) {
+ struct __objc_sel_set_finds ret = {0, 0xffffffff};
+ uint32_t probe = CONSTRAIN((uint32_t)_objc_strhash((const char *)candidate), sset->_bucketsNum);
+ for (;;) {
+ SEL currentSel = sset->_buckets[probe];
+ if (!currentSel) {
+ ret.nomatch = probe;
+ return ret;
+ } else if (!ret.match && 0 == strcmp((const char *)currentSel, (const char *)candidate)) {
+ ret.match = currentSel;
+ }
+ probe++;
+ if (sset->_bucketsNum <= probe) {
+ probe -= sset->_bucketsNum;
+ }
+ }
+}
+
+// create a set with given starting capacity, will resize as needed
+struct __objc_sel_set *__objc_sel_set_create(size_t selrefs) {
+ uint32_t idx;
+
+ 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;
+
+ // 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 = (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
+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
+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;
+ uint32_t idx, capacity = sset->_count + 1;
+ for (idx = 0; __objc_sel_set_capacities[idx] < capacity; idx++);
+ 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 = (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];
+ if (currentSel) {
+ uint32_t nomatch = __objc_sel_set_findBuckets(sset, currentSel).nomatch;
+ sset->_buckets[nomatch] = currentSel;
+ }
+ }
+ _free_internal(oldbuckets);
+ }
+ {
+ uint32_t nomatch = __objc_sel_set_findBuckets(sset, value).nomatch;
+ sset->_buckets[nomatch] = value;
+ sset->_count++;
+ }
+}
.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
#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 <objc-shared-cache.h>
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;
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);
}
#endif
if ('\0' == *key) return (SEL)_objc_empty_selector;
-#if SUPPORT_BUILTINS
+#if SUPPORT_PREOPT
+ assert(builtins);
return (SEL)builtins->get(key);
#endif
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
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);
}
* 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;
# 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;
* 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
s(autorelease);
s(retainCount);
s(alloc);
+ t(allocWithZone:, allocWithZone);
s(copy);
s(new);
s(finalize);
+++ /dev/null
-/*
- * 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 <stdint.h>
-#include <stdlib.h>
-#include <ext/hash_map>
-/*
- 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<const char *, uint64_t, __gnu_cxx::hash<const char *>, 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<n; ++i) h = hash( k[i], len[i], h);
-
-By Bob Jenkins, Jan 4 1997. bob_jenkins@burtleburtle.net. You may
-use this code any way you wish, private, educational, or commercial,
-but I would appreciate if you give me credit.
-
-See http://burtleburtle.net/bob/hash/evahash.html
-Use for hash table lookup, or anything where one collision in 2^^64
-is acceptable. Do NOT use for cryptographic purposes.
---------------------------------------------------------------------
-*/
-
-static uint64_t lookup8( uint8_t *k, size_t length, uint64_t level)
-// uint8_t *k; /* the key */
-// uint64_t length; /* the length of the key */
-// uint64_t level; /* the previous hash, or an arbitrary value */
-{
- uint64_t a,b,c;
- size_t len;
-
- /* Set up the internal state */
- len = length;
- a = b = level; /* the previous hash value */
- c = 0x9e3779b97f4a7c13LL; /* the golden ratio; an arbitrary value */
-
- /*---------------------------------------- handle most of the key */
- while (len >= 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)>>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<<i) < val; ++i)
- ;
- return i;
-}
-
-/* compute p(x), where p is a permutation of 0..(1<<nbits)-1 */
-/* permute(0)=0. This is intended and useful. */
-static ub4 permute(ub4 x, ub4 nbits)
-// ub4 x; /* input, a value in some range */
-// ub4 nbits; /* input, number of bits in range */
-{
- int i;
- int mask = ((ub4)1<<nbits)-1; /* all ones */
- int const2 = 1+nbits/2;
- int const3 = 1+nbits/3;
- int const4 = 1+nbits/4;
- int const5 = 1+nbits/5;
- for (i=0; i<20; ++i)
- {
- x = (x+(x<<const2)) & mask;
- x = (x^(x>>const3));
- x = (x+(x<<const4)) & mask;
- 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; i<SCRAMBLE_LEN; ++i)
- {
- scramble[i] = permute(i, log2u(smax));
- }
-}
-
-
-/*
- * put keys in tabb according to key->b_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; q<tail; ++q)
- {
- bstuff *myb = tabq[q].b_q; /* the b for this node */
- ub4 i; /* possible value for myb->val_b */
-
- if (q == 1)
- break; /* don't do transitive closure */
-
- for (i=0; i<limit; ++i)
- {
- bstuff *childb = (bstuff *)0; /* the b that this i maps to */
- key *mykey; /* for walking through myb's keys */
-
- for (mykey = myb->list_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<blen; ++i)
- if (tabb[i].listlen_b > 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<blen; ++i)
- if (tabb[i].listlen_b == j)
- if (!augment(tabb, tabh, tabq, blen, scramble, smax, &tabb[i], nkeys,
- i+1))
- {
- return FALSE;
- }
-
- /* Success! We found a perfect hash of all keys into 0..nkeys-1. */
- return TRUE;
-}
-
-
-/* guess initial values for alen and blen */
-static void initalen(ub4 *alen, ub4 *blen, ub4 smax, ub4 nkeys)
-// ub4 *alen; /* output, initial alen */
-// ub4 *blen; /* output, initial blen */
-// ub4 smax; /* input, power of two greater or equal to max hash value */
-// ub4 nkeys; /* number of keys being hashed */
-{
- /*
- * Find initial *alen, *blen
- * Initial alen and blen values were found empirically. Some factors:
- *
- * If smax<256 there is no scramble, so tab[b] needs to cover 0..smax-1.
- *
- * alen and blen must be powers of 2 because the values in 0..alen-1 and
- * 0..blen-1 are produced by applying a bitmask to the initial hash function.
- *
- * alen must be less than smax, in fact less than nkeys, because otherwise
- * there would often be no i such that a^scramble[i] is in 0..nkeys-1 for
- * all the *a*s associated with a given *b*, so there would be no legal
- * value to assign to tab[b]. This only matters when we're doing a minimal
- * perfect hash.
- *
- * It takes around 800 trials to find distinct (a,b) with nkey=smax*(5/8)
- * and alen*blen = smax*smax/32.
- *
- * Values of blen less than smax/4 never work, and smax/2 always works.
- *
- * We want blen as small as possible because it is the number of bytes in
- * the huge array we must create for the perfect hash.
- *
- * When nkey <= smax*(5/8), blen=smax/4 works much more often with
- * alen=smax/8 than with alen=smax/4. Above smax*(5/8), blen=smax/4
- * doesn't seem to care whether alen=smax/8 or alen=smax/4. I think it
- * has something to do with 5/8 = 1/8 * 5. For example examine 80000,
- * 85000, and 90000 keys with different values of alen. This only matters
- * if we're doing a minimal perfect hash.
- *
- * When alen*blen <= 1<<UB4BITS, the initial hash must produce one integer.
- * Bigger than that it must produce two integers, which increases the
- * cost of the hash per character hashed.
- */
- *alen = smax; /* no reason to restrict alen to smax/2 */
- *blen = ((nkeys <= smax*0.6) ? smax/16 :
- (nkeys <= smax*0.8) ? smax/8 : smax/4);
-
- if (*alen < 1) *alen = 1;
- if (*blen < 1) *blen = 1;
-
-#if SELOPT_DEBUG
- fprintf(stderr, "alen %d blen %d smax %d nkeys %d\n", *alen, *blen, smax, nkeys);
-#endif
-}
-
-/*
-** Try to find a perfect hash function.
-** Return the successful initializer for the initial hash.
-** Return 0 if no perfect hash could be found.
-*/
-static int findhash(bstuff **tabb, ub4 *alen, ub4 *blen, ub8 *salt,
- ub4 *scramble, ub4 smax, key *keys, ub4 nkeys)
-// bstuff **tabb; /* output, tab[] of the perfect hash, length *blen */
-// ub4 *alen; /* output, 0..alen-1 is range for a of (a,b) */
-// ub4 *blen; /* output, 0..blen-1 is range for b of (a,b) */
-// ub4 *salt; /* output, initializes initial hash */
-// ub4 *scramble; /* input, hash = a^scramble[tab[b]] */
-// ub4 smax; /* input, scramble[i] in 0..smax-1 */
-// key *keys; /* input, keys to hash */
-// ub4 nkeys; /* input, number of keys being hashed */
-{
- ub4 bad_initkey; /* how many times did initkey fail? */
- ub4 bad_perfect; /* how many times did perfect fail? */
- ub4 si; /* trial initializer for initial hash */
- ub4 maxalen;
- hstuff *tabh; /* table of keys indexed by hash value */
- qstuff *tabq; /* table of stuff indexed by queue value, used by augment */
-
- /* guess initial values for alen and blen */
- initalen(alen, blen, smax, nkeys);
-
- scrambleinit(scramble, smax);
-
- maxalen = smax;
-
- /* allocate working memory */
- *tabb = new bstuff[*blen];
- tabq = new qstuff[*blen+1];
- tabh = new hstuff[smax];
-
- /* Actually find the perfect hash */
- *salt = 0;
- bad_initkey = 0;
- bad_perfect = 0;
- for (si=1; ; ++si)
- {
- ub4 rslinit;
- /* Try to find distinct (A,B) for all keys */
- *salt = si * 0x9e3779b97f4a7c13LL; /* golden ratio (arbitrary value) */
- initnorm(keys, nkeys, *alen, *blen, smax, *salt);
- rslinit = inittab(*tabb, *blen, keys, nkeys, FALSE);
- if (rslinit == 0)
- {
- /* didn't find distinct (a,b) */
- if (++bad_initkey >= 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<<log2u(nkeys));
- ok = findhash(&tab, &alen, &blen, &salt,
- scramble, smax, keys, nkeys);
- if (!ok) {
- smax = 2 * ((ub4)1<<log2u(nkeys));
- ok = findhash(&tab, &alen, &blen, &salt,
- scramble, smax, keys, nkeys);
- }
- if (!ok) {
- bzero(&result, sizeof(result));
- } else {
- /* build the tables */
- result.capacity = smax;
- result.occupied = nkeys;
- result.shift = UB8BITS - log2u(alen);
- result.mask = blen - 1;
- result.salt = salt;
-
- result.tab = new uint8_t[blen];
- for (i = 0; i < blen; i++) {
- result.tab[i] = tab[i].val_b;
- }
- for (i = 0; i < 256; i++) {
- result.scramble[i] = scramble[i];
- }
- }
-
- delete[] keys;
- delete[] tab;
-
- return result;
-}
-
-// SELOPT_WRITE
-#endif
-
-// namespace objc_selopt
-};
-
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include "objc-private.h"
-#include "objc-sync.h"
-
-//
-// Allocate a lock only when needed. Since few locks are needed at any point
-// in time, keep them on a single list.
-//
-
-
-typedef struct SyncData {
- struct SyncData* nextData;
- id object;
- int threadCount; // number of THREADS using this block
- recursive_mutex_t mutex;
-} SyncData;
-
-typedef struct {
- SyncData *data;
- unsigned int lockCount; // number of times THIS THREAD locked this block
-} SyncCacheItem;
-
-typedef struct SyncCache {
- unsigned int allocated;
- unsigned int used;
- SyncCacheItem list[0];
-} SyncCache;
-
-/*
- Fast cache: two fixed pthread keys store a single SyncCacheItem.
- This avoids malloc of the SyncCache for threads that only synchronize
- a single object at a time.
- SYNC_DATA_DIRECT_KEY == SyncCacheItem.data
- SYNC_COUNT_DIRECT_KEY == SyncCacheItem.lockCount
- */
-
-typedef struct {
- SyncData *data;
- OSSpinLock lock;
-
- char align[64 - sizeof (OSSpinLock) - sizeof (SyncData *)];
-} SyncList __attribute__((aligned(64)));
-// aligned to put locks on separate cache lines
-
-// Use multiple parallel lists to decrease contention among unrelated objects.
-#define COUNT 16
-#define HASH(obj) ((((uintptr_t)(obj)) >> 5) & (COUNT - 1))
-#define LOCK_FOR_OBJ(obj) sDataLists[HASH(obj)].lock
-#define LIST_FOR_OBJ(obj) sDataLists[HASH(obj)].data
-static SyncList sDataLists[COUNT];
-
-
-enum usage { ACQUIRE, RELEASE, CHECK };
-
-static SyncCache *fetch_cache(BOOL create)
-{
- _objc_pthread_data *data;
-
- data = _objc_fetch_pthread_data(create);
- if (!data) return NULL;
-
- if (!data->syncCache) {
- if (!create) {
- return NULL;
- } else {
- int count = 4;
- data->syncCache = calloc(1, sizeof(SyncCache) +
- count*sizeof(SyncCacheItem));
- data->syncCache->allocated = count;
- }
- }
-
- // 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 =
- realloc(data->syncCache, sizeof(SyncCache)
- + data->syncCache->allocated * sizeof(SyncCacheItem));
- }
-
- return data->syncCache;
-}
-
-
-PRIVATE_EXTERN void _destroySyncCache(struct SyncCache *cache)
-{
- if (cache) free(cache);
-}
-
-
-static SyncData* id2data(id object, enum usage why)
-{
- OSSpinLock *lockp = &LOCK_FOR_OBJ(object);
- SyncData **listp = &LIST_FOR_OBJ(object);
- SyncData* result = NULL;
-
-#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);
- if (data) {
- fastCacheOccupied = YES;
-
- if (data->object == object) {
- // Found a match in fast cache.
- uintptr_t lockCount;
-
- result = data;
- lockCount = (uintptr_t)tls_get_direct(SYNC_COUNT_DIRECT_KEY);
- require_action_string(result->threadCount > 0, fastcache_done,
- result = NULL, "id2data fastcache is buggy");
- require_action_string(lockCount > 0, fastcache_done,
- result = NULL, "id2data fastcache is buggy");
-
- switch(why) {
- case ACQUIRE: {
- lockCount++;
- tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
- break;
- }
- case RELEASE:
- lockCount--;
- tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
- if (lockCount == 0) {
- // remove from fast cache
- tls_set_direct(SYNC_DATA_DIRECT_KEY, NULL);
- // atomic because may collide with concurrent ACQUIRE
- OSAtomicDecrement32Barrier(&result->threadCount);
- }
- break;
- case CHECK:
- // do nothing
- break;
- }
-
- fastcache_done:
- return result;
- }
- }
-#endif
-
- // Check per-thread cache of already-owned locks for matching object
- SyncCache *cache = fetch_cache(NO);
- if (cache) {
- unsigned int i;
- for (i = 0; i < cache->used; i++) {
- SyncCacheItem *item = &cache->list[i];
- if (item->data->object != object) continue;
-
- // Found a match.
- result = item->data;
- require_action_string(result->threadCount > 0, cache_done,
- result = NULL, "id2data cache is buggy");
- require_action_string(item->lockCount > 0, cache_done,
- result = NULL, "id2data cache is buggy");
-
- switch(why) {
- case ACQUIRE:
- item->lockCount++;
- break;
- case RELEASE:
- item->lockCount--;
- if (item->lockCount == 0) {
- // remove from per-thread cache
- cache->list[i] = cache->list[--cache->used];
- // atomic because may collide with concurrent ACQUIRE
- OSAtomicDecrement32Barrier(&result->threadCount);
- }
- break;
- case CHECK:
- // do nothing
- break;
- }
-
- cache_done:
- return result;
- }
- }
-
- // Thread cache didn't find anything.
- // Walk in-use list looking for matching object
- // Spinlock prevents multiple threads from creating multiple
- // locks for the same new object.
- // We could keep the nodes in some hash table if we find that there are
- // more than 20 or so distinct locks active, but we don't do that now.
-
- OSSpinLockLock(lockp);
-
- {
- SyncData* p;
- SyncData* firstUnused = NULL;
- for (p = *listp; p != NULL; p = p->nextData) {
- if ( p->object == object ) {
- result = p;
- // atomic because may collide with concurrent RELEASE
- OSAtomicIncrement32Barrier(&result->threadCount);
- goto done;
- }
- if ( (firstUnused == NULL) && (p->threadCount == 0) )
- firstUnused = p;
- }
-
- // no SyncData currently associated with object
- if ( (why == RELEASE) || (why == CHECK) )
- goto done;
-
- // an unused one was found, use it
- if ( firstUnused != NULL ) {
- result = firstUnused;
- result->object = object;
- result->threadCount = 1;
- goto done;
- }
- }
-
- // malloc a new SyncData and add to list.
- // XXX calling malloc with a global lock held is bad practice,
- // might be worth releasing the lock, mallocing, and searching again.
- // But since we never free these guys we won't be stuck in malloc very often.
- result = (SyncData*)calloc(sizeof(SyncData), 1);
- result->object = object;
- result->threadCount = 1;
- recursive_mutex_init(&result->mutex);
- result->nextData = *listp;
- *listp = result;
-
- done:
- OSSpinLockUnlock(lockp);
- if (result) {
- // Only new ACQUIRE should get here.
- // All RELEASE and CHECK and recursive ACQUIRE are
- // handled by the per-thread caches above.
-
- require_string(result != NULL, really_done, "id2data is buggy");
- require_action_string(why == ACQUIRE, really_done,
- result = NULL, "id2data is buggy");
- require_action_string(result->object == object, really_done,
- result = NULL, "id2data is buggy");
-
-#if SUPPORT_DIRECT_THREAD_KEYS
- if (!fastCacheOccupied) {
- // Save in fast thread cache
- tls_set_direct(SYNC_DATA_DIRECT_KEY, result);
- tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)1);
- } else
-#endif
- {
- // Save in thread cache
- if (!cache) cache = fetch_cache(YES);
- cache->list[cache->used].data = result;
- cache->list[cache->used].lockCount = 1;
- cache->used++;
- }
- }
-
- really_done:
- return result;
-}
-
-
-BREAKPOINT_FUNCTION(
- void objc_sync_nil(void)
-);
-
-
-// Begin synchronizing on 'obj'.
-// Allocates recursive mutex associated with 'obj' if needed.
-// Returns OBJC_SYNC_SUCCESS once lock is acquired.
-int objc_sync_enter(id obj)
-{
- int result = OBJC_SYNC_SUCCESS;
-
- if (obj) {
- SyncData* data = id2data(obj, ACQUIRE);
- require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_INITIALIZED, "id2data failed");
-
- result = recursive_mutex_lock(&data->mutex);
- require_noerr_string(result, done, "mutex_lock failed");
- } else {
- // @synchronized(nil) does nothing
- if (DebugNilSync) {
- _objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
- }
- objc_sync_nil();
- }
-
-done:
- return result;
-}
-
-
-// End synchronizing on 'obj'.
-// Returns OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERROR
-int objc_sync_exit(id obj)
-{
- int result = OBJC_SYNC_SUCCESS;
-
- if (obj) {
- SyncData* data = id2data(obj, RELEASE);
- require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
-
- result = recursive_mutex_unlock(&data->mutex);
- require_noerr_string(result, done, "mutex_unlock failed");
- } else {
- // @synchronized(nil) does nothing
- }
-
-done:
- if ( result == RECURSIVE_MUTEX_NOT_LOCKED )
- result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
-
- return result;
-}
-
--- /dev/null
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "objc-private.h"
+#include "objc-sync.h"
+
+//
+// Allocate a lock only when needed. Since few locks are needed at any point
+// in time, keep them on a single list.
+//
+
+
+typedef struct SyncData {
+ struct SyncData* nextData;
+ id object;
+ int threadCount; // number of THREADS using this block
+ recursive_mutex_t mutex;
+} SyncData;
+
+typedef struct {
+ SyncData *data;
+ unsigned int lockCount; // number of times THIS THREAD locked this block
+} SyncCacheItem;
+
+typedef struct SyncCache {
+ unsigned int allocated;
+ unsigned int used;
+ SyncCacheItem list[0];
+} SyncCache;
+
+/*
+ Fast cache: two fixed pthread keys store a single SyncCacheItem.
+ This avoids malloc of the SyncCache for threads that only synchronize
+ a single object at a time.
+ SYNC_DATA_DIRECT_KEY == SyncCacheItem.data
+ SYNC_COUNT_DIRECT_KEY == SyncCacheItem.lockCount
+ */
+
+typedef struct {
+ SyncData *data;
+ OSSpinLock lock;
+
+ char align[64 - sizeof (OSSpinLock) - sizeof (SyncData *)];
+} SyncList __attribute__((aligned(64)));
+// aligned to put locks on separate cache lines
+
+// Use multiple parallel lists to decrease contention among unrelated objects.
+#define COUNT 16
+#define HASH(obj) ((((uintptr_t)(obj)) >> 5) & (COUNT - 1))
+#define LOCK_FOR_OBJ(obj) sDataLists[HASH(obj)].lock
+#define LIST_FOR_OBJ(obj) sDataLists[HASH(obj)].data
+static SyncList sDataLists[COUNT];
+
+
+enum usage { ACQUIRE, RELEASE, CHECK };
+
+static SyncCache *fetch_cache(BOOL create)
+{
+ _objc_pthread_data *data;
+
+ data = _objc_fetch_pthread_data(create);
+ if (!data) return NULL;
+
+ if (!data->syncCache) {
+ if (!create) {
+ return NULL;
+ } else {
+ int count = 4;
+ data->syncCache = (SyncCache *)
+ calloc(1, sizeof(SyncCache) + count*sizeof(SyncCacheItem));
+ data->syncCache->allocated = count;
+ }
+ }
+
+ // 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 = (SyncCache *)
+ realloc(data->syncCache, sizeof(SyncCache)
+ + data->syncCache->allocated * sizeof(SyncCacheItem));
+ }
+
+ return data->syncCache;
+}
+
+
+void _destroySyncCache(struct SyncCache *cache)
+{
+ if (cache) free(cache);
+}
+
+
+static SyncData* id2data(id object, enum usage why)
+{
+ OSSpinLock *lockp = &LOCK_FOR_OBJ(object);
+ SyncData **listp = &LIST_FOR_OBJ(object);
+ SyncData* result = NULL;
+
+#if SUPPORT_DIRECT_THREAD_KEYS
+ // Check per-thread single-entry fast cache for matching object
+ BOOL fastCacheOccupied = NO;
+ SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY);
+ if (data) {
+ fastCacheOccupied = YES;
+
+ if (data->object == object) {
+ // Found a match in fast cache.
+ uintptr_t lockCount;
+
+ result = data;
+ lockCount = (uintptr_t)tls_get_direct(SYNC_COUNT_DIRECT_KEY);
+ require_action_string(result->threadCount > 0, fastcache_done,
+ result = NULL, "id2data fastcache is buggy");
+ require_action_string(lockCount > 0, fastcache_done,
+ result = NULL, "id2data fastcache is buggy");
+
+ switch(why) {
+ case ACQUIRE: {
+ lockCount++;
+ tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
+ break;
+ }
+ case RELEASE:
+ lockCount--;
+ tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
+ if (lockCount == 0) {
+ // remove from fast cache
+ tls_set_direct(SYNC_DATA_DIRECT_KEY, NULL);
+ // atomic because may collide with concurrent ACQUIRE
+ OSAtomicDecrement32Barrier(&result->threadCount);
+ }
+ break;
+ case CHECK:
+ // do nothing
+ break;
+ }
+
+ fastcache_done:
+ return result;
+ }
+ }
+#endif
+
+ // Check per-thread cache of already-owned locks for matching object
+ SyncCache *cache = fetch_cache(NO);
+ if (cache) {
+ unsigned int i;
+ for (i = 0; i < cache->used; i++) {
+ SyncCacheItem *item = &cache->list[i];
+ if (item->data->object != object) continue;
+
+ // Found a match.
+ result = item->data;
+ require_action_string(result->threadCount > 0, cache_done,
+ result = NULL, "id2data cache is buggy");
+ require_action_string(item->lockCount > 0, cache_done,
+ result = NULL, "id2data cache is buggy");
+
+ switch(why) {
+ case ACQUIRE:
+ item->lockCount++;
+ break;
+ case RELEASE:
+ item->lockCount--;
+ if (item->lockCount == 0) {
+ // remove from per-thread cache
+ cache->list[i] = cache->list[--cache->used];
+ // atomic because may collide with concurrent ACQUIRE
+ OSAtomicDecrement32Barrier(&result->threadCount);
+ }
+ break;
+ case CHECK:
+ // do nothing
+ break;
+ }
+
+ cache_done:
+ return result;
+ }
+ }
+
+ // Thread cache didn't find anything.
+ // Walk in-use list looking for matching object
+ // Spinlock prevents multiple threads from creating multiple
+ // locks for the same new object.
+ // We could keep the nodes in some hash table if we find that there are
+ // more than 20 or so distinct locks active, but we don't do that now.
+
+ OSSpinLockLock(lockp);
+
+ {
+ SyncData* p;
+ SyncData* firstUnused = NULL;
+ for (p = *listp; p != NULL; p = p->nextData) {
+ if ( p->object == object ) {
+ result = p;
+ // atomic because may collide with concurrent RELEASE
+ OSAtomicIncrement32Barrier(&result->threadCount);
+ goto done;
+ }
+ if ( (firstUnused == NULL) && (p->threadCount == 0) )
+ firstUnused = p;
+ }
+
+ // no SyncData currently associated with object
+ if ( (why == RELEASE) || (why == CHECK) )
+ goto done;
+
+ // an unused one was found, use it
+ if ( firstUnused != NULL ) {
+ result = firstUnused;
+ result->object = object;
+ result->threadCount = 1;
+ goto done;
+ }
+ }
+
+ // malloc a new SyncData and add to list.
+ // XXX calling malloc with a global lock held is bad practice,
+ // might be worth releasing the lock, mallocing, and searching again.
+ // But since we never free these guys we won't be stuck in malloc very often.
+ result = (SyncData*)calloc(sizeof(SyncData), 1);
+ result->object = object;
+ result->threadCount = 1;
+ recursive_mutex_init(&result->mutex);
+ result->nextData = *listp;
+ *listp = result;
+
+ done:
+ OSSpinLockUnlock(lockp);
+ if (result) {
+ // Only new ACQUIRE should get here.
+ // All RELEASE and CHECK and recursive ACQUIRE are
+ // handled by the per-thread caches above.
+
+ require_string(result != NULL, really_done, "id2data is buggy");
+ require_action_string(why == ACQUIRE, really_done,
+ result = NULL, "id2data is buggy");
+ require_action_string(result->object == object, really_done,
+ result = NULL, "id2data is buggy");
+
+#if SUPPORT_DIRECT_THREAD_KEYS
+ if (!fastCacheOccupied) {
+ // Save in fast thread cache
+ tls_set_direct(SYNC_DATA_DIRECT_KEY, result);
+ tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)1);
+ } else
+#endif
+ {
+ // Save in thread cache
+ if (!cache) cache = fetch_cache(YES);
+ cache->list[cache->used].data = result;
+ cache->list[cache->used].lockCount = 1;
+ cache->used++;
+ }
+ }
+
+ really_done:
+ return result;
+}
+
+
+BREAKPOINT_FUNCTION(
+ void objc_sync_nil(void)
+);
+
+
+// Begin synchronizing on 'obj'.
+// Allocates recursive mutex associated with 'obj' if needed.
+// Returns OBJC_SYNC_SUCCESS once lock is acquired.
+int objc_sync_enter(id obj)
+{
+ int result = OBJC_SYNC_SUCCESS;
+
+ if (obj) {
+ SyncData* data = id2data(obj, ACQUIRE);
+ require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_INITIALIZED, "id2data failed");
+
+ result = recursive_mutex_lock(&data->mutex);
+ require_noerr_string(result, done, "mutex_lock failed");
+ } else {
+ // @synchronized(nil) does nothing
+ if (DebugNilSync) {
+ _objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
+ }
+ objc_sync_nil();
+ }
+
+done:
+ return result;
+}
+
+
+// End synchronizing on 'obj'.
+// Returns OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERROR
+int objc_sync_exit(id obj)
+{
+ int result = OBJC_SYNC_SUCCESS;
+
+ if (obj) {
+ SyncData* data = id2data(obj, RELEASE);
+ require_action_string(data != NULL, done, result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR, "id2data failed");
+
+ result = recursive_mutex_unlock(&data->mutex);
+ require_noerr_string(result, done, "mutex_unlock failed");
+ } else {
+ // @synchronized(nil) does nothing
+ }
+
+done:
+ if ( result == RECURSIVE_MUTEX_NOT_LOCKED )
+ result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
+
+ return result;
+}
+
+++ /dev/null
-/*
- * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-/***********************************************************************
-* objc-typeencoding.m
-* Parsing of old-style type strings.
-**********************************************************************/
-
-#include "objc-private.h"
-
-/***********************************************************************
-* SubtypeUntil.
-*
-* Delegation.
-**********************************************************************/
-static int SubtypeUntil (const char * type,
- char end)
-{
- int level = 0;
- const char * head = type;
-
- //
- while (*type)
- {
- if (!*type || (!level && (*type == end)))
- return (int)(type - head);
-
- switch (*type)
- {
- case ']': case '}': case ')': level--; break;
- case '[': case '{': case '(': level += 1; break;
- }
-
- type += 1;
- }
-
- _objc_fatal ("Object: SubtypeUntil: end of type encountered prematurely\n");
- return 0;
-}
-
-
-/***********************************************************************
-* SkipFirstType.
-**********************************************************************/
-static const char * SkipFirstType (const char * type)
-{
- while (1)
- {
- switch (*type++)
- {
- case 'O': /* bycopy */
- case 'n': /* in */
- case 'o': /* out */
- case 'N': /* inout */
- case 'r': /* const */
- case 'V': /* oneway */
- case '^': /* pointers */
- break;
-
- case '@': /* objects */
- if (type[0] == '?') type++; /* Blocks */
- return type;
-
- /* arrays */
- case '[':
- while ((*type >= '0') && (*type <= '9'))
- type += 1;
- return type + SubtypeUntil (type, ']') + 1;
-
- /* structures */
- case '{':
- return type + SubtypeUntil (type, '}') + 1;
-
- /* unions */
- case '(':
- return type + SubtypeUntil (type, ')') + 1;
-
- /* basic types */
- default:
- return type;
- }
- }
-}
-
-
-/***********************************************************************
-* encoding_getNumberOfArguments.
-**********************************************************************/
-PRIVATE_EXTERN unsigned int
-encoding_getNumberOfArguments(const char *typedesc)
-{
- unsigned nargs;
-
- // First, skip the return type
- typedesc = SkipFirstType (typedesc);
-
- // Next, skip stack size
- while ((*typedesc >= '0') && (*typedesc <= '9'))
- typedesc += 1;
-
- // Now, we have the arguments - count how many
- nargs = 0;
- while (*typedesc)
- {
- // Traverse argument type
- typedesc = SkipFirstType (typedesc);
-
- // Skip GNU runtime's register parameter hint
- if (*typedesc == '+') typedesc++;
-
- // Traverse (possibly negative) argument offset
- if (*typedesc == '-')
- typedesc += 1;
- while ((*typedesc >= '0') && (*typedesc <= '9'))
- typedesc += 1;
-
- // Made it past an argument
- nargs += 1;
- }
-
- return nargs;
-}
-
-/***********************************************************************
-* encoding_getSizeOfArguments.
-**********************************************************************/
-PRIVATE_EXTERN unsigned
-encoding_getSizeOfArguments(const char *typedesc)
-{
- unsigned stack_size;
-
- // Get our starting points
- stack_size = 0;
-
- // Skip the return type
- typedesc = SkipFirstType (typedesc);
-
- // Convert ASCII number string to integer
- while ((*typedesc >= '0') && (*typedesc <= '9'))
- stack_size = (stack_size * 10) + (*typedesc++ - '0');
-
- return stack_size;
-}
-
-
-/***********************************************************************
-* encoding_getArgumentInfo.
-**********************************************************************/
-PRIVATE_EXTERN unsigned int
-encoding_getArgumentInfo(const char *typedesc, int arg,
- const char **type, int *offset)
-{
- unsigned nargs = 0;
- int self_offset = 0;
- BOOL offset_is_negative = NO;
-
- // First, skip the return type
- typedesc = SkipFirstType (typedesc);
-
- // Next, skip stack size
- while ((*typedesc >= '0') && (*typedesc <= '9'))
- typedesc += 1;
-
- // Now, we have the arguments - position typedesc to the appropriate argument
- while (*typedesc && nargs != arg)
- {
-
- // Skip argument type
- typedesc = SkipFirstType (typedesc);
-
- if (nargs == 0)
- {
- // Skip GNU runtime's register parameter hint
- if (*typedesc == '+') typedesc++;
-
- // Skip negative sign in offset
- if (*typedesc == '-')
- {
- offset_is_negative = YES;
- typedesc += 1;
- }
- else
- offset_is_negative = NO;
-
- while ((*typedesc >= '0') && (*typedesc <= '9'))
- self_offset = self_offset * 10 + (*typedesc++ - '0');
- if (offset_is_negative)
- self_offset = -(self_offset);
-
- }
-
- else
- {
- // Skip GNU runtime's register parameter hint
- if (*typedesc == '+') typedesc++;
-
- // Skip (possibly negative) argument offset
- if (*typedesc == '-')
- typedesc += 1;
- while ((*typedesc >= '0') && (*typedesc <= '9'))
- typedesc += 1;
- }
-
- nargs += 1;
- }
-
- if (*typedesc)
- {
- int arg_offset = 0;
-
- *type = typedesc;
- typedesc = SkipFirstType (typedesc);
-
- if (arg == 0)
- {
- *offset = 0;
- }
-
- else
- {
- // Skip GNU register parameter hint
- if (*typedesc == '+') typedesc++;
-
- // Pick up (possibly negative) argument offset
- if (*typedesc == '-')
- {
- offset_is_negative = YES;
- typedesc += 1;
- }
- else
- offset_is_negative = NO;
-
- while ((*typedesc >= '0') && (*typedesc <= '9'))
- arg_offset = arg_offset * 10 + (*typedesc++ - '0');
- if (offset_is_negative)
- arg_offset = - arg_offset;
-
- *offset = arg_offset - self_offset;
- }
-
- }
-
- else
- {
- *type = 0;
- *offset = 0;
- }
-
- return nargs;
-}
-
-
-PRIVATE_EXTERN void
-encoding_getReturnType(const char *t, char *dst, size_t dst_len)
-{
- size_t len;
- const char *end;
-
- if (!dst) return;
- if (!t) {
- strncpy(dst, "", dst_len);
- return;
- }
-
- end = SkipFirstType(t);
- len = end - t;
- strncpy(dst, t, MIN(len, dst_len));
- if (len < dst_len) memset(dst+len, 0, dst_len - len);
-}
-
-/***********************************************************************
-* encoding_copyReturnType. Returns the method's return type string
-* on the heap.
-**********************************************************************/
-PRIVATE_EXTERN char *
-encoding_copyReturnType(const char *t)
-{
- size_t len;
- const char *end;
- char *result;
-
- if (!t) return NULL;
-
- end = SkipFirstType(t);
- len = end - t;
- result = malloc(len + 1);
- strncpy(result, t, len);
- result[len] = '\0';
- return result;
-}
-
-
-PRIVATE_EXTERN void
-encoding_getArgumentType(const char *t, unsigned int index,
- char *dst, size_t dst_len)
-{
- size_t len;
- const char *end;
- int offset;
-
- if (!dst) return;
- if (!t) {
- strncpy(dst, "", dst_len);
- return;
- }
-
- encoding_getArgumentInfo(t, index, &t, &offset);
-
- if (!t) {
- strncpy(dst, "", dst_len);
- return;
- }
-
- end = SkipFirstType(t);
- len = end - t;
- strncpy(dst, t, MIN(len, dst_len));
- if (len < dst_len) memset(dst+len, 0, dst_len - len);
-}
-
-
-/***********************************************************************
-* encoding_copyArgumentType. Returns a single argument's type string
-* on the heap. Argument 0 is `self`; argument 1 is `_cmd`.
-**********************************************************************/
-PRIVATE_EXTERN char *
-encoding_copyArgumentType(const char *t, unsigned int index)
-{
- size_t len;
- const char *end;
- char *result;
- int offset;
-
- if (!t) return NULL;
-
- encoding_getArgumentInfo(t, index, &t, &offset);
-
- if (!t) return NULL;
-
- end = SkipFirstType(t);
- len = end - t;
- result = malloc(len + 1);
- strncpy(result, t, len);
- result[len] = '\0';
- return result;
-}
--- /dev/null
+/*
+ * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/***********************************************************************
+* objc-typeencoding.m
+* Parsing of old-style type strings.
+**********************************************************************/
+
+#include "objc-private.h"
+
+/***********************************************************************
+* SubtypeUntil.
+*
+* Delegation.
+**********************************************************************/
+static int SubtypeUntil (const char * type,
+ char end)
+{
+ int level = 0;
+ const char * head = type;
+
+ //
+ while (*type)
+ {
+ if (!*type || (!level && (*type == end)))
+ return (int)(type - head);
+
+ switch (*type)
+ {
+ case ']': case '}': case ')': level--; break;
+ case '[': case '{': case '(': level += 1; break;
+ }
+
+ type += 1;
+ }
+
+ _objc_fatal ("Object: SubtypeUntil: end of type encountered prematurely\n");
+ return 0;
+}
+
+
+/***********************************************************************
+* SkipFirstType.
+**********************************************************************/
+static const char * SkipFirstType (const char * type)
+{
+ while (1)
+ {
+ switch (*type++)
+ {
+ case 'O': /* bycopy */
+ case 'n': /* in */
+ case 'o': /* out */
+ case 'N': /* inout */
+ case 'r': /* const */
+ case 'V': /* oneway */
+ case '^': /* pointers */
+ break;
+
+ case '@': /* objects */
+ if (type[0] == '?') type++; /* Blocks */
+ return type;
+
+ /* arrays */
+ case '[':
+ while ((*type >= '0') && (*type <= '9'))
+ type += 1;
+ return type + SubtypeUntil (type, ']') + 1;
+
+ /* structures */
+ case '{':
+ return type + SubtypeUntil (type, '}') + 1;
+
+ /* unions */
+ case '(':
+ return type + SubtypeUntil (type, ')') + 1;
+
+ /* basic types */
+ default:
+ return type;
+ }
+ }
+}
+
+
+/***********************************************************************
+* encoding_getNumberOfArguments.
+**********************************************************************/
+unsigned int
+encoding_getNumberOfArguments(const char *typedesc)
+{
+ unsigned nargs;
+
+ // First, skip the return type
+ typedesc = SkipFirstType (typedesc);
+
+ // Next, skip stack size
+ while ((*typedesc >= '0') && (*typedesc <= '9'))
+ typedesc += 1;
+
+ // Now, we have the arguments - count how many
+ nargs = 0;
+ while (*typedesc)
+ {
+ // Traverse argument type
+ typedesc = SkipFirstType (typedesc);
+
+ // Skip GNU runtime's register parameter hint
+ if (*typedesc == '+') typedesc++;
+
+ // Traverse (possibly negative) argument offset
+ if (*typedesc == '-')
+ typedesc += 1;
+ while ((*typedesc >= '0') && (*typedesc <= '9'))
+ typedesc += 1;
+
+ // Made it past an argument
+ nargs += 1;
+ }
+
+ return nargs;
+}
+
+/***********************************************************************
+* encoding_getSizeOfArguments.
+**********************************************************************/
+unsigned
+encoding_getSizeOfArguments(const char *typedesc)
+{
+ unsigned stack_size;
+
+ // Get our starting points
+ stack_size = 0;
+
+ // Skip the return type
+ typedesc = SkipFirstType (typedesc);
+
+ // Convert ASCII number string to integer
+ while ((*typedesc >= '0') && (*typedesc <= '9'))
+ stack_size = (stack_size * 10) + (*typedesc++ - '0');
+
+ return stack_size;
+}
+
+
+/***********************************************************************
+* encoding_getArgumentInfo.
+**********************************************************************/
+unsigned int
+encoding_getArgumentInfo(const char *typedesc, unsigned int arg,
+ const char **type, int *offset)
+{
+ unsigned nargs = 0;
+ int self_offset = 0;
+ BOOL offset_is_negative = NO;
+
+ // First, skip the return type
+ typedesc = SkipFirstType (typedesc);
+
+ // Next, skip stack size
+ while ((*typedesc >= '0') && (*typedesc <= '9'))
+ typedesc += 1;
+
+ // Now, we have the arguments - position typedesc to the appropriate argument
+ while (*typedesc && nargs != arg)
+ {
+
+ // Skip argument type
+ typedesc = SkipFirstType (typedesc);
+
+ if (nargs == 0)
+ {
+ // Skip GNU runtime's register parameter hint
+ if (*typedesc == '+') typedesc++;
+
+ // Skip negative sign in offset
+ if (*typedesc == '-')
+ {
+ offset_is_negative = YES;
+ typedesc += 1;
+ }
+ else
+ offset_is_negative = NO;
+
+ while ((*typedesc >= '0') && (*typedesc <= '9'))
+ self_offset = self_offset * 10 + (*typedesc++ - '0');
+ if (offset_is_negative)
+ self_offset = -(self_offset);
+
+ }
+
+ else
+ {
+ // Skip GNU runtime's register parameter hint
+ if (*typedesc == '+') typedesc++;
+
+ // Skip (possibly negative) argument offset
+ if (*typedesc == '-')
+ typedesc += 1;
+ while ((*typedesc >= '0') && (*typedesc <= '9'))
+ typedesc += 1;
+ }
+
+ nargs += 1;
+ }
+
+ if (*typedesc)
+ {
+ int arg_offset = 0;
+
+ *type = typedesc;
+ typedesc = SkipFirstType (typedesc);
+
+ if (arg == 0)
+ {
+ *offset = 0;
+ }
+
+ else
+ {
+ // Skip GNU register parameter hint
+ if (*typedesc == '+') typedesc++;
+
+ // Pick up (possibly negative) argument offset
+ if (*typedesc == '-')
+ {
+ offset_is_negative = YES;
+ typedesc += 1;
+ }
+ else
+ offset_is_negative = NO;
+
+ while ((*typedesc >= '0') && (*typedesc <= '9'))
+ arg_offset = arg_offset * 10 + (*typedesc++ - '0');
+ if (offset_is_negative)
+ arg_offset = - arg_offset;
+
+ *offset = arg_offset - self_offset;
+ }
+
+ }
+
+ else
+ {
+ *type = 0;
+ *offset = 0;
+ }
+
+ return nargs;
+}
+
+
+void
+encoding_getReturnType(const char *t, char *dst, size_t dst_len)
+{
+ size_t len;
+ const char *end;
+
+ if (!dst) return;
+ if (!t) {
+ strncpy(dst, "", dst_len);
+ return;
+ }
+
+ end = SkipFirstType(t);
+ len = end - t;
+ strncpy(dst, t, MIN(len, dst_len));
+ if (len < dst_len) memset(dst+len, 0, dst_len - len);
+}
+
+/***********************************************************************
+* encoding_copyReturnType. Returns the method's return type string
+* on the heap.
+**********************************************************************/
+char *
+encoding_copyReturnType(const char *t)
+{
+ size_t len;
+ const char *end;
+ char *result;
+
+ if (!t) return NULL;
+
+ end = SkipFirstType(t);
+ len = end - t;
+ result = (char *)malloc(len + 1);
+ strncpy(result, t, len);
+ result[len] = '\0';
+ return result;
+}
+
+
+void
+encoding_getArgumentType(const char *t, unsigned int index,
+ char *dst, size_t dst_len)
+{
+ size_t len;
+ const char *end;
+ int offset;
+
+ if (!dst) return;
+ if (!t) {
+ strncpy(dst, "", dst_len);
+ return;
+ }
+
+ encoding_getArgumentInfo(t, index, &t, &offset);
+
+ if (!t) {
+ strncpy(dst, "", dst_len);
+ return;
+ }
+
+ end = SkipFirstType(t);
+ len = end - t;
+ strncpy(dst, t, MIN(len, dst_len));
+ if (len < dst_len) memset(dst+len, 0, dst_len - len);
+}
+
+
+/***********************************************************************
+* encoding_copyArgumentType. Returns a single argument's type string
+* on the heap. Argument 0 is `self`; argument 1 is `_cmd`.
+**********************************************************************/
+char *
+encoding_copyArgumentType(const char *t, unsigned int index)
+{
+ size_t len;
+ const char *end;
+ char *result;
+ int offset;
+
+ if (!t) return NULL;
+
+ encoding_getArgumentInfo(t, index, &t, &offset);
+
+ if (!t) return NULL;
+
+ end = SkipFirstType(t);
+ len = end - t;
+ result = (char *)malloc(len + 1);
+ strncpy(result, t, len);
+ result[len] = '\0';
+ return result;
+}
*/
#include <objc/objc.h>
-#import "objc-config.h"
+#include "objc-config.h"
__BEGIN_DECLS
* @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 <stdint.h>
-#import <stdbool.h>
-#import <sys/types.h>
-#import <libkern/OSAtomic.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <libkern/OSAtomic.h>
template <typename T> struct WeakAllocator {
//
// 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); }
//
// 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; }
//
//
// 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; }
//
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);
}
// 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;
// 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);
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) {
}
-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))
// 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
} *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))
#define __autoreleasing /* empty */
#endif
+
OBJC_EXPORT const char *sel_getName(SEL sel)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT SEL sel_registerName(const char *str)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
-#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__
+#ifndef _OBJC_RT_H_\r
+#define _OBJC_RT_H_\r
+\r
#include <objc/objc-api.h>\r
\r
\r
OBJC_EXPORT void *_objc_init_image(HMODULE image, const objc_sections *sects);\r
OBJC_EXPORT void _objc_load_image(HMODULE image, void *hinfo);\r
OBJC_EXPORT void _objc_unload_image(HMODULE image, void *hinfo);\r
+\r
+#endif\r
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)
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);
#import <Foundation/NSObject.h>
@interface ARRBase : NSObject
-@property double number;
+@property long number;
@property(retain) id object;
@property void *pointer;
@property(weak) __weak id delegate;
#if 1
@interface ARRBase () {
@private
- double number;
+ long number;
id object;
void *pointer;
__weak id delegate;
-// 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 <stdio.h>
-#import <assert.h>
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
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;
}
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];
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;
# 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
@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); }
@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__);
--- /dev/null
+// TEST_CONFIG MEM=mrc,arc
+
+#include "test.h"
+#include <objc/runtime.h>
+#include <objc/objc-abi.h>
+
+#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__);
+}
// TEST_CONFIG
#include "test.h"
+#include "testroot.i"
#include <objc/runtime.h>
-@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
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
int main()
{
Protocol *proto, *proto2;
- Protocol * const *protolist;
+ Protocol * __unsafe_unretained *protolist;
struct objc_method_description *desclist;
objc_property_t *proplist;
unsigned int count;
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)));
// 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);
--- /dev/null
+// 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__);
+}
-// 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 <Foundation/Foundation.h>
+
+#include <Foundation/NSObject.h>
static id weak;
static id weak2;
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];
}
@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];
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]");
}
#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
-// TEST_CFLAGS -framework Foundation
+// TEST_CONFIG
#include "test.h"
-#include <Foundation/Foundation.h>
+#include <Foundation/NSObject.h>
#include <objc/runtime.h>
static int values;
+static int supers;
static int subs;
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
{
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
{
@implementation Value
-(void) dealloc {
values++;
- [super dealloc];
+ SUPER_DEALLOC();
}
-(void) finalize {
values++;
}
@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__);
}
--- /dev/null
+// TEST_CONFIG CC=clang
+
+#include "test.h"
+#include <objc/runtime.h>
+#include <objc/objc-internal.h>
+#import <Foundation/NSObject.h>
+
+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__);
+}
#include "test.h"
-#include <pthread.h>
#include <objc/objc-exception.h>
/*
{
}
-void *fn(void *arg __unused)
-{
- @try {
- Token = objc_addExceptionHandler(&handler, NULL);
- } @catch (...) {
- }
-
- return NULL;
-}
-
int main()
{
#if __clang__ && __cplusplus
// 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);
#include <Block_private.h>
-#if !__clang__ && !__llvm__
- // gcc will never support struct-return marking
-# define STRET_OK 0
-#elif !clang
-// llvm-gcc waiting for rdar://8143947
+#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
- (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);
@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
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;
/* End copied code */
int main () {
-#if __llvm__ && !__clang__
- // edit STRET_OK above when you remove this
- testwarn("<rdar://8143947> struct-return blocks not yet integrated in llvm-gcc");
-#endif
-
// make sure the bits are in place
int (^registerReturn)() = ^(){ return 42; };
ArgumentMode aMode;
#define TEST_QUANTITY 100000
static FuncPtr funcArray[TEST_QUANTITY];
- uint32_t i;
+ uintptr_t i;
for(i = 0; i<TEST_QUANTITY; i++) {
- uint32_t (^block)(id self) = ^uint32_t(id self) {
- testassert((vm_address_t) self == (vm_address_t) i);
+ uintptr_t (^block)(void *self) = ^uintptr_t(void *self) {
+ testassert(i == (uintptr_t)self);
return i;
};
- block = Block_copy(block);
+ block = (__bridge id)_Block_copy((__bridge void *)block);
funcArray[i] = (FuncPtr) imp_implementationWithBlock(block);
- testassert(block((id)(uintptr_t) i) == i);
+ testassert(block((void *)i) == i);
- void *blockFromIMPResult = imp_getBlock((IMP)funcArray[i]);
- testassert(blockFromIMPResult == block);
+ id blockFromIMPResult = imp_getBlock((IMP)funcArray[i]);
+ testassert(blockFromIMPResult == (id)block);
- Block_release(block);
+ _Block_release((__bridge void *)block);
}
for(i = 0; i<TEST_QUANTITY; i++) {
- uint32_t result = funcArray[i]((id)(uintptr_t) i, 0);
+ uintptr_t result = funcArray[i]((void *)i, 0);
testassert(i == result);
}
for(i = 0; i < TEST_QUANTITY; i= i + 3) {
imp_removeBlock((IMP)funcArray[i]);
- void *shouldBeNull = imp_getBlock((IMP)funcArray[i]);
- assert(shouldBeNull == NULL);
+ id shouldBeNull = imp_getBlock((IMP)funcArray[i]);
+ testassert(shouldBeNull == NULL);
}
for(i = 0; i < TEST_QUANTITY; i= i + 3) {
- uint32_t j = i * i;
+ uintptr_t j = i * i;
- uint32_t (^block)(id self) = ^uint32_t(id self) {
- uint32_t value = (uint32_t)(uintptr_t) self;
- testassert(j == value);
+ uintptr_t (^block)(void *) = ^uintptr_t(void *self) {
+ testassert(j == (uintptr_t)self);
return j;
};
funcArray[i] = (FuncPtr) imp_implementationWithBlock(block);
- testassert(block((id)(uintptr_t)j) == j);
- testassert(funcArray[i]((id)(uintptr_t)j, 0) == j);
+ testassert(block((void *)j) == j);
+ testassert(funcArray[i]((void *)j, 0) == j);
}
for(i = 0; i < TEST_QUANTITY; i= i + 3) {
- uint32_t j = i * i;
- uint32_t result = funcArray[i]((id)(uintptr_t) j, 0);
+ uintptr_t j = i * i;
+ uintptr_t result = funcArray[i]((void *)j, 0);
testassert(j == result);
}
return -1 * a;
};
- NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
-
- IMP methodImp = imp_implementationWithBlock(implBlock);
-
- BOOL success = class_addMethod([Foo class], @selector(boo:), methodImp, "i@:i");
- if (!success) {
- fprintf(stdout, "class_addMethod failed\n");
- abort();
- }
- Foo *f = [Foo new];
- int (*impF)(id self, SEL _cmd, int x) = (int(*)(id, SEL, int)) [Foo instanceMethodForSelector: @selector(boo:)];
+ PUSH_POOL {
- int x = impF(f, @selector(boo:), -42);
+ IMP methodImp = imp_implementationWithBlock(implBlock);
- testassert(x == 42);
- testassert([f boo: -42] == 42);
+ BOOL success = class_addMethod([Foo class], @selector(boo:), methodImp, "i@:i");
+ testassert(success);
+ Foo *f = [Foo new];
+ int (*impF)(id self, SEL _cmd, int x) = (int(*)(id, SEL, int)) [Foo instanceMethodForSelector: @selector(boo:)];
+
+ int x = impF(f, @selector(boo:), -42);
+
+ testassert(x == 42);
+ testassert([f boo: -42] == 42);
+
#if STRET_OK
- BigStruct a;
- for(i=0; i<200; i++)
- a.datums[i] = i;
-
- // slightly more straightforward here
- __block unsigned int state = 0;
- BigStruct (^structBlock)(id, BigStruct) = ^BigStruct(id self __attribute__((unused)), BigStruct c) {
- state++;
- return c;
- };
- BigStruct blockDirect = structBlock(nil, a);
- testassert(!memcmp(&a, &blockDirect, sizeof(BigStruct)));
- testassert(state==1);
-
- IMP bigStructIMP = imp_implementationWithBlock(structBlock);
-
- class_addMethod([Foo class], @selector(structThatIsBig:), bigStructIMP, "oh, type strings, how I hate thee. Fortunately, the runtime doesn't generally care.");
-
- BigStruct b;
-
- BigStructFuncPtr bFunc;
-
- b = bigfunc(a);
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
- b = bigfunc(a);
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
-
- bFunc = (BigStructFuncPtr) [Foo instanceMethodForSelector: @selector(methodThatReturnsBigStruct:)];
-
- b = bFunc(f, @selector(methodThatReturnsBigStruct:), a);
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
-
- b = [f methodThatReturnsBigStruct: a];
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
-
- bFunc = (BigStructFuncPtr) [Foo instanceMethodForSelector: @selector(structThatIsBig:)];
-
- b = bFunc(f, @selector(structThatIsBig:), a);
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
- testassert(state==2);
-
- b = [f structThatIsBig: a];
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
- testassert(state==3);
-// STRET_OK
+ BigStruct a;
+ for(i=0; i<200; i++)
+ a.datums[i] = i;
+
+ // slightly more straightforward here
+ __block unsigned int state = 0;
+ BigStruct (^structBlock)(id, BigStruct) = ^BigStruct(id self __attribute__((unused)), BigStruct c) {
+ state++;
+ return c;
+ };
+ BigStruct blockDirect = structBlock(nil, a);
+ testassert(!memcmp(&a, &blockDirect, sizeof(BigStruct)));
+ testassert(state==1);
+
+ IMP bigStructIMP = imp_implementationWithBlock(structBlock);
+
+ class_addMethod([Foo class], @selector(structThatIsBig:), bigStructIMP, "oh, type strings, how I hate thee. Fortunately, the runtime doesn't generally care.");
+
+ BigStruct b;
+
+ BigStructFuncPtr bFunc;
+
+ b = bigfunc(a);
+ testassert(!memcmp(&a, &b, sizeof(BigStruct)));
+ b = bigfunc(a);
+ testassert(!memcmp(&a, &b, sizeof(BigStruct)));
+
+ bFunc = (BigStructFuncPtr) [Foo instanceMethodForSelector: @selector(methodThatReturnsBigStruct:)];
+
+ b = bFunc(f, @selector(methodThatReturnsBigStruct:), a);
+ testassert(!memcmp(&a, &b, sizeof(BigStruct)));
+
+ b = [f methodThatReturnsBigStruct: a];
+ testassert(!memcmp(&a, &b, sizeof(BigStruct)));
+
+ bFunc = (BigStructFuncPtr) [Foo instanceMethodForSelector: @selector(structThatIsBig:)];
+
+ b = bFunc(f, @selector(structThatIsBig:), a);
+ testassert(!memcmp(&a, &b, sizeof(BigStruct)));
+ testassert(state==2);
+
+ b = [f structThatIsBig: a];
+ testassert(!memcmp(&a, &b, sizeof(BigStruct)));
+ testassert(state==3);
+ // STRET_OK
#endif
-
-
- IMP floatIMP = imp_implementationWithBlock(^float (id self __attribute__((unused)), float aFloat ) {
- return aFloat;
- });
- class_addMethod([Foo class], @selector(methodThatReturnsFloat:), floatIMP, "ooh.. type string unspecified again... oh noe... runtime might punish. not.");
-
- float e = (float)0.001;
- float retF = (float)[f methodThatReturnsFloat: 37.1212f];
- testassert( ((retF - e) < 37.1212) && ((retF + e) > 37.1212) );
+
+
+ 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__);
}
#include <objc/objc.h>
+#include "test.h"
-@interface Super { id isa; }
-+class;
+@interface TestRoot(cat)
+(int)classMethod;
-(int)instanceMethod;
@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
#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
#include "cacheflush.h"
-@implementation Super (Category2)
+@implementation TestRoot (Category2)
+(int)classMethod { return 2; }
-(int)instanceMethod { return 2; }
@end
#include "cacheflush.h"
-@implementation Super (Category3)
+@implementation TestRoot (Category3)
+(int)classMethod { return 3; }
-(int)instanceMethod { return 3; }
@end
-// TEST_CONFIG
+// TEST_CFLAGS -Wl,-no_objc_category_merging
#include "test.h"
+#include "testroot.i"
#include <string.h>
-#include <objc/objc-runtime.h>
+#include <objc/runtime.h>
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
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
// TEST_CONFIG
-#include <pthread.h>
#include "test.h"
+
+#include <pthread.h>
#include "objc/objc-internal.h"
+#include "testroot.i"
static unsigned ctors1 = 0;
static unsigned dtors1 = 0;
/*
Class hierarchy:
- Base
+ TestRoot
CXXBase
NoCXXSub
CXXSub
*/
-@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
@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 &&
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;
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;
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;
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);
-// TEST_CFLAGS -framework Foundation
+// TEST_CONFIG
#include "test.h"
#include <objc/objc-runtime.h>
#include <objc/objc-gdb.h>
-#import <Foundation/Foundation.h>
+#import <Foundation/NSObject.h>
@interface Foo:NSObject
@end
// TEST_CONFIG
#include "test.h"
+#include "testroot.i"
#include <string.h>
-#include <objc/objc-runtime.h>
+#include <objc/runtime.h>
-@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__);
// TEST_CFLAGS -Wno-deprecated-declarations
#include "test.h"
+
+#include "testroot.i"
#include <objc/runtime.h>
#include <string.h>
#ifndef OBJC_NO_GC
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"); }
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++;
}
fail("fail_fn '%s' called", sel_getName(_cmd));
}
+
static void cycle(void)
{
Class cls;
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));
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);
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)));
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));
[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);
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"));
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__);
}
+
// TEST_CONFIG
#include "test.h"
-#include <objc/objc-runtime.h>
-
-@interface Super { id isa; } @end
-@implementation Super
-+class { return self; }
-+(void)initialize { }
-@end
+#include "testroot.i"
+#include <objc/runtime.h>
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);
@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!"); }
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;
}
-// TEST_CONFIG
+// TEST_CFLAGS -Wl,-no_objc_category_merging
#include "test.h"
+#include "testroot.i"
#include <malloc/malloc.h>
-#include <objc/objc-runtime.h>
-
-#if __OBJC2__
-// methods added by the runtime: +initialize
-# define MC 1 // class methods
-# define MI 0 // instance methods
-#else
-// no magic
-# define MC 0
-# define MI 0
-#endif
-
-@interface SuperMethods { } @end
+#include <objc/runtime.h>
+
+@interface SuperMethods : TestRoot { } @end
@implementation SuperMethods
+(BOOL)SuperMethodClass { return NO; }
+(BOOL)SuperMethodClass2 { return NO; }
-(BOOL)SuperMethodInstance2 { return NO; }
@end
-@interface SubMethods { } @end
+@interface SubMethods : SuperMethods { } @end
@implementation SubMethods
+(BOOL)SubMethodClass { return NO; }
+(BOOL)SubMethodClass2 { return NO; }
@end
-@interface FourMethods @end
+@interface FourMethods : TestRoot @end
@implementation FourMethods
-(void)one { }
-(void)two { }
-(void)four { }
@end
-@interface NoMethods @end
+@interface NoMethods : TestRoot @end
@implementation NoMethods @end
static void checkReplacement(Method *list, const char *name)
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.
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.
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
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__);
}
+// TEST_CONFIG MEM=mrc,gc
// TEST_CFLAGS -Wno-deprecated-declarations
#import <objc/runtime.h>
--- /dev/null
+@interface InheritingSubCat @end
+
+@interface InheritingSubCat (NonClobberingCategory) @end
+
+@implementation InheritingSubCat (NonClobberingCategory)
+-(id) unrelatedMethod { return self; }
+@end
--- /dev/null
+@interface InheritingSubCat @end
+
+@interface InheritingSubCat (ClobberingCategory) @end
+
+@implementation InheritingSubCat (ClobberingCategory)
+-(int) retainCount { return 1; }
+@end
--- /dev/null
+// 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 <dlfcn.h>
+#include <Foundation/NSObject.h>
+
+#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
--- /dev/null
+// 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
+*/
#define _OBJC_PRIVATE_H_
#include <objc/objc-gdb.h>
+#warning this test needs to be augmented for the side table machienery
+
@interface Super { id isa; } @end
@implementation Super
--- /dev/null
+// TEST_CONFIG
+
+// DO NOT include anything else here
+#include <objc/objc.h>
+// 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__);
+}
+++ /dev/null
-// TEST_CONFIG
-
-// DO NOT include anything else here
-#include <objc/objc.h>
-// 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__);
-}
-// TEST_CFLAGS -Wno-deprecated-declarations
+// TEST_CFLAGS -Wno-deprecated-declarations -Wl,-no_objc_category_merging
#include "test.h"
-#include <objc/objc-runtime.h>
+#include "testroot.i"
+#include <objc/runtime.h>
#ifndef OBJC_NO_GC
#include <objc/objc-auto.h>
#include <auto_zone.h>
static int state;
@protocol Proto
-+class;
++(void)classMethod;
+-(void)instanceMethod;
@end
-@interface Super<Proto> {
- id isa;
+@interface Super : TestRoot <Proto> {
int i;
}
@property int i;
@implementation Super
@synthesize i;
-+(void)initialize { }
-+class { return self; }
-+new { return class_createInstance(self, 0); }
--(void)dealloc { object_dispose(self); }
-
+(void)classMethod {
state = 1;
}
@end
+#if __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
+#endif
+
@implementation Super (Category)
+(void)classMethod {
@end
+#if __clang__
+#pragma clang diagnostic pop
+#endif
+
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));
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);
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__);
}
/*
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
*/
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
/*
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
*/
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
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
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
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
__END_DECLS
asm(
- ".globl L_category \n"
".section __DATA,__objc_data \n"
".align 3 \n"
"L_category: \n"
PTR "L_category \n"
#endif
+ ".text \n"
);
// __OBJC2__
/*
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
/*
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
#if !OMIT_NL_SUB
PTR "_OBJC_CLASS_$_Sub \n"
#endif
+
+ ".text \n"
);
// __OBJC2__
-// 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 <objc/runtime.h>
#include <objc/objc-exception.h>
#include <Foundation/Foundation.h>
-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
@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");
state++; \
}
+ALT_HANDLER(1)
ALT_HANDLER(2)
ALT_HANDLER(3)
ALT_HANDLER(4)
state++;
uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3);
// state++ inside alt handler
- @throw [Super new];
+ @throw [Super exception];
state = BAD;
objc_removeExceptionHandler(token);
}
state++;
uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3);
// state++ inside alt handler
- @throw [Super new];
+ @throw [Super exception];
state = BAD;
objc_removeExceptionHandler(token);
}
#endif
+#if __cplusplus && __OBJC2__
+#include <exception>
+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++;
}
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
}
// TEST_CONFIG
#include "test.h"
-#include <objc/objc-runtime.h>
+#include "testroot.i"
+#include <objc/runtime.h>
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; }
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) {
// 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;
}
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) {
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);
}
+// 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__);
}
#include <objc/runtime.h>
#include <objc/message.h>
-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;
@implementation Super
+(void)initialize { }
-+class { return self; }
++(id)class { return self; }
-(long long) forward:(SEL)sel :(marg_list)args
{
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::::::::::::::::::::::::::::) ||
#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++;
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");
// 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();
#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
*/
#include "test.h"
-#include <objc/objc-runtime.h>
+
+#if __has_feature(objc_arc)
+
+int main()
+{
+ testwarn("rdar://10041403 future class API is not ARC-compatible");
+ succeed(__FILE__);
+}
+
+
+#else
+
+#include <objc/runtime.h>
#include <malloc/malloc.h>
#include <string.h>
#include <dlfcn.h>
int main()
{
- Class oldSuper;
+ Class oldTestRoot;
Class oldSub1;
Class newSub1;
#if !__OBJC2__
#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
succeed(__FILE__);
}
+
+#endif
#include "future.h"
-
-@implementation Super
-+class { return self; }
-+(void)initialize { }
-@end
-
+#include "testroot.i"
// 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
// 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
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
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
// 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
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
// 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
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
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
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__);
#else
+#include "testroot.i"
#include <objc/objc-gdb.h>
#include <objc/runtime.h>
-@interface Super { @public id isa; } @end
-@implementation Super
-+(void)initialize { }
-+class { return self; }
-@end
-
-
int main()
{
// Class hashes
// 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
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);
// TEST_CONFIG
#include "test.h"
+#include "testroot.i"
#include <objc/runtime.h>
#include <objc/message.h>
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; }
-(void)instanceMethod { state = 5; }
@end
+typedef void (*imp_t)(id, SEL);
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");
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");
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");
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");
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");
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));
+// TEST_CONFIG MEM=mrc,gc
// TEST_CFLAGS -Wno-deprecated-declarations
#include "test.h"
@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
@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;
@end
@interface Empty (Unimplemented)
-+normal;
-+retain;
-+release;
-+autorelease;
++(id)ordinary;
++(id)retain;
++(void)release;
++(id)autorelease;
+(void)dealloc;
+(uintptr_t)retainCount;
@end
} 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)
{
}
// 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);
}
// 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);
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));
// Test calls via compiled objc_msgSend
state = 0;
- idVal = [cls normal];
+ idVal = [cls ordinary];
testassert(state == 1);
testassert(idVal == 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));
// 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);
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));
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, "");
testassert(state == 0);
testassert(idVal == cls);
- idVal = [Empty release];
+ (void) [Empty release];
testassert(state == 0);
- testassert(idVal == cls);
idVal = [Empty autorelease];
testassert(state == 0);
testassert(state == 0);
testassert(intVal == (uintptr_t)cls);
- idVal = [Empty normal];
+ idVal = [Empty ordinary];
testassert(state == 1);
testassert(idVal == nil);
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);
}
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!");
}
@end
+#if __clang__
+#pragma clang diagnostic pop
+#endif
+
+
@implementation Super
+(void) initialize { }
+(void) method {
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!");
state = 2;
}
@end
+
+#if __clang__
+#pragma clang diagnostic pop
+#endif
cstate = 3;
}
+
+#if __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
+#endif
+
@implementation Super (cat3)
+(void) method {
state = 3;
state = 3;
}
@end
+
+#if __clang__
+#pragma clang diagnostic pop
+#endif
// * 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");
}
@end
-@interface Sub : Super { } @end
+@interface Sub : Super @end
@implementation Sub
+(void)initialize {
testprintf("in [Sub initialize]\n");
@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");
@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");
// 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");
[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;
// TEST_CONFIG
#include "test.h"
-
+#include "testroot.i"
#include <objc/runtime.h>
-@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
}
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__);
}
// TEST_CONFIG
#include "test.h"
+#include "testroot.i"
#include <objc/objc-runtime.h>
-@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__);
}
// TEST_CONFIG
#include "test.h"
+#include "testroot.i"
#include <stdint.h>
#include <string.h>
#include <objc/objc-runtime.h>
-@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
*/
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);
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));
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));
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;
-@interface Base {
- @public
- id isa;
-}
-
-+class;
-+new;
--(void)dealloc;
-@end
-
-@interface Super : Base {
+@interface Super : TestRoot {
@public
#if OLD
// nothing
@end
-@interface ShrinkingSuper : Base {
+@interface ShrinkingSuper : TestRoot {
@public
#if OLD
id superIvar[5];
@end;
-@interface MoreStrongSuper : Base {
+@interface MoreStrongSuper : TestRoot {
@public
#if OLD
void *superIvar;
@end;
-@interface MoreWeakSuper : Base {
+@interface MoreWeakSuper : TestRoot {
@public
#if OLD
id superIvar;
}
@end;
-@interface MoreWeak2Super : Base {
+@interface MoreWeak2Super : TestRoot {
@public
#if OLD
void *superIvar;
}
@end;
-@interface LessStrongSuper : Base {
+@interface LessStrongSuper : TestRoot {
@public
#if OLD
id superIvar;
}
@end;
-@interface LessWeakSuper : Base {
+@interface LessWeakSuper : TestRoot {
@public
#if OLD
__weak id superIvar;
}
@end;
-@interface LessWeak2Super : Base {
+@interface LessWeak2Super : TestRoot {
@public
#if OLD
__weak id superIvar;
}
@end;
-@interface NoGCChangeSuper : Base {
+@interface NoGCChangeSuper : TestRoot {
@public
intptr_t d;
char superc1;
}
@end
-@interface RunsOf15 : Base {
+@interface RunsOf15 : TestRoot {
@public
id scan1;
intptr_t skip15[15];
#include <objc/objc-runtime.h>
#include <objc/objc-auto.h>
+// 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"
@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
@interface Sub2 : ShrinkingSuper {
@public
- __weak void* subIvar;
- __strong void* subIvar2;
+ gc_weak void* subIvar;
+ gc_strong void* subIvar2;
}
@end
{
#if __OBJC2__
+#if __has_feature(objc_arc)
+ testwarn("fixme check ARC layouts too");
+#endif
+
/*
Bitfield ivars.
rdar://5723893 anonymous bitfield ivars crash when slid
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
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*));
#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
-// TEST_CONFIG GC=1 SDK=macos
+// TEST_CONFIG MEM=gc SDK=macos
#include "test.h"
#include <string.h>
--- /dev/null
+// TEST_CONFIG MEM=arc,mrc CC=clang LANGUAGE=objc,objc++
+// TEST_CFLAGS -framework Foundation
+
+#import <Foundation/Foundation.h>
+#import <Foundation/NSDictionary.h>
+#import <objc/runtime.h>
+#import <objc/objc-abi.h>
+#import <math.h>
+#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;
+}
// 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");
testprintf("in +[Sub(Category) load]\n");
testassert(state >= 2);
catstate++;
+
+ // test autorelease pool
+ __autoreleasing id x;
+ x = AUTORELEASE([Deallocator new]);
}
@end
{
testassert(state == 2);
testassert(catstate == 3);
+ testassert(deallocstate == 1);
[Sub class];
testassert(state == 4);
testassert(catstate == 3);
// TEST_CFLAGS -Wno-deprecated-declarations
#include "test.h"
+#include "testroot.i"
#include <string.h>
#include <objc/objc-runtime.h>
-@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
// TEST_CONFIG
-
// rdar://8052003 rdar://8077031
-#include <Foundation/Foundation.h>
+#include "test.h"
+
#include <malloc/malloc.h>
#include <objc/runtime.h>
-#include "test.h"
// add SELCOUNT methods to each of CLASSCOUNT classes
#define CLASSCOUNT 100
succeed(__FILE__);
}
+
-// TEST_CFLAGS -framework Foundation
+// TEST_CONFIG
#include "test.h"
#include <Foundation/NSObject.h>
// 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 <objc/objc.h>
-#include <objc/objc-runtime.h>
+#include <objc/runtime.h>
+#include <objc/objc-internal.h>
#include <objc/objc-abi.h>
#if defined(__arm__)
# 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); \
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
@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);
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);
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);
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);
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);
}
-+(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);
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);
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);
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);
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);
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 <dlfcn.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <libunwind.h>
+
+#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
// 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);
// 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;
// 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,
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);
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
// 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);
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;
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;
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;
#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__
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));
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
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);
// 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__);
}
// 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"
-// TEST_CFLAGS -framework Foundation
+// TEST_CONFIG MEM=mrc,gc
#include "test.h"
-#import <Foundation/Foundation.h>
+#import <Foundation/NSObject.h>
@interface Sub : NSObject { } @end
@implementation Sub
-+allocWithZone:(NSZone *)zone {
++(id)allocWithZone:(NSZone *)zone {
testprintf("in +[Sub alloc]\n");
return [super allocWithZone:zone];
}
int main()
{
- NSAutoreleasePool *pool = [NSAutoreleasePool new];
- [[Sub new] autorelease];
- [pool release];
+ PUSH_POOL {
+ [[Sub new] autorelease];
+ } POP_POOL;
succeed(__FILE__);
}
--- /dev/null
+// TEST_CONFIG
+
+#include "test.h"
+
+#if __OBJC2__
+
+#include <objc/Protocol.h>
+
+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 <dlfcn.h>
+#include <objc/Protocol.h>
+
+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
// TEST_CONFIG
#include "test.h"
+#include "testroot.i"
#include <stdint.h>
#include <string.h>
#include <objc/objc-runtime.h>
-@interface Super {
+@interface Super : TestRoot {
@public
- id isa;
char superIvar;
}
@implementation Super
@synthesize superProp = superIvar;
-+(void)initialize { }
-+class { return self; }
@end
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);
// TEST_CONFIG
#include "test.h"
-
+#include "testroot.i"
#include <stdint.h>
#include <string.h>
#include <objc/runtime.h>
-@interface Foo { id isa; } @end
-@implementation Foo
-+class { return self; }
-+(void)initialize { }
-@end
-
struct objc_property {
const char *name;
const char *attr;
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), ""));
{ "?!?!", "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,\"''''\""));
{ "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"));
{ "?!?!", "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,"
-// 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 <string.h>
-#include <objc/objc-runtime.h>
+#include <objc/runtime.h>
+#include <objc/objc-internal.h>
#if !__OBJC2__
#include <objc/Protocol.h>
#endif
@protocol Proto1
-+proto1ClassMethod;
--proto1InstanceMethod;
++(id)proto1ClassMethod;
+-(id)proto1InstanceMethod;
@end
@protocol Proto2
-+proto2ClassMethod;
--proto2InstanceMethod;
++(id)proto2ClassMethod;
+-(id)proto2InstanceMethod;
@end
@protocol Proto3 <Proto2>
-+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<Proto1>)a;
+-(void)m12:(id<Proto1>)a;
+-(int)m13:(id<Proto1>)a;
++(void)m22:(TestRoot<Proto1>*)a;
++(int)m23:(TestRoot<Proto1>*)a;
++(TestRoot*)m21:(TestRoot<Proto1>*)a;
+@optional
+-(id(^)(id))m31:(id<Proto1>(^)(id<Proto1>))a;
+-(void)m32:(id<Proto1>(^)(id<Proto1>))a;
+-(int)m33:(id<Proto1>(^)(id<Proto1>))a;
++(void)m42:(TestRoot<Proto1>*(^)(TestRoot<Proto1>*))a;
++(int)m43:(TestRoot<Proto1>*(^)(TestRoot<Proto1>*))a;
++(TestRoot*(^)(TestRoot*))m41:(TestRoot<Proto1>*(^)(TestRoot<Proto1>*))a;
+@end
+
+@protocol Proto6 <Proto5>
+@optional
++(TestRoot*(^)(TestRoot*))n41:(TestRoot<Proto1>*(^)(TestRoot<Proto1>*))a;
+@end
+
@protocol ProtoEmpty
@end
-@interface Super <Proto1> { id isa; } @end
+@interface Super : TestRoot <Proto1> @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 <Proto4> { int i; } @end
int main()
{
Class cls;
- Protocol * const *list;
+ Protocol * __unsafe_unretained *list;
Protocol *protocol, *empty;
#if !__OBJC2__
struct objc_method_description *desc;
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)];
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);
testassert(count == 1);
testassert(protocol_isEqual(list[0], @protocol(Proto2)));
testassert(!list[1]);
- free((void*)list);
+ free(list);
count = 100;
cls = objc_getClass("Super");
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");
testassert(cls);
list = class_copyProtocolList(cls, NULL);
testassert(list);
- free((void*)list);
+ free(list);
count = 100;
list = class_copyProtocolList(NULL, &count);
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);
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@\"<Proto1>\"16";
+ const char *types12 = "v24@0:8@\"<Proto1>\"16";
+ const char *types13 = "i24@0:8@\"<Proto1>\"16";
+ const char *types21 = "@\"TestRoot\"24@0:8@\"TestRoot<Proto1>\"16";
+ const char *types22 = "v24@0:8@\"TestRoot<Proto1>\"16";
+ const char *types23 = "i24@0:8@\"TestRoot<Proto1>\"16";
+ const char *types31 = "@?<@@?@>24@0:8@?<@\"<Proto1>\"@?@\"<Proto1>\">16";
+ const char *types32 = "v24@0:8@?<@\"<Proto1>\"@?@\"<Proto1>\">16";
+ const char *types33 = "i24@0:8@?<@\"<Proto1>\"@?@\"<Proto1>\">16";
+ const char *types41 = "@?<@\"TestRoot\"@?@\"TestRoot\">24@0:8@?<@\"TestRoot<Proto1>\"@?@\"TestRoot<Proto1>\">16";
+ const char *types42 = "v24@0:8@?<@\"TestRoot<Proto1>\"@?@\"TestRoot<Proto1>\">16";
+ const char *types43 = "i24@0:8@?<@\"TestRoot<Proto1>\"@?@\"TestRoot<Proto1>\">16";
+#else
+ const char *types11 = "@12@0:4@\"<Proto1>\"8";
+ const char *types12 = "v12@0:4@\"<Proto1>\"8";
+ const char *types13 = "i12@0:4@\"<Proto1>\"8";
+ const char *types21 = "@\"TestRoot\"12@0:4@\"TestRoot<Proto1>\"8";
+ const char *types22 = "v12@0:4@\"TestRoot<Proto1>\"8";
+ const char *types23 = "i12@0:4@\"TestRoot<Proto1>\"8";
+ const char *types31 = "@?<@@?@>12@0:4@?<@\"<Proto1>\"@?@\"<Proto1>\">8";
+ const char *types32 = "v12@0:4@?<@\"<Proto1>\"@?@\"<Proto1>\">8";
+ const char *types33 = "i12@0:4@?<@\"<Proto1>\"@?@\"<Proto1>\">8";
+ const char *types41 = "@?<@\"TestRoot\"@?@\"TestRoot\">12@0:4@?<@\"TestRoot<Proto1>\"@?@\"TestRoot<Proto1>\">8";
+ const char *types42 = "v12@0:4@?<@\"TestRoot<Proto1>\"@?@\"TestRoot<Proto1>\">8";
+ const char *types43 = "i12@0:4@?<@\"TestRoot<Proto1>\"@?@\"TestRoot<Proto1>\">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__);
}
-// 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 <malloc/malloc.h>
-// 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 <string.h>
#include <malloc/malloc.h>
-#include <objc/objc-runtime.h>
+#include <objc/runtime.h>
@protocol SuperProps
@property int prop1;
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 <objc/objc.h>
#include <objc/objc-runtime.h>
#include <unistd.h>
+#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);
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;
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];
}
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);
@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
{
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]);
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;
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;
testassert(state == 36);
testassert(ret == nil);
- _objc_flush_caches([Sub class]->isa);
+ _objc_flush_caches(object_getClass([Sub class]));
// Resolve an instance method
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];
ret = [s instanceMethod];
testassert(state == 51);
testassert(ret == [Sub class]);
- [s dealloc];
+ RELEASE_VAR(s);
succeed(__FILE__);
return 0;
}
+
+#endif
+
--- /dev/null
+// 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 <objc/objc-internal.h>
+#include <objc/objc-abi.h>
+#include <Foundation/Foundation.h>
+
+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
// 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
// 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 <objc/objc-internal.h>
#include <Foundation/Foundation.h>
-(void) dealloc
{
state++;
- AUTORELEASE([[Deallocator alloc] init]);
+ RR_AUTORELEASE([[Deallocator alloc] init]);
[super dealloc];
}
@end
{
// 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
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
}
@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.
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)
// 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);
}
// 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);
}
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);
}
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
#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
/*
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);
}
*/
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);
}
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.
// TEST_CFLAGS -framework Foundation
-// TEST_CONFIG GC=0
+// TEST_CONFIG MEM=mrc
#define FOUNDATION 1
#define NAME "rr-nsautorelease"
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 <string.h>
#include <dlfcn.h>
#include <mach-o/ldsyms.h>
#include <objc/objc-runtime.h>
-@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()
{
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);
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);
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);
succeed(__FILE__);
}
+
+#endif
// sel_getName recognizes the zero SEL
testassert(0 == strcmp("<null selector>", 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("<ignored selector>",
sel_getName(@selector(retain))));
} else
-#endif
+# endif
{
testassert(0 == strcmp("retain",
sel_getName(@selector(retain))));
u.sel = @selector(retain);
testassert(@selector(retain) == sel_registerName(u.ptr));
+#endif
+
succeed(__FILE__);
}
// TEST_CFLAGS -Wno-deprecated-declarations
#include "test.h"
+#include "testroot.i"
#include <objc/runtime.h>
-@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;
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]);
--- /dev/null
+// 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 <Foundation/Foundation.h>
+#import <Foundation/NSDictionary.h>
+#import <objc/runtime.h>
+#import <objc/objc-abi.h>
+#include "test.h"
+
+@interface TestIndexed : NSObject <NSFastEnumeration> {
+ 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 <NSFastEnumeration> {
+ 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
// TEST_CONFIG
#include "test.h"
+#include "testroot.i"
#include <objc/objc-runtime.h>
-@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__);
-// TEST_CFLAGS -framework Foundation
+// TEST_CONFIG
#include "test.h"
#include <pthread.h>
#include <objc/runtime.h>
#include <objc/objc-sync.h>
-#include <Foundation/Foundation.h>
+#include <Foundation/NSObject.h>
#include <System/pthread_machdep.h>
// synchronized stress test
-// TEST_CFLAGS -framework Foundation
+// TEST_CONFIG
#include "test.h"
#include <pthread.h>
#include <objc/runtime.h>
#include <objc/objc-sync.h>
-#include <Foundation/Foundation.h>
+#include <Foundation/NSObject.h>
// synchronized stress test
// 2-D grid of counters and locks.
-// TEST_CFLAGS -framework Foundation
+// TEST_CONFIG
#include "test.h"
-#include <Foundation/Foundation.h>
+#include <Foundation/NSObject.h>
#include <mach/mach.h>
#include <pthread.h>
#include <sys/time.h>
--- /dev/null
+// TEST_CFLAGS -framework Foundation
+
+#include "test.h"
+#include <objc/runtime.h>
+#import <Foundation/Foundation.h>
+
+#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
-// TEST_CFLAGS -framework Foundation
+// TEST_CONFIG
#include "test.h"
#include <objc/runtime.h>
#include <objc/objc-internal.h>
-#import <Foundation/Foundation.h>
+#import <Foundation/NSObject.h>
+
+#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;
}
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
@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);
[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__);
}
#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
#include <string.h>
#include <libgen.h>
#include <unistd.h>
+#include <pthread.h>
#include <sys/param.h>
#include <malloc/malloc.h>
#include <mach/mach.h>
#include <objc/objc.h>
#include <objc/runtime.h>
#include <objc/message.h>
+#include <objc/objc-abi.h>
#include <objc/objc-auto.h>
+#include <objc/objc-internal.h>
#include <TargetConditionals.h>
static inline void succeed(const char *name) __attribute__((noreturn));
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");
}
}
#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__))
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);
}
}
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);
}
}
#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.
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 */
+/* <rdar://problem/9412038> @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
options:
ARCH=<arch>
- GC=0|1
SDK=<sdk name>
ROOT=/path/to/project.roots/
-
+
CC=<compiler name>
+ MEM=mrc,arc,gc
+ STDLIB=libc++,libstdc++
GUARDMALLOC=0|1
BUILD=0|1
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
# 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
#include <signal.h>
#include <string.h>
#include <unistd.h>
-#include <mach-o/dyld-interposing.h>
+
+// 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)
{
}
# 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/;
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 {
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;
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;
}
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;
}
}
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";
$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
}
+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++";
# 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") {
} 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
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";
}
# 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++";
$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;
}
@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 {
$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);
--- /dev/null
+// testroot.i
+// Implementation of class TestRoot
+// Include this file into your main test file to use it.
+
+#include "test.h"
+#include <dlfcn.h>
+#include <objc/objc-internal.h>
+
+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
-@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
#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;
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();
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);
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);
succeed(__FILE__);
}
+
+#endif
#include "unload.h"
-#include <objc/runtime.h>
+#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
-// TEST_CFLAGS -framework Foundation
+// TEST_CONFIG
#include "test.h"
#include <objc/objc-exception.h>
-#include <Foundation/Foundation.h>
+#include <Foundation/NSObject.h>
#if !defined(__OBJC2__)
static int state;
@interface Foo : NSObject @end
+@interface Bar : NSObject @end
@interface Foo (Unimplemented)
+(void)method;
@end
+@implementation Bar @end
@implementation Foo
// 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
($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);
+(int) method;
@end
+@interface MissingRoot (RR)
+-(id) retain;
+-(void) release;
+@end
+
WEAK_IMPORT
@interface MissingSuper : MissingRoot {
@public
+(int) method;
@end
+@interface NotMissingRoot (RR)
+-(id) retain;
+-(void) release;
+@end
+
@interface NotMissingSuper : NotMissingRoot {
@public
int unused[100];
# 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;
}
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]);
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"));
}
// 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"));
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;
#include "test.h"
#include "weak.h"
+#include <objc/objc-internal.h>
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
@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
-// TEST_CONFIG
+// TEST_CONFIG
#include "test.h"
+#include "testroot.i"
#include <stdint.h>
#include <string.h>
#include <objc/objc-runtime.h>
-@interface Base {
- @public
- id isa;
-}
-@end
-@implementation Base
-+(void)initialize { }
-+class { return self; }
-@end
-
-@interface Weak : Base {
+@interface Weak : TestRoot {
@public
__weak id value;
}
@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;
}
-// TEST_CFLAGS -framework Foundation
+// TEST_CFLAGS
-#include <Foundation/Foundation.h>
+#include <Foundation/NSObject.h>
#include <objc/runtime.h>
#include <objc/objc-internal.h>
testassert(_object_readExternalReference(xref) == object);
_object_removeExternalReference(xref);
- [object release];
+ RELEASE_VAR(object);
succeed(__FILE__);
}
__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_