mirror of
https://github.com/darlinghq/darling-objc4.git
synced 2024-11-23 04:09:46 +00:00
objc4-781.2
This commit is contained in:
parent
1db0a624c9
commit
4d6225bbe3
1
interposable.txt
Normal file
1
interposable.txt
Normal file
@ -0,0 +1 @@
|
||||
_objc_release
|
@ -1,7 +1,7 @@
|
||||
__objc_init
|
||||
_environ_init
|
||||
_tls_init
|
||||
_lock_init
|
||||
_runtime_init
|
||||
_recursive_mutex_init
|
||||
_exception_init
|
||||
_map_images
|
||||
@ -17,12 +17,10 @@ _rtp_init
|
||||
_gc_fixup_barrier_stubs
|
||||
__objc_update_stubs_in_mach_header
|
||||
_sel_init
|
||||
_sel_lock
|
||||
___sel_registerName
|
||||
__objc_search_builtins
|
||||
__ZNK8objc_opt13objc_selopt_t3getEPKc
|
||||
__ZNK8objc_opt13objc_selopt_t4hashEPKc
|
||||
_sel_unlock
|
||||
_sel_registerName
|
||||
_arr_init
|
||||
__ZN4objc8DenseMapIP11objc_objectmLb1ENS_12DenseMapInfoIS2_EENS3_ImEEE4initEj
|
||||
@ -98,6 +96,7 @@ __ZNSt3__122__merge_move_constructIRN8method_t16SortBySELAddressEN13method_list_
|
||||
__ZNSt3__119__merge_move_assignIRN8method_t16SortBySELAddressEPS1_S4_N13method_list_t15method_iteratorEEEvT0_S7_T1_S8_T2_T_
|
||||
_NXPtrIsEqual
|
||||
__getObjc2CategoryList
|
||||
__getObjc2CategoryList2
|
||||
__Z29addUnattachedCategoryForClassP10category_tP7class_tP12_header_info
|
||||
__Z16remethodizeClassP7class_t
|
||||
__Z11flushCachesP7class_t
|
||||
@ -145,7 +144,6 @@ __objc_insert_tagged_isa
|
||||
_objc_msgSend_fixup
|
||||
__objc_fixupMessageRef
|
||||
_objc_msgSend
|
||||
__class_lookupMethodAndLoadCache3
|
||||
_lookUpMethod
|
||||
_prepareForMethodLookup
|
||||
__class_initialize
|
||||
@ -332,7 +330,6 @@ _objc_atomicCompareAndSwapGlobalBarrier
|
||||
_sel_getUid
|
||||
__ZN12_GLOBAL__N_119AutoreleasePoolPage11tls_deallocEPv
|
||||
__ZN12_GLOBAL__N_119AutoreleasePoolPage4killEv
|
||||
__objc_constructOrFree
|
||||
_object_cxxConstruct
|
||||
_object_cxxConstructFromClass
|
||||
__class_hasCxxStructors
|
||||
|
22
markgc.cpp
22
markgc.cpp
@ -31,6 +31,7 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/errno.h>
|
||||
#include <os/overflow.h>
|
||||
#include <mach-o/fat.h>
|
||||
#include <mach-o/arch.h>
|
||||
#include <mach-o/loader.h>
|
||||
@ -476,7 +477,15 @@ bool parse_fat(uint8_t *buffer, size_t size)
|
||||
fat_magic = OSSwapBigToHostInt32(fh->magic);
|
||||
fat_nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
|
||||
|
||||
if (size < (sizeof(struct fat_header) + fat_nfat_arch * sizeof(struct fat_arch))) {
|
||||
size_t fat_arch_size;
|
||||
// fat_nfat_arch * sizeof(struct fat_arch) + sizeof(struct fat_header)
|
||||
if (os_mul_and_add_overflow(fat_nfat_arch, sizeof(struct fat_arch),
|
||||
sizeof(struct fat_header), &fat_arch_size))
|
||||
{
|
||||
printf("too many fat archs\n");
|
||||
return false;
|
||||
}
|
||||
if (size < fat_arch_size) {
|
||||
printf("file is too small\n");
|
||||
return false;
|
||||
}
|
||||
@ -484,7 +493,14 @@ bool parse_fat(uint8_t *buffer, size_t size)
|
||||
archs = (struct fat_arch *)(buffer + sizeof(struct fat_header));
|
||||
|
||||
/* Special case hidden CPU_TYPE_ARM64 */
|
||||
if (size >= (sizeof(struct fat_header) + (fat_nfat_arch + 1) * sizeof(struct fat_arch))) {
|
||||
size_t fat_arch_plus_one_size;
|
||||
if (os_add_overflow(fat_arch_size, sizeof(struct fat_arch),
|
||||
&fat_arch_plus_one_size))
|
||||
{
|
||||
printf("too many fat archs\n");
|
||||
return false;
|
||||
}
|
||||
if (size >= fat_arch_plus_one_size) {
|
||||
if (fat_nfat_arch > 0
|
||||
&& OSSwapBigToHostInt32(archs[fat_nfat_arch].cputype) == CPU_TYPE_ARM64) {
|
||||
fat_nfat_arch++;
|
||||
@ -505,7 +521,7 @@ bool parse_fat(uint8_t *buffer, size_t size)
|
||||
arch_cputype, arch_cpusubtype);
|
||||
|
||||
/* Check that slice data is after all fat headers and archs */
|
||||
if (arch_offset < (sizeof(struct fat_header) + fat_nfat_arch * sizeof(struct fat_arch))) {
|
||||
if (arch_offset < fat_arch_size) {
|
||||
printf("file is badly formed\n");
|
||||
return false;
|
||||
}
|
||||
|
@ -7,6 +7,17 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXAggregateTarget section */
|
||||
834F9B01212E560100F95A54 /* objc4_tests */ = {
|
||||
isa = PBXAggregateTarget;
|
||||
buildConfigurationList = 834F9B04212E560200F95A54 /* Build configuration list for PBXAggregateTarget "objc4_tests" */;
|
||||
buildPhases = (
|
||||
834F9B05212E561400F95A54 /* Run Script (build tests) */,
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = objc4_tests;
|
||||
productName = objc4_tests;
|
||||
};
|
||||
837F67A81A771F63004D34FA /* objc-simulator */ = {
|
||||
isa = PBXAggregateTarget;
|
||||
buildConfigurationList = 837F67A91A771F63004D34FA /* Build configuration list for PBXAggregateTarget "objc-simulator" */;
|
||||
@ -25,6 +36,21 @@
|
||||
393CEAC60DC69E67000B69DE /* objc-references.h in Headers */ = {isa = PBXBuildFile; fileRef = 393CEAC50DC69E67000B69DE /* objc-references.h */; };
|
||||
39ABD72312F0B61800D1054C /* objc-weak.h in Headers */ = {isa = PBXBuildFile; fileRef = 39ABD71F12F0B61800D1054C /* objc-weak.h */; };
|
||||
39ABD72412F0B61800D1054C /* objc-weak.mm in Sources */ = {isa = PBXBuildFile; fileRef = 39ABD72012F0B61800D1054C /* objc-weak.mm */; };
|
||||
6E1475EA21DFDB1B001357EA /* llvm-AlignOf.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E1475E521DFDB1A001357EA /* llvm-AlignOf.h */; };
|
||||
6E1475EB21DFDB1B001357EA /* llvm-DenseMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E1475E621DFDB1B001357EA /* llvm-DenseMap.h */; };
|
||||
6E1475EC21DFDB1B001357EA /* llvm-DenseMapInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E1475E721DFDB1B001357EA /* llvm-DenseMapInfo.h */; };
|
||||
6E1475ED21DFDB1B001357EA /* llvm-type_traits.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E1475E821DFDB1B001357EA /* llvm-type_traits.h */; };
|
||||
6E1475EE21DFDB1B001357EA /* llvm-MathExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E1475E921DFDB1B001357EA /* llvm-MathExtras.h */; };
|
||||
6E7B0862232DE7CA00689009 /* PointerUnion.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E7B0861232DE7CA00689009 /* PointerUnion.h */; };
|
||||
6EACB842232C97A400CE9176 /* objc-zalloc.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EACB841232C97A400CE9176 /* objc-zalloc.h */; };
|
||||
6EACB844232C97B900CE9176 /* objc-zalloc.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6EACB843232C97B900CE9176 /* objc-zalloc.mm */; };
|
||||
6ECD0B1F2244999E00910D88 /* llvm-DenseSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 6ECD0B1E2244999E00910D88 /* llvm-DenseSet.h */; };
|
||||
7213C36321FA7C730090A271 /* NSObject-internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 7213C36221FA7C730090A271 /* NSObject-internal.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
7593EC58202248E50046AB96 /* objc-object.h in Headers */ = {isa = PBXBuildFile; fileRef = 7593EC57202248DF0046AB96 /* objc-object.h */; };
|
||||
75A9504F202BAA0600D7D56F /* objc-locks-new.h in Headers */ = {isa = PBXBuildFile; fileRef = 75A9504E202BAA0300D7D56F /* objc-locks-new.h */; };
|
||||
75A95051202BAA9A00D7D56F /* objc-locks.h in Headers */ = {isa = PBXBuildFile; fileRef = 75A95050202BAA9A00D7D56F /* objc-locks.h */; };
|
||||
75A95053202BAC4100D7D56F /* objc-lockdebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 75A95052202BAC4100D7D56F /* objc-lockdebug.h */; };
|
||||
8306440920D24A5D00E356D2 /* objc-block-trampolines.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306440620D24A3E00E356D2 /* objc-block-trampolines.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
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 */; };
|
||||
@ -37,9 +63,6 @@
|
||||
834DF8B715993EE1002F2BC9 /* objc-sel-old.mm in Sources */ = {isa = PBXBuildFile; fileRef = 834DF8B615993EE1002F2BC9 /* objc-sel-old.mm */; };
|
||||
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 */; };
|
||||
8379996E13CBAF6F007C2B5F /* a1a2-blocktramps-arm64.s in Sources */ = {isa = PBXBuildFile; fileRef = 8379996D13CBAF6F007C2B5F /* a1a2-blocktramps-arm64.s */; };
|
||||
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 */; };
|
||||
838485BF0D6D687300CEA253 /* hashtable2.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485B70D6D687300CEA253 /* hashtable2.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
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, ); }; };
|
||||
@ -63,7 +86,7 @@
|
||||
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.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485DA0D6D68A200CEA253 /* objc-loadmethod.mm */; };
|
||||
838486020D6D68A200CEA253 /* objc-lockdebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485DB0D6D68A200CEA253 /* objc-lockdebug.mm */; };
|
||||
838486020D6D68A200CEA253 /* objc-lockdebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = 838485DB0D6D68A200CEA253 /* objc-lockdebug.mm */; settings = {COMPILER_FLAGS = "-Os"; }; };
|
||||
838486030D6D68A200CEA253 /* objc-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485DC0D6D68A200CEA253 /* objc-private.h */; };
|
||||
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 */; };
|
||||
@ -85,6 +108,8 @@
|
||||
838486250D6D68F000CEA253 /* List.m in Sources */ = {isa = PBXBuildFile; fileRef = 838486230D6D68F000CEA253 /* List.m */; };
|
||||
838486260D6D68F000CEA253 /* List.h in Headers */ = {isa = PBXBuildFile; fileRef = 838486240D6D68F000CEA253 /* List.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
838486280D6D6A2400CEA253 /* message.h in Headers */ = {isa = PBXBuildFile; fileRef = 838485BD0D6D687300CEA253 /* message.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
83A4AEDC1EA0840800ACADDE /* module.modulemap in Headers */ = {isa = PBXBuildFile; fileRef = 83A4AED71EA06D9D00ACADDE /* module.modulemap */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
83A4AEDE1EA08C7200ACADDE /* ObjectiveC.apinotes in Headers */ = {isa = PBXBuildFile; fileRef = 83A4AEDD1EA08C5700ACADDE /* ObjectiveC.apinotes */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
83B1A8BE0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 83B1A8BC0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s */; };
|
||||
83BE02E40FCCB23400661494 /* objc-file-old.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83BE02E30FCCB23400661494 /* objc-file-old.mm */; };
|
||||
83BE02E80FCCB24D00661494 /* objc-file-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E50FCCB24D00661494 /* objc-file-old.h */; };
|
||||
@ -92,17 +117,20 @@
|
||||
83BE02EA0FCCB24D00661494 /* objc-runtime-old.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BE02E70FCCB24D00661494 /* objc-runtime-old.h */; };
|
||||
83C9C3391668B50E00F4E544 /* objc-msg-simulator-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = 83C9C3381668B50E00F4E544 /* objc-msg-simulator-x86_64.s */; };
|
||||
83D49E4F13C7C84F0057F1DD /* objc-msg-arm64.s in Sources */ = {isa = PBXBuildFile; fileRef = 83D49E4E13C7C84F0057F1DD /* objc-msg-arm64.s */; };
|
||||
83D92696212254CF00299F69 /* isa.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D92695212254CF00299F69 /* isa.h */; };
|
||||
83D9269821225A7400299F69 /* arm64-asm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D9269721225A7400299F69 /* arm64-asm.h */; };
|
||||
83EB007B121C9EC200B92C16 /* objc-sel-table.s in Sources */ = {isa = PBXBuildFile; fileRef = 83EB007A121C9EC200B92C16 /* objc-sel-table.s */; };
|
||||
83EF5E9820D2298400F486A4 /* objc-blocktramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9C116AB2820071B552 /* objc-blocktramps-i386.s */; };
|
||||
83EF5E9920D2298400F486A4 /* objc-blocktramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = E8923D9D116AB2820071B552 /* objc-blocktramps-x86_64.s */; };
|
||||
83EF5E9C20D2299E00F486A4 /* objc-blocktramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 8383A3A1122600E9009290B8 /* objc-blocktramps-arm.s */; };
|
||||
83F4B52815E843B100E0926F /* NSObjCRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F4B52615E843B100E0926F /* NSObjCRuntime.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
83F4B52915E843B100E0926F /* NSObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F4B52715E843B100E0926F /* NSObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
83F550E0155E030800E95D3B /* objc-cache-old.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83F550DF155E030800E95D3B /* objc-cache-old.mm */; };
|
||||
87BB4EA70EC39854005D08E1 /* objc-probes.d in Sources */ = {isa = PBXBuildFile; fileRef = 87BB4E900EC39633005D08E1 /* objc-probes.d */; };
|
||||
9672F7EE14D5F488007CEC96 /* NSObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9672F7ED14D5F488007CEC96 /* NSObject.mm */; };
|
||||
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 */; };
|
||||
C2E6D3FC2225DCF00059DFAA /* DenseMapExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = C2E6D3FB2225DCF00059DFAA /* DenseMapExtras.h */; };
|
||||
E8923DA5116AB2820071B552 /* objc-block-trampolines.mm in Sources */ = {isa = PBXBuildFile; fileRef = E8923DA0116AB2820071B552 /* objc-block-trampolines.mm */; };
|
||||
F9BCC71B205C68E800DD9AFC /* objc-blocktramps-arm64.s in Sources */ = {isa = PBXBuildFile; fileRef = 8379996D13CBAF6F007C2B5F /* objc-blocktramps-arm64.s */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -113,6 +141,13 @@
|
||||
remoteGlobalIDString = D2AAC0620554660B00DB518D;
|
||||
remoteInfo = objc;
|
||||
};
|
||||
F9BCC728205C6A0900DD9AFC /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = F9BCC6CA205C68E800DD9AFC;
|
||||
remoteInfo = "objc-trampolines";
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@ -120,6 +155,21 @@
|
||||
393CEAC50DC69E67000B69DE /* objc-references.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-references.h"; path = "runtime/objc-references.h"; 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>"; };
|
||||
6E1475E521DFDB1A001357EA /* llvm-AlignOf.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = "llvm-AlignOf.h"; path = "runtime/llvm-AlignOf.h"; sourceTree = "<group>"; tabWidth = 2; };
|
||||
6E1475E621DFDB1B001357EA /* llvm-DenseMap.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = "llvm-DenseMap.h"; path = "runtime/llvm-DenseMap.h"; sourceTree = "<group>"; tabWidth = 2; };
|
||||
6E1475E721DFDB1B001357EA /* llvm-DenseMapInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = "llvm-DenseMapInfo.h"; path = "runtime/llvm-DenseMapInfo.h"; sourceTree = "<group>"; tabWidth = 2; };
|
||||
6E1475E821DFDB1B001357EA /* llvm-type_traits.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = "llvm-type_traits.h"; path = "runtime/llvm-type_traits.h"; sourceTree = "<group>"; tabWidth = 2; };
|
||||
6E1475E921DFDB1B001357EA /* llvm-MathExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = "llvm-MathExtras.h"; path = "runtime/llvm-MathExtras.h"; sourceTree = "<group>"; tabWidth = 2; };
|
||||
6E7B0861232DE7CA00689009 /* PointerUnion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PointerUnion.h; path = runtime/PointerUnion.h; sourceTree = "<group>"; };
|
||||
6EACB841232C97A400CE9176 /* objc-zalloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-zalloc.h"; path = "runtime/objc-zalloc.h"; sourceTree = "<group>"; };
|
||||
6EACB843232C97B900CE9176 /* objc-zalloc.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-zalloc.mm"; path = "runtime/objc-zalloc.mm"; sourceTree = "<group>"; };
|
||||
6ECD0B1E2244999E00910D88 /* llvm-DenseSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "llvm-DenseSet.h"; path = "runtime/llvm-DenseSet.h"; sourceTree = "<group>"; };
|
||||
7213C36221FA7C730090A271 /* NSObject-internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject-internal.h"; path = "runtime/NSObject-internal.h"; sourceTree = "<group>"; };
|
||||
7593EC57202248DF0046AB96 /* objc-object.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "objc-object.h"; path = "runtime/objc-object.h"; sourceTree = "<group>"; };
|
||||
75A9504E202BAA0300D7D56F /* objc-locks-new.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "objc-locks-new.h"; path = "runtime/objc-locks-new.h"; sourceTree = "<group>"; };
|
||||
75A95050202BAA9A00D7D56F /* objc-locks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-locks.h"; path = "runtime/objc-locks.h"; sourceTree = "<group>"; };
|
||||
75A95052202BAC4100D7D56F /* objc-lockdebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-lockdebug.h"; path = "runtime/objc-lockdebug.h"; sourceTree = "<group>"; };
|
||||
8306440620D24A3E00E356D2 /* objc-block-trampolines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "objc-block-trampolines.h"; path = "runtime/objc-block-trampolines.h"; 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; };
|
||||
@ -133,9 +183,8 @@
|
||||
834DF8B615993EE1002F2BC9 /* objc-sel-old.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-sel-old.mm"; path = "runtime/objc-sel-old.mm"; 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>"; };
|
||||
8379996D13CBAF6F007C2B5F /* a1a2-blocktramps-arm64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a1a2-blocktramps-arm64.s"; path = "runtime/a1a2-blocktramps-arm64.s"; 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>"; };
|
||||
8379996D13CBAF6F007C2B5F /* objc-blocktramps-arm64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-blocktramps-arm64.s"; path = "runtime/objc-blocktramps-arm64.s"; sourceTree = "<group>"; };
|
||||
8383A3A1122600E9009290B8 /* objc-blocktramps-arm.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-blocktramps-arm.s"; path = "runtime/objc-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>"; };
|
||||
@ -184,13 +233,18 @@
|
||||
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>"; };
|
||||
83A4AED71EA06D9D00ACADDE /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; name = module.modulemap; path = runtime/Module/module.modulemap; sourceTree = "<group>"; };
|
||||
83A4AEDD1EA08C5700ACADDE /* ObjectiveC.apinotes */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = ObjectiveC.apinotes; path = runtime/Module/ObjectiveC.apinotes; sourceTree = "<group>"; };
|
||||
83B1A8BC0FF1AC0D0019EA5B /* objc-msg-simulator-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-simulator-i386.s"; path = "runtime/Messengers.subproj/objc-msg-simulator-i386.s"; sourceTree = "<group>"; };
|
||||
83BE02E30FCCB23400661494 /* objc-file-old.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-file-old.mm"; path = "runtime/objc-file-old.mm"; 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>"; };
|
||||
83C9C3381668B50E00F4E544 /* objc-msg-simulator-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-simulator-x86_64.s"; path = "runtime/Messengers.subproj/objc-msg-simulator-x86_64.s"; sourceTree = "<group>"; };
|
||||
83CE671D1E6E76B60095A33E /* interposable.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = interposable.txt; sourceTree = "<group>"; };
|
||||
83D49E4E13C7C84F0057F1DD /* objc-msg-arm64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-msg-arm64.s"; path = "runtime/Messengers.subproj/objc-msg-arm64.s"; sourceTree = "<group>"; };
|
||||
83D92695212254CF00299F69 /* isa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = isa.h; path = runtime/isa.h; sourceTree = "<group>"; };
|
||||
83D9269721225A7400299F69 /* arm64-asm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "arm64-asm.h"; path = "runtime/arm64-asm.h"; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
83F4B52615E843B100E0926F /* NSObjCRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSObjCRuntime.h; path = runtime/NSObjCRuntime.h; sourceTree = "<group>"; };
|
||||
83F4B52715E843B100E0926F /* NSObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSObject.h; path = runtime/NSObject.h; sourceTree = "<group>"; };
|
||||
@ -198,12 +252,12 @@
|
||||
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>"; };
|
||||
BC8B5D1212D3D48100C78A5B /* libauto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libauto.dylib; path = /usr/lib/libauto.dylib; sourceTree = "<absolute>"; };
|
||||
C2E6D3FB2225DCF00059DFAA /* DenseMapExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DenseMapExtras.h; path = runtime/DenseMapExtras.h; sourceTree = "<group>"; };
|
||||
D2AAC0630554660B00DB518D /* libobjc.A.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libobjc.A.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E8923D9C116AB2820071B552 /* a1a2-blocktramps-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a1a2-blocktramps-i386.s"; path = "runtime/a1a2-blocktramps-i386.s"; sourceTree = "<group>"; };
|
||||
E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a1a2-blocktramps-x86_64.s"; path = "runtime/a1a2-blocktramps-x86_64.s"; sourceTree = "<group>"; };
|
||||
E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a2a3-blocktramps-i386.s"; path = "runtime/a2a3-blocktramps-i386.s"; sourceTree = "<group>"; };
|
||||
E8923D9F116AB2820071B552 /* a2a3-blocktramps-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a2a3-blocktramps-x86_64.s"; path = "runtime/a2a3-blocktramps-x86_64.s"; sourceTree = "<group>"; };
|
||||
E8923D9C116AB2820071B552 /* objc-blocktramps-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-blocktramps-i386.s"; path = "runtime/objc-blocktramps-i386.s"; sourceTree = "<group>"; };
|
||||
E8923D9D116AB2820071B552 /* objc-blocktramps-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "objc-blocktramps-x86_64.s"; path = "runtime/objc-blocktramps-x86_64.s"; 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>"; };
|
||||
F9BCC727205C68E800DD9AFC /* libobjc-trampolines.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libobjc-trampolines.dylib"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -214,6 +268,13 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F9BCC721205C68E800DD9AFC /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
@ -229,6 +290,7 @@
|
||||
08FB7795FE84155DC02AAC07 /* Source */,
|
||||
838485B20D6D67F900CEA253 /* Other */,
|
||||
1AB674ADFE9D54B511CA2CBB /* Products */,
|
||||
F9BCC72A205C6A1600DD9AFC /* Frameworks */,
|
||||
);
|
||||
name = objc;
|
||||
sourceTree = "<group>";
|
||||
@ -236,8 +298,6 @@
|
||||
08FB7795FE84155DC02AAC07 /* Source */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8383A3A1122600E9009290B8 /* a1a2-blocktramps-arm.s */,
|
||||
8383A3A2122600E9009290B8 /* a2a3-blocktramps-arm.s */,
|
||||
838485B80D6D687300CEA253 /* hashtable2.mm */,
|
||||
838485BC0D6D687300CEA253 /* maptable.mm */,
|
||||
9672F7ED14D5F488007CEC96 /* NSObject.mm */,
|
||||
@ -245,6 +305,7 @@
|
||||
830F2A930D73876100392440 /* objc-accessors.mm */,
|
||||
838485CA0D6D68A200CEA253 /* objc-auto.mm */,
|
||||
39ABD72012F0B61800D1054C /* objc-weak.mm */,
|
||||
6EACB843232C97B900CE9176 /* objc-zalloc.mm */,
|
||||
E8923DA0116AB2820071B552 /* objc-block-trampolines.mm */,
|
||||
838485CB0D6D68A200CEA253 /* objc-cache.mm */,
|
||||
83F550DF155E030800E95D3B /* objc-cache-old.mm */,
|
||||
@ -271,11 +332,10 @@
|
||||
834DF8B615993EE1002F2BC9 /* objc-sel-old.mm */,
|
||||
838485EA0D6D68A200CEA253 /* objc-sync.mm */,
|
||||
838485EB0D6D68A200CEA253 /* objc-typeencoding.mm */,
|
||||
8379996D13CBAF6F007C2B5F /* a1a2-blocktramps-arm64.s */,
|
||||
E8923D9C116AB2820071B552 /* a1a2-blocktramps-i386.s */,
|
||||
E8923D9D116AB2820071B552 /* a1a2-blocktramps-x86_64.s */,
|
||||
E8923D9E116AB2820071B552 /* a2a3-blocktramps-i386.s */,
|
||||
E8923D9F116AB2820071B552 /* a2a3-blocktramps-x86_64.s */,
|
||||
8383A3A1122600E9009290B8 /* objc-blocktramps-arm.s */,
|
||||
8379996D13CBAF6F007C2B5F /* objc-blocktramps-arm64.s */,
|
||||
E8923D9C116AB2820071B552 /* objc-blocktramps-i386.s */,
|
||||
E8923D9D116AB2820071B552 /* objc-blocktramps-x86_64.s */,
|
||||
830F2A690D737FB800392440 /* objc-msg-arm.s */,
|
||||
83D49E4E13C7C84F0057F1DD /* objc-msg-arm64.s */,
|
||||
830F2A6A0D737FB800392440 /* objc-msg-i386.s */,
|
||||
@ -291,6 +351,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D2AAC0630554660B00DB518D /* libobjc.A.dylib */,
|
||||
F9BCC727205C68E800DD9AFC /* libobjc-trampolines.dylib */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@ -301,6 +362,7 @@
|
||||
830F2AA50D7394C200392440 /* markgc.cpp */,
|
||||
838485B40D6D683300CEA253 /* APPLE_LICENSE */,
|
||||
838485B50D6D683300CEA253 /* ReleaseNotes.rtf */,
|
||||
83CE671D1E6E76B60095A33E /* interposable.txt */,
|
||||
838485B30D6D682B00CEA253 /* libobjc.order */,
|
||||
);
|
||||
name = Other;
|
||||
@ -309,6 +371,8 @@
|
||||
838485C60D6D687700CEA253 /* Public Headers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83A4AEDD1EA08C5700ACADDE /* ObjectiveC.apinotes */,
|
||||
83A4AED71EA06D9D00ACADDE /* module.modulemap */,
|
||||
83F4B52615E843B100E0926F /* NSObjCRuntime.h */,
|
||||
83F4B52715E843B100E0926F /* NSObject.h */,
|
||||
838485BD0D6D687300CEA253 /* message.h */,
|
||||
@ -325,10 +389,12 @@
|
||||
838485C70D6D688200CEA253 /* Private Headers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7213C36221FA7C730090A271 /* NSObject-internal.h */,
|
||||
83112ED30F00599600A5FBAF /* objc-internal.h */,
|
||||
834EC0A311614167009B2563 /* objc-abi.h */,
|
||||
838485BB0D6D687300CEA253 /* maptable.h */,
|
||||
834266D70E665A8B002E4DA2 /* objc-gdb.h */,
|
||||
8306440620D24A3E00E356D2 /* objc-block-trampolines.h */,
|
||||
);
|
||||
name = "Private Headers";
|
||||
sourceTree = "<group>";
|
||||
@ -360,11 +426,25 @@
|
||||
8384862A0D6D6ABC00CEA253 /* Project Headers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6E1475E521DFDB1A001357EA /* llvm-AlignOf.h */,
|
||||
6E1475E621DFDB1B001357EA /* llvm-DenseMap.h */,
|
||||
6E1475E721DFDB1B001357EA /* llvm-DenseMapInfo.h */,
|
||||
6ECD0B1E2244999E00910D88 /* llvm-DenseSet.h */,
|
||||
6E1475E921DFDB1B001357EA /* llvm-MathExtras.h */,
|
||||
6E1475E821DFDB1B001357EA /* llvm-type_traits.h */,
|
||||
C2E6D3FB2225DCF00059DFAA /* DenseMapExtras.h */,
|
||||
6E7B0861232DE7CA00689009 /* PointerUnion.h */,
|
||||
83D9269721225A7400299F69 /* arm64-asm.h */,
|
||||
83D92695212254CF00299F69 /* isa.h */,
|
||||
838485CF0D6D68A200CEA253 /* objc-config.h */,
|
||||
83BE02E60FCCB24D00661494 /* objc-file.h */,
|
||||
83BE02E50FCCB24D00661494 /* objc-file-old.h */,
|
||||
83BE02E60FCCB24D00661494 /* objc-file.h */,
|
||||
838485D40D6D68A200CEA253 /* objc-initialize.h */,
|
||||
838485D90D6D68A200CEA253 /* objc-loadmethod.h */,
|
||||
75A9504E202BAA0300D7D56F /* objc-locks-new.h */,
|
||||
75A95052202BAC4100D7D56F /* objc-lockdebug.h */,
|
||||
75A95050202BAA9A00D7D56F /* objc-locks.h */,
|
||||
7593EC57202248DF0046AB96 /* objc-object.h */,
|
||||
831C85D30E10CF850066E64C /* objc-os.h */,
|
||||
838485DC0D6D68A200CEA253 /* objc-private.h */,
|
||||
393CEAC50DC69E67000B69DE /* objc-references.h */,
|
||||
@ -372,19 +452,43 @@
|
||||
83BE02E70FCCB24D00661494 /* objc-runtime-old.h */,
|
||||
838485E50D6D68A200CEA253 /* objc-sel-set.h */,
|
||||
39ABD71F12F0B61800D1054C /* objc-weak.h */,
|
||||
6EACB841232C97A400CE9176 /* objc-zalloc.h */,
|
||||
);
|
||||
name = "Project Headers";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F9BCC72A205C6A1600DD9AFC /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
8306440820D24A5300E356D2 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8306440920D24A5D00E356D2 /* objc-block-trampolines.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D2AAC0600554660B00DB518D /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
83A4AEDE1EA08C7200ACADDE /* ObjectiveC.apinotes in Headers */,
|
||||
75A95051202BAA9A00D7D56F /* objc-locks.h in Headers */,
|
||||
6E1475ED21DFDB1B001357EA /* llvm-type_traits.h in Headers */,
|
||||
83A4AEDC1EA0840800ACADDE /* module.modulemap in Headers */,
|
||||
830F2A980D738DC200392440 /* hashtable.h in Headers */,
|
||||
6EACB842232C97A400CE9176 /* objc-zalloc.h in Headers */,
|
||||
6E1475EA21DFDB1B001357EA /* llvm-AlignOf.h in Headers */,
|
||||
838485BF0D6D687300CEA253 /* hashtable2.h in Headers */,
|
||||
6E1475EC21DFDB1B001357EA /* llvm-DenseMapInfo.h in Headers */,
|
||||
C2E6D3FC2225DCF00059DFAA /* DenseMapExtras.h in Headers */,
|
||||
838486260D6D68F000CEA253 /* List.h in Headers */,
|
||||
838485C30D6D687300CEA253 /* maptable.h in Headers */,
|
||||
838486280D6D6A2400CEA253 /* message.h in Headers */,
|
||||
@ -393,28 +497,38 @@
|
||||
838485F00D6D68A200CEA253 /* objc-auto.h in Headers */,
|
||||
838485F40D6D68A200CEA253 /* objc-class.h in Headers */,
|
||||
838485F60D6D68A200CEA253 /* objc-config.h in Headers */,
|
||||
6E1475EE21DFDB1B001357EA /* llvm-MathExtras.h in Headers */,
|
||||
838485F80D6D68A200CEA253 /* objc-exception.h in Headers */,
|
||||
83BE02E80FCCB24D00661494 /* objc-file-old.h in Headers */,
|
||||
83BE02E90FCCB24D00661494 /* objc-file.h in Headers */,
|
||||
75A9504F202BAA0600D7D56F /* objc-locks-new.h in Headers */,
|
||||
834266D80E665A8B002E4DA2 /* objc-gdb.h in Headers */,
|
||||
838485FB0D6D68A200CEA253 /* objc-initialize.h in Headers */,
|
||||
7593EC58202248E50046AB96 /* objc-object.h in Headers */,
|
||||
83112ED40F00599600A5FBAF /* objc-internal.h in Headers */,
|
||||
838485FE0D6D68A200CEA253 /* objc-load.h in Headers */,
|
||||
838486000D6D68A200CEA253 /* objc-loadmethod.h in Headers */,
|
||||
75A95053202BAC4100D7D56F /* objc-lockdebug.h in Headers */,
|
||||
831C85D50E10CF850066E64C /* objc-os.h in Headers */,
|
||||
838486030D6D68A200CEA253 /* objc-private.h in Headers */,
|
||||
393CEAC60DC69E67000B69DE /* objc-references.h in Headers */,
|
||||
6E1475EB21DFDB1B001357EA /* llvm-DenseMap.h in Headers */,
|
||||
838486070D6D68A200CEA253 /* objc-runtime-new.h in Headers */,
|
||||
83BE02EA0FCCB24D00661494 /* objc-runtime-old.h in Headers */,
|
||||
8384860A0D6D68A200CEA253 /* objc-runtime.h in Headers */,
|
||||
8384860C0D6D68A200CEA253 /* objc-sel-set.h in Headers */,
|
||||
6E7B0862232DE7CA00689009 /* PointerUnion.h in Headers */,
|
||||
7213C36321FA7C730090A271 /* NSObject-internal.h in Headers */,
|
||||
838486100D6D68A200CEA253 /* objc-sync.h in Headers */,
|
||||
83D92696212254CF00299F69 /* isa.h in Headers */,
|
||||
838486130D6D68A200CEA253 /* objc.h in Headers */,
|
||||
83D9269821225A7400299F69 /* arm64-asm.h in Headers */,
|
||||
838486140D6D68A200CEA253 /* Object.h in Headers */,
|
||||
8384861E0D6D68A800CEA253 /* Protocol.h in Headers */,
|
||||
838486200D6D68A800CEA253 /* runtime.h in Headers */,
|
||||
39ABD72312F0B61800D1054C /* objc-weak.h in Headers */,
|
||||
83F4B52815E843B100E0926F /* NSObjCRuntime.h in Headers */,
|
||||
6ECD0B1F2244999E00910D88 /* llvm-DenseSet.h in Headers */,
|
||||
83F4B52915E843B100E0926F /* NSObject.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -435,12 +549,30 @@
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
F9BCC729205C6A0900DD9AFC /* PBXTargetDependency */,
|
||||
);
|
||||
name = objc;
|
||||
productName = objc;
|
||||
productReference = D2AAC0630554660B00DB518D /* libobjc.A.dylib */;
|
||||
productType = "com.apple.product-type.library.dynamic";
|
||||
};
|
||||
F9BCC6CA205C68E800DD9AFC /* objc-trampolines */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = F9BCC724205C68E800DD9AFC /* Build configuration list for PBXNativeTarget "objc-trampolines" */;
|
||||
buildPhases = (
|
||||
8306440820D24A5300E356D2 /* Headers */,
|
||||
F9BCC6EF205C68E800DD9AFC /* Sources */,
|
||||
F9BCC721205C68E800DD9AFC /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "objc-trampolines";
|
||||
productName = objc;
|
||||
productReference = F9BCC727205C68E800DD9AFC /* libobjc-trampolines.dylib */;
|
||||
productType = "com.apple.product-type.library.dynamic";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
@ -450,6 +582,11 @@
|
||||
BuildIndependentTargetsInParallel = NO;
|
||||
LastUpgradeCheck = 0440;
|
||||
TargetAttributes = {
|
||||
834F9B01212E560100F95A54 = {
|
||||
CreatedOnToolsVersion = 10.0;
|
||||
DevelopmentTeam = 59GAB85EFG;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
837F67A81A771F63004D34FA = {
|
||||
CreatedOnToolsVersion = 6.3;
|
||||
};
|
||||
@ -471,6 +608,8 @@
|
||||
targets = (
|
||||
D2AAC0620554660B00DB518D /* objc */,
|
||||
837F67A81A771F63004D34FA /* objc-simulator */,
|
||||
F9BCC6CA205C68E800DD9AFC /* objc-trampolines */,
|
||||
834F9B01212E560100F95A54 /* objc4_tests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@ -489,7 +628,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "set -x\n/usr/bin/xcrun -sdk macosx clang++ -Wall -mmacosx-version-min=10.9 -arch x86_64 -std=c++11 \"${SRCROOT}/markgc.cpp\" -o \"${BUILT_PRODUCTS_DIR}/markgc\"\n\"${BUILT_PRODUCTS_DIR}/markgc\" \"${BUILT_PRODUCTS_DIR}/libobjc.A.dylib\"";
|
||||
shellScript = "set -x\n/usr/bin/xcrun -sdk macosx.internal clang++ -Wall -mmacosx-version-min=10.12 -arch x86_64 -std=c++11 \"${SRCROOT}/markgc.cpp\" -o \"${BUILT_PRODUCTS_DIR}/markgc\"\n\"${BUILT_PRODUCTS_DIR}/markgc\" \"${BUILT_PRODUCTS_DIR}/libobjc.A.dylib\"";
|
||||
};
|
||||
830F2AFA0D73BC5800392440 /* Run Script (symlink) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
@ -503,7 +642,25 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "cd \"${INSTALL_DIR}\"\n/bin/ln -s libobjc.A.dylib libobjc.dylib\n";
|
||||
shellScript = "cd \"${INSTALL_DIR}\"\n/bin/ln -s libobjc.A.dylib libobjc.dylib\n\nTBD_UPPER=`echo ${GENERATE_TEXT_BASED_STUBS} | tr a-z A-Z`\n\nif [ ${TBD_UPPER} = \"YES\" ] || [ ${TBD_UPPER} = \"TRUE\" ] || [ ${TBD_UPPER} = \"1\" ]; then\nGENERATE_TBD=1\nfi\n\nif [ ${GENERATE_TBD} ]; then\n /bin/ln -s libobjc.A.tbd libobjc.tbd\nfi\n";
|
||||
};
|
||||
834F9B05212E561400F95A54 /* Run Script (build tests) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script (build tests)";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "set -x\nset -e\n\n# Set this to empty, or a space-separated list of tests to run.\ntestfiles=\"\"\n\n# Location inside DSTROOT of our test files and our BATS config file.\ntestdir=/AppleInternal/CoreOS/tests/objc4\nconfigdir=/AppleInternal/CoreOS/BATS/unit_tests\n\nmkdir -p ${DSTROOT}${testdir}\nmkdir -p ${DSTROOT}${configdir}\n\n# Common test.pl args for building and running.\ntestargs=\"ARCH=`echo ${ARCHS} | tr ' ' ','` OS=${PLATFORM_NAME} MEM=mrc,arc LANGUAGE=c,c++,objc,objc++ RUN=0 VERBOSE=1 BATS=1 ${testfiles}\"\n\n# Build the tests and BATS plist into DSTROOT.\nperl ${SRCROOT}/test/test.pl $testargs BUILD=1 RUN=0\n\n# Move the BATS plist where BATS expects it, and convert it to binary format.\nmv ${DSTROOT}${testdir}/objc4.plist ${DSTROOT}${configdir}\nplutil -convert binary1 ${DSTROOT}${configdir}/objc4.plist\n\n# Copy test sources to DSTROOT; running the test requires reading them again.\ncp -R ${SRCROOT}/test ${DSTROOT}${testdir}/test\n\n# Don't copy gcfiles because XBS chokes on them.\n# Don't copy other cruft because verifiers dislike them. (This doesn't matter for submissions but does affect local buildit builds.)\nrm -rf ${DSTROOT}${testdir}/test/gcfiles\nrm -rf ${DSTROOT}${testdir}/test/*~\nrm -rf ${DSTROOT}${testdir}/test/\\#*\\#\nrm -rf ${DSTROOT}${testdir}/test/.\\#*\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
@ -544,26 +701,31 @@
|
||||
831C85D60E10CF850066E64C /* objc-os.mm in Sources */,
|
||||
87BB4EA70EC39854005D08E1 /* objc-probes.d in Sources */,
|
||||
83BE02E40FCCB23400661494 /* objc-file-old.mm in Sources */,
|
||||
E8923DA1116AB2820071B552 /* a1a2-blocktramps-i386.s in Sources */,
|
||||
E8923DA2116AB2820071B552 /* a1a2-blocktramps-x86_64.s in Sources */,
|
||||
E8923DA3116AB2820071B552 /* a2a3-blocktramps-i386.s in Sources */,
|
||||
E8923DA4116AB2820071B552 /* a2a3-blocktramps-x86_64.s in Sources */,
|
||||
E8923DA5116AB2820071B552 /* objc-block-trampolines.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 */,
|
||||
39ABD72412F0B61800D1054C /* objc-weak.mm in Sources */,
|
||||
83D49E4F13C7C84F0057F1DD /* objc-msg-arm64.s in Sources */,
|
||||
8379996E13CBAF6F007C2B5F /* a1a2-blocktramps-arm64.s in Sources */,
|
||||
9672F7EE14D5F488007CEC96 /* NSObject.mm in Sources */,
|
||||
83725F4A14CA5BFA0014370E /* objc-opt.mm in Sources */,
|
||||
6EACB844232C97B900CE9176 /* objc-zalloc.mm in Sources */,
|
||||
83F550E0155E030800E95D3B /* objc-cache-old.mm in Sources */,
|
||||
834DF8B715993EE1002F2BC9 /* objc-sel-old.mm in Sources */,
|
||||
83C9C3391668B50E00F4E544 /* objc-msg-simulator-x86_64.s in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
F9BCC6EF205C68E800DD9AFC /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
83EF5E9C20D2299E00F486A4 /* objc-blocktramps-arm.s in Sources */,
|
||||
83EF5E9820D2298400F486A4 /* objc-blocktramps-i386.s in Sources */,
|
||||
83EF5E9920D2298400F486A4 /* objc-blocktramps-x86_64.s in Sources */,
|
||||
F9BCC71B205C68E800DD9AFC /* objc-blocktramps-arm64.s in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
@ -572,14 +734,22 @@
|
||||
target = D2AAC0620554660B00DB518D /* objc */;
|
||||
targetProxy = 837F67AC1A771F6E004D34FA /* PBXContainerItemProxy */;
|
||||
};
|
||||
F9BCC729205C6A0900DD9AFC /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = F9BCC6CA205C68E800DD9AFC /* objc-trampolines */;
|
||||
targetProxy = F9BCC728205C6A0900DD9AFC /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
1DEB914B08733D8E0010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
|
||||
COPY_HEADERS_RUN_UNIFDEF = YES;
|
||||
COPY_HEADERS_UNIFDEF_FLAGS = "-UBUILD_FOR_OSX";
|
||||
"COPY_HEADERS_UNIFDEF_FLAGS[sdk=macosx*]" = "-DBUILD_FOR_OSX";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEPLOYMENT_LOCATION = YES;
|
||||
DYLIB_CURRENT_VERSION = 228;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
GCC_CW_ASM_SYNTAX = NO;
|
||||
@ -593,10 +763,13 @@
|
||||
/System/Library/Frameworks/System.framework/PrivateHeaders,
|
||||
);
|
||||
INSTALL_PATH = /usr/lib;
|
||||
IS_ZIPPERED = YES;
|
||||
LLVM_LTO = NO;
|
||||
ORDER_FILE = "$(SDKROOT)/AppleInternal/OrderFiles/libobjc.order";
|
||||
"ORDER_FILE[sdk=iphonesimulator*]" = "";
|
||||
OTHER_CFLAGS = (
|
||||
"-fdollars-in-identifiers",
|
||||
"-fno-objc-convert-messages-to-runtime-calls",
|
||||
"$(OTHER_CFLAGS)",
|
||||
);
|
||||
"OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
|
||||
@ -610,11 +783,20 @@
|
||||
__objc_data,
|
||||
"-Xlinker",
|
||||
0x1000,
|
||||
"-Xlinker",
|
||||
"-interposable_list",
|
||||
"-Xlinker",
|
||||
interposable.txt,
|
||||
);
|
||||
"OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = (
|
||||
"-lc++abi",
|
||||
"-Xlinker",
|
||||
"-interposable_list",
|
||||
"-Xlinker",
|
||||
interposable.txt,
|
||||
);
|
||||
"OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "-lc++abi";
|
||||
"OTHER_LDFLAGS[sdk=macosx*]" = (
|
||||
"-lCrashReporterClient",
|
||||
"-lauto",
|
||||
"-lc++abi",
|
||||
"-Xlinker",
|
||||
"-sectalign",
|
||||
@ -624,10 +806,17 @@
|
||||
__objc_data,
|
||||
"-Xlinker",
|
||||
0x1000,
|
||||
"-Xlinker",
|
||||
"-interposable_list",
|
||||
"-Xlinker",
|
||||
interposable.txt,
|
||||
);
|
||||
OTHER_TAPI_FLAGS = "-exclude-public-header $(DSTROOT)/usr/include/objc/ObjectiveC.apinotes -exclude-public-header $(DSTROOT)/usr/include/objc/module.modulemap -Xparser -Wno-deprecated-declarations -Xparser -Wno-unavailable-declarations -Xparser -D_OBJC_PRIVATE_H_=1 -DOBJC_DECLARE_SYMBOLS=1";
|
||||
PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc;
|
||||
PRODUCT_NAME = objc.A;
|
||||
PUBLIC_HEADERS_FOLDER_PATH = /usr/include/objc;
|
||||
SUPPORTS_TEXT_BASED_API = YES;
|
||||
TAPI_VERIFY_MODE = Pedantic;
|
||||
UNEXPORTED_SYMBOLS_FILE = unexported_symbols;
|
||||
};
|
||||
name = Debug;
|
||||
@ -635,6 +824,10 @@
|
||||
1DEB914C08733D8E0010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_HEADERS_RUN_UNIFDEF = YES;
|
||||
COPY_HEADERS_UNIFDEF_FLAGS = "-UBUILD_FOR_OSX";
|
||||
"COPY_HEADERS_UNIFDEF_FLAGS[sdk=macosx*]" = "-DBUILD_FOR_OSX";
|
||||
DEPLOYMENT_LOCATION = YES;
|
||||
DYLIB_CURRENT_VERSION = 228;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
GCC_CW_ASM_SYNTAX = NO;
|
||||
@ -647,10 +840,12 @@
|
||||
/System/Library/Frameworks/System.framework/PrivateHeaders,
|
||||
);
|
||||
INSTALL_PATH = /usr/lib;
|
||||
IS_ZIPPERED = YES;
|
||||
ORDER_FILE = "$(SDKROOT)/AppleInternal/OrderFiles/libobjc.order";
|
||||
"ORDER_FILE[sdk=iphonesimulator*]" = "";
|
||||
OTHER_CFLAGS = (
|
||||
"-fdollars-in-identifiers",
|
||||
"-fno-objc-convert-messages-to-runtime-calls",
|
||||
"$(OTHER_CFLAGS)",
|
||||
);
|
||||
"OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
|
||||
@ -664,11 +859,20 @@
|
||||
__objc_data,
|
||||
"-Xlinker",
|
||||
0x1000,
|
||||
"-Xlinker",
|
||||
"-interposable_list",
|
||||
"-Xlinker",
|
||||
interposable.txt,
|
||||
);
|
||||
"OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = (
|
||||
"-lc++abi",
|
||||
"-Xlinker",
|
||||
"-interposable_list",
|
||||
"-Xlinker",
|
||||
interposable.txt,
|
||||
);
|
||||
"OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "-lc++abi";
|
||||
"OTHER_LDFLAGS[sdk=macosx*]" = (
|
||||
"-lCrashReporterClient",
|
||||
"-lauto",
|
||||
"-lc++abi",
|
||||
"-Xlinker",
|
||||
"-sectalign",
|
||||
@ -678,28 +882,41 @@
|
||||
__objc_data,
|
||||
"-Xlinker",
|
||||
0x1000,
|
||||
"-Xlinker",
|
||||
"-interposable_list",
|
||||
"-Xlinker",
|
||||
interposable.txt,
|
||||
);
|
||||
OTHER_TAPI_FLAGS = "-exclude-public-header $(DSTROOT)/usr/include/objc/ObjectiveC.apinotes -exclude-public-header $(DSTROOT)/usr/include/objc/module.modulemap -Xparser -Wno-deprecated-declarations -Xparser -Wno-unavailable-declarations -Xparser -D_OBJC_PRIVATE_H_=1 -DOBJC_DECLARE_SYMBOLS=1";
|
||||
PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc;
|
||||
PRODUCT_NAME = objc.A;
|
||||
PUBLIC_HEADERS_FOLDER_PATH = /usr/include/objc;
|
||||
SUPPORTS_TEXT_BASED_API = YES;
|
||||
TAPI_VERIFY_MODE = Pedantic;
|
||||
UNEXPORTED_SYMBOLS_FILE = unexported_symbols;
|
||||
WARNING_CFLAGS = (
|
||||
"$(inherited)",
|
||||
"-Wglobal-constructors",
|
||||
);
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1DEB914F08733D8E0010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_LINK_OBJC_RUNTIME = NO;
|
||||
CLANG_OBJC_RUNTIME = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
EXCLUDED_INSTALLSRC_SUBDIRECTORY_PATTERNS = "$(inherited) test";
|
||||
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_PREPROCESSOR_DEFINITIONS = (
|
||||
"OS_OBJECT_USE_OBJC=0",
|
||||
"OBJC_IS_DEBUG_BUILD=1",
|
||||
);
|
||||
GCC_STRICT_ALIASING = YES;
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
@ -709,12 +926,15 @@
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
LLVM_LTO = YES;
|
||||
OTHER_CFLAGS = "";
|
||||
"OTHER_CFLAGS[arch=x86_64]" = "-fobjc-legacy-dispatch";
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"$(OTHER_CFLAGS)",
|
||||
"-D_LIBCPP_VISIBLE=\"\"",
|
||||
);
|
||||
SDKROOT = macosx.internal;
|
||||
SUPPORTED_PLATFORMS = "macosx iphoneos appletvos watchos";
|
||||
WARNING_CFLAGS = (
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
@ -730,12 +950,11 @@
|
||||
1DEB915008733D8E0010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_LINK_OBJC_RUNTIME = NO;
|
||||
CLANG_OBJC_RUNTIME = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
EXCLUDED_INSTALLSRC_SUBDIRECTORY_PATTERNS = "$(inherited) test";
|
||||
GCC_ENABLE_CPP_EXCEPTIONS = NO;
|
||||
GCC_ENABLE_CPP_RTTI = NO;
|
||||
GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
|
||||
@ -752,7 +971,8 @@
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
"OTHER_CFLAGS[arch=i386]" = "-momit-leaf-frame-pointer";
|
||||
LLVM_LTO = YES;
|
||||
OTHER_CFLAGS = "-momit-leaf-frame-pointer";
|
||||
"OTHER_CFLAGS[arch=x86_64]" = (
|
||||
"-momit-leaf-frame-pointer",
|
||||
"-fobjc-legacy-dispatch",
|
||||
@ -761,6 +981,8 @@
|
||||
"$(OTHER_CFLAGS)",
|
||||
"-D_LIBCPP_VISIBLE=\"\"",
|
||||
);
|
||||
SDKROOT = macosx.internal;
|
||||
SUPPORTED_PLATFORMS = "macosx iphoneos appletvos watchos";
|
||||
WARNING_CFLAGS = (
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
@ -773,6 +995,24 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
834F9B02212E560200F95A54 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 59GAB85EFG;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
834F9B03212E560200F95A54 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 59GAB85EFG;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
837F67AA1A771F63004D34FA /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@ -787,6 +1027,78 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
F9BCC725205C68E800DD9AFC /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_HEADERS_RUN_UNIFDEF = YES;
|
||||
COPY_HEADERS_UNIFDEF_FLAGS = "-UBUILD_FOR_OSX";
|
||||
"COPY_HEADERS_UNIFDEF_FLAGS[sdk=macosx*]" = "-DBUILD_FOR_OSX";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DYLIB_CURRENT_VERSION = 228;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
GCC_CW_ASM_SYNTAX = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
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/**",
|
||||
/System/Library/Frameworks/System.framework/PrivateHeaders,
|
||||
);
|
||||
INSTALL_PATH = /usr/lib;
|
||||
IS_ZIPPERED = YES;
|
||||
OTHER_CFLAGS = (
|
||||
"-fdollars-in-identifiers",
|
||||
"$(OTHER_CFLAGS)",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"-Xlinker",
|
||||
"-not_for_dyld_shared_cache",
|
||||
);
|
||||
PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PUBLIC_HEADERS_FOLDER_PATH = /usr/include/objc;
|
||||
SUPPORTS_TEXT_BASED_API = YES;
|
||||
TAPI_VERIFY_MODE = Pedantic;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
F9BCC726205C68E800DD9AFC /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_HEADERS_RUN_UNIFDEF = YES;
|
||||
COPY_HEADERS_UNIFDEF_FLAGS = "-UBUILD_FOR_OSX";
|
||||
"COPY_HEADERS_UNIFDEF_FLAGS[sdk=macosx*]" = "-DBUILD_FOR_OSX";
|
||||
DYLIB_CURRENT_VERSION = 228;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
GCC_CW_ASM_SYNTAX = NO;
|
||||
GCC_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/**",
|
||||
/System/Library/Frameworks/System.framework/PrivateHeaders,
|
||||
);
|
||||
INSTALL_PATH = /usr/lib;
|
||||
IS_ZIPPERED = YES;
|
||||
OTHER_CFLAGS = (
|
||||
"-fdollars-in-identifiers",
|
||||
"$(OTHER_CFLAGS)",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"-Xlinker",
|
||||
"-not_for_dyld_shared_cache",
|
||||
);
|
||||
PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/objc;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PUBLIC_HEADERS_FOLDER_PATH = /usr/include/objc;
|
||||
SUPPORTS_TEXT_BASED_API = YES;
|
||||
TAPI_VERIFY_MODE = Pedantic;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
@ -808,6 +1120,15 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
834F9B04212E560200F95A54 /* Build configuration list for PBXAggregateTarget "objc4_tests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
834F9B02212E560200F95A54 /* Debug */,
|
||||
834F9B03212E560200F95A54 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
837F67A91A771F63004D34FA /* Build configuration list for PBXAggregateTarget "objc-simulator" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
@ -817,6 +1138,15 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
F9BCC724205C68E800DD9AFC /* Build configuration list for PBXNativeTarget "objc-trampolines" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
F9BCC725205C68E800DD9AFC /* Debug */,
|
||||
F9BCC726205C68E800DD9AFC /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
|
||||
|
0
objcrt/objcrt.vcproj
Executable file → Normal file
0
objcrt/objcrt.vcproj
Executable file → Normal file
0
prebuild.bat
Executable file → Normal file
0
prebuild.bat
Executable file → Normal file
86
runtime/DenseMapExtras.h
Normal file
86
runtime/DenseMapExtras.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Apple Inc. All Rights Reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#ifndef DENSEMAPEXTRAS_H
|
||||
#define DENSEMAPEXTRAS_H
|
||||
|
||||
#include "llvm-DenseMap.h"
|
||||
#include "llvm-DenseSet.h"
|
||||
|
||||
namespace objc {
|
||||
|
||||
// We cannot use a C++ static initializer to initialize certain globals because
|
||||
// libc calls us before our C++ initializers run. We also don't want a global
|
||||
// pointer to some globals because of the extra indirection.
|
||||
//
|
||||
// ExplicitInit / LazyInit wrap doing it the hard way.
|
||||
template <typename Type>
|
||||
class ExplicitInit {
|
||||
alignas(Type) uint8_t _storage[sizeof(Type)];
|
||||
|
||||
public:
|
||||
template <typename... Ts>
|
||||
void init(Ts &&... Args) {
|
||||
new (_storage) Type(std::forward<Ts>(Args)...);
|
||||
}
|
||||
|
||||
Type &get() {
|
||||
return *reinterpret_cast<Type *>(_storage);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class LazyInit {
|
||||
alignas(Type) uint8_t _storage[sizeof(Type)];
|
||||
bool _didInit;
|
||||
|
||||
public:
|
||||
template <typename... Ts>
|
||||
Type *get(bool allowCreate, Ts &&... Args) {
|
||||
if (!_didInit) {
|
||||
if (!allowCreate) {
|
||||
return nullptr;
|
||||
}
|
||||
new (_storage) Type(std::forward<Ts>(Args)...);
|
||||
_didInit = true;
|
||||
}
|
||||
return reinterpret_cast<Type *>(_storage);
|
||||
}
|
||||
};
|
||||
|
||||
// Convenience class for Dense Maps & Sets
|
||||
template <typename Key, typename Value>
|
||||
class ExplicitInitDenseMap : public ExplicitInit<DenseMap<Key, Value>> { };
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class LazyInitDenseMap : public LazyInit<DenseMap<Key, Value>> { };
|
||||
|
||||
template <typename Value>
|
||||
class ExplicitInitDenseSet : public ExplicitInit<DenseSet<Value>> { };
|
||||
|
||||
template <typename Value>
|
||||
class LazyInitDenseSet : public LazyInit<DenseSet<Value>> { };
|
||||
|
||||
} // namespace objc
|
||||
|
||||
#endif /* DENSEMAPEXTRAS_H */
|
@ -29,6 +29,8 @@
|
||||
#ifdef __arm__
|
||||
|
||||
#include <arm/arch.h>
|
||||
#include "objc-config.h"
|
||||
#include "isa.h"
|
||||
|
||||
#ifndef _ARM_ARCH_7
|
||||
# error requires armv7
|
||||
@ -67,25 +69,6 @@
|
||||
|
||||
#endif
|
||||
|
||||
// Define SUPPORT_INDEXED_ISA for targets which store the class in the ISA as
|
||||
// an index in to a class table.
|
||||
// Note, keep this in sync with objc-config.h.
|
||||
// FIXME: Remove this duplication. We should get this from objc-config.h.
|
||||
#if __ARM_ARCH_7K__ >= 2
|
||||
# define SUPPORT_INDEXED_ISA 1
|
||||
#else
|
||||
# define SUPPORT_INDEXED_ISA 0
|
||||
#endif
|
||||
|
||||
// Note, keep these in sync with objc-private.h
|
||||
#define ISA_INDEX_IS_NPI 1
|
||||
#define ISA_INDEX_MASK 0x0001FFFC
|
||||
#define ISA_INDEX_SHIFT 2
|
||||
#define ISA_INDEX_BITS 15
|
||||
#define ISA_INDEX_COUNT (1 << ISA_INDEX_BITS)
|
||||
#define ISA_INDEX_MAGIC_MASK 0x001E0001
|
||||
#define ISA_INDEX_MAGIC_VALUE 0x001C0001
|
||||
|
||||
.syntax unified
|
||||
|
||||
#define MI_EXTERN(var) \
|
||||
@ -119,98 +102,35 @@ _objc_indexed_classes:
|
||||
|
||||
|
||||
|
||||
// _objc_entryPoints and _objc_exitPoints are used by method dispatch
|
||||
// _objc_restartableRanges is used by method dispatch
|
||||
// caching code to figure out whether any threads are actively
|
||||
// in the cache for dispatching. The labels surround the asm code
|
||||
// that do cache lookups. The tables are zero-terminated.
|
||||
|
||||
.align 2
|
||||
.private_extern _objc_entryPoints
|
||||
_objc_entryPoints:
|
||||
.long _cache_getImp
|
||||
.long _objc_msgSend
|
||||
.long _objc_msgSend_stret
|
||||
.long _objc_msgSendSuper
|
||||
.long _objc_msgSendSuper_stret
|
||||
.long _objc_msgSendSuper2
|
||||
.long _objc_msgSendSuper2_stret
|
||||
.long _objc_msgLookup
|
||||
.long _objc_msgLookup_stret
|
||||
.long _objc_msgLookupSuper2
|
||||
.long _objc_msgLookupSuper2_stret
|
||||
.long 0
|
||||
|
||||
.private_extern _objc_exitPoints
|
||||
_objc_exitPoints:
|
||||
.long LExit_cache_getImp
|
||||
.long LExit_objc_msgSend
|
||||
.long LExit_objc_msgSend_stret
|
||||
.long LExit_objc_msgSendSuper
|
||||
.long LExit_objc_msgSendSuper_stret
|
||||
.long LExit_objc_msgSendSuper2
|
||||
.long LExit_objc_msgSendSuper2_stret
|
||||
.long LExit_objc_msgLookup
|
||||
.long LExit_objc_msgLookup_stret
|
||||
.long LExit_objc_msgLookupSuper2
|
||||
.long LExit_objc_msgLookupSuper2_stret
|
||||
.long 0
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* List every exit insn from every messenger for debugger use.
|
||||
* Format:
|
||||
* (
|
||||
* 1 word instruction's address
|
||||
* 1 word type (ENTER or FAST_EXIT or SLOW_EXIT or NIL_EXIT)
|
||||
* )
|
||||
* 1 word zero
|
||||
*
|
||||
* ENTER is the start of a dispatcher
|
||||
* FAST_EXIT is method dispatch
|
||||
* SLOW_EXIT is uncached method lookup
|
||||
* NIL_EXIT is returning zero from a message sent to nil
|
||||
* These must match objc-gdb.h.
|
||||
********************************************************************/
|
||||
|
||||
#define ENTER 1
|
||||
#define FAST_EXIT 2
|
||||
#define SLOW_EXIT 3
|
||||
#define NIL_EXIT 4
|
||||
|
||||
.section __DATA,__objc_msg_break
|
||||
.globl _gdb_objc_messenger_breakpoints
|
||||
_gdb_objc_messenger_breakpoints:
|
||||
// contents populated by the macros below
|
||||
|
||||
.macro MESSENGER_START
|
||||
7:
|
||||
.section __DATA,__objc_msg_break
|
||||
.long 7b
|
||||
.long ENTER
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_FAST
|
||||
7:
|
||||
.section __DATA,__objc_msg_break
|
||||
.long 7b
|
||||
.long FAST_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_SLOW
|
||||
7:
|
||||
.section __DATA,__objc_msg_break
|
||||
.long 7b
|
||||
.long SLOW_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_NIL
|
||||
7:
|
||||
.section __DATA,__objc_msg_break
|
||||
.long 7b
|
||||
.long NIL_EXIT
|
||||
.text
|
||||
.macro RestartableEntry
|
||||
.long LLookupStart$0
|
||||
.long 0
|
||||
.short LLookupEnd$0 - LLookupStart$0
|
||||
.short 0xffff // poor ol' armv7 doesn't support kernel based recovery
|
||||
.long 0
|
||||
.endmacro
|
||||
|
||||
.align 4
|
||||
.private_extern _objc_restartableRanges
|
||||
_objc_restartableRanges:
|
||||
RestartableEntry _cache_getImp
|
||||
RestartableEntry _objc_msgSend
|
||||
RestartableEntry _objc_msgSend_stret
|
||||
RestartableEntry _objc_msgSendSuper
|
||||
RestartableEntry _objc_msgSendSuper_stret
|
||||
RestartableEntry _objc_msgSendSuper2
|
||||
RestartableEntry _objc_msgSendSuper2_stret
|
||||
RestartableEntry _objc_msgLookup
|
||||
RestartableEntry _objc_msgLookup_stret
|
||||
RestartableEntry _objc_msgLookupSuper2
|
||||
RestartableEntry _objc_msgLookupSuper2_stret
|
||||
.fill 16, 1, 0
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Names for relative labels
|
||||
@ -218,7 +138,7 @@ _gdb_objc_messenger_breakpoints:
|
||||
* Reserved labels: 6: 7: 8: 9:
|
||||
********************************************************************/
|
||||
// 6: used by CacheLookup
|
||||
// 7: used by MI_GET_ADDRESS etc and MESSENGER_START etc
|
||||
// 7: used by MI_GET_ADDRESS etc
|
||||
// 8: used by CacheLookup
|
||||
#define LNilReceiver 9
|
||||
#define LNilReceiver_f 9f
|
||||
@ -249,6 +169,10 @@ _gdb_objc_messenger_breakpoints:
|
||||
#define CACHE 8
|
||||
#define CACHE_MASK 12
|
||||
|
||||
/* Field offsets in method cache bucket */
|
||||
#define CACHED_SEL 0
|
||||
#define CACHED_IMP 4
|
||||
|
||||
/* Selected field offsets in method structure */
|
||||
#define METHOD_NAME 0
|
||||
#define METHOD_TYPES 4
|
||||
@ -300,8 +224,8 @@ LExit$0:
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CacheLookup NORMAL|STRET
|
||||
// CacheLookup2 NORMAL|STRET
|
||||
// CacheLookup NORMAL|STRET <function>
|
||||
// CacheLookup2 NORMAL|STRET <function>
|
||||
//
|
||||
// Locate the implementation for a selector in a class's method cache.
|
||||
//
|
||||
@ -318,7 +242,32 @@ LExit$0:
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
.macro CacheLookup
|
||||
|
||||
//
|
||||
// Restart protocol:
|
||||
//
|
||||
// As soon as we're past the LLookupStart$1 label we may have loaded
|
||||
// an invalid cache pointer or mask.
|
||||
//
|
||||
// When task_restartable_ranges_synchronize() is called,
|
||||
// (or when a signal hits us) before we're past LLookupEnd$1,
|
||||
// then our PC will be reset to LCacheMiss$1 which forcefully
|
||||
// jumps to the cache-miss codepath.
|
||||
//
|
||||
// It is assumed that the CacheMiss codepath starts right at the end
|
||||
// of CacheLookup2 and will re-setup the registers to meet the cache-miss
|
||||
// requirements:
|
||||
//
|
||||
// GETIMP:
|
||||
// The cache-miss is just returning NULL (setting r9 to 0)
|
||||
//
|
||||
// NORMAL and STRET:
|
||||
// - r0 or r1 (STRET) contains the receiver
|
||||
// - r1 or r2 (STRET) contains the selector
|
||||
// - r9 contains the isa (reloaded from r0/r1)
|
||||
// - other registers are set as per calling conventions
|
||||
//
|
||||
LLookupStart$1:
|
||||
|
||||
ldrh r12, [r9, #CACHE_MASK] // r12 = mask
|
||||
ldr r9, [r9, #CACHE] // r9 = buckets
|
||||
.if $0 == STRET
|
||||
@ -327,7 +276,7 @@ LExit$0:
|
||||
and r12, r12, r1 // r12 = index = SEL & mask
|
||||
.endif
|
||||
add r9, r9, r12, LSL #3 // r9 = bucket = buckets+index*8
|
||||
ldr r12, [r9] // r12 = bucket->sel
|
||||
ldr r12, [r9, #CACHED_SEL] // r12 = bucket->sel
|
||||
6:
|
||||
.if $0 == STRET
|
||||
teq r12, r2
|
||||
@ -335,7 +284,7 @@ LExit$0:
|
||||
teq r12, r1
|
||||
.endif
|
||||
bne 8f
|
||||
ldr r12, [r9, #4] // r12 = bucket->imp
|
||||
ldr r12, [r9, #CACHED_IMP] // r12 = bucket->imp
|
||||
|
||||
.if $0 == STRET
|
||||
tst r12, r12 // set ne for stret forwarding
|
||||
@ -346,15 +295,19 @@ LExit$0:
|
||||
.endmacro
|
||||
|
||||
.macro CacheLookup2
|
||||
|
||||
#if CACHED_SEL != 0
|
||||
# error this code requires that SEL be at offset 0
|
||||
#endif
|
||||
8:
|
||||
cmp r12, #1
|
||||
blo 8f // if (bucket->sel == 0) cache miss
|
||||
blo LCacheMiss$1 // if (bucket->sel == 0) cache miss
|
||||
it eq // if (bucket->sel == 1) cache wrap
|
||||
ldreq r9, [r9, #4] // bucket->imp is before first bucket
|
||||
ldreq r9, [r9, #CACHED_IMP] // bucket->imp is before first bucket
|
||||
ldr r12, [r9, #8]! // r12 = (++bucket)->sel
|
||||
b 6b
|
||||
8:
|
||||
|
||||
LLookupEnd$1:
|
||||
LCacheMiss$1:
|
||||
|
||||
.endmacro
|
||||
|
||||
@ -377,7 +330,7 @@ LExit$0:
|
||||
// Note: We are doing a little wasted work here to load values we might not
|
||||
// need. Branching turns out to be even worse when performance was measured.
|
||||
MI_GET_ADDRESS(r12, _objc_indexed_classes)
|
||||
tst.w r9, #ISA_INDEX_IS_NPI
|
||||
tst.w r9, #ISA_INDEX_IS_NPI_MASK
|
||||
itt ne
|
||||
ubfxne r9, r9, #ISA_INDEX_SHIFT, #ISA_INDEX_BITS
|
||||
ldrne.w r9, [r12, r9, lsl #2]
|
||||
@ -399,12 +352,12 @@ LExit$0:
|
||||
STATIC_ENTRY _cache_getImp
|
||||
|
||||
mov r9, r0
|
||||
CacheLookup NORMAL
|
||||
CacheLookup NORMAL, _cache_getImp
|
||||
// cache hit, IMP in r12
|
||||
mov r0, r12
|
||||
bx lr // return imp
|
||||
|
||||
CacheLookup2 GETIMP
|
||||
CacheLookup2 GETIMP, _cache_getImp
|
||||
// cache miss, return nil
|
||||
mov r0, #0
|
||||
bx lr
|
||||
@ -425,22 +378,19 @@ LExit$0:
|
||||
********************************************************************/
|
||||
|
||||
ENTRY _objc_msgSend
|
||||
MESSENGER_START
|
||||
|
||||
cbz r0, LNilReceiver_f
|
||||
|
||||
ldr r9, [r0] // r9 = self->isa
|
||||
GetClassFromIsa // r9 = class
|
||||
CacheLookup NORMAL
|
||||
CacheLookup NORMAL, _objc_msgSend
|
||||
// cache hit, IMP in r12, eq already set for nonstret forwarding
|
||||
MESSENGER_END_FAST
|
||||
bx r12 // call imp
|
||||
|
||||
CacheLookup2 NORMAL
|
||||
CacheLookup2 NORMAL, _objc_msgSend
|
||||
// cache miss
|
||||
ldr r9, [r0] // r9 = self->isa
|
||||
GetClassFromIsa // r9 = class
|
||||
MESSENGER_END_SLOW
|
||||
b __objc_msgSend_uncached
|
||||
|
||||
LNilReceiver:
|
||||
@ -449,7 +399,6 @@ LNilReceiver:
|
||||
mov r2, #0
|
||||
mov r3, #0
|
||||
FP_RETURN_ZERO
|
||||
MESSENGER_END_NIL
|
||||
bx lr
|
||||
|
||||
END_ENTRY _objc_msgSend
|
||||
@ -461,11 +410,11 @@ LNilReceiver:
|
||||
|
||||
ldr r9, [r0] // r9 = self->isa
|
||||
GetClassFromIsa // r9 = class
|
||||
CacheLookup NORMAL
|
||||
CacheLookup NORMAL, _objc_msgLookup
|
||||
// cache hit, IMP in r12, eq already set for nonstret forwarding
|
||||
bx lr
|
||||
|
||||
CacheLookup2 NORMAL
|
||||
CacheLookup2 NORMAL, _objc_msgLookup
|
||||
// cache miss
|
||||
ldr r9, [r0] // r9 = self->isa
|
||||
GetClassFromIsa // r9 = class
|
||||
@ -504,26 +453,22 @@ LNilReceiver:
|
||||
********************************************************************/
|
||||
|
||||
ENTRY _objc_msgSend_stret
|
||||
MESSENGER_START
|
||||
|
||||
cbz r1, LNilReceiver_f
|
||||
|
||||
ldr r9, [r1] // r9 = self->isa
|
||||
GetClassFromIsa // r9 = class
|
||||
CacheLookup STRET
|
||||
CacheLookup STRET, _objc_msgSend_stret
|
||||
// cache hit, IMP in r12, ne already set for stret forwarding
|
||||
MESSENGER_END_FAST
|
||||
bx r12
|
||||
|
||||
CacheLookup2 STRET
|
||||
CacheLookup2 STRET, _objc_msgSend_stret
|
||||
// cache miss
|
||||
ldr r9, [r1] // r9 = self->isa
|
||||
GetClassFromIsa // r9 = class
|
||||
MESSENGER_END_SLOW
|
||||
b __objc_msgSend_stret_uncached
|
||||
|
||||
LNilReceiver:
|
||||
MESSENGER_END_NIL
|
||||
bx lr
|
||||
|
||||
END_ENTRY _objc_msgSend_stret
|
||||
@ -535,11 +480,11 @@ LNilReceiver:
|
||||
|
||||
ldr r9, [r1] // r9 = self->isa
|
||||
GetClassFromIsa // r9 = class
|
||||
CacheLookup STRET
|
||||
CacheLookup STRET, _objc_msgLookup_stret
|
||||
// cache hit, IMP in r12, ne already set for stret forwarding
|
||||
bx lr
|
||||
|
||||
CacheLookup2 STRET
|
||||
CacheLookup2 STRET, _objc_msgLookup_stret
|
||||
// cache miss
|
||||
ldr r9, [r1] // r9 = self->isa
|
||||
GetClassFromIsa // r9 = class
|
||||
@ -569,20 +514,17 @@ LNilReceiver:
|
||||
********************************************************************/
|
||||
|
||||
ENTRY _objc_msgSendSuper
|
||||
MESSENGER_START
|
||||
|
||||
ldr r9, [r0, #CLASS] // r9 = struct super->class
|
||||
CacheLookup NORMAL
|
||||
CacheLookup NORMAL, _objc_msgSendSuper
|
||||
// cache hit, IMP in r12, eq already set for nonstret forwarding
|
||||
ldr r0, [r0, #RECEIVER] // load real receiver
|
||||
MESSENGER_END_FAST
|
||||
bx r12 // call imp
|
||||
|
||||
CacheLookup2 NORMAL
|
||||
CacheLookup2 NORMAL, _objc_msgSendSuper
|
||||
// cache miss
|
||||
ldr r9, [r0, #CLASS] // r9 = struct super->class
|
||||
ldr r0, [r0, #RECEIVER] // load real receiver
|
||||
MESSENGER_END_SLOW
|
||||
b __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper
|
||||
@ -598,22 +540,19 @@ LNilReceiver:
|
||||
********************************************************************/
|
||||
|
||||
ENTRY _objc_msgSendSuper2
|
||||
MESSENGER_START
|
||||
|
||||
ldr r9, [r0, #CLASS] // class = struct super->class
|
||||
ldr r9, [r9, #SUPERCLASS] // class = class->superclass
|
||||
CacheLookup NORMAL
|
||||
CacheLookup NORMAL, _objc_msgSendSuper2
|
||||
// cache hit, IMP in r12, eq already set for nonstret forwarding
|
||||
ldr r0, [r0, #RECEIVER] // load real receiver
|
||||
MESSENGER_END_FAST
|
||||
bx r12 // call imp
|
||||
|
||||
CacheLookup2 NORMAL
|
||||
CacheLookup2 NORMAL, _objc_msgSendSuper2
|
||||
// cache miss
|
||||
ldr r9, [r0, #CLASS] // class = struct super->class
|
||||
ldr r9, [r9, #SUPERCLASS] // class = class->superclass
|
||||
ldr r0, [r0, #RECEIVER] // load real receiver
|
||||
MESSENGER_END_SLOW
|
||||
b __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper2
|
||||
@ -623,12 +562,12 @@ LNilReceiver:
|
||||
|
||||
ldr r9, [r0, #CLASS] // class = struct super->class
|
||||
ldr r9, [r9, #SUPERCLASS] // class = class->superclass
|
||||
CacheLookup NORMAL
|
||||
CacheLookup NORMAL, _objc_msgLookupSuper2
|
||||
// cache hit, IMP in r12, eq already set for nonstret forwarding
|
||||
ldr r0, [r0, #RECEIVER] // load real receiver
|
||||
bx lr
|
||||
|
||||
CacheLookup2 NORMAL
|
||||
CacheLookup2 NORMAL, _objc_msgLookupSuper2
|
||||
// cache miss
|
||||
ldr r9, [r0, #CLASS]
|
||||
ldr r9, [r9, #SUPERCLASS] // r9 = class to search
|
||||
@ -651,20 +590,17 @@ LNilReceiver:
|
||||
********************************************************************/
|
||||
|
||||
ENTRY _objc_msgSendSuper_stret
|
||||
MESSENGER_START
|
||||
|
||||
ldr r9, [r1, #CLASS] // r9 = struct super->class
|
||||
CacheLookup STRET
|
||||
CacheLookup STRET, _objc_msgSendSuper_stret
|
||||
// cache hit, IMP in r12, ne already set for stret forwarding
|
||||
ldr r1, [r1, #RECEIVER] // load real receiver
|
||||
MESSENGER_END_FAST
|
||||
bx r12 // call imp
|
||||
|
||||
CacheLookup2 STRET
|
||||
CacheLookup2 STRET, _objc_msgSendSuper_stret
|
||||
// cache miss
|
||||
ldr r9, [r1, #CLASS] // r9 = struct super->class
|
||||
ldr r1, [r1, #RECEIVER] // load real receiver
|
||||
MESSENGER_END_SLOW
|
||||
b __objc_msgSend_stret_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper_stret
|
||||
@ -675,22 +611,19 @@ LNilReceiver:
|
||||
********************************************************************/
|
||||
|
||||
ENTRY _objc_msgSendSuper2_stret
|
||||
MESSENGER_START
|
||||
|
||||
ldr r9, [r1, #CLASS] // class = struct super->class
|
||||
ldr r9, [r9, #SUPERCLASS] // class = class->superclass
|
||||
CacheLookup STRET
|
||||
CacheLookup STRET, _objc_msgSendSuper2_stret
|
||||
// cache hit, IMP in r12, ne already set for stret forwarding
|
||||
ldr r1, [r1, #RECEIVER] // load real receiver
|
||||
MESSENGER_END_FAST
|
||||
bx r12 // call imp
|
||||
|
||||
CacheLookup2 STRET
|
||||
CacheLookup2 STRET, _objc_msgSendSuper2_stret
|
||||
// cache miss
|
||||
ldr r9, [r1, #CLASS] // class = struct super->class
|
||||
ldr r9, [r9, #SUPERCLASS] // class = class->superclass
|
||||
ldr r1, [r1, #RECEIVER] // load real receiver
|
||||
MESSENGER_END_SLOW
|
||||
b __objc_msgSend_stret_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper2_stret
|
||||
@ -700,12 +633,12 @@ LNilReceiver:
|
||||
|
||||
ldr r9, [r1, #CLASS] // class = struct super->class
|
||||
ldr r9, [r9, #SUPERCLASS] // class = class->superclass
|
||||
CacheLookup STRET
|
||||
CacheLookup STRET, _objc_msgLookupSuper2_stret
|
||||
// cache hit, IMP in r12, ne already set for stret forwarding
|
||||
ldr r1, [r1, #RECEIVER] // load real receiver
|
||||
bx lr
|
||||
|
||||
CacheLookup2 STRET
|
||||
CacheLookup2 STRET, _objc_msgLookupSuper2_stret
|
||||
// cache miss
|
||||
ldr r9, [r1, #CLASS]
|
||||
ldr r9, [r9, #SUPERCLASS] // r9 = class to search
|
||||
@ -738,6 +671,7 @@ LNilReceiver:
|
||||
sub sp, #8 // align stack
|
||||
FP_SAVE
|
||||
|
||||
// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
|
||||
.if $0 == NORMAL
|
||||
// receiver already in r0
|
||||
// selector already in r1
|
||||
@ -746,8 +680,8 @@ LNilReceiver:
|
||||
mov r1, r2 // selector
|
||||
.endif
|
||||
mov r2, r9 // class to search
|
||||
|
||||
blx __class_lookupMethodAndLoadCache3
|
||||
mov r3, #3 // LOOKUP_INITIALIZE | LOOKUP_INITIALIZE
|
||||
blx _lookUpImpOrForward
|
||||
mov r12, r0 // r12 = IMP
|
||||
|
||||
.if $0 == NORMAL
|
||||
@ -837,10 +771,6 @@ LNilReceiver:
|
||||
// THIS IS NOT A CALLABLE C FUNCTION
|
||||
// Out-of-band Z is 0 (EQ) for normal, 1 (NE) for stret
|
||||
|
||||
MESSENGER_START
|
||||
nop
|
||||
MESSENGER_END_SLOW
|
||||
|
||||
beq __objc_msgForward
|
||||
b __objc_msgForward_stret
|
||||
|
||||
|
390
runtime/Messengers.subproj/objc-msg-arm64.S
Executable file → Normal file
390
runtime/Messengers.subproj/objc-msg-arm64.S
Executable file → Normal file
@ -27,110 +27,102 @@
|
||||
********************************************************************/
|
||||
|
||||
#ifdef __arm64__
|
||||
|
||||
#include <arm/arch.h>
|
||||
|
||||
#include <arm/arch.h>
|
||||
#include "isa.h"
|
||||
#include "arm64-asm.h"
|
||||
#include "objc-config.h"
|
||||
|
||||
.data
|
||||
|
||||
// _objc_entryPoints and _objc_exitPoints are used by method dispatch
|
||||
// _objc_restartableRanges is used by method dispatch
|
||||
// caching code to figure out whether any threads are actively
|
||||
// in the cache for dispatching. The labels surround the asm code
|
||||
// that do cache lookups. The tables are zero-terminated.
|
||||
|
||||
.align 4
|
||||
.private_extern _objc_entryPoints
|
||||
_objc_entryPoints:
|
||||
.quad _cache_getImp
|
||||
.quad _objc_msgSend
|
||||
.quad _objc_msgSendSuper
|
||||
.quad _objc_msgSendSuper2
|
||||
.quad _objc_msgLookup
|
||||
.quad _objc_msgLookupSuper2
|
||||
.quad 0
|
||||
|
||||
.private_extern _objc_exitPoints
|
||||
_objc_exitPoints:
|
||||
.quad LExit_cache_getImp
|
||||
.quad LExit_objc_msgSend
|
||||
.quad LExit_objc_msgSendSuper
|
||||
.quad LExit_objc_msgSendSuper2
|
||||
.quad LExit_objc_msgLookup
|
||||
.quad LExit_objc_msgLookupSuper2
|
||||
.quad 0
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* List every exit insn from every messenger for debugger use.
|
||||
* Format:
|
||||
* (
|
||||
* 1 word instruction's address
|
||||
* 1 word type (ENTER or FAST_EXIT or SLOW_EXIT or NIL_EXIT)
|
||||
* )
|
||||
* 1 word zero
|
||||
*
|
||||
* ENTER is the start of a dispatcher
|
||||
* FAST_EXIT is method dispatch
|
||||
* SLOW_EXIT is uncached method lookup
|
||||
* NIL_EXIT is returning zero from a message sent to nil
|
||||
* These must match objc-gdb.h.
|
||||
********************************************************************/
|
||||
|
||||
#define ENTER 1
|
||||
#define FAST_EXIT 2
|
||||
#define SLOW_EXIT 3
|
||||
#define NIL_EXIT 4
|
||||
|
||||
.section __DATA,__objc_msg_break
|
||||
.globl _gdb_objc_messenger_breakpoints
|
||||
_gdb_objc_messenger_breakpoints:
|
||||
// contents populated by the macros below
|
||||
|
||||
.macro MESSENGER_START
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.quad 4b
|
||||
.quad ENTER
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_FAST
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.quad 4b
|
||||
.quad FAST_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_SLOW
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.quad 4b
|
||||
.quad SLOW_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_NIL
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.quad 4b
|
||||
.quad NIL_EXIT
|
||||
.text
|
||||
.macro RestartableEntry
|
||||
#if __LP64__
|
||||
.quad LLookupStart$0
|
||||
#else
|
||||
.long LLookupStart$0
|
||||
.long 0
|
||||
#endif
|
||||
.short LLookupEnd$0 - LLookupStart$0
|
||||
.short LLookupRecover$0 - LLookupStart$0
|
||||
.long 0
|
||||
.endmacro
|
||||
|
||||
.align 4
|
||||
.private_extern _objc_restartableRanges
|
||||
_objc_restartableRanges:
|
||||
RestartableEntry _cache_getImp
|
||||
RestartableEntry _objc_msgSend
|
||||
RestartableEntry _objc_msgSendSuper
|
||||
RestartableEntry _objc_msgSendSuper2
|
||||
RestartableEntry _objc_msgLookup
|
||||
RestartableEntry _objc_msgLookupSuper2
|
||||
.fill 16, 1, 0
|
||||
|
||||
|
||||
/* objc_super parameter to sendSuper */
|
||||
#define RECEIVER 0
|
||||
#define CLASS 8
|
||||
#define CLASS __SIZEOF_POINTER__
|
||||
|
||||
/* Selected field offsets in class structure */
|
||||
#define SUPERCLASS 8
|
||||
#define CACHE 16
|
||||
|
||||
/* Selected field offsets in isa field */
|
||||
#define ISA_MASK 0x0000000ffffffff8
|
||||
#define SUPERCLASS __SIZEOF_POINTER__
|
||||
#define CACHE (2 * __SIZEOF_POINTER__)
|
||||
|
||||
/* Selected field offsets in method structure */
|
||||
#define METHOD_NAME 0
|
||||
#define METHOD_TYPES 8
|
||||
#define METHOD_IMP 16
|
||||
#define METHOD_TYPES __SIZEOF_POINTER__
|
||||
#define METHOD_IMP (2 * __SIZEOF_POINTER__)
|
||||
|
||||
#define BUCKET_SIZE (2 * __SIZEOF_POINTER__)
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* GetClassFromIsa_p16 src
|
||||
* src is a raw isa field. Sets p16 to the corresponding class pointer.
|
||||
* The raw isa might be an indexed isa to be decoded, or a
|
||||
* packed isa that needs to be masked.
|
||||
*
|
||||
* On exit:
|
||||
* $0 is unchanged
|
||||
* p16 is a class pointer
|
||||
* x10 is clobbered
|
||||
********************************************************************/
|
||||
|
||||
#if SUPPORT_INDEXED_ISA
|
||||
.align 3
|
||||
.globl _objc_indexed_classes
|
||||
_objc_indexed_classes:
|
||||
.fill ISA_INDEX_COUNT, PTRSIZE, 0
|
||||
#endif
|
||||
|
||||
.macro GetClassFromIsa_p16 /* src */
|
||||
|
||||
#if SUPPORT_INDEXED_ISA
|
||||
// Indexed isa
|
||||
mov p16, $0 // optimistically set dst = src
|
||||
tbz p16, #ISA_INDEX_IS_NPI_BIT, 1f // done if not non-pointer isa
|
||||
// isa in p16 is indexed
|
||||
adrp x10, _objc_indexed_classes@PAGE
|
||||
add x10, x10, _objc_indexed_classes@PAGEOFF
|
||||
ubfx p16, p16, #ISA_INDEX_SHIFT, #ISA_INDEX_BITS // extract index
|
||||
ldr p16, [x10, p16, UXTP #PTRSHIFT] // load class from array
|
||||
1:
|
||||
|
||||
#elif __LP64__
|
||||
// 64-bit packed isa
|
||||
and p16, $0, #ISA_MASK
|
||||
|
||||
#else
|
||||
// 32-bit raw isa
|
||||
mov p16, $0
|
||||
|
||||
#endif
|
||||
|
||||
.endmacro
|
||||
|
||||
|
||||
/********************************************************************
|
||||
@ -164,12 +156,12 @@ LExit$0:
|
||||
********************************************************************/
|
||||
.macro UNWIND
|
||||
.section __LD,__compact_unwind,regular,debug
|
||||
.quad $0
|
||||
PTR $0
|
||||
.set LUnwind$0, LExit$0 - $0
|
||||
.long LUnwind$0
|
||||
.long $1
|
||||
.quad 0 /* no personality */
|
||||
.quad 0 /* no LSDA */
|
||||
PTR 0 /* no personality */
|
||||
PTR 0 /* no LSDA */
|
||||
.text
|
||||
.endmacro
|
||||
|
||||
@ -179,10 +171,15 @@ LExit$0:
|
||||
|
||||
/********************************************************************
|
||||
*
|
||||
* CacheLookup NORMAL|GETIMP|LOOKUP
|
||||
*
|
||||
* CacheLookup NORMAL|GETIMP|LOOKUP <function>
|
||||
*
|
||||
* Locate the implementation for a selector in a class method cache.
|
||||
*
|
||||
* When this is used in a function that doesn't hold the runtime lock,
|
||||
* this represents the critical section that may access dead memory.
|
||||
* If the kernel causes one of these functions to go down the recovery
|
||||
* path, we pretend the lookup failed by jumping the JumpMiss branch.
|
||||
*
|
||||
* Takes:
|
||||
* x1 = selector
|
||||
* x16 = class to be searched
|
||||
@ -200,14 +197,19 @@ LExit$0:
|
||||
#define GETIMP 1
|
||||
#define LOOKUP 2
|
||||
|
||||
// CacheHit: x17 = cached IMP, x12 = address of cached IMP, x1 = SEL, x16 = isa
|
||||
.macro CacheHit
|
||||
.if $0 == NORMAL
|
||||
MESSENGER_END_FAST
|
||||
br x17 // call imp
|
||||
TailCallCachedImp x17, x12, x1, x16 // authenticate and call imp
|
||||
.elseif $0 == GETIMP
|
||||
mov x0, x17 // return imp
|
||||
ret
|
||||
mov p0, p17
|
||||
cbz p0, 9f // don't ptrauth a nil imp
|
||||
AuthAndResignAsIMP x0, x12, x1, x16 // authenticate imp and re-sign as IMP
|
||||
9: ret // return IMP
|
||||
.elseif $0 == LOOKUP
|
||||
// No nil check for ptrauth: the caller would crash anyway when they
|
||||
// jump to a nil IMP. We don't care if that jump also fails ptrauth.
|
||||
AuthAndResignAsIMP x17, x12, x1, x16 // authenticate imp and re-sign as IMP
|
||||
ret // return imp via x17
|
||||
.else
|
||||
.abort oops
|
||||
@ -217,11 +219,11 @@ LExit$0:
|
||||
.macro CheckMiss
|
||||
// miss if bucket->sel == 0
|
||||
.if $0 == GETIMP
|
||||
cbz x9, LGetImpMiss
|
||||
cbz p9, LGetImpMiss
|
||||
.elseif $0 == NORMAL
|
||||
cbz x9, __objc_msgSend_uncached
|
||||
cbz p9, __objc_msgSend_uncached
|
||||
.elseif $0 == LOOKUP
|
||||
cbz x9, __objc_msgLookup_uncached
|
||||
cbz p9, __objc_msgLookup_uncached
|
||||
.else
|
||||
.abort oops
|
||||
.endif
|
||||
@ -240,44 +242,92 @@ LExit$0:
|
||||
.endmacro
|
||||
|
||||
.macro CacheLookup
|
||||
// x1 = SEL, x16 = isa
|
||||
ldp x10, x11, [x16, #CACHE] // x10 = buckets, x11 = occupied|mask
|
||||
and w12, w1, w11 // x12 = _cmd & mask
|
||||
add x12, x10, x12, LSL #4 // x12 = buckets + ((_cmd & mask)<<4)
|
||||
//
|
||||
// Restart protocol:
|
||||
//
|
||||
// As soon as we're past the LLookupStart$1 label we may have loaded
|
||||
// an invalid cache pointer or mask.
|
||||
//
|
||||
// When task_restartable_ranges_synchronize() is called,
|
||||
// (or when a signal hits us) before we're past LLookupEnd$1,
|
||||
// then our PC will be reset to LLookupRecover$1 which forcefully
|
||||
// jumps to the cache-miss codepath which have the following
|
||||
// requirements:
|
||||
//
|
||||
// GETIMP:
|
||||
// The cache-miss is just returning NULL (setting x0 to 0)
|
||||
//
|
||||
// NORMAL and LOOKUP:
|
||||
// - x0 contains the receiver
|
||||
// - x1 contains the selector
|
||||
// - x16 contains the isa
|
||||
// - other registers are set as per calling conventions
|
||||
//
|
||||
LLookupStart$1:
|
||||
|
||||
ldp x9, x17, [x12] // {x9, x17} = *bucket
|
||||
1: cmp x9, x1 // if (bucket->sel != _cmd)
|
||||
// p1 = SEL, p16 = isa
|
||||
ldr p11, [x16, #CACHE] // p11 = mask|buckets
|
||||
|
||||
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
|
||||
and p10, p11, #0x0000ffffffffffff // p10 = buckets
|
||||
and p12, p1, p11, LSR #48 // x12 = _cmd & mask
|
||||
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
|
||||
and p10, p11, #~0xf // p10 = buckets
|
||||
and p11, p11, #0xf // p11 = maskShift
|
||||
mov p12, #0xffff
|
||||
lsr p11, p12, p11 // p11 = mask = 0xffff >> p11
|
||||
and p12, p1, p11 // x12 = _cmd & mask
|
||||
#else
|
||||
#error Unsupported cache mask storage for ARM64.
|
||||
#endif
|
||||
|
||||
|
||||
add p12, p10, p12, LSL #(1+PTRSHIFT)
|
||||
// p12 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
|
||||
|
||||
ldp p17, p9, [x12] // {imp, sel} = *bucket
|
||||
1: cmp p9, p1 // if (bucket->sel != _cmd)
|
||||
b.ne 2f // scan more
|
||||
CacheHit $0 // call or return imp
|
||||
|
||||
2: // not hit: x12 = not-hit bucket
|
||||
2: // not hit: p12 = not-hit bucket
|
||||
CheckMiss $0 // miss if bucket->sel == 0
|
||||
cmp x12, x10 // wrap if bucket == buckets
|
||||
cmp p12, p10 // wrap if bucket == buckets
|
||||
b.eq 3f
|
||||
ldp x9, x17, [x12, #-16]! // {x9, x17} = *--bucket
|
||||
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
|
||||
b 1b // loop
|
||||
|
||||
3: // wrap: x12 = first bucket, w11 = mask
|
||||
add x12, x12, w11, UXTW #4 // x12 = buckets+(mask<<4)
|
||||
3: // wrap: p12 = first bucket, w11 = mask
|
||||
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
|
||||
add p12, p12, p11, LSR #(48 - (1+PTRSHIFT))
|
||||
// p12 = buckets + (mask << 1+PTRSHIFT)
|
||||
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
|
||||
add p12, p12, p11, LSL #(1+PTRSHIFT)
|
||||
// p12 = buckets + (mask << 1+PTRSHIFT)
|
||||
#else
|
||||
#error Unsupported cache mask storage for ARM64.
|
||||
#endif
|
||||
|
||||
// Clone scanning loop to miss instead of hang when cache is corrupt.
|
||||
// The slow path may detect any corruption and halt later.
|
||||
|
||||
ldp x9, x17, [x12] // {x9, x17} = *bucket
|
||||
1: cmp x9, x1 // if (bucket->sel != _cmd)
|
||||
ldp p17, p9, [x12] // {imp, sel} = *bucket
|
||||
1: cmp p9, p1 // if (bucket->sel != _cmd)
|
||||
b.ne 2f // scan more
|
||||
CacheHit $0 // call or return imp
|
||||
|
||||
2: // not hit: x12 = not-hit bucket
|
||||
2: // not hit: p12 = not-hit bucket
|
||||
CheckMiss $0 // miss if bucket->sel == 0
|
||||
cmp x12, x10 // wrap if bucket == buckets
|
||||
cmp p12, p10 // wrap if bucket == buckets
|
||||
b.eq 3f
|
||||
ldp x9, x17, [x12, #-16]! // {x9, x17} = *--bucket
|
||||
ldp p17, p9, [x12, #-BUCKET_SIZE]! // {imp, sel} = *--bucket
|
||||
b 1b // loop
|
||||
|
||||
LLookupEnd$1:
|
||||
LLookupRecover$1:
|
||||
3: // double wrap
|
||||
JumpMiss $0
|
||||
|
||||
|
||||
.endmacro
|
||||
|
||||
|
||||
@ -292,6 +342,7 @@ LExit$0:
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#if SUPPORT_TAGGED_POINTERS
|
||||
.data
|
||||
.align 3
|
||||
.globl _objc_debug_taggedpointer_classes
|
||||
@ -300,39 +351,46 @@ _objc_debug_taggedpointer_classes:
|
||||
.globl _objc_debug_taggedpointer_ext_classes
|
||||
_objc_debug_taggedpointer_ext_classes:
|
||||
.fill 256, 8, 0
|
||||
#endif
|
||||
|
||||
ENTRY _objc_msgSend
|
||||
UNWIND _objc_msgSend, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
cmp x0, #0 // nil check and tagged pointer check
|
||||
cmp p0, #0 // nil check and tagged pointer check
|
||||
#if SUPPORT_TAGGED_POINTERS
|
||||
b.le LNilOrTagged // (MSB tagged pointer looks negative)
|
||||
ldr x13, [x0] // x13 = isa
|
||||
and x16, x13, #ISA_MASK // x16 = class
|
||||
#else
|
||||
b.eq LReturnZero
|
||||
#endif
|
||||
ldr p13, [x0] // p13 = isa
|
||||
GetClassFromIsa_p16 p13 // p16 = class
|
||||
LGetIsaDone:
|
||||
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
|
||||
// calls imp or objc_msgSend_uncached
|
||||
CacheLookup NORMAL, _objc_msgSend
|
||||
|
||||
#if SUPPORT_TAGGED_POINTERS
|
||||
LNilOrTagged:
|
||||
b.eq LReturnZero // nil check
|
||||
|
||||
// tagged
|
||||
mov x10, #0xf000000000000000
|
||||
cmp x0, x10
|
||||
b.hs LExtTag
|
||||
adrp x10, _objc_debug_taggedpointer_classes@PAGE
|
||||
add x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
|
||||
ubfx x11, x0, #60, #4
|
||||
ldr x16, [x10, x11, LSL #3]
|
||||
b LGetIsaDone
|
||||
adrp x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGE
|
||||
add x10, x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGEOFF
|
||||
cmp x10, x16
|
||||
b.ne LGetIsaDone
|
||||
|
||||
LExtTag:
|
||||
// ext tagged
|
||||
adrp x10, _objc_debug_taggedpointer_ext_classes@PAGE
|
||||
add x10, x10, _objc_debug_taggedpointer_ext_classes@PAGEOFF
|
||||
ubfx x11, x0, #52, #8
|
||||
ldr x16, [x10, x11, LSL #3]
|
||||
b LGetIsaDone
|
||||
|
||||
// SUPPORT_TAGGED_POINTERS
|
||||
#endif
|
||||
|
||||
LReturnZero:
|
||||
// x0 is already zero
|
||||
mov x1, #0
|
||||
@ -340,7 +398,6 @@ LReturnZero:
|
||||
movi d1, #0
|
||||
movi d2, #0
|
||||
movi d3, #0
|
||||
MESSENGER_END_NIL
|
||||
ret
|
||||
|
||||
END_ENTRY _objc_msgSend
|
||||
@ -348,26 +405,31 @@ LReturnZero:
|
||||
|
||||
ENTRY _objc_msgLookup
|
||||
UNWIND _objc_msgLookup, NoFrame
|
||||
|
||||
cmp x0, #0 // nil check and tagged pointer check
|
||||
cmp p0, #0 // nil check and tagged pointer check
|
||||
#if SUPPORT_TAGGED_POINTERS
|
||||
b.le LLookup_NilOrTagged // (MSB tagged pointer looks negative)
|
||||
ldr x13, [x0] // x13 = isa
|
||||
and x16, x13, #ISA_MASK // x16 = class
|
||||
#else
|
||||
b.eq LLookup_Nil
|
||||
#endif
|
||||
ldr p13, [x0] // p13 = isa
|
||||
GetClassFromIsa_p16 p13 // p16 = class
|
||||
LLookup_GetIsaDone:
|
||||
CacheLookup LOOKUP // returns imp
|
||||
// returns imp
|
||||
CacheLookup LOOKUP, _objc_msgLookup
|
||||
|
||||
#if SUPPORT_TAGGED_POINTERS
|
||||
LLookup_NilOrTagged:
|
||||
b.eq LLookup_Nil // nil check
|
||||
|
||||
// tagged
|
||||
mov x10, #0xf000000000000000
|
||||
cmp x0, x10
|
||||
b.hs LLookup_ExtTag
|
||||
adrp x10, _objc_debug_taggedpointer_classes@PAGE
|
||||
add x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
|
||||
ubfx x11, x0, #60, #4
|
||||
ldr x16, [x10, x11, LSL #3]
|
||||
b LLookup_GetIsaDone
|
||||
adrp x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGE
|
||||
add x10, x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGEOFF
|
||||
cmp x10, x16
|
||||
b.ne LLookup_GetIsaDone
|
||||
|
||||
LLookup_ExtTag:
|
||||
adrp x10, _objc_debug_taggedpointer_ext_classes@PAGE
|
||||
@ -375,6 +437,8 @@ LLookup_ExtTag:
|
||||
ubfx x11, x0, #52, #8
|
||||
ldr x16, [x10, x11, LSL #3]
|
||||
b LLookup_GetIsaDone
|
||||
// SUPPORT_TAGGED_POINTERS
|
||||
#endif
|
||||
|
||||
LLookup_Nil:
|
||||
adrp x17, __objc_msgNil@PAGE
|
||||
@ -399,10 +463,10 @@ LLookup_Nil:
|
||||
|
||||
ENTRY _objc_msgSendSuper
|
||||
UNWIND _objc_msgSendSuper, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
ldp x0, x16, [x0] // x0 = real receiver, x16 = class
|
||||
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
|
||||
ldp p0, p16, [x0] // p0 = real receiver, p16 = class
|
||||
// calls imp or objc_msgSend_uncached
|
||||
CacheLookup NORMAL, _objc_msgSendSuper
|
||||
|
||||
END_ENTRY _objc_msgSendSuper
|
||||
|
||||
@ -410,11 +474,10 @@ LLookup_Nil:
|
||||
|
||||
ENTRY _objc_msgSendSuper2
|
||||
UNWIND _objc_msgSendSuper2, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
ldp x0, x16, [x0] // x0 = real receiver, x16 = class
|
||||
ldr x16, [x16, #SUPERCLASS] // x16 = class->superclass
|
||||
CacheLookup NORMAL
|
||||
ldp p0, p16, [x0] // p0 = real receiver, p16 = class
|
||||
ldr p16, [x16, #SUPERCLASS] // p16 = class->superclass
|
||||
CacheLookup NORMAL, _objc_msgSendSuper2
|
||||
|
||||
END_ENTRY _objc_msgSendSuper2
|
||||
|
||||
@ -422,9 +485,9 @@ LLookup_Nil:
|
||||
ENTRY _objc_msgLookupSuper2
|
||||
UNWIND _objc_msgLookupSuper2, NoFrame
|
||||
|
||||
ldp x0, x16, [x0] // x0 = real receiver, x16 = class
|
||||
ldr x16, [x16, #SUPERCLASS] // x16 = class->superclass
|
||||
CacheLookup LOOKUP
|
||||
ldp p0, p16, [x0] // p0 = real receiver, p16 = class
|
||||
ldr p16, [x16, #SUPERCLASS] // p16 = class->superclass
|
||||
CacheLookup LOOKUP, _objc_msgLookupSuper2
|
||||
|
||||
END_ENTRY _objc_msgLookupSuper2
|
||||
|
||||
@ -432,6 +495,7 @@ LLookup_Nil:
|
||||
.macro MethodTableLookup
|
||||
|
||||
// push frame
|
||||
SignLR
|
||||
stp fp, lr, [sp, #-16]!
|
||||
mov fp, sp
|
||||
|
||||
@ -447,11 +511,13 @@ LLookup_Nil:
|
||||
stp x6, x7, [sp, #(8*16+6*8)]
|
||||
str x8, [sp, #(8*16+8*8)]
|
||||
|
||||
// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
|
||||
// receiver and selector already in x0 and x1
|
||||
mov x2, x16
|
||||
bl __class_lookupMethodAndLoadCache3
|
||||
mov x3, #3
|
||||
bl _lookUpImpOrForward
|
||||
|
||||
// imp in x0
|
||||
// IMP in x0
|
||||
mov x17, x0
|
||||
|
||||
// restore registers and return
|
||||
@ -467,6 +533,7 @@ LLookup_Nil:
|
||||
|
||||
mov sp, fp
|
||||
ldp fp, lr, [sp], #16
|
||||
AuthenticateLR
|
||||
|
||||
.endmacro
|
||||
|
||||
@ -474,10 +541,10 @@ LLookup_Nil:
|
||||
UNWIND __objc_msgSend_uncached, FrameWithNoSaves
|
||||
|
||||
// THIS IS NOT A CALLABLE C FUNCTION
|
||||
// Out-of-band x16 is the class to search
|
||||
// Out-of-band p16 is the class to search
|
||||
|
||||
MethodTableLookup
|
||||
br x17
|
||||
TailCallFunctionPointer x17
|
||||
|
||||
END_ENTRY __objc_msgSend_uncached
|
||||
|
||||
@ -486,7 +553,7 @@ LLookup_Nil:
|
||||
UNWIND __objc_msgLookup_uncached, FrameWithNoSaves
|
||||
|
||||
// THIS IS NOT A CALLABLE C FUNCTION
|
||||
// Out-of-band x16 is the class to search
|
||||
// Out-of-band p16 is the class to search
|
||||
|
||||
MethodTableLookup
|
||||
ret
|
||||
@ -496,11 +563,11 @@ LLookup_Nil:
|
||||
|
||||
STATIC_ENTRY _cache_getImp
|
||||
|
||||
and x16, x0, #ISA_MASK
|
||||
CacheLookup GETIMP
|
||||
GetClassFromIsa_p16 p0
|
||||
CacheLookup GETIMP, _cache_getImp
|
||||
|
||||
LGetImpMiss:
|
||||
mov x0, #0
|
||||
mov p0, #0
|
||||
ret
|
||||
|
||||
END_ENTRY _cache_getImp
|
||||
@ -519,10 +586,6 @@ LGetImpMiss:
|
||||
|
||||
STATIC_ENTRY __objc_msgForward_impcache
|
||||
|
||||
MESSENGER_START
|
||||
nop
|
||||
MESSENGER_END_SLOW
|
||||
|
||||
// No stret specialization.
|
||||
b __objc_msgForward
|
||||
|
||||
@ -532,8 +595,8 @@ LGetImpMiss:
|
||||
ENTRY __objc_msgForward
|
||||
|
||||
adrp x17, __objc_forward_handler@PAGE
|
||||
ldr x17, [x17, __objc_forward_handler@PAGEOFF]
|
||||
br x17
|
||||
ldr p17, [x17, __objc_forward_handler@PAGEOFF]
|
||||
TailCallFunctionPointer x17
|
||||
|
||||
END_ENTRY __objc_msgForward
|
||||
|
||||
@ -553,9 +616,10 @@ LGetImpMiss:
|
||||
|
||||
ENTRY _method_invoke
|
||||
// x1 is method triplet instead of SEL
|
||||
ldr x17, [x1, #METHOD_IMP]
|
||||
ldr x1, [x1, #METHOD_NAME]
|
||||
br x17
|
||||
add p16, p1, #METHOD_IMP
|
||||
ldr p17, [x16]
|
||||
ldr p1, [x1, #METHOD_NAME]
|
||||
TailCallMethodListImp x17, x16
|
||||
END_ENTRY _method_invoke
|
||||
|
||||
#endif
|
||||
|
@ -40,89 +40,30 @@
|
||||
|
||||
.data
|
||||
|
||||
// _objc_entryPoints and _objc_exitPoints are used by objc
|
||||
// _objc_restartableRanges is used by method dispatch
|
||||
// to get the critical regions for which method caches
|
||||
// cannot be garbage collected.
|
||||
|
||||
.align 2
|
||||
.private_extern _objc_entryPoints
|
||||
_objc_entryPoints:
|
||||
.long __cache_getImp
|
||||
.long __cache_getMethod
|
||||
.long _objc_msgSend
|
||||
.long _objc_msgSend_fpret
|
||||
.long _objc_msgSend_stret
|
||||
.long _objc_msgSendSuper
|
||||
.long _objc_msgSendSuper_stret
|
||||
.macro RestartableEntry
|
||||
.long $0
|
||||
.long 0
|
||||
|
||||
.private_extern _objc_exitPoints
|
||||
_objc_exitPoints:
|
||||
.long LGetImpExit
|
||||
.long LGetMethodExit
|
||||
.long LMsgSendExit
|
||||
.long LMsgSendFpretExit
|
||||
.long LMsgSendStretExit
|
||||
.long LMsgSendSuperExit
|
||||
.long LMsgSendSuperStretExit
|
||||
.short $1 - $0
|
||||
.short 0xffff // The old runtime doesn't support kernel based recovery
|
||||
.long 0
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* List every exit insn from every messenger for debugger use.
|
||||
* Format:
|
||||
* (
|
||||
* 1 word instruction's address
|
||||
* 1 word type (ENTER or FAST_EXIT or SLOW_EXIT or NIL_EXIT)
|
||||
* )
|
||||
* 1 word zero
|
||||
*
|
||||
* ENTER is the start of a dispatcher
|
||||
* FAST_EXIT is method dispatch
|
||||
* SLOW_EXIT is uncached method lookup
|
||||
* NIL_EXIT is returning zero from a message sent to nil
|
||||
* These must match objc-gdb.h.
|
||||
********************************************************************/
|
||||
|
||||
#define ENTER 1
|
||||
#define FAST_EXIT 2
|
||||
#define SLOW_EXIT 3
|
||||
#define NIL_EXIT 4
|
||||
|
||||
.section __DATA,__objc_msg_break
|
||||
.globl _gdb_objc_messenger_breakpoints
|
||||
_gdb_objc_messenger_breakpoints:
|
||||
// contents populated by the macros below
|
||||
|
||||
.macro MESSENGER_START
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.long 4b
|
||||
.long ENTER
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_FAST
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.long 4b
|
||||
.long FAST_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_SLOW
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.long 4b
|
||||
.long SLOW_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_NIL
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.long 4b
|
||||
.long NIL_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
|
||||
.align 4
|
||||
.private_extern _objc_restartableRanges
|
||||
_objc_restartableRanges:
|
||||
RestartableEntry __cache_getImp, LGetImpExit
|
||||
RestartableEntry __cache_getMethod, LGetMethodExit
|
||||
RestartableEntry _objc_msgSend, LMsgSendExit
|
||||
RestartableEntry _objc_msgSend_fpret, LMsgSendFpretExit
|
||||
RestartableEntry _objc_msgSend_stret, LMsgSendStretExit
|
||||
RestartableEntry _objc_msgSendSuper, LMsgSendSuperExit
|
||||
RestartableEntry _objc_msgSendSuper_stret, LMsgSendSuperStretExit
|
||||
.fill 16, 1, 0
|
||||
|
||||
|
||||
/********************************************************************
|
||||
*
|
||||
@ -492,8 +433,6 @@ LMsgSendHitInstrumentDone_$0_$1_$2:
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
.macro MethodTableLookup
|
||||
MESSENGER_END_SLOW
|
||||
|
||||
// stack has return address and nothing else
|
||||
subl $$(12+5*16), %esp
|
||||
|
||||
@ -502,10 +441,12 @@ LMsgSendHitInstrumentDone_$0_$1_$2:
|
||||
movdqa %xmm1, 2*16(%esp)
|
||||
movdqa %xmm0, 1*16(%esp)
|
||||
|
||||
// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
|
||||
movl $$3, 12(%esp) // LOOKUP_INITIALIZE | LOOKUP_RESOLVER
|
||||
movl %eax, 8(%esp) // class
|
||||
movl %ecx, 4(%esp) // selector
|
||||
movl %edx, 0(%esp) // receiver
|
||||
call __class_lookupMethodAndLoadCache3
|
||||
call _lookUpImpOrForward
|
||||
|
||||
movdqa 4*16(%esp), %xmm3
|
||||
movdqa 3*16(%esp), %xmm2
|
||||
@ -593,7 +534,6 @@ LGetImpExit:
|
||||
********************************************************************/
|
||||
|
||||
ENTRY _objc_msgSend
|
||||
MESSENGER_START
|
||||
CALL_MCOUNTER
|
||||
|
||||
// load receiver and selector
|
||||
@ -609,7 +549,6 @@ LMsgSendReceiverOk:
|
||||
movl isa(%eax), %edx // class = self->isa
|
||||
CacheLookup WORD_RETURN, MSG_SEND, LMsgSendCacheMiss
|
||||
xor %edx, %edx // set nonstret for msgForward_internal
|
||||
MESSENGER_END_FAST
|
||||
jmp *%eax
|
||||
|
||||
// cache miss: go search the method lists
|
||||
@ -624,7 +563,6 @@ LMsgSendNilSelf:
|
||||
movl $0,%edx
|
||||
xorps %xmm0, %xmm0
|
||||
LMsgSendDone:
|
||||
MESSENGER_END_NIL
|
||||
ret
|
||||
|
||||
// guaranteed non-nil entry point (disabled for now)
|
||||
@ -647,7 +585,6 @@ LMsgSendExit:
|
||||
********************************************************************/
|
||||
|
||||
ENTRY _objc_msgSendSuper
|
||||
MESSENGER_START
|
||||
CALL_MCOUNTER
|
||||
|
||||
// load selector and class to search
|
||||
@ -658,7 +595,6 @@ LMsgSendExit:
|
||||
// search the cache (class in %edx)
|
||||
CacheLookup WORD_RETURN, MSG_SENDSUPER, LMsgSendSuperCacheMiss
|
||||
xor %edx, %edx // set nonstret for msgForward_internal
|
||||
MESSENGER_END_FAST
|
||||
jmp *%eax // goto *imp
|
||||
|
||||
// cache miss: go search the method lists
|
||||
@ -671,7 +607,6 @@ LMsgSendSuperCacheMiss:
|
||||
LMsgSendSuperIgnored:
|
||||
movl super(%esp), %eax
|
||||
movl receiver(%eax), %eax
|
||||
MESSENGER_END_NIL
|
||||
ret
|
||||
|
||||
LMsgSendSuperExit:
|
||||
@ -736,7 +671,6 @@ LMsgSendvArgsOK:
|
||||
********************************************************************/
|
||||
|
||||
ENTRY _objc_msgSend_fpret
|
||||
MESSENGER_START
|
||||
CALL_MCOUNTER
|
||||
|
||||
// load receiver and selector
|
||||
@ -752,7 +686,6 @@ LMsgSendFpretReceiverOk:
|
||||
movl isa(%eax), %edx // class = self->isa
|
||||
CacheLookup WORD_RETURN, MSG_SEND, LMsgSendFpretCacheMiss
|
||||
xor %edx, %edx // set nonstret for msgForward_internal
|
||||
MESSENGER_END_FAST
|
||||
jmp *%eax // goto *imp
|
||||
|
||||
// cache miss: go search the method lists
|
||||
@ -766,7 +699,6 @@ LMsgSendFpretNilSelf:
|
||||
// %eax is already zero
|
||||
fldz
|
||||
LMsgSendFpretDone:
|
||||
MESSENGER_END_NIL
|
||||
ret
|
||||
|
||||
LMsgSendFpretExit:
|
||||
@ -839,7 +771,6 @@ LMsgSendvFpretArgsOK:
|
||||
********************************************************************/
|
||||
|
||||
ENTRY _objc_msgSend_stret
|
||||
MESSENGER_START
|
||||
CALL_MCOUNTER
|
||||
|
||||
// load receiver and selector
|
||||
@ -855,7 +786,6 @@ LMsgSendStretReceiverOk:
|
||||
movl isa(%eax), %edx // class = self->isa
|
||||
CacheLookup STRUCT_RETURN, MSG_SEND, LMsgSendStretCacheMiss
|
||||
movl $1, %edx // set stret for objc_msgForward
|
||||
MESSENGER_END_FAST
|
||||
jmp *%eax // goto *imp
|
||||
|
||||
// cache miss: go search the method lists
|
||||
@ -866,7 +796,6 @@ LMsgSendStretCacheMiss:
|
||||
|
||||
// message sent to nil: redirect to nil receiver, if any
|
||||
LMsgSendStretNilSelf:
|
||||
MESSENGER_END_NIL
|
||||
ret $4 // pop struct return address (#2995932)
|
||||
|
||||
// guaranteed non-nil entry point (disabled for now)
|
||||
@ -899,7 +828,6 @@ LMsgSendStretExit:
|
||||
********************************************************************/
|
||||
|
||||
ENTRY _objc_msgSendSuper_stret
|
||||
MESSENGER_START
|
||||
CALL_MCOUNTER
|
||||
|
||||
// load selector and class to search
|
||||
@ -910,7 +838,6 @@ LMsgSendStretExit:
|
||||
// search the cache (class in %edx)
|
||||
CacheLookup STRUCT_RETURN, MSG_SENDSUPER, LMsgSendSuperStretCacheMiss
|
||||
movl $1, %edx // set stret for objc_msgForward
|
||||
MESSENGER_END_FAST
|
||||
jmp *%eax // goto *imp
|
||||
|
||||
// cache miss: go search the method lists
|
||||
@ -1012,10 +939,6 @@ L_forward_stret_handler:
|
||||
|
||||
// THIS IS NOT A CALLABLE C FUNCTION
|
||||
// Out-of-band register %edx is nonzero for stret, zero otherwise
|
||||
|
||||
MESSENGER_START
|
||||
nop
|
||||
MESSENGER_END_SLOW
|
||||
|
||||
// Check return type (stret or not)
|
||||
testl %edx, %edx
|
||||
|
@ -28,101 +28,36 @@
|
||||
|
||||
.data
|
||||
|
||||
// _objc_entryPoints and _objc_exitPoints are used by objc
|
||||
// _objc_restartableRanges is used by method dispatch
|
||||
// to get the critical regions for which method caches
|
||||
// cannot be garbage collected.
|
||||
|
||||
.align 2
|
||||
.private_extern _objc_entryPoints
|
||||
_objc_entryPoints:
|
||||
.long _cache_getImp
|
||||
.long _objc_msgSend
|
||||
.long _objc_msgSend_fpret
|
||||
.long _objc_msgSend_stret
|
||||
.long _objc_msgSendSuper
|
||||
.long _objc_msgSendSuper2
|
||||
.long _objc_msgSendSuper_stret
|
||||
.long _objc_msgSendSuper2_stret
|
||||
.long _objc_msgLookup
|
||||
.long _objc_msgLookup_fpret
|
||||
.long _objc_msgLookup_stret
|
||||
.long _objc_msgLookupSuper2
|
||||
.long _objc_msgLookupSuper2_stret
|
||||
.macro RestartableEntry
|
||||
.long $0
|
||||
.long 0
|
||||
|
||||
.private_extern _objc_exitPoints
|
||||
_objc_exitPoints:
|
||||
.long LExit_cache_getImp
|
||||
.long LExit_objc_msgSend
|
||||
.long LExit_objc_msgSend_fpret
|
||||
.long LExit_objc_msgSend_stret
|
||||
.long LExit_objc_msgSendSuper
|
||||
.long LExit_objc_msgSendSuper2
|
||||
.long LExit_objc_msgSendSuper_stret
|
||||
.long LExit_objc_msgSendSuper2_stret
|
||||
.long LExit_objc_msgLookup
|
||||
.long LExit_objc_msgLookup_fpret
|
||||
.long LExit_objc_msgLookup_stret
|
||||
.long LExit_objc_msgLookupSuper2
|
||||
.long LExit_objc_msgLookupSuper2_stret
|
||||
.short LExit$0 - $0
|
||||
.short 0xffff // The simulator doesn't support kernel based recovery
|
||||
.long 0
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* List every exit insn from every messenger for debugger use.
|
||||
* Format:
|
||||
* (
|
||||
* 1 word instruction's address
|
||||
* 1 word type (ENTER or FAST_EXIT or SLOW_EXIT or NIL_EXIT)
|
||||
* )
|
||||
* 1 word zero
|
||||
*
|
||||
* ENTER is the start of a dispatcher
|
||||
* FAST_EXIT is method dispatch
|
||||
* SLOW_EXIT is uncached method lookup
|
||||
* NIL_EXIT is returning zero from a message sent to nil
|
||||
* These must match objc-gdb.h.
|
||||
********************************************************************/
|
||||
|
||||
#define ENTER 1
|
||||
#define FAST_EXIT 2
|
||||
#define SLOW_EXIT 3
|
||||
#define NIL_EXIT 4
|
||||
|
||||
.section __DATA,__objc_msg_break
|
||||
.globl _gdb_objc_messenger_breakpoints
|
||||
_gdb_objc_messenger_breakpoints:
|
||||
// contents populated by the macros below
|
||||
|
||||
.macro MESSENGER_START
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.long 4b
|
||||
.long ENTER
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_FAST
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.long 4b
|
||||
.long FAST_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_SLOW
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.long 4b
|
||||
.long SLOW_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_NIL
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.long 4b
|
||||
.long NIL_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
|
||||
.align 4
|
||||
.private_extern _objc_restartableRanges
|
||||
_objc_restartableRanges:
|
||||
RestartableEntry _cache_getImp
|
||||
RestartableEntry _objc_msgSend
|
||||
RestartableEntry _objc_msgSend_fpret
|
||||
RestartableEntry _objc_msgSend_stret
|
||||
RestartableEntry _objc_msgSendSuper
|
||||
RestartableEntry _objc_msgSendSuper2
|
||||
RestartableEntry _objc_msgSendSuper_stret
|
||||
RestartableEntry _objc_msgSendSuper2_stret
|
||||
RestartableEntry _objc_msgLookup
|
||||
RestartableEntry _objc_msgLookup_fpret
|
||||
RestartableEntry _objc_msgLookup_stret
|
||||
RestartableEntry _objc_msgLookupSuper2
|
||||
RestartableEntry _objc_msgLookupSuper2_stret
|
||||
.fill 16, 1, 0
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Names for relative labels
|
||||
@ -188,6 +123,12 @@ _gdb_objc_messenger_breakpoints:
|
||||
// Selected field offsets in class structure
|
||||
#define isa 0
|
||||
#define superclass 4
|
||||
#define cache_buckets 8
|
||||
#define cache_mask 12
|
||||
|
||||
// Method cache
|
||||
#define cached_sel 0
|
||||
#define cached_imp 4
|
||||
|
||||
// Method descriptor
|
||||
#define method_name 0
|
||||
@ -277,23 +218,26 @@ LExit$0:
|
||||
// eax = found bucket
|
||||
|
||||
.if $1 == GETIMP
|
||||
movl 4(%eax), %eax // return imp
|
||||
ret
|
||||
movl cached_imp(%eax), %eax // return imp
|
||||
cmpl $$0, %eax
|
||||
jz 9f // don't xor a nil imp
|
||||
xorl %edx, %eax // xor the isa with the imp
|
||||
9: ret
|
||||
|
||||
.else
|
||||
|
||||
.if $0 != STRET
|
||||
// eq already set for forwarding by `jne`
|
||||
.else
|
||||
test %eax, %eax // set ne for stret forwarding
|
||||
.endif
|
||||
|
||||
.if $1 == CALL
|
||||
MESSENGER_END_FAST
|
||||
jmp *4(%eax) // call imp
|
||||
xorl cached_imp(%eax), %edx // xor imp and isa
|
||||
.if $0 != STRET
|
||||
// ne already set for forwarding by `xor`
|
||||
.else
|
||||
cmp %eax, %eax // set eq for stret forwarding
|
||||
.endif
|
||||
jmp *%edx // call imp
|
||||
|
||||
.elseif $1 == LOOKUP
|
||||
movl 4(%eax), %eax // return imp
|
||||
movl cached_imp(%eax), %eax // return imp
|
||||
xorl %edx, %eax // xor isa into imp
|
||||
ret
|
||||
|
||||
.else
|
||||
@ -307,23 +251,23 @@ LExit$0:
|
||||
|
||||
.macro CacheLookup
|
||||
|
||||
movzwl 12(%edx), %eax // eax = mask
|
||||
movzwl cache_mask(%edx), %eax // eax = mask
|
||||
andl %ecx, %eax // eax = SEL & mask
|
||||
shll $$3, %eax // eax = offset = (SEL & mask) * 8
|
||||
addl 8(%edx), %eax // eax = bucket = cache->buckets+offset
|
||||
cmpl (%eax), %ecx // if (bucket->sel != SEL)
|
||||
addl cache_buckets(%edx), %eax // eax = bucket = buckets+offset
|
||||
cmpl cached_sel(%eax), %ecx // if (bucket->sel != SEL)
|
||||
jne 1f // scan more
|
||||
// The `jne` above sets flags for CacheHit
|
||||
CacheHit $0, $1 // call or return imp
|
||||
|
||||
1:
|
||||
// loop
|
||||
cmpl $$1, (%eax)
|
||||
cmpl $$1, cached_sel(%eax)
|
||||
jbe 3f // if (bucket->sel <= 1) wrap or miss
|
||||
|
||||
addl $$8, %eax // bucket++
|
||||
2:
|
||||
cmpl (%eax), %ecx // if (bucket->sel != sel)
|
||||
cmpl cached_sel(%eax), %ecx // if (bucket->sel != sel)
|
||||
jne 1b // scan more
|
||||
// The `jne` above sets flags for CacheHit
|
||||
CacheHit $0, $1 // call or return imp
|
||||
@ -332,7 +276,7 @@ LExit$0:
|
||||
// wrap or miss
|
||||
jb LCacheMiss_f // if (bucket->sel < 1) cache miss
|
||||
// wrap
|
||||
movl 4(%eax), %eax // bucket->imp is really first bucket
|
||||
movl cached_imp(%eax), %eax // bucket->imp is really first bucket
|
||||
jmp 2f
|
||||
|
||||
// Clone scanning loop to miss instead of hang when cache is corrupt.
|
||||
@ -340,12 +284,12 @@ LExit$0:
|
||||
|
||||
1:
|
||||
// loop
|
||||
cmpq $$1, (%eax)
|
||||
cmpl $$1, cached_sel(%eax)
|
||||
jbe 3f // if (bucket->sel <= 1) wrap or miss
|
||||
|
||||
addl $$8, %eax // bucket++
|
||||
2:
|
||||
cmpl (%eax), %ecx // if (bucket->sel != sel)
|
||||
cmpl cached_sel(%eax), %ecx // if (bucket->sel != sel)
|
||||
jne 1b // scan more
|
||||
// The `jne` above sets flags for CacheHit
|
||||
CacheHit $0, $1 // call or return imp
|
||||
@ -388,10 +332,12 @@ LExit$0:
|
||||
movdqa %xmm1, 2*16(%esp)
|
||||
movdqa %xmm0, 1*16(%esp)
|
||||
|
||||
// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
|
||||
movl $$3, 12(%esp) // LOOKUP_INITIALIZE | LOOKUP_RESOLVER
|
||||
movl %edx, 8(%esp) // class
|
||||
movl %ecx, 4(%esp) // selector
|
||||
movl %eax, 0(%esp) // receiver
|
||||
call __class_lookupMethodAndLoadCache3
|
||||
call _lookUpImpOrForward
|
||||
|
||||
// imp in eax
|
||||
|
||||
@ -401,9 +347,9 @@ LExit$0:
|
||||
movdqa 1*16(%esp), %xmm0
|
||||
|
||||
.if $0 == NORMAL
|
||||
cmp %eax, %eax // set eq for nonstret forwarding
|
||||
.else
|
||||
test %eax, %eax // set ne for stret forwarding
|
||||
.else
|
||||
cmp %eax, %eax // set eq for nonstret forwarding
|
||||
.endif
|
||||
|
||||
leave
|
||||
@ -482,15 +428,12 @@ LNilTestSlow:
|
||||
|
||||
.if $0 == NORMAL
|
||||
ZeroReturn
|
||||
MESSENGER_END_NIL
|
||||
ret
|
||||
.elseif $0 == FPRET
|
||||
ZeroReturnFPRET
|
||||
MESSENGER_END_NIL
|
||||
ret
|
||||
.elseif $0 == STRET
|
||||
ZeroReturnSTRET
|
||||
MESSENGER_END_NIL
|
||||
ret $$4
|
||||
.else
|
||||
.abort oops
|
||||
@ -553,7 +496,6 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSend
|
||||
UNWIND _objc_msgSend, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
movl selector(%esp), %ecx
|
||||
movl self(%esp), %eax
|
||||
@ -567,7 +509,6 @@ LCacheMiss:
|
||||
|
||||
LCacheMiss:
|
||||
// isa still in edx
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSend
|
||||
@ -596,13 +537,11 @@ LCacheMiss:
|
||||
/********************************************************************
|
||||
*
|
||||
* id objc_msgSendSuper(struct objc_super *super, SEL _cmd, ...);
|
||||
* IMP objc_msgLookupSuper(struct objc_super *super, SEL _cmd, ...);
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
ENTRY _objc_msgSendSuper
|
||||
UNWIND _objc_msgSendSuper, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
movl selector(%esp), %ecx
|
||||
movl super(%esp), %eax // struct objc_super
|
||||
@ -613,30 +552,11 @@ LCacheMiss:
|
||||
|
||||
LCacheMiss:
|
||||
// class still in edx
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper
|
||||
|
||||
|
||||
|
||||
ENTRY _objc_msgLookupSuper
|
||||
UNWIND _objc_msgLookupSuper, NoFrame
|
||||
|
||||
movl selector(%esp), %ecx
|
||||
movl super(%esp), %eax // struct objc_super
|
||||
movl class(%eax), %edx // struct objc_super->class
|
||||
movl receiver(%eax), %eax // struct objc_super->receiver
|
||||
movl %eax, super(%esp) // replace super arg with receiver
|
||||
CacheLookup NORMAL, LOOKUP // returns IMP on success
|
||||
|
||||
LCacheMiss:
|
||||
// class still in edx
|
||||
jmp __objc_msgLookup_uncached
|
||||
|
||||
END_ENTRY _objc_msgLookupSuper
|
||||
|
||||
|
||||
/********************************************************************
|
||||
*
|
||||
* id objc_msgSendSuper2(struct objc_super *super, SEL _cmd, ...);
|
||||
@ -646,7 +566,6 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSendSuper2
|
||||
UNWIND _objc_msgSendSuper2, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
movl selector(%esp), %ecx
|
||||
movl super(%esp), %eax // struct objc_super
|
||||
@ -658,7 +577,6 @@ LCacheMiss:
|
||||
|
||||
LCacheMiss:
|
||||
// class still in edx
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper2
|
||||
@ -691,7 +609,6 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSend_fpret
|
||||
UNWIND _objc_msgSend_fpret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
movl selector(%esp), %ecx
|
||||
movl self(%esp), %eax
|
||||
@ -705,7 +622,6 @@ LCacheMiss:
|
||||
|
||||
LCacheMiss:
|
||||
// class still in edx
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSend_fpret
|
||||
@ -740,7 +656,6 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSend_stret
|
||||
UNWIND _objc_msgSend_stret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
movl selector_stret(%esp), %ecx
|
||||
movl self_stret(%esp), %eax
|
||||
@ -754,7 +669,6 @@ LCacheMiss:
|
||||
|
||||
LCacheMiss:
|
||||
// class still in edx
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_stret_uncached
|
||||
|
||||
END_ENTRY _objc_msgSend_stret
|
||||
@ -783,13 +697,11 @@ LCacheMiss:
|
||||
/********************************************************************
|
||||
*
|
||||
* void objc_msgSendSuper_stret(void *st_addr, struct objc_super *super, SEL _cmd, ...);
|
||||
* IMP objc_msgLookupSuper_stret(void *st_addr, struct objc_super *super, SEL _cmd, ...);
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
ENTRY _objc_msgSendSuper_stret
|
||||
UNWIND _objc_msgSendSuper_stret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
movl selector_stret(%esp), %ecx
|
||||
movl super_stret(%esp), %eax // struct objc_super
|
||||
@ -800,29 +712,11 @@ LCacheMiss:
|
||||
|
||||
LCacheMiss:
|
||||
// class still in edx
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_stret_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper_stret
|
||||
|
||||
|
||||
ENTRY _objc_msgLookupSuper_stret
|
||||
UNWIND _objc_msgLookupSuper_stret, NoFrame
|
||||
|
||||
movl selector_stret(%esp), %ecx
|
||||
movl super_stret(%esp), %eax // struct objc_super
|
||||
movl class(%eax), %edx // struct objc_super->class
|
||||
movl receiver(%eax), %eax // struct objc_super->receiver
|
||||
movl %eax, super_stret(%esp) // replace super arg with receiver
|
||||
CacheLookup STRET, LOOKUP // returns IMP on success
|
||||
|
||||
LCacheMiss:
|
||||
// class still in edx
|
||||
jmp __objc_msgLookup_stret_uncached
|
||||
|
||||
END_ENTRY _objc_msgLookupSuper_stret
|
||||
|
||||
|
||||
/********************************************************************
|
||||
*
|
||||
* void objc_msgSendSuper2_stret(void *st_addr, struct objc_super *super, SEL _cmd, ...);
|
||||
@ -832,7 +726,6 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSendSuper2_stret
|
||||
UNWIND _objc_msgSendSuper2_stret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
movl selector_stret(%esp), %ecx
|
||||
movl super_stret(%esp), %eax // struct objc_super
|
||||
@ -845,7 +738,6 @@ LCacheMiss:
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
// class still in edx
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_stret_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper2_stret
|
||||
@ -958,11 +850,7 @@ L_forward_stret_handler:
|
||||
// THIS IS NOT A CALLABLE C FUNCTION
|
||||
// Out-of-band condition register is NE for stret, EQ otherwise.
|
||||
|
||||
MESSENGER_START
|
||||
nop
|
||||
MESSENGER_END_SLOW
|
||||
|
||||
jne __objc_msgForward_stret
|
||||
je __objc_msgForward_stret
|
||||
jmp __objc_msgForward
|
||||
|
||||
END_ENTRY _objc_msgForward_impcache
|
||||
|
@ -22,7 +22,7 @@
|
||||
*/
|
||||
|
||||
#include <TargetConditionals.h>
|
||||
#if __x86_64__ && TARGET_OS_SIMULATOR
|
||||
#if __x86_64__ && TARGET_OS_SIMULATOR && !TARGET_OS_IOSMAC
|
||||
|
||||
/********************************************************************
|
||||
********************************************************************
|
||||
@ -34,105 +34,37 @@
|
||||
|
||||
.data
|
||||
|
||||
// _objc_entryPoints and _objc_exitPoints are used by objc
|
||||
// _objc_restartableRanges is used by method dispatch
|
||||
// to get the critical regions for which method caches
|
||||
// cannot be garbage collected.
|
||||
|
||||
.align 4
|
||||
.private_extern _objc_entryPoints
|
||||
_objc_entryPoints:
|
||||
.quad _cache_getImp
|
||||
.quad _objc_msgSend
|
||||
.quad _objc_msgSend_fpret
|
||||
.quad _objc_msgSend_fp2ret
|
||||
.quad _objc_msgSend_stret
|
||||
.quad _objc_msgSendSuper
|
||||
.quad _objc_msgSendSuper_stret
|
||||
.quad _objc_msgSendSuper2
|
||||
.quad _objc_msgSendSuper2_stret
|
||||
.quad _objc_msgLookup
|
||||
.quad _objc_msgLookup_fpret
|
||||
.quad _objc_msgLookup_fp2ret
|
||||
.quad _objc_msgLookup_stret
|
||||
.quad _objc_msgLookupSuper2
|
||||
.quad _objc_msgLookupSuper2_stret
|
||||
.quad 0
|
||||
|
||||
.private_extern _objc_exitPoints
|
||||
_objc_exitPoints:
|
||||
.quad LExit_cache_getImp
|
||||
.quad LExit_objc_msgSend
|
||||
.quad LExit_objc_msgSend_fpret
|
||||
.quad LExit_objc_msgSend_fp2ret
|
||||
.quad LExit_objc_msgSend_stret
|
||||
.quad LExit_objc_msgSendSuper
|
||||
.quad LExit_objc_msgSendSuper_stret
|
||||
.quad LExit_objc_msgSendSuper2
|
||||
.quad LExit_objc_msgSendSuper2_stret
|
||||
.quad LExit_objc_msgLookup
|
||||
.quad LExit_objc_msgLookup_fpret
|
||||
.quad LExit_objc_msgLookup_fp2ret
|
||||
.quad LExit_objc_msgLookup_stret
|
||||
.quad LExit_objc_msgLookupSuper2
|
||||
.quad LExit_objc_msgLookupSuper2_stret
|
||||
.quad 0
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* List every exit insn from every messenger for debugger use.
|
||||
* Format:
|
||||
* (
|
||||
* 1 word instruction's address
|
||||
* 1 word type (ENTER or FAST_EXIT or SLOW_EXIT or NIL_EXIT)
|
||||
* )
|
||||
* 1 word zero
|
||||
*
|
||||
* ENTER is the start of a dispatcher
|
||||
* FAST_EXIT is method dispatch
|
||||
* SLOW_EXIT is uncached method lookup
|
||||
* NIL_EXIT is returning zero from a message sent to nil
|
||||
* These must match objc-gdb.h.
|
||||
********************************************************************/
|
||||
|
||||
#define ENTER 1
|
||||
#define FAST_EXIT 2
|
||||
#define SLOW_EXIT 3
|
||||
#define NIL_EXIT 4
|
||||
|
||||
.section __DATA,__objc_msg_break
|
||||
.globl _gdb_objc_messenger_breakpoints
|
||||
_gdb_objc_messenger_breakpoints:
|
||||
// contents populated by the macros below
|
||||
|
||||
.macro MESSENGER_START
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.quad 4b
|
||||
.quad ENTER
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_FAST
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.quad 4b
|
||||
.quad FAST_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_SLOW
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.quad 4b
|
||||
.quad SLOW_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_NIL
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.quad 4b
|
||||
.quad NIL_EXIT
|
||||
.text
|
||||
.macro RestartableEntry
|
||||
.quad $0
|
||||
.short LExit$0 - $0
|
||||
.short 0xffff // The simulator doesn't support kernel based recovery
|
||||
.long 0
|
||||
.endmacro
|
||||
|
||||
.align 4
|
||||
.private_extern _objc_restartableRanges
|
||||
_objc_restartableRanges:
|
||||
RestartableEntry _cache_getImp
|
||||
RestartableEntry _objc_msgSend
|
||||
RestartableEntry _objc_msgSend_fpret
|
||||
RestartableEntry _objc_msgSend_fp2ret
|
||||
RestartableEntry _objc_msgSend_stret
|
||||
RestartableEntry _objc_msgSendSuper
|
||||
RestartableEntry _objc_msgSendSuper_stret
|
||||
RestartableEntry _objc_msgSendSuper2
|
||||
RestartableEntry _objc_msgSendSuper2_stret
|
||||
RestartableEntry _objc_msgLookup
|
||||
RestartableEntry _objc_msgLookup_fpret
|
||||
RestartableEntry _objc_msgLookup_fp2ret
|
||||
RestartableEntry _objc_msgLookup_stret
|
||||
RestartableEntry _objc_msgLookupSuper2
|
||||
RestartableEntry _objc_msgLookupSuper2_stret
|
||||
.fill 16, 1, 0
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Recommended multi-byte NOP instructions
|
||||
@ -218,6 +150,10 @@ _gdb_objc_messenger_breakpoints:
|
||||
#define method_name 0
|
||||
#define method_imp 16
|
||||
|
||||
// Method cache
|
||||
#define cached_sel 0
|
||||
#define cached_imp 8
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -297,29 +233,30 @@ LExit$0:
|
||||
|
||||
.macro CacheHit
|
||||
|
||||
// CacheHit must always be preceded by a not-taken `jne` instruction
|
||||
// in order to set the correct flags for _objc_msgForward_impcache.
|
||||
|
||||
// r11 = found bucket
|
||||
|
||||
.if $1 == GETIMP
|
||||
movq 8(%r11), %rax // return imp
|
||||
ret
|
||||
movq cached_imp(%r11), %rax // return imp
|
||||
cmpq $$0, %rax
|
||||
jz 9f // don't xor a nil imp
|
||||
xorq %r10, %rax // xor the isa with the imp
|
||||
9: ret
|
||||
|
||||
.else
|
||||
|
||||
.if $0 != STRET
|
||||
// eq already set for forwarding by `jne`
|
||||
.else
|
||||
test %r11, %r11 // set ne for stret forwarding
|
||||
.endif
|
||||
|
||||
.if $1 == CALL
|
||||
MESSENGER_END_FAST
|
||||
jmp *8(%r11) // call imp
|
||||
|
||||
movq cached_imp(%r11), %r11 // load imp
|
||||
xorq %r10, %r11 // xor imp and isa
|
||||
.if $0 != STRET
|
||||
// ne already set for forwarding by `xor`
|
||||
.else
|
||||
cmp %r11, %r11 // set eq for stret forwarding
|
||||
.endif
|
||||
jmp *%r11 // call imp
|
||||
|
||||
.elseif $1 == LOOKUP
|
||||
movq 8(%r11), %r11 // return imp
|
||||
movq cached_imp(%r11), %r11
|
||||
xorq %r10, %r11 // return imp ^ isa
|
||||
ret
|
||||
|
||||
.else
|
||||
@ -342,35 +279,33 @@ LExit$0:
|
||||
addq 16(%r10), %r11 // r11 = class->cache.buckets + offset
|
||||
|
||||
.if $0 != STRET
|
||||
cmpq (%r11), %a2 // if (bucket->sel != _cmd)
|
||||
cmpq cached_sel(%r11), %a2 // if (bucket->sel != _cmd)
|
||||
.else
|
||||
cmpq (%r11), %a3 // if (bucket->sel != _cmd)
|
||||
cmpq cached_sel(%r11), %a3 // if (bucket->sel != _cmd)
|
||||
.endif
|
||||
jne 1f // scan more
|
||||
// CacheHit must always be preceded by a not-taken `jne` instruction
|
||||
CacheHit $0, $1 // call or return imp
|
||||
|
||||
1:
|
||||
// loop
|
||||
cmpq $$1, (%r11)
|
||||
cmpq $$1, cached_sel(%r11)
|
||||
jbe 3f // if (bucket->sel <= 1) wrap or miss
|
||||
|
||||
addq $$16, %r11 // bucket++
|
||||
2:
|
||||
.if $0 != STRET
|
||||
cmpq (%r11), %a2 // if (bucket->sel != _cmd)
|
||||
cmpq cached_sel(%r11), %a2 // if (bucket->sel != _cmd)
|
||||
.else
|
||||
cmpq (%r11), %a3 // if (bucket->sel != _cmd)
|
||||
cmpq cached_sel(%r11), %a3 // if (bucket->sel != _cmd)
|
||||
.endif
|
||||
jne 1b // scan more
|
||||
// CacheHit must always be preceded by a not-taken `jne` instruction
|
||||
CacheHit $0, $1 // call or return imp
|
||||
|
||||
3:
|
||||
// wrap or miss
|
||||
jb LCacheMiss_f // if (bucket->sel < 1) cache miss
|
||||
// wrap
|
||||
movq 8(%r11), %r11 // bucket->imp is really first bucket
|
||||
movq cached_imp(%r11), %r11 // bucket->imp is really first bucket
|
||||
jmp 2f
|
||||
|
||||
// Clone scanning loop to miss instead of hang when cache is corrupt.
|
||||
@ -378,18 +313,17 @@ LExit$0:
|
||||
|
||||
1:
|
||||
// loop
|
||||
cmpq $$1, (%r11)
|
||||
cmpq $$1, cached_sel(%r11)
|
||||
jbe 3f // if (bucket->sel <= 1) wrap or miss
|
||||
|
||||
addq $$16, %r11 // bucket++
|
||||
2:
|
||||
.if $0 != STRET
|
||||
cmpq (%r11), %a2 // if (bucket->sel != _cmd)
|
||||
cmpq cached_sel(%r11), %a2 // if (bucket->sel != _cmd)
|
||||
.else
|
||||
cmpq (%r11), %a3 // if (bucket->sel != _cmd)
|
||||
cmpq cached_sel(%r11), %a3 // if (bucket->sel != _cmd)
|
||||
.endif
|
||||
jne 1b // scan more
|
||||
// CacheHit must always be preceded by a not-taken `jne` instruction
|
||||
CacheHit $0, $1 // call or return imp
|
||||
|
||||
3:
|
||||
@ -434,8 +368,7 @@ LExit$0:
|
||||
push %a6
|
||||
movdqa %xmm7, -0x10(%rbp)
|
||||
|
||||
// _class_lookupMethodAndLoadCache3(receiver, selector, class)
|
||||
|
||||
// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
|
||||
.if $0 == NORMAL
|
||||
// receiver already in a1
|
||||
// selector already in a2
|
||||
@ -444,7 +377,8 @@ LExit$0:
|
||||
movq %a3, %a2
|
||||
.endif
|
||||
movq %r10, %a3
|
||||
call __class_lookupMethodAndLoadCache3
|
||||
movl $$3, %a4d
|
||||
call _lookUpImpOrForward
|
||||
|
||||
// IMP is now in %rax
|
||||
movq %rax, %r11
|
||||
@ -466,9 +400,9 @@ LExit$0:
|
||||
movdqa -0x10(%rbp), %xmm7
|
||||
|
||||
.if $0 == NORMAL
|
||||
cmp %r11, %r11 // set eq for nonstret forwarding
|
||||
.else
|
||||
test %r11, %r11 // set ne for stret forwarding
|
||||
.else
|
||||
cmp %r11, %r11 // set eq for nonstret forwarding
|
||||
.endif
|
||||
|
||||
leave
|
||||
@ -567,14 +501,13 @@ LNilOrTagged:
|
||||
.else
|
||||
movq %a2, %r11
|
||||
.endif
|
||||
shrq $$60, %r11
|
||||
cmpl $$0xf, %r11d
|
||||
je 1f
|
||||
// basic tagged
|
||||
shrq $$60, %r11
|
||||
leaq _objc_debug_taggedpointer_classes(%rip), %r10
|
||||
movq (%r10, %r11, 8), %r10 // read isa from table
|
||||
jmp LGetIsaDone_b
|
||||
1:
|
||||
leaq _OBJC_CLASS_$___NSUnrecognizedTaggedPointer(%rip), %r11
|
||||
cmp %r10, %r11
|
||||
jne LGetIsaDone_b
|
||||
// ext tagged
|
||||
.if $0 != STRET
|
||||
movq %a1, %r11
|
||||
@ -602,7 +535,6 @@ LNil:
|
||||
.else
|
||||
.abort oops
|
||||
.endif
|
||||
MESSENGER_END_NIL
|
||||
ret
|
||||
.endmacro
|
||||
|
||||
@ -671,7 +603,6 @@ _objc_debug_taggedpointer_ext_classes:
|
||||
|
||||
ENTRY _objc_msgSend
|
||||
UNWIND _objc_msgSend, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
GetIsaCheckNil NORMAL // r10 = self->isa, or return zero
|
||||
CacheLookup NORMAL, CALL // calls IMP on success
|
||||
@ -682,7 +613,6 @@ _objc_debug_taggedpointer_ext_classes:
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
// isa still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSend
|
||||
@ -728,7 +658,6 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSendSuper
|
||||
UNWIND _objc_msgSendSuper, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
// search the cache (objc_super in %a1)
|
||||
movq class(%a1), %r10 // class = objc_super->class
|
||||
@ -738,7 +667,6 @@ LCacheMiss:
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
// class still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper
|
||||
@ -750,7 +678,6 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSendSuper2
|
||||
UNWIND _objc_msgSendSuper2, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
// objc_super->class is superclass of class to search
|
||||
|
||||
@ -763,7 +690,6 @@ LCacheMiss:
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
// superclass still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper2
|
||||
@ -808,7 +734,6 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSend_fpret
|
||||
UNWIND _objc_msgSend_fpret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
GetIsaCheckNil FPRET // r10 = self->isa, or return zero
|
||||
CacheLookup FPRET, CALL // calls IMP on success
|
||||
@ -819,7 +744,6 @@ LCacheMiss:
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
// isa still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSend_fpret
|
||||
@ -862,7 +786,6 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSend_fp2ret
|
||||
UNWIND _objc_msgSend_fp2ret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
GetIsaCheckNil FP2RET // r10 = self->isa, or return zero
|
||||
CacheLookup FP2RET, CALL // calls IMP on success
|
||||
@ -873,7 +796,6 @@ LCacheMiss:
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
// isa still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSend_fp2ret
|
||||
@ -922,7 +844,6 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSend_stret
|
||||
UNWIND _objc_msgSend_stret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
GetIsaCheckNil STRET // r10 = self->isa, or return zero
|
||||
CacheLookup STRET, CALL // calls IMP on success
|
||||
@ -933,7 +854,6 @@ LCacheMiss:
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
// isa still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_stret_uncached
|
||||
|
||||
END_ENTRY _objc_msgSend_stret
|
||||
@ -988,7 +908,6 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSendSuper_stret
|
||||
UNWIND _objc_msgSendSuper_stret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
// search the cache (objc_super in %a2)
|
||||
movq class(%a2), %r10 // class = objc_super->class
|
||||
@ -998,7 +917,6 @@ LCacheMiss:
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
// class still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_stret_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper_stret
|
||||
@ -1010,7 +928,6 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSendSuper2_stret
|
||||
UNWIND _objc_msgSendSuper2_stret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
// search the cache (objc_super in %a2)
|
||||
movq class(%a2), %r10 // class = objc_super->class
|
||||
@ -1021,7 +938,6 @@ LCacheMiss:
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
// superclass still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_stret_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper2_stret
|
||||
@ -1132,11 +1048,7 @@ LCacheMiss:
|
||||
// THIS IS NOT A CALLABLE C FUNCTION
|
||||
// Out-of-band condition register is NE for stret, EQ otherwise.
|
||||
|
||||
MESSENGER_START
|
||||
nop
|
||||
MESSENGER_END_SLOW
|
||||
|
||||
jne __objc_msgForward_stret
|
||||
je __objc_msgForward_stret
|
||||
jmp __objc_msgForward
|
||||
|
||||
END_ENTRY __objc_msgForward_impcache
|
||||
|
@ -175,10 +175,11 @@ MISS:
|
||||
mov eax, isa[edx]
|
||||
|
||||
// MethodTableLookup WORD_RETURN, MSG_SEND
|
||||
push $3
|
||||
push eax
|
||||
push ecx
|
||||
push edx
|
||||
call _class_lookupMethodAndLoadCache3
|
||||
call lookUpImpOrFoward
|
||||
|
||||
mov edx, kFwdMsgSend
|
||||
leave
|
||||
@ -244,10 +245,11 @@ MISS:
|
||||
mov eax, isa[edx]
|
||||
|
||||
// MethodTableLookup WORD_RETURN, MSG_SEND
|
||||
push $3
|
||||
push eax
|
||||
push ecx
|
||||
push edx
|
||||
call _class_lookupMethodAndLoadCache3
|
||||
call lookUpImpOrFoward
|
||||
|
||||
mov edx, kFwdMsgSend
|
||||
leave
|
||||
@ -313,10 +315,11 @@ MISS:
|
||||
mov eax, super_class[eax]
|
||||
|
||||
// MethodTableLookup WORD_RETURN, MSG_SENDSUPER
|
||||
push $3
|
||||
push eax
|
||||
push ecx
|
||||
push edx
|
||||
call _class_lookupMethodAndLoadCache3
|
||||
call lookUpImpOrFoward
|
||||
|
||||
mov edx, kFwdMsgSend
|
||||
leave
|
||||
@ -375,10 +378,11 @@ MISS:
|
||||
mov eax, isa[edx]
|
||||
|
||||
// MethodTableLookup WORD_RETURN, MSG_SEND
|
||||
push $3
|
||||
push eax
|
||||
push ecx
|
||||
push edx
|
||||
call _class_lookupMethodAndLoadCache3
|
||||
call lookUpImpOrFoward
|
||||
|
||||
mov edx, kFwdMsgSendStret
|
||||
leave
|
||||
@ -445,10 +449,11 @@ MISS:
|
||||
mov eax, super_class[eax]
|
||||
|
||||
// MethodTableLookup WORD_RETURN, MSG_SENDSUPER
|
||||
push $3
|
||||
push eax
|
||||
push ecx
|
||||
push edx
|
||||
call _class_lookupMethodAndLoadCache3
|
||||
call lookUpImpOrFoward
|
||||
|
||||
mov edx, kFwdMsgSendStret
|
||||
leave
|
||||
|
@ -22,7 +22,9 @@
|
||||
*/
|
||||
|
||||
#include <TargetConditionals.h>
|
||||
#if __x86_64__ && !TARGET_OS_SIMULATOR
|
||||
#if __x86_64__ && !(TARGET_OS_SIMULATOR && !TARGET_OS_IOSMAC)
|
||||
|
||||
#include "isa.h"
|
||||
|
||||
/********************************************************************
|
||||
********************************************************************
|
||||
@ -34,105 +36,37 @@
|
||||
|
||||
.data
|
||||
|
||||
// _objc_entryPoints and _objc_exitPoints are used by objc
|
||||
// _objc_restartableRanges is used by method dispatch
|
||||
// to get the critical regions for which method caches
|
||||
// cannot be garbage collected.
|
||||
|
||||
.align 4
|
||||
.private_extern _objc_entryPoints
|
||||
_objc_entryPoints:
|
||||
.quad _cache_getImp
|
||||
.quad _objc_msgSend
|
||||
.quad _objc_msgSend_fpret
|
||||
.quad _objc_msgSend_fp2ret
|
||||
.quad _objc_msgSend_stret
|
||||
.quad _objc_msgSendSuper
|
||||
.quad _objc_msgSendSuper_stret
|
||||
.quad _objc_msgSendSuper2
|
||||
.quad _objc_msgSendSuper2_stret
|
||||
.quad _objc_msgLookup
|
||||
.quad _objc_msgLookup_fpret
|
||||
.quad _objc_msgLookup_fp2ret
|
||||
.quad _objc_msgLookup_stret
|
||||
.quad _objc_msgLookupSuper2
|
||||
.quad _objc_msgLookupSuper2_stret
|
||||
.quad 0
|
||||
|
||||
.private_extern _objc_exitPoints
|
||||
_objc_exitPoints:
|
||||
.quad LExit_cache_getImp
|
||||
.quad LExit_objc_msgSend
|
||||
.quad LExit_objc_msgSend_fpret
|
||||
.quad LExit_objc_msgSend_fp2ret
|
||||
.quad LExit_objc_msgSend_stret
|
||||
.quad LExit_objc_msgSendSuper
|
||||
.quad LExit_objc_msgSendSuper_stret
|
||||
.quad LExit_objc_msgSendSuper2
|
||||
.quad LExit_objc_msgSendSuper2_stret
|
||||
.quad LExit_objc_msgLookup
|
||||
.quad LExit_objc_msgLookup_fpret
|
||||
.quad LExit_objc_msgLookup_fp2ret
|
||||
.quad LExit_objc_msgLookup_stret
|
||||
.quad LExit_objc_msgLookupSuper2
|
||||
.quad LExit_objc_msgLookupSuper2_stret
|
||||
.quad 0
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* List every exit insn from every messenger for debugger use.
|
||||
* Format:
|
||||
* (
|
||||
* 1 word instruction's address
|
||||
* 1 word type (ENTER or FAST_EXIT or SLOW_EXIT or NIL_EXIT)
|
||||
* )
|
||||
* 1 word zero
|
||||
*
|
||||
* ENTER is the start of a dispatcher
|
||||
* FAST_EXIT is method dispatch
|
||||
* SLOW_EXIT is uncached method lookup
|
||||
* NIL_EXIT is returning zero from a message sent to nil
|
||||
* These must match objc-gdb.h.
|
||||
********************************************************************/
|
||||
|
||||
#define ENTER 1
|
||||
#define FAST_EXIT 2
|
||||
#define SLOW_EXIT 3
|
||||
#define NIL_EXIT 4
|
||||
|
||||
.section __DATA,__objc_msg_break
|
||||
.globl _gdb_objc_messenger_breakpoints
|
||||
_gdb_objc_messenger_breakpoints:
|
||||
// contents populated by the macros below
|
||||
|
||||
.macro MESSENGER_START
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.quad 4b
|
||||
.quad ENTER
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_FAST
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.quad 4b
|
||||
.quad FAST_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_SLOW
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.quad 4b
|
||||
.quad SLOW_EXIT
|
||||
.text
|
||||
.endmacro
|
||||
.macro MESSENGER_END_NIL
|
||||
4:
|
||||
.section __DATA,__objc_msg_break
|
||||
.quad 4b
|
||||
.quad NIL_EXIT
|
||||
.text
|
||||
.macro RestartableEntry
|
||||
.quad LLookupStart$0
|
||||
.short LLookupEnd$0 - LLookupStart$0
|
||||
.short LCacheMiss$0 - LLookupStart$0
|
||||
.long 0
|
||||
.endmacro
|
||||
|
||||
.align 4
|
||||
.private_extern _objc_restartableRanges
|
||||
_objc_restartableRanges:
|
||||
RestartableEntry _cache_getImp
|
||||
RestartableEntry _objc_msgSend
|
||||
RestartableEntry _objc_msgSend_fpret
|
||||
RestartableEntry _objc_msgSend_fp2ret
|
||||
RestartableEntry _objc_msgSend_stret
|
||||
RestartableEntry _objc_msgSendSuper
|
||||
RestartableEntry _objc_msgSendSuper_stret
|
||||
RestartableEntry _objc_msgSendSuper2
|
||||
RestartableEntry _objc_msgSendSuper2_stret
|
||||
RestartableEntry _objc_msgLookup
|
||||
RestartableEntry _objc_msgLookup_fpret
|
||||
RestartableEntry _objc_msgLookup_fp2ret
|
||||
RestartableEntry _objc_msgLookup_stret
|
||||
RestartableEntry _objc_msgLookupSuper2
|
||||
RestartableEntry _objc_msgLookupSuper2_stret
|
||||
.fill 16, 1, 0
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Recommended multi-byte NOP instructions
|
||||
@ -181,9 +115,6 @@ _gdb_objc_messenger_breakpoints:
|
||||
* DO NOT USE THESE LABELS ELSEWHERE
|
||||
* Reserved labels: 6: 7: 8: 9:
|
||||
********************************************************************/
|
||||
#define LCacheMiss 6
|
||||
#define LCacheMiss_f 6f
|
||||
#define LCacheMiss_b 6b
|
||||
#define LNilTestSlow 7
|
||||
#define LNilTestSlow_f 7f
|
||||
#define LNilTestSlow_b 7b
|
||||
@ -225,6 +156,10 @@ _gdb_objc_messenger_breakpoints:
|
||||
#define method_name 0
|
||||
#define method_imp 16
|
||||
|
||||
// Method cache
|
||||
#define cached_sel 0
|
||||
#define cached_imp 8
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -285,10 +220,15 @@ LExit$0:
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CacheLookup return-type, caller
|
||||
// CacheLookup return-type, caller, function
|
||||
//
|
||||
// Locate the implementation for a class in a selector's method cache.
|
||||
//
|
||||
// When this is used in a function that doesn't hold the runtime lock,
|
||||
// this represents the critical section that may access dead memory.
|
||||
// If the kernel causes one of these functions to go down the recovery
|
||||
// path, we pretend the lookup failed by jumping the JumpMiss branch.
|
||||
//
|
||||
// Takes:
|
||||
// $0 = NORMAL, FPRET, FP2RET, STRET
|
||||
// $1 = CALL, LOOKUP, GETIMP
|
||||
@ -304,29 +244,30 @@ LExit$0:
|
||||
|
||||
.macro CacheHit
|
||||
|
||||
// CacheHit must always be preceded by a not-taken `jne` instruction
|
||||
// in order to set the correct flags for _objc_msgForward_impcache.
|
||||
|
||||
// r11 = found bucket
|
||||
|
||||
.if $1 == GETIMP
|
||||
movq 8(%r11), %rax // return imp
|
||||
ret
|
||||
movq cached_imp(%r11), %rax // return imp
|
||||
cmpq $$0, %rax
|
||||
jz 9f // don't xor a nil imp
|
||||
xorq %r10, %rax // xor the isa with the imp
|
||||
9: ret
|
||||
|
||||
.else
|
||||
|
||||
.if $0 != STRET
|
||||
// eq already set for forwarding by `jne`
|
||||
.else
|
||||
test %r11, %r11 // set ne for stret forwarding
|
||||
.endif
|
||||
|
||||
.if $1 == CALL
|
||||
MESSENGER_END_FAST
|
||||
jmp *8(%r11) // call imp
|
||||
|
||||
movq cached_imp(%r11), %r11 // load imp
|
||||
xorq %r10, %r11 // xor imp and isa
|
||||
.if $0 != STRET
|
||||
// ne already set for forwarding by `xor`
|
||||
.else
|
||||
cmp %r11, %r11 // set eq for stret forwarding
|
||||
.endif
|
||||
jmp *%r11 // call imp
|
||||
|
||||
.elseif $1 == LOOKUP
|
||||
movq 8(%r11), %r11 // return imp
|
||||
movq cached_imp(%r11), %r11
|
||||
xorq %r10, %r11 // return imp ^ isa
|
||||
ret
|
||||
|
||||
.else
|
||||
@ -339,6 +280,29 @@ LExit$0:
|
||||
|
||||
|
||||
.macro CacheLookup
|
||||
//
|
||||
// Restart protocol:
|
||||
//
|
||||
// As soon as we're past the LLookupStart$1 label we may have loaded
|
||||
// an invalid cache pointer or mask.
|
||||
//
|
||||
// When task_restartable_ranges_synchronize() is called,
|
||||
// (or when a signal hits us) before we're past LLookupEnd$1,
|
||||
// then our PC will be reset to LCacheMiss$1 which forcefully
|
||||
// jumps to the cache-miss codepath which have the following
|
||||
// requirements:
|
||||
//
|
||||
// GETIMP:
|
||||
// The cache-miss is just returning NULL (setting %rax to 0)
|
||||
//
|
||||
// NORMAL and STRET:
|
||||
// - a1 or a2 (STRET) contains the receiver
|
||||
// - a2 or a3 (STRET) contains the selector
|
||||
// - r10 contains the isa
|
||||
// - other registers are set as per calling conventions
|
||||
//
|
||||
LLookupStart$2:
|
||||
|
||||
.if $0 != STRET
|
||||
movq %a2, %r11 // r11 = _cmd
|
||||
.else
|
||||
@ -349,35 +313,33 @@ LExit$0:
|
||||
addq 16(%r10), %r11 // r11 = class->cache.buckets + offset
|
||||
|
||||
.if $0 != STRET
|
||||
cmpq (%r11), %a2 // if (bucket->sel != _cmd)
|
||||
cmpq cached_sel(%r11), %a2 // if (bucket->sel != _cmd)
|
||||
.else
|
||||
cmpq (%r11), %a3 // if (bucket->sel != _cmd)
|
||||
cmpq cached_sel(%r11), %a3 // if (bucket->sel != _cmd)
|
||||
.endif
|
||||
jne 1f // scan more
|
||||
// CacheHit must always be preceded by a not-taken `jne` instruction
|
||||
CacheHit $0, $1 // call or return imp
|
||||
|
||||
1:
|
||||
// loop
|
||||
cmpq $$1, (%r11)
|
||||
cmpq $$1, cached_sel(%r11)
|
||||
jbe 3f // if (bucket->sel <= 1) wrap or miss
|
||||
|
||||
addq $$16, %r11 // bucket++
|
||||
2:
|
||||
.if $0 != STRET
|
||||
cmpq (%r11), %a2 // if (bucket->sel != _cmd)
|
||||
cmpq cached_sel(%r11), %a2 // if (bucket->sel != _cmd)
|
||||
.else
|
||||
cmpq (%r11), %a3 // if (bucket->sel != _cmd)
|
||||
cmpq cached_sel(%r11), %a3 // if (bucket->sel != _cmd)
|
||||
.endif
|
||||
jne 1b // scan more
|
||||
// CacheHit must always be preceded by a not-taken `jne` instruction
|
||||
CacheHit $0, $1 // call or return imp
|
||||
|
||||
3:
|
||||
// wrap or miss
|
||||
jb LCacheMiss_f // if (bucket->sel < 1) cache miss
|
||||
jb LCacheMiss$2 // if (bucket->sel < 1) cache miss
|
||||
// wrap
|
||||
movq 8(%r11), %r11 // bucket->imp is really first bucket
|
||||
movq cached_imp(%r11), %r11 // bucket->imp is really first bucket
|
||||
jmp 2f
|
||||
|
||||
// Clone scanning loop to miss instead of hang when cache is corrupt.
|
||||
@ -385,24 +347,24 @@ LExit$0:
|
||||
|
||||
1:
|
||||
// loop
|
||||
cmpq $$1, (%r11)
|
||||
cmpq $$1, cached_sel(%r11)
|
||||
jbe 3f // if (bucket->sel <= 1) wrap or miss
|
||||
|
||||
addq $$16, %r11 // bucket++
|
||||
2:
|
||||
.if $0 != STRET
|
||||
cmpq (%r11), %a2 // if (bucket->sel != _cmd)
|
||||
cmpq cached_sel(%r11), %a2 // if (bucket->sel != _cmd)
|
||||
.else
|
||||
cmpq (%r11), %a3 // if (bucket->sel != _cmd)
|
||||
cmpq cached_sel(%r11), %a3 // if (bucket->sel != _cmd)
|
||||
.endif
|
||||
jne 1b // scan more
|
||||
// CacheHit must always be preceded by a not-taken `jne` instruction
|
||||
CacheHit $0, $1 // call or return imp
|
||||
|
||||
3:
|
||||
// double wrap or miss
|
||||
jmp LCacheMiss_f
|
||||
jmp LCacheMiss$2
|
||||
|
||||
LLookupEnd$2:
|
||||
.endmacro
|
||||
|
||||
|
||||
@ -441,8 +403,7 @@ LExit$0:
|
||||
push %a6
|
||||
movdqa %xmm7, -0x10(%rbp)
|
||||
|
||||
// _class_lookupMethodAndLoadCache3(receiver, selector, class)
|
||||
|
||||
// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
|
||||
.if $0 == NORMAL
|
||||
// receiver already in a1
|
||||
// selector already in a2
|
||||
@ -451,7 +412,8 @@ LExit$0:
|
||||
movq %a3, %a2
|
||||
.endif
|
||||
movq %r10, %a3
|
||||
call __class_lookupMethodAndLoadCache3
|
||||
movl $$3, %a4d
|
||||
call _lookUpImpOrForward
|
||||
|
||||
// IMP is now in %rax
|
||||
movq %rax, %r11
|
||||
@ -473,9 +435,9 @@ LExit$0:
|
||||
movdqa -0x10(%rbp), %xmm7
|
||||
|
||||
.if $0 == NORMAL
|
||||
cmp %r11, %r11 // set eq for nonstret forwarding
|
||||
test %r11, %r11 // set ne for nonstret forwarding
|
||||
.else
|
||||
test %r11, %r11 // set ne for stret forwarding
|
||||
cmp %r11, %r11 // set eq for stret forwarding
|
||||
.endif
|
||||
|
||||
leave
|
||||
@ -503,13 +465,13 @@ LExit$0:
|
||||
testb $$1, %a1b
|
||||
PN
|
||||
jnz LGetIsaSlow_f
|
||||
movq $$0x00007ffffffffff8, %r10
|
||||
movq $$ ISA_MASK, %r10
|
||||
andq (%a1), %r10
|
||||
.else
|
||||
testb $$1, %a2b
|
||||
PN
|
||||
jnz LGetIsaSlow_f
|
||||
movq $$0x00007ffffffffff8, %r10
|
||||
movq $$ ISA_MASK, %r10
|
||||
andq (%a2), %r10
|
||||
.endif
|
||||
LGetIsaDone:
|
||||
@ -523,13 +485,12 @@ LGetIsaSlow:
|
||||
movl %a2d, %r11d
|
||||
.endif
|
||||
andl $$0xF, %r11d
|
||||
cmp $$0xF, %r11d
|
||||
je 1f
|
||||
// basic tagged
|
||||
leaq _objc_debug_taggedpointer_classes(%rip), %r10
|
||||
movq (%r10, %r11, 8), %r10 // read isa from table
|
||||
jmp LGetIsaDone_b
|
||||
1:
|
||||
leaq _OBJC_CLASS_$___NSUnrecognizedTaggedPointer(%rip), %r11
|
||||
cmp %r10, %r11
|
||||
jne LGetIsaDone_b
|
||||
// extended tagged
|
||||
.if $0 != STRET
|
||||
movl %a1d, %r11d
|
||||
@ -642,7 +603,6 @@ LNilTestSlow:
|
||||
.else
|
||||
.abort oops
|
||||
.endif
|
||||
MESSENGER_END_NIL
|
||||
ret
|
||||
.endmacro
|
||||
|
||||
@ -680,9 +640,10 @@ LNilTestSlow:
|
||||
|
||||
// do lookup
|
||||
movq %a1, %r10 // move class to r10 for CacheLookup
|
||||
CacheLookup NORMAL, GETIMP // returns IMP on success
|
||||
// returns IMP on success
|
||||
CacheLookup NORMAL, GETIMP, _cache_getImp
|
||||
|
||||
LCacheMiss:
|
||||
LCacheMiss_cache_getImp:
|
||||
// cache miss, return nil
|
||||
xorl %eax, %eax
|
||||
ret
|
||||
@ -713,21 +674,20 @@ _objc_debug_taggedpointer_ext_classes:
|
||||
|
||||
ENTRY _objc_msgSend
|
||||
UNWIND _objc_msgSend, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
NilTest NORMAL
|
||||
|
||||
GetIsaFast NORMAL // r10 = self->isa
|
||||
CacheLookup NORMAL, CALL // calls IMP on success
|
||||
// calls IMP on success
|
||||
CacheLookup NORMAL, CALL, _objc_msgSend
|
||||
|
||||
NilTestReturnZero NORMAL
|
||||
|
||||
GetIsaSupport NORMAL
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgSend:
|
||||
// isa still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSend
|
||||
@ -738,14 +698,15 @@ LCacheMiss:
|
||||
NilTest NORMAL
|
||||
|
||||
GetIsaFast NORMAL // r10 = self->isa
|
||||
CacheLookup NORMAL, LOOKUP // returns IMP on success
|
||||
// returns IMP on success
|
||||
CacheLookup NORMAL, LOOKUP, _objc_msgLookup
|
||||
|
||||
NilTestReturnIMP NORMAL
|
||||
|
||||
GetIsaSupport NORMAL
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgLookup:
|
||||
// isa still in r10
|
||||
jmp __objc_msgLookup_uncached
|
||||
|
||||
@ -776,17 +737,16 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSendSuper
|
||||
UNWIND _objc_msgSendSuper, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
// search the cache (objc_super in %a1)
|
||||
movq class(%a1), %r10 // class = objc_super->class
|
||||
movq receiver(%a1), %a1 // load real receiver
|
||||
CacheLookup NORMAL, CALL // calls IMP on success
|
||||
// calls IMP on success
|
||||
CacheLookup NORMAL, CALL, _objc_msgSendSuper
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgSendSuper:
|
||||
// class still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper
|
||||
@ -798,7 +758,6 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSendSuper2
|
||||
UNWIND _objc_msgSendSuper2, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
// objc_super->class is superclass of class to search
|
||||
|
||||
@ -806,12 +765,12 @@ LCacheMiss:
|
||||
movq class(%a1), %r10 // cls = objc_super->class
|
||||
movq receiver(%a1), %a1 // load real receiver
|
||||
movq 8(%r10), %r10 // cls = class->superclass
|
||||
CacheLookup NORMAL, CALL // calls IMP on success
|
||||
// calls IMP on success
|
||||
CacheLookup NORMAL, CALL, _objc_msgSendSuper2
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgSendSuper2:
|
||||
// superclass still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper2
|
||||
@ -825,10 +784,11 @@ LCacheMiss:
|
||||
movq class(%a1), %r10 // cls = objc_super->class
|
||||
movq receiver(%a1), %a1 // load real receiver
|
||||
movq 8(%r10), %r10 // cls = class->superclass
|
||||
CacheLookup NORMAL, LOOKUP // returns IMP on success
|
||||
// returns IMP on success
|
||||
CacheLookup NORMAL, LOOKUP, _objc_msgLookupSuper2
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgLookupSuper2:
|
||||
// superclass still in r10
|
||||
jmp __objc_msgLookup_uncached
|
||||
|
||||
@ -856,21 +816,20 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSend_fpret
|
||||
UNWIND _objc_msgSend_fpret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
NilTest FPRET
|
||||
|
||||
GetIsaFast FPRET // r10 = self->isa
|
||||
CacheLookup FPRET, CALL // calls IMP on success
|
||||
// calls IMP on success
|
||||
CacheLookup FPRET, CALL, _objc_msgSend_fpret
|
||||
|
||||
NilTestReturnZero FPRET
|
||||
|
||||
GetIsaSupport FPRET
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgSend_fpret:
|
||||
// isa still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSend_fpret
|
||||
@ -881,14 +840,15 @@ LCacheMiss:
|
||||
NilTest FPRET
|
||||
|
||||
GetIsaFast FPRET // r10 = self->isa
|
||||
CacheLookup FPRET, LOOKUP // returns IMP on success
|
||||
// returns IMP on success
|
||||
CacheLookup FPRET, LOOKUP, _objc_msgLookup_fpret
|
||||
|
||||
NilTestReturnIMP FPRET
|
||||
|
||||
GetIsaSupport FPRET
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgLookup_fpret:
|
||||
// isa still in r10
|
||||
jmp __objc_msgLookup_uncached
|
||||
|
||||
@ -916,21 +876,20 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSend_fp2ret
|
||||
UNWIND _objc_msgSend_fp2ret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
NilTest FP2RET
|
||||
|
||||
GetIsaFast FP2RET // r10 = self->isa
|
||||
CacheLookup FP2RET, CALL // calls IMP on success
|
||||
// calls IMP on success
|
||||
CacheLookup FP2RET, CALL, _objc_msgSend_fp2ret
|
||||
|
||||
NilTestReturnZero FP2RET
|
||||
|
||||
GetIsaSupport FP2RET
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgSend_fp2ret:
|
||||
// isa still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_uncached
|
||||
|
||||
END_ENTRY _objc_msgSend_fp2ret
|
||||
@ -941,14 +900,15 @@ LCacheMiss:
|
||||
NilTest FP2RET
|
||||
|
||||
GetIsaFast FP2RET // r10 = self->isa
|
||||
CacheLookup FP2RET, LOOKUP // returns IMP on success
|
||||
// returns IMP on success
|
||||
CacheLookup FP2RET, LOOKUP, _objc_msgLookup_fp2ret
|
||||
|
||||
NilTestReturnIMP FP2RET
|
||||
|
||||
GetIsaSupport FP2RET
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgLookup_fp2ret:
|
||||
// isa still in r10
|
||||
jmp __objc_msgLookup_uncached
|
||||
|
||||
@ -982,21 +942,20 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSend_stret
|
||||
UNWIND _objc_msgSend_stret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
NilTest STRET
|
||||
|
||||
GetIsaFast STRET // r10 = self->isa
|
||||
CacheLookup STRET, CALL // calls IMP on success
|
||||
// calls IMP on success
|
||||
CacheLookup STRET, CALL, _objc_msgSend_stret
|
||||
|
||||
NilTestReturnZero STRET
|
||||
|
||||
GetIsaSupport STRET
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgSend_stret:
|
||||
// isa still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_stret_uncached
|
||||
|
||||
END_ENTRY _objc_msgSend_stret
|
||||
@ -1007,14 +966,15 @@ LCacheMiss:
|
||||
NilTest STRET
|
||||
|
||||
GetIsaFast STRET // r10 = self->isa
|
||||
CacheLookup STRET, LOOKUP // returns IMP on success
|
||||
// returns IMP on success
|
||||
CacheLookup STRET, LOOKUP, _objc_msgLookup_stret
|
||||
|
||||
NilTestReturnIMP STRET
|
||||
|
||||
GetIsaSupport STRET
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgLookup_stret:
|
||||
// isa still in r10
|
||||
jmp __objc_msgLookup_stret_uncached
|
||||
|
||||
@ -1054,17 +1014,16 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSendSuper_stret
|
||||
UNWIND _objc_msgSendSuper_stret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
// search the cache (objc_super in %a2)
|
||||
movq class(%a2), %r10 // class = objc_super->class
|
||||
movq receiver(%a2), %a2 // load real receiver
|
||||
CacheLookup STRET, CALL // calls IMP on success
|
||||
// calls IMP on success
|
||||
CacheLookup STRET, CALL, _objc_msgSendSuper_stret
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgSendSuper_stret:
|
||||
// class still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_stret_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper_stret
|
||||
@ -1076,18 +1035,17 @@ LCacheMiss:
|
||||
|
||||
ENTRY _objc_msgSendSuper2_stret
|
||||
UNWIND _objc_msgSendSuper2_stret, NoFrame
|
||||
MESSENGER_START
|
||||
|
||||
// search the cache (objc_super in %a2)
|
||||
movq class(%a2), %r10 // class = objc_super->class
|
||||
movq receiver(%a2), %a2 // load real receiver
|
||||
movq 8(%r10), %r10 // class = class->superclass
|
||||
CacheLookup STRET, CALL // calls IMP on success
|
||||
// calls IMP on success
|
||||
CacheLookup STRET, CALL, _objc_msgSendSuper2_stret
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgSendSuper2_stret:
|
||||
// superclass still in r10
|
||||
MESSENGER_END_SLOW
|
||||
jmp __objc_msgSend_stret_uncached
|
||||
|
||||
END_ENTRY _objc_msgSendSuper2_stret
|
||||
@ -1099,10 +1057,11 @@ LCacheMiss:
|
||||
movq class(%a2), %r10 // class = objc_super->class
|
||||
movq receiver(%a2), %a2 // load real receiver
|
||||
movq 8(%r10), %r10 // class = class->superclass
|
||||
CacheLookup STRET, LOOKUP // returns IMP on success
|
||||
// returns IMP on success
|
||||
CacheLookup STRET, LOOKUP, _objc_msgLookupSuper2_stret
|
||||
|
||||
// cache miss: go search the method lists
|
||||
LCacheMiss:
|
||||
LCacheMiss_objc_msgLookupSuper2_stret:
|
||||
// superclass still in r10
|
||||
jmp __objc_msgLookup_stret_uncached
|
||||
|
||||
@ -1200,12 +1159,8 @@ LCacheMiss:
|
||||
|
||||
// THIS IS NOT A CALLABLE C FUNCTION
|
||||
// Out-of-band condition register is NE for stret, EQ otherwise.
|
||||
|
||||
MESSENGER_START
|
||||
nop
|
||||
MESSENGER_END_SLOW
|
||||
|
||||
jne __objc_msgForward_stret
|
||||
je __objc_msgForward_stret
|
||||
jmp __objc_msgForward
|
||||
|
||||
END_ENTRY __objc_msgForward_impcache
|
||||
@ -1281,49 +1236,4 @@ LCacheMiss:
|
||||
.quad 0
|
||||
.quad 0
|
||||
|
||||
|
||||
// Workaround for Skype evil (rdar://19715989)
|
||||
|
||||
.text
|
||||
.align 4
|
||||
.private_extern _map_images
|
||||
.private_extern _map_2_images
|
||||
.private_extern _hax
|
||||
_hax:
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
_map_images:
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
jmp _map_2_images
|
||||
|
||||
#endif
|
||||
|
437
runtime/Module/ObjectiveC.apinotes
Normal file
437
runtime/Module/ObjectiveC.apinotes
Normal file
@ -0,0 +1,437 @@
|
||||
---
|
||||
Name: ObjectiveC
|
||||
Classes:
|
||||
- Name: NSArray
|
||||
SwiftBridge: 'Swift.Array'
|
||||
- Name: NSDictionary
|
||||
SwiftBridge: 'Swift.Dictionary'
|
||||
- Name: NSSet
|
||||
SwiftBridge: 'Swift.Set'
|
||||
- Name: NSString
|
||||
SwiftBridge: 'Swift.String'
|
||||
- Name: List
|
||||
Methods:
|
||||
- Selector: init
|
||||
MethodKind: Instance
|
||||
NullabilityOfRet: N
|
||||
- Selector: 'isEqual:'
|
||||
MethodKind: Instance
|
||||
Nullability:
|
||||
- O
|
||||
NullabilityOfRet: S
|
||||
- Name: NSObject
|
||||
SwiftName: NSObject
|
||||
Methods:
|
||||
- Selector: alloc
|
||||
MethodKind: Class
|
||||
NullabilityOfRet: N
|
||||
- Selector: 'allocWithZone:'
|
||||
MethodKind: Class
|
||||
Nullability:
|
||||
- S
|
||||
NullabilityOfRet: N
|
||||
- Selector: class
|
||||
MethodKind: Class
|
||||
Availability: nonswift
|
||||
AvailabilityMsg: use 'self' instead
|
||||
- Selector: 'conformsToProtocol:'
|
||||
MethodKind: Class
|
||||
Nullability:
|
||||
- N
|
||||
NullabilityOfRet: S
|
||||
- Selector: copy
|
||||
MethodKind: Instance
|
||||
NullabilityOfRet: N
|
||||
- Selector: dealloc
|
||||
MethodKind: Instance
|
||||
Availability: nonswift
|
||||
AvailabilityMsg: use 'deinit' to define a de-initializer
|
||||
- Selector: debugDescription
|
||||
MethodKind: Class
|
||||
NullabilityOfRet: N
|
||||
- Selector: description
|
||||
MethodKind: Class
|
||||
NullabilityOfRet: N
|
||||
- Selector: 'forwardingTargetForSelector:'
|
||||
MethodKind: Instance
|
||||
Nullability:
|
||||
- S
|
||||
NullabilityOfRet: O
|
||||
- Selector: 'forwardInvocation:'
|
||||
MethodKind: Instance
|
||||
Availability: nonswift
|
||||
- Selector: init
|
||||
MethodKind: Instance
|
||||
NullabilityOfRet: N
|
||||
DesignatedInit: true
|
||||
- Selector: 'instanceMethodSignatureForSelector:'
|
||||
MethodKind: Class
|
||||
Availability: nonswift
|
||||
- Selector: 'isSubclassOfClass:'
|
||||
MethodKind: Class
|
||||
Nullability:
|
||||
- N
|
||||
NullabilityOfRet: S
|
||||
- Selector: 'methodSignatureForSelector:'
|
||||
MethodKind: Instance
|
||||
Availability: nonswift
|
||||
- Selector: mutableCopy
|
||||
MethodKind: Instance
|
||||
NullabilityOfRet: N
|
||||
- Selector: new
|
||||
MethodKind: Class
|
||||
NullabilityOfRet: N
|
||||
- Selector: superclass
|
||||
MethodKind: Class
|
||||
NullabilityOfRet: O
|
||||
- Name: Object
|
||||
Methods:
|
||||
- Selector: init
|
||||
MethodKind: Instance
|
||||
NullabilityOfRet: N
|
||||
- Selector: 'isEqual:'
|
||||
MethodKind: Instance
|
||||
Nullability:
|
||||
- O
|
||||
NullabilityOfRet: S
|
||||
Protocols:
|
||||
- Name: NSObject
|
||||
SwiftName: NSObjectProtocol
|
||||
Methods:
|
||||
- Selector: class
|
||||
MethodKind: Instance
|
||||
Availability: nonswift
|
||||
AvailabilityMsg: use 'type(of:)' instead
|
||||
- Selector: 'conformsToProtocol:'
|
||||
MethodKind: Instance
|
||||
Nullability:
|
||||
- N
|
||||
NullabilityOfRet: S
|
||||
- Selector: 'isEqual:'
|
||||
MethodKind: Instance
|
||||
Nullability:
|
||||
- O
|
||||
NullabilityOfRet: S
|
||||
- Selector: 'isKindOfClass:'
|
||||
MethodKind: Instance
|
||||
Nullability:
|
||||
- N
|
||||
NullabilityOfRet: S
|
||||
- Selector: 'isMemberOfClass:'
|
||||
MethodKind: Instance
|
||||
Nullability:
|
||||
- N
|
||||
NullabilityOfRet: S
|
||||
- Selector: self
|
||||
MethodKind: Instance
|
||||
NullabilityOfRet: N
|
||||
Properties:
|
||||
- Name: debugDescription
|
||||
Nullability: N
|
||||
- Name: description
|
||||
Nullability: N
|
||||
- Name: superclass
|
||||
Nullability: O
|
||||
Tags:
|
||||
- Name: _NSZone
|
||||
SwiftName: _NSZone
|
||||
|
||||
|
||||
# Runtime functions did not yet have nullability in Swift 3.
|
||||
|
||||
SwiftVersions:
|
||||
- Version: 3
|
||||
Functions:
|
||||
# objc.h swift3
|
||||
- Name: object_getClassName
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: sel_isMapped
|
||||
Nullability: [U]
|
||||
- Name: sel_getUid
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
|
||||
# objc-exception.h swift3
|
||||
- Name: objc_exception_throw
|
||||
Nullability: [U]
|
||||
- Name: objc_begin_catch
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: objc_setExceptionPreprocessor
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: objc_setExceptionMatcher
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: objc_setUncaughtExceptionHandler
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: objc_addExceptionHandler
|
||||
Nullability: [U, U]
|
||||
|
||||
# objc-sync.h swift3
|
||||
- Name: objc_sync_enter
|
||||
Nullability: [U]
|
||||
- Name: objc_sync_exit
|
||||
Nullability: [U]
|
||||
|
||||
# runtime.h swift3
|
||||
- Name: object_getClass
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: object_setClass
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: object_isClass
|
||||
Nullability: [U]
|
||||
- Name: object_getIvar
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: object_setIvar
|
||||
Nullability: [U, U, U]
|
||||
- Name: object_setIvarWithStrongDefault
|
||||
Nullability: [U, U, U]
|
||||
- Name: objc_getClass
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: objc_getMetaClass
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: objc_lookUpClass
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: objc_getRequiredClass
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: objc_getClassList
|
||||
Parameters:
|
||||
- Position: 0
|
||||
Type: "Class _Nullable * _Null_unspecified"
|
||||
- Name: objc_copyClassList
|
||||
ResultType: "Class _Nullable * _Null_unspecified"
|
||||
Nullability: [U]
|
||||
- Name: class_getName
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: class_isMetaClass
|
||||
Nullability: [U]
|
||||
- Name: class_getSuperclass
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: class_getVersion
|
||||
Nullability: [U]
|
||||
- Name: class_setVersion
|
||||
Nullability: [U]
|
||||
- Name: class_getInstanceSize
|
||||
Nullability: [U]
|
||||
- Name: class_getInstanceVariable
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: class_getClassVariable
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: class_copyIvarList
|
||||
ResultType: "Ivar _Nullable * _Null_unspecified"
|
||||
Nullability: [U, U]
|
||||
- Name: class_getInstanceMethod
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: class_getClassMethod
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: class_getMethodImplementation
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: class_getMethodImplementation_stret
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: class_respondsToSelector
|
||||
Nullability: [U, U]
|
||||
- Name: class_copyMethodList
|
||||
Nullability: [U, U]
|
||||
ResultType: "Method _Nullable * _Null_unspecified"
|
||||
- Name: class_conformsToProtocol
|
||||
Nullability: [U, U]
|
||||
- Name: class_copyProtocolList
|
||||
# fixme ResultType:
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: class_getProperty
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: class_copyPropertyList
|
||||
ResultType: "objc_property_t _Nullable * _Null_unspecified"
|
||||
Nullability: [U, U]
|
||||
- Name: class_getIvarLayout
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: class_getWeakIvarLayout
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: class_addMethod
|
||||
Nullability: [U, U, U, U]
|
||||
- Name: class_replaceMethod
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U, U, U]
|
||||
- Name: class_addIvar
|
||||
Nullability: [U, U, U, U, U]
|
||||
- Name: class_addProtocol
|
||||
Nullability: [U, U]
|
||||
- Name: class_addProperty
|
||||
Nullability: [U, U, U, U]
|
||||
- Name: class_replaceProperty
|
||||
Nullability: [U, U, U, U]
|
||||
- Name: class_setIvarLayout
|
||||
Nullability: [U, U]
|
||||
- Name: class_setWeakIvarLayout
|
||||
Nullability: [U, U]
|
||||
- Name: class_createInstance
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: objc_allocateClassPair
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U, U]
|
||||
- Name: objc_registerClassPair
|
||||
Nullability: [U]
|
||||
- Name: objc_duplicateClass
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U, U]
|
||||
- Name: objc_disposeClassPair
|
||||
Nullability: [U]
|
||||
- Name: method_getName
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: method_getImplementation
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: method_getTypeEncoding
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: method_getNumberOfArguments
|
||||
Nullability: [U]
|
||||
- Name: method_copyReturnType
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: method_copyArgumentType
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: method_getReturnType
|
||||
Nullability: [U, U, U]
|
||||
- Name: method_getArgumentType
|
||||
Nullability: [U, U, U, U]
|
||||
- Name: method_getDescription
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: method_setImplementation
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: method_exchangeImplementations
|
||||
Nullability: [U, U]
|
||||
- Name: ivar_getName
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: ivar_getTypeEncoding
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: ivar_getOffset
|
||||
Nullability: [U]
|
||||
- Name: property_getName
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: property_getAttributes
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: property_copyAttributeList
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: property_copyAttributeValue
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: objc_getProtocol
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: objc_copyProtocolList
|
||||
# fixme ResultType:
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: protocol_conformsToProtocol
|
||||
Nullability: [U, U]
|
||||
- Name: protocol_isEqual
|
||||
Nullability: [U, U]
|
||||
- Name: protocol_getName
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: protocol_getMethodDescription
|
||||
Nullability: [U, U, U, U]
|
||||
- Name: protocol_copyMethodDescriptionList
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U, U, U]
|
||||
- Name: protocol_getProperty
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U, U, U]
|
||||
- Name: protocol_copyPropertyList
|
||||
ResultType: "objc_property_t _Nullable * _Null_unspecified"
|
||||
Nullability: [U, U]
|
||||
- Name: protocol_copyPropertyList2
|
||||
ResultType: "objc_property_t _Nullable * _Null_unspecified"
|
||||
Nullability: [U, U, U, U]
|
||||
- Name: protocol_copyProtocolList
|
||||
# fixme ResultType:
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: objc_allocateProtocol
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: objc_registerProtocol
|
||||
Nullability: [U]
|
||||
- Name: protocol_addMethodDescription
|
||||
Nullability: [U, U, U, U, U]
|
||||
- Name: protocol_addProtocol
|
||||
Nullability: [U, U]
|
||||
- Name: protocol_addProperty
|
||||
Nullability: [U, U, U, U, U, U]
|
||||
- Name: objc_copyImageNames
|
||||
ResultType: "const char * _Nullable * _Null_unspecified"
|
||||
Nullability: [U]
|
||||
- Name: class_getImageName
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: objc_copyClassNamesForImage
|
||||
ResultType: "const char * _Nullable * _Null_unspecified"
|
||||
Nullability: [U, U]
|
||||
- Name: sel_getName
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: sel_registerName
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: sel_isEqual
|
||||
Nullability: [U, U]
|
||||
- Name: objc_enumerationMutation
|
||||
Nullability: [U]
|
||||
- Name: objc_setEnumerationMutationHandler
|
||||
Nullability: [U]
|
||||
- Name: objc_setForwardHandler
|
||||
Nullability: [U, U]
|
||||
- Name: imp_implementationWithBlock
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: imp_getBlock
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: imp_removeBlock
|
||||
Nullability: [U]
|
||||
- Name: objc_loadWeak
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U]
|
||||
- Name: objc_storeWeak
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: objc_setAssociatedObject
|
||||
Nullability: [U, U, U, U]
|
||||
- Name: objc_getAssociatedObject
|
||||
NullabilityOfRet: U
|
||||
Nullability: [U, U]
|
||||
- Name: objc_removeAssociatedObjects
|
||||
Nullability: [U]
|
40
runtime/Module/module.modulemap
Normal file
40
runtime/Module/module.modulemap
Normal file
@ -0,0 +1,40 @@
|
||||
module ObjectiveC [system] [extern_c] {
|
||||
umbrella "."
|
||||
export *
|
||||
module * {
|
||||
export *
|
||||
}
|
||||
|
||||
module NSObject {
|
||||
requires objc
|
||||
header "NSObject.h"
|
||||
export *
|
||||
}
|
||||
|
||||
#if defined(BUILD_FOR_OSX)
|
||||
module List {
|
||||
// Uses @defs, which does not work in ObjC++ or non-ARC.
|
||||
requires objc, !objc_arc, !cplusplus
|
||||
header "List.h"
|
||||
export *
|
||||
}
|
||||
|
||||
module Object {
|
||||
requires objc
|
||||
header "Object.h"
|
||||
export *
|
||||
}
|
||||
|
||||
module Protocol {
|
||||
requires objc
|
||||
header "Protocol.h"
|
||||
export *
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_FOR_OSX)
|
||||
// These file are not available outside macOS.
|
||||
exclude header "hashtable.h"
|
||||
exclude header "hashtable2.h"
|
||||
#endif
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
#include <TargetConditionals.h>
|
||||
#include <objc/objc.h>
|
||||
|
||||
#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
|
||||
#if __LP64__ || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
|
||||
typedef long NSInteger;
|
||||
typedef unsigned long NSUInteger;
|
||||
#else
|
||||
|
161
runtime/NSObject-internal.h
Normal file
161
runtime/NSObject-internal.h
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Apple Inc. All Rights Reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#ifndef _NSOBJECT_INTERNAL_H
|
||||
#define _NSOBJECT_INTERNAL_H
|
||||
|
||||
/*
|
||||
* WARNING DANGER HAZARD BEWARE EEK
|
||||
*
|
||||
* Everything in this file is for Apple Internal use only.
|
||||
* These will change in arbitrary OS updates and in unpredictable ways.
|
||||
* When your program breaks, you get to keep both pieces.
|
||||
*/
|
||||
|
||||
/*
|
||||
* NSObject-internal.h: Private SPI for use by other system frameworks.
|
||||
*/
|
||||
|
||||
/***********************************************************************
|
||||
Autorelease pool implementation
|
||||
|
||||
A thread's autorelease pool is a stack of pointers.
|
||||
Each pointer is either an object to release, or POOL_BOUNDARY which is
|
||||
an autorelease pool boundary.
|
||||
A pool token is a pointer to the POOL_BOUNDARY 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.
|
||||
**********************************************************************/
|
||||
|
||||
// structure version number. Only bump if ABI compatability is broken
|
||||
#define AUTORELEASEPOOL_VERSION 1
|
||||
|
||||
// Set this to 1 to mprotect() autorelease pool contents
|
||||
#define PROTECT_AUTORELEASEPOOL 0
|
||||
|
||||
// Set this to 1 to validate the entire autorelease pool header all the time
|
||||
// (i.e. use check() instead of fastcheck() everywhere)
|
||||
#define CHECK_AUTORELEASEPOOL (DEBUG)
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <objc/objc.h>
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
#ifndef C_ASSERT
|
||||
#if __has_feature(cxx_static_assert)
|
||||
#define C_ASSERT(expr) static_assert(expr, "(" #expr ")!")
|
||||
#elif __has_feature(c_static_assert)
|
||||
#define C_ASSERT(expr) _Static_assert(expr, "(" #expr ")!")
|
||||
#else
|
||||
#define C_ASSERT(expr)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Make ASSERT work when objc-private.h hasn't been included.
|
||||
#ifndef ASSERT
|
||||
#define ASSERT(x) assert(x)
|
||||
#endif
|
||||
|
||||
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() {
|
||||
// Clear magic before deallocation.
|
||||
// This prevents some false positives in memory debugging tools.
|
||||
// fixme semantically this should be memset_s(), but the
|
||||
// compiler doesn't optimize that at all (rdar://44856676).
|
||||
volatile uint64_t *p = (volatile uint64_t *)m;
|
||||
p[0] = 0; p[1] = 0;
|
||||
}
|
||||
|
||||
bool check() const {
|
||||
return (m[0] == M0 && 0 == strncmp((char *)&m[1], M1, M1_len));
|
||||
}
|
||||
|
||||
bool fastcheck() const {
|
||||
#if CHECK_AUTORELEASEPOOL
|
||||
return check();
|
||||
#else
|
||||
return (m[0] == M0);
|
||||
#endif
|
||||
}
|
||||
|
||||
# undef M1
|
||||
};
|
||||
|
||||
class AutoreleasePoolPage;
|
||||
struct AutoreleasePoolPageData
|
||||
{
|
||||
magic_t const magic;
|
||||
__unsafe_unretained id *next;
|
||||
pthread_t const thread;
|
||||
AutoreleasePoolPage * const parent;
|
||||
AutoreleasePoolPage *child;
|
||||
uint32_t const depth;
|
||||
uint32_t hiwat;
|
||||
|
||||
AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat)
|
||||
: magic(), next(_next), thread(_thread),
|
||||
parent(_parent), child(nil),
|
||||
depth(_depth), hiwat(_hiwat)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct thread_data_t
|
||||
{
|
||||
#ifdef __LP64__
|
||||
pthread_t const thread;
|
||||
uint32_t const hiwat;
|
||||
uint32_t const depth;
|
||||
#else
|
||||
pthread_t const thread;
|
||||
uint32_t const hiwat;
|
||||
uint32_t const depth;
|
||||
uint32_t padding;
|
||||
#endif
|
||||
};
|
||||
C_ASSERT(sizeof(thread_data_t) == 16);
|
||||
|
||||
#undef C_ASSERT
|
||||
|
||||
#endif
|
||||
#endif
|
@ -18,7 +18,7 @@
|
||||
@property (readonly) NSUInteger hash;
|
||||
|
||||
@property (readonly) Class superclass;
|
||||
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'anObject.dynamicType' instead");
|
||||
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'type(of: anObject)' instead");
|
||||
- (instancetype)self;
|
||||
|
||||
- (id)performSelector:(SEL)aSelector;
|
||||
@ -47,11 +47,14 @@
|
||||
@end
|
||||
|
||||
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
|
||||
OBJC_ROOT_CLASS
|
||||
OBJC_EXPORT
|
||||
@interface NSObject <NSObject> {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
|
||||
Class isa OBJC_ISA_AVAILABILITY;
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
+ (void)load;
|
||||
@ -82,7 +85,7 @@ OBJC_EXPORT
|
||||
+ (IMP)instanceMethodForSelector:(SEL)aSelector;
|
||||
- (void)doesNotRecognizeSelector:(SEL)aSelector;
|
||||
|
||||
- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
|
||||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
|
||||
|
||||
@ -93,8 +96,8 @@ OBJC_EXPORT
|
||||
|
||||
+ (BOOL)isSubclassOfClass:(Class)aClass;
|
||||
|
||||
+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
+ (NSUInteger)hash;
|
||||
+ (Class)superclass;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -38,7 +38,8 @@
|
||||
#if __OBJC__ && !__OBJC2__
|
||||
|
||||
__OSX_AVAILABLE(10.0)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE
|
||||
__WATCHOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE
|
||||
OBJC_ROOT_CLASS
|
||||
@interface Object
|
||||
{
|
||||
|
@ -35,14 +35,6 @@ typedef struct objc_object *id;
|
||||
|
||||
#if __OBJC2__
|
||||
|
||||
__OSX_AVAILABLE(10.0)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE
|
||||
OBJC_ROOT_CLASS
|
||||
@interface Object {
|
||||
Class isa;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation Object
|
||||
|
||||
+ (id)initialize
|
||||
|
182
runtime/PointerUnion.h
Normal file
182
runtime/PointerUnion.h
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Apple Inc. All Rights Reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#ifndef POINTERUNION_H
|
||||
#define POINTERUNION_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
|
||||
namespace objc {
|
||||
|
||||
template <typename T> struct PointerUnionTypeSelectorReturn {
|
||||
using Return = T;
|
||||
};
|
||||
|
||||
/// Get a type based on whether two types are the same or not.
|
||||
///
|
||||
/// For:
|
||||
///
|
||||
/// \code
|
||||
/// using Ret = typename PointerUnionTypeSelector<T1, T2, EQ, NE>::Return;
|
||||
/// \endcode
|
||||
///
|
||||
/// Ret will be EQ type if T1 is same as T2 or NE type otherwise.
|
||||
template <typename T1, typename T2, typename RET_EQ, typename RET_NE>
|
||||
struct PointerUnionTypeSelector {
|
||||
using Return = typename PointerUnionTypeSelectorReturn<RET_NE>::Return;
|
||||
};
|
||||
|
||||
template <typename T, typename RET_EQ, typename RET_NE>
|
||||
struct PointerUnionTypeSelector<T, T, RET_EQ, RET_NE> {
|
||||
using Return = typename PointerUnionTypeSelectorReturn<RET_EQ>::Return;
|
||||
};
|
||||
|
||||
template <typename T1, typename T2, typename RET_EQ, typename RET_NE>
|
||||
struct PointerUnionTypeSelectorReturn<
|
||||
PointerUnionTypeSelector<T1, T2, RET_EQ, RET_NE>> {
|
||||
using Return =
|
||||
typename PointerUnionTypeSelector<T1, T2, RET_EQ, RET_NE>::Return;
|
||||
};
|
||||
|
||||
template <class PT1, class PT2>
|
||||
class PointerUnion {
|
||||
uintptr_t _value;
|
||||
|
||||
static_assert(alignof(PT1) >= 2, "alignment requirement");
|
||||
static_assert(alignof(PT2) >= 2, "alignment requirement");
|
||||
|
||||
struct IsPT1 {
|
||||
static const uintptr_t Num = 0;
|
||||
};
|
||||
struct IsPT2 {
|
||||
static const uintptr_t Num = 1;
|
||||
};
|
||||
template <typename T> struct UNION_DOESNT_CONTAIN_TYPE {};
|
||||
|
||||
uintptr_t getPointer() const {
|
||||
return _value & ~1;
|
||||
}
|
||||
uintptr_t getTag() const {
|
||||
return _value & 1;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit PointerUnion(const std::atomic<uintptr_t> &raw)
|
||||
: _value(raw.load(std::memory_order_relaxed))
|
||||
{ }
|
||||
PointerUnion(PT1 t) : _value((uintptr_t)t) { }
|
||||
PointerUnion(PT2 t) : _value((uintptr_t)t | 1) { }
|
||||
|
||||
void storeAt(std::atomic<uintptr_t> &raw, std::memory_order order) const {
|
||||
raw.store(_value, order);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool is() const {
|
||||
using Ty = typename PointerUnionTypeSelector<PT1, T, IsPT1,
|
||||
PointerUnionTypeSelector<PT2, T, IsPT2,
|
||||
UNION_DOESNT_CONTAIN_TYPE<T>>>::Return;
|
||||
return getTag() == Ty::Num;
|
||||
}
|
||||
|
||||
template <typename T> T get() const {
|
||||
ASSERT(is<T>() && "Invalid accessor called");
|
||||
return reinterpret_cast<T>(getPointer());
|
||||
}
|
||||
|
||||
template <typename T> T dyn_cast() const {
|
||||
if (is<T>())
|
||||
return get<T>();
|
||||
return T();
|
||||
}
|
||||
};
|
||||
|
||||
template <class PT1, class PT2, class PT3, class PT4 = void>
|
||||
class PointerUnion4 {
|
||||
uintptr_t _value;
|
||||
|
||||
static_assert(alignof(PT1) >= 4, "alignment requirement");
|
||||
static_assert(alignof(PT2) >= 4, "alignment requirement");
|
||||
static_assert(alignof(PT3) >= 4, "alignment requirement");
|
||||
static_assert(alignof(PT4) >= 4, "alignment requirement");
|
||||
|
||||
struct IsPT1 {
|
||||
static const uintptr_t Num = 0;
|
||||
};
|
||||
struct IsPT2 {
|
||||
static const uintptr_t Num = 1;
|
||||
};
|
||||
struct IsPT3 {
|
||||
static const uintptr_t Num = 2;
|
||||
};
|
||||
struct IsPT4 {
|
||||
static const uintptr_t Num = 3;
|
||||
};
|
||||
template <typename T> struct UNION_DOESNT_CONTAIN_TYPE {};
|
||||
|
||||
uintptr_t getPointer() const {
|
||||
return _value & ~3;
|
||||
}
|
||||
uintptr_t getTag() const {
|
||||
return _value & 3;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit PointerUnion4(const std::atomic<uintptr_t> &raw)
|
||||
: _value(raw.load(std::memory_order_relaxed))
|
||||
{ }
|
||||
PointerUnion4(PT1 t) : _value((uintptr_t)t) { }
|
||||
PointerUnion4(PT2 t) : _value((uintptr_t)t | 1) { }
|
||||
PointerUnion4(PT3 t) : _value((uintptr_t)t | 2) { }
|
||||
PointerUnion4(PT4 t) : _value((uintptr_t)t | 3) { }
|
||||
|
||||
void storeAt(std::atomic<uintptr_t> &raw, std::memory_order order) const {
|
||||
raw.store(_value, order);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool is() const {
|
||||
using Ty = typename PointerUnionTypeSelector<PT1, T, IsPT1,
|
||||
PointerUnionTypeSelector<PT2, T, IsPT2,
|
||||
PointerUnionTypeSelector<PT3, T, IsPT3,
|
||||
PointerUnionTypeSelector<PT4, T, IsPT4,
|
||||
UNION_DOESNT_CONTAIN_TYPE<T>>>>>::Return;
|
||||
return getTag() == Ty::Num;
|
||||
}
|
||||
|
||||
template <typename T> T get() const {
|
||||
ASSERT(is<T>() && "Invalid accessor called");
|
||||
return reinterpret_cast<T>(getPointer());
|
||||
}
|
||||
|
||||
template <typename T> T dyn_cast() const {
|
||||
if (is<T>())
|
||||
return get<T>();
|
||||
return T();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace objc
|
||||
|
||||
#endif /* DENSEMAPEXTRAS_H */
|
@ -41,7 +41,7 @@
|
||||
// All methods of class Protocol are unavailable.
|
||||
// Use the functions in objc/runtime.h instead.
|
||||
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
|
||||
@interface Protocol : NSObject
|
||||
@end
|
||||
|
||||
@ -50,7 +50,7 @@ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
|
||||
|
||||
#include <objc/Object.h>
|
||||
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
|
||||
@interface Protocol : Object
|
||||
{
|
||||
@private
|
||||
@ -74,12 +74,20 @@ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
|
||||
__OSX_DEPRECATED(10.0, 10.5, "use protocol_getMethodDescription instead")
|
||||
__IOS_DEPRECATED(2.0, 2.0, "use protocol_getMethodDescription instead")
|
||||
__TVOS_DEPRECATED(9.0, 9.0, "use protocol_getMethodDescription instead")
|
||||
__WATCHOS_DEPRECATED(1.0, 1.0, "use protocol_getMethodDescription instead");
|
||||
__WATCHOS_DEPRECATED(1.0, 1.0, "use protocol_getMethodDescription instead")
|
||||
#ifndef __APPLE_BLEACH_SDK__
|
||||
__BRIDGEOS_DEPRECATED(2.0, 2.0, "use protocol_getMethodDescription instead")
|
||||
#endif
|
||||
;
|
||||
- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel
|
||||
__OSX_DEPRECATED(10.0, 10.5, "use protocol_getMethodDescription instead")
|
||||
__IOS_DEPRECATED(2.0, 2.0, "use protocol_getMethodDescription instead")
|
||||
__TVOS_DEPRECATED(9.0, 9.0, "use protocol_getMethodDescription instead")
|
||||
__WATCHOS_DEPRECATED(1.0, 1.0, "use protocol_getMethodDescription instead");
|
||||
__WATCHOS_DEPRECATED(1.0, 1.0, "use protocol_getMethodDescription instead")
|
||||
#ifndef __APPLE_BLEACH_SDK__
|
||||
__BRIDGEOS_DEPRECATED(2.0, 2.0, "use protocol_getMethodDescription instead")
|
||||
#endif
|
||||
;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -45,22 +45,20 @@
|
||||
// by CF, so __IncompleteProtocol would be left without an R/R implementation
|
||||
// otherwise, which would break ARC.
|
||||
|
||||
@interface __IncompleteProtocol : NSObject @end
|
||||
@implementation __IncompleteProtocol
|
||||
@interface __IncompleteProtocol : NSObject
|
||||
@end
|
||||
|
||||
#if __OBJC2__
|
||||
// fixme hack - make __IncompleteProtocol a non-lazy class
|
||||
+ (void) load { }
|
||||
__attribute__((objc_nonlazy_class))
|
||||
#endif
|
||||
@implementation __IncompleteProtocol
|
||||
@end
|
||||
|
||||
|
||||
@implementation Protocol
|
||||
|
||||
#if __OBJC2__
|
||||
// fixme hack - make Protocol a non-lazy class
|
||||
+ (void) load { }
|
||||
__attribute__((objc_nonlazy_class))
|
||||
#endif
|
||||
|
||||
@implementation Protocol
|
||||
|
||||
- (BOOL) conformsTo: (Protocol *)aProtocolObj
|
||||
{
|
||||
|
@ -1,148 +0,0 @@
|
||||
#if __arm__
|
||||
|
||||
#include <arm/arch.h>
|
||||
#include <mach/vm_param.h>
|
||||
|
||||
.syntax unified
|
||||
|
||||
.text
|
||||
|
||||
.private_extern __a1a2_tramphead
|
||||
.private_extern __a1a2_firsttramp
|
||||
.private_extern __a1a2_trampend
|
||||
|
||||
// Trampoline machinery assumes the trampolines are Thumb function pointers
|
||||
#if !__thumb2__
|
||||
# error sorry
|
||||
#endif
|
||||
|
||||
.thumb
|
||||
.thumb_func __a1a2_tramphead
|
||||
.thumb_func __a1a2_firsttramp
|
||||
.thumb_func __a1a2_trampend
|
||||
|
||||
.align PAGE_MAX_SHIFT
|
||||
__a1a2_tramphead:
|
||||
/*
|
||||
r0 == self
|
||||
r12 == pc of trampoline's first instruction + PC bias
|
||||
lr == original return address
|
||||
*/
|
||||
|
||||
mov r1, r0 // _cmd = self
|
||||
|
||||
// Trampoline's data is one page before the trampoline text.
|
||||
// Also correct PC bias of 4 bytes.
|
||||
sub r12, #PAGE_MAX_SIZE
|
||||
ldr r0, [r12, #-4] // self = block object
|
||||
ldr pc, [r0, #12] // tail call block->invoke
|
||||
// not reached
|
||||
|
||||
// Align trampolines to 8 bytes
|
||||
.align 3
|
||||
|
||||
.macro TrampolineEntry
|
||||
mov r12, pc
|
||||
b __a1a2_tramphead
|
||||
.align 3
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX16
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX256
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
.endmacro
|
||||
|
||||
.private_extern __a1a2_firsttramp
|
||||
__a1a2_firsttramp:
|
||||
// 2048-2 trampolines to fill 16K page
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
|
||||
.private_extern __a1a2_trampend
|
||||
__a1a2_trampend:
|
||||
|
||||
#endif
|
@ -1,566 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
#include <mach/vm_param.h>
|
||||
|
||||
.text
|
||||
.private_extern __a1a2_tramphead
|
||||
.private_extern __a1a2_firsttramp
|
||||
.private_extern __a1a2_nexttramp
|
||||
.private_extern __a1a2_trampend
|
||||
|
||||
.align PAGE_SHIFT
|
||||
__a1a2_tramphead:
|
||||
popl %eax
|
||||
andl $0xFFFFFFF8, %eax
|
||||
subl $ PAGE_SIZE, %eax
|
||||
movl 4(%esp), %ecx // self -> ecx
|
||||
movl %ecx, 8(%esp) // ecx -> _cmd
|
||||
movl (%eax), %ecx // blockPtr -> ecx
|
||||
movl %ecx, 4(%esp) // ecx -> self
|
||||
jmp *12(%ecx) // tail to block->invoke
|
||||
|
||||
.macro TrampolineEntry
|
||||
call __a1a2_tramphead
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
.endmacro
|
||||
|
||||
.align 5
|
||||
__a1a2_firsttramp:
|
||||
TrampolineEntry
|
||||
__a1a2_nexttramp: // used to calculate size of each trampoline
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
__a1a2_trampend:
|
||||
|
||||
#endif
|
@ -1,564 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
#include <mach/vm_param.h>
|
||||
|
||||
.text
|
||||
.private_extern __a1a2_tramphead
|
||||
.private_extern __a1a2_firsttramp
|
||||
.private_extern __a1a2_nexttramp
|
||||
.private_extern __a1a2_trampend
|
||||
|
||||
.align PAGE_SHIFT
|
||||
__a1a2_tramphead:
|
||||
popq %r10
|
||||
andq $0xFFFFFFFFFFFFFFF8, %r10
|
||||
subq $ PAGE_SIZE, %r10
|
||||
movq %rdi, %rsi // arg1 -> arg2
|
||||
movq (%r10), %rdi // block -> arg1
|
||||
jmp *16(%rdi)
|
||||
|
||||
.macro TrampolineEntry
|
||||
callq __a1a2_tramphead
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
.endmacro
|
||||
|
||||
.align 5
|
||||
__a1a2_firsttramp:
|
||||
TrampolineEntry
|
||||
__a1a2_nexttramp:
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
__a1a2_trampend:
|
||||
|
||||
#endif
|
@ -1,148 +0,0 @@
|
||||
#if __arm__
|
||||
|
||||
#include <arm/arch.h>
|
||||
#include <mach/vm_param.h>
|
||||
|
||||
.syntax unified
|
||||
|
||||
.text
|
||||
|
||||
.private_extern __a2a3_tramphead
|
||||
.private_extern __a2a3_firsttramp
|
||||
.private_extern __a2a3_trampend
|
||||
|
||||
// Trampoline machinery assumes the trampolines are Thumb function pointers
|
||||
#if !__thumb2__
|
||||
# error sorry
|
||||
#endif
|
||||
|
||||
.thumb
|
||||
.thumb_func __a2a3_tramphead
|
||||
.thumb_func __a2a3_firsttramp
|
||||
.thumb_func __a2a3_trampend
|
||||
|
||||
.align PAGE_MAX_SHIFT
|
||||
__a2a3_tramphead:
|
||||
/*
|
||||
r1 == self
|
||||
r12 == pc of trampoline's first instruction + PC bias
|
||||
lr == original return address
|
||||
*/
|
||||
|
||||
mov r2, r1 // _cmd = self
|
||||
|
||||
// Trampoline's data is one page before the trampoline text.
|
||||
// Also correct PC bias of 4 bytes.
|
||||
sub r12, #PAGE_MAX_SIZE
|
||||
ldr r1, [r12, #-4] // self = block object
|
||||
ldr pc, [r1, #12] // tail call block->invoke
|
||||
// not reached
|
||||
|
||||
// Align trampolines to 8 bytes
|
||||
.align 3
|
||||
|
||||
.macro TrampolineEntry
|
||||
mov r12, pc
|
||||
b __a2a3_tramphead
|
||||
.align 3
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX16
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX256
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
.endmacro
|
||||
|
||||
.private_extern __a2a3_firsttramp
|
||||
__a2a3_firsttramp:
|
||||
// 2048-2 trampolines to fill 16K page
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
|
||||
.private_extern __a2a3_trampend
|
||||
__a2a3_trampend:
|
||||
|
||||
#endif
|
@ -1,566 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
#include <mach/vm_param.h>
|
||||
|
||||
.text
|
||||
.private_extern __a2a3_tramphead
|
||||
.private_extern __a2a3_firsttramp
|
||||
.private_extern __a2a3_nexttramp
|
||||
.private_extern __a2a3_trampend
|
||||
|
||||
.align PAGE_SHIFT
|
||||
__a2a3_tramphead:
|
||||
popl %eax
|
||||
andl $0xFFFFFFF8, %eax
|
||||
subl $ PAGE_SIZE, %eax
|
||||
movl 8(%esp), %ecx // self -> ecx
|
||||
movl %ecx, 12(%esp) // ecx -> _cmd
|
||||
movl (%eax), %ecx // blockPtr -> ecx
|
||||
movl %ecx, 8(%esp) // ecx -> self
|
||||
jmp *12(%ecx) // tail to block->invoke
|
||||
|
||||
.macro TrampolineEntry
|
||||
call __a2a3_tramphead
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
.endmacro
|
||||
|
||||
.align 5
|
||||
__a2a3_firsttramp:
|
||||
TrampolineEntry
|
||||
__a2a3_nexttramp:
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
__a2a3_trampend:
|
||||
|
||||
#endif
|
@ -1,565 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
#include <mach/vm_param.h>
|
||||
|
||||
.text
|
||||
.private_extern __a2a3_tramphead
|
||||
.private_extern __a2a3_firsttramp
|
||||
.private_extern __a2a3_nexttramp
|
||||
.private_extern __a2a3_trampend
|
||||
|
||||
.align PAGE_SHIFT
|
||||
__a2a3_tramphead:
|
||||
popq %r10
|
||||
andq $0xFFFFFFFFFFFFFFF8, %r10
|
||||
subq $ PAGE_SIZE, %r10
|
||||
// %rdi -- first arg -- is address of return value's space. Don't mess with it.
|
||||
movq %rsi, %rdx // arg2 -> arg3
|
||||
movq (%r10), %rsi // block -> arg2
|
||||
jmp *16(%rsi)
|
||||
|
||||
.macro TrampolineEntry
|
||||
callq __a2a3_tramphead
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
.endmacro
|
||||
|
||||
.align 5
|
||||
__a2a3_firsttramp:
|
||||
TrampolineEntry
|
||||
__a2a3_nexttramp:
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
__a2a3_trampend:
|
||||
|
||||
#endif
|
174
runtime/arm64-asm.h
Normal file
174
runtime/arm64-asm.h
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* Copyright (c) 2018 Apple Inc. All Rights Reserved.
|
||||
*
|
||||
* 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@
|
||||
*/
|
||||
/********************************************************************
|
||||
*
|
||||
* arm64-asm.h - asm tools for arm64/arm64_32 and ROP/JOP
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#if __arm64__
|
||||
|
||||
#if __LP64__
|
||||
// true arm64
|
||||
|
||||
#define SUPPORT_TAGGED_POINTERS 1
|
||||
#define PTR .quad
|
||||
#define PTRSIZE 8
|
||||
#define PTRSHIFT 3 // 1<<PTRSHIFT == PTRSIZE
|
||||
// "p" registers are pointer-sized
|
||||
#define UXTP UXTX
|
||||
#define p0 x0
|
||||
#define p1 x1
|
||||
#define p2 x2
|
||||
#define p3 x3
|
||||
#define p4 x4
|
||||
#define p5 x5
|
||||
#define p6 x6
|
||||
#define p7 x7
|
||||
#define p8 x8
|
||||
#define p9 x9
|
||||
#define p10 x10
|
||||
#define p11 x11
|
||||
#define p12 x12
|
||||
#define p13 x13
|
||||
#define p14 x14
|
||||
#define p15 x15
|
||||
#define p16 x16
|
||||
#define p17 x17
|
||||
|
||||
// true arm64
|
||||
#else
|
||||
// arm64_32
|
||||
|
||||
#define SUPPORT_TAGGED_POINTERS 0
|
||||
#define PTR .long
|
||||
#define PTRSIZE 4
|
||||
#define PTRSHIFT 2 // 1<<PTRSHIFT == PTRSIZE
|
||||
// "p" registers are pointer-sized
|
||||
#define UXTP UXTW
|
||||
#define p0 w0
|
||||
#define p1 w1
|
||||
#define p2 w2
|
||||
#define p3 w3
|
||||
#define p4 w4
|
||||
#define p5 w5
|
||||
#define p6 w6
|
||||
#define p7 w7
|
||||
#define p8 w8
|
||||
#define p9 w9
|
||||
#define p10 w10
|
||||
#define p11 w11
|
||||
#define p12 w12
|
||||
#define p13 w13
|
||||
#define p14 w14
|
||||
#define p15 w15
|
||||
#define p16 w16
|
||||
#define p17 w17
|
||||
|
||||
// arm64_32
|
||||
#endif
|
||||
|
||||
|
||||
#if __has_feature(ptrauth_returns)
|
||||
// ROP
|
||||
# define SignLR pacibsp
|
||||
# define AuthenticateLR autibsp
|
||||
#else
|
||||
// not ROP
|
||||
# define SignLR
|
||||
# define AuthenticateLR
|
||||
#endif
|
||||
|
||||
#if __has_feature(ptrauth_calls)
|
||||
// JOP
|
||||
|
||||
.macro TailCallFunctionPointer
|
||||
// $0 = function pointer value
|
||||
braaz $0
|
||||
.endmacro
|
||||
|
||||
.macro TailCallCachedImp
|
||||
// $0 = cached imp, $1 = address of cached imp, $2 = SEL, $3 = isa
|
||||
eor $1, $1, $2 // mix SEL into ptrauth modifier
|
||||
eor $1, $1, $3 // mix isa into ptrauth modifier
|
||||
brab $0, $1
|
||||
.endmacro
|
||||
|
||||
.macro TailCallMethodListImp
|
||||
// $0 = method list imp, $1 = address of method list imp
|
||||
braa $0, $1
|
||||
.endmacro
|
||||
|
||||
.macro TailCallBlockInvoke
|
||||
// $0 = invoke function, $1 = address of invoke function
|
||||
braa $0, $1
|
||||
.endmacro
|
||||
|
||||
.macro AuthAndResignAsIMP
|
||||
// $0 = cached imp, $1 = address of cached imp, $2 = SEL, $3 = isa
|
||||
// note: assumes the imp is not nil
|
||||
eor $1, $1, $2 // mix SEL into ptrauth modifier
|
||||
eor $1, $1, $3 // mix isa into ptrauth modifier
|
||||
autib $0, $1 // authenticate cached imp
|
||||
ldr xzr, [$0] // crash if authentication failed
|
||||
paciza $0 // resign cached imp as IMP
|
||||
.endmacro
|
||||
|
||||
// JOP
|
||||
#else
|
||||
// not JOP
|
||||
|
||||
.macro TailCallFunctionPointer
|
||||
// $0 = function pointer value
|
||||
br $0
|
||||
.endmacro
|
||||
|
||||
.macro TailCallCachedImp
|
||||
// $0 = cached imp, $1 = address of cached imp, $2 = SEL, $3 = isa
|
||||
eor $0, $0, $3
|
||||
br $0
|
||||
.endmacro
|
||||
|
||||
.macro TailCallMethodListImp
|
||||
// $0 = method list imp, $1 = address of method list imp
|
||||
br $0
|
||||
.endmacro
|
||||
|
||||
.macro TailCallBlockInvoke
|
||||
// $0 = invoke function, $1 = address of invoke function
|
||||
br $0
|
||||
.endmacro
|
||||
|
||||
.macro AuthAndResignAsIMP
|
||||
// $0 = cached imp, $1 = address of cached imp, $2 = SEL
|
||||
eor $0, $0, $3
|
||||
.endmacro
|
||||
|
||||
// not JOP
|
||||
#endif
|
||||
|
||||
#define TailCallBlockInvoke TailCallMethodListImp
|
||||
|
||||
|
||||
// __arm64__
|
||||
#endif
|
@ -30,9 +30,8 @@
|
||||
#define _OBJC_LITTLE_HASHTABLE_H_
|
||||
|
||||
#ifndef _OBJC_PRIVATE_H_
|
||||
# define OBJC_HASH_AVAILABILITY \
|
||||
__OSX_DEPRECATED(10.0, 10.1, "NXHashTable is deprecated") \
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE
|
||||
# define OBJC_HASH_AVAILABILITY \
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.0, 10.1, "NXHashTable is deprecated")
|
||||
#else
|
||||
# define OBJC_HASH_AVAILABILITY
|
||||
#endif
|
||||
@ -52,9 +51,13 @@ The objective C class HashTable is preferred when dealing with (key, values) ass
|
||||
As well-behaved scalable data structures, hash tables double in size when they start becoming full, thus guaranteeing both average constant time access and linear size. */
|
||||
|
||||
typedef struct {
|
||||
uintptr_t (*hash)(const void *info, const void *data);
|
||||
int (*isEqual)(const void *info, const void *data1, const void *data2);
|
||||
void (*free)(const void *info, void *data);
|
||||
uintptr_t (* _Nonnull hash)(const void * _Nullable info,
|
||||
const void * _Nullable data);
|
||||
int (* _Nonnull isEqual)(const void * _Nullable info,
|
||||
const void * _Nullable data1,
|
||||
const void * _Nullable data2);
|
||||
void (* _Nonnull free)(const void * _Nullable info,
|
||||
void * _Nullable data);
|
||||
int style; /* reserved for future expansion; currently 0 */
|
||||
} NXHashTablePrototype;
|
||||
|
||||
@ -67,41 +70,63 @@ typedef struct {
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
const NXHashTablePrototype *prototype OBJC_HASH_AVAILABILITY;
|
||||
const NXHashTablePrototype * _Nonnull prototype OBJC_HASH_AVAILABILITY;
|
||||
unsigned count OBJC_HASH_AVAILABILITY;
|
||||
unsigned nbBuckets OBJC_HASH_AVAILABILITY;
|
||||
void *buckets OBJC_HASH_AVAILABILITY;
|
||||
const void *info OBJC_HASH_AVAILABILITY;
|
||||
void * _Nullable buckets OBJC_HASH_AVAILABILITY;
|
||||
const void * _Nullable info OBJC_HASH_AVAILABILITY;
|
||||
} NXHashTable OBJC_HASH_AVAILABILITY;
|
||||
/* private data structure; may change */
|
||||
|
||||
OBJC_EXPORT NXHashTable *NXCreateHashTableFromZone (NXHashTablePrototype prototype, unsigned capacity, const void *info, void *z) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT NXHashTable *NXCreateHashTable (NXHashTablePrototype prototype, unsigned capacity, const void *info) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT NXHashTable * _Nonnull
|
||||
NXCreateHashTableFromZone (NXHashTablePrototype prototype, unsigned capacity,
|
||||
const void * _Nullable info, void * _Nullable z)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
|
||||
OBJC_EXPORT NXHashTable * _Nonnull
|
||||
NXCreateHashTable (NXHashTablePrototype prototype, unsigned capacity,
|
||||
const void * _Nullable info)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* if hash is 0, pointer hash is assumed */
|
||||
/* if isEqual is 0, pointer equality is assumed */
|
||||
/* if free is 0, elements are not freed */
|
||||
/* capacity is only a hint; 0 creates a small table */
|
||||
/* info allows call backs to be very general */
|
||||
|
||||
OBJC_EXPORT void NXFreeHashTable (NXHashTable *table) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT void
|
||||
NXFreeHashTable (NXHashTable * _Nonnull table)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* calls free for each data, and recovers table */
|
||||
|
||||
OBJC_EXPORT void NXEmptyHashTable (NXHashTable *table) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT void
|
||||
NXEmptyHashTable (NXHashTable * _Nonnull table)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* does not deallocate table nor data; keeps current capacity */
|
||||
|
||||
OBJC_EXPORT void NXResetHashTable (NXHashTable *table) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT void
|
||||
NXResetHashTable (NXHashTable * _Nonnull table)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* frees each entry; keeps current capacity */
|
||||
|
||||
OBJC_EXPORT BOOL NXCompareHashTables (NXHashTable *table1, NXHashTable *table2) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT BOOL
|
||||
NXCompareHashTables (NXHashTable * _Nonnull table1,
|
||||
NXHashTable * _Nonnull table2)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* Returns YES if the two sets are equal (each member of table1 in table2, and table have same size) */
|
||||
|
||||
OBJC_EXPORT NXHashTable *NXCopyHashTable (NXHashTable *table) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT NXHashTable * _Nonnull
|
||||
NXCopyHashTable (NXHashTable * _Nonnull table)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* makes a fresh table, copying data pointers, not data itself. */
|
||||
|
||||
OBJC_EXPORT unsigned NXCountHashTable (NXHashTable *table) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT unsigned
|
||||
NXCountHashTable (NXHashTable * _Nonnull table)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* current number of data in table */
|
||||
|
||||
OBJC_EXPORT int NXHashMember (NXHashTable *table, const void *data) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT int
|
||||
NXHashMember (NXHashTable * _Nonnull table, const void * _Nullable data)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* returns non-0 iff data is present in table.
|
||||
Example of use when the hashed data is a struct containing the key,
|
||||
and when the callee only has a key:
|
||||
@ -110,7 +135,9 @@ OBJC_EXPORT int NXHashMember (NXHashTable *table, const void *data) OBJC_HASH_AV
|
||||
return NXHashMember (myTable, &pseudo)
|
||||
*/
|
||||
|
||||
OBJC_EXPORT void *NXHashGet (NXHashTable *table, const void *data) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT void * _Nullable
|
||||
NXHashGet (NXHashTable * _Nonnull table, const void * _Nullable data)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* return original table data or NULL.
|
||||
Example of use when the hashed data is a struct containing the key,
|
||||
and when the callee only has a key:
|
||||
@ -120,14 +147,20 @@ OBJC_EXPORT void *NXHashGet (NXHashTable *table, const void *data) OBJC_HASH_AVA
|
||||
original = NXHashGet (myTable, &pseudo)
|
||||
*/
|
||||
|
||||
OBJC_EXPORT void *NXHashInsert (NXHashTable *table, const void *data) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT void * _Nullable
|
||||
NXHashInsert (NXHashTable * _Nonnull table, const void * _Nullable data)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* previous data or NULL is returned. */
|
||||
|
||||
OBJC_EXPORT void *NXHashInsertIfAbsent (NXHashTable *table, const void *data) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT void * _Nullable
|
||||
NXHashInsertIfAbsent (NXHashTable * _Nonnull table, const void * _Nullable data)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* If data already in table, returns the one in table
|
||||
else adds argument to table and returns argument. */
|
||||
|
||||
OBJC_EXPORT void *NXHashRemove (NXHashTable *table, const void *data) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT void * _Nullable
|
||||
NXHashRemove (NXHashTable * _Nonnull table, const void * _Nullable data)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* previous data or NULL is returned */
|
||||
|
||||
/* Iteration over all elements of a table consists in setting up an iteration state and then to progress until all entries have been visited. An example of use for counting elements in a table is:
|
||||
@ -142,9 +175,13 @@ OBJC_EXPORT void *NXHashRemove (NXHashTable *table, const void *data) OBJC_HASH_
|
||||
typedef struct {int i; int j;} NXHashState OBJC_HASH_AVAILABILITY;
|
||||
/* callers should not rely on actual contents of the struct */
|
||||
|
||||
OBJC_EXPORT NXHashState NXInitHashState(NXHashTable *table) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT NXHashState
|
||||
NXInitHashState(NXHashTable * _Nonnull table)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
|
||||
OBJC_EXPORT int NXNextHashState(NXHashTable *table, NXHashState *state, void **data) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT int
|
||||
NXNextHashState(NXHashTable * _Nonnull table, NXHashState * _Nonnull state,
|
||||
void * _Nullable * _Nonnull data) OBJC_HASH_AVAILABILITY;
|
||||
/* returns 0 when all elements have been visited */
|
||||
|
||||
/*************************************************************************
|
||||
@ -152,23 +189,45 @@ OBJC_EXPORT int NXNextHashState(NXHashTable *table, NXHashState *state, void **d
|
||||
* and common prototypes
|
||||
*************************************************************************/
|
||||
|
||||
OBJC_EXPORT uintptr_t NXPtrHash(const void *info, const void *data) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT uintptr_t
|
||||
NXPtrHash(const void * _Nullable info, const void * _Nullable data)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* scrambles the address bits; info unused */
|
||||
OBJC_EXPORT uintptr_t NXStrHash(const void *info, const void *data) OBJC_HASH_AVAILABILITY;
|
||||
|
||||
OBJC_EXPORT uintptr_t
|
||||
NXStrHash(const void * _Nullable info, const void * _Nullable data)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* string hashing; info unused */
|
||||
OBJC_EXPORT int NXPtrIsEqual(const void *info, const void *data1, const void *data2) OBJC_HASH_AVAILABILITY;
|
||||
|
||||
OBJC_EXPORT int
|
||||
NXPtrIsEqual(const void * _Nullable info, const void * _Nullable data1,
|
||||
const void * _Nullable data2)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* pointer comparison; info unused */
|
||||
OBJC_EXPORT int NXStrIsEqual(const void *info, const void *data1, const void *data2) OBJC_HASH_AVAILABILITY;
|
||||
|
||||
OBJC_EXPORT int
|
||||
NXStrIsEqual(const void * _Nullable info, const void * _Nullable data1,
|
||||
const void * _Nullable data2)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* string comparison; NULL ok; info unused */
|
||||
OBJC_EXPORT void NXNoEffectFree(const void *info, void *data) OBJC_HASH_AVAILABILITY;
|
||||
|
||||
OBJC_EXPORT void
|
||||
NXNoEffectFree(const void * _Nullable info, void * _Nullable data)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* no effect; info unused */
|
||||
OBJC_EXPORT void NXReallyFree(const void *info, void *data) OBJC_HASH_AVAILABILITY;
|
||||
|
||||
OBJC_EXPORT void
|
||||
NXReallyFree(const void * _Nullable info, void * _Nullable data)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* frees it; info unused */
|
||||
|
||||
/* The two following prototypes are useful for manipulating set of pointers or set of strings; For them free is defined as NXNoEffectFree */
|
||||
OBJC_EXPORT const NXHashTablePrototype NXPtrPrototype OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT const NXHashTablePrototype NXPtrPrototype
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* prototype when data is a pointer (void *) */
|
||||
OBJC_EXPORT const NXHashTablePrototype NXStrPrototype OBJC_HASH_AVAILABILITY;
|
||||
|
||||
OBJC_EXPORT const NXHashTablePrototype NXStrPrototype
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* prototype when data is a string (char *) */
|
||||
|
||||
/* following prototypes help describe mappings where the key is the first element of a struct and is either a pointer or a string.
|
||||
@ -181,8 +240,10 @@ For example NXStrStructKeyPrototype can be used to hash pointers to Example, whe
|
||||
|
||||
For the following prototypes, free is defined as NXReallyFree.
|
||||
*/
|
||||
OBJC_EXPORT const NXHashTablePrototype NXPtrStructKeyPrototype OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT const NXHashTablePrototype NXStrStructKeyPrototype OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT const NXHashTablePrototype NXPtrStructKeyPrototype
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT const NXHashTablePrototype NXStrStructKeyPrototype
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
|
||||
|
||||
#if !__OBJC2__ && !TARGET_OS_WIN32
|
||||
@ -196,29 +257,39 @@ A unique string is a string that is allocated once for all (never de-allocated)
|
||||
|
||||
typedef const char *NXAtom OBJC_HASH_AVAILABILITY;
|
||||
|
||||
OBJC_EXPORT NXAtom NXUniqueString(const char *buffer) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT NXAtom _Nullable
|
||||
NXUniqueString(const char * _Nullable buffer)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* assumes that buffer is \0 terminated, and returns
|
||||
a previously created string or a new string that is a copy of buffer.
|
||||
If NULL is passed returns NULL.
|
||||
Returned string should never be modified. To ensure this invariant,
|
||||
allocations are made in a special read only zone. */
|
||||
|
||||
OBJC_EXPORT NXAtom NXUniqueStringWithLength(const char *buffer, int length) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT NXAtom _Nonnull
|
||||
NXUniqueStringWithLength(const char * _Nullable buffer, int length)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* assumes that buffer is a non NULL buffer of at least
|
||||
length characters. Returns a previously created string or
|
||||
a new string that is a copy of buffer.
|
||||
If buffer contains \0, string will be truncated.
|
||||
As for NXUniqueString, returned string should never be modified. */
|
||||
|
||||
OBJC_EXPORT NXAtom NXUniqueStringNoCopy(const char *string) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT NXAtom _Nullable
|
||||
NXUniqueStringNoCopy(const char * _Nullable string)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* If there is already a unique string equal to string, returns the original.
|
||||
Otherwise, string is entered in the table, without making a copy. Argument should then never be modified. */
|
||||
|
||||
OBJC_EXPORT char *NXCopyStringBuffer(const char *buffer) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT char * _Nullable
|
||||
NXCopyStringBuffer(const char * _Nullable buffer)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* given a buffer, allocates a new string copy of buffer.
|
||||
Buffer should be \0 terminated; returned string is \0 terminated. */
|
||||
|
||||
OBJC_EXPORT char *NXCopyStringBufferFromZone(const char *buffer, void *z) OBJC_HASH_AVAILABILITY;
|
||||
OBJC_EXPORT char * _Nullable
|
||||
NXCopyStringBufferFromZone(const char * _Nullable buffer, void * _Nullable z)
|
||||
OBJC_HASH_AVAILABILITY;
|
||||
/* given a buffer, allocates a new string copy of buffer.
|
||||
Buffer should be \0 terminated; returned string is \0 terminated. */
|
||||
|
||||
|
@ -562,7 +562,7 @@ static int accessUniqueString = 0;
|
||||
|
||||
static char *z = NULL;
|
||||
static size_t zSize = 0;
|
||||
static mutex_t uniquerLock;
|
||||
mutex_t NXUniqueStringLock;
|
||||
|
||||
static const char *CopyIntoReadOnly (const char *str) {
|
||||
size_t len = strlen (str) + 1;
|
||||
@ -574,7 +574,7 @@ static const char *CopyIntoReadOnly (const char *str) {
|
||||
return result;
|
||||
}
|
||||
|
||||
mutex_locker_t lock(uniquerLock);
|
||||
mutex_locker_t lock(NXUniqueStringLock);
|
||||
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 */
|
||||
|
@ -1 +0,0 @@
|
||||
../../NSObjCRuntime.h
|
@ -1 +0,0 @@
|
||||
../../NSObject.h
|
@ -1 +0,0 @@
|
||||
../../Object.h
|
@ -1 +0,0 @@
|
||||
../../Protocol.h
|
@ -1 +0,0 @@
|
||||
../../hashtable.h
|
@ -1 +0,0 @@
|
||||
../../hashtable2.h
|
@ -1 +0,0 @@
|
||||
../../maptable.h
|
@ -1 +0,0 @@
|
||||
../../message.h
|
@ -1 +0,0 @@
|
||||
../../objc-api.h
|
@ -1 +0,0 @@
|
||||
../../objc-auto.h
|
@ -1 +0,0 @@
|
||||
../../objc-class.h
|
@ -1 +0,0 @@
|
||||
../../objc-exception.h
|
@ -1 +0,0 @@
|
||||
../../objc-internal.h
|
@ -1 +0,0 @@
|
||||
../../objc-load.h
|
@ -1 +0,0 @@
|
||||
../../objc-runtime.h
|
@ -1 +0,0 @@
|
||||
../../objc-sync.h
|
@ -1 +0,0 @@
|
||||
../../objc.h
|
@ -1 +0,0 @@
|
||||
../../runtime.h
|
134
runtime/isa.h
Normal file
134
runtime/isa.h
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* Copyright (c) 2018 Apple Inc. All Rights Reserved.
|
||||
*
|
||||
* 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@
|
||||
*/
|
||||
/********************************************************************
|
||||
*
|
||||
* isa.h - Definitions of isa fields for C and assembly code.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#ifndef _OBJC_ISA_H_
|
||||
#define _OBJC_ISA_H_
|
||||
|
||||
#include "objc-config.h"
|
||||
|
||||
|
||||
#if (!SUPPORT_NONPOINTER_ISA && !SUPPORT_PACKED_ISA && !SUPPORT_INDEXED_ISA) ||\
|
||||
( SUPPORT_NONPOINTER_ISA && SUPPORT_PACKED_ISA && !SUPPORT_INDEXED_ISA) ||\
|
||||
( SUPPORT_NONPOINTER_ISA && !SUPPORT_PACKED_ISA && SUPPORT_INDEXED_ISA)
|
||||
// good config
|
||||
#else
|
||||
# error bad config
|
||||
#endif
|
||||
|
||||
|
||||
#if SUPPORT_PACKED_ISA
|
||||
|
||||
// extra_rc must be the MSB-most field (so it matches carry/overflow flags)
|
||||
// nonpointer must be the LSB (fixme or get rid of it)
|
||||
// shiftcls must occupy the same bits that a real class pointer would
|
||||
// bits + RC_ONE is equivalent to extra_rc + 1
|
||||
// RC_HALF is the high bit of extra_rc (i.e. half of its range)
|
||||
|
||||
// future expansion:
|
||||
// uintptr_t fast_rr : 1; // no r/r overrides
|
||||
// uintptr_t lock : 2; // lock for atomic property, @synch
|
||||
// uintptr_t extraBytes : 1; // allocated with extra bytes
|
||||
|
||||
# if __arm64__
|
||||
# define ISA_MASK 0x0000000ffffffff8ULL
|
||||
# define ISA_MAGIC_MASK 0x000003f000000001ULL
|
||||
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
|
||||
# define ISA_BITFIELD \
|
||||
uintptr_t nonpointer : 1; \
|
||||
uintptr_t has_assoc : 1; \
|
||||
uintptr_t has_cxx_dtor : 1; \
|
||||
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
|
||||
uintptr_t magic : 6; \
|
||||
uintptr_t weakly_referenced : 1; \
|
||||
uintptr_t deallocating : 1; \
|
||||
uintptr_t has_sidetable_rc : 1; \
|
||||
uintptr_t extra_rc : 19
|
||||
# define RC_ONE (1ULL<<45)
|
||||
# define RC_HALF (1ULL<<18)
|
||||
|
||||
# elif __x86_64__
|
||||
# define ISA_MASK 0x00007ffffffffff8ULL
|
||||
# define ISA_MAGIC_MASK 0x001f800000000001ULL
|
||||
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
|
||||
# define ISA_BITFIELD \
|
||||
uintptr_t nonpointer : 1; \
|
||||
uintptr_t has_assoc : 1; \
|
||||
uintptr_t has_cxx_dtor : 1; \
|
||||
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
|
||||
uintptr_t magic : 6; \
|
||||
uintptr_t weakly_referenced : 1; \
|
||||
uintptr_t deallocating : 1; \
|
||||
uintptr_t has_sidetable_rc : 1; \
|
||||
uintptr_t extra_rc : 8
|
||||
# define RC_ONE (1ULL<<56)
|
||||
# define RC_HALF (1ULL<<7)
|
||||
|
||||
# else
|
||||
# error unknown architecture for packed isa
|
||||
# endif
|
||||
|
||||
// SUPPORT_PACKED_ISA
|
||||
#endif
|
||||
|
||||
|
||||
#if SUPPORT_INDEXED_ISA
|
||||
|
||||
# if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__)
|
||||
// armv7k or arm64_32
|
||||
|
||||
# define ISA_INDEX_IS_NPI_BIT 0
|
||||
# define ISA_INDEX_IS_NPI_MASK 0x00000001
|
||||
# define ISA_INDEX_MASK 0x0001FFFC
|
||||
# define ISA_INDEX_SHIFT 2
|
||||
# define ISA_INDEX_BITS 15
|
||||
# define ISA_INDEX_COUNT (1 << ISA_INDEX_BITS)
|
||||
# define ISA_INDEX_MAGIC_MASK 0x001E0001
|
||||
# define ISA_INDEX_MAGIC_VALUE 0x001C0001
|
||||
# define ISA_BITFIELD \
|
||||
uintptr_t nonpointer : 1; \
|
||||
uintptr_t has_assoc : 1; \
|
||||
uintptr_t indexcls : 15; \
|
||||
uintptr_t magic : 4; \
|
||||
uintptr_t has_cxx_dtor : 1; \
|
||||
uintptr_t weakly_referenced : 1; \
|
||||
uintptr_t deallocating : 1; \
|
||||
uintptr_t has_sidetable_rc : 1; \
|
||||
uintptr_t extra_rc : 7
|
||||
# define RC_ONE (1ULL<<25)
|
||||
# define RC_HALF (1ULL<<6)
|
||||
|
||||
# else
|
||||
# error unknown architecture for indexed isa
|
||||
# endif
|
||||
|
||||
// SUPPORT_INDEXED_ISA
|
||||
#endif
|
||||
|
||||
|
||||
// _OBJC_ISA_H_
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -75,6 +75,15 @@ template<> struct DenseMapInfo<const char*> {
|
||||
return _objc_strhash(Val);
|
||||
}
|
||||
static bool isEqual(const char* const &LHS, const char* const &RHS) {
|
||||
if (LHS == RHS) {
|
||||
return true;
|
||||
}
|
||||
if (LHS == getEmptyKey() || RHS == getEmptyKey()) {
|
||||
return false;
|
||||
}
|
||||
if (LHS == getTombstoneKey() || RHS == getTombstoneKey()) {
|
||||
return false;
|
||||
}
|
||||
return 0 == strcmp(LHS, RHS);
|
||||
}
|
||||
};
|
||||
@ -195,6 +204,13 @@ struct DenseMapInfo<std::pair<T, U> > {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct DenseMapValueInfo {
|
||||
static inline bool isPurgeable(const T &value) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace objc
|
||||
|
||||
#endif
|
||||
|
293
runtime/llvm-DenseSet.h
Normal file
293
runtime/llvm-DenseSet.h
Normal file
@ -0,0 +1,293 @@
|
||||
//===- llvm/ADT/DenseSet.h - Dense probed hash table ------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the DenseSet and SmallDenseSet classes.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Taken from clang-1100.247.11.10.9
|
||||
|
||||
#ifndef LLVM_ADT_DENSESET_H
|
||||
#define LLVM_ADT_DENSESET_H
|
||||
|
||||
#include "llvm-DenseMap.h"
|
||||
#include "llvm-DenseMapInfo.h"
|
||||
#include "llvm-type_traits.h"
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#include "objc-private.h"
|
||||
|
||||
namespace objc {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct DenseSetEmpty {};
|
||||
|
||||
// Use the empty base class trick so we can create a DenseMap where the buckets
|
||||
// contain only a single item.
|
||||
template <typename KeyT> class DenseSetPair : public DenseSetEmpty {
|
||||
KeyT key;
|
||||
|
||||
public:
|
||||
KeyT &getFirst() { return key; }
|
||||
const KeyT &getFirst() const { return key; }
|
||||
DenseSetEmpty &getSecond() { return *this; }
|
||||
const DenseSetEmpty &getSecond() const { return *this; }
|
||||
};
|
||||
|
||||
/// Base class for DenseSet and DenseSmallSet.
|
||||
///
|
||||
/// MapTy should be either
|
||||
///
|
||||
/// DenseMap<ValueT, detail::DenseSetEmpty,
|
||||
/// DenseMapValueInfo<detail::DenseSetEmpty>,
|
||||
/// ValueInfoT, detail::DenseSetPair<ValueT>>
|
||||
///
|
||||
/// or the equivalent SmallDenseMap type. ValueInfoT must implement the
|
||||
/// DenseMapInfo "concept".
|
||||
template <typename ValueT, typename MapTy, typename ValueInfoT>
|
||||
class DenseSetImpl {
|
||||
static_assert(sizeof(typename MapTy::value_type) == sizeof(ValueT),
|
||||
"DenseMap buckets unexpectedly large!");
|
||||
MapTy TheMap;
|
||||
|
||||
template <typename T>
|
||||
using const_arg_type_t = typename const_pointer_or_const_ref<T>::type;
|
||||
|
||||
public:
|
||||
using key_type = ValueT;
|
||||
using value_type = ValueT;
|
||||
using size_type = unsigned;
|
||||
|
||||
explicit DenseSetImpl(unsigned InitialReserve = 0) : TheMap(InitialReserve) {}
|
||||
|
||||
DenseSetImpl(std::initializer_list<ValueT> Elems)
|
||||
: DenseSetImpl(PowerOf2Ceil(Elems.size())) {
|
||||
insert(Elems.begin(), Elems.end());
|
||||
}
|
||||
|
||||
bool empty() const { return TheMap.empty(); }
|
||||
size_type size() const { return TheMap.size(); }
|
||||
size_t getMemorySize() const { return TheMap.getMemorySize(); }
|
||||
|
||||
/// Grow the DenseSet so that it has at least Size buckets. Will not shrink
|
||||
/// the Size of the set.
|
||||
void resize(size_t Size) { TheMap.resize(Size); }
|
||||
|
||||
/// Grow the DenseSet so that it can contain at least \p NumEntries items
|
||||
/// before resizing again.
|
||||
void reserve(size_t Size) { TheMap.reserve(Size); }
|
||||
|
||||
void clear() {
|
||||
TheMap.clear();
|
||||
}
|
||||
|
||||
/// Return 1 if the specified key is in the set, 0 otherwise.
|
||||
size_type count(const_arg_type_t<ValueT> V) const {
|
||||
return TheMap.count(V);
|
||||
}
|
||||
|
||||
bool erase(const ValueT &V) {
|
||||
return TheMap.erase(V);
|
||||
}
|
||||
|
||||
void swap(DenseSetImpl &RHS) { TheMap.swap(RHS.TheMap); }
|
||||
|
||||
// Iterators.
|
||||
|
||||
class ConstIterator;
|
||||
|
||||
class Iterator {
|
||||
typename MapTy::iterator I;
|
||||
friend class DenseSetImpl;
|
||||
friend class ConstIterator;
|
||||
|
||||
public:
|
||||
using difference_type = typename MapTy::iterator::difference_type;
|
||||
using value_type = ValueT;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
Iterator() = default;
|
||||
Iterator(const typename MapTy::iterator &i) : I(i) {}
|
||||
|
||||
ValueT &operator*() { return I->getFirst(); }
|
||||
const ValueT &operator*() const { return I->getFirst(); }
|
||||
ValueT *operator->() { return &I->getFirst(); }
|
||||
const ValueT *operator->() const { return &I->getFirst(); }
|
||||
|
||||
Iterator& operator++() { ++I; return *this; }
|
||||
Iterator operator++(int) { auto T = *this; ++I; return T; }
|
||||
bool operator==(const ConstIterator& X) const { return I == X.I; }
|
||||
bool operator!=(const ConstIterator& X) const { return I != X.I; }
|
||||
};
|
||||
|
||||
class ConstIterator {
|
||||
typename MapTy::const_iterator I;
|
||||
friend class DenseSet;
|
||||
friend class Iterator;
|
||||
|
||||
public:
|
||||
using difference_type = typename MapTy::const_iterator::difference_type;
|
||||
using value_type = ValueT;
|
||||
using pointer = const value_type *;
|
||||
using reference = const value_type &;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
ConstIterator() = default;
|
||||
ConstIterator(const Iterator &B) : I(B.I) {}
|
||||
ConstIterator(const typename MapTy::const_iterator &i) : I(i) {}
|
||||
|
||||
const ValueT &operator*() const { return I->getFirst(); }
|
||||
const ValueT *operator->() const { return &I->getFirst(); }
|
||||
|
||||
ConstIterator& operator++() { ++I; return *this; }
|
||||
ConstIterator operator++(int) { auto T = *this; ++I; return T; }
|
||||
bool operator==(const ConstIterator& X) const { return I == X.I; }
|
||||
bool operator!=(const ConstIterator& X) const { return I != X.I; }
|
||||
};
|
||||
|
||||
using iterator = Iterator;
|
||||
using const_iterator = ConstIterator;
|
||||
|
||||
iterator begin() { return Iterator(TheMap.begin()); }
|
||||
iterator end() { return Iterator(TheMap.end()); }
|
||||
|
||||
const_iterator begin() const { return ConstIterator(TheMap.begin()); }
|
||||
const_iterator end() const { return ConstIterator(TheMap.end()); }
|
||||
|
||||
iterator find(const_arg_type_t<ValueT> V) { return Iterator(TheMap.find(V)); }
|
||||
const_iterator find(const_arg_type_t<ValueT> V) const {
|
||||
return ConstIterator(TheMap.find(V));
|
||||
}
|
||||
|
||||
/// Alternative version of find() which allows a different, and possibly less
|
||||
/// expensive, key type.
|
||||
/// The DenseMapInfo is responsible for supplying methods
|
||||
/// getHashValue(LookupKeyT) and isEqual(LookupKeyT, KeyT) for each key type
|
||||
/// used.
|
||||
template <class LookupKeyT>
|
||||
iterator find_as(const LookupKeyT &Val) {
|
||||
return Iterator(TheMap.find_as(Val));
|
||||
}
|
||||
template <class LookupKeyT>
|
||||
const_iterator find_as(const LookupKeyT &Val) const {
|
||||
return ConstIterator(TheMap.find_as(Val));
|
||||
}
|
||||
|
||||
void erase(Iterator I) { return TheMap.erase(I.I); }
|
||||
void erase(ConstIterator CI) { return TheMap.erase(CI.I); }
|
||||
|
||||
std::pair<iterator, bool> insert(const ValueT &V) {
|
||||
detail::DenseSetEmpty Empty;
|
||||
return TheMap.try_emplace(V, Empty);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(ValueT &&V) {
|
||||
detail::DenseSetEmpty Empty;
|
||||
return TheMap.try_emplace(std::move(V), Empty);
|
||||
}
|
||||
|
||||
/// Alternative version of insert that uses a different (and possibly less
|
||||
/// expensive) key type.
|
||||
template <typename LookupKeyT>
|
||||
std::pair<iterator, bool> insert_as(const ValueT &V,
|
||||
const LookupKeyT &LookupKey) {
|
||||
return TheMap.insert_as({V, detail::DenseSetEmpty()}, LookupKey);
|
||||
}
|
||||
template <typename LookupKeyT>
|
||||
std::pair<iterator, bool> insert_as(ValueT &&V, const LookupKeyT &LookupKey) {
|
||||
return TheMap.insert_as({std::move(V), detail::DenseSetEmpty()}, LookupKey);
|
||||
}
|
||||
|
||||
// Range insertion of values.
|
||||
template<typename InputIt>
|
||||
void insert(InputIt I, InputIt E) {
|
||||
for (; I != E; ++I)
|
||||
insert(*I);
|
||||
}
|
||||
};
|
||||
|
||||
/// Equality comparison for DenseSet.
|
||||
///
|
||||
/// Iterates over elements of LHS confirming that each element is also a member
|
||||
/// of RHS, and that RHS contains no additional values.
|
||||
/// Equivalent to N calls to RHS.count. Amortized complexity is linear, worst
|
||||
/// case is O(N^2) (if every hash collides).
|
||||
template <typename ValueT, typename MapTy, typename ValueInfoT>
|
||||
bool operator==(const DenseSetImpl<ValueT, MapTy, ValueInfoT> &LHS,
|
||||
const DenseSetImpl<ValueT, MapTy, ValueInfoT> &RHS) {
|
||||
if (LHS.size() != RHS.size())
|
||||
return false;
|
||||
|
||||
for (auto &E : LHS)
|
||||
if (!RHS.count(E))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Inequality comparison for DenseSet.
|
||||
///
|
||||
/// Equivalent to !(LHS == RHS). See operator== for performance notes.
|
||||
template <typename ValueT, typename MapTy, typename ValueInfoT>
|
||||
bool operator!=(const DenseSetImpl<ValueT, MapTy, ValueInfoT> &LHS,
|
||||
const DenseSetImpl<ValueT, MapTy, ValueInfoT> &RHS) {
|
||||
return !(LHS == RHS);
|
||||
}
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
/// Implements a dense probed hash-table based set.
|
||||
template <typename ValueT, typename ValueInfoT = DenseMapInfo<ValueT>>
|
||||
class DenseSet : public detail::DenseSetImpl<
|
||||
ValueT, DenseMap<ValueT, detail::DenseSetEmpty,
|
||||
DenseMapValueInfo<detail::DenseSetEmpty>,
|
||||
ValueInfoT, detail::DenseSetPair<ValueT>>,
|
||||
ValueInfoT> {
|
||||
using BaseT =
|
||||
detail::DenseSetImpl<ValueT,
|
||||
DenseMap<ValueT, detail::DenseSetEmpty,
|
||||
DenseMapValueInfo<detail::DenseSetEmpty>,
|
||||
ValueInfoT, detail::DenseSetPair<ValueT>>,
|
||||
ValueInfoT>;
|
||||
|
||||
public:
|
||||
using BaseT::BaseT;
|
||||
};
|
||||
|
||||
/// Implements a dense probed hash-table based set with some number of buckets
|
||||
/// stored inline.
|
||||
template <typename ValueT, unsigned InlineBuckets = 4,
|
||||
typename ValueInfoT = DenseMapInfo<ValueT>>
|
||||
class SmallDenseSet
|
||||
: public detail::DenseSetImpl<
|
||||
ValueT, SmallDenseMap<ValueT, detail::DenseSetEmpty, InlineBuckets,
|
||||
DenseMapValueInfo<detail::DenseSetEmpty>,
|
||||
ValueInfoT, detail::DenseSetPair<ValueT>>,
|
||||
ValueInfoT> {
|
||||
using BaseT = detail::DenseSetImpl<
|
||||
ValueT, SmallDenseMap<ValueT, detail::DenseSetEmpty, InlineBuckets,
|
||||
DenseMapValueInfo<detail::DenseSetEmpty>,
|
||||
ValueInfoT, detail::DenseSetPair<ValueT>>,
|
||||
ValueInfoT>;
|
||||
|
||||
public:
|
||||
using BaseT::BaseT;
|
||||
};
|
||||
|
||||
} // end namespace objc
|
||||
|
||||
#endif // LLVM_ADT_DENSESET_H
|
@ -1,24 +1,22 @@
|
||||
//===- llvm/Support/type_traits.h - Simplfied type traits -------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides a template class that determines if a type is a class or
|
||||
// not. The basic mechanism, based on using the pointer to member function of
|
||||
// a zero argument to a function was "boosted" from the boost type_traits
|
||||
// library. See http://www.boost.org/ for all the gory details.
|
||||
// This file provides useful additions to the standard type_traits library.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Taken from llvmCore-3425.0.31.
|
||||
// Taken from clang-1100.247.11.10.9
|
||||
|
||||
#ifndef LLVM_SUPPORT_TYPE_TRAITS_H
|
||||
#define LLVM_SUPPORT_TYPE_TRAITS_H
|
||||
|
||||
#define HAVE_STD_IS_TRIVIALLY_COPYABLE 1
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
@ -27,195 +25,180 @@
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
// This is actually the conforming implementation which works with abstract
|
||||
// classes. However, enough compilers have trouble with it that most will use
|
||||
// the one in boost/type_traits/object_traits.hpp. This implementation actually
|
||||
// works with VC7.0, but other interactions seem to fail when we use it.
|
||||
|
||||
namespace objc {
|
||||
|
||||
namespace dont_use
|
||||
{
|
||||
// These two functions should never be used. They are helpers to
|
||||
// the is_class template below. They cannot be located inside
|
||||
// is_class because doing so causes at least GCC to think that
|
||||
// the value of the "value" enumerator is not constant. Placing
|
||||
// them out here (for some strange reason) allows the sizeof
|
||||
// operator against them to magically be constant. This is
|
||||
// important to make the is_class<T>::value idiom zero cost. it
|
||||
// evaluates to a constant 1 or 0 depending on whether the
|
||||
// parameter T is a class or not (respectively).
|
||||
template<typename T> char is_class_helper(void(T::*)());
|
||||
template<typename T> double is_class_helper(...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct is_class
|
||||
{
|
||||
// is_class<> metafunction due to Paul Mensonides (leavings@attbi.com). For
|
||||
// more details:
|
||||
// http://groups.google.com/groups?hl=en&selm=000001c1cc83%24e154d5e0%247772e50c%40c161550a&rnum=1
|
||||
|
||||
/// Metafunction that determines whether the given type is either an
|
||||
/// integral type or an enumeration type, including enum classes.
|
||||
///
|
||||
/// Note that this accepts potentially more integral types than is_integral
|
||||
/// because it is based on being implicitly convertible to an integral type.
|
||||
/// Also note that enum classes aren't implicitly convertible to integral types,
|
||||
/// the value may therefore need to be explicitly converted before being used.
|
||||
template <typename T> class is_integral_or_enum {
|
||||
using UnderlyingT = typename std::remove_reference<T>::type;
|
||||
|
||||
public:
|
||||
static const bool value =
|
||||
sizeof(char) == sizeof(dont_use::is_class_helper<T>(0));
|
||||
!std::is_class<UnderlyingT>::value && // Filter conversion operators.
|
||||
!std::is_pointer<UnderlyingT>::value &&
|
||||
!std::is_floating_point<UnderlyingT>::value &&
|
||||
(std::is_enum<UnderlyingT>::value ||
|
||||
std::is_convertible<UnderlyingT, unsigned long long>::value);
|
||||
};
|
||||
|
||||
|
||||
/// isPodLike - This is a type trait that is used to determine whether a given
|
||||
/// type can be copied around with memcpy instead of running ctors etc.
|
||||
|
||||
/// If T is a pointer, just return it. If it is not, return T&.
|
||||
template<typename T, typename Enable = void>
|
||||
struct add_lvalue_reference_if_not_pointer { using type = T &; };
|
||||
|
||||
template <typename T>
|
||||
struct isPodLike {
|
||||
#if __has_feature(is_trivially_copyable)
|
||||
// If the compiler supports the is_trivially_copyable trait use it, as it
|
||||
// matches the definition of isPodLike closely.
|
||||
static const bool value = __is_trivially_copyable(T);
|
||||
#else
|
||||
// If we don't know anything else, we can (at least) assume that all non-class
|
||||
// types are PODs.
|
||||
static const bool value = !is_class<T>::value;
|
||||
struct add_lvalue_reference_if_not_pointer<
|
||||
T, typename std::enable_if<std::is_pointer<T>::value>::type> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
/// If T is a pointer to X, return a pointer to const X. If it is not,
|
||||
/// return const T.
|
||||
template<typename T, typename Enable = void>
|
||||
struct add_const_past_pointer { using type = const T; };
|
||||
|
||||
template <typename T>
|
||||
struct add_const_past_pointer<
|
||||
T, typename std::enable_if<std::is_pointer<T>::value>::type> {
|
||||
using type = const typename std::remove_pointer<T>::type *;
|
||||
};
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct const_pointer_or_const_ref {
|
||||
using type = const T &;
|
||||
};
|
||||
template <typename T>
|
||||
struct const_pointer_or_const_ref<
|
||||
T, typename std::enable_if<std::is_pointer<T>::value>::type> {
|
||||
using type = typename add_const_past_pointer<T>::type;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
/// Internal utility to detect trivial copy construction.
|
||||
template<typename T> union copy_construction_triviality_helper {
|
||||
T t;
|
||||
copy_construction_triviality_helper() = default;
|
||||
copy_construction_triviality_helper(const copy_construction_triviality_helper&) = default;
|
||||
~copy_construction_triviality_helper() = default;
|
||||
};
|
||||
/// Internal utility to detect trivial move construction.
|
||||
template<typename T> union move_construction_triviality_helper {
|
||||
T t;
|
||||
move_construction_triviality_helper() = default;
|
||||
move_construction_triviality_helper(move_construction_triviality_helper&&) = default;
|
||||
~move_construction_triviality_helper() = default;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
union trivial_helper {
|
||||
T t;
|
||||
};
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
/// An implementation of `std::is_trivially_copy_constructible` since we have
|
||||
/// users with STLs that don't yet include it.
|
||||
template <typename T>
|
||||
struct is_trivially_copy_constructible
|
||||
: std::is_copy_constructible<
|
||||
::objc::detail::copy_construction_triviality_helper<T>> {};
|
||||
template <typename T>
|
||||
struct is_trivially_copy_constructible<T &> : std::true_type {};
|
||||
template <typename T>
|
||||
struct is_trivially_copy_constructible<T &&> : std::false_type {};
|
||||
|
||||
/// An implementation of `std::is_trivially_move_constructible` since we have
|
||||
/// users with STLs that don't yet include it.
|
||||
template <typename T>
|
||||
struct is_trivially_move_constructible
|
||||
: std::is_move_constructible<
|
||||
::objc::detail::move_construction_triviality_helper<T>> {};
|
||||
template <typename T>
|
||||
struct is_trivially_move_constructible<T &> : std::true_type {};
|
||||
template <typename T>
|
||||
struct is_trivially_move_constructible<T &&> : std::true_type {};
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct is_copy_assignable {
|
||||
template<class F>
|
||||
static auto get(F*) -> decltype(std::declval<F &>() = std::declval<const F &>(), std::true_type{});
|
||||
static std::false_type get(...);
|
||||
static constexpr bool value = decltype(get((T*)nullptr))::value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_move_assignable {
|
||||
template<class F>
|
||||
static auto get(F*) -> decltype(std::declval<F &>() = std::declval<F &&>(), std::true_type{});
|
||||
static std::false_type get(...);
|
||||
static constexpr bool value = decltype(get((T*)nullptr))::value;
|
||||
};
|
||||
|
||||
|
||||
// An implementation of `std::is_trivially_copyable` since STL version
|
||||
// is not equally supported by all compilers, especially GCC 4.9.
|
||||
// Uniform implementation of this trait is important for ABI compatibility
|
||||
// as it has an impact on SmallVector's ABI (among others).
|
||||
template <typename T>
|
||||
class is_trivially_copyable {
|
||||
|
||||
// copy constructors
|
||||
static constexpr bool has_trivial_copy_constructor =
|
||||
std::is_copy_constructible<detail::trivial_helper<T>>::value;
|
||||
static constexpr bool has_deleted_copy_constructor =
|
||||
!std::is_copy_constructible<T>::value;
|
||||
|
||||
// move constructors
|
||||
static constexpr bool has_trivial_move_constructor =
|
||||
std::is_move_constructible<detail::trivial_helper<T>>::value;
|
||||
static constexpr bool has_deleted_move_constructor =
|
||||
!std::is_move_constructible<T>::value;
|
||||
|
||||
// copy assign
|
||||
static constexpr bool has_trivial_copy_assign =
|
||||
is_copy_assignable<detail::trivial_helper<T>>::value;
|
||||
static constexpr bool has_deleted_copy_assign =
|
||||
!is_copy_assignable<T>::value;
|
||||
|
||||
// move assign
|
||||
static constexpr bool has_trivial_move_assign =
|
||||
is_move_assignable<detail::trivial_helper<T>>::value;
|
||||
static constexpr bool has_deleted_move_assign =
|
||||
!is_move_assignable<T>::value;
|
||||
|
||||
// destructor
|
||||
static constexpr bool has_trivial_destructor =
|
||||
std::is_destructible<detail::trivial_helper<T>>::value;
|
||||
|
||||
public:
|
||||
|
||||
static constexpr bool value =
|
||||
has_trivial_destructor &&
|
||||
(has_deleted_move_assign || has_trivial_move_assign) &&
|
||||
(has_deleted_move_constructor || has_trivial_move_constructor) &&
|
||||
(has_deleted_copy_assign || has_trivial_copy_assign) &&
|
||||
(has_deleted_copy_constructor || has_trivial_copy_constructor);
|
||||
|
||||
#ifdef HAVE_STD_IS_TRIVIALLY_COPYABLE
|
||||
static_assert(value == std::is_trivially_copyable<T>::value,
|
||||
"inconsistent behavior between llvm:: and std:: implementation of is_trivially_copyable");
|
||||
#endif
|
||||
};
|
||||
|
||||
// std::pair's are pod-like if their elements are.
|
||||
template<typename T, typename U>
|
||||
struct isPodLike<std::pair<T, U> > {
|
||||
static const bool value = isPodLike<T>::value && isPodLike<U>::value;
|
||||
};
|
||||
|
||||
|
||||
template <class T, T v>
|
||||
struct integral_constant {
|
||||
typedef T value_type;
|
||||
static const value_type value = v;
|
||||
typedef integral_constant<T,v> type;
|
||||
operator value_type() { return value; }
|
||||
};
|
||||
|
||||
typedef integral_constant<bool, true> true_type;
|
||||
typedef integral_constant<bool, false> false_type;
|
||||
|
||||
/// \brief Metafunction that determines whether the two given types are
|
||||
/// equivalent.
|
||||
template<typename T, typename U> struct is_same : public false_type {};
|
||||
template<typename T> struct is_same<T, T> : public true_type {};
|
||||
|
||||
/// \brief Metafunction that removes const qualification from a type.
|
||||
template <typename T> struct remove_const { typedef T type; };
|
||||
template <typename T> struct remove_const<const T> { typedef T type; };
|
||||
|
||||
/// \brief Metafunction that removes volatile qualification from a type.
|
||||
template <typename T> struct remove_volatile { typedef T type; };
|
||||
template <typename T> struct remove_volatile<volatile T> { typedef T type; };
|
||||
|
||||
/// \brief Metafunction that removes both const and volatile qualification from
|
||||
/// a type.
|
||||
template <typename T> struct remove_cv {
|
||||
typedef typename remove_const<typename remove_volatile<T>::type>::type type;
|
||||
};
|
||||
|
||||
/// \brief Helper to implement is_integral metafunction.
|
||||
template <typename T> struct is_integral_impl : false_type {};
|
||||
template <> struct is_integral_impl< bool> : true_type {};
|
||||
template <> struct is_integral_impl< char> : true_type {};
|
||||
template <> struct is_integral_impl< signed char> : true_type {};
|
||||
template <> struct is_integral_impl<unsigned char> : true_type {};
|
||||
template <> struct is_integral_impl< wchar_t> : true_type {};
|
||||
template <> struct is_integral_impl< short> : true_type {};
|
||||
template <> struct is_integral_impl<unsigned short> : true_type {};
|
||||
template <> struct is_integral_impl< int> : true_type {};
|
||||
template <> struct is_integral_impl<unsigned int> : true_type {};
|
||||
template <> struct is_integral_impl< long> : true_type {};
|
||||
template <> struct is_integral_impl<unsigned long> : true_type {};
|
||||
template <> struct is_integral_impl< long long> : true_type {};
|
||||
template <> struct is_integral_impl<unsigned long long> : true_type {};
|
||||
|
||||
/// \brief Metafunction that determines whether the given type is an integral
|
||||
/// type.
|
||||
template <typename T>
|
||||
struct is_integral : is_integral_impl<T> {};
|
||||
|
||||
/// \brief Metafunction to remove reference from a type.
|
||||
template <typename T> struct remove_reference { typedef T type; };
|
||||
template <typename T> struct remove_reference<T&> { typedef T type; };
|
||||
|
||||
/// \brief Metafunction that determines whether the given type is a pointer
|
||||
/// type.
|
||||
template <typename T> struct is_pointer : false_type {};
|
||||
template <typename T> struct is_pointer<T*> : true_type {};
|
||||
template <typename T> struct is_pointer<T* const> : true_type {};
|
||||
template <typename T> struct is_pointer<T* volatile> : true_type {};
|
||||
template <typename T> struct is_pointer<T* const volatile> : true_type {};
|
||||
|
||||
/// \brief Metafunction that determines whether the given type is either an
|
||||
/// integral type or an enumeration type.
|
||||
///
|
||||
/// Note that this accepts potentially more integral types than we whitelist
|
||||
/// above for is_integral because it is based on merely being convertible
|
||||
/// implicitly to an integral type.
|
||||
template <typename T> class is_integral_or_enum {
|
||||
// Provide an overload which can be called with anything implicitly
|
||||
// convertible to an unsigned long long. This should catch integer types and
|
||||
// enumeration types at least. We blacklist classes with conversion operators
|
||||
// below.
|
||||
static double check_int_convertible(unsigned long long);
|
||||
static char check_int_convertible(...);
|
||||
|
||||
typedef typename remove_reference<T>::type UnderlyingT;
|
||||
static UnderlyingT &nonce_instance;
|
||||
|
||||
public:
|
||||
static const bool
|
||||
value = (!is_class<UnderlyingT>::value && !is_pointer<UnderlyingT>::value &&
|
||||
!is_same<UnderlyingT, float>::value &&
|
||||
!is_same<UnderlyingT, double>::value &&
|
||||
sizeof(char) != sizeof(check_int_convertible(nonce_instance)));
|
||||
class is_trivially_copyable<T*> : public std::true_type {
|
||||
};
|
||||
|
||||
// enable_if_c - Enable/disable a template based on a metafunction
|
||||
template<bool Cond, typename T = void>
|
||||
struct enable_if_c {
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template<typename T> struct enable_if_c<false, T> { };
|
||||
|
||||
// enable_if - Enable/disable a template based on a metafunction
|
||||
template<typename Cond, typename T = void>
|
||||
struct enable_if : public enable_if_c<Cond::value, T> { };
|
||||
|
||||
namespace dont_use {
|
||||
template<typename Base> char base_of_helper(const volatile Base*);
|
||||
template<typename Base> double base_of_helper(...);
|
||||
}
|
||||
|
||||
/// is_base_of - Metafunction to determine whether one type is a base class of
|
||||
/// (or identical to) another type.
|
||||
template<typename Base, typename Derived>
|
||||
struct is_base_of {
|
||||
static const bool value
|
||||
= is_class<Base>::value && is_class<Derived>::value &&
|
||||
sizeof(char) == sizeof(dont_use::base_of_helper<Base>((Derived*)0));
|
||||
};
|
||||
|
||||
// remove_pointer - Metafunction to turn Foo* into Foo. Defined in
|
||||
// C++0x [meta.trans.ptr].
|
||||
template <typename T> struct remove_pointer { typedef T type; };
|
||||
template <typename T> struct remove_pointer<T*> { typedef T type; };
|
||||
template <typename T> struct remove_pointer<T*const> { typedef T type; };
|
||||
template <typename T> struct remove_pointer<T*volatile> { typedef T type; };
|
||||
template <typename T> struct remove_pointer<T*const volatile> {
|
||||
typedef T type; };
|
||||
|
||||
template <bool, typename T, typename F>
|
||||
struct conditional { typedef T type; };
|
||||
|
||||
template <typename T, typename F>
|
||||
struct conditional<false, T, F> { typedef F type; };
|
||||
|
||||
}
|
||||
} // end namespace llvm
|
||||
|
||||
#ifdef LLVM_DEFINED_HAS_FEATURE
|
||||
#undef __has_feature
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif // LLVM_SUPPORT_TYPE_TRAITS_H
|
||||
|
@ -30,9 +30,8 @@
|
||||
#define _OBJC_MAPTABLE_H_
|
||||
|
||||
#ifndef _OBJC_PRIVATE_H_
|
||||
# define OBJC_MAP_AVAILABILITY \
|
||||
__OSX_DEPRECATED(10.0, 10.1, "NXMapTable is deprecated") \
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE
|
||||
# define OBJC_MAP_AVAILABILITY \
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.0, 10.1, "NXMapTable is deprecated")
|
||||
#else
|
||||
# define OBJC_MAP_AVAILABILITY
|
||||
#endif
|
||||
@ -49,16 +48,21 @@ __BEGIN_DECLS
|
||||
|
||||
typedef struct _NXMapTable {
|
||||
/* private data structure; may change */
|
||||
const struct _NXMapTablePrototype *prototype;
|
||||
const struct _NXMapTablePrototype * _Nonnull prototype;
|
||||
unsigned count;
|
||||
unsigned nbBucketsMinusOne;
|
||||
void *buckets;
|
||||
void * _Nullable buckets;
|
||||
} NXMapTable OBJC_MAP_AVAILABILITY;
|
||||
|
||||
typedef struct _NXMapTablePrototype {
|
||||
unsigned (*hash)(NXMapTable *, const void *key);
|
||||
int (*isEqual)(NXMapTable *, const void *key1, const void *key2);
|
||||
void (*free)(NXMapTable *, void *key, void *value);
|
||||
unsigned (* _Nonnull hash)(NXMapTable * _Nonnull,
|
||||
const void * _Nullable key);
|
||||
int (* _Nonnull isEqual)(NXMapTable * _Nonnull,
|
||||
const void * _Nullable key1,
|
||||
const void * _Nullable key2);
|
||||
void (* _Nonnull free)(NXMapTable * _Nonnull,
|
||||
void * _Nullable key,
|
||||
void * _Nullable value);
|
||||
int style; /* reserved for future expansion; currently 0 */
|
||||
} NXMapTablePrototype OBJC_MAP_AVAILABILITY;
|
||||
|
||||
@ -70,36 +74,59 @@ typedef struct _NXMapTablePrototype {
|
||||
C - isEqual(key1, key2) => key1 == key2
|
||||
*/
|
||||
|
||||
#define NX_MAPNOTAKEY ((void *)(-1))
|
||||
#define NX_MAPNOTAKEY ((void * _Nonnull)(-1))
|
||||
|
||||
/*************** Functions ***************/
|
||||
|
||||
OBJC_EXPORT NXMapTable *NXCreateMapTableFromZone(NXMapTablePrototype prototype, unsigned capacity, void *z) OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT NXMapTable *NXCreateMapTable(NXMapTablePrototype prototype, unsigned capacity) OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT NXMapTable * _Nonnull
|
||||
NXCreateMapTableFromZone(NXMapTablePrototype prototype,
|
||||
unsigned capacity, void * _Nullable z)
|
||||
OBJC_MAP_AVAILABILITY;
|
||||
|
||||
OBJC_EXPORT NXMapTable * _Nonnull
|
||||
NXCreateMapTable(NXMapTablePrototype prototype, unsigned capacity)
|
||||
OBJC_MAP_AVAILABILITY;
|
||||
/* capacity is only a hint; 0 creates a small table */
|
||||
|
||||
OBJC_EXPORT void NXFreeMapTable(NXMapTable *table) OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT void
|
||||
NXFreeMapTable(NXMapTable * _Nonnull table)
|
||||
OBJC_MAP_AVAILABILITY;
|
||||
/* call free for each pair, and recovers table */
|
||||
|
||||
OBJC_EXPORT void NXResetMapTable(NXMapTable *table) OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT void
|
||||
NXResetMapTable(NXMapTable * _Nonnull table)
|
||||
OBJC_MAP_AVAILABILITY;
|
||||
/* free each pair; keep current capacity */
|
||||
|
||||
OBJC_EXPORT BOOL NXCompareMapTables(NXMapTable *table1, NXMapTable *table2) OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT BOOL
|
||||
NXCompareMapTables(NXMapTable * _Nonnull table1, NXMapTable * _Nonnull table2)
|
||||
OBJC_MAP_AVAILABILITY;
|
||||
/* Returns YES if the two sets are equal (each member of table1 in table2, and table have same size) */
|
||||
|
||||
OBJC_EXPORT unsigned NXCountMapTable(NXMapTable *table) OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT unsigned
|
||||
NXCountMapTable(NXMapTable * _Nonnull table)
|
||||
OBJC_MAP_AVAILABILITY;
|
||||
/* current number of data in table */
|
||||
|
||||
OBJC_EXPORT void *NXMapMember(NXMapTable *table, const void *key, void **value) OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT void * _Nullable
|
||||
NXMapMember(NXMapTable * _Nonnull table, const void * _Nullable key,
|
||||
void * _Nullable * _Nonnull value) OBJC_MAP_AVAILABILITY;
|
||||
/* return original table key or NX_MAPNOTAKEY. If key is found, value is set */
|
||||
|
||||
OBJC_EXPORT void *NXMapGet(NXMapTable *table, const void *key) OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT void * _Nullable
|
||||
NXMapGet(NXMapTable * _Nonnull table, const void * _Nullable key)
|
||||
OBJC_MAP_AVAILABILITY;
|
||||
/* return original corresponding value or NULL. When NULL need be stored as value, NXMapMember can be used to test for presence */
|
||||
|
||||
OBJC_EXPORT void *NXMapInsert(NXMapTable *table, const void *key, const void *value) OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT void * _Nullable
|
||||
NXMapInsert(NXMapTable * _Nonnull table, const void * _Nullable key,
|
||||
const void * _Nullable value)
|
||||
OBJC_MAP_AVAILABILITY;
|
||||
/* override preexisting pair; Return previous value or NULL. */
|
||||
|
||||
OBJC_EXPORT void *NXMapRemove(NXMapTable *table, const void *key) OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT void * _Nullable
|
||||
NXMapRemove(NXMapTable * _Nonnull table, const void * _Nullable key)
|
||||
OBJC_MAP_AVAILABILITY;
|
||||
/* previous value or NULL is returned */
|
||||
|
||||
/* Iteration over all elements of a table consists in setting up an iteration state and then to progress until all entries have been visited. An example of use for counting elements in a table is:
|
||||
@ -115,22 +142,31 @@ OBJC_EXPORT void *NXMapRemove(NXMapTable *table, const void *key) OBJC_MAP_AVAIL
|
||||
typedef struct {int index;} NXMapState OBJC_MAP_AVAILABILITY;
|
||||
/* callers should not rely on actual contents of the struct */
|
||||
|
||||
OBJC_EXPORT NXMapState NXInitMapState(NXMapTable *table) OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT NXMapState
|
||||
NXInitMapState(NXMapTable * _Nonnull table)
|
||||
OBJC_MAP_AVAILABILITY;
|
||||
|
||||
OBJC_EXPORT int NXNextMapState(NXMapTable *table, NXMapState *state, const void **key, const void **value) OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT int
|
||||
NXNextMapState(NXMapTable * _Nonnull table, NXMapState * _Nonnull state,
|
||||
const void * _Nullable * _Nonnull key,
|
||||
const void * _Nullable * _Nonnull value)
|
||||
OBJC_MAP_AVAILABILITY;
|
||||
/* returns 0 when all elements have been visited */
|
||||
|
||||
/*************** Conveniences ***************/
|
||||
|
||||
OBJC_EXPORT const NXMapTablePrototype NXPtrValueMapPrototype OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT const NXMapTablePrototype NXPtrValueMapPrototype
|
||||
OBJC_MAP_AVAILABILITY;
|
||||
/* hashing is pointer/integer hashing;
|
||||
isEqual is identity;
|
||||
free is no-op. */
|
||||
OBJC_EXPORT const NXMapTablePrototype NXStrValueMapPrototype OBJC_MAP_AVAILABILITY;
|
||||
OBJC_EXPORT const NXMapTablePrototype NXStrValueMapPrototype
|
||||
OBJC_MAP_AVAILABILITY;
|
||||
/* hashing is string hashing;
|
||||
isEqual is strcmp;
|
||||
free is no-op. */
|
||||
OBJC_EXPORT const NXMapTablePrototype NXObjectMapPrototype OBJC2_UNAVAILABLE;
|
||||
OBJC_EXPORT const NXMapTablePrototype NXObjectMapPrototype
|
||||
OBJC2_UNAVAILABLE;
|
||||
/* for objects; uses methods: hash, isEqual:, free, all for key. */
|
||||
|
||||
__END_DECLS
|
||||
|
@ -161,11 +161,107 @@ BOOL NXCompareMapTables(NXMapTable *table1, NXMapTable *table2) {
|
||||
|
||||
unsigned NXCountMapTable(NXMapTable *table) { return table->count; }
|
||||
|
||||
#if __x86_64__
|
||||
extern "C" void __NXMAPTABLE_CORRUPTED__
|
||||
(const void *table, const void *buckets, uint64_t count,
|
||||
uint64_t nbBucketsMinusOne, uint64_t badkeys, uint64_t index,
|
||||
uint64_t index2, uint64_t pairIndexes, const void *key1,
|
||||
const void *value1, const void *key2, const void *value2,
|
||||
const void *key3, const void *value3);
|
||||
|
||||
static int _mapStrIsEqual(NXMapTable *table, const void *key1, const void *key2);
|
||||
|
||||
asm("\n .text"
|
||||
"\n .private_extern ___NXMAPTABLE_CORRUPTED__"
|
||||
"\n ___NXMAPTABLE_CORRUPTED__:"
|
||||
// push a frame for the unwinder to see
|
||||
"\n pushq %rbp"
|
||||
"\n mov %rsp, %rbp"
|
||||
// push register parameters to the stack in reverse order
|
||||
"\n pushq %r9"
|
||||
"\n pushq %r8"
|
||||
"\n pushq %rcx"
|
||||
"\n pushq %rdx"
|
||||
"\n pushq %rsi"
|
||||
"\n pushq %rdi"
|
||||
// pop the pushed register parameters into their destinations
|
||||
"\n popq %rax" // table
|
||||
"\n popq %rbx" // buckets
|
||||
"\n popq %rcx" // count
|
||||
"\n popq %rdx" // nbBucketsMinusOne
|
||||
"\n popq %rdi" // badkeys
|
||||
"\n popq %rsi" // index
|
||||
// read stack parameters into their destinations
|
||||
"\n mov 0*8+16(%rbp), %r8" // index2
|
||||
"\n mov 1*8+16(%rbp), %r9" // pairIndexes
|
||||
"\n mov 2*8+16(%rbp), %r10" // key1
|
||||
"\n mov 3*8+16(%rbp), %r11" // value1
|
||||
"\n mov 4*8+16(%rbp), %r12" // key2
|
||||
"\n mov 5*8+16(%rbp), %r13" // value2
|
||||
"\n mov 6*8+16(%rbp), %r14" // key3
|
||||
"\n mov 7*8+16(%rbp), %r15" // value3
|
||||
"\n ud2");
|
||||
#endif
|
||||
|
||||
// Look for a particular case of data corruption (rdar://36373000)
|
||||
// and investigate it further before crashing.
|
||||
static void validateKey(NXMapTable *table, MapPair *pair,
|
||||
unsigned index, unsigned index2)
|
||||
{
|
||||
#if __x86_64__
|
||||
# define BADKEY ((void * _Nonnull)(0xfffffffffffffffeULL))
|
||||
if (pair->key != BADKEY ||
|
||||
table->prototype->isEqual != _mapStrIsEqual)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_objc_inform_now_and_on_crash
|
||||
("NXMapTable %p (%p) has invalid key/value pair %p->%p (%p)",
|
||||
table, table->buckets, pair->key, pair->value, pair);
|
||||
_objc_inform_now_and_on_crash
|
||||
("table %p, buckets %p, count %u, nbNucketsMinusOne %u, "
|
||||
"prototype %p (hash %p, isEqual %p, free %p)",
|
||||
table, table->buckets, table->count, table->nbBucketsMinusOne,
|
||||
table->prototype, table->prototype->hash, table->prototype->isEqual,
|
||||
table->prototype->free);
|
||||
|
||||
// Count the number of bad keys in the table.
|
||||
MapPair *pairs = (MapPair *)table->buckets;
|
||||
unsigned badKeys = 0;
|
||||
for (unsigned i = 0; i < table->nbBucketsMinusOne+1; i++) {
|
||||
if (pairs[i].key == BADKEY) badKeys++;
|
||||
}
|
||||
|
||||
_objc_inform_now_and_on_crash("%u invalid keys in table", badKeys);
|
||||
|
||||
// Record some additional key pairs for posterity.
|
||||
unsigned pair2Index = nextIndex(table, index);
|
||||
unsigned pair3Index = nextIndex(table, pair2Index);
|
||||
MapPair *pair2 = pairs + pair2Index;
|
||||
MapPair *pair3 = pairs + pair3Index;
|
||||
uint64_t pairIndexes = ((uint64_t)pair2Index << 32) | pair3Index;
|
||||
|
||||
// Save a bunch of values to registers so we can see them in the crash log.
|
||||
__NXMAPTABLE_CORRUPTED__
|
||||
(// rax, rbx, rcx, rdx
|
||||
table, table->buckets, table->count, table->nbBucketsMinusOne,
|
||||
// rdi, rsi, skip rbp, skip rsp
|
||||
badKeys, index,
|
||||
// r8, r9, r10, r11
|
||||
index2, pairIndexes, pair->key, pair->value,
|
||||
// r12, r13, r14, r15
|
||||
pair2->key, pair2->value, pair3->key, pair3->value);
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
validateKey(table, pair, index, index);
|
||||
|
||||
if (isEqual(table, pair->key, key)) {
|
||||
*value = (void *)pair->value;
|
||||
return (void *)pair->key;
|
||||
@ -174,6 +270,7 @@ static INLINE void *_NXMapMember(NXMapTable *table, const void *key, void **valu
|
||||
while ((index2 = nextIndex(table, index2)) != index) {
|
||||
pair = pairs + index2;
|
||||
if (pair->key == NX_MAPNOTAKEY) return NX_MAPNOTAKEY;
|
||||
validateKey(table, pair, index, index2);
|
||||
if (isEqual(table, pair->key, key)) {
|
||||
*value = (void *)pair->value;
|
||||
return (void *)pair->key;
|
||||
@ -244,7 +341,7 @@ void *NXMapInsert(NXMapTable *table, const void *key, const void *value) {
|
||||
while ((index2 = nextIndex(table, index2)) != index) {
|
||||
pair = pairs + index2;
|
||||
if (pair->key == NX_MAPNOTAKEY) {
|
||||
pair->key = key; pair->value = value;
|
||||
pair->key = key; pair->value = value;
|
||||
table->count++;
|
||||
if (table->count * 4 > numBuckets * 3) _NXMapRehash(table);
|
||||
return NULL;
|
||||
@ -261,8 +358,6 @@ void *NXMapInsert(NXMapTable *table, const void *key, const void *value) {
|
||||
}
|
||||
}
|
||||
|
||||
static int mapRemove = 0;
|
||||
|
||||
void *NXMapRemove(NXMapTable *table, const void *key) {
|
||||
MapPair *pairs = (MapPair *)table->buckets;
|
||||
unsigned index = bucketOf(table, key);
|
||||
@ -271,7 +366,6 @@ void *NXMapRemove(NXMapTable *table, const void *key) {
|
||||
int found = 0;
|
||||
const void *old = NULL;
|
||||
if (pair->key == NX_MAPNOTAKEY) return NULL;
|
||||
mapRemove ++;
|
||||
/* compute chain */
|
||||
{
|
||||
unsigned index2 = index;
|
||||
|
@ -24,27 +24,23 @@
|
||||
#ifndef _OBJC_MESSAGE_H
|
||||
#define _OBJC_MESSAGE_H
|
||||
|
||||
#pragma GCC system_header
|
||||
|
||||
#include <objc/objc.h>
|
||||
#include <objc/runtime.h>
|
||||
|
||||
#pragma GCC system_header
|
||||
|
||||
#ifndef OBJC_SUPER
|
||||
#define OBJC_SUPER
|
||||
|
||||
/// Specifies the superclass of an instance.
|
||||
struct objc_super {
|
||||
/// Specifies an instance of a class.
|
||||
__unsafe_unretained id receiver;
|
||||
__unsafe_unretained _Nonnull id receiver;
|
||||
|
||||
/// Specifies the particular superclass of the instance to message.
|
||||
#if !defined(__cplusplus) && !__OBJC2__
|
||||
/* For compatibility with old objc-runtime.h header */
|
||||
__unsafe_unretained Class class;
|
||||
__unsafe_unretained _Nonnull Class class;
|
||||
#else
|
||||
__unsafe_unretained Class super_class;
|
||||
__unsafe_unretained _Nonnull Class super_class;
|
||||
#endif
|
||||
/* super_class is the first class to search */
|
||||
};
|
||||
@ -61,10 +57,16 @@ struct objc_super {
|
||||
* before being called.
|
||||
*/
|
||||
#if !OBJC_OLD_DISPATCH_PROTOTYPES
|
||||
OBJC_EXPORT void objc_msgSend(void /* id self, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wincompatible-library-redeclaration"
|
||||
OBJC_EXPORT void
|
||||
objc_msgSend(void /* id self, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
/**
|
||||
* Sends a message with a simple return value to an instance of a class.
|
||||
@ -82,8 +84,9 @@ OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ...
|
||||
* other messages are sent using \c objc_msgSend. Methods that have data structures as return values
|
||||
* are sent using \c objc_msgSendSuper_stret and \c objc_msgSend_stret.
|
||||
*/
|
||||
OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
|
||||
/**
|
||||
* Sends a message with a simple return value to the superclass of an instance of a class.
|
||||
*
|
||||
@ -98,8 +101,9 @@ OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
|
||||
*
|
||||
* @see objc_msgSend
|
||||
*/
|
||||
OBJC_EXPORT id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
|
||||
#endif
|
||||
|
||||
|
||||
@ -113,20 +117,27 @@ OBJC_EXPORT id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
|
||||
* before being called.
|
||||
*/
|
||||
#if !OBJC_OLD_DISPATCH_PROTOTYPES
|
||||
OBJC_EXPORT void objc_msgSend_stret(void /* id self, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wincompatible-library-redeclaration"
|
||||
OBJC_EXPORT void
|
||||
objc_msgSend_stret(void /* id self, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
OBJC_EXPORT void objc_msgSendSuper_stret(void /* struct objc_super *super, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgSendSuper_stret(void /* struct objc_super *super, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
/**
|
||||
* Sends a message with a data-structure return value to an instance of a class.
|
||||
*
|
||||
* @see objc_msgSend
|
||||
*/
|
||||
OBJC_EXPORT void objc_msgSend_stret(id self, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
|
||||
OBJC_EXPORT void
|
||||
objc_msgSend_stret(id _Nullable self, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
@ -134,8 +145,10 @@ OBJC_EXPORT void objc_msgSend_stret(id self, SEL op, ...)
|
||||
*
|
||||
* @see objc_msgSendSuper
|
||||
*/
|
||||
OBJC_EXPORT void objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
|
||||
OBJC_EXPORT void
|
||||
objc_msgSendSuper_stret(struct objc_super * _Nonnull super,
|
||||
SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
#endif
|
||||
|
||||
@ -158,19 +171,26 @@ OBJC_EXPORT void objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...)
|
||||
* before being called.
|
||||
*/
|
||||
#if !OBJC_OLD_DISPATCH_PROTOTYPES
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wincompatible-library-redeclaration"
|
||||
|
||||
# if defined(__i386__)
|
||||
|
||||
OBJC_EXPORT void objc_msgSend_fpret(void /* id self, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.4, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
objc_msgSend_fpret(void /* id self, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.4, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
# elif defined(__x86_64__)
|
||||
|
||||
OBJC_EXPORT void objc_msgSend_fpret(void /* id self, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void objc_msgSend_fp2ret(void /* id self, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
objc_msgSend_fpret(void /* id self, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgSend_fp2ret(void /* id self, SEL op, ... */ )
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
# endif
|
||||
|
||||
// !OBJC_OLD_DISPATCH_PROTOTYPES
|
||||
@ -187,8 +207,12 @@ OBJC_EXPORT void objc_msgSend_fp2ret(void /* id self, SEL op, ... */ )
|
||||
* you must use \c objc_msgSend_fpret for functions returning non-integral type. For \c float or
|
||||
* \c long \c double return types, cast the function to an appropriate function pointer type first.
|
||||
*/
|
||||
OBJC_EXPORT double objc_msgSend_fpret(id self, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.4, 2.0, 9.0, 1.0);
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wincompatible-library-redeclaration"
|
||||
OBJC_EXPORT double
|
||||
objc_msgSend_fpret(id _Nullable self, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.4, 2.0, 9.0, 1.0, 2.0);
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
/* Use objc_msgSendSuper() for fp-returning messages to super. */
|
||||
/* See also objc_msgSendv_fpret() below. */
|
||||
@ -199,15 +223,17 @@ OBJC_EXPORT double objc_msgSend_fpret(id self, SEL op, ...)
|
||||
*
|
||||
* @see objc_msgSend
|
||||
*/
|
||||
OBJC_EXPORT long double objc_msgSend_fpret(id self, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT long double
|
||||
objc_msgSend_fpret(id _Nullable self, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
# if __STDC_VERSION__ >= 199901L
|
||||
OBJC_EXPORT _Complex long double objc_msgSend_fp2ret(id self, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT _Complex long double
|
||||
objc_msgSend_fp2ret(id _Nullable self, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
# else
|
||||
OBJC_EXPORT void objc_msgSend_fp2ret(id self, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void objc_msgSend_fp2ret(id _Nullable self, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
# endif
|
||||
|
||||
/* Use objc_msgSendSuper() for fp-returning messages to super. */
|
||||
@ -229,16 +255,25 @@ OBJC_EXPORT void objc_msgSend_fp2ret(id self, SEL op, ...)
|
||||
* before being called.
|
||||
*/
|
||||
#if !OBJC_OLD_DISPATCH_PROTOTYPES
|
||||
OBJC_EXPORT void method_invoke(void /* id receiver, Method m, ... */ )
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void method_invoke_stret(void /* id receiver, Method m, ... */ )
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wincompatible-library-redeclaration"
|
||||
OBJC_EXPORT void
|
||||
method_invoke(void /* id receiver, Method m, ... */ )
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
method_invoke_stret(void /* id receiver, Method m, ... */ )
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
OBJC_EXPORT id method_invoke(id receiver, Method m, ...)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void method_invoke_stret(id receiver, Method m, ...)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0)
|
||||
OBJC_EXPORT id _Nullable
|
||||
method_invoke(id _Nullable receiver, Method _Nonnull m, ...)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
method_invoke_stret(id _Nullable receiver, Method _Nonnull m, ...)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
#endif
|
||||
|
||||
@ -259,16 +294,25 @@ OBJC_EXPORT void method_invoke_stret(id receiver, Method m, ...)
|
||||
* but may be compared to other IMP values.
|
||||
*/
|
||||
#if !OBJC_OLD_DISPATCH_PROTOTYPES
|
||||
OBJC_EXPORT void _objc_msgForward(void /* id receiver, SEL sel, ... */ )
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void _objc_msgForward_stret(void /* id receiver, SEL sel, ... */ )
|
||||
OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wincompatible-library-redeclaration"
|
||||
OBJC_EXPORT void
|
||||
_objc_msgForward(void /* id receiver, SEL sel, ... */ )
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
_objc_msgForward_stret(void /* id receiver, SEL sel, ... */ )
|
||||
OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
OBJC_EXPORT id _objc_msgForward(id receiver, SEL sel, ...)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void _objc_msgForward_stret(id receiver, SEL sel, ...)
|
||||
OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0)
|
||||
OBJC_EXPORT id _Nullable
|
||||
_objc_msgForward(id _Nonnull receiver, SEL _Nonnull sel, ...)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
_objc_msgForward_stret(id _Nonnull receiver, SEL _Nonnull sel, ...)
|
||||
OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
#endif
|
||||
|
||||
@ -288,14 +332,25 @@ OBJC_EXPORT void _objc_msgForward_stret(id receiver, SEL sel, ...)
|
||||
|
||||
typedef void* marg_list;
|
||||
|
||||
OBJC_EXPORT id objc_msgSendv(id self, SEL op, size_t arg_size, marg_list arg_frame) OBJC2_UNAVAILABLE;
|
||||
OBJC_EXPORT void objc_msgSendv_stret(void *stretAddr, id self, SEL op, size_t arg_size, marg_list arg_frame) OBJC2_UNAVAILABLE;
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_msgSendv(id _Nullable self, SEL _Nonnull op, size_t arg_size,
|
||||
marg_list _Nonnull arg_frame)
|
||||
OBJC2_UNAVAILABLE;
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgSendv_stret(void * _Nonnull stretAddr, id _Nullable self,
|
||||
SEL _Nonnull op, size_t arg_size,
|
||||
marg_list _Nullable arg_frame)
|
||||
OBJC2_UNAVAILABLE;
|
||||
/* Note that objc_msgSendv_stret() does not return a structure type,
|
||||
* and should not be cast to do so. This is unlike objc_msgSend_stret()
|
||||
* and objc_msgSendSuper_stret().
|
||||
*/
|
||||
#if defined(__i386__)
|
||||
OBJC_EXPORT double objc_msgSendv_fpret(id self, SEL op, unsigned arg_size, marg_list arg_frame) OBJC2_UNAVAILABLE;
|
||||
OBJC_EXPORT double
|
||||
objc_msgSendv_fpret(id _Nullable self, SEL _Nonnull op,
|
||||
unsigned arg_size, marg_list _Nullable arg_frame)
|
||||
OBJC2_UNAVAILABLE;
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -43,11 +43,51 @@
|
||||
#include <objc/runtime.h>
|
||||
#include <objc/message.h>
|
||||
|
||||
/* Linker metadata symbols */
|
||||
|
||||
// NSObject was in Foundation/CF on macOS < 10.8.
|
||||
#if TARGET_OS_OSX
|
||||
#if __OBJC2__
|
||||
|
||||
OBJC_EXPORT const char __objc_nsobject_class_10_5
|
||||
__asm__("$ld$hide$os10.5$_OBJC_CLASS_$_NSObject");
|
||||
OBJC_EXPORT const char __objc_nsobject_class_10_6
|
||||
__asm__("$ld$hide$os10.6$_OBJC_CLASS_$_NSObject");
|
||||
OBJC_EXPORT const char __objc_nsobject_class_10_7
|
||||
__asm__("$ld$hide$os10.7$_OBJC_CLASS_$_NSObject");
|
||||
|
||||
OBJC_EXPORT const char __objc_nsobject_metaclass_10_5
|
||||
__asm__("$ld$hide$os10.5$_OBJC_METACLASS_$_NSObject");
|
||||
OBJC_EXPORT const char __objc_nsobject_metaclass_10_6
|
||||
__asm__("$ld$hide$os10.6$_OBJC_METACLASS_$_NSObject");
|
||||
OBJC_EXPORT const char __objc_nsobject_metaclass_10_7
|
||||
__asm__("$ld$hide$os10.7$_OBJC_METACLASS_$_NSObject");
|
||||
|
||||
OBJC_EXPORT const char __objc_nsobject_isa_10_5
|
||||
__asm__("$ld$hide$os10.5$_OBJC_IVAR_$_NSObject.isa");
|
||||
OBJC_EXPORT const char __objc_nsobject_isa_10_6
|
||||
__asm__("$ld$hide$os10.6$_OBJC_IVAR_$_NSObject.isa");
|
||||
OBJC_EXPORT const char __objc_nsobject_isa_10_7
|
||||
__asm__("$ld$hide$os10.7$_OBJC_IVAR_$_NSObject.isa");
|
||||
|
||||
#else
|
||||
|
||||
OBJC_EXPORT const char __objc_nsobject_class_10_5
|
||||
__asm__("$ld$hide$os10.5$.objc_class_name_NSObject");
|
||||
OBJC_EXPORT const char __objc_nsobject_class_10_6
|
||||
__asm__("$ld$hide$os10.6$.objc_class_name_NSObject");
|
||||
OBJC_EXPORT const char __objc_nsobject_class_10_7
|
||||
__asm__("$ld$hide$os10.7$.objc_class_name_NSObject");
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Runtime startup. */
|
||||
|
||||
// Old static initializer. Used by old crt1.o and old bug workarounds.
|
||||
OBJC_EXPORT void _objcInit(void)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
_objcInit(void)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
/* Images */
|
||||
|
||||
@ -60,6 +100,7 @@ typedef struct objc_image_info {
|
||||
#if __cplusplus >= 201103L
|
||||
private:
|
||||
enum : uint32_t {
|
||||
// 1 byte assorted flags
|
||||
IsReplacement = 1<<0, // used for Fix&Continue, now ignored
|
||||
SupportsGC = 1<<1, // image supports GC
|
||||
RequiresGC = 1<<2, // image requires GC
|
||||
@ -67,17 +108,28 @@ typedef struct objc_image_info {
|
||||
CorrectedSynthesize = 1<<4, // used for an old workaround, now ignored
|
||||
IsSimulated = 1<<5, // image compiled for a simulator platform
|
||||
HasCategoryClassProperties = 1<<6, // class properties in category_t
|
||||
OptimizedByDyldClosure = 1 << 7, // dyld (not the shared cache) optimized this.
|
||||
|
||||
SwiftVersionMaskShift = 8,
|
||||
SwiftVersionMask = 0xff << SwiftVersionMaskShift // Swift ABI version
|
||||
// 1 byte Swift unstable ABI version number
|
||||
SwiftUnstableVersionMaskShift = 8,
|
||||
SwiftUnstableVersionMask = 0xff << SwiftUnstableVersionMaskShift,
|
||||
|
||||
// 2 byte Swift stable ABI version number
|
||||
SwiftStableVersionMaskShift = 16,
|
||||
SwiftStableVersionMask = 0xffffUL << SwiftStableVersionMaskShift
|
||||
};
|
||||
public:
|
||||
public:
|
||||
enum : uint32_t {
|
||||
// Values for SwiftUnstableVersion
|
||||
// All stable ABIs store SwiftVersion5 here.
|
||||
SwiftVersion1 = 1,
|
||||
SwiftVersion1_2 = 2,
|
||||
SwiftVersion2 = 3,
|
||||
SwiftVersion3 = 4
|
||||
SwiftVersion3 = 4,
|
||||
SwiftVersion4 = 5,
|
||||
SwiftVersion4_1 = 6,
|
||||
SwiftVersion4_2 = 6, // [sic]
|
||||
SwiftVersion5 = 7
|
||||
};
|
||||
|
||||
public:
|
||||
@ -86,8 +138,9 @@ typedef struct objc_image_info {
|
||||
bool requiresGC() const { return flags & RequiresGC; }
|
||||
bool optimizedByDyld() const { return flags & OptimizedByDyld; }
|
||||
bool hasCategoryClassProperties() const { return flags & HasCategoryClassProperties; }
|
||||
bool containsSwift() const { return (flags & SwiftVersionMask) != 0; }
|
||||
uint32_t swiftVersion() const { return (flags & SwiftVersionMask) >> SwiftVersionMaskShift; }
|
||||
bool optimizedByDyldClosure() const { return flags & OptimizedByDyldClosure; }
|
||||
bool containsSwift() const { return (flags & SwiftUnstableVersionMask) != 0; }
|
||||
uint32_t swiftUnstableVersion() const { return (flags & SwiftUnstableVersionMask) >> SwiftUnstableVersionMaskShift; }
|
||||
#endif
|
||||
} objc_image_info;
|
||||
|
||||
@ -122,52 +175,78 @@ HasClassProperties:
|
||||
/* Properties */
|
||||
|
||||
// Read or write an object property. Not all object properties use these.
|
||||
OBJC_EXPORT id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_getProperty(id _Nullable self, SEL _Nonnull _cmd,
|
||||
ptrdiff_t offset, BOOL atomic)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset)
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset)
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
objc_setProperty(id _Nullable self, SEL _Nonnull _cmd, ptrdiff_t offset,
|
||||
id _Nullable newValue, BOOL atomic, signed char shouldCopy)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_setProperty_atomic(id _Nullable self, SEL _Nonnull _cmd,
|
||||
id _Nullable newValue, ptrdiff_t offset)
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_setProperty_nonatomic(id _Nullable self, SEL _Nonnull _cmd,
|
||||
id _Nullable newValue, ptrdiff_t offset)
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_setProperty_atomic_copy(id _Nullable self, SEL _Nonnull _cmd,
|
||||
id _Nullable newValue, ptrdiff_t offset)
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_setProperty_nonatomic_copy(id _Nullable self, SEL _Nonnull _cmd,
|
||||
id _Nullable newValue, ptrdiff_t offset)
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0, 2.0);
|
||||
|
||||
|
||||
// Read or write a non-object property. Not all uses are C structs,
|
||||
// and not all C struct properties use this.
|
||||
OBJC_EXPORT void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
objc_copyStruct(void * _Nonnull dest, const void * _Nonnull src,
|
||||
ptrdiff_t size, BOOL atomic, BOOL hasStrong)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 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))
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
objc_copyCppObjectAtomic(void * _Nonnull dest, const void * _Nonnull src,
|
||||
void (* _Nonnull copyHelper)
|
||||
(void * _Nonnull dest, const void * _Nonnull source))
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0, 2.0);
|
||||
|
||||
/* Classes. */
|
||||
#if __OBJC2__
|
||||
OBJC_EXPORT IMP _objc_empty_vtable
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT IMP _Nonnull _objc_empty_vtable
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
#endif
|
||||
OBJC_EXPORT struct objc_cache _objc_empty_cache
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
|
||||
/* Messages */
|
||||
|
||||
#if __OBJC2__
|
||||
// objc_msgSendSuper2() takes the current search class, not its superclass.
|
||||
OBJC_EXPORT id objc_msgSendSuper2(struct objc_super *super, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.6, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void objc_msgSendSuper2_stret(struct objc_super *super, SEL op,...)
|
||||
OBJC_AVAILABLE(10.6, 2.0, 9.0, 1.0)
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.6, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgSendSuper2_stret(struct objc_super * _Nonnull super,
|
||||
SEL _Nonnull op,...)
|
||||
OBJC_AVAILABLE(10.6, 2.0, 9.0, 1.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
|
||||
// objc_msgSend_noarg() may be faster for methods with no additional arguments.
|
||||
OBJC_EXPORT id objc_msgSend_noarg(id self, SEL _cmd)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_msgSend_noarg(id _Nullable self, SEL _Nonnull _cmd)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
#endif
|
||||
|
||||
#if __OBJC2__
|
||||
@ -175,29 +254,42 @@ OBJC_EXPORT id objc_msgSend_noarg(id self, SEL _cmd)
|
||||
// may perform extra sanity checking.
|
||||
// Old objc_msgSendSuper() does not have a debug version; this is OBJC2 only.
|
||||
// *_fixup() do not have debug versions; use non-fixup only for debug mode.
|
||||
OBJC_EXPORT id objc_msgSend_debug(id self, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id objc_msgSendSuper2_debug(struct objc_super *super, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void objc_msgSend_stret_debug(id self, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0)
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_msgSend_debug(id _Nullable self, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_msgSendSuper2_debug(struct objc_super * _Nonnull super,
|
||||
SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgSend_stret_debug(id _Nullable self, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
OBJC_EXPORT void objc_msgSendSuper2_stret_debug(struct objc_super *super, SEL op,...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0)
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgSendSuper2_stret_debug(struct objc_super * _Nonnull super,
|
||||
SEL _Nonnull op,...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
|
||||
# if defined(__i386__)
|
||||
OBJC_EXPORT double objc_msgSend_fpret_debug(id self, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT double
|
||||
objc_msgSend_fpret_debug(id _Nullable self, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
# elif defined(__x86_64__)
|
||||
OBJC_EXPORT long double objc_msgSend_fpret_debug(id self, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT long double
|
||||
objc_msgSend_fpret_debug(id _Nullable self, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
# if __STDC_VERSION__ >= 199901L
|
||||
OBJC_EXPORT _Complex long double objc_msgSend_fp2ret_debug(id self, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT _Complex long double
|
||||
objc_msgSend_fp2ret_debug(id _Nullable self, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
# else
|
||||
OBJC_EXPORT void objc_msgSend_fp2ret_debug(id self, SEL op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
objc_msgSend_fp2ret_debug(id _Nullable self, SEL _Nonnull op, ...)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
# endif
|
||||
# endif
|
||||
|
||||
@ -219,66 +311,80 @@ OBJC_EXPORT void objc_msgSend_fp2ret_debug(id self, SEL op, ...)
|
||||
// - Red zone is not preserved.
|
||||
// See each architecture's implementation for details.
|
||||
|
||||
OBJC_EXPORT void objc_msgLookup(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_EXPORT void objc_msgLookupSuper2(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_EXPORT void objc_msgLookup_stret(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0)
|
||||
OBJC_EXPORT void
|
||||
objc_msgLookup(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgLookupSuper2(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgLookup_stret(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
OBJC_EXPORT void objc_msgLookupSuper2_stret(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0)
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgLookupSuper2_stret(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
|
||||
# if defined(__i386__)
|
||||
OBJC_EXPORT void objc_msgLookup_fpret(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_EXPORT void
|
||||
objc_msgLookup_fpret(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
# elif defined(__x86_64__)
|
||||
OBJC_EXPORT void objc_msgLookup_fpret(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_EXPORT void objc_msgLookup_fp2ret(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_EXPORT void
|
||||
objc_msgLookup_fpret(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgLookup_fp2ret(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_OSX && defined(__x86_64__)
|
||||
// objc_msgSend_fixup() is used for vtable-dispatchable call sites.
|
||||
OBJC_EXPORT void objc_msgSend_fixup(void)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "fixup dispatch is no longer optimized")
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT void objc_msgSend_stret_fixup(void)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "fixup dispatch is no longer optimized")
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT void objc_msgSendSuper2_fixup(void)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "fixup dispatch is no longer optimized")
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT void objc_msgSendSuper2_stret_fixup(void)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "fixup dispatch is no longer optimized")
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT void objc_msgSend_fpret_fixup(void)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "fixup dispatch is no longer optimized")
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT void objc_msgSend_fp2ret_fixup(void)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "fixup dispatch is no longer optimized")
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
#if (TARGET_OS_OSX || TARGET_OS_SIMULATOR) && defined(__x86_64__)
|
||||
// objc_msgSend_fixup() was used for vtable-dispatchable call sites.
|
||||
// The symbols remain exported on macOS for binary compatibility.
|
||||
// The symbols can probably be removed from iOS simulator but we haven't tried.
|
||||
OBJC_EXPORT void
|
||||
objc_msgSend_fixup(void)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "fixup dispatch is no longer optimized");
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgSend_stret_fixup(void)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "fixup dispatch is no longer optimized");
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgSendSuper2_fixup(void)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "fixup dispatch is no longer optimized");
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgSendSuper2_stret_fixup(void)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "fixup dispatch is no longer optimized");
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgSend_fpret_fixup(void)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "fixup dispatch is no longer optimized");
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_msgSend_fp2ret_fixup(void)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "fixup dispatch is no longer optimized");
|
||||
#endif
|
||||
|
||||
/* C++-compatible exception handling. */
|
||||
#if __OBJC2__
|
||||
|
||||
// fixme these conflict with C++ compiler's internal definitions
|
||||
#if !defined(__cplusplus)
|
||||
|
||||
// Vtable for C++ exception typeinfo for Objective-C types.
|
||||
OBJC_EXPORT const void *objc_ehtype_vtable[]
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT const void * _Nullable objc_ehtype_vtable[]
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// C++ exception typeinfo for type `id`.
|
||||
OBJC_EXPORT struct objc_typeinfo OBJC_EHTYPE_id
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
|
||||
#endif
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Exception personality function for Objective-C and Objective-C++ code.
|
||||
struct _Unwind_Exception;
|
||||
@ -287,16 +393,17 @@ OBJC_EXPORT int
|
||||
__objc_personality_v0(int version,
|
||||
int actions,
|
||||
uint64_t exceptionClass,
|
||||
struct _Unwind_Exception *exceptionObject,
|
||||
struct _Unwind_Context *context)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
struct _Unwind_Exception * _Nonnull exceptionObject,
|
||||
struct _Unwind_Context * _Nonnull context)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
#endif
|
||||
|
||||
/* ARC */
|
||||
|
||||
OBJC_EXPORT id objc_retainBlock(id)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_retainBlock(id _Nullable)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
|
||||
/* Non-pointer isa */
|
||||
@ -305,7 +412,7 @@ OBJC_EXPORT id objc_retainBlock(id)
|
||||
|
||||
// Extract class pointer from an isa field.
|
||||
|
||||
#if TARGET_OS_SIMULATOR
|
||||
#if TARGET_OS_SIMULATOR && !TARGET_OS_IOSMAC
|
||||
// No simulators use nonpointer isa yet.
|
||||
|
||||
#elif __LP64__
|
||||
@ -315,9 +422,9 @@ OBJC_EXPORT id objc_retainBlock(id)
|
||||
// Packed-isa version. This one is used directly by Swift code.
|
||||
// (Class)(isa & (uintptr_t)&objc_absolute_packed_isa_class_mask) == class ptr
|
||||
OBJC_EXPORT const struct { char c; } objc_absolute_packed_isa_class_mask
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
#elif __ARM_ARCH_7K__ >= 2
|
||||
#elif (__ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__))
|
||||
# define OBJC_HAVE_NONPOINTER_ISA 1
|
||||
# define OBJC_HAVE_INDEXED_NONPOINTER_ISA 1
|
||||
|
||||
@ -329,18 +436,42 @@ OBJC_EXPORT const struct { char c; } objc_absolute_packed_isa_class_mask
|
||||
// cls = (Class)isa;
|
||||
// }
|
||||
OBJC_EXPORT const struct { char c; } objc_absolute_indexed_isa_magic_mask
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
OBJC_EXPORT const struct { char c; } objc_absolute_indexed_isa_magic_value
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
OBJC_EXPORT const struct { char c; } objc_absolute_indexed_isa_index_mask
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
OBJC_EXPORT const struct { char c; } objc_absolute_indexed_isa_index_shift
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
#endif
|
||||
|
||||
// OBJC2
|
||||
#endif
|
||||
|
||||
|
||||
/* Object class */
|
||||
|
||||
// This symbol might be required for binary compatibility, so we
|
||||
// declare it here where TAPI will see it.
|
||||
#if __OBJC__ && __OBJC2__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
|
||||
#if !defined(OBJC_DECLARE_SYMBOLS)
|
||||
__OSX_AVAILABLE(10.0)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE
|
||||
__WATCHOS_UNAVAILABLE
|
||||
# ifndef __APPLE_BLEACH_SDK__
|
||||
__BRIDGEOS_UNAVAILABLE
|
||||
# endif
|
||||
#endif
|
||||
OBJC_ROOT_CLASS
|
||||
@interface Object {
|
||||
Class isa;
|
||||
}
|
||||
@end
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
// _OBJC_ABI_H
|
||||
#endif
|
||||
|
@ -39,10 +39,9 @@
|
||||
- (id)mutableCopyWithZone:(void *)zone;
|
||||
@end
|
||||
|
||||
// These locks must not be at function scope.
|
||||
static StripedMap<spinlock_t> PropertyLocks;
|
||||
static StripedMap<spinlock_t> StructLocks;
|
||||
static StripedMap<spinlock_t> CppObjectLocks;
|
||||
StripedMap<spinlock_t> PropertyLocks;
|
||||
StripedMap<spinlock_t> StructLocks;
|
||||
StripedMap<spinlock_t> CppObjectLocks;
|
||||
|
||||
#define MUTABLE_COPY 2
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <Availability.h>
|
||||
#include <AvailabilityMacros.h>
|
||||
#include <TargetConditionals.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef __has_feature
|
||||
# define __has_feature(x) 0
|
||||
@ -41,6 +42,41 @@
|
||||
# define __has_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#if !__has_feature(nullability)
|
||||
# ifndef _Nullable
|
||||
# define _Nullable
|
||||
# endif
|
||||
# ifndef _Nonnull
|
||||
# define _Nonnull
|
||||
# endif
|
||||
# ifndef _Null_unspecified
|
||||
# define _Null_unspecified
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef __APPLE_BLEACH_SDK__
|
||||
# if __has_feature(attribute_availability_bridgeos)
|
||||
# ifndef __BRIDGEOS_AVAILABLE
|
||||
# define __BRIDGEOS_AVAILABLE(_vers) __OS_AVAILABILITY(bridgeos,introduced=_vers)
|
||||
# endif
|
||||
# ifndef __BRIDGEOS_DEPRECATED
|
||||
# define __BRIDGEOS_DEPRECATED(_start, _dep, _msg) __BRIDGEOS_AVAILABLE(_start) __OS_AVAILABILITY_MSG(bridgeos,deprecated=_dep,_msg)
|
||||
# endif
|
||||
# ifndef __BRIDGEOS_UNAVAILABLE
|
||||
# define __BRIDGEOS_UNAVAILABLE __OS_AVAILABILITY(bridgeos,unavailable)
|
||||
# endif
|
||||
# else
|
||||
# ifndef __BRIDGEOS_AVAILABLE
|
||||
# define __BRIDGEOS_AVAILABLE(_vers)
|
||||
# endif
|
||||
# ifndef __BRIDGEOS_DEPRECATED
|
||||
# define __BRIDGEOS_DEPRECATED(_start, _dep, _msg)
|
||||
# endif
|
||||
# ifndef __BRIDGEOS_UNAVAILABLE
|
||||
# define __BRIDGEOS_UNAVAILABLE
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* OBJC_API_VERSION 0 or undef: Tiger and earlier API only
|
||||
@ -86,15 +122,67 @@
|
||||
/* 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
|
||||
# if __swift__
|
||||
// Existing Swift code expects IMP to be Comparable.
|
||||
// Variadic IMP is comparable via OpaquePointer; non-variadic IMP isn't.
|
||||
# define OBJC_OLD_DISPATCH_PROTOTYPES 1
|
||||
# else
|
||||
# define OBJC_OLD_DISPATCH_PROTOTYPES 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/* OBJC_AVAILABLE: shorthand for all-OS availability */
|
||||
#if !defined(OBJC_AVAILABLE)
|
||||
# define OBJC_AVAILABLE(x, i, t, w) \
|
||||
__OSX_AVAILABLE(x) __IOS_AVAILABLE(i) \
|
||||
__TVOS_AVAILABLE(t) __WATCHOS_AVAILABLE(w)
|
||||
#ifndef __APPLE_BLEACH_SDK__
|
||||
# if !defined(OBJC_AVAILABLE)
|
||||
# define OBJC_AVAILABLE(x, i, t, w, b) \
|
||||
__OSX_AVAILABLE(x) __IOS_AVAILABLE(i) __TVOS_AVAILABLE(t) \
|
||||
__WATCHOS_AVAILABLE(w) __BRIDGEOS_AVAILABLE(b)
|
||||
# endif
|
||||
#else
|
||||
# if !defined(OBJC_AVAILABLE)
|
||||
# define OBJC_AVAILABLE(x, i, t, w, b) \
|
||||
__OSX_AVAILABLE(x) __IOS_AVAILABLE(i) __TVOS_AVAILABLE(t) \
|
||||
__WATCHOS_AVAILABLE(w)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/* OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE: Deprecated on OS X,
|
||||
* unavailable everywhere else. */
|
||||
#ifndef __APPLE_BLEACH_SDK__
|
||||
# if !defined(OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE)
|
||||
# define OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(_start, _dep, _msg) \
|
||||
__OSX_DEPRECATED(_start, _dep, _msg) \
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE \
|
||||
__WATCHOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE
|
||||
# endif
|
||||
#else
|
||||
# if !defined(OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE)
|
||||
# define OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(_start, _dep, _msg) \
|
||||
__OSX_DEPRECATED(_start, _dep, _msg) \
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE \
|
||||
__WATCHOS_UNAVAILABLE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/* OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE: Available on OS X,
|
||||
* unavailable everywhere else. */
|
||||
#ifndef __APPLE_BLEACH_SDK__
|
||||
# if !defined(OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE)
|
||||
# define OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE(vers) \
|
||||
__OSX_AVAILABLE(vers) \
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE \
|
||||
__WATCHOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE
|
||||
# endif
|
||||
#else
|
||||
# if !defined(OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE)
|
||||
# define OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE(vers) \
|
||||
__OSX_AVAILABLE(vers) \
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE \
|
||||
__WATCHOS_UNAVAILABLE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
@ -118,7 +206,7 @@
|
||||
# define OBJC2_UNAVAILABLE \
|
||||
__OSX_DEPRECATED(10.5, 10.5, "not available in __OBJC2__") \
|
||||
__IOS_DEPRECATED(2.0, 2.0, "not available in __OBJC2__") \
|
||||
__TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE
|
||||
__TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@ -229,4 +317,12 @@
|
||||
#define OBJC_OPTIONS(_type, _name) _type _name; enum
|
||||
#endif
|
||||
|
||||
#if !defined(OBJC_RETURNS_RETAINED)
|
||||
# if __OBJC__ && __has_attribute(ns_returns_retained)
|
||||
# define OBJC_RETURNS_RETAINED __attribute__((ns_returns_retained))
|
||||
# else
|
||||
# define OBJC_RETURNS_RETAINED
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -24,8 +24,6 @@
|
||||
#ifndef _OBJC_AUTO_H_
|
||||
#define _OBJC_AUTO_H_
|
||||
|
||||
#pragma GCC system_header
|
||||
|
||||
#include <objc/objc.h>
|
||||
#include <malloc/malloc.h>
|
||||
#include <stdint.h>
|
||||
@ -57,7 +55,7 @@ enum {
|
||||
OBJC_EXHAUSTIVE_COLLECTION = (3 << 0),
|
||||
|
||||
OBJC_COLLECT_IF_NEEDED = (1 << 3),
|
||||
OBJC_WAIT_UNTIL_DONE = (1 << 4),
|
||||
OBJC_WAIT_UNTIL_DONE = (1 << 4)
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -65,71 +63,72 @@ enum {
|
||||
};
|
||||
|
||||
|
||||
#ifndef OBJC_NO_GC
|
||||
#if !defined(OBJC_NO_GC) || \
|
||||
(OBJC_DECLARE_SYMBOLS && !defined(OBJC_NO_GC_API))
|
||||
|
||||
|
||||
/* Out-of-line declarations */
|
||||
|
||||
OBJC_EXPORT void objc_collect(unsigned long options)
|
||||
__OSX_DEPRECATED(10.6, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.6, 10.8, "it does nothing");
|
||||
OBJC_EXPORT BOOL objc_collectingEnabled(void)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "it always returns NO") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.5, 10.8, "it always returns NO");
|
||||
OBJC_EXPORT malloc_zone_t *objc_collectableZone(void)
|
||||
__OSX_DEPRECATED(10.7, 10.8, "it always returns nil") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.7, 10.8, "it always returns nil");
|
||||
OBJC_EXPORT void objc_setCollectionThreshold(size_t threshold)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.5, 10.8, "it does nothing");
|
||||
OBJC_EXPORT void objc_setCollectionRatio(size_t ratio)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.5, 10.8, "it does nothing");
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation)
|
||||
__OSX_DEPRECATED(10.6, 10.8, "use OSAtomicCompareAndSwapPtr instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE OBJC_ARC_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.6, 10.8, "use OSAtomicCompareAndSwapPtr instead");
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapPtrBarrier(id predicate, id replacement, volatile id *objectLocation)
|
||||
__OSX_DEPRECATED(10.6, 10.8, "use OSAtomicCompareAndSwapPtrBarrier instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE OBJC_ARC_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.6, 10.8, "use OSAtomicCompareAndSwapPtrBarrier instead");
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapGlobal(id predicate, id replacement, volatile id *objectLocation)
|
||||
__OSX_DEPRECATED(10.6, 10.8, "use OSAtomicCompareAndSwapPtr instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE OBJC_ARC_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.6, 10.8, "use OSAtomicCompareAndSwapPtr instead");
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapGlobalBarrier(id predicate, id replacement, volatile id *objectLocation)
|
||||
__OSX_DEPRECATED(10.6, 10.8, "use OSAtomicCompareAndSwapPtrBarrier instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE OBJC_ARC_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.6, 10.8, "use OSAtomicCompareAndSwapPtrBarrier instead");
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapInstanceVariable(id predicate, id replacement, volatile id *objectLocation)
|
||||
__OSX_DEPRECATED(10.6, 10.8, "use OSAtomicCompareAndSwapPtr instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE OBJC_ARC_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.6, 10.8, "use OSAtomicCompareAndSwapPtr instead");
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, id replacement, volatile id *objectLocation)
|
||||
__OSX_DEPRECATED(10.6, 10.8, "use OSAtomicCompareAndSwapPtrBarrier instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE OBJC_ARC_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.6, 10.8, "use OSAtomicCompareAndSwapPtrBarrier instead");
|
||||
OBJC_EXPORT id objc_assign_strongCast(id val, id *dest)
|
||||
__OSX_DEPRECATED(10.4, 10.8, "use a simple assignment instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.4, 10.8, "use a simple assignment instead");
|
||||
OBJC_EXPORT id objc_assign_global(id val, id *dest)
|
||||
__OSX_DEPRECATED(10.4, 10.8, "use a simple assignment instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.4, 10.8, "use a simple assignment instead");
|
||||
OBJC_EXPORT id objc_assign_threadlocal(id val, id *dest)
|
||||
__OSX_DEPRECATED(10.7, 10.8, "use a simple assignment instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.7, 10.8, "use a simple assignment instead");
|
||||
OBJC_EXPORT id objc_assign_ivar(id value, id dest, ptrdiff_t offset)
|
||||
__OSX_DEPRECATED(10.4, 10.8, "use a simple assignment instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.4, 10.8, "use a simple assignment instead");
|
||||
OBJC_EXPORT void *objc_memmove_collectable(void *dst, const void *src, size_t size)
|
||||
__OSX_DEPRECATED(10.4, 10.8, "use memmove instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.4, 10.8, "use memmove instead");
|
||||
OBJC_EXPORT id objc_read_weak(id *location)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "use a simple read instead, or convert to zeroing __weak") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.5, 10.8, "use a simple read instead, or convert to zeroing __weak");
|
||||
OBJC_EXPORT id objc_assign_weak(id value, id *location)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "use a simple assignment instead, or convert to zeroing __weak") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.5, 10.8, "use a simple assignment instead, or convert to zeroing __weak");
|
||||
OBJC_EXPORT void objc_registerThreadWithCollector(void)
|
||||
__OSX_DEPRECATED(10.6, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.6, 10.8, "it does nothing");
|
||||
OBJC_EXPORT void objc_unregisterThreadWithCollector(void)
|
||||
__OSX_DEPRECATED(10.6, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.6, 10.8, "it does nothing");
|
||||
OBJC_EXPORT void objc_assertRegisteredThreadWithCollector(void)
|
||||
__OSX_DEPRECATED(10.6, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.6, 10.8, "it does nothing");
|
||||
OBJC_EXPORT void objc_clear_stack(unsigned long options)
|
||||
__OSX_DEPRECATED(10.5, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.5, 10.8, "it does nothing");
|
||||
OBJC_EXPORT BOOL objc_is_finalized(void *ptr)
|
||||
__OSX_DEPRECATED(10.4, 10.8, "it always returns NO") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.4, 10.8, "it always returns NO");
|
||||
OBJC_EXPORT void objc_finalizeOnMainThread(Class cls)
|
||||
__OSX_DEPRECATED(10.5, 10.5, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.5, 10.5, "it does nothing");
|
||||
OBJC_EXPORT BOOL objc_collecting_enabled(void)
|
||||
__OSX_DEPRECATED(10.4, 10.5, "it always returns NO") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.4, 10.5, "it always returns NO");
|
||||
OBJC_EXPORT void objc_set_collection_threshold(size_t threshold)
|
||||
__OSX_DEPRECATED(10.4, 10.5, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.4, 10.5, "it does nothing");
|
||||
OBJC_EXPORT void objc_set_collection_ratio(size_t ratio)
|
||||
__OSX_DEPRECATED(10.4, 10.5, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.4, 10.5, "it does nothing");
|
||||
OBJC_EXPORT void objc_start_collector_thread(void)
|
||||
__OSX_DEPRECATED(10.4, 10.5, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.4, 10.5, "it does nothing");
|
||||
OBJC_EXPORT void objc_startCollectorThread(void)
|
||||
__OSX_DEPRECATED(10.5, 10.7, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.5, 10.7, "it does nothing");
|
||||
OBJC_EXPORT id objc_allocate_object(Class cls, int extra)
|
||||
__OSX_DEPRECATED(10.4, 10.4, "use class_createInstance instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.4, 10.4, "use class_createInstance instead");
|
||||
|
||||
|
||||
/* !defined(OBJC_NO_GC) */
|
||||
@ -199,7 +198,7 @@ static OBJC_INLINE id objc_assign_threadlocal(id val, id *dest)
|
||||
|
||||
OBJC_GC_DEPRECATED("use a simple assignment instead")
|
||||
static OBJC_INLINE id objc_assign_ivar(id val, id dest, ptrdiff_t offset)
|
||||
{ return (*(id*)((char *)dest+offset) = val); }
|
||||
{ return (*(id*)((intptr_t)(char *)dest+offset) = val); }
|
||||
|
||||
OBJC_GC_DEPRECATED("use a simple read instead, or convert to zeroing __weak")
|
||||
static OBJC_INLINE id objc_read_weak(id *location)
|
||||
@ -235,10 +234,10 @@ static OBJC_INLINE void objc_start_collector_thread(void) { }
|
||||
extern id objc_allocate_object(Class cls, int extra) UNAVAILABLE_ATTRIBUTE;
|
||||
#else
|
||||
OBJC_EXPORT id class_createInstance(Class cls, size_t extraBytes)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
|
||||
OBJC_GC_DEPRECATED("use class_createInstance instead")
|
||||
static OBJC_INLINE id objc_allocate_object(Class cls, int extra)
|
||||
{ return class_createInstance(cls, extra); }
|
||||
{ return class_createInstance(cls, (size_t)extra); }
|
||||
#endif
|
||||
|
||||
OBJC_GC_DEPRECATED("it does nothing")
|
||||
|
@ -21,7 +21,9 @@
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#define OBJC_DECLARE_SYMBOLS 1
|
||||
#include "objc-private.h"
|
||||
#include "objc-auto.h"
|
||||
|
||||
// GC is no longer supported.
|
||||
|
||||
@ -35,86 +37,85 @@
|
||||
#else
|
||||
|
||||
// No GC but we do need to export GC symbols.
|
||||
// These are mostly the same as the OBJC_NO_GC inline versions in objc-auto.h.
|
||||
|
||||
# if !SUPPORT_GC_COMPAT
|
||||
# error inconsistent config settings
|
||||
# endif
|
||||
|
||||
OBJC_EXPORT void objc_collect(unsigned long options __unused) { }
|
||||
OBJC_EXPORT BOOL objc_collectingEnabled(void) { return NO; }
|
||||
OBJC_EXPORT void objc_setCollectionThreshold(size_t threshold __unused) { }
|
||||
OBJC_EXPORT void objc_setCollectionRatio(size_t ratio __unused) { }
|
||||
OBJC_EXPORT void objc_startCollectorThread(void) { }
|
||||
void objc_collect(unsigned long options __unused) { }
|
||||
BOOL objc_collectingEnabled(void) { return NO; }
|
||||
void objc_setCollectionThreshold(size_t threshold __unused) { }
|
||||
void objc_setCollectionRatio(size_t ratio __unused) { }
|
||||
void objc_startCollectorThread(void) { }
|
||||
|
||||
#if TARGET_OS_WIN32
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation)
|
||||
BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation)
|
||||
{ void *original = InterlockedCompareExchangePointer((void * volatile *)objectLocation, (void *)replacement, (void *)predicate); return (original == predicate); }
|
||||
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapPtrBarrier(id predicate, id replacement, volatile id *objectLocation)
|
||||
BOOL objc_atomicCompareAndSwapPtrBarrier(id predicate, id replacement, volatile id *objectLocation)
|
||||
{ void *original = InterlockedCompareExchangePointer((void * volatile *)objectLocation, (void *)replacement, (void *)predicate); return (original == predicate); }
|
||||
#else
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation)
|
||||
BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation)
|
||||
{ return OSAtomicCompareAndSwapPtr((void *)predicate, (void *)replacement, (void * volatile *)objectLocation); }
|
||||
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapPtrBarrier(id predicate, id replacement, volatile id *objectLocation)
|
||||
BOOL objc_atomicCompareAndSwapPtrBarrier(id predicate, id replacement, volatile id *objectLocation)
|
||||
{ return OSAtomicCompareAndSwapPtrBarrier((void *)predicate, (void *)replacement, (void * volatile *)objectLocation); }
|
||||
#endif
|
||||
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapGlobal(id predicate, id replacement, volatile id *objectLocation)
|
||||
BOOL objc_atomicCompareAndSwapGlobal(id predicate, id replacement, volatile id *objectLocation)
|
||||
{ return objc_atomicCompareAndSwapPtr(predicate, replacement, objectLocation); }
|
||||
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapGlobalBarrier(id predicate, id replacement, volatile id *objectLocation)
|
||||
BOOL objc_atomicCompareAndSwapGlobalBarrier(id predicate, id replacement, volatile id *objectLocation)
|
||||
{ return objc_atomicCompareAndSwapPtrBarrier(predicate, replacement, objectLocation); }
|
||||
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapInstanceVariable(id predicate, id replacement, volatile id *objectLocation)
|
||||
BOOL objc_atomicCompareAndSwapInstanceVariable(id predicate, id replacement, volatile id *objectLocation)
|
||||
{ return objc_atomicCompareAndSwapPtr(predicate, replacement, objectLocation); }
|
||||
|
||||
OBJC_EXPORT BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, id replacement, volatile id *objectLocation)
|
||||
BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, id replacement, volatile id *objectLocation)
|
||||
{ return objc_atomicCompareAndSwapPtrBarrier(predicate, replacement, objectLocation); }
|
||||
|
||||
OBJC_EXPORT id objc_assign_strongCast(id val, id *dest)
|
||||
id objc_assign_strongCast(id val, id *dest)
|
||||
{ return (*dest = val); }
|
||||
|
||||
OBJC_EXPORT id objc_assign_global(id val, id *dest)
|
||||
id objc_assign_global(id val, id *dest)
|
||||
{ return (*dest = val); }
|
||||
|
||||
OBJC_EXPORT id objc_assign_threadlocal(id val, id *dest)
|
||||
id objc_assign_threadlocal(id val, id *dest)
|
||||
{ return (*dest = val); }
|
||||
|
||||
OBJC_EXPORT id objc_assign_ivar(id val, id dest, ptrdiff_t offset)
|
||||
id objc_assign_ivar(id val, id dest, ptrdiff_t offset)
|
||||
{ return (*(id*)((char *)dest+offset) = val); }
|
||||
|
||||
OBJC_EXPORT id objc_read_weak(id *location)
|
||||
id objc_read_weak(id *location)
|
||||
{ return *location; }
|
||||
|
||||
OBJC_EXPORT id objc_assign_weak(id value, id *location)
|
||||
id objc_assign_weak(id value, id *location)
|
||||
{ return (*location = value); }
|
||||
|
||||
OBJC_EXPORT void *objc_memmove_collectable(void *dst, const void *src, size_t size)
|
||||
void *objc_memmove_collectable(void *dst, const void *src, size_t size)
|
||||
{ return memmove(dst, src, size); }
|
||||
|
||||
OBJC_EXPORT void objc_finalizeOnMainThread(Class cls __unused) { }
|
||||
OBJC_EXPORT BOOL objc_is_finalized(void *ptr __unused) { return NO; }
|
||||
OBJC_EXPORT void objc_clear_stack(unsigned long options __unused) { }
|
||||
void objc_finalizeOnMainThread(Class cls __unused) { }
|
||||
BOOL objc_is_finalized(void *ptr __unused) { return NO; }
|
||||
void objc_clear_stack(unsigned long options __unused) { }
|
||||
|
||||
OBJC_EXPORT BOOL objc_collecting_enabled(void) { return NO; }
|
||||
OBJC_EXPORT void objc_set_collection_threshold(size_t threshold __unused) { }
|
||||
OBJC_EXPORT void objc_set_collection_ratio(size_t ratio __unused) { }
|
||||
OBJC_EXPORT void objc_start_collector_thread(void) { }
|
||||
BOOL objc_collecting_enabled(void) { return NO; }
|
||||
void objc_set_collection_threshold(size_t threshold __unused) { }
|
||||
void objc_set_collection_ratio(size_t ratio __unused) { }
|
||||
void objc_start_collector_thread(void) { }
|
||||
|
||||
OBJC_EXPORT id objc_allocate_object(Class cls, int extra)
|
||||
id objc_allocate_object(Class cls, int extra)
|
||||
{ return class_createInstance(cls, extra); }
|
||||
|
||||
OBJC_EXPORT void objc_registerThreadWithCollector() { }
|
||||
OBJC_EXPORT void objc_unregisterThreadWithCollector() { }
|
||||
OBJC_EXPORT void objc_assertRegisteredThreadWithCollector() { }
|
||||
void objc_registerThreadWithCollector() { }
|
||||
void objc_unregisterThreadWithCollector() { }
|
||||
void objc_assertRegisteredThreadWithCollector() { }
|
||||
|
||||
OBJC_EXPORT malloc_zone_t* objc_collect_init(int(*callback)() __unused) { return nil; }
|
||||
OBJC_EXPORT void* objc_collectableZone() { return nil; }
|
||||
malloc_zone_t* objc_collect_init(int(*callback)() __unused) { return nil; }
|
||||
malloc_zone_t* objc_collectableZone() { return nil; }
|
||||
|
||||
OBJC_EXPORT BOOL objc_isAuto(id object __unused) { return NO; }
|
||||
OBJC_EXPORT BOOL objc_dumpHeap(char *filename __unused, unsigned long length __unused)
|
||||
BOOL objc_isAuto(id object __unused) { return NO; }
|
||||
BOOL objc_dumpHeap(char *filename __unused, unsigned long length __unused)
|
||||
{ return NO; }
|
||||
|
||||
// not OBJC_NO_GC_API
|
||||
|
71
runtime/objc-block-trampolines.h
Normal file
71
runtime/objc-block-trampolines.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Apple Inc. All Rights Reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _OBJC_TRAMPOLINES_H
|
||||
#define _OBJC_TRAMPOLINES_H
|
||||
|
||||
/*
|
||||
* WARNING DANGER HAZARD BEWARE EEK
|
||||
*
|
||||
* Everything in this file is for Apple Internal use only.
|
||||
* These will change in arbitrary OS updates and in unpredictable ways.
|
||||
* When your program breaks, you get to keep both pieces.
|
||||
*/
|
||||
|
||||
/*
|
||||
* objc-block-trampolines.h: Symbols for IMP block trampolines
|
||||
*/
|
||||
|
||||
// WARNING: remapped code and dtrace do not play well together. Dtrace
|
||||
// will place trap instructions to instrument the code, which then get
|
||||
// remapped along with everything else. The remapped traps are not
|
||||
// recognized by dtrace and the process crashes. To avoid this, dtrace
|
||||
// blacklists this library by name. Do not change the name of this
|
||||
// library. rdar://problem/42627391
|
||||
|
||||
#include <objc/objc-api.h>
|
||||
|
||||
OBJC_EXPORT const char _objc_blockTrampolineImpl
|
||||
OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0);
|
||||
|
||||
OBJC_EXPORT const char _objc_blockTrampolineStart
|
||||
OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0);
|
||||
|
||||
OBJC_EXPORT const char _objc_blockTrampolineLast
|
||||
OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0);
|
||||
|
||||
|
||||
OBJC_EXPORT const char _objc_blockTrampolineImpl_stret
|
||||
OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
|
||||
OBJC_EXPORT const char _objc_blockTrampolineStart_stret
|
||||
OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
|
||||
OBJC_EXPORT const char _objc_blockTrampolineLast_stret
|
||||
OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
|
||||
#endif
|
@ -35,27 +35,161 @@
|
||||
#include <Block.h>
|
||||
#include <Block_private.h>
|
||||
#include <mach/mach.h>
|
||||
#include <objc/objc-block-trampolines.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 __unused 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_trampend); // after the last trampoline
|
||||
// fixme C++ compilers don't implemement memory_order_consume efficiently.
|
||||
// Use memory_order_relaxed and cross our fingers.
|
||||
#define MEMORY_ORDER_CONSUME std::memory_order_relaxed
|
||||
|
||||
#if SUPPORT_STRET
|
||||
// Struct return
|
||||
TRAMP(a2a3_tramphead);
|
||||
TRAMP(a2a3_firsttramp);
|
||||
TRAMP(a2a3_trampend);
|
||||
// 8 bytes of text and data per trampoline on all architectures.
|
||||
#define SLOT_SIZE 8
|
||||
|
||||
// The trampolines are defined in assembly files in libobjc-trampolines.dylib.
|
||||
// We can't link to libobjc-trampolines.dylib directly because
|
||||
// for security reasons it isn't in the dyld shared cache.
|
||||
|
||||
// Trampoline addresses are lazily looked up.
|
||||
// All of them are hidden behind a single atomic pointer for lock-free init.
|
||||
|
||||
#ifdef __PTRAUTH_INTRINSICS__
|
||||
# define TrampolinePtrauth __ptrauth(ptrauth_key_function_pointer, 1, 0x3af1)
|
||||
#else
|
||||
# define TrampolinePtrauth
|
||||
#endif
|
||||
|
||||
class TrampolinePointerWrapper {
|
||||
struct TrampolinePointers {
|
||||
class TrampolineAddress {
|
||||
const void * TrampolinePtrauth storage;
|
||||
|
||||
public:
|
||||
TrampolineAddress(void *dylib, const char *name) {
|
||||
#define PREFIX "_objc_blockTrampoline"
|
||||
char symbol[strlen(PREFIX) + strlen(name) + 1];
|
||||
strcpy(symbol, PREFIX);
|
||||
strcat(symbol, name);
|
||||
// dlsym() from a text segment returns a signed pointer
|
||||
// Authenticate it manually and let the compiler re-sign it.
|
||||
storage = ptrauth_auth_data(dlsym(dylib, symbol),
|
||||
ptrauth_key_function_pointer, 0);
|
||||
if (!storage) {
|
||||
_objc_fatal("couldn't dlsym %s", symbol);
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t address() {
|
||||
return (uintptr_t)(void*)storage;
|
||||
}
|
||||
};
|
||||
|
||||
TrampolineAddress impl; // trampoline header code
|
||||
TrampolineAddress start; // first trampoline
|
||||
#if DEBUG
|
||||
// These symbols are only used in assertions.
|
||||
// fixme might be able to move the assertions to libobjc-trampolines itself
|
||||
TrampolineAddress last; // start of the last trampoline
|
||||
// We don't use the address after the last trampoline because that
|
||||
// address might be in a different section, and then dlsym() would not
|
||||
// sign it as a function pointer.
|
||||
# if SUPPORT_STRET
|
||||
TrampolineAddress impl_stret;
|
||||
TrampolineAddress start_stret;
|
||||
TrampolineAddress last_stret;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
uintptr_t textSegment;
|
||||
uintptr_t textSegmentSize;
|
||||
|
||||
void check() {
|
||||
#if DEBUG
|
||||
ASSERT(impl.address() == textSegment + PAGE_MAX_SIZE);
|
||||
ASSERT(impl.address() % PAGE_SIZE == 0); // not PAGE_MAX_SIZE
|
||||
assert(impl.address() + PAGE_MAX_SIZE ==
|
||||
last.address() + SLOT_SIZE);
|
||||
ASSERT(last.address()+8 < textSegment + textSegmentSize);
|
||||
ASSERT((last.address() - start.address()) % SLOT_SIZE == 0);
|
||||
# if SUPPORT_STRET
|
||||
ASSERT(impl_stret.address() == textSegment + 2*PAGE_MAX_SIZE);
|
||||
ASSERT(impl_stret.address() % PAGE_SIZE == 0); // not PAGE_MAX_SIZE
|
||||
assert(impl_stret.address() + PAGE_MAX_SIZE ==
|
||||
last_stret.address() + SLOT_SIZE);
|
||||
assert(start.address() - impl.address() ==
|
||||
start_stret.address() - impl_stret.address());
|
||||
assert(last_stret.address() + SLOT_SIZE <
|
||||
textSegment + textSegmentSize);
|
||||
assert((last_stret.address() - start_stret.address())
|
||||
% SLOT_SIZE == 0);
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
TrampolinePointers(void *dylib)
|
||||
: impl(dylib, "Impl")
|
||||
, start(dylib, "Start")
|
||||
#if DEBUG
|
||||
, last(dylib, "Last")
|
||||
# if SUPPORT_STRET
|
||||
, impl_stret(dylib, "Impl_stret")
|
||||
, start_stret(dylib, "Start_stret")
|
||||
, last_stret(dylib, "Last_stret")
|
||||
# endif
|
||||
#endif
|
||||
{
|
||||
const auto *mh =
|
||||
dyld_image_header_containing_address((void *)impl.address());
|
||||
unsigned long size = 0;
|
||||
textSegment = (uintptr_t)
|
||||
getsegmentdata((headerType *)mh, "__TEXT", &size);
|
||||
textSegmentSize = size;
|
||||
|
||||
check();
|
||||
}
|
||||
};
|
||||
|
||||
std::atomic<TrampolinePointers *> trampolines{nil};
|
||||
|
||||
TrampolinePointers *get() {
|
||||
return trampolines.load(MEMORY_ORDER_CONSUME);
|
||||
}
|
||||
|
||||
public:
|
||||
void Initialize() {
|
||||
if (get()) return;
|
||||
|
||||
// This code may be called concurrently.
|
||||
// In the worst case we perform extra dyld operations.
|
||||
void *dylib = dlopen("/usr/lib/libobjc-trampolines.dylib",
|
||||
RTLD_NOW | RTLD_LOCAL | RTLD_FIRST);
|
||||
if (!dylib) {
|
||||
_objc_fatal("couldn't dlopen libobjc-trampolines.dylib: %s",
|
||||
dlerror());
|
||||
}
|
||||
|
||||
auto t = new TrampolinePointers(dylib);
|
||||
TrampolinePointers *old = nil;
|
||||
if (! trampolines.compare_exchange_strong(old, t, memory_order_release))
|
||||
{
|
||||
delete t; // Lost an initialization race.
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t textSegment() { return get()->textSegment; }
|
||||
uintptr_t textSegmentSize() { return get()->textSegmentSize; }
|
||||
|
||||
// See comments below about PAGE_SIZE and PAGE_MAX_SIZE.
|
||||
uintptr_t dataSize() { return PAGE_MAX_SIZE; }
|
||||
|
||||
uintptr_t impl() { return get()->impl.address(); }
|
||||
uintptr_t start() { return get()->start.address(); }
|
||||
};
|
||||
|
||||
static TrampolinePointerWrapper Trampolines;
|
||||
|
||||
// argument mode identifier
|
||||
// Some calculations assume that these modes are sequential starting from 0.
|
||||
// This order must match the order of the trampoline's assembly code.
|
||||
typedef enum {
|
||||
ReturnValueInRegisterArgumentMode,
|
||||
#if SUPPORT_STRET
|
||||
@ -65,34 +199,46 @@ typedef enum {
|
||||
ArgumentModeCount
|
||||
} ArgumentMode;
|
||||
|
||||
|
||||
// We must take care with our data layout on architectures that support
|
||||
// multiple page sizes.
|
||||
//
|
||||
// The trampoline template in __TEXT is sized and aligned with PAGE_MAX_SIZE.
|
||||
// On some platforms this requires additional linker flags.
|
||||
//
|
||||
// When we allocate a page pair, we use PAGE_MAX_SIZE size.
|
||||
// When we allocate a page group, we use PAGE_MAX_SIZE size.
|
||||
// This allows trampoline code to find its data by subtracting PAGE_MAX_SIZE.
|
||||
//
|
||||
// When we allocate a page pair, we use the process's page alignment.
|
||||
// When we allocate a page group, we use the process's page alignment.
|
||||
// This simplifies allocation because we don't need to force greater than
|
||||
// default alignment when running with small pages, but it also means
|
||||
// the trampoline code MUST NOT look for its data by masking with PAGE_MAX_MASK.
|
||||
|
||||
struct TrampolineBlockPagePair
|
||||
struct TrampolineBlockPageGroup
|
||||
{
|
||||
TrampolineBlockPagePair *nextPagePair; // linked list of all pages
|
||||
TrampolineBlockPagePair *nextAvailablePage; // linked list of pages with available slots
|
||||
|
||||
TrampolineBlockPageGroup *nextPageGroup; // linked list of all pages
|
||||
TrampolineBlockPageGroup *nextAvailablePage; // linked list of pages with available slots
|
||||
|
||||
uintptr_t nextAvailable; // index of next available slot, endIndex() if no more available
|
||||
|
||||
const void * TrampolinePtrauth const text; // text VM region; stored only for the benefit of the leaks tool
|
||||
|
||||
TrampolineBlockPageGroup()
|
||||
: nextPageGroup(nil)
|
||||
, nextAvailablePage(nil)
|
||||
, nextAvailable(startIndex())
|
||||
, text((const void *)((uintptr_t)this + Trampolines.dataSize()))
|
||||
{ }
|
||||
|
||||
// Payload data: block pointers and free list.
|
||||
// Bytes parallel with trampoline header code are the fields above or unused
|
||||
// uint8_t blocks[ PAGE_MAX_SIZE - sizeof(TrampolineBlockPagePair) ]
|
||||
|
||||
// Code: trampoline header followed by trampolines.
|
||||
// uint8_t trampolines[PAGE_MAX_SIZE];
|
||||
// uint8_t payloads[PAGE_MAX_SIZE - sizeof(TrampolineBlockPageGroup)]
|
||||
|
||||
// Code: Mach-O header, then trampoline header followed by trampolines.
|
||||
// On platforms with struct return we have non-stret trampolines and
|
||||
// stret trampolines. The stret and non-stret trampolines at a given
|
||||
// index share the same data page.
|
||||
// uint8_t macho[PAGE_MAX_SIZE];
|
||||
// uint8_t trampolines[ArgumentModeCount][PAGE_MAX_SIZE];
|
||||
|
||||
// Per-trampoline block data format:
|
||||
// initial value is 0 while page data is filled sequentially
|
||||
@ -105,11 +251,11 @@ struct TrampolineBlockPagePair
|
||||
};
|
||||
|
||||
static uintptr_t headerSize() {
|
||||
return (uintptr_t) (a1a2_firsttramp() - a1a2_tramphead());
|
||||
return (uintptr_t) (Trampolines.start() - Trampolines.impl());
|
||||
}
|
||||
|
||||
static uintptr_t slotSize() {
|
||||
return 8;
|
||||
return SLOT_SIZE;
|
||||
}
|
||||
|
||||
static uintptr_t startIndex() {
|
||||
@ -118,7 +264,7 @@ struct TrampolineBlockPagePair
|
||||
}
|
||||
|
||||
static uintptr_t endIndex() {
|
||||
return (uintptr_t)PAGE_MAX_SIZE / slotSize();
|
||||
return (uintptr_t)Trampolines.dataSize() / slotSize();
|
||||
}
|
||||
|
||||
static bool validIndex(uintptr_t index) {
|
||||
@ -126,208 +272,161 @@ struct TrampolineBlockPagePair
|
||||
}
|
||||
|
||||
Payload *payload(uintptr_t index) {
|
||||
assert(validIndex(index));
|
||||
ASSERT(validIndex(index));
|
||||
return (Payload *)((char *)this + index*slotSize());
|
||||
}
|
||||
|
||||
IMP trampoline(uintptr_t index) {
|
||||
assert(validIndex(index));
|
||||
char *imp = (char *)this + index*slotSize() + PAGE_MAX_SIZE;
|
||||
uintptr_t trampolinesForMode(int aMode) {
|
||||
// Skip over the data area, one page of Mach-O headers,
|
||||
// and one text page for each mode before this one.
|
||||
return (uintptr_t)this + Trampolines.dataSize() +
|
||||
PAGE_MAX_SIZE * (1 + aMode);
|
||||
}
|
||||
|
||||
IMP trampoline(int aMode, uintptr_t index) {
|
||||
ASSERT(validIndex(index));
|
||||
char *base = (char *)trampolinesForMode(aMode);
|
||||
char *imp = base + index*slotSize();
|
||||
#if __arm__
|
||||
imp++; // trampoline is Thumb instructions
|
||||
#endif
|
||||
#if __has_feature(ptrauth_calls)
|
||||
imp = ptrauth_sign_unauthenticated(imp,
|
||||
ptrauth_key_function_pointer, 0);
|
||||
#endif
|
||||
return (IMP)imp;
|
||||
}
|
||||
|
||||
uintptr_t indexForTrampoline(IMP tramp) {
|
||||
uintptr_t tramp0 = (uintptr_t)this + PAGE_MAX_SIZE;
|
||||
uintptr_t start = tramp0 + headerSize();
|
||||
uintptr_t end = tramp0 + PAGE_MAX_SIZE;
|
||||
uintptr_t address = (uintptr_t)tramp;
|
||||
if (address >= start && address < end) {
|
||||
return (uintptr_t)(address - tramp0) / slotSize();
|
||||
uintptr_t indexForTrampoline(uintptr_t tramp) {
|
||||
for (int aMode = 0; aMode < ArgumentModeCount; aMode++) {
|
||||
uintptr_t base = trampolinesForMode(aMode);
|
||||
uintptr_t start = base + startIndex() * slotSize();
|
||||
uintptr_t end = base + endIndex() * slotSize();
|
||||
if (tramp >= start && tramp < end) {
|
||||
return (uintptr_t)(tramp - base) / slotSize();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check() {
|
||||
assert(TrampolineBlockPagePair::slotSize() == 8);
|
||||
assert(TrampolineBlockPagePair::headerSize() >= sizeof(TrampolineBlockPagePair));
|
||||
assert(TrampolineBlockPagePair::headerSize() % TrampolineBlockPagePair::slotSize() == 0);
|
||||
|
||||
// _objc_inform("%p %p %p", a1a2_tramphead(), a1a2_firsttramp(),
|
||||
// a1a2_trampend());
|
||||
assert(a1a2_tramphead() % PAGE_SIZE == 0); // not PAGE_MAX_SIZE
|
||||
assert(a1a2_tramphead() + PAGE_MAX_SIZE == a1a2_trampend());
|
||||
#if SUPPORT_STRET
|
||||
// _objc_inform("%p %p %p", a2a3_tramphead(), a2a3_firsttramp(),
|
||||
// a2a3_trampend());
|
||||
assert(a2a3_tramphead() % PAGE_SIZE == 0); // not PAGE_MAX_SIZE
|
||||
assert(a2a3_tramphead() + PAGE_MAX_SIZE == a2a3_trampend());
|
||||
#endif
|
||||
|
||||
#if __arm__
|
||||
// make sure trampolines are Thumb
|
||||
extern void *_a1a2_firsttramp;
|
||||
extern void *_a2a3_firsttramp;
|
||||
assert(((uintptr_t)&_a1a2_firsttramp) % 2 == 1);
|
||||
assert(((uintptr_t)&_a2a3_firsttramp) % 2 == 1);
|
||||
#endif
|
||||
ASSERT(TrampolineBlockPageGroup::headerSize() >= sizeof(TrampolineBlockPageGroup));
|
||||
ASSERT(TrampolineBlockPageGroup::headerSize() % TrampolineBlockPageGroup::slotSize() == 0);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// two sets of trampoline pages; one for stack returns and one for register returns
|
||||
static TrampolineBlockPagePair *headPagePairs[ArgumentModeCount];
|
||||
static TrampolineBlockPageGroup *HeadPageGroup;
|
||||
|
||||
#pragma mark Utility Functions
|
||||
|
||||
static inline void _lock() {
|
||||
#if __OBJC2__
|
||||
runtimeLock.write();
|
||||
#else
|
||||
classLock.lock();
|
||||
#if !__OBJC2__
|
||||
#define runtimeLock classLock
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void _unlock() {
|
||||
#if __OBJC2__
|
||||
runtimeLock.unlockWrite();
|
||||
#else
|
||||
classLock.unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void _assert_locked() {
|
||||
#if __OBJC2__
|
||||
runtimeLock.assertWriting();
|
||||
#else
|
||||
classLock.assertLocked();
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark Trampoline Management Functions
|
||||
static TrampolineBlockPagePair *_allocateTrampolinesAndData(ArgumentMode aMode)
|
||||
static TrampolineBlockPageGroup *_allocateTrampolinesAndData()
|
||||
{
|
||||
_assert_locked();
|
||||
runtimeLock.assertLocked();
|
||||
|
||||
vm_address_t dataAddress;
|
||||
|
||||
TrampolineBlockPagePair::check();
|
||||
TrampolineBlockPageGroup::check();
|
||||
|
||||
TrampolineBlockPagePair *headPagePair = headPagePairs[aMode];
|
||||
|
||||
if (headPagePair) {
|
||||
assert(headPagePair->nextAvailablePage == nil);
|
||||
}
|
||||
|
||||
// Our final mapping will look roughly like this:
|
||||
// r/w data
|
||||
// r/o text mapped from libobjc-trampolines.dylib
|
||||
// with fixed offsets from the text to the data embedded in the text.
|
||||
//
|
||||
// More precisely it will look like this:
|
||||
// 1 page r/w data
|
||||
// 1 page libobjc-trampolines.dylib Mach-O header
|
||||
// N pages trampoline code, one for each ArgumentMode
|
||||
// M pages for the rest of libobjc-trampolines' TEXT segment.
|
||||
// The kernel requires that we remap the entire TEXT segment every time.
|
||||
// We assume that our code begins on the second TEXT page, but are robust
|
||||
// against other additions to the end of the TEXT segment.
|
||||
|
||||
ASSERT(HeadPageGroup == nil || HeadPageGroup->nextAvailablePage == nil);
|
||||
|
||||
auto textSource = Trampolines.textSegment();
|
||||
auto textSourceSize = Trampolines.textSegmentSize();
|
||||
auto dataSize = Trampolines.dataSize();
|
||||
|
||||
// Allocate a single contiguous region big enough to hold data+text.
|
||||
kern_return_t result;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
result = vm_allocate(mach_task_self(), &dataAddress,
|
||||
PAGE_MAX_SIZE * 2,
|
||||
TRUE | VM_MAKE_TAG(VM_MEMORY_FOUNDATION));
|
||||
if (result != KERN_SUCCESS) {
|
||||
mach_error("vm_allocate failed", result);
|
||||
return nil;
|
||||
}
|
||||
|
||||
vm_address_t codeAddress = dataAddress + PAGE_MAX_SIZE;
|
||||
result = vm_deallocate(mach_task_self(), codeAddress, PAGE_MAX_SIZE);
|
||||
if (result != KERN_SUCCESS) {
|
||||
mach_error("vm_deallocate failed", result);
|
||||
return nil;
|
||||
}
|
||||
|
||||
uintptr_t codePage;
|
||||
switch(aMode) {
|
||||
case ReturnValueInRegisterArgumentMode:
|
||||
codePage = a1a2_tramphead();
|
||||
break;
|
||||
#if SUPPORT_STRET
|
||||
case ReturnValueOnStackArgumentMode:
|
||||
codePage = a2a3_tramphead();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
_objc_fatal("unknown return mode %d", (int)aMode);
|
||||
break;
|
||||
}
|
||||
vm_prot_t currentProtection, maxProtection;
|
||||
result = vm_remap(mach_task_self(), &codeAddress, PAGE_MAX_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_MAX_SIZE);
|
||||
if (result != KERN_SUCCESS) {
|
||||
mach_error("vm_deallocate for retry failed.", result);
|
||||
return nil;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result = vm_allocate(mach_task_self(), &dataAddress,
|
||||
dataSize + textSourceSize,
|
||||
VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_FOUNDATION));
|
||||
if (result != KERN_SUCCESS) {
|
||||
return nil;
|
||||
_objc_fatal("vm_allocate trampolines failed (%d)", result);
|
||||
}
|
||||
|
||||
// Remap libobjc-trampolines' TEXT segment atop all
|
||||
// but the first of the pages we just allocated:
|
||||
vm_address_t textDest = dataAddress + dataSize;
|
||||
vm_prot_t currentProtection, maxProtection;
|
||||
result = vm_remap(mach_task_self(), &textDest,
|
||||
textSourceSize,
|
||||
0, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
|
||||
mach_task_self(), textSource, TRUE,
|
||||
¤tProtection, &maxProtection, VM_INHERIT_SHARE);
|
||||
if (result != KERN_SUCCESS) {
|
||||
_objc_fatal("vm_remap trampolines failed (%d)", result);
|
||||
}
|
||||
|
||||
auto *pageGroup = new ((void*)dataAddress) TrampolineBlockPageGroup;
|
||||
|
||||
TrampolineBlockPagePair *pagePair = (TrampolineBlockPagePair *) dataAddress;
|
||||
pagePair->nextAvailable = pagePair->startIndex();
|
||||
pagePair->nextPagePair = nil;
|
||||
pagePair->nextAvailablePage = nil;
|
||||
|
||||
if (headPagePair) {
|
||||
TrampolineBlockPagePair *lastPagePair = headPagePair;
|
||||
while(lastPagePair->nextPagePair)
|
||||
lastPagePair = lastPagePair->nextPagePair;
|
||||
|
||||
lastPagePair->nextPagePair = pagePair;
|
||||
headPagePairs[aMode]->nextAvailablePage = pagePair;
|
||||
if (HeadPageGroup) {
|
||||
TrampolineBlockPageGroup *lastPageGroup = HeadPageGroup;
|
||||
while(lastPageGroup->nextPageGroup) {
|
||||
lastPageGroup = lastPageGroup->nextPageGroup;
|
||||
}
|
||||
lastPageGroup->nextPageGroup = pageGroup;
|
||||
HeadPageGroup->nextAvailablePage = pageGroup;
|
||||
} else {
|
||||
headPagePairs[aMode] = pagePair;
|
||||
HeadPageGroup = pageGroup;
|
||||
}
|
||||
|
||||
return pagePair;
|
||||
return pageGroup;
|
||||
}
|
||||
|
||||
static TrampolineBlockPagePair *
|
||||
_getOrAllocatePagePairWithNextAvailable(ArgumentMode aMode)
|
||||
static TrampolineBlockPageGroup *
|
||||
getOrAllocatePageGroupWithNextAvailable()
|
||||
{
|
||||
_assert_locked();
|
||||
runtimeLock.assertLocked();
|
||||
|
||||
TrampolineBlockPagePair *headPagePair = headPagePairs[aMode];
|
||||
|
||||
if (!headPagePair)
|
||||
return _allocateTrampolinesAndData(aMode);
|
||||
if (!HeadPageGroup)
|
||||
return _allocateTrampolinesAndData();
|
||||
|
||||
// make sure head page is filled first
|
||||
if (headPagePair->nextAvailable != headPagePair->endIndex())
|
||||
return headPagePair;
|
||||
if (HeadPageGroup->nextAvailable != HeadPageGroup->endIndex())
|
||||
return HeadPageGroup;
|
||||
|
||||
if (headPagePair->nextAvailablePage) // check if there is a page w/a hole
|
||||
return headPagePair->nextAvailablePage;
|
||||
if (HeadPageGroup->nextAvailablePage) // check if there is a page w/a hole
|
||||
return HeadPageGroup->nextAvailablePage;
|
||||
|
||||
return _allocateTrampolinesAndData(aMode); // tack on a new one
|
||||
return _allocateTrampolinesAndData(); // tack on a new one
|
||||
}
|
||||
|
||||
static TrampolineBlockPagePair *
|
||||
_pageAndIndexContainingIMP(IMP anImp, uintptr_t *outIndex,
|
||||
TrampolineBlockPagePair **outHeadPagePair)
|
||||
static TrampolineBlockPageGroup *
|
||||
pageAndIndexContainingIMP(IMP anImp, uintptr_t *outIndex)
|
||||
{
|
||||
_assert_locked();
|
||||
runtimeLock.assertLocked();
|
||||
|
||||
for (int arg = 0; arg < ArgumentModeCount; arg++) {
|
||||
for (TrampolineBlockPagePair *pagePair = headPagePairs[arg];
|
||||
pagePair;
|
||||
pagePair = pagePair->nextPagePair)
|
||||
{
|
||||
uintptr_t index = pagePair->indexForTrampoline(anImp);
|
||||
if (index) {
|
||||
if (outIndex) *outIndex = index;
|
||||
if (outHeadPagePair) *outHeadPagePair = headPagePairs[arg];
|
||||
return pagePair;
|
||||
}
|
||||
// Authenticate as a function pointer, returning an un-signed address.
|
||||
uintptr_t trampAddress =
|
||||
(uintptr_t)ptrauth_auth_data((const char *)anImp,
|
||||
ptrauth_key_function_pointer, 0);
|
||||
|
||||
for (TrampolineBlockPageGroup *pageGroup = HeadPageGroup;
|
||||
pageGroup;
|
||||
pageGroup = pageGroup->nextPageGroup)
|
||||
{
|
||||
uintptr_t index = pageGroup->indexForTrampoline(trampAddress);
|
||||
if (index) {
|
||||
if (outIndex) *outIndex = index;
|
||||
return pageGroup;
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,7 +435,7 @@ _pageAndIndexContainingIMP(IMP anImp, uintptr_t *outIndex,
|
||||
|
||||
|
||||
static ArgumentMode
|
||||
_argumentModeForBlock(id block)
|
||||
argumentModeForBlock(id block)
|
||||
{
|
||||
ArgumentMode aMode = ReturnValueInRegisterArgumentMode;
|
||||
|
||||
@ -344,29 +443,50 @@ _argumentModeForBlock(id block)
|
||||
if (_Block_has_signature(block) && _Block_use_stret(block))
|
||||
aMode = ReturnValueOnStackArgumentMode;
|
||||
#else
|
||||
assert(! (_Block_has_signature(block) && _Block_use_stret(block)));
|
||||
ASSERT(! (_Block_has_signature(block) && _Block_use_stret(block)));
|
||||
#endif
|
||||
|
||||
return aMode;
|
||||
}
|
||||
|
||||
/// Initialize the trampoline machinery. Normally this does nothing, as
|
||||
/// everything is initialized lazily, but for certain processes we eagerly load
|
||||
/// the trampolines dylib.
|
||||
void
|
||||
_imp_implementationWithBlock_init(void)
|
||||
{
|
||||
#if TARGET_OS_OSX
|
||||
// Eagerly load libobjc-trampolines.dylib in certain processes. Some
|
||||
// programs (most notably QtWebEngineProcess used by older versions of
|
||||
// embedded Chromium) enable a highly restrictive sandbox profile which
|
||||
// blocks access to that dylib. If anything calls
|
||||
// imp_implementationWithBlock (as AppKit has started doing) then we'll
|
||||
// crash trying to load it. Loading it here sets it up before the sandbox
|
||||
// profile is enabled and blocks it.
|
||||
//
|
||||
// This fixes EA Origin (rdar://problem/50813789)
|
||||
// and Steam (rdar://problem/55286131)
|
||||
if (__progname &&
|
||||
(strcmp(__progname, "QtWebEngineProcess") == 0 ||
|
||||
strcmp(__progname, "Steam Helper") == 0)) {
|
||||
Trampolines.Initialize();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// `block` must already have been copied
|
||||
IMP
|
||||
_imp_implementationWithBlockNoCopy(id block)
|
||||
{
|
||||
_assert_locked();
|
||||
runtimeLock.assertLocked();
|
||||
|
||||
ArgumentMode aMode = _argumentModeForBlock(block);
|
||||
TrampolineBlockPageGroup *pageGroup =
|
||||
getOrAllocatePageGroupWithNextAvailable();
|
||||
|
||||
TrampolineBlockPagePair *pagePair =
|
||||
_getOrAllocatePagePairWithNextAvailable(aMode);
|
||||
if (!headPagePairs[aMode])
|
||||
headPagePairs[aMode] = pagePair;
|
||||
|
||||
uintptr_t index = pagePair->nextAvailable;
|
||||
assert(index >= pagePair->startIndex() && index < pagePair->endIndex());
|
||||
TrampolineBlockPagePair::Payload *payload = pagePair->payload(index);
|
||||
uintptr_t index = pageGroup->nextAvailable;
|
||||
ASSERT(index >= pageGroup->startIndex() && index < pageGroup->endIndex());
|
||||
TrampolineBlockPageGroup::Payload *payload = pageGroup->payload(index);
|
||||
|
||||
uintptr_t nextAvailableIndex = payload->nextAvailable;
|
||||
if (nextAvailableIndex == 0) {
|
||||
@ -374,104 +494,109 @@ _imp_implementationWithBlockNoCopy(id block)
|
||||
// If the page is now full this will now be endIndex(), handled below.
|
||||
nextAvailableIndex = index + 1;
|
||||
}
|
||||
pagePair->nextAvailable = nextAvailableIndex;
|
||||
if (nextAvailableIndex == pagePair->endIndex()) {
|
||||
// PagePair is now full (free list or wilderness exhausted)
|
||||
pageGroup->nextAvailable = nextAvailableIndex;
|
||||
if (nextAvailableIndex == pageGroup->endIndex()) {
|
||||
// PageGroup is now full (free list or wilderness exhausted)
|
||||
// Remove from available page linked list
|
||||
TrampolineBlockPagePair *iterator = headPagePairs[aMode];
|
||||
while(iterator && (iterator->nextAvailablePage != pagePair)) {
|
||||
TrampolineBlockPageGroup *iterator = HeadPageGroup;
|
||||
while(iterator && (iterator->nextAvailablePage != pageGroup)) {
|
||||
iterator = iterator->nextAvailablePage;
|
||||
}
|
||||
if (iterator) {
|
||||
iterator->nextAvailablePage = pagePair->nextAvailablePage;
|
||||
pagePair->nextAvailablePage = nil;
|
||||
iterator->nextAvailablePage = pageGroup->nextAvailablePage;
|
||||
pageGroup->nextAvailablePage = nil;
|
||||
}
|
||||
}
|
||||
|
||||
payload->block = block;
|
||||
return pagePair->trampoline(index);
|
||||
return pageGroup->trampoline(argumentModeForBlock(block), index);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Public API
|
||||
IMP imp_implementationWithBlock(id block)
|
||||
{
|
||||
// Block object must be copied outside runtimeLock
|
||||
// because it performs arbitrary work.
|
||||
block = Block_copy(block);
|
||||
_lock();
|
||||
IMP returnIMP = _imp_implementationWithBlockNoCopy(block);
|
||||
_unlock();
|
||||
return returnIMP;
|
||||
|
||||
// Trampolines must be initialized outside runtimeLock
|
||||
// because it calls dlopen().
|
||||
Trampolines.Initialize();
|
||||
|
||||
mutex_locker_t lock(runtimeLock);
|
||||
|
||||
return _imp_implementationWithBlockNoCopy(block);
|
||||
}
|
||||
|
||||
|
||||
id imp_getBlock(IMP anImp) {
|
||||
uintptr_t index;
|
||||
TrampolineBlockPagePair *pagePair;
|
||||
TrampolineBlockPageGroup *pageGroup;
|
||||
|
||||
if (!anImp) return nil;
|
||||
|
||||
_lock();
|
||||
mutex_locker_t lock(runtimeLock);
|
||||
|
||||
pagePair = _pageAndIndexContainingIMP(anImp, &index, nil);
|
||||
pageGroup = pageAndIndexContainingIMP(anImp, &index);
|
||||
|
||||
if (!pagePair) {
|
||||
_unlock();
|
||||
if (!pageGroup) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
TrampolineBlockPagePair::Payload *payload = pagePair->payload(index);
|
||||
TrampolineBlockPageGroup::Payload *payload = pageGroup->payload(index);
|
||||
|
||||
if (payload->nextAvailable <= TrampolineBlockPagePair::endIndex()) {
|
||||
if (payload->nextAvailable <= TrampolineBlockPageGroup::endIndex()) {
|
||||
// unallocated
|
||||
_unlock();
|
||||
return nil;
|
||||
}
|
||||
|
||||
_unlock();
|
||||
|
||||
return payload->block;
|
||||
}
|
||||
|
||||
BOOL imp_removeBlock(IMP anImp) {
|
||||
TrampolineBlockPagePair *pagePair;
|
||||
TrampolineBlockPagePair *headPagePair;
|
||||
uintptr_t index;
|
||||
|
||||
if (!anImp) return NO;
|
||||
|
||||
id block;
|
||||
|
||||
_lock();
|
||||
pagePair = _pageAndIndexContainingIMP(anImp, &index, &headPagePair);
|
||||
{
|
||||
mutex_locker_t lock(runtimeLock);
|
||||
|
||||
if (!pagePair) {
|
||||
_unlock();
|
||||
return NO;
|
||||
uintptr_t index;
|
||||
TrampolineBlockPageGroup *pageGroup =
|
||||
pageAndIndexContainingIMP(anImp, &index);
|
||||
|
||||
if (!pageGroup) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
TrampolineBlockPageGroup::Payload *payload = pageGroup->payload(index);
|
||||
block = payload->block;
|
||||
// block is released below, outside the lock
|
||||
|
||||
payload->nextAvailable = pageGroup->nextAvailable;
|
||||
pageGroup->nextAvailable = index;
|
||||
|
||||
// make sure this page is on available linked list
|
||||
TrampolineBlockPageGroup *pageGroupIterator = HeadPageGroup;
|
||||
|
||||
// see if page is the next available page for any existing pages
|
||||
while (pageGroupIterator->nextAvailablePage &&
|
||||
pageGroupIterator->nextAvailablePage != pageGroup)
|
||||
{
|
||||
pageGroupIterator = pageGroupIterator->nextAvailablePage;
|
||||
}
|
||||
|
||||
if (! pageGroupIterator->nextAvailablePage) {
|
||||
// if iteration stopped because nextAvail was nil
|
||||
// add to end of list.
|
||||
pageGroupIterator->nextAvailablePage = pageGroup;
|
||||
pageGroup->nextAvailablePage = nil;
|
||||
}
|
||||
}
|
||||
|
||||
TrampolineBlockPagePair::Payload *payload = pagePair->payload(index);
|
||||
id block = payload->block;
|
||||
// block is released below
|
||||
|
||||
payload->nextAvailable = pagePair->nextAvailable;
|
||||
pagePair->nextAvailable = index;
|
||||
|
||||
// make sure this page is on available linked list
|
||||
TrampolineBlockPagePair *pagePairIterator = headPagePair;
|
||||
|
||||
// see if page 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 nil
|
||||
// add to end of list.
|
||||
pagePairIterator->nextAvailablePage = pagePair;
|
||||
pagePair->nextAvailablePage = nil;
|
||||
}
|
||||
|
||||
_unlock();
|
||||
// do this AFTER dropping the lock
|
||||
Block_release(block);
|
||||
return YES;
|
||||
}
|
||||
|
282
runtime/objc-blocktramps-arm.S
Normal file
282
runtime/objc-blocktramps-arm.S
Normal file
@ -0,0 +1,282 @@
|
||||
#if __arm__
|
||||
|
||||
#include <arm/arch.h>
|
||||
#include <mach/vm_param.h>
|
||||
|
||||
.syntax unified
|
||||
|
||||
.text
|
||||
.globl __objc_blockTrampolineImpl
|
||||
.globl __objc_blockTrampolineStart
|
||||
.globl __objc_blockTrampolineLast
|
||||
|
||||
// Trampoline machinery assumes the trampolines are Thumb function pointers
|
||||
#if !__thumb2__
|
||||
# error sorry
|
||||
#endif
|
||||
|
||||
.thumb
|
||||
|
||||
// Exported symbols are not marked as functions.
|
||||
// The trampoline construction code assumes that the Thumb bit is not set.
|
||||
.thumb_func L__objc_blockTrampolineImpl_func
|
||||
|
||||
.align PAGE_MAX_SHIFT
|
||||
__objc_blockTrampolineImpl:
|
||||
L__objc_blockTrampolineImpl_func:
|
||||
/*
|
||||
r0 == self
|
||||
r12 == pc of trampoline's first instruction + PC bias
|
||||
lr == original return address
|
||||
*/
|
||||
|
||||
mov r1, r0 // _cmd = self
|
||||
|
||||
// Trampoline's data is two pages before the trampoline text.
|
||||
// Also correct PC bias of 4 bytes.
|
||||
sub r12, # 2*PAGE_MAX_SIZE
|
||||
ldr r0, [r12, #-4] // self = block object
|
||||
ldr pc, [r0, #12] // tail call block->invoke
|
||||
// not reached
|
||||
|
||||
.macro TrampolineEntry
|
||||
mov r12, pc
|
||||
b L__objc_blockTrampolineImpl_func
|
||||
.align 3
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX16
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX256
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
.endmacro
|
||||
|
||||
.align 5
|
||||
__objc_blockTrampolineStart:
|
||||
// 2048-4 trampolines to fill 16K page
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
__objc_blockTrampolineLast:
|
||||
TrampolineEntry
|
||||
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
|
||||
|
||||
|
||||
.text
|
||||
.globl __objc_blockTrampolineImpl_stret
|
||||
.globl __objc_blockTrampolineStart_stret
|
||||
.globl __objc_blockTrampolineLast_stret
|
||||
|
||||
// Trampoline machinery assumes the trampolines are Thumb function pointers
|
||||
#if !__thumb2__
|
||||
# error sorry
|
||||
#endif
|
||||
|
||||
.thumb
|
||||
|
||||
// Exported symbols are not marked as functions.
|
||||
// The trampoline construction code assumes that the Thumb bit is not set.
|
||||
.thumb_func L__objc_blockTrampolineImpl_stret_func
|
||||
|
||||
.align PAGE_MAX_SHIFT
|
||||
__objc_blockTrampolineImpl_stret:
|
||||
L__objc_blockTrampolineImpl_stret_func:
|
||||
/*
|
||||
r1 == self
|
||||
r12 == pc of trampoline's first instruction + PC bias
|
||||
lr == original return address
|
||||
*/
|
||||
|
||||
mov r2, r1 // _cmd = self
|
||||
|
||||
// Trampoline's data is three pages before the trampoline text.
|
||||
// Also correct PC bias of 4 bytes.
|
||||
sub r12, # 3*PAGE_MAX_SIZE
|
||||
ldr r1, [r12, #-4] // self = block object
|
||||
ldr pc, [r1, #12] // tail call block->invoke
|
||||
// not reached
|
||||
|
||||
.macro TrampolineEntry_stret
|
||||
mov r12, pc
|
||||
b L__objc_blockTrampolineImpl_stret_func
|
||||
.align 3
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX16_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX256_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
.endmacro
|
||||
|
||||
.align 5
|
||||
__objc_blockTrampolineStart_stret:
|
||||
// 2048-4 trampolines to fill 16K page
|
||||
TrampolineEntryX256_stret
|
||||
TrampolineEntryX256_stret
|
||||
TrampolineEntryX256_stret
|
||||
TrampolineEntryX256_stret
|
||||
|
||||
TrampolineEntryX256_stret
|
||||
TrampolineEntryX256_stret
|
||||
TrampolineEntryX256_stret
|
||||
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
TrampolineEntryX16_stret
|
||||
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
TrampolineEntry_stret
|
||||
__objc_blockTrampolineLast_stret:
|
||||
TrampolineEntry_stret
|
||||
|
||||
// TrampolineEntry_stret
|
||||
// TrampolineEntry_stret
|
||||
// TrampolineEntry_stret
|
||||
// TrampolineEntry_stret
|
||||
|
||||
#endif
|
@ -1,35 +1,46 @@
|
||||
#if __arm64__
|
||||
|
||||
#include <mach/vm_param.h>
|
||||
#include "arm64-asm.h"
|
||||
|
||||
// Offset of block->invoke field.
|
||||
#if __LP64__
|
||||
// true arm64
|
||||
# define BLOCK_INVOKE 16
|
||||
#else
|
||||
// arm64_32
|
||||
# define BLOCK_INVOKE 12
|
||||
#endif
|
||||
|
||||
.text
|
||||
|
||||
.private_extern __a1a2_tramphead
|
||||
.private_extern __a1a2_firsttramp
|
||||
.private_extern __a1a2_trampend
|
||||
.globl __objc_blockTrampolineImpl
|
||||
.globl __objc_blockTrampolineStart
|
||||
.globl __objc_blockTrampolineLast
|
||||
|
||||
.align PAGE_MAX_SHIFT
|
||||
__a1a2_tramphead:
|
||||
L_a1a2_tramphead:
|
||||
__objc_blockTrampolineImpl:
|
||||
L_objc_blockTrampolineImpl:
|
||||
/*
|
||||
x0 == self
|
||||
x17 == address of called trampoline's data (1 page before its code)
|
||||
x17 == address of called trampoline's data (2 pages before its code)
|
||||
lr == original return address
|
||||
*/
|
||||
|
||||
mov x1, x0 // _cmd = self
|
||||
ldr x0, [x17] // self = block object
|
||||
ldr x16, [x0, #16] // tail call block->invoke
|
||||
br x16
|
||||
ldr p0, [x17] // self = block object
|
||||
add p15, p0, #BLOCK_INVOKE // x15 = &block->invoke
|
||||
ldr p16, [x15] // x16 = block->invoke
|
||||
TailCallBlockInvoke x16, x15
|
||||
|
||||
// pad up to TrampolineBlockPagePair header size
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
.macro TrampolineEntry
|
||||
// load address of trampoline data (one page before this instruction)
|
||||
adr x17, -PAGE_MAX_SIZE
|
||||
b L_a1a2_tramphead
|
||||
// load address of trampoline data (two pages before this instruction)
|
||||
adr x17, -2*PAGE_MAX_SIZE
|
||||
b L_objc_blockTrampolineImpl
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX16
|
||||
@ -77,9 +88,8 @@ L_a1a2_tramphead:
|
||||
.endmacro
|
||||
|
||||
.align 3
|
||||
.private_extern __a1a2_firsttramp
|
||||
__a1a2_firsttramp:
|
||||
// 2048-3 trampolines to fill 16K page
|
||||
__objc_blockTrampolineStart:
|
||||
// 2048-4 trampolines to fill 16K page
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
@ -121,14 +131,12 @@ __a1a2_firsttramp:
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
__objc_blockTrampolineLast:
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
|
||||
.private_extern __a1a2_trampend
|
||||
__a1a2_trampend:
|
||||
// TrampolineEntry
|
||||
|
||||
#endif
|
1102
runtime/objc-blocktramps-i386.S
Normal file
1102
runtime/objc-blocktramps-i386.S
Normal file
File diff suppressed because it is too large
Load Diff
1097
runtime/objc-blocktramps-x86_64.S
Normal file
1097
runtime/objc-blocktramps-x86_64.S
Normal file
File diff suppressed because it is too large
Load Diff
@ -31,6 +31,7 @@ __BEGIN_DECLS
|
||||
extern IMP _cache_getImp(Class cls, SEL sel);
|
||||
extern Method _cache_getMethod(Class cls, SEL sel, IMP objc_msgForward_internal_imp);
|
||||
|
||||
extern void cache_init(void);
|
||||
extern void flush_cache(Class cls);
|
||||
extern bool _cache_fill(Class cls, Method meth, SEL sel);
|
||||
extern void _cache_addForwardEntry(Class cls, SEL sel);
|
||||
|
@ -693,8 +693,14 @@ static uintptr_t _get_pc_for_thread(thread_t thread)
|
||||
* 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[];
|
||||
typedef struct {
|
||||
uint64_t location;
|
||||
unsigned short length;
|
||||
unsigned short recovery_offs;
|
||||
unsigned int flags;
|
||||
} task_restartable_range_t;
|
||||
|
||||
extern "C" task_restartable_range_t objc_restartableRanges[];
|
||||
|
||||
static int _collecting_in_critical(void)
|
||||
{
|
||||
@ -707,7 +713,7 @@ static int _collecting_in_critical(void)
|
||||
kern_return_t ret;
|
||||
int result;
|
||||
|
||||
mach_port_t mythread = pthread_mach_thread_np(pthread_self());
|
||||
mach_port_t mythread = pthread_mach_thread_np(objc_thread_self());
|
||||
|
||||
// Get a list of all the threads in the current task
|
||||
ret = task_threads (mach_task_self (), &threads, &number);
|
||||
@ -738,10 +744,11 @@ static int _collecting_in_critical(void)
|
||||
}
|
||||
|
||||
// Check whether it is in the cache lookup code
|
||||
for (region = 0; objc_entryPoints[region] != 0; region++)
|
||||
for (region = 0; objc_restartableRanges[region].location != 0; region++)
|
||||
{
|
||||
if ((pc >= objc_entryPoints[region]) &&
|
||||
(pc <= objc_exitPoints[region]))
|
||||
uint32_t loc = (uint32_t)objc_restartableRanges[region].location;
|
||||
if ((pc > loc) &&
|
||||
(pc - loc) < objc_restartableRanges[region].length)
|
||||
{
|
||||
result = TRUE;
|
||||
goto done;
|
||||
@ -1788,6 +1795,9 @@ void _class_printMethodCacheStatistics(void)
|
||||
|
||||
#endif
|
||||
|
||||
void cache_init()
|
||||
{
|
||||
}
|
||||
|
||||
// !__OBJC2__
|
||||
#endif
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
extern void cache_init(void);
|
||||
|
||||
extern IMP cache_getImp(Class cls, SEL sel);
|
||||
|
||||
extern void cache_fill(Class cls, SEL sel, IMP imp, id receiver);
|
||||
|
@ -90,7 +90,9 @@
|
||||
/* 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)
|
||||
INIT_CACHE_SIZE = (1 << INIT_CACHE_SIZE_LOG2),
|
||||
MAX_CACHE_SIZE_LOG2 = 16,
|
||||
MAX_CACHE_SIZE = (1 << MAX_CACHE_SIZE_LOG2),
|
||||
};
|
||||
|
||||
static void cache_collect_free(struct bucket_t *data, mask_t capacity);
|
||||
@ -147,8 +149,14 @@ asm("\n .section __TEXT,__const"
|
||||
"\n .globl __objc_empty_vtable"
|
||||
"\n .set __objc_empty_vtable, 0"
|
||||
"\n .globl __objc_empty_cache"
|
||||
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
|
||||
"\n .align 4"
|
||||
"\n L__objc_empty_cache: .space " stringize2(EMPTY_BYTES)
|
||||
"\n .set __objc_empty_cache, L__objc_empty_cache + 0xf"
|
||||
#else
|
||||
"\n .align 3"
|
||||
"\n __objc_empty_cache: .space " stringize2(EMPTY_BYTES)
|
||||
#endif
|
||||
);
|
||||
|
||||
|
||||
@ -173,111 +181,133 @@ static inline mask_t cache_next(mask_t i, mask_t mask) {
|
||||
#endif
|
||||
|
||||
|
||||
// copied from dispatch_atomic_maximally_synchronizing_barrier
|
||||
// fixme verify that this barrier hack does in fact work here
|
||||
#if __x86_64__
|
||||
#define mega_barrier() \
|
||||
do { unsigned long _clbr; __asm__ __volatile__( \
|
||||
"cpuid" \
|
||||
: "=a" (_clbr) : "0" (0) : "rbx", "rcx", "rdx", "cc", "memory" \
|
||||
); } while(0)
|
||||
|
||||
#elif __i386__
|
||||
#define mega_barrier() \
|
||||
do { unsigned long _clbr; __asm__ __volatile__( \
|
||||
"cpuid" \
|
||||
: "=a" (_clbr) : "0" (0) : "ebx", "ecx", "edx", "cc", "memory" \
|
||||
); } while(0)
|
||||
|
||||
#elif __arm__ || __arm64__
|
||||
// mega_barrier doesn't really work, but it works enough on ARM that
|
||||
// we leave well enough alone and keep using it there.
|
||||
#if __arm__
|
||||
#define mega_barrier() \
|
||||
__asm__ __volatile__( \
|
||||
"dsb ish" \
|
||||
: : : "memory")
|
||||
|
||||
#else
|
||||
#error unknown architecture
|
||||
#endif
|
||||
|
||||
#if __arm64__
|
||||
|
||||
// Pointer-size register prefix for inline asm
|
||||
# if __LP64__
|
||||
# define p "x" // true arm64
|
||||
# else
|
||||
# define p "w" // arm64_32
|
||||
# endif
|
||||
|
||||
// Use atomic double-word instructions to update cache entries.
|
||||
// This requires cache buckets not cross cache line boundaries.
|
||||
#define stp(onep, twop, destp) \
|
||||
__asm__ ("stp %[one], %[two], [%[dest]]" \
|
||||
: "=m" (((uint64_t *)(destp))[0]), \
|
||||
"=m" (((uint64_t *)(destp))[1]) \
|
||||
: [one] "r" (onep), \
|
||||
[two] "r" (twop), \
|
||||
[dest] "r" (destp) \
|
||||
: /* no clobbers */ \
|
||||
)
|
||||
#define ldp(onep, twop, srcp) \
|
||||
__asm__ ("ldp %[one], %[two], [%[src]]" \
|
||||
: [one] "=r" (onep), \
|
||||
[two] "=r" (twop) \
|
||||
: "m" (((uint64_t *)(srcp))[0]), \
|
||||
"m" (((uint64_t *)(srcp))[1]), \
|
||||
[src] "r" (srcp) \
|
||||
: /* no clobbers */ \
|
||||
)
|
||||
static ALWAYS_INLINE void
|
||||
stp(uintptr_t onep, uintptr_t twop, void *destp)
|
||||
{
|
||||
__asm__ ("stp %" p "[one], %" p "[two], [%x[dest]]"
|
||||
: "=m" (((uintptr_t *)(destp))[0]),
|
||||
"=m" (((uintptr_t *)(destp))[1])
|
||||
: [one] "r" (onep),
|
||||
[two] "r" (twop),
|
||||
[dest] "r" (destp)
|
||||
: /* no clobbers */
|
||||
);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE void __unused
|
||||
ldp(uintptr_t& onep, uintptr_t& twop, const void *srcp)
|
||||
{
|
||||
__asm__ ("ldp %" p "[one], %" p "[two], [%x[src]]"
|
||||
: [one] "=r" (onep),
|
||||
[two] "=r" (twop)
|
||||
: "m" (((const uintptr_t *)(srcp))[0]),
|
||||
"m" (((const uintptr_t *)(srcp))[1]),
|
||||
[src] "r" (srcp)
|
||||
: /* no clobbers */
|
||||
);
|
||||
}
|
||||
|
||||
#undef p
|
||||
#endif
|
||||
|
||||
|
||||
// Class points to cache. SEL is key. Cache buckets store SEL+IMP.
|
||||
// Caches are never built in the dyld shared cache.
|
||||
|
||||
static inline mask_t cache_hash(cache_key_t key, mask_t mask)
|
||||
static inline mask_t cache_hash(SEL sel, mask_t mask)
|
||||
{
|
||||
return (mask_t)(key & mask);
|
||||
return (mask_t)(uintptr_t)sel & mask;
|
||||
}
|
||||
|
||||
cache_t *getCache(Class cls)
|
||||
{
|
||||
assert(cls);
|
||||
ASSERT(cls);
|
||||
return &cls->cache;
|
||||
}
|
||||
|
||||
cache_key_t getKey(SEL sel)
|
||||
{
|
||||
assert(sel);
|
||||
return (cache_key_t)sel;
|
||||
}
|
||||
|
||||
#if __arm64__
|
||||
|
||||
void bucket_t::set(cache_key_t newKey, IMP newImp)
|
||||
template<Atomicity atomicity, IMPEncoding impEncoding>
|
||||
void bucket_t::set(SEL newSel, IMP newImp, Class cls)
|
||||
{
|
||||
assert(_key == 0 || _key == newKey);
|
||||
ASSERT(_sel.load(memory_order::memory_order_relaxed) == 0 ||
|
||||
_sel.load(memory_order::memory_order_relaxed) == newSel);
|
||||
|
||||
// LDP/STP guarantees that all observers get
|
||||
// either key/imp or newKey/newImp
|
||||
stp(newKey, newImp, this);
|
||||
static_assert(offsetof(bucket_t,_imp) == 0 &&
|
||||
offsetof(bucket_t,_sel) == sizeof(void *),
|
||||
"bucket_t layout doesn't match arm64 bucket_t::set()");
|
||||
|
||||
uintptr_t encodedImp = (impEncoding == Encoded
|
||||
? encodeImp(newImp, newSel, cls)
|
||||
: (uintptr_t)newImp);
|
||||
|
||||
// LDP/STP guarantees that all observers get
|
||||
// either imp/sel or newImp/newSel
|
||||
stp(encodedImp, (uintptr_t)newSel, this);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void bucket_t::set(cache_key_t newKey, IMP newImp)
|
||||
template<Atomicity atomicity, IMPEncoding impEncoding>
|
||||
void bucket_t::set(SEL newSel, IMP newImp, Class cls)
|
||||
{
|
||||
assert(_key == 0 || _key == newKey);
|
||||
ASSERT(_sel.load(memory_order::memory_order_relaxed) == 0 ||
|
||||
_sel.load(memory_order::memory_order_relaxed) == newSel);
|
||||
|
||||
// objc_msgSend uses key and imp with no locks.
|
||||
// It is safe for objc_msgSend to see new imp but NULL key
|
||||
// objc_msgSend uses sel and imp with no locks.
|
||||
// It is safe for objc_msgSend to see new imp but NULL sel
|
||||
// (It will get a cache miss but not dispatch to the wrong place.)
|
||||
// It is unsafe for objc_msgSend to see old imp and new key.
|
||||
// Therefore we write new imp, wait a lot, then write new key.
|
||||
// It is unsafe for objc_msgSend to see old imp and new sel.
|
||||
// Therefore we write new imp, wait a lot, then write new sel.
|
||||
|
||||
_imp = newImp;
|
||||
|
||||
if (_key != newKey) {
|
||||
mega_barrier();
|
||||
_key = newKey;
|
||||
uintptr_t newIMP = (impEncoding == Encoded
|
||||
? encodeImp(newImp, newSel, cls)
|
||||
: (uintptr_t)newImp);
|
||||
|
||||
if (atomicity == Atomic) {
|
||||
_imp.store(newIMP, memory_order::memory_order_relaxed);
|
||||
|
||||
if (_sel.load(memory_order::memory_order_relaxed) != newSel) {
|
||||
#ifdef __arm__
|
||||
mega_barrier();
|
||||
_sel.store(newSel, memory_order::memory_order_relaxed);
|
||||
#elif __x86_64__ || __i386__
|
||||
_sel.store(newSel, memory_order::memory_order_release);
|
||||
#else
|
||||
#error Don't know how to do bucket_t::set on this architecture.
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
_imp.store(newIMP, memory_order::memory_order_relaxed);
|
||||
_sel.store(newSel, memory_order::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
|
||||
|
||||
void cache_t::setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask)
|
||||
{
|
||||
// objc_msgSend uses mask and buckets with no locks.
|
||||
@ -287,29 +317,138 @@ void cache_t::setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask)
|
||||
// Therefore we write new buckets, wait a lot, then write new mask.
|
||||
// objc_msgSend reads mask first, then buckets.
|
||||
|
||||
#ifdef __arm__
|
||||
// ensure other threads see buckets contents before buckets pointer
|
||||
mega_barrier();
|
||||
|
||||
_buckets = newBuckets;
|
||||
_buckets.store(newBuckets, memory_order::memory_order_relaxed);
|
||||
|
||||
// ensure other threads see new buckets before new mask
|
||||
mega_barrier();
|
||||
|
||||
_mask = newMask;
|
||||
_mask.store(newMask, memory_order::memory_order_relaxed);
|
||||
_occupied = 0;
|
||||
#elif __x86_64__ || i386
|
||||
// ensure other threads see buckets contents before buckets pointer
|
||||
_buckets.store(newBuckets, memory_order::memory_order_release);
|
||||
|
||||
// ensure other threads see new buckets before new mask
|
||||
_mask.store(newMask, memory_order::memory_order_release);
|
||||
_occupied = 0;
|
||||
#else
|
||||
#error Don't know how to do setBucketsAndMask on this architecture.
|
||||
#endif
|
||||
}
|
||||
|
||||
struct bucket_t *cache_t::emptyBuckets()
|
||||
{
|
||||
return (bucket_t *)&_objc_empty_cache;
|
||||
}
|
||||
|
||||
struct bucket_t *cache_t::buckets()
|
||||
{
|
||||
return _buckets;
|
||||
return _buckets.load(memory_order::memory_order_relaxed);
|
||||
}
|
||||
|
||||
mask_t cache_t::mask()
|
||||
{
|
||||
return _mask;
|
||||
return _mask.load(memory_order::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void cache_t::initializeToEmpty()
|
||||
{
|
||||
bzero(this, sizeof(*this));
|
||||
_buckets.store((bucket_t *)&_objc_empty_cache, memory_order::memory_order_relaxed);
|
||||
}
|
||||
|
||||
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
|
||||
|
||||
void cache_t::setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask)
|
||||
{
|
||||
uintptr_t buckets = (uintptr_t)newBuckets;
|
||||
uintptr_t mask = (uintptr_t)newMask;
|
||||
|
||||
ASSERT(buckets <= bucketsMask);
|
||||
ASSERT(mask <= maxMask);
|
||||
|
||||
_maskAndBuckets.store(((uintptr_t)newMask << maskShift) | (uintptr_t)newBuckets, std::memory_order_relaxed);
|
||||
_occupied = 0;
|
||||
}
|
||||
|
||||
struct bucket_t *cache_t::emptyBuckets()
|
||||
{
|
||||
return (bucket_t *)&_objc_empty_cache;
|
||||
}
|
||||
|
||||
struct bucket_t *cache_t::buckets()
|
||||
{
|
||||
uintptr_t maskAndBuckets = _maskAndBuckets.load(memory_order::memory_order_relaxed);
|
||||
return (bucket_t *)(maskAndBuckets & bucketsMask);
|
||||
}
|
||||
|
||||
mask_t cache_t::mask()
|
||||
{
|
||||
uintptr_t maskAndBuckets = _maskAndBuckets.load(memory_order::memory_order_relaxed);
|
||||
return maskAndBuckets >> maskShift;
|
||||
}
|
||||
|
||||
void cache_t::initializeToEmpty()
|
||||
{
|
||||
bzero(this, sizeof(*this));
|
||||
_maskAndBuckets.store((uintptr_t)&_objc_empty_cache, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
|
||||
|
||||
void cache_t::setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask)
|
||||
{
|
||||
uintptr_t buckets = (uintptr_t)newBuckets;
|
||||
unsigned mask = (unsigned)newMask;
|
||||
|
||||
ASSERT(buckets == (buckets & bucketsMask));
|
||||
ASSERT(mask <= 0xffff);
|
||||
|
||||
// The shift amount is equal to the number of leading zeroes in
|
||||
// the last 16 bits of mask. Count all the leading zeroes, then
|
||||
// subtract to ignore the top half.
|
||||
uintptr_t maskShift = __builtin_clz(mask) - (sizeof(mask) * CHAR_BIT - 16);
|
||||
ASSERT(mask == (0xffff >> maskShift));
|
||||
|
||||
_maskAndBuckets.store(buckets | maskShift, memory_order::memory_order_relaxed);
|
||||
_occupied = 0;
|
||||
|
||||
ASSERT(this->buckets() == newBuckets);
|
||||
ASSERT(this->mask() == newMask);
|
||||
}
|
||||
|
||||
struct bucket_t *cache_t::emptyBuckets()
|
||||
{
|
||||
return (bucket_t *)((uintptr_t)&_objc_empty_cache & bucketsMask);
|
||||
}
|
||||
|
||||
struct bucket_t *cache_t::buckets()
|
||||
{
|
||||
uintptr_t maskAndBuckets = _maskAndBuckets.load(memory_order::memory_order_relaxed);
|
||||
return (bucket_t *)(maskAndBuckets & bucketsMask);
|
||||
}
|
||||
|
||||
mask_t cache_t::mask()
|
||||
{
|
||||
uintptr_t maskAndBuckets = _maskAndBuckets.load(memory_order::memory_order_relaxed);
|
||||
uintptr_t maskShift = (maskAndBuckets & maskMask);
|
||||
return 0xffff >> maskShift;
|
||||
}
|
||||
|
||||
void cache_t::initializeToEmpty()
|
||||
{
|
||||
bzero(this, sizeof(*this));
|
||||
_maskAndBuckets.store((uintptr_t)&_objc_empty_cache, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
#else
|
||||
#error Unknown cache mask storage type.
|
||||
#endif
|
||||
|
||||
mask_t cache_t::occupied()
|
||||
{
|
||||
return _occupied;
|
||||
@ -320,30 +459,21 @@ void cache_t::incrementOccupied()
|
||||
_occupied++;
|
||||
}
|
||||
|
||||
void cache_t::initializeToEmpty()
|
||||
{
|
||||
bzero(this, sizeof(*this));
|
||||
_buckets = (bucket_t *)&_objc_empty_cache;
|
||||
}
|
||||
|
||||
|
||||
mask_t cache_t::capacity()
|
||||
unsigned cache_t::capacity()
|
||||
{
|
||||
return mask() ? mask()+1 : 0;
|
||||
}
|
||||
|
||||
|
||||
#if CACHE_END_MARKER
|
||||
|
||||
size_t cache_t::bytesForCapacity(uint32_t cap)
|
||||
size_t cache_t::bytesForCapacity(uint32_t cap)
|
||||
{
|
||||
// fixme put end marker inline when capacity+1 malloc is inefficient
|
||||
return sizeof(bucket_t) * (cap + 1);
|
||||
return sizeof(bucket_t) * cap;
|
||||
}
|
||||
|
||||
bucket_t *cache_t::endMarker(struct bucket_t *b, uint32_t cap)
|
||||
#if CACHE_END_MARKER
|
||||
|
||||
bucket_t *cache_t::endMarker(struct bucket_t *b, uint32_t cap)
|
||||
{
|
||||
// bytesForCapacity() chooses whether the end marker is inline or not
|
||||
return (bucket_t *)((uintptr_t)b + bytesForCapacity(cap)) - 1;
|
||||
}
|
||||
|
||||
@ -351,21 +481,18 @@ bucket_t *allocateBuckets(mask_t newCapacity)
|
||||
{
|
||||
// Allocate one extra bucket to mark the end of the list.
|
||||
// This can't overflow mask_t because newCapacity is a power of 2.
|
||||
// fixme instead put the end mark inline when +1 is malloc-inefficient
|
||||
bucket_t *newBuckets = (bucket_t *)
|
||||
calloc(cache_t::bytesForCapacity(newCapacity), 1);
|
||||
|
||||
bucket_t *end = cache_t::endMarker(newBuckets, newCapacity);
|
||||
|
||||
#if __arm__
|
||||
// End marker's key is 1 and imp points BEFORE the first bucket.
|
||||
// End marker's sel is 1 and imp points BEFORE the first bucket.
|
||||
// This saves an instruction in objc_msgSend.
|
||||
end->setKey((cache_key_t)(uintptr_t)1);
|
||||
end->setImp((IMP)(newBuckets - 1));
|
||||
end->set<NotAtomic, Raw>((SEL)(uintptr_t)1, (IMP)(newBuckets - 1), nil);
|
||||
#else
|
||||
// End marker's key is 1 and imp points to the first bucket.
|
||||
end->setKey((cache_key_t)(uintptr_t)1);
|
||||
end->setImp((IMP)newBuckets);
|
||||
// End marker's sel is 1 and imp points to the first bucket.
|
||||
end->set<NotAtomic, Raw>((SEL)(uintptr_t)1, (IMP)newBuckets, nil);
|
||||
#endif
|
||||
|
||||
if (PrintCaches) recordNewCache(newCapacity);
|
||||
@ -375,11 +502,6 @@ bucket_t *allocateBuckets(mask_t newCapacity)
|
||||
|
||||
#else
|
||||
|
||||
size_t cache_t::bytesForCapacity(uint32_t cap)
|
||||
{
|
||||
return sizeof(bucket_t) * cap;
|
||||
}
|
||||
|
||||
bucket_t *allocateBuckets(mask_t newCapacity)
|
||||
{
|
||||
if (PrintCaches) recordNewCache(newCapacity);
|
||||
@ -392,13 +514,17 @@ bucket_t *allocateBuckets(mask_t newCapacity)
|
||||
|
||||
bucket_t *emptyBucketsForCapacity(mask_t capacity, bool allocate = true)
|
||||
{
|
||||
#if CONFIG_USE_CACHE_LOCK
|
||||
cacheUpdateLock.assertLocked();
|
||||
#else
|
||||
runtimeLock.assertLocked();
|
||||
#endif
|
||||
|
||||
size_t bytes = cache_t::bytesForCapacity(capacity);
|
||||
|
||||
// Use _objc_empty_cache if the buckets is small enough.
|
||||
if (bytes <= EMPTY_BYTES) {
|
||||
return (bucket_t *)&_objc_empty_cache;
|
||||
return cache_t::emptyBuckets();
|
||||
}
|
||||
|
||||
// Use shared empty buckets allocated on the heap.
|
||||
@ -443,11 +569,9 @@ bool cache_t::canBeFreed()
|
||||
return !isConstantEmptyCache();
|
||||
}
|
||||
|
||||
|
||||
void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity)
|
||||
ALWAYS_INLINE
|
||||
void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld)
|
||||
{
|
||||
bool freeOld = canBeFreed();
|
||||
|
||||
bucket_t *oldBuckets = buckets();
|
||||
bucket_t *newBuckets = allocateBuckets(newCapacity);
|
||||
|
||||
@ -455,14 +579,13 @@ void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity)
|
||||
// This is thought to save cache memory at the cost of extra cache fills.
|
||||
// fixme re-measure this
|
||||
|
||||
assert(newCapacity > 0);
|
||||
assert((uintptr_t)(mask_t)(newCapacity-1) == newCapacity-1);
|
||||
ASSERT(newCapacity > 0);
|
||||
ASSERT((uintptr_t)(mask_t)(newCapacity-1) == newCapacity-1);
|
||||
|
||||
setBucketsAndMask(newBuckets, newCapacity - 1);
|
||||
|
||||
if (freeOld) {
|
||||
cache_collect_free(oldBuckets, oldCapacity);
|
||||
cache_collect(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -474,16 +597,35 @@ void cache_t::bad_cache(id receiver, SEL sel, Class isa)
|
||||
("Method cache corrupted. This may be a message to an "
|
||||
"invalid object, or a memory error somewhere else.");
|
||||
cache_t *cache = &isa->cache;
|
||||
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
|
||||
bucket_t *buckets = cache->_buckets.load(memory_order::memory_order_relaxed);
|
||||
_objc_inform_now_and_on_crash
|
||||
("%s %p, SEL %p, isa %p, cache %p, buckets %p, "
|
||||
"mask 0x%x, occupied 0x%x",
|
||||
receiver ? "receiver" : "unused", receiver,
|
||||
sel, isa, cache, cache->_buckets,
|
||||
cache->_mask, cache->_occupied);
|
||||
sel, isa, cache, buckets,
|
||||
cache->_mask.load(memory_order::memory_order_relaxed),
|
||||
cache->_occupied);
|
||||
_objc_inform_now_and_on_crash
|
||||
("%s %zu bytes, buckets %zu bytes",
|
||||
receiver ? "receiver" : "unused", malloc_size(receiver),
|
||||
malloc_size(cache->_buckets));
|
||||
malloc_size(buckets));
|
||||
#elif (CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16 || \
|
||||
CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4)
|
||||
uintptr_t maskAndBuckets = cache->_maskAndBuckets.load(memory_order::memory_order_relaxed);
|
||||
_objc_inform_now_and_on_crash
|
||||
("%s %p, SEL %p, isa %p, cache %p, buckets and mask 0x%lx, "
|
||||
"occupied 0x%x",
|
||||
receiver ? "receiver" : "unused", receiver,
|
||||
sel, isa, cache, maskAndBuckets,
|
||||
cache->_occupied);
|
||||
_objc_inform_now_and_on_crash
|
||||
("%s %zu bytes, buckets %zu bytes",
|
||||
receiver ? "receiver" : "unused", malloc_size(receiver),
|
||||
malloc_size(cache->buckets()));
|
||||
#else
|
||||
#error Unknown cache mask storage type.
|
||||
#endif
|
||||
_objc_inform_now_and_on_crash
|
||||
("selector '%s'", sel_getName(sel));
|
||||
_objc_inform_now_and_on_crash
|
||||
@ -493,89 +635,75 @@ void cache_t::bad_cache(id receiver, SEL sel, Class isa)
|
||||
"invalid object, or a memory error somewhere else.");
|
||||
}
|
||||
|
||||
|
||||
bucket_t * cache_t::find(cache_key_t k, id receiver)
|
||||
{
|
||||
assert(k != 0);
|
||||
|
||||
bucket_t *b = buckets();
|
||||
mask_t m = mask();
|
||||
mask_t begin = cache_hash(k, m);
|
||||
mask_t i = begin;
|
||||
do {
|
||||
if (b[i].key() == 0 || b[i].key() == k) {
|
||||
return &b[i];
|
||||
}
|
||||
} while ((i = cache_next(i, m)) != begin);
|
||||
|
||||
// hack
|
||||
Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
|
||||
cache_t::bad_cache(receiver, (SEL)k, cls);
|
||||
}
|
||||
|
||||
|
||||
void cache_t::expand()
|
||||
ALWAYS_INLINE
|
||||
void cache_t::insert(Class cls, SEL sel, IMP imp, id receiver)
|
||||
{
|
||||
#if CONFIG_USE_CACHE_LOCK
|
||||
cacheUpdateLock.assertLocked();
|
||||
|
||||
uint32_t oldCapacity = capacity();
|
||||
uint32_t newCapacity = oldCapacity ? oldCapacity*2 : INIT_CACHE_SIZE;
|
||||
#else
|
||||
runtimeLock.assertLocked();
|
||||
#endif
|
||||
|
||||
if ((uint32_t)(mask_t)newCapacity != newCapacity) {
|
||||
// mask overflow - can't grow further
|
||||
// fixme this wastes one bit of mask
|
||||
newCapacity = oldCapacity;
|
||||
}
|
||||
|
||||
reallocate(oldCapacity, newCapacity);
|
||||
}
|
||||
|
||||
|
||||
static void cache_fill_nolock(Class cls, SEL sel, IMP imp, id receiver)
|
||||
{
|
||||
cacheUpdateLock.assertLocked();
|
||||
|
||||
// Never cache before +initialize is done
|
||||
if (!cls->isInitialized()) return;
|
||||
|
||||
// Make sure the entry wasn't added to the cache by some other thread
|
||||
// before we grabbed the cacheUpdateLock.
|
||||
if (cache_getImp(cls, sel)) return;
|
||||
|
||||
cache_t *cache = getCache(cls);
|
||||
cache_key_t key = getKey(sel);
|
||||
ASSERT(sel != 0 && cls->isInitialized());
|
||||
|
||||
// Use the cache as-is if it is less than 3/4 full
|
||||
mask_t newOccupied = cache->occupied() + 1;
|
||||
mask_t capacity = cache->capacity();
|
||||
if (cache->isConstantEmptyCache()) {
|
||||
mask_t newOccupied = occupied() + 1;
|
||||
unsigned oldCapacity = capacity(), capacity = oldCapacity;
|
||||
if (slowpath(isConstantEmptyCache())) {
|
||||
// Cache is read-only. Replace it.
|
||||
cache->reallocate(capacity, capacity ?: INIT_CACHE_SIZE);
|
||||
if (!capacity) capacity = INIT_CACHE_SIZE;
|
||||
reallocate(oldCapacity, capacity, /* freeOld */false);
|
||||
}
|
||||
else if (newOccupied <= capacity / 4 * 3) {
|
||||
else if (fastpath(newOccupied + CACHE_END_MARKER <= capacity / 4 * 3)) {
|
||||
// Cache is less than 3/4 full. Use it as-is.
|
||||
}
|
||||
else {
|
||||
// Cache is too full. Expand it.
|
||||
cache->expand();
|
||||
capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;
|
||||
if (capacity > MAX_CACHE_SIZE) {
|
||||
capacity = MAX_CACHE_SIZE;
|
||||
}
|
||||
reallocate(oldCapacity, capacity, true);
|
||||
}
|
||||
|
||||
bucket_t *b = buckets();
|
||||
mask_t m = capacity - 1;
|
||||
mask_t begin = cache_hash(sel, m);
|
||||
mask_t i = begin;
|
||||
|
||||
// Scan for the first unused slot and insert there.
|
||||
// There is guaranteed to be an empty slot because the
|
||||
// There is guaranteed to be an empty slot because the
|
||||
// minimum size is 4 and we resized at 3/4 full.
|
||||
bucket_t *bucket = cache->find(key, receiver);
|
||||
if (bucket->key() == 0) cache->incrementOccupied();
|
||||
bucket->set(key, imp);
|
||||
do {
|
||||
if (fastpath(b[i].sel() == 0)) {
|
||||
incrementOccupied();
|
||||
b[i].set<Atomic, Encoded>(sel, imp, cls);
|
||||
return;
|
||||
}
|
||||
if (b[i].sel() == sel) {
|
||||
// The entry was added to the cache by some other thread
|
||||
// before we grabbed the cacheUpdateLock.
|
||||
return;
|
||||
}
|
||||
} while (fastpath((i = cache_next(i, m)) != begin));
|
||||
|
||||
cache_t::bad_cache(receiver, (SEL)sel, cls);
|
||||
}
|
||||
|
||||
void cache_fill(Class cls, SEL sel, IMP imp, id receiver)
|
||||
{
|
||||
runtimeLock.assertLocked();
|
||||
|
||||
#if !DEBUG_TASK_THREADS
|
||||
mutex_locker_t lock(cacheUpdateLock);
|
||||
cache_fill_nolock(cls, sel, imp, receiver);
|
||||
// Never cache before +initialize is done
|
||||
if (cls->isInitialized()) {
|
||||
cache_t *cache = getCache(cls);
|
||||
#if CONFIG_USE_CACHE_LOCK
|
||||
mutex_locker_t lock(cacheUpdateLock);
|
||||
#endif
|
||||
cache->insert(cls, sel, imp, receiver);
|
||||
}
|
||||
#else
|
||||
_collecting_in_critical();
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -584,7 +712,11 @@ void cache_fill(Class cls, SEL sel, IMP imp, id receiver)
|
||||
// This must not shrink the cache - that breaks the lock-free scheme.
|
||||
void cache_erase_nolock(Class cls)
|
||||
{
|
||||
#if CONFIG_USE_CACHE_LOCK
|
||||
cacheUpdateLock.assertLocked();
|
||||
#else
|
||||
runtimeLock.assertLocked();
|
||||
#endif
|
||||
|
||||
cache_t *cache = getCache(cls);
|
||||
|
||||
@ -595,14 +727,17 @@ void cache_erase_nolock(Class cls)
|
||||
cache->setBucketsAndMask(buckets, capacity - 1); // also clears occupied
|
||||
|
||||
cache_collect_free(oldBuckets, capacity);
|
||||
cache_collect(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cache_delete(Class cls)
|
||||
{
|
||||
#if CONFIG_USE_CACHE_LOCK
|
||||
mutex_locker_t lock(cacheUpdateLock);
|
||||
#else
|
||||
runtimeLock.assertLocked();
|
||||
#endif
|
||||
if (cls->cache.canBeFreed()) {
|
||||
if (PrintCaches) recordDeadCache(cls->cache.capacity());
|
||||
free(cls->cache.buckets());
|
||||
@ -648,7 +783,7 @@ static uintptr_t _get_pc_for_thread(thread_t thread)
|
||||
arm_thread_state64_t state;
|
||||
unsigned int count = ARM_THREAD_STATE64_COUNT;
|
||||
kern_return_t okay = thread_get_state (thread, ARM_THREAD_STATE64, (thread_state_t)&state, &count);
|
||||
return (okay == KERN_SUCCESS) ? state.__pc : PC_SENTINEL;
|
||||
return (okay == KERN_SUCCESS) ? (uintptr_t)arm_thread_state64_get_pc(state) : PC_SENTINEL;
|
||||
}
|
||||
#else
|
||||
{
|
||||
@ -665,21 +800,64 @@ static uintptr_t _get_pc_for_thread(thread_t thread)
|
||||
* 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[];
|
||||
#if HAVE_TASK_RESTARTABLE_RANGES
|
||||
#include <kern/restartable.h>
|
||||
#else
|
||||
typedef struct {
|
||||
uint64_t location;
|
||||
unsigned short length;
|
||||
unsigned short recovery_offs;
|
||||
unsigned int flags;
|
||||
} task_restartable_range_t;
|
||||
#endif
|
||||
|
||||
extern "C" task_restartable_range_t objc_restartableRanges[];
|
||||
|
||||
#if HAVE_TASK_RESTARTABLE_RANGES
|
||||
static bool shouldUseRestartableRanges = true;
|
||||
#endif
|
||||
|
||||
void cache_init()
|
||||
{
|
||||
#if HAVE_TASK_RESTARTABLE_RANGES
|
||||
mach_msg_type_number_t count = 0;
|
||||
kern_return_t kr;
|
||||
|
||||
while (objc_restartableRanges[count].location) {
|
||||
count++;
|
||||
}
|
||||
|
||||
kr = task_restartable_ranges_register(mach_task_self(),
|
||||
objc_restartableRanges, count);
|
||||
if (kr == KERN_SUCCESS) return;
|
||||
_objc_fatal("task_restartable_ranges_register failed (result 0x%x: %s)",
|
||||
kr, mach_error_string(kr));
|
||||
#endif // HAVE_TASK_RESTARTABLE_RANGES
|
||||
}
|
||||
|
||||
static int _collecting_in_critical(void)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
return TRUE;
|
||||
#else
|
||||
#elif HAVE_TASK_RESTARTABLE_RANGES
|
||||
// Only use restartable ranges if we registered them earlier.
|
||||
if (shouldUseRestartableRanges) {
|
||||
kern_return_t kr = task_restartable_ranges_synchronize(mach_task_self());
|
||||
if (kr == KERN_SUCCESS) return FALSE;
|
||||
_objc_fatal("task_restartable_ranges_synchronize failed (result 0x%x: %s)",
|
||||
kr, mach_error_string(kr));
|
||||
}
|
||||
#endif // !HAVE_TASK_RESTARTABLE_RANGES
|
||||
|
||||
// Fallthrough if we didn't use restartable ranges.
|
||||
|
||||
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());
|
||||
mach_port_t mythread = pthread_mach_thread_np(objc_thread_self());
|
||||
|
||||
// Get a list of all the threads in the current task
|
||||
#if !DEBUG_TASK_THREADS
|
||||
@ -715,10 +893,11 @@ static int _collecting_in_critical(void)
|
||||
}
|
||||
|
||||
// Check whether it is in the cache lookup code
|
||||
for (region = 0; objc_entryPoints[region] != 0; region++)
|
||||
for (region = 0; objc_restartableRanges[region].location != 0; region++)
|
||||
{
|
||||
if ((pc >= objc_entryPoints[region]) &&
|
||||
(pc <= objc_exitPoints[region]))
|
||||
uint64_t loc = objc_restartableRanges[region].location;
|
||||
if ((pc > loc) &&
|
||||
(pc - loc < (uint64_t)objc_restartableRanges[region].length))
|
||||
{
|
||||
result = TRUE;
|
||||
goto done;
|
||||
@ -737,7 +916,6 @@ static int _collecting_in_critical(void)
|
||||
|
||||
// Return our finding
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -798,13 +976,18 @@ static void _garbage_make_room(void)
|
||||
**********************************************************************/
|
||||
static void cache_collect_free(bucket_t *data, mask_t capacity)
|
||||
{
|
||||
#if CONFIG_USE_CACHE_LOCK
|
||||
cacheUpdateLock.assertLocked();
|
||||
#else
|
||||
runtimeLock.assertLocked();
|
||||
#endif
|
||||
|
||||
if (PrintCaches) recordDeadCache(capacity);
|
||||
|
||||
_garbage_make_room ();
|
||||
garbage_byte_size += cache_t::bytesForCapacity(capacity);
|
||||
garbage_refs[garbage_count++] = data;
|
||||
cache_collect(false);
|
||||
}
|
||||
|
||||
|
||||
@ -815,7 +998,11 @@ static void cache_collect_free(bucket_t *data, mask_t capacity)
|
||||
**********************************************************************/
|
||||
void cache_collect(bool collectALot)
|
||||
{
|
||||
#if CONFIG_USE_CACHE_LOCK
|
||||
cacheUpdateLock.assertLocked();
|
||||
#else
|
||||
runtimeLock.assertLocked();
|
||||
#endif
|
||||
|
||||
// Done if the garbage is not full
|
||||
if (garbage_byte_size < garbage_threshold && !collectALot) {
|
||||
|
@ -36,6 +36,7 @@
|
||||
static Method _class_getMethod(Class cls, SEL sel);
|
||||
static Method _class_getMethodNoSuper(Class cls, SEL sel);
|
||||
static Method _class_getMethodNoSuper_nolock(Class cls, SEL sel);
|
||||
static Class _class_getNonMetaClass(Class cls, id obj);
|
||||
static void flush_caches(Class cls, bool flush_meta);
|
||||
|
||||
|
||||
@ -163,13 +164,12 @@ static old_method_list *fixupSelectorsInMethodList(Class cls, old_method_list *m
|
||||
// Mach-O bundles are fixed up in place.
|
||||
// This prevents leaks when a bundle is unloaded.
|
||||
}
|
||||
sel_lock();
|
||||
mutex_locker_t lock(selLock);
|
||||
for ( i = 0; i < mlist->method_count; i += 1 ) {
|
||||
method = &mlist->method_list[i];
|
||||
method->method_name =
|
||||
sel_registerNameNoLock((const char *)method->method_name, isBundle); // Always copy selector data from bundles.
|
||||
}
|
||||
sel_unlock();
|
||||
mlist->obsolete = fixed_up_method_list;
|
||||
}
|
||||
return mlist;
|
||||
@ -325,6 +325,112 @@ static void _freedHandler(id obj, SEL sel)
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* _class_resolveClassMethod
|
||||
* Call +resolveClassMethod, looking for a method to be added to class cls.
|
||||
* cls should be a metaclass.
|
||||
* Does not check if the method already exists.
|
||||
**********************************************************************/
|
||||
static void _class_resolveClassMethod(id inst, SEL sel, Class cls)
|
||||
{
|
||||
ASSERT(cls->isMetaClass());
|
||||
SEL resolve_sel = @selector(resolveClassMethod:);
|
||||
|
||||
if (!lookUpImpOrNil(inst, resolve_sel, cls)) {
|
||||
// Resolver not implemented.
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
|
||||
bool resolved = msg(_class_getNonMetaClass(cls, inst), resolve_sel, sel);
|
||||
|
||||
// Cache the result (good or bad) so the resolver doesn't fire next time.
|
||||
// +resolveClassMethod adds to self->ISA() a.k.a. cls
|
||||
IMP imp = lookUpImpOrNil(inst, sel, cls);
|
||||
if (resolved && PrintResolving) {
|
||||
if (imp) {
|
||||
_objc_inform("RESOLVE: method %c[%s %s] "
|
||||
"dynamically resolved to %p",
|
||||
cls->isMetaClass() ? '+' : '-',
|
||||
cls->nameForLogging(), sel_getName(sel), imp);
|
||||
}
|
||||
else {
|
||||
// Method resolver didn't add anything?
|
||||
_objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
|
||||
", but no new implementation of %c[%s %s] was found",
|
||||
cls->nameForLogging(), sel_getName(sel),
|
||||
cls->isMetaClass() ? '+' : '-',
|
||||
cls->nameForLogging(), sel_getName(sel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* _class_resolveInstanceMethod
|
||||
* Call +resolveInstanceMethod, looking for a method to be added to class cls.
|
||||
* cls may be a metaclass or a non-meta class.
|
||||
* Does not check if the method already exists.
|
||||
**********************************************************************/
|
||||
static void _class_resolveInstanceMethod(id inst, SEL sel, Class cls)
|
||||
{
|
||||
SEL resolve_sel = @selector(resolveInstanceMethod:);
|
||||
|
||||
if (! lookUpImpOrNil(cls, resolve_sel, cls->ISA())) {
|
||||
// Resolver not implemented.
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
|
||||
bool resolved = msg(cls, resolve_sel, sel);
|
||||
|
||||
// Cache the result (good or bad) so the resolver doesn't fire next time.
|
||||
// +resolveInstanceMethod adds to self a.k.a. cls
|
||||
IMP imp = lookUpImpOrNil(inst, sel, cls);
|
||||
|
||||
if (resolved && PrintResolving) {
|
||||
if (imp) {
|
||||
_objc_inform("RESOLVE: method %c[%s %s] "
|
||||
"dynamically resolved to %p",
|
||||
cls->isMetaClass() ? '+' : '-',
|
||||
cls->nameForLogging(), sel_getName(sel), imp);
|
||||
}
|
||||
else {
|
||||
// Method resolver didn't add anything?
|
||||
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
|
||||
", but no new implementation of %c[%s %s] was found",
|
||||
cls->nameForLogging(), sel_getName(sel),
|
||||
cls->isMetaClass() ? '+' : '-',
|
||||
cls->nameForLogging(), sel_getName(sel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* _class_resolveMethod
|
||||
* Call +resolveClassMethod or +resolveInstanceMethod.
|
||||
* Returns nothing; any result would be potentially out-of-date already.
|
||||
* Does not check if the method already exists.
|
||||
**********************************************************************/
|
||||
static void
|
||||
_class_resolveMethod(id inst, SEL sel, Class cls)
|
||||
{
|
||||
if (! cls->isMetaClass()) {
|
||||
// try [cls resolveInstanceMethod:sel]
|
||||
_class_resolveInstanceMethod(inst, sel, cls);
|
||||
}
|
||||
else {
|
||||
// try [nonMetaClass resolveClassMethod:sel]
|
||||
// and [cls resolveInstanceMethod:sel]
|
||||
_class_resolveClassMethod(inst, sel, cls);
|
||||
if (!lookUpImpOrNil(inst, sel, cls)) {
|
||||
_class_resolveInstanceMethod(inst, sel, cls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* log_and_fill_cache
|
||||
* Log this method call. If the logger permits it, fill the method cache.
|
||||
@ -347,33 +453,19 @@ log_and_fill_cache(Class cls, Class implementer, Method meth, SEL sel)
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* _class_lookupMethodAndLoadCache.
|
||||
* Method lookup for dispatchers ONLY. OTHER CODE SHOULD USE lookUpImp().
|
||||
* This lookup avoids optimistic cache scan because the dispatcher
|
||||
* already tried that.
|
||||
**********************************************************************/
|
||||
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
|
||||
{
|
||||
return lookUpImpOrForward(cls, sel, obj,
|
||||
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* lookUpImpOrForward.
|
||||
* The standard IMP 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.
|
||||
* Without LOOKUP_INITIALIZE: tries to avoid +initialize (but sometimes fails)
|
||||
* Without LOOKUP_CACHE: skips optimistic unlocked lookup (but uses cache elsewhere)
|
||||
* Most callers should use LOOKUP_INITIALIZE and LOOKUP_CACHE
|
||||
* 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_impcache. IMPs destined for external use
|
||||
* must be converted to _objc_msgForward or _objc_msgForward_stret.
|
||||
* If you don't want forwarding at all, use lookUpImpOrNil() instead.
|
||||
* If you don't want forwarding at all, use LOOKUP_NIL.
|
||||
**********************************************************************/
|
||||
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
|
||||
bool initialize, bool cache, bool resolver)
|
||||
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
|
||||
{
|
||||
Class curClass;
|
||||
IMP methodPC = nil;
|
||||
@ -383,9 +475,9 @@ IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
|
||||
methodListLock.assertUnlocked();
|
||||
|
||||
// Optimistic cache lookup
|
||||
if (cache) {
|
||||
if (behavior & LOOKUP_CACHE) {
|
||||
methodPC = _cache_getImp(cls, sel);
|
||||
if (methodPC) return methodPC;
|
||||
if (methodPC) goto out_nolock;
|
||||
}
|
||||
|
||||
// Check for freed class
|
||||
@ -393,10 +485,10 @@ IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
|
||||
return (IMP) _freedHandler;
|
||||
|
||||
// Check for +initialize
|
||||
if (initialize && !cls->isInitialized()) {
|
||||
_class_initialize (_class_getNonMetaClass(cls, inst));
|
||||
// If sel == initialize, _class_initialize will send +initialize and
|
||||
// then the messenger will send +initialize again after this
|
||||
if ((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized()) {
|
||||
initializeNonMetaClass (_class_getNonMetaClass(cls, inst));
|
||||
// If sel == initialize, initializeNonMetaClass will send +initialize
|
||||
// and then the messenger will send +initialize again after this
|
||||
// procedure finishes. Of course, if this is not being called
|
||||
// from the messenger then it won't happen. 2778172
|
||||
}
|
||||
@ -454,7 +546,7 @@ IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
|
||||
|
||||
// No implementation found. Try method resolver once.
|
||||
|
||||
if (resolver && !triedResolver) {
|
||||
if ((behavior & LOOKUP_RESOLVER) && !triedResolver) {
|
||||
methodListLock.unlock();
|
||||
_class_resolveMethod(cls, sel, inst);
|
||||
triedResolver = YES;
|
||||
@ -470,23 +562,14 @@ IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
|
||||
done:
|
||||
methodListLock.unlock();
|
||||
|
||||
out_nolock:
|
||||
if ((behavior & LOOKUP_NIL) && methodPC == (IMP)_objc_msgForward_impcache) {
|
||||
return nil;
|
||||
}
|
||||
return methodPC;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* lookUpImpOrNil.
|
||||
* Like lookUpImpOrForward, but returns nil instead of _objc_msgForward_impcache
|
||||
**********************************************************************/
|
||||
IMP lookUpImpOrNil(Class cls, SEL sel, id inst,
|
||||
bool initialize, bool cache, bool resolver)
|
||||
{
|
||||
IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
|
||||
if (imp == _objc_msgForward_impcache) return nil;
|
||||
else return imp;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* lookupMethodInClassAndLoadCache.
|
||||
* Like _class_lookupMethodAndLoadCache, but does not search superclasses.
|
||||
@ -683,33 +766,6 @@ void class_setIvarLayout(Class cls, const uint8_t *layout)
|
||||
cls->ivar_layout = ustrdupMaybeNil(layout);
|
||||
}
|
||||
|
||||
// SPI: Instance-specific object layout.
|
||||
|
||||
void _class_setIvarLayoutAccessor(Class cls, const uint8_t* (*accessor) (id object)) {
|
||||
if (!cls) return;
|
||||
|
||||
if (! (cls->info & CLS_EXT)) {
|
||||
_objc_inform("class '%s' needs to be recompiled", cls->name);
|
||||
return;
|
||||
}
|
||||
|
||||
// fixme leak
|
||||
cls->ivar_layout = (const uint8_t *)accessor;
|
||||
cls->setInfo(CLS_HAS_INSTANCE_SPECIFIC_LAYOUT);
|
||||
}
|
||||
|
||||
const uint8_t *_object_getIvarLayout(Class cls, id object) {
|
||||
if (cls && (cls->info & CLS_EXT)) {
|
||||
const uint8_t* layout = cls->ivar_layout;
|
||||
if (cls->info & CLS_HAS_INSTANCE_SPECIFIC_LAYOUT) {
|
||||
const uint8_t* (*accessor) (id object) = (const uint8_t* (*)(id))layout;
|
||||
layout = accessor(object);
|
||||
}
|
||||
return layout;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* class_setWeakIvarLayout
|
||||
@ -762,7 +818,7 @@ const char *class_getName(Class cls)
|
||||
* Return the ordinary class for this class or metaclass.
|
||||
* Used by +initialize.
|
||||
**********************************************************************/
|
||||
Class _class_getNonMetaClass(Class cls, id obj)
|
||||
static Class _class_getNonMetaClass(Class cls, id obj)
|
||||
{
|
||||
// fixme ick
|
||||
if (cls->isMetaClass()) {
|
||||
@ -780,13 +836,21 @@ Class _class_getNonMetaClass(Class cls, id obj)
|
||||
else {
|
||||
cls = objc_getClass(cls->name);
|
||||
}
|
||||
assert(cls);
|
||||
ASSERT(cls);
|
||||
}
|
||||
|
||||
return cls;
|
||||
}
|
||||
|
||||
|
||||
Class class_initialize(Class cls, id inst) {
|
||||
if (!cls->isInitialized()) {
|
||||
initializeNonMetaClass (_class_getNonMetaClass(cls, inst));
|
||||
}
|
||||
return cls;
|
||||
}
|
||||
|
||||
|
||||
Cache _class_getCache(Class cls)
|
||||
{
|
||||
return cls->cache;
|
||||
@ -837,7 +901,7 @@ IMP _category_getLoadMethod(Category cat)
|
||||
* If methods are removed between calls to class_nextMethodList(), it may
|
||||
* omit surviving method lists or simply crash.
|
||||
**********************************************************************/
|
||||
OBJC_EXPORT struct objc_method_list *class_nextMethodList(Class cls, void **it)
|
||||
struct objc_method_list *class_nextMethodList(Class cls, void **it)
|
||||
{
|
||||
OBJC_WARN_DEPRECATED;
|
||||
|
||||
@ -851,7 +915,7 @@ OBJC_EXPORT struct objc_method_list *class_nextMethodList(Class cls, void **it)
|
||||
*
|
||||
* Formerly class_addInstanceMethods ()
|
||||
**********************************************************************/
|
||||
OBJC_EXPORT void class_addMethods(Class cls, struct objc_method_list *meths)
|
||||
void class_addMethods(Class cls, struct objc_method_list *meths)
|
||||
{
|
||||
OBJC_WARN_DEPRECATED;
|
||||
|
||||
@ -871,7 +935,7 @@ OBJC_EXPORT void class_addMethods(Class cls, struct objc_method_list *meths)
|
||||
/***********************************************************************
|
||||
* class_removeMethods.
|
||||
**********************************************************************/
|
||||
OBJC_EXPORT void class_removeMethods(Class cls, struct objc_method_list *meths)
|
||||
void class_removeMethods(Class cls, struct objc_method_list *meths)
|
||||
{
|
||||
OBJC_WARN_DEPRECATED;
|
||||
|
||||
@ -942,8 +1006,7 @@ Method class_getInstanceMethod(Class cls, SEL sel)
|
||||
}
|
||||
|
||||
// Search method lists, try method resolver, etc.
|
||||
lookUpImpOrNil(cls, sel, nil,
|
||||
NO/*initialize*/, NO/*cache*/, YES/*resolver*/);
|
||||
lookUpImpOrForward(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
|
||||
|
||||
meth = _cache_getMethod(cls, sel, _objc_msgForward_impcache);
|
||||
if (meth == (Method)1) {
|
||||
@ -1494,6 +1557,9 @@ unsigned int method_getSizeOfArguments(Method m)
|
||||
return encoding_getSizeOfArguments(method_getTypeEncoding(m));
|
||||
}
|
||||
|
||||
// This function was accidentally un-exported beginning in macOS 10.9.
|
||||
// As of macOS 10.13 nobody had complained.
|
||||
/*
|
||||
unsigned int method_getArgumentInfo(Method m, int arg,
|
||||
const char **type, int *offset)
|
||||
{
|
||||
@ -1502,9 +1568,10 @@ unsigned int method_getArgumentInfo(Method m, int arg,
|
||||
return encoding_getArgumentInfo(method_getTypeEncoding(m),
|
||||
arg, type, offset);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
static spinlock_t impLock;
|
||||
spinlock_t impLock;
|
||||
|
||||
IMP method_setImplementation(Method m_gen, IMP imp)
|
||||
{
|
||||
@ -2289,7 +2356,7 @@ objc_constructInstance(Class cls, void *bytes)
|
||||
obj->initIsa(cls);
|
||||
|
||||
if (cls->hasCxxCtor()) {
|
||||
return object_cxxConstructFromClass(obj, cls);
|
||||
return object_cxxConstructFromClass(obj, cls, OBJECT_CONSTRUCT_NONE);
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
@ -2464,6 +2531,21 @@ id class_createInstanceFromZone(Class cls, size_t extraBytes, void *z)
|
||||
return (*_zoneAlloc)(cls, extraBytes, z);
|
||||
}
|
||||
|
||||
id
|
||||
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
|
||||
{
|
||||
id obj;
|
||||
|
||||
if (fastpath(!zone)) {
|
||||
obj = class_createInstance(cls, 0);
|
||||
} else {
|
||||
obj = class_createInstanceFromZone(cls, 0, zone);
|
||||
}
|
||||
|
||||
if (slowpath(!obj)) obj = _objc_callBadAllocHandler(cls);
|
||||
return obj;
|
||||
}
|
||||
|
||||
unsigned class_createInstances(Class cls, size_t extraBytes,
|
||||
id *results, unsigned num_requested)
|
||||
{
|
||||
|
@ -160,18 +160,6 @@
|
||||
#include "objc-abi.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 *);
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* Information about multi-thread support:
|
||||
*
|
||||
@ -207,7 +195,9 @@ Class object_setClass(id obj, Class cls)
|
||||
// weakly-referenced object has an un-+initialized isa.
|
||||
// Unresolved future classes are not so protected.
|
||||
if (!cls->isFuture() && !cls->isInitialized()) {
|
||||
_class_initialize(_class_getNonMetaClass(cls, nil));
|
||||
// use lookUpImpOrNil to indirectly provoke +initialize
|
||||
// to avoid duplicating the code to actually send +initialize
|
||||
lookUpImpOrNil(nil, @selector(initialize), cls, LOOKUP_INITIALIZE);
|
||||
}
|
||||
|
||||
return obj->changeIsa(cls);
|
||||
@ -494,9 +484,9 @@ void object_cxxDestruct(id obj)
|
||||
* return nil: construction failed because a C++ constructor threw an exception
|
||||
**********************************************************************/
|
||||
id
|
||||
object_cxxConstructFromClass(id obj, Class cls)
|
||||
object_cxxConstructFromClass(id obj, Class cls, int flags)
|
||||
{
|
||||
assert(cls->hasCxxCtor()); // required for performance, not correctness
|
||||
ASSERT(cls->hasCxxCtor()); // required for performance, not correctness
|
||||
|
||||
id (*ctor)(id);
|
||||
Class supercls;
|
||||
@ -505,8 +495,8 @@ object_cxxConstructFromClass(id obj, Class cls)
|
||||
|
||||
// Call superclasses' ctors first, if any.
|
||||
if (supercls && supercls->hasCxxCtor()) {
|
||||
bool ok = object_cxxConstructFromClass(obj, supercls);
|
||||
if (!ok) return nil; // some superclass's ctor failed - give up
|
||||
bool ok = object_cxxConstructFromClass(obj, supercls, flags);
|
||||
if (slowpath(!ok)) return nil; // some superclass's ctor failed - give up
|
||||
}
|
||||
|
||||
// Find this class's ctor, if any.
|
||||
@ -518,11 +508,17 @@ object_cxxConstructFromClass(id obj, Class cls)
|
||||
_objc_inform("CXX: calling C++ constructors for class %s",
|
||||
cls->nameForLogging());
|
||||
}
|
||||
if ((*ctor)(obj)) return obj; // ctor called and succeeded - ok
|
||||
if (fastpath((*ctor)(obj))) return obj; // ctor called and succeeded - ok
|
||||
|
||||
// This class's ctor was called and failed.
|
||||
supercls = cls->superclass; // this reload avoids a spill on the stack
|
||||
|
||||
// This class's ctor was called and failed.
|
||||
// Call superclasses's dtors to clean up.
|
||||
if (supercls) object_cxxDestructFromClass(obj, supercls);
|
||||
if (flags & OBJECT_CONSTRUCT_FREE_ONFAILURE) free(obj);
|
||||
if (flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
|
||||
return _objc_callBadAllocHandler(cls);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@ -576,117 +572,6 @@ void fixupCopiedIvars(id newObject, id oldObject)
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* _class_resolveClassMethod
|
||||
* Call +resolveClassMethod, looking for a method to be added to class cls.
|
||||
* cls should be a metaclass.
|
||||
* Does not check if the method already exists.
|
||||
**********************************************************************/
|
||||
static void _class_resolveClassMethod(Class cls, SEL sel, id inst)
|
||||
{
|
||||
assert(cls->isMetaClass());
|
||||
|
||||
if (! lookUpImpOrNil(cls, SEL_resolveClassMethod, inst,
|
||||
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
|
||||
{
|
||||
// Resolver not implemented.
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
|
||||
bool resolved = msg(_class_getNonMetaClass(cls, inst),
|
||||
SEL_resolveClassMethod, sel);
|
||||
|
||||
// Cache the result (good or bad) so the resolver doesn't fire next time.
|
||||
// +resolveClassMethod adds to self->ISA() a.k.a. cls
|
||||
IMP imp = lookUpImpOrNil(cls, sel, inst,
|
||||
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
|
||||
|
||||
if (resolved && PrintResolving) {
|
||||
if (imp) {
|
||||
_objc_inform("RESOLVE: method %c[%s %s] "
|
||||
"dynamically resolved to %p",
|
||||
cls->isMetaClass() ? '+' : '-',
|
||||
cls->nameForLogging(), sel_getName(sel), imp);
|
||||
}
|
||||
else {
|
||||
// Method resolver didn't add anything?
|
||||
_objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
|
||||
", but no new implementation of %c[%s %s] was found",
|
||||
cls->nameForLogging(), sel_getName(sel),
|
||||
cls->isMetaClass() ? '+' : '-',
|
||||
cls->nameForLogging(), sel_getName(sel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* _class_resolveInstanceMethod
|
||||
* Call +resolveInstanceMethod, looking for a method to be added to class cls.
|
||||
* cls may be a metaclass or a non-meta class.
|
||||
* Does not check if the method already exists.
|
||||
**********************************************************************/
|
||||
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
|
||||
{
|
||||
if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls,
|
||||
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
|
||||
{
|
||||
// Resolver not implemented.
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
|
||||
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);
|
||||
|
||||
// Cache the result (good or bad) so the resolver doesn't fire next time.
|
||||
// +resolveInstanceMethod adds to self a.k.a. cls
|
||||
IMP imp = lookUpImpOrNil(cls, sel, inst,
|
||||
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
|
||||
|
||||
if (resolved && PrintResolving) {
|
||||
if (imp) {
|
||||
_objc_inform("RESOLVE: method %c[%s %s] "
|
||||
"dynamically resolved to %p",
|
||||
cls->isMetaClass() ? '+' : '-',
|
||||
cls->nameForLogging(), sel_getName(sel), imp);
|
||||
}
|
||||
else {
|
||||
// Method resolver didn't add anything?
|
||||
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
|
||||
", but no new implementation of %c[%s %s] was found",
|
||||
cls->nameForLogging(), sel_getName(sel),
|
||||
cls->isMetaClass() ? '+' : '-',
|
||||
cls->nameForLogging(), sel_getName(sel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* _class_resolveMethod
|
||||
* Call +resolveClassMethod or +resolveInstanceMethod.
|
||||
* Returns nothing; any result would be potentially out-of-date already.
|
||||
* Does not check if the method already exists.
|
||||
**********************************************************************/
|
||||
void _class_resolveMethod(Class cls, SEL sel, id inst)
|
||||
{
|
||||
if (! cls->isMetaClass()) {
|
||||
// try [cls resolveInstanceMethod:sel]
|
||||
_class_resolveInstanceMethod(cls, sel, inst);
|
||||
}
|
||||
else {
|
||||
// try [nonMetaClass resolveClassMethod:sel]
|
||||
// and [cls resolveInstanceMethod:sel]
|
||||
_class_resolveClassMethod(cls, sel, inst);
|
||||
if (!lookUpImpOrNil(cls, sel, inst,
|
||||
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
|
||||
{
|
||||
_class_resolveInstanceMethod(cls, sel, inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* class_getClassMethod. Return the class method for the specified
|
||||
@ -745,23 +630,18 @@ BOOL class_respondsToMethod(Class cls, SEL sel)
|
||||
|
||||
BOOL class_respondsToSelector(Class cls, SEL sel)
|
||||
{
|
||||
return class_respondsToSelector_inst(cls, sel, nil);
|
||||
return class_respondsToSelector_inst(nil, sel, cls);
|
||||
}
|
||||
|
||||
|
||||
// inst is an instance of cls or a subclass thereof, or nil if none is known.
|
||||
// Non-nil inst is faster in some cases. See lookUpImpOrForward() for details.
|
||||
bool class_respondsToSelector_inst(Class cls, SEL sel, id inst)
|
||||
NEVER_INLINE BOOL
|
||||
class_respondsToSelector_inst(id inst, SEL sel, Class cls)
|
||||
{
|
||||
IMP imp;
|
||||
|
||||
if (!sel || !cls) return NO;
|
||||
|
||||
// Avoids +initialize because it historically did so.
|
||||
// We're not returning a callable IMP anyway.
|
||||
imp = lookUpImpOrNil(cls, sel, inst,
|
||||
NO/*initialize*/, YES/*cache*/, YES/*resolver*/);
|
||||
return bool(imp);
|
||||
return sel && cls && lookUpImpOrNil(inst, sel, cls, LOOKUP_RESOLVER);
|
||||
}
|
||||
|
||||
|
||||
@ -788,8 +668,7 @@ IMP class_getMethodImplementation(Class cls, SEL sel)
|
||||
|
||||
if (!cls || !sel) return nil;
|
||||
|
||||
imp = lookUpImpOrNil(cls, sel, nil,
|
||||
YES/*initialize*/, YES/*cache*/, YES/*resolver*/);
|
||||
imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
|
||||
|
||||
// Translate forwarding function to C-callable external version
|
||||
if (!imp) {
|
||||
@ -816,6 +695,9 @@ IMP class_getMethodImplementation_stret(Class cls, SEL sel)
|
||||
/***********************************************************************
|
||||
* instrumentObjcMessageSends
|
||||
**********************************************************************/
|
||||
// Define this everywhere even if it isn't used to simplify fork() safety code.
|
||||
spinlock_t objcMsgLogLock;
|
||||
|
||||
#if !SUPPORT_MESSAGE_LOGGING
|
||||
|
||||
void instrumentObjcMessageSends(BOOL flag)
|
||||
@ -826,7 +708,6 @@ void instrumentObjcMessageSends(BOOL flag)
|
||||
|
||||
bool objcMsgLogEnabled = false;
|
||||
static int objcMsgLogFD = -1;
|
||||
static spinlock_t objcMsgLogLock;
|
||||
|
||||
bool logMessageSend(bool isClassMethod,
|
||||
const char *objectsClass,
|
||||
@ -946,26 +827,6 @@ char * method_copyArgumentType(Method m, unsigned int index)
|
||||
return encoding_copyArgumentType(method_getTypeEncoding(m), index);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* _objc_constructOrFree
|
||||
* Call C++ constructors, and free() if they fail.
|
||||
* bytes->isa must already be set.
|
||||
* cls must have cxx constructors.
|
||||
* Returns the object, or nil.
|
||||
**********************************************************************/
|
||||
id
|
||||
_objc_constructOrFree(id bytes, Class cls)
|
||||
{
|
||||
assert(cls->hasCxxCtor()); // for performance, not correctness
|
||||
|
||||
id obj = object_cxxConstructFromClass(bytes, cls);
|
||||
if (!obj) free(bytes);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* _class_createInstancesFromZone
|
||||
* Batch-allocating version of _class_createInstanceFromZone.
|
||||
@ -996,8 +857,10 @@ _class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone,
|
||||
for (unsigned i = 0; i < num_allocated; i++) {
|
||||
id obj = results[i];
|
||||
obj->initIsa(cls); // fixme allow nonpointer
|
||||
if (ctor) obj = _objc_constructOrFree(obj, cls);
|
||||
|
||||
if (ctor) {
|
||||
obj = object_cxxConstructFromClass(obj, cls,
|
||||
OBJECT_CONSTRUCT_FREE_ONFAILURE);
|
||||
}
|
||||
if (obj) {
|
||||
results[i-shift] = obj;
|
||||
} else {
|
||||
@ -1043,11 +906,11 @@ copyPropertyAttributeString(const objc_property_attribute_t *attrs,
|
||||
#if DEBUG
|
||||
// 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, ','));
|
||||
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
|
||||
|
||||
@ -1133,8 +996,8 @@ iteratePropertyAttributes(const char *attrs,
|
||||
const char *nameStart;
|
||||
const char *nameEnd;
|
||||
|
||||
assert(start < end);
|
||||
assert(*start);
|
||||
ASSERT(start < end);
|
||||
ASSERT(*start);
|
||||
if (*start != '\"') {
|
||||
// single-char short name
|
||||
nameStart = start;
|
||||
@ -1154,7 +1017,7 @@ iteratePropertyAttributes(const char *attrs,
|
||||
const char *valueStart;
|
||||
const char *valueEnd;
|
||||
|
||||
assert(start <= end);
|
||||
ASSERT(start <= end);
|
||||
|
||||
valueStart = start;
|
||||
valueEnd = end;
|
||||
@ -1231,8 +1094,8 @@ copyPropertyAttributeList(const char *attrs, unsigned int *outCount)
|
||||
|
||||
attrcount = iteratePropertyAttributes(attrs, copyOneAttribute, &ra, &rs);
|
||||
|
||||
assert((uint8_t *)(ra+1) <= (uint8_t *)result+size);
|
||||
assert((uint8_t *)rs <= (uint8_t *)result+size);
|
||||
ASSERT((uint8_t *)(ra+1) <= (uint8_t *)result+size);
|
||||
ASSERT((uint8_t *)rs <= (uint8_t *)result+size);
|
||||
|
||||
if (attrcount == 0) {
|
||||
free(result);
|
||||
|
@ -26,6 +26,15 @@
|
||||
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
// Define __OBJC2__ for the benefit of our asm files.
|
||||
#ifndef __OBJC2__
|
||||
# if TARGET_OS_OSX && !TARGET_OS_IOSMAC && __i386__
|
||||
// old ABI
|
||||
# else
|
||||
# define __OBJC2__ 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Avoid the !NDEBUG double negative.
|
||||
#if !NDEBUG
|
||||
# define DEBUG 1
|
||||
@ -42,7 +51,7 @@
|
||||
#endif
|
||||
|
||||
// Define SUPPORT_ZONES=1 to enable malloc zone support in NXHashTable.
|
||||
#if !TARGET_OS_OSX
|
||||
#if !(TARGET_OS_OSX || TARGET_OS_IOSMAC)
|
||||
# define SUPPORT_ZONES 0
|
||||
#else
|
||||
# define SUPPORT_ZONES 1
|
||||
@ -56,7 +65,7 @@
|
||||
#endif
|
||||
|
||||
// Define SUPPORT_PREOPT=1 to enable dyld shared cache optimizations
|
||||
#if TARGET_OS_WIN32 || TARGET_OS_SIMULATOR
|
||||
#if TARGET_OS_WIN32
|
||||
# define SUPPORT_PREOPT 0
|
||||
#else
|
||||
# define SUPPORT_PREOPT 1
|
||||
@ -73,7 +82,7 @@
|
||||
// Define SUPPORT_MSB_TAGGED_POINTERS to use the MSB
|
||||
// as the tagged pointer marker instead of the LSB.
|
||||
// Be sure to edit tagged pointer SPI in objc-internal.h as well.
|
||||
#if !SUPPORT_TAGGED_POINTERS || !TARGET_OS_IPHONE
|
||||
#if !SUPPORT_TAGGED_POINTERS || (TARGET_OS_OSX || TARGET_OS_IOSMAC)
|
||||
# define SUPPORT_MSB_TAGGED_POINTERS 0
|
||||
#else
|
||||
# define SUPPORT_MSB_TAGGED_POINTERS 1
|
||||
@ -83,7 +92,7 @@
|
||||
// field as an index into a class table.
|
||||
// Note, keep this in sync with any .s files which also define it.
|
||||
// Be sure to edit objc-abi.h as well.
|
||||
#if __ARM_ARCH_7K__ >= 2
|
||||
#if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__)
|
||||
# define SUPPORT_INDEXED_ISA 1
|
||||
#else
|
||||
# define SUPPORT_INDEXED_ISA 0
|
||||
@ -91,7 +100,8 @@
|
||||
|
||||
// Define SUPPORT_PACKED_ISA=1 on platforms that store the class in the isa
|
||||
// field as a maskable pointer with other data around it.
|
||||
#if (!__LP64__ || TARGET_OS_WIN32 || TARGET_OS_SIMULATOR)
|
||||
#if (!__LP64__ || TARGET_OS_WIN32 || \
|
||||
(TARGET_OS_SIMULATOR && !TARGET_OS_IOSMAC))
|
||||
# define SUPPORT_PACKED_ISA 0
|
||||
#else
|
||||
# define SUPPORT_PACKED_ISA 1
|
||||
@ -125,7 +135,7 @@
|
||||
// Define SUPPORT_ALT_HANDLERS if you're using zero-cost exceptions
|
||||
// but also need to support AppKit's alt-handler scheme
|
||||
// Be sure to edit objc-exception.h as well (objc_add/removeExceptionHandler)
|
||||
#if !SUPPORT_ZEROCOST_EXCEPTIONS || TARGET_OS_IPHONE || TARGET_OS_EMBEDDED
|
||||
#if !SUPPORT_ZEROCOST_EXCEPTIONS || !TARGET_OS_OSX
|
||||
# define SUPPORT_ALT_HANDLERS 0
|
||||
#else
|
||||
# define SUPPORT_ALT_HANDLERS 1
|
||||
@ -146,17 +156,18 @@
|
||||
#endif
|
||||
|
||||
// Define SUPPORT_MESSAGE_LOGGING to enable NSObjCMessageLoggingEnabled
|
||||
#if TARGET_OS_WIN32 || TARGET_OS_EMBEDDED
|
||||
#if !TARGET_OS_OSX
|
||||
# define SUPPORT_MESSAGE_LOGGING 0
|
||||
#else
|
||||
# define SUPPORT_MESSAGE_LOGGING 1
|
||||
#endif
|
||||
|
||||
// Define SUPPORT_QOS_HACK to work around deadlocks due to QoS bugs.
|
||||
#if !__OBJC2__ || TARGET_OS_WIN32 || DARLING
|
||||
# define SUPPORT_QOS_HACK 0
|
||||
// Define HAVE_TASK_RESTARTABLE_RANGES to enable usage of
|
||||
// task_restartable_ranges_synchronize()
|
||||
#if TARGET_OS_SIMULATOR || defined(__i386__) || defined(__arm__) || !TARGET_OS_MAC
|
||||
# define HAVE_TASK_RESTARTABLE_RANGES 0
|
||||
#else
|
||||
# define SUPPORT_QOS_HACK 1
|
||||
# define HAVE_TASK_RESTARTABLE_RANGES 1
|
||||
#endif
|
||||
|
||||
// OBJC_INSTRUMENTED controls whether message dispatching is dynamically
|
||||
@ -167,4 +178,43 @@
|
||||
// because objc-class.h is public and objc-config.h is not.
|
||||
//#define OBJC_INSTRUMENTED
|
||||
|
||||
// In __OBJC2__, the runtimeLock is a mutex always held
|
||||
// hence the cache lock is redundant and can be elided.
|
||||
//
|
||||
// If the runtime lock ever becomes a rwlock again,
|
||||
// the cache lock would need to be used again
|
||||
#if __OBJC2__
|
||||
#define CONFIG_USE_CACHE_LOCK 0
|
||||
#else
|
||||
#define CONFIG_USE_CACHE_LOCK 1
|
||||
#endif
|
||||
|
||||
// Determine how the method cache stores IMPs.
|
||||
#define CACHE_IMP_ENCODING_NONE 1 // Method cache contains raw IMP.
|
||||
#define CACHE_IMP_ENCODING_ISA_XOR 2 // Method cache contains ISA ^ IMP.
|
||||
#define CACHE_IMP_ENCODING_PTRAUTH 3 // Method cache contains ptrauth'd IMP.
|
||||
|
||||
#if __PTRAUTH_INTRINSICS__
|
||||
// Always use ptrauth when it's supported.
|
||||
#define CACHE_IMP_ENCODING CACHE_IMP_ENCODING_PTRAUTH
|
||||
#elif defined(__arm__)
|
||||
// 32-bit ARM uses no encoding.
|
||||
#define CACHE_IMP_ENCODING CACHE_IMP_ENCODING_NONE
|
||||
#else
|
||||
// Everything else uses ISA ^ IMP.
|
||||
#define CACHE_IMP_ENCODING CACHE_IMP_ENCODING_ISA_XOR
|
||||
#endif
|
||||
|
||||
#define CACHE_MASK_STORAGE_OUTLINED 1
|
||||
#define CACHE_MASK_STORAGE_HIGH_16 2
|
||||
#define CACHE_MASK_STORAGE_LOW_4 3
|
||||
|
||||
#if defined(__arm64__) && __LP64__
|
||||
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_HIGH_16
|
||||
#elif defined(__arm64__) && !__LP64__
|
||||
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_LOW_4
|
||||
#else
|
||||
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_OUTLINED
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -22,8 +22,9 @@ OPTION( PrintAltHandlers, OBJC_PRINT_ALT_HANDLERS, "log processi
|
||||
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( PrintCustomCore, OBJC_PRINT_CUSTOM_CORE, "log classes with custom core methods")
|
||||
OPTION( PrintCustomRR, OBJC_PRINT_CUSTOM_RR, "log classes with custom retain/release methods")
|
||||
OPTION( PrintCustomAWZ, OBJC_PRINT_CUSTOM_AWZ, "log classes with custom allocWithZone methods")
|
||||
OPTION( PrintRawIsa, OBJC_PRINT_RAW_ISA, "log classes that require raw pointer isa fields")
|
||||
|
||||
OPTION( DebugUnload, OBJC_DEBUG_UNLOAD, "warn about poorly-behaving bundles when unloaded")
|
||||
@ -39,4 +40,6 @@ OPTION( DebugDontCrash, OBJC_DEBUG_DONT_CRASH, "halt the pro
|
||||
OPTION( DisableVtables, OBJC_DISABLE_VTABLES, "disable vtable dispatch")
|
||||
OPTION( DisablePreopt, OBJC_DISABLE_PREOPTIMIZATION, "disable preoptimization courtesy of dyld shared cache")
|
||||
OPTION( DisableTaggedPointers, OBJC_DISABLE_TAGGED_POINTERS, "disable tagged pointer optimization of NSNumber et al.")
|
||||
OPTION( DisableTaggedPointerObfuscation, OBJC_DISABLE_TAG_OBFUSCATION, "disable obfuscation of tagged pointers")
|
||||
OPTION( DisableNonpointerIsa, OBJC_DISABLE_NONPOINTER_ISA, "disable non-pointer isa fields")
|
||||
OPTION( DisableInitializeForkSafety, OBJC_DISABLE_INITIALIZE_FORK_SAFETY, "disable safety checks for +initialize after fork")
|
||||
|
@ -76,8 +76,6 @@ void _objc_error(id rcv, const char *fmt, va_list args)
|
||||
|
||||
#include <_simple.h>
|
||||
|
||||
OBJC_EXPORT void (*_error)(id, const char *, va_list);
|
||||
|
||||
// Return true if c is a UTF8 continuation byte
|
||||
static bool isUTF8Continuation(char c)
|
||||
{
|
||||
@ -85,7 +83,7 @@ static bool isUTF8Continuation(char c)
|
||||
}
|
||||
|
||||
// Add "message" to any forthcoming crash log.
|
||||
static mutex_t crashlog_lock;
|
||||
mutex_t crashlog_lock;
|
||||
static void _objc_crashlog(const char *message)
|
||||
{
|
||||
char *newmsg;
|
||||
@ -160,7 +158,7 @@ static void _objc_syslog(const char *message)
|
||||
#if !__OBJC2__
|
||||
// used by ExceptionHandling.framework
|
||||
#endif
|
||||
__attribute__((noreturn))
|
||||
__attribute__((noreturn, cold))
|
||||
void _objc_error(id self, const char *fmt, va_list ap)
|
||||
{
|
||||
char *buf;
|
||||
@ -183,7 +181,7 @@ void __objc_error(id rcv, const char *fmt, ...)
|
||||
va_end(vp);
|
||||
}
|
||||
|
||||
static __attribute__((noreturn))
|
||||
static __attribute__((noreturn, cold))
|
||||
void _objc_fatalv(uint64_t reason, uint64_t flags, const char *fmt, va_list ap)
|
||||
{
|
||||
char *buf1;
|
||||
@ -200,8 +198,8 @@ void _objc_fatalv(uint64_t reason, uint64_t flags, const char *fmt, va_list ap)
|
||||
_Exit(1);
|
||||
}
|
||||
else {
|
||||
// abort_with_reason(OS_REASON_OBJC, reason, buf1, flags);
|
||||
abort();
|
||||
_objc_crashlog(buf1);
|
||||
abort_with_reason(OS_REASON_OBJC, reason, buf1, flags);
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,7 +216,7 @@ void _objc_fatal(const char *fmt, ...)
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
_objc_fatalv(OBJC_EXIT_REASON_UNSPECIFIED,
|
||||
/*OS_REASON_FLAG_ONE_TIME_FAILURE*/ 0,
|
||||
OS_REASON_FLAG_ONE_TIME_FAILURE,
|
||||
fmt, ap);
|
||||
}
|
||||
|
||||
|
@ -31,51 +31,57 @@
|
||||
|
||||
// compiler reserves a setjmp buffer + 4 words as localExceptionData
|
||||
|
||||
OBJC_EXPORT void objc_exception_throw(id exception)
|
||||
__OSX_AVAILABLE(10.3)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT void objc_exception_try_enter(void *localExceptionData)
|
||||
__OSX_AVAILABLE(10.3)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT void objc_exception_try_exit(void *localExceptionData)
|
||||
__OSX_AVAILABLE(10.3)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT id objc_exception_extract(void *localExceptionData)
|
||||
__OSX_AVAILABLE(10.3)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT int objc_exception_match(Class exceptionClass, id exception)
|
||||
__OSX_AVAILABLE(10.3)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT void
|
||||
objc_exception_throw(id _Nonnull exception)
|
||||
OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE(10.3);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_exception_try_enter(void * _Nonnull localExceptionData)
|
||||
OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE(10.3);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_exception_try_exit(void * _Nonnull localExceptionData)
|
||||
OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE(10.3);
|
||||
|
||||
OBJC_EXPORT id _Nonnull
|
||||
objc_exception_extract(void * _Nonnull localExceptionData)
|
||||
OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE(10.3);
|
||||
|
||||
OBJC_EXPORT int objc_exception_match(Class _Nonnull exceptionClass,
|
||||
id _Nonnull exception)
|
||||
OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE(10.3);
|
||||
|
||||
|
||||
typedef struct {
|
||||
int version;
|
||||
void (*throw_exc)(id); // version 0
|
||||
void (*try_enter)(void *); // version 0
|
||||
void (*try_exit)(void *); // version 0
|
||||
id (*extract)(void *); // version 0
|
||||
int (*match)(Class, id); // version 0
|
||||
void (* _Nonnull throw_exc)(id _Nonnull); // version 0
|
||||
void (* _Nonnull try_enter)(void * _Nonnull); // version 0
|
||||
void (* _Nonnull try_exit)(void * _Nonnull); // version 0
|
||||
id _Nonnull (* _Nonnull extract)(void * _Nonnull); // version 0
|
||||
int (* _Nonnull match)(Class _Nonnull, id _Nonnull); // version 0
|
||||
} objc_exception_functions_t;
|
||||
|
||||
// get table; version tells how many
|
||||
OBJC_EXPORT void objc_exception_get_functions(objc_exception_functions_t *table)
|
||||
__OSX_AVAILABLE(10.3)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT void
|
||||
objc_exception_get_functions(objc_exception_functions_t * _Nullable table)
|
||||
OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE(10.3);
|
||||
|
||||
// set table
|
||||
OBJC_EXPORT void objc_exception_set_functions(objc_exception_functions_t *table)
|
||||
__OSX_AVAILABLE(10.3)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT void
|
||||
objc_exception_set_functions(objc_exception_functions_t * _Nullable table)
|
||||
OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE(10.3);
|
||||
|
||||
|
||||
// !__OBJC2__
|
||||
#else
|
||||
// __OBJC2__
|
||||
|
||||
typedef id (*objc_exception_preprocessor)(id exception);
|
||||
typedef int (*objc_exception_matcher)(Class catch_type, id exception);
|
||||
typedef void (*objc_uncaught_exception_handler)(id exception);
|
||||
typedef void (*objc_exception_handler)(id unused, void *context);
|
||||
typedef id _Nonnull (*objc_exception_preprocessor)(id _Nonnull exception);
|
||||
typedef int (*objc_exception_matcher)(Class _Nonnull catch_type,
|
||||
id _Nonnull exception);
|
||||
typedef void (*objc_uncaught_exception_handler)(id _Null_unspecified /* _Nonnull */ exception);
|
||||
typedef void (*objc_exception_handler)(id _Nullable unused,
|
||||
void * _Nullable context);
|
||||
|
||||
/**
|
||||
* Throw a runtime exception. This function is inserted by the compiler
|
||||
@ -83,31 +89,47 @@ typedef void (*objc_exception_handler)(id unused, void *context);
|
||||
*
|
||||
* @param exception The exception to be thrown.
|
||||
*/
|
||||
OBJC_EXPORT void objc_exception_throw(id exception)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void objc_exception_rethrow(void)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id objc_begin_catch(void *exc_buf)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void objc_end_catch(void)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void objc_terminate(void)
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
objc_exception_throw(id _Nonnull exception)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT objc_exception_preprocessor objc_setExceptionPreprocessor(objc_exception_preprocessor fn)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT objc_exception_matcher objc_setExceptionMatcher(objc_exception_matcher fn)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
objc_exception_rethrow(void)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT id _Nonnull
|
||||
objc_begin_catch(void * _Nonnull exc_buf)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_end_catch(void)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_terminate(void)
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT objc_exception_preprocessor _Nonnull
|
||||
objc_setExceptionPreprocessor(objc_exception_preprocessor _Nonnull fn)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT objc_exception_matcher _Nonnull
|
||||
objc_setExceptionMatcher(objc_exception_matcher _Nonnull fn)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT objc_uncaught_exception_handler _Nonnull
|
||||
objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler _Nonnull fn)
|
||||
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Not for iOS.
|
||||
OBJC_EXPORT uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context)
|
||||
__OSX_AVAILABLE(10.5)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT void objc_removeExceptionHandler(uintptr_t token)
|
||||
__OSX_AVAILABLE(10.5)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT uintptr_t
|
||||
objc_addExceptionHandler(objc_exception_handler _Nonnull fn,
|
||||
void * _Nullable context)
|
||||
OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE(10.5);
|
||||
|
||||
OBJC_EXPORT void
|
||||
objc_removeExceptionHandler(uintptr_t token)
|
||||
OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE(10.5);
|
||||
|
||||
// __OBJC2__
|
||||
#endif
|
||||
|
@ -133,8 +133,8 @@ typedef struct _threadChain {
|
||||
static ThreadChainLink_t ThreadChainLink;
|
||||
|
||||
static ThreadChainLink_t *getChainLink() {
|
||||
// follow links until thread_self() found (someday) XXX
|
||||
objc_thread_t self = thread_self();
|
||||
// follow links until objc_thread_self() found (someday) XXX
|
||||
objc_thread_t self = objc_thread_self();
|
||||
ThreadChainLink_t *walker = &ThreadChainLink;
|
||||
while (walker->perThreadID != self) {
|
||||
if (walker->next != nil) {
|
||||
@ -238,6 +238,7 @@ void _destroyAltHandlerList(struct alt_handler_list *list)
|
||||
**********************************************************************/
|
||||
|
||||
#include "objc-private.h"
|
||||
#include <objc/objc-abi.h>
|
||||
#include <objc/objc-exception.h>
|
||||
#include <objc/NSObject.h>
|
||||
#include <execinfo.h>
|
||||
@ -310,7 +311,7 @@ CXX_PERSONALITY(int version,
|
||||
|
||||
struct objc_typeinfo {
|
||||
// Position of vtable and name fields must match C++ typeinfo object
|
||||
const void **vtable; // always objc_ehtype_vtable+2
|
||||
const void ** __ptrauth_cxx_vtable_pointer vtable; // objc_ehtype_vtable+2
|
||||
const char *name; // c++ typeinfo string
|
||||
|
||||
Class cls_unremapped;
|
||||
@ -321,54 +322,97 @@ struct objc_exception {
|
||||
struct objc_typeinfo tinfo;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
static void _objc_exception_noop(void) { }
|
||||
static bool _objc_exception_false(void) { return 0; }
|
||||
// static bool _objc_exception_true(void) { return 1; }
|
||||
static void _objc_exception_abort1(void) {
|
||||
_objc_fatal("unexpected call into objc exception typeinfo vtable %d", 1);
|
||||
}
|
||||
static void _objc_exception_abort2(void) {
|
||||
_objc_fatal("unexpected call into objc exception typeinfo vtable %d", 2);
|
||||
}
|
||||
static void _objc_exception_abort3(void) {
|
||||
_objc_fatal("unexpected call into objc exception typeinfo vtable %d", 3);
|
||||
}
|
||||
static void _objc_exception_abort4(void) {
|
||||
_objc_fatal("unexpected call into objc exception typeinfo vtable %d", 4);
|
||||
}
|
||||
__attribute__((used))
|
||||
void _objc_exception_noop(void) { }
|
||||
__attribute__((used))
|
||||
bool _objc_exception_false(void) { return 0; }
|
||||
// bool _objc_exception_true(void) { return 1; }
|
||||
__attribute__((used))
|
||||
void _objc_exception_abort1(void) {
|
||||
_objc_fatal("unexpected call into objc exception typeinfo vtable %d", 1);
|
||||
}
|
||||
__attribute__((used))
|
||||
void _objc_exception_abort2(void) {
|
||||
_objc_fatal("unexpected call into objc exception typeinfo vtable %d", 2);
|
||||
}
|
||||
__attribute__((used))
|
||||
void _objc_exception_abort3(void) {
|
||||
_objc_fatal("unexpected call into objc exception typeinfo vtable %d", 3);
|
||||
}
|
||||
__attribute__((used))
|
||||
void _objc_exception_abort4(void) {
|
||||
_objc_fatal("unexpected call into objc exception typeinfo vtable %d", 4);
|
||||
}
|
||||
__attribute__((used))
|
||||
bool _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo,
|
||||
struct objc_typeinfo *throw_tinfo,
|
||||
void **throw_obj_p,
|
||||
unsigned outer);
|
||||
}
|
||||
|
||||
static bool _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo,
|
||||
struct objc_typeinfo *throw_tinfo,
|
||||
void **throw_obj_p,
|
||||
unsigned outer);
|
||||
// C++ pointers to vtables are signed with no extra data.
|
||||
// C++ vtable entries are signed with a number derived from the function name.
|
||||
// For this fake vtable, we hardcode number as deciphered from the
|
||||
// assembly output during libc++abi's build.
|
||||
#if __has_feature(ptrauth_calls)
|
||||
# define VTABLE_PTR_AUTH "@AUTH(da, 0)"
|
||||
# define VTABLE_ENTRY_AUTH(x) "@AUTH(ia," #x ",addr)"
|
||||
#else
|
||||
# define VTABLE_PTR_AUTH ""
|
||||
# define VTABLE_ENTRY_AUTH(x) ""
|
||||
#endif
|
||||
|
||||
// forward declaration
|
||||
OBJC_EXPORT struct objc_typeinfo OBJC_EHTYPE_id;
|
||||
#if __LP64__
|
||||
# define PTR ".quad "
|
||||
# define TWOPTRSIZE "16"
|
||||
#else
|
||||
# define PTR ".long "
|
||||
# define TWOPTRSIZE "8"
|
||||
#endif
|
||||
|
||||
OBJC_EXPORT
|
||||
const void *objc_ehtype_vtable[] = {
|
||||
nil, // 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, NEW search_above_dst
|
||||
(void*)_objc_exception_false, // NEW search_below_dst
|
||||
(void*)_objc_exception_abort1, // paranoia: blow up if libc++abi
|
||||
(void*)_objc_exception_abort2, // adds something new
|
||||
(void*)_objc_exception_abort3,
|
||||
(void*)_objc_exception_abort4,
|
||||
};
|
||||
// Hand-built vtable for objc exception typeinfo.
|
||||
// "OLD" is GNU libcpp, "NEW" is libc++abi.
|
||||
|
||||
OBJC_EXPORT
|
||||
struct objc_typeinfo OBJC_EHTYPE_id = {
|
||||
objc_ehtype_vtable+2,
|
||||
"id",
|
||||
nil
|
||||
};
|
||||
asm(
|
||||
"\n .cstring"
|
||||
"\n l_.id_str: .asciz \"id\""
|
||||
|
||||
"\n .section __DATA,__const"
|
||||
"\n .globl _OBJC_EHTYPE_id"
|
||||
"\n .globl _objc_ehtype_vtable"
|
||||
"\n .p2align 4"
|
||||
|
||||
"\n _OBJC_EHTYPE_id:"
|
||||
"\n " PTR "(_objc_ehtype_vtable+" TWOPTRSIZE ") " VTABLE_PTR_AUTH
|
||||
"\n " PTR "l_.id_str"
|
||||
"\n " PTR "0"
|
||||
|
||||
"\n _objc_ehtype_vtable:"
|
||||
"\n " PTR "0"
|
||||
// typeinfo's typeinfo - fixme hack
|
||||
"\n " PTR "_OBJC_EHTYPE_id"
|
||||
// destructor and in-place destructor
|
||||
"\n " PTR "__objc_exception_noop" VTABLE_ENTRY_AUTH(52634)
|
||||
"\n " PTR "__objc_exception_noop" VTABLE_ENTRY_AUTH(10344)
|
||||
// OLD __is_pointer_p
|
||||
"\n " PTR "__objc_exception_noop" VTABLE_ENTRY_AUTH(6889)
|
||||
// OLD __is_function_p
|
||||
"\n " PTR "__objc_exception_noop" VTABLE_ENTRY_AUTH(23080)
|
||||
// OLD __do_catch, NEW can_catch
|
||||
"\n " PTR "__objc_exception_do_catch" VTABLE_ENTRY_AUTH(27434)
|
||||
// OLD __do_upcast, NEW search_above_dst
|
||||
"\n " PTR "__objc_exception_false" VTABLE_ENTRY_AUTH(48481)
|
||||
// NEW search_below_dst
|
||||
"\n " PTR "__objc_exception_false" VTABLE_ENTRY_AUTH(41165)
|
||||
// NEW has_unambiguous_public_base (fixme need this?)
|
||||
"\n " PTR "__objc_exception_abort1" VTABLE_ENTRY_AUTH(14357)
|
||||
// paranoia: die if libcxxabi adds anything else
|
||||
"\n " PTR "__objc_exception_abort2"
|
||||
"\n " PTR "__objc_exception_abort3"
|
||||
"\n " PTR "__objc_exception_abort4"
|
||||
);
|
||||
|
||||
|
||||
|
||||
@ -583,10 +627,10 @@ void objc_end_catch(void)
|
||||
|
||||
|
||||
// `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)
|
||||
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;
|
||||
|
||||
@ -1059,7 +1103,6 @@ struct alt_handler_list {
|
||||
struct alt_handler_list *next_DEBUGONLY;
|
||||
};
|
||||
|
||||
static mutex_t DebugLock;
|
||||
static struct alt_handler_list *DebugLists;
|
||||
static uintptr_t DebugCounter;
|
||||
|
||||
@ -1080,7 +1123,7 @@ fetch_handler_list(bool create)
|
||||
|
||||
if (DebugAltHandlers) {
|
||||
// Save this list so the debug code can find it from other threads
|
||||
mutex_locker_t lock(DebugLock);
|
||||
mutex_locker_t lock(AltHandlerDebugLock);
|
||||
list->next_DEBUGONLY = DebugLists;
|
||||
DebugLists = list;
|
||||
}
|
||||
@ -1095,7 +1138,7 @@ void _destroyAltHandlerList(struct alt_handler_list *list)
|
||||
if (list) {
|
||||
if (DebugAltHandlers) {
|
||||
// Detach from the list-of-lists.
|
||||
mutex_locker_t lock(DebugLock);
|
||||
mutex_locker_t lock(AltHandlerDebugLock);
|
||||
struct alt_handler_list **listp = &DebugLists;
|
||||
while (*listp && *listp != list) listp = &(*listp)->next_DEBUGONLY;
|
||||
if (*listp) *listp = (*listp)->next_DEBUGONLY;
|
||||
@ -1160,7 +1203,7 @@ uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context)
|
||||
|
||||
if (DebugAltHandlers) {
|
||||
// Record backtrace in case this handler is misused later.
|
||||
mutex_locker_t lock(DebugLock);
|
||||
mutex_locker_t lock(AltHandlerDebugLock);
|
||||
|
||||
token = DebugCounter++;
|
||||
if (token == 0) token = DebugCounter++;
|
||||
@ -1172,9 +1215,9 @@ uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context)
|
||||
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()),
|
||||
pthread_getname_np(objc_thread_self(), data->debug->thread, THREADNAME_COUNT);
|
||||
strlcpy(data->debug->queue,
|
||||
dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL),
|
||||
THREADNAME_COUNT);
|
||||
data->debug->backtraceSize =
|
||||
backtrace(data->debug->backtrace, BACKTRACE_COUNT);
|
||||
@ -1274,7 +1317,7 @@ void alt_handler_error(uintptr_t token)
|
||||
"or break in objc_alt_handler_error() to debug.");
|
||||
|
||||
if (DebugAltHandlers) {
|
||||
DebugLock.lock();
|
||||
AltHandlerDebugLock.lock();
|
||||
|
||||
// Search other threads' alt handler lists for this handler.
|
||||
struct alt_handler_list *list;
|
||||
@ -1314,7 +1357,7 @@ void alt_handler_error(uintptr_t token)
|
||||
}
|
||||
}
|
||||
done:
|
||||
DebugLock.unlock();
|
||||
AltHandlerDebugLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
@ -1394,3 +1437,6 @@ void exception_init(void)
|
||||
|
||||
// __OBJC2__
|
||||
#endif
|
||||
|
||||
// Define this everywhere even if it isn't used, to simplify fork() safety code
|
||||
mutex_t AltHandlerDebugLock;
|
||||
|
@ -40,8 +40,8 @@ extern struct old_protocol **_getObjcProtocols(const header_info *hi, size_t *np
|
||||
extern Class *_getObjcClassRefs(const header_info *hi, size_t *nclasses);
|
||||
extern const char *_getObjcClassNames(const header_info *hi, size_t *size);
|
||||
|
||||
using Initializer = void(*)(void);
|
||||
extern Initializer* getLibobjcInitializers(const headerType *mhdr, size_t *count);
|
||||
using UnsignedInitializer = void(*)(void);
|
||||
extern UnsignedInitializer* getLibobjcInitializers(const headerType *mhdr, size_t *count);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
|
@ -88,7 +88,7 @@ GETSECT(_getObjcSelectorRefs, SEL, "__OBJC", "__message_refs");
|
||||
GETSECT(_getObjcClassRefs, Class, "__OBJC", "__cls_refs");
|
||||
GETSECT(_getObjcClassNames, const char, "__OBJC", "__class_names");
|
||||
// __OBJC,__class_names section only emitted by CodeWarrior rdar://4951638
|
||||
GETSECT(getLibobjcInitializers, Initializer, "__DATA", "__objc_init_func");
|
||||
GETSECT(getLibobjcInitializers, UnsignedInitializer, "__DATA", "__objc_init_func");
|
||||
|
||||
|
||||
objc_image_info *
|
||||
|
@ -30,22 +30,70 @@
|
||||
|
||||
// classref_t is not fixed up at launch; use remapClass() to convert
|
||||
|
||||
// classlist and catlist and protolist sections are marked const here
|
||||
// because those sections may be in read-only __DATA_CONST segments.
|
||||
|
||||
extern SEL *_getObjc2SelectorRefs(const header_info *hi, size_t *count);
|
||||
extern message_ref_t *_getObjc2MessageRefs(const header_info *hi, size_t *count);
|
||||
extern Class*_getObjc2ClassRefs(const header_info *hi, size_t *count);
|
||||
extern Class*_getObjc2SuperRefs(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);
|
||||
extern classref_t const *_getObjc2ClassList(const header_info *hi, size_t *count);
|
||||
extern classref_t const *_getObjc2NonlazyClassList(const header_info *hi, size_t *count);
|
||||
extern category_t * const *_getObjc2CategoryList(const header_info *hi, size_t *count);
|
||||
extern category_t * const *_getObjc2CategoryList2(const header_info *hi, size_t *count);
|
||||
extern category_t * const *_getObjc2NonlazyCategoryList(const header_info *hi, size_t *count);
|
||||
extern protocol_t * const *_getObjc2ProtocolList(const header_info *hi, size_t *count);
|
||||
extern protocol_t **_getObjc2ProtocolRefs(const header_info *hi, size_t *count);
|
||||
using Initializer = void(*)(void);
|
||||
extern Initializer* getLibobjcInitializers(const header_info *hi, size_t *count);
|
||||
|
||||
extern classref_t *_getObjc2NonlazyClassList(const headerType *mhdr, size_t *count);
|
||||
extern category_t **_getObjc2NonlazyCategoryList(const headerType *mhdr, size_t *count);
|
||||
extern Initializer* getLibobjcInitializers(const headerType *mhdr, size_t *count);
|
||||
// FIXME: rdar://29241917&33734254 clang doesn't sign static initializers.
|
||||
struct UnsignedInitializer {
|
||||
private:
|
||||
uintptr_t storage;
|
||||
public:
|
||||
void operator () () const {
|
||||
using Initializer = void(*)();
|
||||
Initializer init =
|
||||
ptrauth_sign_unauthenticated((Initializer)storage,
|
||||
ptrauth_key_function_pointer, 0);
|
||||
init();
|
||||
}
|
||||
};
|
||||
|
||||
extern UnsignedInitializer *getLibobjcInitializers(const header_info *hi, size_t *count);
|
||||
|
||||
extern classref_t const *_getObjc2NonlazyClassList(const headerType *mhdr, size_t *count);
|
||||
extern category_t * const *_getObjc2NonlazyCategoryList(const headerType *mhdr, size_t *count);
|
||||
extern UnsignedInitializer *getLibobjcInitializers(const headerType *mhdr, size_t *count);
|
||||
|
||||
static inline void
|
||||
foreach_data_segment(const headerType *mhdr,
|
||||
std::function<void(const segmentType *, intptr_t slide)> code)
|
||||
{
|
||||
intptr_t slide = 0;
|
||||
|
||||
// compute VM slide
|
||||
const segmentType *seg = (const segmentType *) (mhdr + 1);
|
||||
for (unsigned long i = 0; i < mhdr->ncmds; i++) {
|
||||
if (seg->cmd == SEGMENT_CMD &&
|
||||
segnameEquals(seg->segname, "__TEXT"))
|
||||
{
|
||||
slide = (char *)mhdr - (char *)seg->vmaddr;
|
||||
break;
|
||||
}
|
||||
seg = (const segmentType *)((char *)seg + seg->cmdsize);
|
||||
}
|
||||
|
||||
// enumerate __DATA* segments
|
||||
seg = (const segmentType *) (mhdr + 1);
|
||||
for (unsigned long i = 0; i < mhdr->ncmds; i++) {
|
||||
if (seg->cmd == SEGMENT_CMD &&
|
||||
segnameStartsWith(seg->segname, "__DATA"))
|
||||
{
|
||||
code(seg, slide);
|
||||
}
|
||||
seg = (const segmentType *)((char *)seg + seg->cmdsize);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -59,13 +59,14 @@ GETSECT(_getObjc2SelectorRefs, SEL, "__objc_selrefs");
|
||||
GETSECT(_getObjc2MessageRefs, message_ref_t, "__objc_msgrefs");
|
||||
GETSECT(_getObjc2ClassRefs, Class, "__objc_classrefs");
|
||||
GETSECT(_getObjc2SuperRefs, Class, "__objc_superrefs");
|
||||
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(_getObjc2ClassList, classref_t const, "__objc_classlist");
|
||||
GETSECT(_getObjc2NonlazyClassList, classref_t const, "__objc_nlclslist");
|
||||
GETSECT(_getObjc2CategoryList, category_t * const, "__objc_catlist");
|
||||
GETSECT(_getObjc2CategoryList2, category_t * const, "__objc_catlist2");
|
||||
GETSECT(_getObjc2NonlazyCategoryList, category_t * const, "__objc_nlcatlist");
|
||||
GETSECT(_getObjc2ProtocolList, protocol_t * const, "__objc_protolist");
|
||||
GETSECT(_getObjc2ProtocolRefs, protocol_t *, "__objc_protorefs");
|
||||
GETSECT(getLibobjcInitializers, Initializer, "__objc_init_func");
|
||||
GETSECT(getLibobjcInitializers, UnsignedInitializer, "__objc_init_func");
|
||||
|
||||
|
||||
objc_image_info *
|
||||
@ -75,31 +76,15 @@ _getObjcImageInfo(const headerType *mhdr, size_t *outBytes)
|
||||
outBytes, nil);
|
||||
}
|
||||
|
||||
|
||||
static const segmentType *
|
||||
getsegbynamefromheader(const headerType *mhdr, const char *segname)
|
||||
{
|
||||
const segmentType *seg = (const segmentType *) (mhdr + 1);
|
||||
for (unsigned long i = 0; i < mhdr->ncmds; i++){
|
||||
if (seg->cmd == SEGMENT_CMD && segnameEquals(seg->segname, segname)) {
|
||||
return seg;
|
||||
}
|
||||
seg = (const segmentType *)((char *)seg + seg->cmdsize);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Look for an __objc* section other than __objc_imageinfo
|
||||
static bool segmentHasObjcContents(const segmentType *seg)
|
||||
{
|
||||
if (seg) {
|
||||
for (uint32_t i = 0; i < seg->nsects; i++) {
|
||||
const sectionType *sect = ((const sectionType *)(seg+1))+i;
|
||||
if (sectnameStartsWith(sect->sectname, "__objc_") &&
|
||||
!sectnameEquals(sect->sectname, "__objc_imageinfo"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
for (uint32_t i = 0; i < seg->nsects; i++) {
|
||||
const sectionType *sect = ((const sectionType *)(seg+1))+i;
|
||||
if (sectnameStartsWith(sect->sectname, "__objc_") &&
|
||||
!sectnameEquals(sect->sectname, "__objc_imageinfo"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,16 +95,15 @@ static bool segmentHasObjcContents(const segmentType *seg)
|
||||
bool
|
||||
_hasObjcContents(const header_info *hi)
|
||||
{
|
||||
const segmentType *data =
|
||||
getsegbynamefromheader(hi->mhdr(), "__DATA");
|
||||
const segmentType *data_const =
|
||||
getsegbynamefromheader(hi->mhdr(), "__DATA_CONST");
|
||||
const segmentType *data_dirty =
|
||||
getsegbynamefromheader(hi->mhdr(), "__DATA_DIRTY");
|
||||
bool foundObjC = false;
|
||||
|
||||
foreach_data_segment(hi->mhdr(), [&](const segmentType *seg, intptr_t slide)
|
||||
{
|
||||
if (segmentHasObjcContents(seg)) foundObjC = true;
|
||||
});
|
||||
|
||||
return foundObjC;
|
||||
|
||||
return segmentHasObjcContents(data)
|
||||
|| segmentHasObjcContents(data_const)
|
||||
|| segmentHasObjcContents(data_dirty);
|
||||
}
|
||||
|
||||
|
||||
|
@ -34,7 +34,9 @@
|
||||
|
||||
#ifdef __APPLE_API_PRIVATE
|
||||
|
||||
#define _OBJC_PRIVATE_H_
|
||||
#ifndef _OBJC_PRIVATE_H_
|
||||
# define _OBJC_PRIVATE_H_
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
#include <objc/hashtable.h>
|
||||
#include <objc/maptable.h>
|
||||
@ -47,17 +49,30 @@ __BEGIN_DECLS
|
||||
**********************************************************************/
|
||||
|
||||
// Return cls if it's a valid class, or crash.
|
||||
OBJC_EXPORT Class gdb_class_getClass(Class cls)
|
||||
OBJC_EXPORT Class _Nonnull
|
||||
gdb_class_getClass(Class _Nonnull cls)
|
||||
#if __OBJC2__
|
||||
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
|
||||
#else
|
||||
OBJC_AVAILABLE(10.7, 3.1, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.7, 3.1, 9.0, 1.0, 2.0);
|
||||
#endif
|
||||
|
||||
// Same as gdb_class_getClass(object_getClass(cls)).
|
||||
OBJC_EXPORT Class gdb_object_getClass(id obj)
|
||||
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
|
||||
OBJC_EXPORT Class _Nonnull gdb_object_getClass(id _Nullable obj)
|
||||
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
|
||||
|
||||
/***********************************************************************
|
||||
* Class inspection
|
||||
**********************************************************************/
|
||||
|
||||
#if __OBJC2__
|
||||
|
||||
// Return the raw, mangled name of cls.
|
||||
OBJC_EXPORT const char * _Nonnull
|
||||
objc_debug_class_getNameRaw(Class _Nullable cls)
|
||||
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
|
||||
#endif
|
||||
|
||||
/***********************************************************************
|
||||
* Class lists for heap.
|
||||
@ -66,15 +81,25 @@ OBJC_EXPORT Class gdb_object_getClass(id obj)
|
||||
#if __OBJC2__
|
||||
|
||||
// Maps class name to Class, for in-use classes only. NXStrValueMapPrototype.
|
||||
OBJC_EXPORT NXMapTable *gdb_objc_realized_classes
|
||||
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
|
||||
OBJC_EXPORT NXMapTable * _Nullable gdb_objc_realized_classes
|
||||
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
|
||||
|
||||
// A generation count of realized classes. Increments when new classes
|
||||
// are realized. This is NOT an exact count of the number of classes.
|
||||
// It's not guaranteed by how much it increases when classes are
|
||||
// realized, only that it increases by something. When classes are
|
||||
// removed (unloading bundles or destroying dynamically allocated
|
||||
// classes) the number will also increase to signal that there has
|
||||
// been a change.
|
||||
OBJC_EXPORT uintptr_t objc_debug_realized_class_generation_count;
|
||||
|
||||
#else
|
||||
|
||||
// Hashes Classes, for all known classes. Custom prototype.
|
||||
OBJC_EXPORT NXHashTable *_objc_debug_class_hash
|
||||
OBJC_EXPORT NXHashTable * _Nullable _objc_debug_class_hash
|
||||
__OSX_AVAILABLE(10.2)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE
|
||||
__WATCHOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE;
|
||||
|
||||
#endif
|
||||
|
||||
@ -88,14 +113,14 @@ OBJC_EXPORT NXHashTable *_objc_debug_class_hash
|
||||
// Extract isa pointer from an isa field.
|
||||
// (Class)(isa & mask) == class pointer
|
||||
OBJC_EXPORT const uintptr_t objc_debug_isa_class_mask
|
||||
OBJC_AVAILABLE(10.10, 7.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.10, 7.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Extract magic cookie from an isa field.
|
||||
// (isa & magic_mask) == magic_value
|
||||
OBJC_EXPORT const uintptr_t objc_debug_isa_magic_mask
|
||||
OBJC_AVAILABLE(10.10, 7.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.10, 7.0, 9.0, 1.0, 2.0);
|
||||
OBJC_EXPORT const uintptr_t objc_debug_isa_magic_value
|
||||
OBJC_AVAILABLE(10.10, 7.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.10, 7.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Use indexed ISAs for targets which store index of the class in the ISA.
|
||||
// This index can be used to index the array of classes.
|
||||
@ -109,7 +134,7 @@ OBJC_EXPORT const uintptr_t objc_debug_indexed_isa_index_shift;
|
||||
// And then we can use that index to get the class from this array. Note
|
||||
// the size is provided so that clients can ensure the index they get is in
|
||||
// bounds and not read off the end of the array.
|
||||
OBJC_EXPORT Class objc_indexed_classes[];
|
||||
OBJC_EXPORT Class _Nullable objc_indexed_classes[];
|
||||
|
||||
// When we don't have enough bits to store a class*, we can instead store an
|
||||
// index in to this array. Classes are added here when they are realized.
|
||||
@ -121,6 +146,63 @@ OBJC_EXPORT uintptr_t objc_indexed_classes_count;
|
||||
#endif
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* Class structure decoding
|
||||
**********************************************************************/
|
||||
#if __OBJC2__
|
||||
|
||||
// Mask for the pointer from class struct to class rw data.
|
||||
// Other bits may be used for flags.
|
||||
// Use 0x00007ffffffffff8UL or 0xfffffffcUL when this variable is unavailable.
|
||||
OBJC_EXPORT const uintptr_t objc_debug_class_rw_data_mask
|
||||
OBJC_AVAILABLE(10.13, 11.0, 11.0, 4.0, 2.0);
|
||||
|
||||
// The ABI version for the internal runtime representations
|
||||
// lldb, CoreSymbolication and debugging tools need to know
|
||||
OBJC_EXTERN const uint32_t objc_class_abi_version
|
||||
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
|
||||
// the maximum ABI version in existence for now
|
||||
#define OBJC_CLASS_ABI_VERSION_MAX 1
|
||||
|
||||
// Used when objc_class_abi_version is absent or 0
|
||||
struct class_rw_v0_t {
|
||||
uint32_t flags;
|
||||
uint32_t version;
|
||||
uintptr_t ro; // class_ro_t
|
||||
uintptr_t methods; // method_array_t
|
||||
uintptr_t properties; // property_array_t
|
||||
uintptr_t protocols; // protocol_array_t
|
||||
Class _Nullable firstSubclass;
|
||||
Class _Nullable nextSiblingClass;
|
||||
char *_Nullable demangledName;
|
||||
|
||||
// uint32_t index; // only when indexed-isa is used
|
||||
};
|
||||
|
||||
// Used when objc_class_abi_version is 1
|
||||
struct class_rw_v1_t {
|
||||
uint32_t flags;
|
||||
uint16_t witness;
|
||||
uint16_t index; // only when indexed-isa is used
|
||||
uintptr_t ro_or_rw_ext; // tagged union based on the low bit:
|
||||
// 0: class_ro_t 1: class_rw_ext_t
|
||||
Class _Nullable firstSubclass;
|
||||
Class _Nullable nextSiblingClass;
|
||||
};
|
||||
|
||||
struct class_rw_ext_v1_t {
|
||||
uintptr_t ro; // class_ro_t
|
||||
uintptr_t methods; // method_array_t
|
||||
uintptr_t properties; // property_array_t
|
||||
uintptr_t protocols; // protocol_array_t
|
||||
char *_Nullable demangledName;
|
||||
uint32_t version;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* Tagged pointer decoding
|
||||
**********************************************************************/
|
||||
@ -130,24 +212,30 @@ OBJC_EXPORT uintptr_t objc_indexed_classes_count;
|
||||
|
||||
// if (obj & mask) obj is a tagged pointer object
|
||||
OBJC_EXPORT uintptr_t objc_debug_taggedpointer_mask
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// tagged pointers are obfuscated by XORing with a random value
|
||||
// decoded_obj = (obj ^ obfuscator)
|
||||
OBJC_EXPORT uintptr_t objc_debug_taggedpointer_obfuscator
|
||||
OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0);
|
||||
|
||||
|
||||
// tag_slot = (obj >> slot_shift) & slot_mask
|
||||
OBJC_EXPORT unsigned int objc_debug_taggedpointer_slot_shift
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
|
||||
OBJC_EXPORT uintptr_t objc_debug_taggedpointer_slot_mask
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// class = classes[tag_slot]
|
||||
OBJC_EXPORT Class objc_debug_taggedpointer_classes[]
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
|
||||
OBJC_EXPORT Class _Nullable objc_debug_taggedpointer_classes[]
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// payload = (obj << payload_lshift) >> payload_rshift
|
||||
// payload = (decoded_obj << payload_lshift) >> payload_rshift
|
||||
// Payload signedness is determined by the signedness of the right-shift.
|
||||
OBJC_EXPORT unsigned int objc_debug_taggedpointer_payload_lshift
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
|
||||
OBJC_EXPORT unsigned int objc_debug_taggedpointer_payload_rshift
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
|
||||
|
||||
|
||||
// Extended tagged pointers (255 classes, 52-bit payload).
|
||||
@ -156,68 +244,56 @@ OBJC_EXPORT unsigned int objc_debug_taggedpointer_payload_rshift
|
||||
// tagged pointer scheme alone, it will appear to have an isa
|
||||
// that is either nil or class __NSUnrecognizedTaggedPointer.
|
||||
|
||||
// if (ext_mask != 0 && (obj & ext_mask) == ext_mask)
|
||||
// if (ext_mask != 0 && (decoded_obj & ext_mask) == ext_mask)
|
||||
// obj is a ext tagged pointer object
|
||||
OBJC_EXPORT uintptr_t objc_debug_taggedpointer_ext_mask
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
// ext_tag_slot = (obj >> ext_slot_shift) & ext_slot_mask
|
||||
OBJC_EXPORT unsigned int objc_debug_taggedpointer_ext_slot_shift
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
OBJC_EXPORT uintptr_t objc_debug_taggedpointer_ext_slot_mask
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
// class = ext_classes[ext_tag_slot]
|
||||
OBJC_EXPORT Class objc_debug_taggedpointer_ext_classes[]
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_EXPORT Class _Nullable objc_debug_taggedpointer_ext_classes[]
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
// payload = (obj << ext_payload_lshift) >> ext_payload_rshift
|
||||
// payload = (decoded_obj << ext_payload_lshift) >> ext_payload_rshift
|
||||
// Payload signedness is determined by the signedness of the right-shift.
|
||||
OBJC_EXPORT unsigned int objc_debug_taggedpointer_ext_payload_lshift
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
OBJC_EXPORT unsigned int objc_debug_taggedpointer_ext_payload_rshift
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* Breakpoints in objc_msgSend for debugger stepping.
|
||||
* The array is a {0,0} terminated list of addresses.
|
||||
* Each address is one of the following:
|
||||
* OBJC_MESSENGER_START: Address is the start of a messenger function.
|
||||
* OBJC_MESSENGER_END_FAST: Address is a jump insn that calls an IMP.
|
||||
* OBJC_MESSENGER_END_SLOW: Address is some insn in the slow lookup path.
|
||||
* OBJC_MESSENGER_END_NIL: Address is a return insn for messages to nil.
|
||||
*
|
||||
* Every path from OBJC_MESSENGER_START should reach some OBJC_MESSENGER_END.
|
||||
* At all ENDs, the stack and parameter register state is the same as START.
|
||||
*
|
||||
* In some cases, the END_FAST case jumps to something other than the
|
||||
* method's implementation. In those cases the jump's destination will
|
||||
* be another function that is marked OBJC_MESSENGER_START.
|
||||
* Swift marker bits
|
||||
**********************************************************************/
|
||||
#if __OBJC2__
|
||||
|
||||
#define OBJC_MESSENGER_START 1
|
||||
#define OBJC_MESSENGER_END_FAST 2
|
||||
#define OBJC_MESSENGER_END_SLOW 3
|
||||
#define OBJC_MESSENGER_END_NIL 4
|
||||
|
||||
struct objc_messenger_breakpoint {
|
||||
uintptr_t address;
|
||||
uintptr_t kind;
|
||||
};
|
||||
|
||||
OBJC_EXPORT struct objc_messenger_breakpoint
|
||||
gdb_objc_messenger_breakpoints[]
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
|
||||
|
||||
OBJC_EXPORT const uintptr_t objc_debug_swift_stable_abi_bit
|
||||
OBJC_AVAILABLE(10.14.4, 12.2, 12.2, 5.2, 3.2);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* AutoreleasePoolPage
|
||||
**********************************************************************/
|
||||
OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_magic_offset OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_next_offset OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_thread_offset OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_parent_offset OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_child_offset OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_depth_offset OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_hiwat_offset OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
// APPLE_API_PRIVATE
|
||||
#endif
|
||||
|
||||
// _OBJC_GDB_H
|
||||
#endif
|
||||
|
@ -30,7 +30,7 @@ __BEGIN_DECLS
|
||||
|
||||
struct _objc_initializing_classes;
|
||||
|
||||
extern void _class_initialize(Class cls);
|
||||
extern void initializeNonMetaClass(Class cls);
|
||||
|
||||
extern void _destroyInitializingClassList(struct _objc_initializing_classes *list);
|
||||
|
||||
|
@ -95,11 +95,19 @@
|
||||
#include "objc-private.h"
|
||||
#include "message.h"
|
||||
#include "objc-initialize.h"
|
||||
#include "DenseMapExtras.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_t classInitLock;
|
||||
|
||||
|
||||
struct _objc_willInitializeClassCallback {
|
||||
_objc_func_willInitializeClass f;
|
||||
void *context;
|
||||
};
|
||||
static GlobalSmallVector<_objc_willInitializeClassCallback, 1> willInitializeFuncs;
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
@ -262,9 +270,12 @@ static void _setThisThreadIsNotInitializingClass(Class cls)
|
||||
typedef struct PendingInitialize {
|
||||
Class subclass;
|
||||
struct PendingInitialize *next;
|
||||
|
||||
PendingInitialize(Class cls) : subclass(cls), next(nullptr) { }
|
||||
} PendingInitialize;
|
||||
|
||||
static NXMapTable *pendingInitializeMap;
|
||||
typedef objc::DenseMap<Class, PendingInitialize *> PendingInitializeMap;
|
||||
static PendingInitializeMap *pendingInitializeMap;
|
||||
|
||||
/***********************************************************************
|
||||
* _finishInitializing
|
||||
@ -277,11 +288,11 @@ static void _finishInitializing(Class cls, Class supercls)
|
||||
PendingInitialize *pending;
|
||||
|
||||
classInitLock.assertLocked();
|
||||
assert(!supercls || supercls->isInitialized());
|
||||
ASSERT(!supercls || supercls->isInitialized());
|
||||
|
||||
if (PrintInitializing) {
|
||||
_objc_inform("INITIALIZE: %s is fully +initialized",
|
||||
cls->nameForLogging());
|
||||
_objc_inform("INITIALIZE: thread %p: %s is fully +initialized",
|
||||
objc_thread_self(), cls->nameForLogging());
|
||||
}
|
||||
|
||||
// mark this class as fully +initialized
|
||||
@ -291,21 +302,23 @@ static void _finishInitializing(Class cls, Class supercls)
|
||||
|
||||
// mark any subclasses that were merely waiting for this class
|
||||
if (!pendingInitializeMap) return;
|
||||
pending = (PendingInitialize *)NXMapGet(pendingInitializeMap, cls);
|
||||
if (!pending) return;
|
||||
|
||||
NXMapRemove(pendingInitializeMap, cls);
|
||||
|
||||
auto it = pendingInitializeMap->find(cls);
|
||||
if (it == pendingInitializeMap->end()) return;
|
||||
|
||||
pending = it->second;
|
||||
pendingInitializeMap->erase(it);
|
||||
|
||||
// Destroy the pending table if it's now empty, to save memory.
|
||||
if (NXCountMapTable(pendingInitializeMap) == 0) {
|
||||
NXFreeMapTable(pendingInitializeMap);
|
||||
if (pendingInitializeMap->size() == 0) {
|
||||
delete pendingInitializeMap;
|
||||
pendingInitializeMap = nil;
|
||||
}
|
||||
|
||||
while (pending) {
|
||||
PendingInitialize *next = pending->next;
|
||||
if (pending->subclass) _finishInitializing(pending->subclass, cls);
|
||||
free(pending);
|
||||
delete pending;
|
||||
pending = next;
|
||||
}
|
||||
}
|
||||
@ -318,26 +331,27 @@ static void _finishInitializing(Class cls, Class supercls)
|
||||
**********************************************************************/
|
||||
static void _finishInitializingAfter(Class cls, Class supercls)
|
||||
{
|
||||
PendingInitialize *pending;
|
||||
|
||||
classInitLock.assertLocked();
|
||||
|
||||
if (PrintInitializing) {
|
||||
_objc_inform("INITIALIZE: %s waiting for superclass +[%s initialize]",
|
||||
cls->nameForLogging(), supercls->nameForLogging());
|
||||
_objc_inform("INITIALIZE: thread %p: class %s will be marked as fully "
|
||||
"+initialized after superclass +[%s initialize] completes",
|
||||
objc_thread_self(), cls->nameForLogging(),
|
||||
supercls->nameForLogging());
|
||||
}
|
||||
|
||||
if (!pendingInitializeMap) {
|
||||
pendingInitializeMap =
|
||||
NXCreateMapTable(NXPtrValueMapPrototype, 10);
|
||||
pendingInitializeMap = new PendingInitializeMap{10};
|
||||
// fixme pre-size this table for CF/NSObject +initialize
|
||||
}
|
||||
|
||||
pending = (PendingInitialize *)malloc(sizeof(*pending));
|
||||
pending->subclass = cls;
|
||||
pending->next = (PendingInitialize *)
|
||||
NXMapGet(pendingInitializeMap, supercls);
|
||||
NXMapInsert(pendingInitializeMap, supercls, pending);
|
||||
PendingInitialize *pending = new PendingInitialize{cls};
|
||||
auto result = pendingInitializeMap->try_emplace(supercls, pending);
|
||||
if (!result.second) {
|
||||
pending->next = result.first->second;
|
||||
result.first->second = pending;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -352,6 +366,11 @@ void callInitialize(Class cls)
|
||||
|
||||
void waitForInitializeToComplete(Class cls)
|
||||
{
|
||||
if (PrintInitializing) {
|
||||
_objc_inform("INITIALIZE: thread %p: blocking until +[%s initialize] "
|
||||
"completes", objc_thread_self(), cls->nameForLogging());
|
||||
}
|
||||
|
||||
monitor_locker_t lock(classInitLock);
|
||||
while (!cls->isInitialized()) {
|
||||
classInitLock.wait();
|
||||
@ -362,18 +381,119 @@ void waitForInitializeToComplete(Class cls)
|
||||
|
||||
void callInitialize(Class cls)
|
||||
{
|
||||
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
|
||||
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
|
||||
asm("");
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* classHasTrivialInitialize
|
||||
* Returns true if the class has no +initialize implementation or
|
||||
* has a +initialize implementation that looks empty.
|
||||
* Any root class +initialize implemetation is assumed to be trivial.
|
||||
**********************************************************************/
|
||||
static bool classHasTrivialInitialize(Class cls)
|
||||
{
|
||||
if (cls->isRootClass() || cls->isRootMetaclass()) return true;
|
||||
|
||||
Class rootCls = cls->ISA()->ISA()->superclass;
|
||||
|
||||
IMP rootImp = lookUpImpOrNil(rootCls, @selector(initialize), rootCls->ISA());
|
||||
IMP imp = lookUpImpOrNil(cls, @selector(initialize), cls->ISA());
|
||||
return (imp == nil || imp == (IMP)&objc_noop_imp || imp == rootImp);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* lockAndFinishInitializing
|
||||
* Mark a class as finished initializing and notify waiters, or queue for later.
|
||||
* 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.)
|
||||
**********************************************************************/
|
||||
static void lockAndFinishInitializing(Class cls, Class supercls)
|
||||
{
|
||||
monitor_locker_t lock(classInitLock);
|
||||
if (!supercls || supercls->isInitialized()) {
|
||||
_finishInitializing(cls, supercls);
|
||||
} else {
|
||||
_finishInitializingAfter(cls, supercls);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* performForkChildInitialize
|
||||
* +initialize after fork() is problematic. It's possible for the
|
||||
* fork child process to call some +initialize that would deadlock waiting
|
||||
* for another +initialize in the parent process.
|
||||
* We wouldn't know how much progress it made therein, so we can't
|
||||
* act as if +initialize completed nor can we restart +initialize
|
||||
* from scratch.
|
||||
*
|
||||
* Instead we proceed introspectively. If the class has some
|
||||
* +initialize implementation, we halt. If the class has no
|
||||
* +initialize implementation of its own, we continue. Root
|
||||
* class +initialize is assumed to be empty if it exists.
|
||||
*
|
||||
* We apply this rule even if the child's +initialize does not appear
|
||||
* to be blocked by anything. This prevents races wherein the +initialize
|
||||
* deadlock only rarely hits. Instead we disallow it even when we "won"
|
||||
* the race.
|
||||
*
|
||||
* Exception: processes that are single-threaded when fork() is called
|
||||
* have no restrictions on +initialize in the child. Examples: sshd and httpd.
|
||||
*
|
||||
* Classes that wish to implement +initialize and be callable after
|
||||
* fork() must use an atfork() handler to provoke +initialize in fork prepare.
|
||||
**********************************************************************/
|
||||
|
||||
// Called before halting when some +initialize
|
||||
// method can't be called after fork().
|
||||
BREAKPOINT_FUNCTION(
|
||||
void objc_initializeAfterForkError(Class cls)
|
||||
);
|
||||
|
||||
void performForkChildInitialize(Class cls, Class supercls)
|
||||
{
|
||||
if (classHasTrivialInitialize(cls)) {
|
||||
if (PrintInitializing) {
|
||||
_objc_inform("INITIALIZE: thread %p: skipping trivial +[%s "
|
||||
"initialize] in fork() child process",
|
||||
objc_thread_self(), cls->nameForLogging());
|
||||
}
|
||||
lockAndFinishInitializing(cls, supercls);
|
||||
}
|
||||
else {
|
||||
if (PrintInitializing) {
|
||||
_objc_inform("INITIALIZE: thread %p: refusing to call +[%s "
|
||||
"initialize] in fork() child process because "
|
||||
"it may have been in progress when fork() was called",
|
||||
objc_thread_self(), cls->nameForLogging());
|
||||
}
|
||||
_objc_inform_now_and_on_crash
|
||||
("+[%s initialize] may have been in progress in another thread "
|
||||
"when fork() was called.",
|
||||
cls->nameForLogging());
|
||||
objc_initializeAfterForkError(cls);
|
||||
_objc_fatal
|
||||
("+[%s initialize] may have been in progress in another thread "
|
||||
"when fork() was called. We cannot safely call it or "
|
||||
"ignore it in the fork() child process. Crashing instead. "
|
||||
"Set a breakpoint on objc_initializeAfterForkError to debug.",
|
||||
cls->nameForLogging());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* class_initialize. Send the '+initialize' message on demand to any
|
||||
* uninitialized class. Force initialization of superclasses first.
|
||||
**********************************************************************/
|
||||
void _class_initialize(Class cls)
|
||||
void initializeNonMetaClass(Class cls)
|
||||
{
|
||||
assert(!cls->isMetaClass());
|
||||
ASSERT(!cls->isMetaClass());
|
||||
|
||||
Class supercls;
|
||||
bool reallyInitialize = NO;
|
||||
@ -382,15 +502,19 @@ void _class_initialize(Class cls)
|
||||
// See note about deadlock above.
|
||||
supercls = cls->superclass;
|
||||
if (supercls && !supercls->isInitialized()) {
|
||||
_class_initialize(supercls);
|
||||
initializeNonMetaClass(supercls);
|
||||
}
|
||||
|
||||
// Try to atomically set CLS_INITIALIZING.
|
||||
SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;
|
||||
{
|
||||
monitor_locker_t lock(classInitLock);
|
||||
if (!cls->isInitialized() && !cls->isInitializing()) {
|
||||
cls->setInitializing();
|
||||
reallyInitialize = YES;
|
||||
|
||||
// Grab a copy of the will-initialize funcs with the lock held.
|
||||
localWillInitializeFuncs.initFrom(willInitializeFuncs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -399,44 +523,55 @@ void _class_initialize(Class cls)
|
||||
|
||||
// Record that we're initializing this class so we can message it.
|
||||
_setThisThreadIsInitializingClass(cls);
|
||||
|
||||
if (MultithreadedForkChild) {
|
||||
// LOL JK we don't really call +initialize methods after fork().
|
||||
performForkChildInitialize(cls, supercls);
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto callback : localWillInitializeFuncs)
|
||||
callback.f(callback.context, 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]",
|
||||
cls->nameForLogging());
|
||||
_objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
|
||||
objc_thread_self(), cls->nameForLogging());
|
||||
}
|
||||
|
||||
// Exceptions: A +initialize call that throws an exception
|
||||
// is deemed to be a complete and successful +initialize.
|
||||
@try {
|
||||
//
|
||||
// Only __OBJC2__ adds these handlers. !__OBJC2__ has a
|
||||
// bootstrapping problem of this versus CF's call to
|
||||
// objc_exception_set_functions().
|
||||
#if __OBJC2__
|
||||
@try
|
||||
#endif
|
||||
{
|
||||
callInitialize(cls);
|
||||
|
||||
if (PrintInitializing) {
|
||||
_objc_inform("INITIALIZE: finished +[%s initialize]",
|
||||
cls->nameForLogging());
|
||||
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
|
||||
objc_thread_self(), cls->nameForLogging());
|
||||
}
|
||||
}
|
||||
#if __OBJC2__
|
||||
@catch (...) {
|
||||
if (PrintInitializing) {
|
||||
_objc_inform("INITIALIZE: +[%s initialize] threw an exception",
|
||||
cls->nameForLogging());
|
||||
_objc_inform("INITIALIZE: thread %p: +[%s initialize] "
|
||||
"threw an exception",
|
||||
objc_thread_self(), cls->nameForLogging());
|
||||
}
|
||||
@throw;
|
||||
}
|
||||
@finally {
|
||||
// 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_locker_t lock(classInitLock);
|
||||
if (!supercls || supercls->isInitialized()) {
|
||||
_finishInitializing(cls, supercls);
|
||||
} else {
|
||||
_finishInitializingAfter(cls, supercls);
|
||||
}
|
||||
@finally
|
||||
#endif
|
||||
{
|
||||
// Done initializing.
|
||||
lockAndFinishInitializing(cls, supercls);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -450,9 +585,14 @@ void _class_initialize(Class cls)
|
||||
// before blocking.
|
||||
if (_thisThreadIsInitializingClass(cls)) {
|
||||
return;
|
||||
} else {
|
||||
} else if (!MultithreadedForkChild) {
|
||||
waitForInitializeToComplete(cls);
|
||||
return;
|
||||
} else {
|
||||
// We're on the child side of fork(), facing a class that
|
||||
// was initializing by some other thread when fork() was called.
|
||||
_setThisThreadIsInitializingClass(cls);
|
||||
performForkChildInitialize(cls, supercls);
|
||||
}
|
||||
}
|
||||
|
||||
@ -472,3 +612,33 @@ void _class_initialize(Class cls)
|
||||
_objc_fatal("thread-safe class init in objc runtime is buggy!");
|
||||
}
|
||||
}
|
||||
|
||||
void _objc_addWillInitializeClassFunc(_objc_func_willInitializeClass _Nonnull func, void * _Nullable context) {
|
||||
#if __OBJC2__
|
||||
unsigned count;
|
||||
Class *realizedClasses;
|
||||
|
||||
// Fetch all currently initialized classes. Do this with classInitLock held
|
||||
// so we don't race with setting those flags.
|
||||
{
|
||||
monitor_locker_t initLock(classInitLock);
|
||||
realizedClasses = objc_copyRealizedClassList(&count);
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
// Remove uninitialized classes from the array.
|
||||
if (!realizedClasses[i]->isInitializing() && !realizedClasses[i]->isInitialized())
|
||||
realizedClasses[i] = Nil;
|
||||
}
|
||||
|
||||
willInitializeFuncs.append({func, context});
|
||||
}
|
||||
|
||||
// Invoke the callback for all realized classes that weren't cleared out.
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (Class cls = realizedClasses[i]) {
|
||||
func(context, cls);
|
||||
}
|
||||
}
|
||||
|
||||
free(realizedClasses);
|
||||
#endif
|
||||
}
|
||||
|
@ -41,9 +41,9 @@
|
||||
#include <objc/runtime.h>
|
||||
#include <Availability.h>
|
||||
#include <malloc/malloc.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <dispatch/dispatch.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
// Termination reasons in the OS_REASON_OBJC namespace.
|
||||
#define OBJC_EXIT_REASON_UNSPECIFIED 1
|
||||
@ -54,13 +54,23 @@ __BEGIN_DECLS
|
||||
// The runtime's class structure will never grow beyond this.
|
||||
#define OBJC_MAX_CLASS_SIZE (32*sizeof(void*))
|
||||
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
// This symbol is exported only from debug builds of libobjc itself.
|
||||
#if defined(OBJC_IS_DEBUG_BUILD)
|
||||
OBJC_EXPORT void _objc_isDebugBuild(void);
|
||||
#endif
|
||||
|
||||
// In-place construction of an Objective-C class.
|
||||
// cls and metacls must each be OBJC_MAX_CLASS_SIZE bytes.
|
||||
// Returns nil if a class with the same name already exists.
|
||||
// Returns nil if the superclass is under construction.
|
||||
// Call objc_registerClassPair() when you are done.
|
||||
OBJC_EXPORT Class objc_initializeClassPair(Class superclass, const char *name, Class cls, Class metacls)
|
||||
OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0);
|
||||
OBJC_EXPORT Class _Nullable
|
||||
objc_initializeClassPair(Class _Nullable superclass, const char * _Nonnull name,
|
||||
Class _Nonnull cls, Class _Nonnull metacls)
|
||||
OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Class and metaclass construction from a compiler-generated memory image.
|
||||
// cls and cls->isa must each be OBJC_MAX_CLASS_SIZE bytes.
|
||||
@ -72,86 +82,164 @@ OBJC_EXPORT Class objc_initializeClassPair(Class superclass, const char *name, C
|
||||
// Do not call objc_registerClassPair().
|
||||
#if __OBJC2__
|
||||
struct objc_image_info;
|
||||
OBJC_EXPORT Class objc_readClassPair(Class cls,
|
||||
const struct objc_image_info *info)
|
||||
OBJC_AVAILABLE(10.10, 8.0, 9.0, 1.0);
|
||||
OBJC_EXPORT Class _Nullable
|
||||
objc_readClassPair(Class _Nonnull cls,
|
||||
const struct objc_image_info * _Nonnull info)
|
||||
OBJC_AVAILABLE(10.10, 8.0, 9.0, 1.0, 2.0);
|
||||
#endif
|
||||
|
||||
// Batch object allocation using malloc_zone_batch_malloc().
|
||||
OBJC_EXPORT unsigned class_createInstances(Class cls, size_t extraBytes,
|
||||
id *results, unsigned num_requested)
|
||||
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0)
|
||||
OBJC_EXPORT unsigned
|
||||
class_createInstances(Class _Nullable cls, size_t extraBytes,
|
||||
id _Nonnull * _Nonnull results, unsigned num_requested)
|
||||
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0)
|
||||
OBJC_ARC_UNAVAILABLE;
|
||||
|
||||
// Get the isa pointer written into objects just before being freed.
|
||||
OBJC_EXPORT Class _objc_getFreedObjectClass(void)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT Class _Nonnull
|
||||
_objc_getFreedObjectClass(void)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// env NSObjCMessageLoggingEnabled
|
||||
OBJC_EXPORT void instrumentObjcMessageSends(BOOL flag)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
instrumentObjcMessageSends(BOOL flag)
|
||||
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Initializer called by libSystem
|
||||
OBJC_EXPORT void _objc_init(void)
|
||||
OBJC_EXPORT void
|
||||
_objc_init(void)
|
||||
#if __OBJC2__
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0, 2.0);
|
||||
#else
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
#endif
|
||||
|
||||
// fork() safety called by libSystem
|
||||
OBJC_EXPORT void
|
||||
_objc_atfork_prepare(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
_objc_atfork_parent(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void
|
||||
_objc_atfork_child(void)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
// Return YES if GC is on and `object` is a GC allocation.
|
||||
OBJC_EXPORT BOOL objc_isAuto(id object)
|
||||
__OSX_DEPRECATED(10.4, 10.8, "it always returns NO")
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT BOOL
|
||||
objc_isAuto(id _Nullable object)
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.4, 10.8, "it always returns NO");
|
||||
|
||||
// GC debugging
|
||||
OBJC_EXPORT BOOL
|
||||
objc_dumpHeap(char * _Nonnull filename, unsigned long length)
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.4, 10.8, "it always returns NO");
|
||||
|
||||
// GC startup callback from Foundation
|
||||
OBJC_EXPORT malloc_zone_t *objc_collect_init(int (*callback)(void))
|
||||
__OSX_DEPRECATED(10.4, 10.8, "it does nothing")
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT malloc_zone_t * _Nullable
|
||||
objc_collect_init(int (* _Nonnull callback)(void))
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.4, 10.8, "it does nothing");
|
||||
|
||||
#if __OBJC2__
|
||||
// Copies the list of currently realized classes
|
||||
// intended for introspection only
|
||||
// most users will want objc_copyClassList instead.
|
||||
OBJC_EXPORT
|
||||
Class _Nonnull * _Nullable
|
||||
objc_copyRealizedClassList(unsigned int *_Nullable outCount)
|
||||
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
|
||||
typedef struct objc_imp_cache_entry {
|
||||
SEL _Nonnull sel;
|
||||
IMP _Nonnull imp;
|
||||
} objc_imp_cache_entry;
|
||||
|
||||
OBJC_EXPORT
|
||||
objc_imp_cache_entry *_Nullable
|
||||
class_copyImpCache(Class _Nonnull cls, int * _Nullable outCount)
|
||||
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
#endif
|
||||
|
||||
// Plainly-implemented GC barriers. Rosetta used to use these.
|
||||
OBJC_EXPORT id objc_assign_strongCast_generic(id value, id *dest)
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_assign_strongCast_generic(id _Nullable value, id _Nullable * _Nonnull dest)
|
||||
UNAVAILABLE_ATTRIBUTE;
|
||||
OBJC_EXPORT id objc_assign_global_generic(id value, id *dest)
|
||||
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_assign_global_generic(id _Nullable value, id _Nullable * _Nonnull dest)
|
||||
UNAVAILABLE_ATTRIBUTE;
|
||||
OBJC_EXPORT id objc_assign_threadlocal_generic(id value, id *dest)
|
||||
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_assign_threadlocal_generic(id _Nullable value,
|
||||
id _Nullable * _Nonnull dest)
|
||||
UNAVAILABLE_ATTRIBUTE;
|
||||
OBJC_EXPORT id objc_assign_ivar_generic(id value, id dest, ptrdiff_t offset)
|
||||
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_assign_ivar_generic(id _Nullable value, id _Nonnull dest, ptrdiff_t offset)
|
||||
UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
// GC preflight for an app executable.
|
||||
// 1: some slice requires GC
|
||||
// 0: no slice requires GC
|
||||
// -1: I/O or file format error
|
||||
OBJC_EXPORT int objc_appRequiresGC(int fd)
|
||||
OBJC_EXPORT int
|
||||
objc_appRequiresGC(int fd)
|
||||
__OSX_AVAILABLE(10.11)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE
|
||||
__WATCHOS_UNAVAILABLE
|
||||
#ifndef __APPLE_BLEACH_SDK__
|
||||
__BRIDGEOS_UNAVAILABLE
|
||||
#endif
|
||||
;
|
||||
|
||||
// Install missing-class callback. Used by the late unlamented ZeroLink.
|
||||
OBJC_EXPORT void _objc_setClassLoader(BOOL (*newClassLoader)(const char *)) OBJC2_UNAVAILABLE;
|
||||
OBJC_EXPORT void
|
||||
_objc_setClassLoader(BOOL (* _Nonnull newClassLoader)(const char * _Nonnull))
|
||||
OBJC2_UNAVAILABLE;
|
||||
|
||||
#if !(TARGET_OS_OSX && !TARGET_OS_IOSMAC && __i386__)
|
||||
// Add a class copy fixup handler. The name is a misnomer, as
|
||||
// multiple calls will install multiple handlers. Older versions
|
||||
// of the Swift runtime call it by name, and it's only used by Swift
|
||||
// so it's not worth deprecating this name in favor of a better one.
|
||||
OBJC_EXPORT void
|
||||
_objc_setClassCopyFixupHandler(void (* _Nonnull newFixupHandler)
|
||||
(Class _Nonnull oldClass, Class _Nonnull newClass))
|
||||
OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0);
|
||||
#endif
|
||||
|
||||
// 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))
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
|
||||
|
||||
// This can go away when AppKit stops calling it (rdar://7811851)
|
||||
#if __OBJC2__
|
||||
OBJC_EXPORT void objc_setMultithreaded (BOOL flag)
|
||||
__OSX_DEPRECATED(10.0, 10.5, "multithreading is always available")
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
#endif
|
||||
OBJC_EXPORT void
|
||||
_objc_setBadAllocHandler(id _Nullable (* _Nonnull newHandler)
|
||||
(Class _Nullable isa))
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Used by ExceptionHandling.framework
|
||||
#if !__OBJC2__
|
||||
OBJC_EXPORT void _objc_error(id rcv, const char *fmt, va_list args)
|
||||
__attribute__((noreturn))
|
||||
__OSX_DEPRECATED(10.0, 10.5, "use other logging facilities instead")
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT void
|
||||
_objc_error(id _Nullable rcv, const char * _Nonnull fmt, va_list args)
|
||||
__attribute__((noreturn, cold))
|
||||
OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE(10.0, 10.5, "use other logging facilities instead");
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Returns the names of all the classes within a library.
|
||||
*
|
||||
* @param image The mach header for library or framework you are inquiring about.
|
||||
* @param outCount The number of class names returned.
|
||||
*
|
||||
* @return An array of C strings representing the class names.
|
||||
*/
|
||||
OBJC_EXPORT const char * _Nonnull * _Nullable
|
||||
objc_copyClassNamesForImageHeader(const struct mach_header * _Nonnull mh,
|
||||
unsigned int * _Nullable outCount)
|
||||
OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0);
|
||||
|
||||
// Tagged pointer objects.
|
||||
|
||||
#if __LP64__
|
||||
@ -174,15 +262,32 @@ typedef uint16_t objc_tag_index_t;
|
||||
enum
|
||||
#endif
|
||||
{
|
||||
// 60-bit payloads
|
||||
OBJC_TAG_NSAtom = 0,
|
||||
OBJC_TAG_1 = 1,
|
||||
OBJC_TAG_NSString = 2,
|
||||
OBJC_TAG_NSNumber = 3,
|
||||
OBJC_TAG_NSIndexPath = 4,
|
||||
OBJC_TAG_NSManagedObjectID = 5,
|
||||
OBJC_TAG_NSDate = 6,
|
||||
OBJC_TAG_NSDate = 6,
|
||||
|
||||
// 60-bit reserved
|
||||
OBJC_TAG_RESERVED_7 = 7,
|
||||
|
||||
// 52-bit payloads
|
||||
OBJC_TAG_Photos_1 = 8,
|
||||
OBJC_TAG_Photos_2 = 9,
|
||||
OBJC_TAG_Photos_3 = 10,
|
||||
OBJC_TAG_Photos_4 = 11,
|
||||
OBJC_TAG_XPC_1 = 12,
|
||||
OBJC_TAG_XPC_2 = 13,
|
||||
OBJC_TAG_XPC_3 = 14,
|
||||
OBJC_TAG_XPC_4 = 15,
|
||||
OBJC_TAG_NSColor = 16,
|
||||
OBJC_TAG_UIColor = 17,
|
||||
OBJC_TAG_CGColor = 18,
|
||||
OBJC_TAG_NSIndexSet = 19,
|
||||
|
||||
OBJC_TAG_First60BitPayload = 0,
|
||||
OBJC_TAG_Last60BitPayload = 6,
|
||||
OBJC_TAG_First52BitPayload = 8,
|
||||
@ -202,48 +307,50 @@ _objc_taggedPointersEnabled(void);
|
||||
|
||||
// Register a class for a tagged pointer tag.
|
||||
// Aborts if the tag is invalid or already in use.
|
||||
OBJC_EXPORT void _objc_registerTaggedPointerClass(objc_tag_index_t tag, Class cls)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
_objc_registerTaggedPointerClass(objc_tag_index_t tag, Class _Nonnull cls)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Returns the registered class for the given tag.
|
||||
// Returns nil if the tag is valid but has no registered class.
|
||||
// Aborts if the tag is invalid.
|
||||
OBJC_EXPORT Class _objc_getClassForTag(objc_tag_index_t tag)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
|
||||
OBJC_EXPORT Class _Nullable
|
||||
_objc_getClassForTag(objc_tag_index_t tag)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Create a tagged pointer object with the given tag and payload.
|
||||
// Assumes the tag is valid.
|
||||
// Assumes tagged pointers are enabled.
|
||||
// The payload will be silently truncated to fit.
|
||||
static inline void *
|
||||
static inline void * _Nonnull
|
||||
_objc_makeTaggedPointer(objc_tag_index_t tag, uintptr_t payload);
|
||||
|
||||
// Return true if ptr is a tagged pointer object.
|
||||
// Does not check the validity of ptr's class.
|
||||
static inline bool
|
||||
_objc_isTaggedPointer(const void *ptr);
|
||||
_objc_isTaggedPointer(const void * _Nullable ptr);
|
||||
|
||||
// Extract the tag value from the given tagged pointer object.
|
||||
// Assumes ptr is a valid tagged pointer object.
|
||||
// Does not check the validity of ptr's tag.
|
||||
static inline objc_tag_index_t
|
||||
_objc_getTaggedPointerTag(const void *ptr);
|
||||
_objc_getTaggedPointerTag(const void * _Nullable ptr);
|
||||
|
||||
// Extract the payload from the given tagged pointer object.
|
||||
// Assumes ptr is a valid tagged pointer object.
|
||||
// The payload value is zero-extended.
|
||||
static inline uintptr_t
|
||||
_objc_getTaggedPointerValue(const void *ptr);
|
||||
_objc_getTaggedPointerValue(const void * _Nullable ptr);
|
||||
|
||||
// Extract the payload from the given tagged pointer object.
|
||||
// Assumes ptr is a valid tagged pointer object.
|
||||
// The payload value is sign-extended.
|
||||
static inline intptr_t
|
||||
_objc_getTaggedPointerSignedValue(const void *ptr);
|
||||
_objc_getTaggedPointerSignedValue(const void * _Nullable ptr);
|
||||
|
||||
// Don't use the values below. Use the declarations above.
|
||||
|
||||
#if TARGET_OS_OSX && __x86_64__
|
||||
#if (TARGET_OS_OSX || TARGET_OS_IOSMAC) && __x86_64__
|
||||
// 64-bit Mac - tag bit is LSB
|
||||
# define OBJC_MSB_TAGGED_POINTERS 0
|
||||
#else
|
||||
@ -262,29 +369,43 @@ _objc_getTaggedPointerSignedValue(const void *ptr);
|
||||
#define _OBJC_TAG_EXT_SLOT_MASK 0xff
|
||||
|
||||
#if OBJC_MSB_TAGGED_POINTERS
|
||||
# define _OBJC_TAG_MASK (1ULL<<63)
|
||||
# define _OBJC_TAG_MASK (1UL<<63)
|
||||
# define _OBJC_TAG_INDEX_SHIFT 60
|
||||
# define _OBJC_TAG_SLOT_SHIFT 60
|
||||
# define _OBJC_TAG_PAYLOAD_LSHIFT 4
|
||||
# define _OBJC_TAG_PAYLOAD_RSHIFT 4
|
||||
# define _OBJC_TAG_EXT_MASK (0xfULL<<60)
|
||||
# define _OBJC_TAG_EXT_MASK (0xfUL<<60)
|
||||
# define _OBJC_TAG_EXT_INDEX_SHIFT 52
|
||||
# define _OBJC_TAG_EXT_SLOT_SHIFT 52
|
||||
# define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 12
|
||||
# define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12
|
||||
#else
|
||||
# define _OBJC_TAG_MASK 1
|
||||
# define _OBJC_TAG_MASK 1UL
|
||||
# define _OBJC_TAG_INDEX_SHIFT 1
|
||||
# define _OBJC_TAG_SLOT_SHIFT 0
|
||||
# define _OBJC_TAG_PAYLOAD_LSHIFT 0
|
||||
# define _OBJC_TAG_PAYLOAD_RSHIFT 4
|
||||
# define _OBJC_TAG_EXT_MASK 0xfULL
|
||||
# define _OBJC_TAG_EXT_MASK 0xfUL
|
||||
# define _OBJC_TAG_EXT_INDEX_SHIFT 4
|
||||
# define _OBJC_TAG_EXT_SLOT_SHIFT 4
|
||||
# define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 0
|
||||
# define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12
|
||||
#endif
|
||||
|
||||
extern uintptr_t objc_debug_taggedpointer_obfuscator;
|
||||
|
||||
static inline void * _Nonnull
|
||||
_objc_encodeTaggedPointer(uintptr_t ptr)
|
||||
{
|
||||
return (void *)(objc_debug_taggedpointer_obfuscator ^ ptr);
|
||||
}
|
||||
|
||||
static inline uintptr_t
|
||||
_objc_decodeTaggedPointer(const void * _Nullable ptr)
|
||||
{
|
||||
return (uintptr_t)ptr ^ objc_debug_taggedpointer_obfuscator;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
_objc_taggedPointersEnabled(void)
|
||||
{
|
||||
@ -292,42 +413,45 @@ _objc_taggedPointersEnabled(void)
|
||||
return (objc_debug_taggedpointer_mask != 0);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
static inline void * _Nonnull
|
||||
_objc_makeTaggedPointer(objc_tag_index_t tag, uintptr_t value)
|
||||
{
|
||||
// PAYLOAD_LSHIFT and PAYLOAD_RSHIFT are the payload extraction shifts.
|
||||
// They are reversed here for payload insertion.
|
||||
|
||||
// assert(_objc_taggedPointersEnabled());
|
||||
// ASSERT(_objc_taggedPointersEnabled());
|
||||
if (tag <= OBJC_TAG_Last60BitPayload) {
|
||||
// assert(((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT) == value);
|
||||
return (void*)
|
||||
// ASSERT(((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT) == value);
|
||||
uintptr_t result =
|
||||
(_OBJC_TAG_MASK |
|
||||
((uintptr_t)tag << _OBJC_TAG_INDEX_SHIFT) |
|
||||
((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT));
|
||||
return _objc_encodeTaggedPointer(result);
|
||||
} else {
|
||||
// assert(tag >= OBJC_TAG_First52BitPayload);
|
||||
// assert(tag <= OBJC_TAG_Last52BitPayload);
|
||||
// assert(((value << _OBJC_TAG_EXT_PAYLOAD_RSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_LSHIFT) == value);
|
||||
return (void*)
|
||||
// ASSERT(tag >= OBJC_TAG_First52BitPayload);
|
||||
// ASSERT(tag <= OBJC_TAG_Last52BitPayload);
|
||||
// ASSERT(((value << _OBJC_TAG_EXT_PAYLOAD_RSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_LSHIFT) == value);
|
||||
uintptr_t result =
|
||||
(_OBJC_TAG_EXT_MASK |
|
||||
((uintptr_t)(tag - OBJC_TAG_First52BitPayload) << _OBJC_TAG_EXT_INDEX_SHIFT) |
|
||||
((value << _OBJC_TAG_EXT_PAYLOAD_RSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_LSHIFT));
|
||||
return _objc_encodeTaggedPointer(result);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
_objc_isTaggedPointer(const void *ptr)
|
||||
_objc_isTaggedPointer(const void * _Nullable ptr)
|
||||
{
|
||||
return ((intptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
|
||||
return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
|
||||
}
|
||||
|
||||
static inline objc_tag_index_t
|
||||
_objc_getTaggedPointerTag(const void *ptr)
|
||||
_objc_getTaggedPointerTag(const void * _Nullable ptr)
|
||||
{
|
||||
// assert(_objc_isTaggedPointer(ptr));
|
||||
uintptr_t basicTag = ((uintptr_t)ptr >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
|
||||
uintptr_t extTag = ((uintptr_t)ptr >> _OBJC_TAG_EXT_INDEX_SHIFT) & _OBJC_TAG_EXT_INDEX_MASK;
|
||||
// ASSERT(_objc_isTaggedPointer(ptr));
|
||||
uintptr_t value = _objc_decodeTaggedPointer(ptr);
|
||||
uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
|
||||
uintptr_t extTag = (value >> _OBJC_TAG_EXT_INDEX_SHIFT) & _OBJC_TAG_EXT_INDEX_MASK;
|
||||
if (basicTag == _OBJC_TAG_INDEX_MASK) {
|
||||
return (objc_tag_index_t)(extTag + OBJC_TAG_First52BitPayload);
|
||||
} else {
|
||||
@ -336,26 +460,28 @@ _objc_getTaggedPointerTag(const void *ptr)
|
||||
}
|
||||
|
||||
static inline uintptr_t
|
||||
_objc_getTaggedPointerValue(const void *ptr)
|
||||
_objc_getTaggedPointerValue(const void * _Nullable ptr)
|
||||
{
|
||||
// assert(_objc_isTaggedPointer(ptr));
|
||||
uintptr_t basicTag = ((uintptr_t)ptr >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
|
||||
// ASSERT(_objc_isTaggedPointer(ptr));
|
||||
uintptr_t value = _objc_decodeTaggedPointer(ptr);
|
||||
uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
|
||||
if (basicTag == _OBJC_TAG_INDEX_MASK) {
|
||||
return ((uintptr_t)ptr << _OBJC_TAG_EXT_PAYLOAD_LSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_RSHIFT;
|
||||
return (value << _OBJC_TAG_EXT_PAYLOAD_LSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_RSHIFT;
|
||||
} else {
|
||||
return ((uintptr_t)ptr << _OBJC_TAG_PAYLOAD_LSHIFT) >> _OBJC_TAG_PAYLOAD_RSHIFT;
|
||||
return (value << _OBJC_TAG_PAYLOAD_LSHIFT) >> _OBJC_TAG_PAYLOAD_RSHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
static inline intptr_t
|
||||
_objc_getTaggedPointerSignedValue(const void *ptr)
|
||||
_objc_getTaggedPointerSignedValue(const void * _Nullable ptr)
|
||||
{
|
||||
// assert(_objc_isTaggedPointer(ptr));
|
||||
uintptr_t basicTag = ((uintptr_t)ptr >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
|
||||
// ASSERT(_objc_isTaggedPointer(ptr));
|
||||
uintptr_t value = _objc_decodeTaggedPointer(ptr);
|
||||
uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
|
||||
if (basicTag == _OBJC_TAG_INDEX_MASK) {
|
||||
return ((intptr_t)ptr << _OBJC_TAG_EXT_PAYLOAD_LSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_RSHIFT;
|
||||
return ((intptr_t)value << _OBJC_TAG_EXT_PAYLOAD_LSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_RSHIFT;
|
||||
} else {
|
||||
return ((intptr_t)ptr << _OBJC_TAG_PAYLOAD_LSHIFT) >> _OBJC_TAG_PAYLOAD_RSHIFT;
|
||||
return ((intptr_t)value << _OBJC_TAG_PAYLOAD_LSHIFT) >> _OBJC_TAG_PAYLOAD_RSHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
@ -376,22 +502,78 @@ _objc_getTaggedPointerSignedValue(const void *ptr)
|
||||
*
|
||||
* class_getMethodImplementation(object_getClass(obj), name);
|
||||
*/
|
||||
OBJC_EXPORT IMP object_getMethodImplementation(id obj, SEL name)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
|
||||
OBJC_EXPORT IMP _Nonnull
|
||||
object_getMethodImplementation(id _Nullable obj, SEL _Nonnull name)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT IMP object_getMethodImplementation_stret(id obj, SEL name)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0)
|
||||
OBJC_EXPORT IMP _Nonnull
|
||||
object_getMethodImplementation_stret(id _Nullable obj, SEL _Nonnull name)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0)
|
||||
OBJC_ARM64_UNAVAILABLE;
|
||||
|
||||
|
||||
// Instance-specific instance variable layout.
|
||||
/**
|
||||
* Adds multiple methods to a class in bulk. This amortizes overhead that can be
|
||||
* expensive when adding methods one by one with class_addMethod.
|
||||
*
|
||||
* @param cls The class to which to add the methods.
|
||||
* @param names An array of selectors for the methods to add.
|
||||
* @param imps An array of functions which implement the new methods.
|
||||
* @param types An array of strings that describe the types of each method's
|
||||
* arguments.
|
||||
* @param count The number of items in the names, imps, and types arrays.
|
||||
* @param outFiledCount Upon return, contains the number of failed selectors in
|
||||
* the returned array.
|
||||
*
|
||||
* @return A NULL-terminated C array of selectors which could not be added. A
|
||||
* method cannot be added when a method of that name already exists on that
|
||||
* class. When no failures occur, the return value is \c NULL. When a non-NULL
|
||||
* value is returned, the caller must free the array with \c free().
|
||||
*
|
||||
*/
|
||||
#if __OBJC2__
|
||||
OBJC_EXPORT _Nullable SEL * _Nullable
|
||||
class_addMethodsBulk(_Nullable Class cls, _Nonnull const SEL * _Nonnull names,
|
||||
_Nonnull const IMP * _Nonnull imps,
|
||||
const char * _Nonnull * _Nonnull types, uint32_t count,
|
||||
uint32_t * _Nullable outFailedCount)
|
||||
OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Replaces multiple methods in a class in bulk. This amortizes overhead that
|
||||
* can be expensive when adding methods one by one with class_replaceMethod.
|
||||
*
|
||||
* @param cls The class to modify.
|
||||
* @param names An array of selectors for the methods to replace.
|
||||
* @param imps An array of functions will be the new method implementantations.
|
||||
* @param types An array of strings that describe the types of each method's
|
||||
* arguments.
|
||||
* @param count The number of items in the names, imps, and types arrays.
|
||||
*/
|
||||
#if __OBJC2__
|
||||
OBJC_EXPORT void
|
||||
class_replaceMethodsBulk(_Nullable Class cls,
|
||||
_Nonnull const SEL * _Nonnull names,
|
||||
_Nonnull const IMP * _Nonnull imps,
|
||||
const char * _Nonnull * _Nonnull types,
|
||||
uint32_t count)
|
||||
OBJC_AVAILABLE(10.14, 12.0, 12.0, 5.0, 3.0);
|
||||
#endif
|
||||
|
||||
|
||||
// Instance-specific instance variable layout. This is no longer implemented.
|
||||
|
||||
OBJC_EXPORT void
|
||||
_class_setIvarLayoutAccessor(Class _Nullable cls,
|
||||
const uint8_t* _Nullable (* _Nonnull accessor)
|
||||
(id _Nullable object))
|
||||
UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
OBJC_EXPORT const uint8_t * _Nullable
|
||||
_object_getIvarLayout(Class _Nullable cls, id _Nullable object)
|
||||
UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
OBJC_EXPORT void _class_setIvarLayoutAccessor(Class cls_gen, const uint8_t* (*accessor) (id object))
|
||||
__OSX_AVAILABLE(10.7)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
OBJC_EXPORT const uint8_t *_object_getIvarLayout(Class cls_gen, id object)
|
||||
__OSX_AVAILABLE(10.7)
|
||||
__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
|
||||
|
||||
/*
|
||||
"Unknown" includes non-object ivars and non-ARC non-__weak ivars
|
||||
@ -406,230 +588,299 @@ typedef enum {
|
||||
objc_ivar_memoryUnretained // direct access / direct access
|
||||
} objc_ivar_memory_management_t;
|
||||
|
||||
OBJC_EXPORT objc_ivar_memory_management_t _class_getIvarMemoryManagement(Class cls, Ivar ivar)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
|
||||
OBJC_EXPORT objc_ivar_memory_management_t
|
||||
_class_getIvarMemoryManagement(Class _Nullable cls, Ivar _Nonnull ivar)
|
||||
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
|
||||
|
||||
OBJC_EXPORT BOOL _class_isFutureClass(Class cls)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
|
||||
OBJC_EXPORT BOOL _class_isFutureClass(Class _Nullable cls)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
|
||||
|
||||
|
||||
// API to only be called by root classes like NSObject or NSProxy
|
||||
|
||||
OBJC_EXPORT
|
||||
id
|
||||
_objc_rootRetain(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
id _Nonnull
|
||||
_objc_rootRetain(id _Nonnull obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
void
|
||||
_objc_rootRelease(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
_objc_rootRelease(id _Nonnull obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
bool
|
||||
_objc_rootReleaseWasZero(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
_objc_rootReleaseWasZero(id _Nonnull obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
bool
|
||||
_objc_rootTryRetain(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
_objc_rootTryRetain(id _Nonnull obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
bool
|
||||
_objc_rootIsDeallocating(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
_objc_rootIsDeallocating(id _Nonnull obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
id
|
||||
_objc_rootAutorelease(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
id _Nonnull
|
||||
_objc_rootAutorelease(id _Nonnull obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
uintptr_t
|
||||
_objc_rootRetainCount(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
_objc_rootRetainCount(id _Nonnull obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
id
|
||||
_objc_rootInit(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
id _Nonnull
|
||||
_objc_rootInit(id _Nonnull obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
id
|
||||
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
id _Nullable
|
||||
_objc_rootAllocWithZone(Class _Nonnull cls, malloc_zone_t * _Nullable zone)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
id
|
||||
_objc_rootAlloc(Class cls)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
id _Nullable
|
||||
_objc_rootAlloc(Class _Nonnull cls)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
void
|
||||
_objc_rootDealloc(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
_objc_rootDealloc(id _Nonnull obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
void
|
||||
_objc_rootFinalize(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
_objc_rootFinalize(id _Nonnull obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
malloc_zone_t *
|
||||
_objc_rootZone(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
malloc_zone_t * _Nonnull
|
||||
_objc_rootZone(id _Nonnull obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
uintptr_t
|
||||
_objc_rootHash(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
_objc_rootHash(id _Nonnull obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
void *
|
||||
void * _Nonnull
|
||||
objc_autoreleasePoolPush(void)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
void
|
||||
objc_autoreleasePoolPop(void *context)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
objc_autoreleasePoolPop(void * _Nonnull context)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
|
||||
OBJC_EXPORT id objc_alloc(Class cls)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_alloc(Class _Nullable cls)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT id objc_allocWithZone(Class cls)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_allocWithZone(Class _Nullable cls)
|
||||
OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT id objc_retain(id obj)
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_alloc_init(Class _Nullable cls)
|
||||
OBJC_AVAILABLE(10.14.4, 12.2, 12.2, 5.2, 3.2);
|
||||
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_opt_new(Class _Nullable cls)
|
||||
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_opt_self(id _Nullable obj)
|
||||
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
|
||||
OBJC_EXPORT Class _Nullable
|
||||
objc_opt_class(id _Nullable obj)
|
||||
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
|
||||
OBJC_EXPORT BOOL
|
||||
objc_opt_respondsToSelector(id _Nullable obj, SEL _Nullable sel)
|
||||
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
|
||||
OBJC_EXPORT BOOL
|
||||
objc_opt_isKindOfClass(id _Nullable obj, Class _Nullable cls)
|
||||
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
|
||||
|
||||
OBJC_EXPORT BOOL
|
||||
objc_sync_try_enter(id _Nonnull obj)
|
||||
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_retain(id _Nullable obj)
|
||||
__asm__("_objc_retain")
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void objc_release(id obj)
|
||||
OBJC_EXPORT void
|
||||
objc_release(id _Nullable obj)
|
||||
__asm__("_objc_release")
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT id objc_autorelease(id obj)
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_autorelease(id _Nullable obj)
|
||||
__asm__("_objc_autorelease")
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Prepare a value at +1 for return through a +0 autoreleasing convention.
|
||||
OBJC_EXPORT
|
||||
id
|
||||
objc_autoreleaseReturnValue(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_autoreleaseReturnValue(id _Nullable obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Prepare a value at +0 for return through a +0 autoreleasing convention.
|
||||
OBJC_EXPORT
|
||||
id
|
||||
objc_retainAutoreleaseReturnValue(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_retainAutoreleaseReturnValue(id _Nullable obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Accept a value returned through a +0 autoreleasing convention for use at +1.
|
||||
OBJC_EXPORT
|
||||
id
|
||||
objc_retainAutoreleasedReturnValue(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_retainAutoreleasedReturnValue(id _Nullable obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Accept a value returned through a +0 autoreleasing convention for use at +0.
|
||||
OBJC_EXPORT
|
||||
id
|
||||
objc_unsafeClaimAutoreleasedReturnValue(id obj)
|
||||
OBJC_AVAILABLE(10.11, 9.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_unsafeClaimAutoreleasedReturnValue(id _Nullable obj)
|
||||
OBJC_AVAILABLE(10.11, 9.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
void
|
||||
objc_storeStrong(id *location, id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
objc_storeStrong(id _Nullable * _Nonnull location, id _Nullable obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
id
|
||||
objc_retainAutorelease(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_retainAutorelease(id _Nullable obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// obsolete.
|
||||
OBJC_EXPORT id objc_retain_autorelease(id obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_retain_autorelease(id _Nullable obj)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
id
|
||||
objc_loadWeakRetained(id *location)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_loadWeakRetained(id _Nullable * _Nonnull location)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
id
|
||||
objc_initWeak(id *location, id val)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_initWeak(id _Nullable * _Nonnull location, id _Nullable val)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Like objc_storeWeak, but stores nil if the new object is deallocating
|
||||
// or the new object's class does not support weak references.
|
||||
// Returns the value stored (either the new object or nil).
|
||||
OBJC_EXPORT
|
||||
id
|
||||
objc_storeWeakOrNil(id *location, id obj)
|
||||
OBJC_AVAILABLE(10.11, 9.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_storeWeakOrNil(id _Nullable * _Nonnull location, id _Nullable obj)
|
||||
OBJC_AVAILABLE(10.11, 9.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// Like objc_initWeak, but stores nil if the new object is deallocating
|
||||
// or the new object's class does not support weak references.
|
||||
// Returns the value stored (either the new object or nil).
|
||||
OBJC_EXPORT
|
||||
id
|
||||
objc_initWeakOrNil(id *location, id val)
|
||||
OBJC_AVAILABLE(10.11, 9.0, 9.0, 1.0);
|
||||
OBJC_EXPORT id _Nullable
|
||||
objc_initWeakOrNil(id _Nullable * _Nonnull location, id _Nullable val)
|
||||
OBJC_AVAILABLE(10.11, 9.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
void
|
||||
objc_destroyWeak(id *location)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
objc_destroyWeak(id _Nullable * _Nonnull location)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
void
|
||||
objc_copyWeak(id *to, id *from)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
objc_copyWeak(id _Nullable * _Nonnull to, id _Nullable * _Nonnull from)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
void
|
||||
objc_moveWeak(id *to, id *from)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
objc_moveWeak(id _Nullable * _Nonnull to, id _Nullable * _Nonnull from)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
|
||||
OBJC_EXPORT
|
||||
void
|
||||
OBJC_EXPORT void
|
||||
_objc_autoreleasePoolPrint(void)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT BOOL objc_should_deallocate(id object)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT BOOL
|
||||
objc_should_deallocate(id _Nonnull object)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT void objc_clear_deallocating(id object)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
objc_clear_deallocating(id _Nonnull object)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
|
||||
// to make CF link for now
|
||||
|
||||
OBJC_EXPORT
|
||||
void *
|
||||
OBJC_EXPORT void * _Nonnull
|
||||
_objc_autoreleasePoolPush(void)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
OBJC_EXPORT
|
||||
void
|
||||
_objc_autoreleasePoolPop(void *context)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
_objc_autoreleasePoolPop(void * _Nonnull context)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
|
||||
/**
|
||||
* Load a classref, which is a chunk of data containing a class
|
||||
* pointer. May perform initialization and rewrite the classref to
|
||||
* point to a new object, if needed. Returns the loaded Class.
|
||||
*
|
||||
* In particular, if the classref points to a stub class (indicated
|
||||
* by setting the bottom bit of the class pointer to 1), then this
|
||||
* will call the stub's initializer and then replace the classref
|
||||
* value with the value returned by the initializer.
|
||||
*
|
||||
* @param clsref The classref to load.
|
||||
* @return The loaded Class pointer.
|
||||
*/
|
||||
#if __OBJC2__
|
||||
OBJC_EXPORT _Nullable Class
|
||||
objc_loadClassref(_Nullable Class * _Nonnull clsref)
|
||||
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
|
||||
#endif
|
||||
|
||||
|
||||
// Extra @encode data for XPC, or NULL
|
||||
OBJC_EXPORT const char *_protocol_getMethodTypeEncoding(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod)
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
|
||||
OBJC_EXPORT const char * _Nullable
|
||||
_protocol_getMethodTypeEncoding(Protocol * _Nonnull proto, SEL _Nonnull sel,
|
||||
BOOL isRequiredMethod, BOOL isInstanceMethod)
|
||||
OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0, 2.0);
|
||||
|
||||
|
||||
/**
|
||||
* Function type for a function that is called when a realized class
|
||||
* is about to be initialized.
|
||||
*
|
||||
* @param context The context pointer the function was registered with.
|
||||
* @param cls The class that's about to be initialized.
|
||||
*/
|
||||
struct mach_header;
|
||||
typedef void (*_objc_func_willInitializeClass)(void * _Nullable context, Class _Nonnull cls);
|
||||
|
||||
/**
|
||||
* Add a function to be called when a realized class is about to be
|
||||
* initialized. The class can be queried and manipulated using runtime
|
||||
* functions. Don't message it.
|
||||
*
|
||||
* When adding a new function, that function is immediately called with all
|
||||
* realized classes that are already initialized or are in the process
|
||||
* of initialization.
|
||||
*
|
||||
* @param func The function to add.
|
||||
* @param context A context pointer that will be passed to the function when called.
|
||||
*/
|
||||
#define OBJC_WILLINITIALIZECLASSFUNC_DEFINED 1
|
||||
OBJC_EXPORT void _objc_addWillInitializeClassFunc(_objc_func_willInitializeClass _Nonnull func, void * _Nullable context)
|
||||
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 4.0);
|
||||
|
||||
// API to only be called by classes that provide their own reference count storage
|
||||
|
||||
OBJC_EXPORT
|
||||
void
|
||||
_objc_deallocOnMainThreadHelper(void *context)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
|
||||
OBJC_EXPORT void
|
||||
_objc_deallocOnMainThreadHelper(void * _Nullable context)
|
||||
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
|
||||
|
||||
// On async versus sync deallocation and the _dealloc2main flag
|
||||
//
|
||||
|
@ -338,8 +338,8 @@ layout_bitmap_grow(layout_bitmap *bits, size_t newCount)
|
||||
realloc(bits->bits, (newAllocated+7) / 8);
|
||||
bits->bitsAllocated = newAllocated;
|
||||
}
|
||||
assert(bits->bitsAllocated >= bits->bitCount);
|
||||
assert(bits->bitsAllocated >= newCount);
|
||||
ASSERT(bits->bitsAllocated >= bits->bitCount);
|
||||
ASSERT(bits->bitsAllocated >= newCount);
|
||||
}
|
||||
|
||||
|
||||
|
@ -35,20 +35,21 @@
|
||||
/* dynamically loading Mach-O object files that contain Objective-C code */
|
||||
|
||||
OBJC_EXPORT long objc_loadModules (
|
||||
char *modlist[],
|
||||
void *errStream,
|
||||
void (*class_callback) (Class, Category),
|
||||
/*headerType*/ struct mach_header **hdr_addr,
|
||||
char *debug_file
|
||||
char * _Nullable modlist[_Nullable],
|
||||
void * _Nullable errStream,
|
||||
void (* _Nullable class_callback) (Class _Nullable, Category _Nullable),
|
||||
/*headerType*/ struct mach_header * _Nullable * _Nullable hdr_addr,
|
||||
char * _Nullable debug_file
|
||||
) OBJC2_UNAVAILABLE;
|
||||
|
||||
OBJC_EXPORT int objc_loadModule (
|
||||
char * moduleName,
|
||||
void (*class_callback) (Class, Category),
|
||||
int * errorCode
|
||||
char * _Nullable moduleName,
|
||||
void (* _Nullable class_callback) (Class _Nullable, Category _Nullable),
|
||||
int * _Nullable errorCode
|
||||
) OBJC2_UNAVAILABLE;
|
||||
OBJC_EXPORT long objc_unloadModules(
|
||||
void *errorStream, /* input (optional) */
|
||||
void (*unloadCallback)(Class, Category) /* input (optional) */
|
||||
void * _Nullable errorStream, /* input (optional) */
|
||||
void (* _Nullable unloadCallback)(Class _Nullable, Category _Nullable) /* input (optional) */
|
||||
) OBJC2_UNAVAILABLE;
|
||||
|
||||
#endif /* _OBJC_LOAD_H_ */
|
||||
|
@ -201,7 +201,7 @@ static void call_class_loads(void)
|
||||
if (PrintLoading) {
|
||||
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
|
||||
}
|
||||
(*load_method)(cls, SEL_load);
|
||||
(*load_method)(cls, @selector(load));
|
||||
}
|
||||
|
||||
// Destroy the detached list.
|
||||
@ -248,7 +248,7 @@ static bool call_category_loads(void)
|
||||
cls->nameForLogging(),
|
||||
_category_getName(cat));
|
||||
}
|
||||
(*load_method)(cls, SEL_load);
|
||||
(*load_method)(cls, @selector(load));
|
||||
cats[i].cat = nil;
|
||||
}
|
||||
}
|
||||
|
@ -21,32 +21,50 @@
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
|
||||
#if LOCKDEBUG
|
||||
extern void lockdebug_assert_all_locks_locked();
|
||||
extern void lockdebug_assert_no_locks_locked();
|
||||
extern void lockdebug_setInForkPrepare(bool);
|
||||
extern void lockdebug_lock_precedes_lock(const void *oldlock, const void *newlock);
|
||||
#else
|
||||
static constexpr inline void lockdebug_assert_all_locks_locked() { }
|
||||
static constexpr inline void lockdebug_assert_no_locks_locked() { }
|
||||
static constexpr inline void lockdebug_setInForkPrepare(bool) { }
|
||||
static constexpr inline void lockdebug_lock_precedes_lock(const void *, const void *) { }
|
||||
#endif
|
||||
|
||||
extern void lockdebug_remember_mutex(mutex_tt<true> *lock);
|
||||
extern void lockdebug_mutex_lock(mutex_tt<true> *lock);
|
||||
extern void lockdebug_mutex_try_lock(mutex_tt<true> *lock);
|
||||
extern void lockdebug_mutex_unlock(mutex_tt<true> *lock);
|
||||
extern void lockdebug_mutex_assert_locked(mutex_tt<true> *lock);
|
||||
extern void lockdebug_mutex_assert_unlocked(mutex_tt<true> *lock);
|
||||
|
||||
static inline void lockdebug_mutex_lock(mutex_tt<false> *lock) { }
|
||||
static inline void lockdebug_mutex_try_lock(mutex_tt<false> *lock) { }
|
||||
static inline void lockdebug_mutex_unlock(mutex_tt<false> *lock) { }
|
||||
static inline void lockdebug_mutex_assert_locked(mutex_tt<false> *lock) { }
|
||||
static inline void lockdebug_mutex_assert_unlocked(mutex_tt<false> *lock) { }
|
||||
static constexpr inline void lockdebug_remember_mutex(mutex_tt<false> *lock) { }
|
||||
static constexpr inline void lockdebug_mutex_lock(mutex_tt<false> *lock) { }
|
||||
static constexpr inline void lockdebug_mutex_try_lock(mutex_tt<false> *lock) { }
|
||||
static constexpr inline void lockdebug_mutex_unlock(mutex_tt<false> *lock) { }
|
||||
static constexpr inline void lockdebug_mutex_assert_locked(mutex_tt<false> *lock) { }
|
||||
static constexpr inline void lockdebug_mutex_assert_unlocked(mutex_tt<false> *lock) { }
|
||||
|
||||
|
||||
extern void lockdebug_remember_monitor(monitor_tt<true> *lock);
|
||||
extern void lockdebug_monitor_enter(monitor_tt<true> *lock);
|
||||
extern void lockdebug_monitor_leave(monitor_tt<true> *lock);
|
||||
extern void lockdebug_monitor_wait(monitor_tt<true> *lock);
|
||||
extern void lockdebug_monitor_assert_locked(monitor_tt<true> *lock);
|
||||
extern void lockdebug_monitor_assert_unlocked(monitor_tt<true> *lock);
|
||||
|
||||
static inline void lockdebug_monitor_enter(monitor_tt<false> *lock) { }
|
||||
static inline void lockdebug_monitor_leave(monitor_tt<false> *lock) { }
|
||||
static inline void lockdebug_monitor_wait(monitor_tt<false> *lock) { }
|
||||
static inline void lockdebug_monitor_assert_locked(monitor_tt<false> *lock) { }
|
||||
static inline void lockdebug_monitor_assert_unlocked(monitor_tt<false> *lock) {}
|
||||
static constexpr inline void lockdebug_remember_monitor(monitor_tt<false> *lock) { }
|
||||
static constexpr inline void lockdebug_monitor_enter(monitor_tt<false> *lock) { }
|
||||
static constexpr inline void lockdebug_monitor_leave(monitor_tt<false> *lock) { }
|
||||
static constexpr inline void lockdebug_monitor_wait(monitor_tt<false> *lock) { }
|
||||
static constexpr inline void lockdebug_monitor_assert_locked(monitor_tt<false> *lock) { }
|
||||
static constexpr inline void lockdebug_monitor_assert_unlocked(monitor_tt<false> *lock) {}
|
||||
|
||||
|
||||
extern void
|
||||
lockdebug_remember_recursive_mutex(recursive_mutex_tt<true> *lock);
|
||||
extern void
|
||||
lockdebug_recursive_mutex_lock(recursive_mutex_tt<true> *lock);
|
||||
extern void
|
||||
@ -56,34 +74,13 @@ lockdebug_recursive_mutex_assert_locked(recursive_mutex_tt<true> *lock);
|
||||
extern void
|
||||
lockdebug_recursive_mutex_assert_unlocked(recursive_mutex_tt<true> *lock);
|
||||
|
||||
static inline void
|
||||
static constexpr inline void
|
||||
lockdebug_remember_recursive_mutex(recursive_mutex_tt<false> *lock) { }
|
||||
static constexpr inline void
|
||||
lockdebug_recursive_mutex_lock(recursive_mutex_tt<false> *lock) { }
|
||||
static inline void
|
||||
static constexpr inline void
|
||||
lockdebug_recursive_mutex_unlock(recursive_mutex_tt<false> *lock) { }
|
||||
static inline void
|
||||
static constexpr inline void
|
||||
lockdebug_recursive_mutex_assert_locked(recursive_mutex_tt<false> *lock) { }
|
||||
static inline void
|
||||
static constexpr inline void
|
||||
lockdebug_recursive_mutex_assert_unlocked(recursive_mutex_tt<false> *lock) { }
|
||||
|
||||
|
||||
extern void lockdebug_rwlock_read(rwlock_tt<true> *lock);
|
||||
extern void lockdebug_rwlock_try_read_success(rwlock_tt<true> *lock);
|
||||
extern void lockdebug_rwlock_unlock_read(rwlock_tt<true> *lock);
|
||||
extern void lockdebug_rwlock_write(rwlock_tt<true> *lock);
|
||||
extern void lockdebug_rwlock_try_write_success(rwlock_tt<true> *lock);
|
||||
extern void lockdebug_rwlock_unlock_write(rwlock_tt<true> *lock);
|
||||
extern void lockdebug_rwlock_assert_reading(rwlock_tt<true> *lock);
|
||||
extern void lockdebug_rwlock_assert_writing(rwlock_tt<true> *lock);
|
||||
extern void lockdebug_rwlock_assert_locked(rwlock_tt<true> *lock);
|
||||
extern void lockdebug_rwlock_assert_unlocked(rwlock_tt<true> *lock);
|
||||
|
||||
static inline void lockdebug_rwlock_read(rwlock_tt<false> *) { }
|
||||
static inline void lockdebug_rwlock_try_read_success(rwlock_tt<false> *) { }
|
||||
static inline void lockdebug_rwlock_unlock_read(rwlock_tt<false> *) { }
|
||||
static inline void lockdebug_rwlock_write(rwlock_tt<false> *) { }
|
||||
static inline void lockdebug_rwlock_try_write_success(rwlock_tt<false> *) { }
|
||||
static inline void lockdebug_rwlock_unlock_write(rwlock_tt<false> *) { }
|
||||
static inline void lockdebug_rwlock_assert_reading(rwlock_tt<false> *) { }
|
||||
static inline void lockdebug_rwlock_assert_writing(rwlock_tt<false> *) { }
|
||||
static inline void lockdebug_rwlock_assert_locked(rwlock_tt<false> *) { }
|
||||
static inline void lockdebug_rwlock_assert_unlocked(rwlock_tt<false> *) { }
|
||||
|
@ -28,117 +28,253 @@
|
||||
|
||||
#include "objc-private.h"
|
||||
|
||||
#if DEBUG && !TARGET_OS_WIN32
|
||||
#if LOCKDEBUG && !TARGET_OS_WIN32
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* Thread-local bool set during _objc_atfork_prepare().
|
||||
* That function is allowed to break some lock ordering rules.
|
||||
**********************************************************************/
|
||||
|
||||
static tls_key_t fork_prepare_tls;
|
||||
|
||||
void
|
||||
lockdebug_setInForkPrepare(bool inForkPrepare)
|
||||
{
|
||||
INIT_ONCE_PTR(fork_prepare_tls, tls_create(nil), (void)0);
|
||||
tls_set(fork_prepare_tls, (void*)inForkPrepare);
|
||||
}
|
||||
|
||||
static bool
|
||||
inForkPrepare()
|
||||
{
|
||||
INIT_ONCE_PTR(fork_prepare_tls, tls_create(nil), (void)0);
|
||||
return (bool)tls_get(fork_prepare_tls);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* Lock order graph.
|
||||
* "lock X precedes lock Y" means that X must be acquired first.
|
||||
* This property is transitive.
|
||||
**********************************************************************/
|
||||
|
||||
struct lockorder {
|
||||
const void *l;
|
||||
std::vector<const lockorder *> predecessors;
|
||||
|
||||
mutable std::unordered_map<const lockorder *, bool> memo;
|
||||
|
||||
lockorder(const void *newl) : l(newl) { }
|
||||
};
|
||||
|
||||
static std::unordered_map<const void*, lockorder *> lockOrderList;
|
||||
// not mutex_t because we don't want lock debugging on this lock
|
||||
static mutex_tt<false> lockOrderLock;
|
||||
|
||||
static bool
|
||||
lockPrecedesLock(const lockorder *oldlock, const lockorder *newlock)
|
||||
{
|
||||
auto memoed = newlock->memo.find(oldlock);
|
||||
if (memoed != newlock->memo.end()) {
|
||||
return memoed->second;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
for (const auto *pre : newlock->predecessors) {
|
||||
if (oldlock == pre || lockPrecedesLock(oldlock, pre)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
newlock->memo[oldlock] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool
|
||||
lockPrecedesLock(const void *oldlock, const void *newlock)
|
||||
{
|
||||
mutex_tt<false>::locker lock(lockOrderLock);
|
||||
|
||||
auto oldorder = lockOrderList.find(oldlock);
|
||||
auto neworder = lockOrderList.find(newlock);
|
||||
if (neworder == lockOrderList.end() || oldorder == lockOrderList.end()) {
|
||||
return false;
|
||||
}
|
||||
return lockPrecedesLock(oldorder->second, neworder->second);
|
||||
}
|
||||
|
||||
static bool
|
||||
lockUnorderedWithLock(const void *oldlock, const void *newlock)
|
||||
{
|
||||
mutex_tt<false>::locker lock(lockOrderLock);
|
||||
|
||||
auto oldorder = lockOrderList.find(oldlock);
|
||||
auto neworder = lockOrderList.find(newlock);
|
||||
if (neworder == lockOrderList.end() || oldorder == lockOrderList.end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lockPrecedesLock(oldorder->second, neworder->second) ||
|
||||
lockPrecedesLock(neworder->second, oldorder->second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void lockdebug_lock_precedes_lock(const void *oldlock, const void *newlock)
|
||||
{
|
||||
if (lockPrecedesLock(newlock, oldlock)) {
|
||||
_objc_fatal("contradiction in lock order declaration");
|
||||
}
|
||||
|
||||
mutex_tt<false>::locker lock(lockOrderLock);
|
||||
|
||||
auto oldorder = lockOrderList.find(oldlock);
|
||||
auto neworder = lockOrderList.find(newlock);
|
||||
if (oldorder == lockOrderList.end()) {
|
||||
lockOrderList[oldlock] = new lockorder(oldlock);
|
||||
oldorder = lockOrderList.find(oldlock);
|
||||
}
|
||||
if (neworder == lockOrderList.end()) {
|
||||
lockOrderList[newlock] = new lockorder(newlock);
|
||||
neworder = lockOrderList.find(newlock);
|
||||
}
|
||||
|
||||
neworder->second->predecessors.push_back(oldorder->second);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* 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;
|
||||
enum class lockkind {
|
||||
MUTEX = 1, MONITOR = 2, RDLOCK = 3, WRLOCK = 4, RECURSIVE = 5
|
||||
};
|
||||
|
||||
#define MUTEX 1
|
||||
#define MONITOR 2
|
||||
#define RDLOCK 3
|
||||
#define WRLOCK 4
|
||||
#define RECURSIVE 5
|
||||
#define MUTEX lockkind::MUTEX
|
||||
#define MONITOR lockkind::MONITOR
|
||||
#define RDLOCK lockkind::RDLOCK
|
||||
#define WRLOCK lockkind::WRLOCK
|
||||
#define RECURSIVE lockkind::RECURSIVE
|
||||
|
||||
typedef struct _objc_lock_list {
|
||||
int allocated;
|
||||
int used;
|
||||
lockcount list[0];
|
||||
} _objc_lock_list;
|
||||
struct lockcount {
|
||||
lockkind k; // the kind of lock it is (MUTEX, MONITOR, etc)
|
||||
int i; // the lock's nest count
|
||||
};
|
||||
|
||||
using objc_lock_list = std::unordered_map<const void *, lockcount>;
|
||||
|
||||
|
||||
// Thread-local list of locks owned by a thread.
|
||||
// Used by lock ownership checks.
|
||||
static tls_key_t lock_tls;
|
||||
|
||||
// Global list of all locks.
|
||||
// Used by fork() safety check.
|
||||
// This can't be a static struct because of C++ initialization order problems.
|
||||
static objc_lock_list& AllLocks() {
|
||||
static objc_lock_list *locks;
|
||||
INIT_ONCE_PTR(locks, new objc_lock_list, (void)0);
|
||||
return *locks;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
destroyLocks(void *value)
|
||||
{
|
||||
_objc_lock_list *locks = (_objc_lock_list *)value;
|
||||
auto locks = (objc_lock_list *)value;
|
||||
// fixme complain about any still-held locks?
|
||||
if (locks) free(locks);
|
||||
if (locks) delete locks;
|
||||
}
|
||||
|
||||
static struct _objc_lock_list *
|
||||
getLocks(BOOL create)
|
||||
static objc_lock_list&
|
||||
ownedLocks()
|
||||
{
|
||||
_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);
|
||||
auto locks = (objc_lock_list *)tls_get(lock_tls);
|
||||
if (!locks) {
|
||||
if (!create) {
|
||||
return NULL;
|
||||
} else {
|
||||
locks = (_objc_lock_list *)calloc(1, sizeof(_objc_lock_list) + sizeof(lockcount) * 16);
|
||||
locks->allocated = 16;
|
||||
locks->used = 0;
|
||||
tls_set(lock_tls, locks);
|
||||
}
|
||||
locks = new objc_lock_list;
|
||||
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(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(oldlocks);
|
||||
}
|
||||
}
|
||||
|
||||
return locks;
|
||||
return *locks;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
hasLock(_objc_lock_list *locks, void *lock, int kind)
|
||||
static bool
|
||||
hasLock(objc_lock_list& locks, const void *lock, lockkind 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;
|
||||
auto iter = locks.find(lock);
|
||||
if (iter != locks.end() && iter->second.k == kind) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
setLock(_objc_lock_list *locks, void *lock, int kind)
|
||||
static const char *sym(const void *lock)
|
||||
{
|
||||
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++;
|
||||
Dl_info info;
|
||||
int ok = dladdr(lock, &info);
|
||||
if (ok && info.dli_sname && info.dli_sname[0]) return info.dli_sname;
|
||||
else return "??";
|
||||
}
|
||||
|
||||
static void
|
||||
clearLock(_objc_lock_list *locks, void *lock, int kind)
|
||||
setLock(objc_lock_list& locks, const void *lock, lockkind 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];
|
||||
// Check if we already own this lock.
|
||||
auto iter = locks.find(lock);
|
||||
if (iter != locks.end() && iter->second.k == kind) {
|
||||
iter->second.i++;
|
||||
return;
|
||||
}
|
||||
|
||||
// Newly-acquired lock. Verify lock ordering.
|
||||
// Locks not in AllLocks are exempt (i.e. @synchronize locks)
|
||||
if (&locks != &AllLocks() && AllLocks().find(lock) != AllLocks().end()) {
|
||||
for (auto& oldlock : locks) {
|
||||
if (AllLocks().find(oldlock.first) == AllLocks().end()) {
|
||||
// oldlock is exempt
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lockPrecedesLock(lock, oldlock.first)) {
|
||||
_objc_fatal("lock %p (%s) incorrectly acquired before %p (%s)",
|
||||
oldlock.first, sym(oldlock.first), lock, sym(lock));
|
||||
}
|
||||
if (!inForkPrepare() &&
|
||||
lockUnorderedWithLock(lock, oldlock.first))
|
||||
{
|
||||
// _objc_atfork_prepare is allowed to acquire
|
||||
// otherwise-unordered locks, but nothing else may.
|
||||
_objc_fatal("lock %p (%s) acquired before %p (%s) "
|
||||
"with no defined lock order",
|
||||
oldlock.first, sym(oldlock.first), lock, sym(lock));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
locks[lock] = lockcount{kind, 1};
|
||||
}
|
||||
|
||||
static void
|
||||
clearLock(objc_lock_list& locks, const void *lock, lockkind kind)
|
||||
{
|
||||
auto iter = locks.find(lock);
|
||||
if (iter != locks.end()) {
|
||||
auto& l = iter->second;
|
||||
if (l.k == kind) {
|
||||
if (--l.i == 0) {
|
||||
locks.erase(iter);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -149,49 +285,61 @@ clearLock(_objc_lock_list *locks, void *lock, int kind)
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* Mutex checking
|
||||
* fork() safety checking
|
||||
**********************************************************************/
|
||||
|
||||
#if !TARGET_OS_SIMULATOR
|
||||
// Non-simulator platforms have lock debugging built into os_unfair_lock.
|
||||
|
||||
|
||||
void
|
||||
lockdebug_mutex_lock(mutex_t *lock)
|
||||
lockdebug_remember_mutex(mutex_t *lock)
|
||||
{
|
||||
// empty
|
||||
setLock(AllLocks(), lock, MUTEX);
|
||||
}
|
||||
|
||||
void
|
||||
lockdebug_mutex_unlock(mutex_t *lock)
|
||||
lockdebug_remember_recursive_mutex(recursive_mutex_t *lock)
|
||||
{
|
||||
// empty
|
||||
setLock(AllLocks(), lock, RECURSIVE);
|
||||
}
|
||||
|
||||
void
|
||||
lockdebug_mutex_assert_locked(mutex_t *lock)
|
||||
lockdebug_remember_monitor(monitor_t *lock)
|
||||
{
|
||||
os_unfair_lock_assert_owner((os_unfair_lock *)lock);
|
||||
setLock(AllLocks(), lock, MONITOR);
|
||||
}
|
||||
|
||||
void
|
||||
lockdebug_mutex_assert_unlocked(mutex_t *lock)
|
||||
lockdebug_assert_all_locks_locked()
|
||||
{
|
||||
os_unfair_lock_assert_not_owner((os_unfair_lock *)lock);
|
||||
auto& owned = ownedLocks();
|
||||
|
||||
for (const auto& l : AllLocks()) {
|
||||
if (!hasLock(owned, l.first, l.second.k)) {
|
||||
_objc_fatal("lock %p:%d is incorrectly not owned",
|
||||
l.first, l.second.k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lockdebug_assert_no_locks_locked()
|
||||
{
|
||||
auto& owned = ownedLocks();
|
||||
|
||||
for (const auto& l : AllLocks()) {
|
||||
if (hasLock(owned, l.first, l.second.k)) {
|
||||
_objc_fatal("lock %p:%d is incorrectly owned", l.first, l.second.k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// !TARGET_OS_SIMULATOR
|
||||
#else
|
||||
// TARGET_OS_SIMULATOR
|
||||
|
||||
// Simulator platforms have no built-in lock debugging in os_unfair_lock.
|
||||
|
||||
/***********************************************************************
|
||||
* Mutex checking
|
||||
**********************************************************************/
|
||||
|
||||
void
|
||||
lockdebug_mutex_lock(mutex_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(YES);
|
||||
auto& locks = ownedLocks();
|
||||
|
||||
if (hasLock(locks, lock, MUTEX)) {
|
||||
_objc_fatal("deadlock: relocking mutex");
|
||||
@ -205,14 +353,14 @@ lockdebug_mutex_lock(mutex_t *lock)
|
||||
void
|
||||
lockdebug_mutex_try_lock_success(mutex_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(YES);
|
||||
auto& locks = ownedLocks();
|
||||
setLock(locks, lock, MUTEX);
|
||||
}
|
||||
|
||||
void
|
||||
lockdebug_mutex_unlock(mutex_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
auto& locks = ownedLocks();
|
||||
|
||||
if (!hasLock(locks, lock, MUTEX)) {
|
||||
_objc_fatal("unlocking unowned mutex");
|
||||
@ -224,7 +372,7 @@ lockdebug_mutex_unlock(mutex_t *lock)
|
||||
void
|
||||
lockdebug_mutex_assert_locked(mutex_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
auto& locks = ownedLocks();
|
||||
|
||||
if (!hasLock(locks, lock, MUTEX)) {
|
||||
_objc_fatal("mutex incorrectly not locked");
|
||||
@ -234,7 +382,7 @@ lockdebug_mutex_assert_locked(mutex_t *lock)
|
||||
void
|
||||
lockdebug_mutex_assert_unlocked(mutex_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
auto& locks = ownedLocks();
|
||||
|
||||
if (hasLock(locks, lock, MUTEX)) {
|
||||
_objc_fatal("mutex incorrectly locked");
|
||||
@ -242,24 +390,21 @@ lockdebug_mutex_assert_unlocked(mutex_t *lock)
|
||||
}
|
||||
|
||||
|
||||
// TARGET_OS_SIMULATOR
|
||||
#endif
|
||||
|
||||
/***********************************************************************
|
||||
* Recursive mutex checking
|
||||
**********************************************************************/
|
||||
|
||||
void
|
||||
lockdebug_recursive_mutex_lock(recursive_mutex_tt<true> *lock)
|
||||
lockdebug_recursive_mutex_lock(recursive_mutex_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(YES);
|
||||
auto& locks = ownedLocks();
|
||||
setLock(locks, lock, RECURSIVE);
|
||||
}
|
||||
|
||||
void
|
||||
lockdebug_recursive_mutex_unlock(recursive_mutex_tt<true> *lock)
|
||||
lockdebug_recursive_mutex_unlock(recursive_mutex_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
auto& locks = ownedLocks();
|
||||
|
||||
if (!hasLock(locks, lock, RECURSIVE)) {
|
||||
_objc_fatal("unlocking unowned recursive mutex");
|
||||
@ -269,9 +414,9 @@ lockdebug_recursive_mutex_unlock(recursive_mutex_tt<true> *lock)
|
||||
|
||||
|
||||
void
|
||||
lockdebug_recursive_mutex_assert_locked(recursive_mutex_tt<true> *lock)
|
||||
lockdebug_recursive_mutex_assert_locked(recursive_mutex_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
auto& locks = ownedLocks();
|
||||
|
||||
if (!hasLock(locks, lock, RECURSIVE)) {
|
||||
_objc_fatal("recursive mutex incorrectly not locked");
|
||||
@ -279,9 +424,9 @@ lockdebug_recursive_mutex_assert_locked(recursive_mutex_tt<true> *lock)
|
||||
}
|
||||
|
||||
void
|
||||
lockdebug_recursive_mutex_assert_unlocked(recursive_mutex_tt<true> *lock)
|
||||
lockdebug_recursive_mutex_assert_unlocked(recursive_mutex_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
auto& locks = ownedLocks();
|
||||
|
||||
if (hasLock(locks, lock, RECURSIVE)) {
|
||||
_objc_fatal("recursive mutex incorrectly locked");
|
||||
@ -296,7 +441,7 @@ lockdebug_recursive_mutex_assert_unlocked(recursive_mutex_tt<true> *lock)
|
||||
void
|
||||
lockdebug_monitor_enter(monitor_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(YES);
|
||||
auto& locks = ownedLocks();
|
||||
|
||||
if (hasLock(locks, lock, MONITOR)) {
|
||||
_objc_fatal("deadlock: relocking monitor");
|
||||
@ -307,7 +452,7 @@ lockdebug_monitor_enter(monitor_t *lock)
|
||||
void
|
||||
lockdebug_monitor_leave(monitor_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
auto& locks = ownedLocks();
|
||||
|
||||
if (!hasLock(locks, lock, MONITOR)) {
|
||||
_objc_fatal("unlocking unowned monitor");
|
||||
@ -318,7 +463,7 @@ lockdebug_monitor_leave(monitor_t *lock)
|
||||
void
|
||||
lockdebug_monitor_wait(monitor_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
auto& locks = ownedLocks();
|
||||
|
||||
if (!hasLock(locks, lock, MONITOR)) {
|
||||
_objc_fatal("waiting in unowned monitor");
|
||||
@ -329,7 +474,7 @@ lockdebug_monitor_wait(monitor_t *lock)
|
||||
void
|
||||
lockdebug_monitor_assert_locked(monitor_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
auto& locks = ownedLocks();
|
||||
|
||||
if (!hasLock(locks, lock, MONITOR)) {
|
||||
_objc_fatal("monitor incorrectly not locked");
|
||||
@ -339,133 +484,11 @@ lockdebug_monitor_assert_locked(monitor_t *lock)
|
||||
void
|
||||
lockdebug_monitor_assert_unlocked(monitor_t *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
auto& locks = ownedLocks();
|
||||
|
||||
if (hasLock(locks, lock, MONITOR)) {
|
||||
_objc_fatal("monitor incorrectly held");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* rwlock checking
|
||||
**********************************************************************/
|
||||
|
||||
void
|
||||
lockdebug_rwlock_read(rwlock_tt<true> *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(YES);
|
||||
|
||||
if (hasLock(locks, lock, RDLOCK)) {
|
||||
// Recursive rwlock read is bad (may deadlock vs pending writer)
|
||||
_objc_fatal("recursive rwlock read");
|
||||
}
|
||||
if (hasLock(locks, lock, WRLOCK)) {
|
||||
_objc_fatal("deadlock: read after write for rwlock");
|
||||
}
|
||||
setLock(locks, lock, RDLOCK);
|
||||
}
|
||||
|
||||
// try-read success is the only case with lockdebug effects.
|
||||
// try-read when already reading is OK (won't deadlock)
|
||||
// try-read when already writing is OK (will fail)
|
||||
// try-read failure does nothing.
|
||||
void
|
||||
lockdebug_rwlock_try_read_success(rwlock_tt<true> *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(YES);
|
||||
setLock(locks, lock, RDLOCK);
|
||||
}
|
||||
|
||||
void
|
||||
lockdebug_rwlock_unlock_read(rwlock_tt<true> *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
|
||||
if (!hasLock(locks, lock, RDLOCK)) {
|
||||
_objc_fatal("un-reading unowned rwlock");
|
||||
}
|
||||
clearLock(locks, lock, RDLOCK);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lockdebug_rwlock_write(rwlock_tt<true> *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(YES);
|
||||
|
||||
if (hasLock(locks, lock, RDLOCK)) {
|
||||
// Lock promotion not allowed (may deadlock)
|
||||
_objc_fatal("deadlock: write after read for rwlock");
|
||||
}
|
||||
if (hasLock(locks, lock, WRLOCK)) {
|
||||
_objc_fatal("recursive rwlock write");
|
||||
}
|
||||
setLock(locks, lock, WRLOCK);
|
||||
}
|
||||
|
||||
// try-write success is the only case with lockdebug effects.
|
||||
// try-write when already reading is OK (will fail)
|
||||
// try-write when already writing is OK (will fail)
|
||||
// try-write failure does nothing.
|
||||
void
|
||||
lockdebug_rwlock_try_write_success(rwlock_tt<true> *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(YES);
|
||||
setLock(locks, lock, WRLOCK);
|
||||
}
|
||||
|
||||
void
|
||||
lockdebug_rwlock_unlock_write(rwlock_tt<true> *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
|
||||
if (!hasLock(locks, lock, WRLOCK)) {
|
||||
_objc_fatal("un-writing unowned rwlock");
|
||||
}
|
||||
clearLock(locks, lock, WRLOCK);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
lockdebug_rwlock_assert_reading(rwlock_tt<true> *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
|
||||
if (!hasLock(locks, lock, RDLOCK)) {
|
||||
_objc_fatal("rwlock incorrectly not reading");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lockdebug_rwlock_assert_writing(rwlock_tt<true> *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
|
||||
if (!hasLock(locks, lock, WRLOCK)) {
|
||||
_objc_fatal("rwlock incorrectly not writing");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lockdebug_rwlock_assert_locked(rwlock_tt<true> *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
|
||||
if (!hasLock(locks, lock, RDLOCK) && !hasLock(locks, lock, WRLOCK)) {
|
||||
_objc_fatal("rwlock incorrectly neither reading nor writing");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lockdebug_rwlock_assert_unlocked(rwlock_tt<true> *lock)
|
||||
{
|
||||
_objc_lock_list *locks = getLocks(NO);
|
||||
|
||||
if (hasLock(locks, lock, RDLOCK) || hasLock(locks, lock, WRLOCK)) {
|
||||
_objc_fatal("rwlock incorrectly not unlocked");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
38
runtime/objc-locks-new.h
Normal file
38
runtime/objc-locks-new.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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-locks-new.h
|
||||
* Declarations of all locks used in the runtime.
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _OBJC_LOCKS_NEW_H
|
||||
#define _OBJC_LOCKS_NEW_H
|
||||
|
||||
// fork() safety requires careful tracking of all locks used in the runtime.
|
||||
// Thou shalt not declare any locks outside this file.
|
||||
|
||||
extern mutex_t runtimeLock;
|
||||
extern mutex_t DemangleCacheLock;
|
||||
|
||||
#endif
|
40
runtime/objc-locks-old.h
Normal file
40
runtime/objc-locks-old.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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-locks-old.h
|
||||
* Declarations of all locks used in the runtime.
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _OBJC_LOCKS_OLD_H
|
||||
#define _OBJC_LOCKS_OLD_H
|
||||
|
||||
// fork() safety requires careful tracking of all locks used in the runtime.
|
||||
// Thou shalt not declare any locks outside this file.
|
||||
|
||||
extern mutex_t classLock;
|
||||
extern mutex_t methodListLock;
|
||||
extern mutex_t NXUniqueStringLock;
|
||||
extern spinlock_t impLock;
|
||||
|
||||
#endif
|
68
runtime/objc-locks.h
Normal file
68
runtime/objc-locks.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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-locks.h
|
||||
* Declarations of all locks used in the runtime.
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _OBJC_LOCKS_H
|
||||
#define _OBJC_LOCKS_H
|
||||
|
||||
// fork() safety requires careful tracking of all locks used in the runtime.
|
||||
// Thou shalt not declare any locks outside this file.
|
||||
|
||||
// Lock ordering is declared in _objc_fork_prepare()
|
||||
// and is enforced by lockdebug.
|
||||
|
||||
extern monitor_t classInitLock;
|
||||
extern mutex_t selLock;
|
||||
#if CONFIG_USE_CACHE_LOCK
|
||||
extern mutex_t cacheUpdateLock;
|
||||
#endif
|
||||
extern recursive_mutex_t loadMethodLock;
|
||||
extern mutex_t crashlog_lock;
|
||||
extern spinlock_t objcMsgLogLock;
|
||||
extern mutex_t AltHandlerDebugLock;
|
||||
extern mutex_t AssociationsManagerLock;
|
||||
extern StripedMap<spinlock_t> PropertyLocks;
|
||||
extern StripedMap<spinlock_t> StructLocks;
|
||||
extern StripedMap<spinlock_t> CppObjectLocks;
|
||||
|
||||
// SideTable lock is buried awkwardly. Call a function to manipulate it.
|
||||
extern void SideTableLockAll();
|
||||
extern void SideTableUnlockAll();
|
||||
extern void SideTableForceResetAll();
|
||||
extern void SideTableDefineLockOrder();
|
||||
extern void SideTableLocksPrecedeLock(const void *newlock);
|
||||
extern void SideTableLocksSucceedLock(const void *oldlock);
|
||||
extern void SideTableLocksPrecedeLocks(StripedMap<spinlock_t>& newlocks);
|
||||
extern void SideTableLocksSucceedLocks(StripedMap<spinlock_t>& oldlocks);
|
||||
|
||||
#if __OBJC2__
|
||||
#include "objc-locks-new.h"
|
||||
#else
|
||||
#include "objc-locks-old.h"
|
||||
#endif
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user