_object_cxxDestruct
_object_cxxDestructFromClass
_class_copyIvarList
-__object_addExternalReference_rr
__objc_rootRetain_slow
__objc_rootReleaseWasZero_slow
_object_copy
#include <mach-o/arch.h>
#include <mach-o/loader.h>
-// from "objc-private.h"
-// masks for objc_image_info.flags
-#define OBJC_IMAGE_SUPPORTS_GC (1<<1)
-
// Some OS X SDKs don't define these.
#ifndef CPU_TYPE_ARM
#define CPU_TYPE_ARM ((cpu_type_t) 12)
template <typename P>
-void dosect(uint8_t *start, macho_section<P> *sect, bool isOldABI, bool isOSX)
+void dosect(uint8_t *start, macho_section<P> *sect)
{
if (debug) printf("section %.16s from segment %.16s\n",
sect->sectname(), sect->segname());
- if (isOSX) {
- // Add "supports GC" flag to objc image info
- if ((segnameStartsWith(sect->segname(), "__DATA") &&
- sectnameEquals(sect->sectname(), "__objc_imageinfo")) ||
- (segnameEquals(sect->segname(), "__OBJC") &&
- sectnameEquals(sect->sectname(), "__image_info")))
- {
- imageinfo *ii = (imageinfo*)(start + sect->offset());
- P::E::set32(ii->flags, P::E::get32(ii->flags) | OBJC_IMAGE_SUPPORTS_GC);
- if (debug) printf("added GC support flag\n");
- }
+ // Strip S_MOD_INIT/TERM_FUNC_POINTERS. We don't want dyld to call
+ // our init funcs because it is too late, and we don't want anyone to
+ // call our term funcs ever.
+ if (segnameStartsWith(sect->segname(), "__DATA") &&
+ sectnameEquals(sect->sectname(), "__mod_init_func"))
+ {
+ // section type 0 is S_REGULAR
+ sect->set_flags(sect->flags() & ~SECTION_TYPE);
+ sect->set_sectname("__objc_init_func");
+ if (debug) printf("disabled __mod_init_func section\n");
}
-
- if (isOldABI) {
- // Keep init funcs because libSystem doesn't call _objc_init().
- } else {
- // Strip S_MOD_INIT/TERM_FUNC_POINTERS. We don't want dyld to call
- // our init funcs because it is too late, and we don't want anyone to
- // call our term funcs ever.
- if (segnameStartsWith(sect->segname(), "__DATA") &&
- sectnameEquals(sect->sectname(), "__mod_init_func"))
- {
- // section type 0 is S_REGULAR
- sect->set_flags(sect->flags() & ~SECTION_TYPE);
- sect->set_sectname("__objc_init_func");
- if (debug) printf("disabled __mod_init_func section\n");
- }
- if (segnameStartsWith(sect->segname(), "__DATA") &&
- sectnameEquals(sect->sectname(), "__mod_term_func"))
- {
- // section type 0 is S_REGULAR
- sect->set_flags(sect->flags() & ~SECTION_TYPE);
- sect->set_sectname("__objc_term_func");
- if (debug) printf("disabled __mod_term_func section\n");
- }
+ if (segnameStartsWith(sect->segname(), "__DATA") &&
+ sectnameEquals(sect->sectname(), "__mod_term_func"))
+ {
+ // section type 0 is S_REGULAR
+ sect->set_flags(sect->flags() & ~SECTION_TYPE);
+ sect->set_sectname("__objc_term_func");
+ if (debug) printf("disabled __mod_term_func section\n");
}
}
template <typename P>
-void doseg(uint8_t *start, macho_segment_command<P> *seg,
- bool isOldABI, bool isOSX)
+void doseg(uint8_t *start, macho_segment_command<P> *seg)
{
if (debug) printf("segment name: %.16s, nsects %u\n",
seg->segname(), seg->nsects());
macho_section<P> *sect = (macho_section<P> *)(seg + 1);
for (uint32_t i = 0; i < seg->nsects(); ++i) {
- dosect(start, §[i], isOldABI, isOSX);
+ dosect(start, §[i]);
}
}
bool parse_macho(uint8_t *buffer)
{
macho_header<P>* mh = (macho_header<P>*)buffer;
- uint8_t *cmds;
-
- bool isOldABI = false;
- bool isOSX = false;
- cmds = (uint8_t *)(mh + 1);
- for (uint32_t c = 0; c < mh->ncmds(); c++) {
- macho_load_command<P>* cmd = (macho_load_command<P>*)cmds;
- cmds += cmd->cmdsize();
- if (cmd->cmd() == LC_SEGMENT || cmd->cmd() == LC_SEGMENT_64) {
- macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
- if (segnameEquals(seg->segname(), "__OBJC")) isOldABI = true;
- }
- else if (cmd->cmd() == LC_VERSION_MIN_MACOSX) {
- isOSX = true;
- }
- }
-
- if (debug) printf("ABI=%s, OS=%s\n",
- isOldABI ? "old" : "new", isOSX ? "osx" : "ios");
-
- cmds = (uint8_t *)(mh + 1);
+ uint8_t *cmds = (uint8_t *)(mh + 1);
for (uint32_t c = 0; c < mh->ncmds(); c++) {
macho_load_command<P>* cmd = (macho_load_command<P>*)cmds;
cmds += cmd->cmdsize();
if (cmd->cmd() == LC_SEGMENT || cmd->cmd() == LC_SEGMENT_64) {
- doseg(buffer, (macho_segment_command<P>*)cmd, isOldABI, isOSX);
+ doseg(buffer, (macho_segment_command<P>*)cmd);
}
}
/* Begin PBXBuildFile section */
393CEAC00DC69E3E000B69DE /* objc-references.mm in Sources */ = {isa = PBXBuildFile; fileRef = 393CEABF0DC69E3E000B69DE /* objc-references.mm */; };
393CEAC60DC69E67000B69DE /* objc-references.h in Headers */ = {isa = PBXBuildFile; fileRef = 393CEAC50DC69E67000B69DE /* objc-references.h */; };
- 399BC72E1224831B007FBDF0 /* objc-externalref.mm in Sources */ = {isa = PBXBuildFile; fileRef = 399BC72D1224831B007FBDF0 /* objc-externalref.mm */; };
39ABD72312F0B61800D1054C /* objc-weak.h in Headers */ = {isa = PBXBuildFile; fileRef = 39ABD71F12F0B61800D1054C /* objc-weak.h */; };
39ABD72412F0B61800D1054C /* objc-weak.mm in Sources */ = {isa = PBXBuildFile; fileRef = 39ABD72012F0B61800D1054C /* objc-weak.mm */; };
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 */; };
- 830F2A940D73876100392440 /* objc-accessors.h in Headers */ = {isa = PBXBuildFile; fileRef = 830F2A920D73876100392440 /* objc-accessors.h */; };
830F2A950D73876100392440 /* objc-accessors.mm in Sources */ = {isa = PBXBuildFile; fileRef = 830F2A930D73876100392440 /* objc-accessors.mm */; };
830F2A980D738DC200392440 /* hashtable.h in Headers */ = {isa = PBXBuildFile; fileRef = 830F2A970D738DC200392440 /* hashtable.h */; settings = {ATTRIBUTES = (Public, ); }; };
83112ED40F00599600A5FBAF /* objc-internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 83112ED30F00599600A5FBAF /* objc-internal.h */; settings = {ATTRIBUTES = (Private, ); }; };
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 */; };
- BC07A00C0EF72D360014EC61 /* objc-auto-dump.h in Headers */ = {isa = PBXBuildFile; fileRef = BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */; settings = {ATTRIBUTES = (Private, ); }; };
- BC07A0110EF72D9C0014EC61 /* objc-auto-dump.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC07A0100EF72D9C0014EC61 /* objc-auto-dump.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 */; };
/* Begin PBXFileReference section */
393CEABF0DC69E3E000B69DE /* objc-references.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-references.mm"; path = "runtime/objc-references.mm"; sourceTree = "<group>"; };
393CEAC50DC69E67000B69DE /* objc-references.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-references.h"; path = "runtime/objc-references.h"; sourceTree = "<group>"; };
- 399BC72D1224831B007FBDF0 /* objc-externalref.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-externalref.mm"; path = "runtime/objc-externalref.mm"; sourceTree = "<group>"; };
39ABD71F12F0B61800D1054C /* objc-weak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-weak.h"; path = "runtime/objc-weak.h"; sourceTree = "<group>"; };
39ABD72012F0B61800D1054C /* objc-weak.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-weak.mm"; path = "runtime/objc-weak.mm"; sourceTree = "<group>"; };
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; };
- 830F2A920D73876100392440 /* objc-accessors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-accessors.h"; path = "runtime/objc-accessors.h"; sourceTree = "<group>"; };
830F2A930D73876100392440 /* objc-accessors.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-accessors.mm"; path = "runtime/objc-accessors.mm"; sourceTree = "<group>"; };
830F2A970D738DC200392440 /* hashtable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hashtable.h; path = runtime/hashtable.h; sourceTree = "<group>"; };
830F2AA50D7394C200392440 /* markgc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = markgc.cpp; sourceTree = "<group>"; };
83F550DF155E030800E95D3B /* objc-cache-old.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-cache-old.mm"; path = "runtime/objc-cache-old.mm"; sourceTree = "<group>"; };
87BB4E900EC39633005D08E1 /* objc-probes.d */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.dtrace; name = "objc-probes.d"; path = "runtime/objc-probes.d"; sourceTree = "<group>"; };
9672F7ED14D5F488007CEC96 /* NSObject.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = NSObject.mm; path = runtime/NSObject.mm; sourceTree = "<group>"; };
- BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-auto-dump.h"; path = "runtime/objc-auto-dump.h"; sourceTree = "<group>"; };
- BC07A0100EF72D9C0014EC61 /* objc-auto-dump.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-auto-dump.mm"; path = "runtime/objc-auto-dump.mm"; sourceTree = "<group>"; };
BC8B5D1212D3D48100C78A5B /* libauto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libauto.dylib; path = /usr/lib/libauto.dylib; sourceTree = "<absolute>"; };
D2AAC0630554660B00DB518D /* libobjc.A.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libobjc.A.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
E8923D9C116AB2820071B552 /* a1a2-blocktramps-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = "a1a2-blocktramps-i386.s"; path = "runtime/a1a2-blocktramps-i386.s"; sourceTree = "<group>"; };
838486190D6D68A800CEA253 /* Protocol.mm */,
830F2A930D73876100392440 /* objc-accessors.mm */,
838485CA0D6D68A200CEA253 /* objc-auto.mm */,
- BC07A0100EF72D9C0014EC61 /* objc-auto-dump.mm */,
39ABD72012F0B61800D1054C /* objc-weak.mm */,
E8923DA0116AB2820071B552 /* objc-block-trampolines.mm */,
838485CB0D6D68A200CEA253 /* objc-cache.mm */,
838485CE0D6D68A200CEA253 /* objc-class.mm */,
838485D00D6D68A200CEA253 /* objc-errors.mm */,
838485D20D6D68A200CEA253 /* objc-exception.mm */,
- 399BC72D1224831B007FBDF0 /* objc-externalref.mm */,
838485D30D6D68A200CEA253 /* objc-file.mm */,
83BE02E30FCCB23400661494 /* objc-file-old.mm */,
838485D50D6D68A200CEA253 /* objc-initialize.mm */,
83112ED30F00599600A5FBAF /* objc-internal.h */,
834EC0A311614167009B2563 /* objc-abi.h */,
838485BB0D6D687300CEA253 /* maptable.h */,
- BC07A00B0EF72D360014EC61 /* objc-auto-dump.h */,
834266D70E665A8B002E4DA2 /* objc-gdb.h */,
);
name = "Private Headers";
8384862A0D6D6ABC00CEA253 /* Project Headers */ = {
isa = PBXGroup;
children = (
- 830F2A920D73876100392440 /* objc-accessors.h */,
838485CF0D6D68A200CEA253 /* objc-config.h */,
83BE02E60FCCB24D00661494 /* objc-file.h */,
83BE02E50FCCB24D00661494 /* objc-file-old.h */,
838485C30D6D687300CEA253 /* maptable.h in Headers */,
838486280D6D6A2400CEA253 /* message.h in Headers */,
834EC0A411614167009B2563 /* objc-abi.h in Headers */,
- 830F2A940D73876100392440 /* objc-accessors.h in Headers */,
838485EF0D6D68A200CEA253 /* objc-api.h in Headers */,
- BC07A00C0EF72D360014EC61 /* objc-auto-dump.h in Headers */,
838485F00D6D68A200CEA253 /* objc-auto.h in Headers */,
838485F40D6D68A200CEA253 /* objc-class.h in Headers */,
838485F60D6D68A200CEA253 /* objc-config.h in Headers */,
830F2AB60D739AB600392440 /* Run Script (markgc) */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
- comments = "Set the GC-supported bit in libobjc itself.\n\nlibobjc cannot be built with -fobjc-gc, because it needs more precise control over write-barrier use.\n\nThis is done on all architectures and platforms, even though some don't actually support GC. In those cases, a program that actually tries to use GC will fail with link errors.";
+ comments = "Modify the built dylib (mod_init_funcs and mod_term_funcs).";
files = (
);
inputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "set -x\n/usr/bin/xcrun -toolchain XcodeDefault -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 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\"";
};
830F2AFA0D73BC5800392440 /* Run Script (symlink) */ = {
isa = PBXShellScriptBuildPhase;
393CEAC00DC69E3E000B69DE /* objc-references.mm in Sources */,
831C85D60E10CF850066E64C /* objc-os.mm in Sources */,
87BB4EA70EC39854005D08E1 /* objc-probes.d in Sources */,
- BC07A0110EF72D9C0014EC61 /* objc-auto-dump.mm in Sources */,
83BE02E40FCCB23400661494 /* objc-file-old.mm in Sources */,
E8923DA1116AB2820071B552 /* a1a2-blocktramps-i386.s in Sources */,
E8923DA2116AB2820071B552 /* a1a2-blocktramps-x86_64.s in Sources */,
83EB007B121C9EC200B92C16 /* objc-sel-table.s in Sources */,
8383A3A3122600E9009290B8 /* a1a2-blocktramps-arm.s in Sources */,
8383A3A4122600E9009290B8 /* a2a3-blocktramps-arm.s in Sources */,
- 399BC72E1224831B007FBDF0 /* objc-externalref.mm in Sources */,
39ABD72412F0B61800D1054C /* objc-weak.mm in Sources */,
83D49E4F13C7C84F0057F1DD /* objc-msg-arm64.s in Sources */,
8379996E13CBAF6F007C2B5F /* a1a2-blocktramps-arm64.s in Sources */,
EXECUTABLE_PREFIX = lib;
GCC_CW_ASM_SYNTAX = NO;
GCC_OPTIMIZATION_LEVEL = 0;
- GCC_THREADSAFE_STATICS = NO;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
HEADER_SEARCH_PATHS = (
"$(DSTROOT)/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;
ORDER_FILE = "$(SDKROOT)/AppleInternal/OrderFiles/libobjc.order";
DYLIB_CURRENT_VERSION = 228;
EXECUTABLE_PREFIX = lib;
GCC_CW_ASM_SYNTAX = NO;
- GCC_THREADSAFE_STATICS = NO;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
HEADER_SEARCH_PATHS = (
"$(DSTROOT)/usr/include/**",
"$(DSTROOT)/usr/local/include/**",
"$(CONFIGURATION_BUILD_DIR)/usr/include/**",
"$(CONFIGURATION_BUILD_DIR)/usr/local/include/**",
+ /System/Library/Frameworks/System.framework/PrivateHeaders,
);
INSTALL_PATH = /usr/lib;
ORDER_FILE = "$(SDKROOT)/AppleInternal/OrderFiles/libobjc.order";
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;
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;
#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) \
.non_lazy_symbol_pointer ;\
-L ## var ## $$non_lazy_ptr: ;\
+L##var##$$non_lazy_ptr: ;\
.indirect_symbol var ;\
.long 0
#define MI_GET_EXTERN(reg,var) \
- movw reg, :lower16:(L##var##$$non_lazy_ptr-4f-4) ;\
- movt reg, :upper16:(L##var##$$non_lazy_ptr-4f-4) ;\
-4: add reg, pc ;\
+ movw reg, :lower16:(L##var##$$non_lazy_ptr-7f-4) ;\
+ movt reg, :upper16:(L##var##$$non_lazy_ptr-7f-4) ;\
+7: add reg, pc ;\
ldr reg, [reg]
-
-#define MI_CALL_EXTERNAL(var) \
- MI_GET_EXTERN(r12,var) ;\
- blx r12
-
#define MI_GET_ADDRESS(reg,var) \
- movw reg, :lower16:(var-4f-4) ;\
- movt reg, :upper16:(var-4f-4) ;\
-4: add reg, pc ;\
+ movw reg, :lower16:(var-7f-4) ;\
+ movt reg, :upper16:(var-7f-4) ;\
+7: add reg, pc ;\
-MI_EXTERN(__class_lookupMethodAndLoadCache3)
-MI_EXTERN(___objc_error)
+.data
+
+#if SUPPORT_INDEXED_ISA
+
+ .align 2
+ .globl _objc_indexed_classes
+_objc_indexed_classes:
+ .fill ISA_INDEX_COUNT, 4, 0
+
+#endif
-.data
// _objc_entryPoints and _objc_exitPoints are used by method dispatch
// caching code to figure out whether any threads are actively
.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 LGetImpExit
- .long LMsgSendExit
- .long LMsgSendStretExit
- .long LMsgSendSuperExit
- .long LMsgSendSuperStretExit
- .long LMsgSendSuper2Exit
- .long LMsgSendSuper2StretExit
+ .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
// contents populated by the macros below
.macro MESSENGER_START
-4:
+7:
.section __DATA,__objc_msg_break
- .long 4b
+ .long 7b
.long ENTER
.text
.endmacro
.macro MESSENGER_END_FAST
-4:
+7:
.section __DATA,__objc_msg_break
- .long 4b
+ .long 7b
.long FAST_EXIT
.text
.endmacro
.macro MESSENGER_END_SLOW
-4:
+7:
.section __DATA,__objc_msg_break
- .long 4b
+ .long 7b
.long SLOW_EXIT
.text
.endmacro
.macro MESSENGER_END_NIL
-4:
+7:
.section __DATA,__objc_msg_break
- .long 4b
+ .long 7b
.long NIL_EXIT
.text
.endmacro
/********************************************************************
* Names for relative labels
* DO NOT USE THESE LABELS ELSEWHERE
- * Reserved labels: 8: 9:
+ * Reserved labels: 6: 7: 8: 9:
********************************************************************/
-#define LCacheMiss 8
-#define LCacheMiss_f 8f
-#define LCacheMiss_b 8b
+// 6: used by CacheLookup
+// 7: used by MI_GET_ADDRESS etc and MESSENGER_START etc
+// 8: used by CacheLookup
#define LNilReceiver 9
#define LNilReceiver_f 9f
#define LNilReceiver_b 9b
********************************************************************/
#define NORMAL 0
-#define FPRET 1
-#define FP2RET 2
-#define GETIMP 3
-#define STRET 4
-#define SUPER 5
-#define SUPER2 6
-#define SUPER_STRET 7
-#define SUPER2_STRET 8
+#define STRET 1
/********************************************************************
.text
.thumb
.align 5
- .globl _$0
+ .globl $0
.thumb_func
-_$0:
+$0:
.endmacro
.macro STATIC_ENTRY /*name*/
.text
.thumb
.align 5
- .private_extern _$0
+ .private_extern $0
.thumb_func
-_$0:
+$0:
.endmacro
//////////////////////////////////////////////////////////////////////
.macro END_ENTRY /* name */
+LExit$0:
.endmacro
/////////////////////////////////////////////////////////////////////
//
-// CacheLookup return-type
+// CacheLookup NORMAL|STRET
+// CacheLookup2 NORMAL|STRET
//
// Locate the implementation for a selector in a class's method cache.
//
// Takes:
-// $0 = NORMAL, STRET, SUPER, SUPER_STRET, SUPER2, SUPER2_STRET, GETIMP
+// $0 = NORMAL, STRET
// r0 or r1 (STRET) = receiver
// r1 or r2 (STRET) = selector
// r9 = class to search in
//
-// On exit: r9 and r12 clobbered
-// (found) calls or returns IMP, eq/ne/r9 set for forwarding
-// (not found) jumps to LCacheMiss
+// On exit: r9 clobbered
+// (found) continues after CacheLookup, IMP in r12, eq set
+// (not found) continues after CacheLookup2
//
/////////////////////////////////////////////////////////////////////
-.macro CacheHit
-
-.if $0 == GETIMP
- ldr r0, [r9, #4] // r0 = bucket->imp
- MI_GET_ADDRESS(r1, __objc_msgSend_uncached_impcache)
- teq r0, r1
- it eq
- moveq r0, #0 // don't return msgSend_uncached
- bx lr // return imp
-.elseif $0 == NORMAL
- ldr r12, [r9, #4] // r12 = bucket->imp
- // eq already set for nonstret forward
- MESSENGER_END_FAST
- bx r12 // call imp
-.elseif $0 == STRET
- ldr r12, [r9, #4] // r12 = bucket->imp
- movs r9, #1 // r9=1, Z=0 for stret forwarding
- MESSENGER_END_FAST
- bx r12 // call imp
-.elseif $0 == SUPER
- ldr r12, [r9, #4] // r12 = bucket->imp
- ldr r9, [r0, #CLASS] // r9 = class to search for forwarding
- ldr r0, [r0, #RECEIVER] // fetch real receiver
- tst r12, r12 // set ne for forwarding (r12!=0)
- MESSENGER_END_FAST
- bx r12 // call imp
-.elseif $0 == SUPER2
- ldr r12, [r9, #4] // r12 = bucket->imp
- ldr r9, [r0, #CLASS]
- ldr r9, [r9, #SUPERCLASS] // r9 = class to search for forwarding
- ldr r0, [r0, #RECEIVER] // fetch real receiver
- tst r12, r12 // set ne for forwarding (r12!=0)
- MESSENGER_END_FAST
- bx r12 // call imp
-.elseif $0 == SUPER_STRET
- ldr r12, [r9, #4] // r12 = bucket->imp
- ldr r9, [r1, #CLASS] // r9 = class to search for forwarding
- orr r9, r9, #1 // r9 = class|1 for super_stret forward
- ldr r1, [r1, #RECEIVER] // fetch real receiver
- tst r12, r12 // set ne for forwarding (r12!=0)
- MESSENGER_END_FAST
- bx r12 // call imp
-.elseif $0 == SUPER2_STRET
- ldr r12, [r9, #4] // r12 = bucket->imp
- ldr r9, [r1, #CLASS] // r9 = class to search for forwarding
- ldr r9, [r9, #SUPERCLASS] // r9 = class to search for forwarding
- orr r9, r9, #1 // r9 = class|1 for super_stret forward
- ldr r1, [r1, #RECEIVER] // fetch real receiver
- tst r12, r12 // set ne for forwarding (r12!=0)
- MESSENGER_END_FAST
- bx r12 // call imp
-.else
-.abort oops
-.endif
-
-.endmacro
-
.macro CacheLookup
ldrh r12, [r9, #CACHE_MASK] // r12 = mask
ldr r9, [r9, #CACHE] // r9 = buckets
-.if $0 == STRET || $0 == SUPER_STRET
+.if $0 == STRET
and r12, r12, r2 // r12 = index = SEL & mask
.else
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
-2:
-.if $0 == STRET || $0 == SUPER_STRET
+6:
+.if $0 == STRET
teq r12, r2
.else
teq r12, r1
.endif
- bne 1f
- CacheHit $0
-1:
+ bne 8f
+ ldr r12, [r9, #4] // r12 = bucket->imp
+
+.if $0 == STRET
+ tst r12, r12 // set ne for stret forwarding
+.else
+ // eq already set for nonstret forwarding by `teq` above
+.endif
+
+.endmacro
+
+.macro CacheLookup2
+
+8:
cmp r12, #1
- blo LCacheMiss_f // if (bucket->sel == 0) cache miss
+ blo 8f // if (bucket->sel == 0) cache miss
it eq // if (bucket->sel == 1) cache wrap
ldreq r9, [r9, #4] // bucket->imp is before first bucket
ldr r12, [r9, #8]! // r12 = (++bucket)->sel
- b 2b
+ b 6b
+8:
+
+.endmacro
+
+/////////////////////////////////////////////////////////////////////
+//
+// GetClassFromIsa return-type
+//
+// Given an Isa, return the class for the Isa.
+//
+// Takes:
+// r9 = class
+//
+// On exit: r12 clobbered
+// r9 contains the class for this Isa.
+//
+/////////////////////////////////////////////////////////////////////
+.macro GetClassFromIsa
+
+#if SUPPORT_INDEXED_ISA
+ // 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
+ itt ne
+ ubfxne r9, r9, #ISA_INDEX_SHIFT, #ISA_INDEX_BITS
+ ldrne.w r9, [r12, r9, lsl #2]
+#endif
.endmacro
* If not found, returns NULL.
********************************************************************/
- STATIC_ENTRY cache_getImp
+ STATIC_ENTRY _cache_getImp
mov r9, r0
- CacheLookup GETIMP // returns IMP on success
+ CacheLookup NORMAL
+ // cache hit, IMP in r12
+ mov r0, r12
+ bx lr // return imp
-LCacheMiss:
- mov r0, #0 // return nil if cache miss
+ CacheLookup2 GETIMP
+ // cache miss, return nil
+ mov r0, #0
bx lr
-LGetImpExit:
- END_ENTRY cache_getImp
+ END_ENTRY _cache_getImp
/********************************************************************
*
- * id objc_msgSend(id self, SEL _cmd,...);
+ * id objc_msgSend(id self, SEL _cmd, ...);
+ * IMP objc_msgLookup(id self, SEL _cmd, ...);
+ *
+ * objc_msgLookup ABI:
+ * IMP returned in r12
+ * Forwarding returned in Z flag
+ * r9 reserved for our use but not used
*
********************************************************************/
- ENTRY objc_msgSend
+ ENTRY _objc_msgSend
MESSENGER_START
cbz r0, LNilReceiver_f
ldr r9, [r0] // r9 = self->isa
+ GetClassFromIsa // r9 = class
CacheLookup NORMAL
- // calls IMP or LCacheMiss
+ // cache hit, IMP in r12, eq already set for nonstret forwarding
+ MESSENGER_END_FAST
+ bx r12 // call imp
-LCacheMiss:
+ CacheLookup2 NORMAL
+ // cache miss
+ ldr r9, [r0] // r9 = self->isa
+ GetClassFromIsa // r9 = class
MESSENGER_END_SLOW
- ldr r9, [r0, #ISA] // class = receiver->isa
b __objc_msgSend_uncached
LNilReceiver:
MESSENGER_END_NIL
bx lr
-LMsgSendExit:
- END_ENTRY objc_msgSend
+ END_ENTRY _objc_msgSend
+
+ ENTRY _objc_msgLookup
-/********************************************************************
- * id objc_msgSend_noarg(id self, SEL op)
- *
- * On entry: r0 is the message receiver,
- * r1 is the selector
- ********************************************************************/
+ cbz r0, LNilReceiver_f
- ENTRY objc_msgSend_noarg
- b _objc_msgSend
- END_ENTRY objc_msgSend_noarg
+ ldr r9, [r0] // r9 = self->isa
+ GetClassFromIsa // r9 = class
+ CacheLookup NORMAL
+ // cache hit, IMP in r12, eq already set for nonstret forwarding
+ bx lr
+
+ CacheLookup2 NORMAL
+ // cache miss
+ ldr r9, [r0] // r9 = self->isa
+ GetClassFromIsa // r9 = class
+ b __objc_msgLookup_uncached
+
+LNilReceiver:
+ MI_GET_ADDRESS(r12, __objc_msgNil)
+ bx lr
+
+ END_ENTRY _objc_msgLookup
+
+
+ STATIC_ENTRY __objc_msgNil
+
+ // r0 is already zero
+ mov r1, #0
+ mov r2, #0
+ mov r3, #0
+ FP_RETURN_ZERO
+ bx lr
+
+ END_ENTRY __objc_msgNil
/********************************************************************
* void objc_msgSend_stret(void *st_addr, id self, SEL op, ...);
+ * IMP objc_msgLookup_stret(void *st_addr, id self, SEL op, ...);
*
* objc_msgSend_stret is the struct-return form of msgSend.
* The ABI calls for r0 to be used as the address of the structure
* r2 is the selector
********************************************************************/
- ENTRY objc_msgSend_stret
+ ENTRY _objc_msgSend_stret
MESSENGER_START
cbz r1, LNilReceiver_f
ldr r9, [r1] // r9 = self->isa
+ GetClassFromIsa // r9 = class
CacheLookup STRET
- // calls IMP or LCacheMiss
+ // cache hit, IMP in r12, ne already set for stret forwarding
+ MESSENGER_END_FAST
+ bx r12
-LCacheMiss:
- MESSENGER_END_SLOW
+ CacheLookup2 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
-LMsgSendStretExit:
- END_ENTRY objc_msgSend_stret
+ END_ENTRY _objc_msgSend_stret
+
+
+ ENTRY _objc_msgLookup_stret
+
+ cbz r1, LNilReceiver_f
+
+ ldr r9, [r1] // r9 = self->isa
+ GetClassFromIsa // r9 = class
+ CacheLookup STRET
+ // cache hit, IMP in r12, ne already set for stret forwarding
+ bx lr
+
+ CacheLookup2 STRET
+ // cache miss
+ ldr r9, [r1] // r9 = self->isa
+ GetClassFromIsa // r9 = class
+ b __objc_msgLookup_stret_uncached
+
+LNilReceiver:
+ MI_GET_ADDRESS(r12, __objc_msgNil_stret)
+ bx lr
+
+ END_ENTRY _objc_msgLookup_stret
+
+
+ STATIC_ENTRY __objc_msgNil_stret
+
+ bx lr
+
+ END_ENTRY __objc_msgNil_stret
/********************************************************************
* }
********************************************************************/
- ENTRY objc_msgSendSuper
+ ENTRY _objc_msgSendSuper
MESSENGER_START
ldr r9, [r0, #CLASS] // r9 = struct super->class
- CacheLookup SUPER
- // calls IMP or LCacheMiss
+ CacheLookup NORMAL
+ // 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
-LCacheMiss:
- MESSENGER_END_SLOW
+ CacheLookup2 NORMAL
+ // cache miss
ldr r9, [r0, #CLASS] // r9 = struct super->class
ldr r0, [r0, #RECEIVER] // load real receiver
+ MESSENGER_END_SLOW
b __objc_msgSend_uncached
-LMsgSendSuperExit:
- END_ENTRY objc_msgSendSuper
+ END_ENTRY _objc_msgSendSuper
/********************************************************************
* }
********************************************************************/
- ENTRY objc_msgSendSuper2
+ ENTRY _objc_msgSendSuper2
MESSENGER_START
ldr r9, [r0, #CLASS] // class = struct super->class
- ldr r9, [r9, #SUPERCLASS] // class = class->superclass
- CacheLookup SUPER2
- // calls IMP or LCacheMiss
+ ldr r9, [r9, #SUPERCLASS] // class = class->superclass
+ CacheLookup NORMAL
+ // 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
-LCacheMiss:
- MESSENGER_END_SLOW
+ CacheLookup2 NORMAL
+ // cache miss
ldr r9, [r0, #CLASS] // class = struct super->class
- ldr r9, [r9, #SUPERCLASS] // class = class->superclass
+ ldr r9, [r9, #SUPERCLASS] // class = class->superclass
ldr r0, [r0, #RECEIVER] // load real receiver
+ MESSENGER_END_SLOW
b __objc_msgSend_uncached
-LMsgSendSuper2Exit:
- END_ENTRY objc_msgSendSuper2
+ END_ENTRY _objc_msgSendSuper2
+
+
+ ENTRY _objc_msgLookupSuper2
+
+ ldr r9, [r0, #CLASS] // class = struct super->class
+ ldr r9, [r9, #SUPERCLASS] // class = class->superclass
+ CacheLookup NORMAL
+ // cache hit, IMP in r12, eq already set for nonstret forwarding
+ ldr r0, [r0, #RECEIVER] // load real receiver
+ bx lr
+
+ CacheLookup2 NORMAL
+ // cache miss
+ ldr r9, [r0, #CLASS]
+ ldr r9, [r9, #SUPERCLASS] // r9 = class to search
+ ldr r0, [r0, #RECEIVER] // load real receiver
+ b __objc_msgLookup_uncached
+
+ END_ENTRY _objc_msgLookupSuper2
/********************************************************************
* r2 is the selector
********************************************************************/
- ENTRY objc_msgSendSuper_stret
+ ENTRY _objc_msgSendSuper_stret
MESSENGER_START
- ldr r9, [r1, #CLASS] // r9 = struct super->class
- CacheLookup SUPER_STRET
- // calls IMP or LCacheMiss
+ ldr r9, [r1, #CLASS] // r9 = struct super->class
+ CacheLookup 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
-LCacheMiss:
+ CacheLookup2 STRET
+ // cache miss
+ ldr r9, [r1, #CLASS] // r9 = struct super->class
+ ldr r1, [r1, #RECEIVER] // load real receiver
MESSENGER_END_SLOW
- ldr r9, [r1, #CLASS] // r9 = struct super->class
- ldr r1, [r1, #RECEIVER] // load real receiver
b __objc_msgSend_stret_uncached
-LMsgSendSuperStretExit:
- END_ENTRY objc_msgSendSuper_stret
+ END_ENTRY _objc_msgSendSuper_stret
/********************************************************************
* id objc_msgSendSuper2_stret
********************************************************************/
- ENTRY objc_msgSendSuper2_stret
+ ENTRY _objc_msgSendSuper2_stret
MESSENGER_START
- ldr r9, [r1, #CLASS] // class = struct super->class
- ldr r9, [r9, #SUPERCLASS] // class = class->superclass
- CacheLookup SUPER2_STRET
+ ldr r9, [r1, #CLASS] // class = struct super->class
+ ldr r9, [r9, #SUPERCLASS] // class = class->superclass
+ CacheLookup 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
-LCacheMiss:
- MESSENGER_END_SLOW
- ldr r9, [r1, #CLASS] // class = struct super->class
- ldr r9, [r9, #SUPERCLASS] // class = class->superclass
+ CacheLookup2 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
-LMsgSendSuper2StretExit:
- END_ENTRY objc_msgSendSuper2_stret
+ END_ENTRY _objc_msgSendSuper2_stret
+
+
+ ENTRY _objc_msgLookupSuper2_stret
+
+ ldr r9, [r1, #CLASS] // class = struct super->class
+ ldr r9, [r9, #SUPERCLASS] // class = class->superclass
+ CacheLookup STRET
+ // cache hit, IMP in r12, ne already set for stret forwarding
+ ldr r1, [r1, #RECEIVER] // load real receiver
+ bx lr
+
+ CacheLookup2 STRET
+ // cache miss
+ ldr r9, [r1, #CLASS]
+ ldr r9, [r9, #SUPERCLASS] // r9 = class to search
+ ldr r1, [r1, #RECEIVER] // load real receiver
+ b __objc_msgLookup_stret_uncached
+
+ END_ENTRY _objc_msgLookupSuper2_stret
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// MethodTableLookup NORMAL|STRET
+//
+// Locate the implementation for a selector in a class's method lists.
+//
+// Takes:
+// $0 = NORMAL, STRET
+// r0 or r1 (STRET) = receiver
+// r1 or r2 (STRET) = selector
+// r9 = class to search in
+//
+// On exit: IMP in r12, eq/ne set for forwarding
+//
+/////////////////////////////////////////////////////////////////////
+
+.macro MethodTableLookup
+
+ stmfd sp!, {r0-r3,r7,lr}
+ add r7, sp, #16
+ sub sp, #8 // align stack
+ FP_SAVE
+
+.if $0 == NORMAL
+ // receiver already in r0
+ // selector already in r1
+.else
+ mov r0, r1 // receiver
+ mov r1, r2 // selector
+.endif
+ mov r2, r9 // class to search
+
+ blx __class_lookupMethodAndLoadCache3
+ mov r12, r0 // r12 = IMP
+
+.if $0 == NORMAL
+ cmp r12, r12 // set eq for nonstret forwarding
+.else
+ tst r12, r12 // set ne for stret forwarding
+.endif
+
+ FP_RESTORE
+ add sp, #8 // align stack
+ ldmfd sp!, {r0-r3,r7,lr}
+
+.endmacro
/********************************************************************
- *
- * _objc_msgSend_uncached_impcache
- * Used to erase method cache entries in-place by
- * bouncing them to the uncached lookup.
*
* _objc_msgSend_uncached
* _objc_msgSend_stret_uncached
- * The uncached lookup.
+ * _objc_msgLookup_uncached
+ * _objc_msgLookup_stret_uncached
+ * The uncached method lookup.
*
********************************************************************/
-
- STATIC_ENTRY _objc_msgSend_uncached_impcache
- // Method cache version
- // THIS IS NOT A CALLABLE C FUNCTION
- // Out-of-band Z is 0 (EQ) for normal, 1 (NE) for stret and/or super
- // Out-of-band r9 is 1 for stret, cls for super, cls|1 for super_stret
- // Note objc_msgForward_impcache uses the same parameters
+ STATIC_ENTRY __objc_msgSend_uncached
- MESSENGER_START
- nop
- MESSENGER_END_SLOW
+ // THIS IS NOT A CALLABLE C FUNCTION
+ // Out-of-band r9 is the class to search
- ite eq
- ldreq r9, [r0] // normal: r9 = class = self->isa
- tstne r9, #1 // low bit clear?
- beq __objc_msgSend_uncached // super: r9 is already the class
- // stret or super_stret
- eors r9, r9, #1 // clear low bit
- it eq // r9 now zero?
- ldreq r9, [r1] // stret: r9 = class = self->isa
- // super_stret: r9 is already the class
- b __objc_msgSend_stret_uncached
+ MethodTableLookup NORMAL // returns IMP in r12
+ bx r12
- END_ENTRY _objc_msgSend_uncached_impcache
+ END_ENTRY __objc_msgSend_uncached
- STATIC_ENTRY _objc_msgSend_uncached
+ STATIC_ENTRY __objc_msgSend_stret_uncached
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band r9 is the class to search
- stmfd sp!, {r0-r3,r7,lr}
- add r7, sp, #16
- sub sp, #8 // align stack
- FP_SAVE
- // receiver already in r0
- // selector already in r1
- mov r2, r9 // class to search
-
- MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache3)
- mov r12, r0 // r12 = IMP
-
- movs r9, #0 // r9=0, Z=1 for nonstret forwarding
- FP_RESTORE
- add sp, #8 // align stack
- ldmfd sp!, {r0-r3,r7,lr}
+ MethodTableLookup STRET // returns IMP in r12
bx r12
+
+ END_ENTRY __objc_msgSend_stret_uncached
- END_ENTRY _objc_msgSend_uncached
-
-
- STATIC_ENTRY _objc_msgSend_stret_uncached
+
+ STATIC_ENTRY __objc_msgLookup_uncached
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band r9 is the class to search
- stmfd sp!, {r0-r3,r7,lr}
- add r7, sp, #16
- sub sp, #8 // align stack
- FP_SAVE
+ MethodTableLookup NORMAL // returns IMP in r12
+ bx lr
- mov r0, r1 // receiver
- mov r1, r2 // selector
- mov r2, r9 // class to search
+ END_ENTRY __objc_msgLookup_uncached
- MI_CALL_EXTERNAL(__class_lookupMethodAndLoadCache3)
- mov r12, r0 // r12 = IMP
- movs r9, #1 // r9=1, Z=0 for stret forwarding
- FP_RESTORE
- add sp, #8 // align stack
- ldmfd sp!, {r0-r3,r7,lr}
- bx r12
+ STATIC_ENTRY __objc_msgLookup_stret_uncached
+
+ // THIS IS NOT A CALLABLE C FUNCTION
+ // Out-of-band r9 is the class to search
+
+ MethodTableLookup STRET // returns IMP in r12
+ bx lr
- END_ENTRY _objc_msgSend_stret_uncached
+ END_ENTRY __objc_msgLookup_stret_uncached
/********************************************************************
MI_EXTERN(__objc_forward_handler)
MI_EXTERN(__objc_forward_stret_handler)
- STATIC_ENTRY _objc_msgForward_impcache
+ STATIC_ENTRY __objc_msgForward_impcache
// Method cache version
// THIS IS NOT A CALLABLE C FUNCTION
- // Out-of-band Z is 0 (EQ) for normal, 1 (NE) for stret and/or super
- // Out-of-band r9 is 1 for stret, cls for super, cls|1 for super_stret
- // Note _objc_msgSend_uncached_impcache uses the same parameters
+ // Out-of-band Z is 0 (EQ) for normal, 1 (NE) for stret
MESSENGER_START
nop
MESSENGER_END_SLOW
- it ne
- tstne r9, #1
beq __objc_msgForward
b __objc_msgForward_stret
- END_ENTRY _objc_msgForward_impcache
+ END_ENTRY __objc_msgForward_impcache
- ENTRY _objc_msgForward
+ ENTRY __objc_msgForward
// Non-stret version
MI_GET_EXTERN(r12, __objc_forward_handler)
ldr r12, [r12]
bx r12
- END_ENTRY _objc_msgForward
+ END_ENTRY __objc_msgForward
- ENTRY _objc_msgForward_stret
+ ENTRY __objc_msgForward_stret
// Struct-return version
MI_GET_EXTERN(r12, __objc_forward_stret_handler)
ldr r12, [r12]
bx r12
- END_ENTRY _objc_msgForward_stret
+ END_ENTRY __objc_msgForward_stret
- ENTRY objc_msgSend_debug
+ ENTRY _objc_msgSend_noarg
+ b _objc_msgSend
+ END_ENTRY _objc_msgSend_noarg
+
+ ENTRY _objc_msgSend_debug
b _objc_msgSend
- END_ENTRY objc_msgSend_debug
+ END_ENTRY _objc_msgSend_debug
- ENTRY objc_msgSendSuper2_debug
+ ENTRY _objc_msgSendSuper2_debug
b _objc_msgSendSuper2
- END_ENTRY objc_msgSendSuper2_debug
+ END_ENTRY _objc_msgSendSuper2_debug
- ENTRY objc_msgSend_stret_debug
+ ENTRY _objc_msgSend_stret_debug
b _objc_msgSend_stret
- END_ENTRY objc_msgSend_stret_debug
+ END_ENTRY _objc_msgSend_stret_debug
- ENTRY objc_msgSendSuper2_stret_debug
+ ENTRY _objc_msgSendSuper2_stret_debug
b _objc_msgSendSuper2_stret
- END_ENTRY objc_msgSendSuper2_stret_debug
+ END_ENTRY _objc_msgSendSuper2_stret_debug
- ENTRY method_invoke
+ ENTRY _method_invoke
// r1 is method triplet instead of SEL
ldr r12, [r1, #METHOD_IMP]
ldr r1, [r1, #METHOD_NAME]
bx r12
- END_ENTRY method_invoke
+ END_ENTRY _method_invoke
- ENTRY method_invoke_stret
+ ENTRY _method_invoke_stret
// r2 is method triplet instead of SEL
ldr r12, [r2, #METHOD_IMP]
ldr r2, [r2, #METHOD_NAME]
bx r12
- END_ENTRY method_invoke_stret
-
-
- STATIC_ENTRY _objc_ignored_method
-
- // self is already in a0
- bx lr
-
- END_ENTRY _objc_ignored_method
+ END_ENTRY _method_invoke_stret
.section __DATA,__objc_msg_break
.quad _objc_msgSend
.quad _objc_msgSendSuper
.quad _objc_msgSendSuper2
+ .quad _objc_msgLookup
+ .quad _objc_msgLookupSuper2
.quad 0
.private_extern _objc_exitPoints
.quad LExit_objc_msgSend
.quad LExit_objc_msgSendSuper
.quad LExit_objc_msgSendSuper2
+ .quad LExit_objc_msgLookup
+ .quad LExit_objc_msgLookupSuper2
.quad 0
#define CACHE 16
/* Selected field offsets in isa field */
-#define ISA_MASK 0x00000001fffffff8
+#define ISA_MASK 0x0000000ffffffff8
/* Selected field offsets in method structure */
#define METHOD_NAME 0
.endmacro
+/********************************************************************
+ * UNWIND name, flags
+ * Unwind info generation
+ ********************************************************************/
+.macro UNWIND
+ .section __LD,__compact_unwind,regular,debug
+ .quad $0
+ .set LUnwind$0, LExit$0 - $0
+ .long LUnwind$0
+ .long $1
+ .quad 0 /* no personality */
+ .quad 0 /* no LSDA */
+ .text
+.endmacro
+
+#define NoFrame 0x02000000 // no frame, no SP adjustment
+#define FrameWithNoSaves 0x04000000 // frame, no non-volatile saves
+
+
/********************************************************************
*
- * CacheLookup NORMAL|GETIMP
+ * CacheLookup NORMAL|GETIMP|LOOKUP
*
* Locate the implementation for a selector in a class method cache.
*
* Takes:
* x1 = selector
- * x9 = class to be searched
+ * x16 = class to be searched
*
* Kills:
- * x10,x11,x12, x16,x17
+ * x9,x10,x11,x12, x17
*
- * On exit: (found) exits CacheLookup
- * with x9 = class, x17 = IMP
+ * On exit: (found) calls or returns IMP
+ * with x16 = class, x17 = IMP
* (not found) jumps to LCacheMiss
*
********************************************************************/
#define NORMAL 0
#define GETIMP 1
+#define LOOKUP 2
.macro CacheHit
- MESSENGER_END_FAST
.if $0 == NORMAL
+ MESSENGER_END_FAST
br x17 // call imp
+.elseif $0 == GETIMP
+ mov x0, x17 // return imp
+ ret
+.elseif $0 == LOOKUP
+ ret // return imp via x17
.else
- b LGetImpHit
+.abort oops
.endif
.endmacro
.macro CheckMiss
-.if $0 == NORMAL // miss if bucket->cls == 0
- cbz x16, __objc_msgSend_uncached_impcache
+ // miss if bucket->sel == 0
+.if $0 == GETIMP
+ cbz x9, LGetImpMiss
+.elseif $0 == NORMAL
+ cbz x9, __objc_msgSend_uncached
+.elseif $0 == LOOKUP
+ cbz x9, __objc_msgLookup_uncached
.else
- cbz x16, LGetImpMiss
+.abort oops
.endif
.endmacro
.macro JumpMiss
-.if $0 == NORMAL
- b __objc_msgSend_uncached_impcache
-.else
+.if $0 == GETIMP
b LGetImpMiss
+.elseif $0 == NORMAL
+ b __objc_msgSend_uncached
+.elseif $0 == LOOKUP
+ b __objc_msgLookup_uncached
+.else
+.abort oops
.endif
.endmacro
.macro CacheLookup
- // x1 = SEL, x9 = isa
- ldp x10, x11, [x9, #CACHE] // x10 = buckets, x11 = occupied|mask
+ // 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)
- ldp x16, x17, [x12] // {x16, x17} = *bucket
-1: cmp x16, x1 // if (bucket->sel != _cmd)
+ ldp x9, x17, [x12] // {x9, x17} = *bucket
+1: cmp x9, x1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp
2: // not hit: x12 = not-hit bucket
- CheckMiss $0 // miss if bucket->cls == 0
+ CheckMiss $0 // miss if bucket->sel == 0
cmp x12, x10 // wrap if bucket == buckets
b.eq 3f
- ldp x16, x17, [x12, #-16]! // {x16, x17} = *--bucket
+ ldp x9, x17, [x12, #-16]! // {x9, x17} = *--bucket
b 1b // loop
3: // wrap: x12 = first bucket, w11 = mask
// Clone scanning loop to miss instead of hang when cache is corrupt.
// The slow path may detect any corruption and halt later.
- ldp x16, x17, [x12] // {x16, x17} = *bucket
-1: cmp x16, x1 // if (bucket->sel != _cmd)
+ ldp x9, x17, [x12] // {x9, x17} = *bucket
+1: cmp x9, x1 // if (bucket->sel != _cmd)
b.ne 2f // scan more
CacheHit $0 // call or return imp
2: // not hit: x12 = not-hit bucket
- CheckMiss $0 // miss if bucket->cls == 0
+ CheckMiss $0 // miss if bucket->sel == 0
cmp x12, x10 // wrap if bucket == buckets
b.eq 3f
- ldp x16, x17, [x12, #-16]! // {x16, x17} = *--bucket
+ ldp x9, x17, [x12, #-16]! // {x9, x17} = *--bucket
b 1b // loop
3: // double wrap
.endmacro
+/********************************************************************
+ *
+ * id objc_msgSend(id self, SEL _cmd, ...);
+ * IMP objc_msgLookup(id self, SEL _cmd, ...);
+ *
+ * objc_msgLookup ABI:
+ * IMP returned in x17
+ * x16 reserved for our use but not used
+ *
+ ********************************************************************/
+
.data
.align 3
.globl _objc_debug_taggedpointer_classes
_objc_debug_taggedpointer_classes:
.fill 16, 8, 0
+ .globl _objc_debug_taggedpointer_ext_classes
+_objc_debug_taggedpointer_ext_classes:
+ .fill 256, 8, 0
ENTRY _objc_msgSend
+ UNWIND _objc_msgSend, NoFrame
MESSENGER_START
cmp x0, #0 // nil check and tagged pointer check
b.le LNilOrTagged // (MSB tagged pointer looks negative)
ldr x13, [x0] // x13 = isa
- and x9, x13, #ISA_MASK // x9 = class
+ and x16, x13, #ISA_MASK // x16 = class
LGetIsaDone:
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
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 x9, [x10, x11, LSL #3]
+ ldr x16, [x10, x11, LSL #3]
b 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
+
LReturnZero:
// x0 is already zero
mov x1, #0
END_ENTRY _objc_msgSend
+ ENTRY _objc_msgLookup
+ UNWIND _objc_msgLookup, NoFrame
+
+ cmp x0, #0 // nil check and tagged pointer check
+ b.le LLookup_NilOrTagged // (MSB tagged pointer looks negative)
+ ldr x13, [x0] // x13 = isa
+ and x16, x13, #ISA_MASK // x16 = class
+LLookup_GetIsaDone:
+ CacheLookup LOOKUP // returns imp
+
+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
+
+LLookup_ExtTag:
+ 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 LLookup_GetIsaDone
+
+LLookup_Nil:
+ adrp x17, __objc_msgNil@PAGE
+ add x17, x17, __objc_msgNil@PAGEOFF
+ ret
+
+ END_ENTRY _objc_msgLookup
+
+
+ STATIC_ENTRY __objc_msgNil
+
+ // x0 is already zero
+ mov x1, #0
+ movi d0, #0
+ movi d1, #0
+ movi d2, #0
+ movi d3, #0
+ ret
+
+ END_ENTRY __objc_msgNil
+
+
ENTRY _objc_msgSendSuper
+ UNWIND _objc_msgSendSuper, NoFrame
MESSENGER_START
- ldr x9, [x0, #CLASS] // load class to search
- ldr x0, [x0, #RECEIVER] // load real receiver
+ ldp x0, x16, [x0] // x0 = real receiver, x16 = class
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
END_ENTRY _objc_msgSendSuper
-
+ // no _objc_msgLookupSuper
+
ENTRY _objc_msgSendSuper2
+ UNWIND _objc_msgSendSuper2, NoFrame
MESSENGER_START
- ldr x9, [x0, #CLASS]
- ldr x9, [x9, #SUPERCLASS] // load class to search
- ldr x0, [x0, #RECEIVER] // load real receiver
+ ldp x0, x16, [x0] // x0 = real receiver, x16 = class
+ ldr x16, [x16, #SUPERCLASS] // x16 = class->superclass
CacheLookup NORMAL
END_ENTRY _objc_msgSendSuper2
+
+ ENTRY _objc_msgLookupSuper2
+ UNWIND _objc_msgLookupSuper2, NoFrame
- ENTRY _objc_msgSend_noarg
- b _objc_msgSend
- END_ENTRY _objc_msgSend_noarg
-
+ ldp x0, x16, [x0] // x0 = real receiver, x16 = class
+ ldr x16, [x16, #SUPERCLASS] // x16 = class->superclass
+ CacheLookup LOOKUP
- STATIC_ENTRY __objc_msgSend_uncached_impcache
+ END_ENTRY _objc_msgLookupSuper2
- // THIS IS NOT A CALLABLE C FUNCTION
- // Out-of-band x9 is the class to search
- MESSENGER_START
+.macro MethodTableLookup
// push frame
stp fp, lr, [sp, #-16]!
mov fp, sp
- MESSENGER_END_SLOW
-
// save parameter registers: x0..x8, q0..q7
sub sp, sp, #(10*8 + 8*16)
stp q0, q1, [sp, #(0*16)]
str x8, [sp, #(8*16+8*8)]
// receiver and selector already in x0 and x1
- mov x2, x9
+ mov x2, x16
bl __class_lookupMethodAndLoadCache3
// imp in x0
mov sp, fp
ldp fp, lr, [sp], #16
+
+.endmacro
+
+ STATIC_ENTRY __objc_msgSend_uncached
+ UNWIND __objc_msgSend_uncached, FrameWithNoSaves
+
+ // THIS IS NOT A CALLABLE C FUNCTION
+ // Out-of-band x16 is the class to search
+ MethodTableLookup
br x17
- END_ENTRY __objc_msgSend_uncached_impcache
-
-
-.section __LD,__compact_unwind,regular,debug
- .quad _objc_msgSend
- .set LUnwind_objc_msgSend, LExit_objc_msgSend-_objc_msgSend
- .long LUnwind_objc_msgSend
- .long 0x02000000 // no frame, no SP adjustment
- .quad 0 // no personality
- .quad 0 // no LSDA
-
-.section __LD,__compact_unwind,regular,debug
- .quad _objc_msgSendSuper
- .set LUnwind_objc_msgSendSuper, LExit_objc_msgSendSuper-_objc_msgSendSuper
- .long LUnwind_objc_msgSendSuper
- .long 0x02000000 // no frame, no SP adjustment
- .quad 0 // no personality
- .quad 0 // no LSDA
-
-.section __LD,__compact_unwind,regular,debug
- .quad _objc_msgSendSuper2
- .set LUnwind_objc_msgSendSuper2, LExit_objc_msgSendSuper2-_objc_msgSendSuper2
- .long LUnwind_objc_msgSendSuper2
- .long 0x02000000 // no frame, no SP adjustment
- .quad 0 // no personality
- .quad 0 // no LSDA
-
-.section __LD,__compact_unwind,regular,debug
- .quad __objc_msgSend_uncached_impcache
- .set LUnwind__objc_msgSend_uncached_impcache, LExit__objc_msgSend_uncached_impcache-__objc_msgSend_uncached_impcache
- .long LUnwind__objc_msgSend_uncached_impcache
- .long 0x04000000 // frame, no non-volatile registers saved
- .quad 0 // no personality
- .quad 0 // no LSDA
+ END_ENTRY __objc_msgSend_uncached
+
+
+ STATIC_ENTRY __objc_msgLookup_uncached
+ UNWIND __objc_msgLookup_uncached, FrameWithNoSaves
+
+ // THIS IS NOT A CALLABLE C FUNCTION
+ // Out-of-band x16 is the class to search
+
+ MethodTableLookup
+ ret
+
+ END_ENTRY __objc_msgLookup_uncached
STATIC_ENTRY _cache_getImp
- and x9, x0, #ISA_MASK
+ and x16, x0, #ISA_MASK
CacheLookup GETIMP
-LGetImpHit:
- // imp in x17
- // don't return msgSend_uncached
- adrp x16, __objc_msgSend_uncached_impcache@PAGE
- add x16, x16, __objc_msgSend_uncached_impcache@PAGEOFF
- cmp x16, x17
- csel x0, x17, xzr, ne // if imp!=uncached then imp else 0
- ret
-
LGetImpMiss:
mov x0, #0
ret
END_ENTRY __objc_msgForward
+ ENTRY _objc_msgSend_noarg
+ b _objc_msgSend
+ END_ENTRY _objc_msgSend_noarg
+
ENTRY _objc_msgSend_debug
b _objc_msgSend
END_ENTRY _objc_msgSend_debug
br x17
END_ENTRY _method_invoke
-
- STATIC_ENTRY __objc_ignored_method
-
- // self is already in x0
- ret
-
- END_ENTRY __objc_ignored_method
-
#endif
*/
#include <TargetConditionals.h>
-#if defined(__i386__) && !TARGET_IPHONE_SIMULATOR
+#if defined(__i386__) && !TARGET_OS_SIMULATOR
/********************************************************************
********************************************************************
********************************************************************
********************************************************************/
-// for kIgnore
-#include "objc-config.h"
-
/********************************************************************
* Data used by the ObjC runtime.
movl selector(%esp), %ecx
movl self(%esp), %eax
-// check whether selector is ignored
- cmpl $ kIgnore, %ecx
- je LMsgSendDone // return self from %eax
-
// check whether receiver is nil
testl %eax, %eax
je LMsgSendNilSelf
movl selector(%esp), %ecx
movl class(%eax), %edx // struct objc_super->class
-// check whether selector is ignored
- cmpl $ kIgnore, %ecx
- je LMsgSendSuperIgnored // return self from %eax
-
// search the cache (class in %edx)
CacheLookup WORD_RETURN, MSG_SENDSUPER, LMsgSendSuperCacheMiss
xor %edx, %edx // set nonstret for msgForward_internal
movl selector(%esp), %ecx
movl self(%esp), %eax
-// check whether selector is ignored
- cmpl $ kIgnore, %ecx
- je LMsgSendFpretDone // return self from %eax
-
// check whether receiver is nil
testl %eax, %eax
je LMsgSendFpretNilSelf
END_ENTRY _method_invoke_stret
-
- STATIC_ENTRY __objc_ignored_method
-
- movl self(%esp), %eax
- ret
-
- END_ENTRY __objc_ignored_method
-
.section __DATA,__objc_msg_break
.long 0
*/
#include <TargetConditionals.h>
-#if defined(__i386__) && TARGET_IPHONE_SIMULATOR
+#if defined(__i386__) && TARGET_OS_SIMULATOR
#include "objc-config.h"
.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
.long 0
.private_extern _objc_exitPoints
_objc_exitPoints:
- .long LGetImpExit
- .long LMsgSendExit
- .long LMsgSendFpretExit
- .long LMsgSendStretExit
- .long LMsgSendSuperExit
- .long LMsgSendSuper2Exit
- .long LMsgSendSuperStretExit
- .long LMsgSendSuper2StretExit
+ .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
.long 0
* Macro parameters
********************************************************************/
+
#define NORMAL 0
#define FPRET 1
-#define GETIMP 3
-#define STRET 4
-#define SUPER 5
-#define SUPER_STRET 6
+#define STRET 2
+
+#define CALL 100
+#define GETIMP 101
+#define LOOKUP 102
/********************************************************************
.globl $0
.align 2, 0x90
$0:
- .cfi_startproc
.endmacro
.macro STATIC_ENTRY
.private_extern $0
.align 4, 0x90
$0:
- .cfi_startproc
.endmacro
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
.macro END_ENTRY
- .cfi_endproc
+LExit$0:
+.endmacro
+
+
+ /********************************************************************
+ * UNWIND name, flags
+ * Unwind info generation
+ ********************************************************************/
+.macro UNWIND
+ .section __LD,__compact_unwind,regular,debug
+ .long $0
+ .set LUnwind$0, LExit$0 - $0
+ .long LUnwind$0
+ .long $1
+ .long 0 /* no personality */
+ .long 0 /* no LSDA */
+ .text
.endmacro
+#define NoFrame 0x02010000 // no frame, no SP adjustment except return address
+#define FrameWithNoSaves 0x01000000 // frame, no non-volatile saves
+
/////////////////////////////////////////////////////////////////////
//
-// CacheLookup return-type
+// CacheLookup return-type, caller
//
// Locate the implementation for a selector in a class method cache.
//
// Takes:
-// $0 = NORMAL, FPRET, STRET, SUPER, SUPER_STRET, GETIMP
+// $0 = NORMAL, FPRET, STRET
+// $1 = CALL, LOOKUP, GETIMP
// ecx = selector to search for
// edx = class to search
//
// CacheHit must always be preceded by a not-taken `jne` instruction
// in case the imp is _objc_msgForward_impcache.
-.if $0 == GETIMP
+ // eax = found bucket
+
+.if $1 == GETIMP
movl 4(%eax), %eax // return imp
- call 4f
-4: pop %edx
- leal __objc_msgSend_uncached_impcache-4b(%edx), %edx
- cmpl %edx, %eax
- jne 4f
- xor %eax, %eax // don't return msgSend_uncached
-4: ret
-.elseif $0 == NORMAL || $0 == FPRET
+ ret
+
+.else
+
+.if $0 != STRET
// eq already set for forwarding by `jne`
- MESSENGER_END_FAST
- jmp *4(%eax) // call imp
-.elseif $0 == STRET
- test %eax, %eax // set ne for stret forwarding
- MESSENGER_END_FAST
- jmp *4(%eax) // call imp
-.elseif $0 == SUPER
- // replace "super" arg with "receiver"
- movl super(%esp), %ecx // get super structure
- movl receiver(%ecx), %ecx // get messaged object
- movl %ecx, super(%esp) // make it the first argument
- cmp %eax, %eax // set eq for non-stret forwarding
- MESSENGER_END_FAST
- jmp *4(%eax) // call imp
-.elseif $0 == SUPER_STRET
- // replace "super" arg with "receiver"
- movl super_stret(%esp), %ecx // get super structure
- movl receiver(%ecx), %ecx // get messaged object
- movl %ecx, super_stret(%esp) // make it the first argument
+.else
test %eax, %eax // set ne for stret forwarding
+.endif
+
+.if $1 == CALL
MESSENGER_END_FAST
jmp *4(%eax) // call imp
+
+.elseif $1 == LOOKUP
+ movl 4(%eax), %eax // return imp
+ ret
+
.else
.abort oops
.endif
+.endif
+
.endmacro
cmpl (%eax), %ecx // if (bucket->sel != SEL)
jne 1f // scan more
// The `jne` above sets flags for CacheHit
- CacheHit $0 // call or return imp
+ CacheHit $0, $1 // call or return imp
1:
// loop
cmpl (%eax), %ecx // if (bucket->sel != sel)
jne 1b // scan more
// The `jne` above sets flags for CacheHit
- CacheHit $0 // call or return imp
+ CacheHit $0, $1 // call or return imp
3:
// wrap or miss
cmpl (%eax), %ecx // if (bucket->sel != sel)
jne 1b // scan more
// The `jne` above sets flags for CacheHit
- CacheHit $0 // call or return imp
+ CacheHit $0, $1 // call or return imp
3:
// double wrap or miss
/////////////////////////////////////////////////////////////////////
//
-// MethodTableLookup
+// MethodTableLookup NORMAL|STRET
//
// Takes:
-// $0 = NORMAL, FPRET, STRET, SUPER, SUPER_STRET
-// eax = receiver
-// ecx = selector
+// receiver (not struct objc_super) and selector on stack
// edx = class to search
//
-// On exit: calls IMP, eq/ne set for forwarding
+// On exit: IMP in eax, eq/ne set for forwarding
//
/////////////////////////////////////////////////////////////////////
.macro MethodTableLookup
- MESSENGER_END_SLOW
pushl %ebp
- .cfi_def_cfa_offset 8
- .cfi_offset ebp, -8
-
movl %esp, %ebp
- .cfi_def_cfa_register ebp
subl $$(8+5*16), %esp
+.if $0 == NORMAL
+ movl self+4(%ebp), %eax
+ movl selector+4(%ebp), %ecx
+.else
+ movl self_stret+4(%ebp), %eax
+ movl selector_stret+4(%ebp), %ecx
+.endif
+
movdqa %xmm3, 4*16(%esp)
movdqa %xmm2, 3*16(%esp)
movdqa %xmm1, 2*16(%esp)
movdqa 2*16(%esp), %xmm1
movdqa 1*16(%esp), %xmm0
- leave
- .cfi_def_cfa esp, 4
- .cfi_same_value ebp
-
-.if $0 == SUPER
- // replace "super" arg with "receiver"
- movl super(%esp), %ecx // get super structure
- movl receiver(%ecx), %ecx // get messaged object
- movl %ecx, super(%esp) // make it the first argument
-.elseif $0 == SUPER_STRET
- // replace "super" arg with "receiver"
- movl super_stret(%esp), %ecx // get super structure
- movl receiver(%ecx), %ecx // get messaged object
- movl %ecx, super_stret(%esp) // make it the first argument
-.endif
-
-.if $0 == STRET || $0 == SUPER_STRET
- // set ne (stret) for forwarding; eax != 0
- test %eax, %eax
- jmp *%eax // call imp
+.if $0 == NORMAL
+ cmp %eax, %eax // set eq for nonstret forwarding
.else
- // set eq (non-stret) for forwarding
- cmp %eax, %eax
- jmp *%eax // call imp
+ test %eax, %eax // set ne for stret forwarding
.endif
+ leave
+
.endmacro
// On exit: Loads non-nil receiver in eax and self(esp) or self_stret(esp),
// or returns zero.
//
-// NilTestSupport return-type
+// NilTestReturnZero return-type
//
// Takes: $0 = NORMAL or FPRET or STRET
// eax = receiver
// On exit: Loads non-nil receiver in eax and self(esp) or self_stret(esp),
// or returns zero.
//
+// NilTestReturnIMP return-type
+//
+// Takes: $0 = NORMAL or FPRET or STRET
+// eax = receiver
+//
+// On exit: Loads non-nil receiver in eax and self(esp) or self_stret(esp),
+// or returns an IMP in eax that returns zero.
+//
/////////////////////////////////////////////////////////////////////
+.macro ZeroReturn
+ xorl %eax, %eax
+ xorl %edx, %edx
+ xorps %xmm0, %xmm0
+ xorps %xmm1, %xmm1
+.endmacro
+
+.macro ZeroReturnFPRET
+ fldz
+.endmacro
+
+.macro ZeroReturnSTRET
+ // empty
+.endmacro
+
+ STATIC_ENTRY __objc_msgNil
+ ZeroReturn
+ ret
+ END_ENTRY __objc_msgNil
+
+ STATIC_ENTRY __objc_msgNil_fpret
+ ZeroReturnFPRET
+ ret
+ END_ENTRY __objc_msgNil_fpret
+
+ STATIC_ENTRY __objc_msgNil_stret
+ ZeroReturnSTRET
+ ret $4
+ END_ENTRY __objc_msgNil_stret
+
+
.macro NilTest
testl %eax, %eax
jz LNilTestSlow_f
LNilTestDone:
.endmacro
-.macro NilTestSupport
+.macro NilTestReturnZero
.align 3
LNilTestSlow:
-.if $0 == FPRET
- fldz
+.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
-.elseif $0 == NORMAL
- // eax is already zero
- xorl %edx, %edx
- xorps %xmm0, %xmm0
- xorps %xmm1, %xmm1
- MESSENGER_END_NIL
- ret
+.else
+.abort oops
+.endif
+.endmacro
+
+.macro NilTestReturnIMP
+ .align 3
+LNilTestSlow:
+
+ call 1f
+1: pop %eax
+.if $0 == NORMAL
+ leal __objc_msgNil-1b(%eax), %eax
+.elseif $0 == FPRET
+ leal __objc_msgNil_fpret-1b(%eax), %eax
+.elseif $0 == STRET
+ leal __objc_msgNil_stret-1b(%eax), %eax
+.else
+.abort oops
.endif
+ ret
.endmacro
movl selector(%esp), %ecx
movl self(%esp), %edx
- CacheLookup GETIMP // returns IMP on success
+ CacheLookup NORMAL, GETIMP // returns IMP on success
LCacheMiss:
// cache miss, return nil
xorl %eax, %eax
ret
-LGetImpExit:
END_ENTRY _cache_getImp
/********************************************************************
*
- * id objc_msgSend(id self, SEL _cmd,...);
+ * id objc_msgSend(id self, SEL _cmd, ...);
+ * IMP objc_msgLookup(id self, SEL _cmd, ...);
+ *
+ * objc_msgLookup ABI:
+ * IMP returned in eax
+ * Forwarding returned in Z flag
+ * edx reserved for our use but not used
*
********************************************************************/
- ENTRY _objc_msgSend
+ ENTRY _objc_msgSend
+ UNWIND _objc_msgSend, NoFrame
MESSENGER_START
movl selector(%esp), %ecx
NilTest NORMAL
movl isa(%eax), %edx // class = self->isa
- CacheLookup NORMAL // calls IMP on success
+ CacheLookup NORMAL, CALL // calls IMP on success
- NilTestSupport NORMAL
+ NilTestReturnZero NORMAL
LCacheMiss:
// isa still in edx
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_uncached
+
+ END_ENTRY _objc_msgSend
+
+
+ ENTRY _objc_msgLookup
+ UNWIND _objc_msgLookup, NoFrame
+
movl selector(%esp), %ecx
movl self(%esp), %eax
- MethodTableLookup NORMAL // calls IMP
-LMsgSendExit:
- END_ENTRY _objc_msgSend
+ NilTest NORMAL
+
+ movl isa(%eax), %edx // class = self->isa
+ CacheLookup NORMAL, LOOKUP // returns IMP on success
+
+ NilTestReturnIMP NORMAL
+
+LCacheMiss:
+ // isa still in edx
+ jmp __objc_msgLookup_uncached
+
+ END_ENTRY _objc_msgLookup
/********************************************************************
*
- * id objc_msgSendSuper(struct objc_super *super, SEL _cmd,...);
+ * id objc_msgSendSuper(struct objc_super *super, SEL _cmd, ...);
+ * IMP objc_msgLookupSuper(struct objc_super *super, SEL _cmd, ...);
*
- * struct objc_super {
- * id receiver;
- * Class class;
- * };
********************************************************************/
- ENTRY _objc_msgSendSuper
+ ENTRY _objc_msgSendSuper
+ UNWIND _objc_msgSendSuper, NoFrame
MESSENGER_START
movl selector(%esp), %ecx
movl super(%esp), %eax // struct objc_super
movl class(%eax), %edx // struct objc_super->class
- CacheLookup SUPER // calls IMP on success
+ movl receiver(%eax), %eax // struct objc_super->receiver
+ movl %eax, super(%esp) // replace super arg with receiver
+ CacheLookup NORMAL, CALL // calls IMP on success
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
- movl receiver(%eax), %eax
- MethodTableLookup SUPER // calls IMP
-
-LMsgSendSuperExit:
- END_ENTRY _objc_msgSendSuper
+ 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
- ENTRY _objc_msgSendSuper2
+/********************************************************************
+ *
+ * id objc_msgSendSuper2(struct objc_super *super, SEL _cmd, ...);
+ * IMP objc_msgLookupSuper2(struct objc_super *super, SEL _cmd, ...);
+ *
+ ********************************************************************/
+
+ ENTRY _objc_msgSendSuper2
+ UNWIND _objc_msgSendSuper2, NoFrame
MESSENGER_START
movl selector(%esp), %ecx
movl super(%esp), %eax // struct objc_super
- movl class(%eax), %eax // struct objc_super->class
- mov superclass(%eax), %edx // edx = objc_super->class->super_class
- CacheLookup SUPER // calls IMP on success
+ 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
+ movl superclass(%edx), %edx // edx = objc_super->class->super_class
+ CacheLookup NORMAL, CALL // calls IMP on success
LCacheMiss:
// class still in edx
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_uncached
+
+ END_ENTRY _objc_msgSendSuper2
+
+
+ ENTRY _objc_msgLookupSuper2
+ UNWIND _objc_msgLookupSuper2, NoFrame
+
movl selector(%esp), %ecx
- movl super(%esp), %eax
- movl receiver(%eax), %eax
- MethodTableLookup SUPER // calls IMP
+ 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
+ movl superclass(%edx), %edx // edx = objc_super->class->super_class
+ CacheLookup NORMAL, LOOKUP // returns IMP on success
-LMsgSendSuper2Exit:
- END_ENTRY _objc_msgSendSuper2
+LCacheMiss:
+ // class still in edx
+ jmp __objc_msgLookup_uncached
+
+ END_ENTRY _objc_msgLookupSuper2
/********************************************************************
*
- * double objc_msgSend_fpret(id self, SEL _cmd,...);
+ * double objc_msgSend_fpret(id self, SEL _cmd, ...);
+ * IMP objc_msgLookup_fpret(id self, SEL _cmd, ...);
*
********************************************************************/
- ENTRY _objc_msgSend_fpret
+ ENTRY _objc_msgSend_fpret
+ UNWIND _objc_msgSend_fpret, NoFrame
MESSENGER_START
movl selector(%esp), %ecx
NilTest FPRET
movl isa(%eax), %edx // class = self->isa
- CacheLookup FPRET // calls IMP on success
+ CacheLookup FPRET, CALL // calls IMP on success
- NilTestSupport FPRET
+ NilTestReturnZero FPRET
LCacheMiss:
// class still in edx
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_uncached
+
+ END_ENTRY _objc_msgSend_fpret
+
+
+ ENTRY _objc_msgLookup_fpret
+ UNWIND _objc_msgLookup_fpret, NoFrame
+
movl selector(%esp), %ecx
movl self(%esp), %eax
- MethodTableLookup FPRET // calls IMP
-LMsgSendFpretExit:
- END_ENTRY _objc_msgSend_fpret
+ NilTest FPRET
+
+ movl isa(%eax), %edx // class = self->isa
+ CacheLookup FPRET, LOOKUP // returns IMP on success
+
+ NilTestReturnIMP FPRET
+
+LCacheMiss:
+ // class still in edx
+ jmp __objc_msgLookup_uncached
+
+ END_ENTRY _objc_msgLookup_fpret
/********************************************************************
*
- * void objc_msgSend_stret(void *st_addr , id self, SEL _cmd, ...);
- *
- *
- * objc_msgSend_stret is the struct-return form of msgSend.
- * The ABI calls for (sp+4) to be used as the address of the structure
- * being returned, with the parameters in the succeeding locations.
+ * void objc_msgSend_stret(void *st_addr, id self, SEL _cmd, ...);
+ * IMP objc_msgLookup_stret(void *st_addr, id self, SEL _cmd, ...);
*
- * On entry: (sp+4)is the address where the structure is returned,
- * (sp+8) is the message receiver,
- * (sp+12) is the selector
********************************************************************/
- ENTRY _objc_msgSend_stret
+ ENTRY _objc_msgSend_stret
+ UNWIND _objc_msgSend_stret, NoFrame
MESSENGER_START
movl selector_stret(%esp), %ecx
NilTest STRET
movl isa(%eax), %edx // class = self->isa
- CacheLookup STRET // calls IMP on success
+ CacheLookup STRET, CALL // calls IMP on success
- NilTestSupport STRET
+ NilTestReturnZero STRET
LCacheMiss:
// class still in edx
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_stret_uncached
+
+ END_ENTRY _objc_msgSend_stret
+
+
+ ENTRY _objc_msgLookup_stret
+ UNWIND _objc_msgLookup_stret, NoFrame
+
movl selector_stret(%esp), %ecx
movl self_stret(%esp), %eax
- MethodTableLookup STRET // calls IMP
-LMsgSendStretExit:
- END_ENTRY _objc_msgSend_stret
+ NilTest STRET
+
+ movl isa(%eax), %edx // class = self->isa
+ CacheLookup STRET, LOOKUP // returns IMP on success
+
+ NilTestReturnIMP STRET
+
+LCacheMiss:
+ // class still in edx
+ jmp __objc_msgLookup_stret_uncached
+
+ END_ENTRY _objc_msgLookup_stret
/********************************************************************
*
* void objc_msgSendSuper_stret(void *st_addr, struct objc_super *super, SEL _cmd, ...);
- *
- * struct objc_super {
- * id receiver;
- * Class class;
- * };
- *
- * objc_msgSendSuper_stret is the struct-return form of msgSendSuper.
- * The ABI calls for (sp+4) to be used as the address of the structure
- * being returned, with the parameters in the succeeding registers.
- *
- * On entry: (sp+4)is the address where the structure is returned,
- * (sp+8) is the address of the objc_super structure,
- * (sp+12) is the selector
+ * IMP objc_msgLookupSuper_stret(void *st_addr, struct objc_super *super, SEL _cmd, ...);
*
********************************************************************/
- ENTRY _objc_msgSendSuper_stret
+ ENTRY _objc_msgSendSuper_stret
+ UNWIND _objc_msgSendSuper_stret, NoFrame
MESSENGER_START
movl selector_stret(%esp), %ecx
movl super_stret(%esp), %eax // struct objc_super
movl class(%eax), %edx // struct objc_super->class
- CacheLookup SUPER_STRET // calls IMP on success
+ movl receiver(%eax), %eax // struct objc_super->receiver
+ movl %eax, super_stret(%esp) // replace super arg with receiver
+ CacheLookup STRET, CALL // calls IMP on success
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
- movl receiver(%eax), %eax
- MethodTableLookup SUPER_STRET // calls IMP
+ 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
-LMsgSendSuperStretExit:
- END_ENTRY _objc_msgSendSuper_stret
+ END_ENTRY _objc_msgLookupSuper_stret
- ENTRY _objc_msgSendSuper2_stret
+/********************************************************************
+ *
+ * void objc_msgSendSuper2_stret(void *st_addr, struct objc_super *super, SEL _cmd, ...);
+ * IMP objc_msgLookupSuper2_stret(void *st_addr, struct objc_super *super, SEL _cmd, ...);
+ *
+ ********************************************************************/
+
+ ENTRY _objc_msgSendSuper2_stret
+ UNWIND _objc_msgSendSuper2_stret, NoFrame
MESSENGER_START
movl selector_stret(%esp), %ecx
movl super_stret(%esp), %eax // struct objc_super
- movl class(%eax), %eax // struct objc_super->class
- mov superclass(%eax), %edx // edx = objc_super->class->super_class
- CacheLookup SUPER_STRET // calls IMP on success
+ 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
+ mov superclass(%edx), %edx // edx = objc_super->class->super_class
+ CacheLookup STRET, CALL // calls IMP on success
// 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
+
+
+ ENTRY _objc_msgLookupSuper2_stret
+ UNWIND _objc_msgLookupSuper2_stret, NoFrame
+
movl selector_stret(%esp), %ecx
- movl super_stret(%esp), %eax
- movl receiver(%eax), %eax
- MethodTableLookup SUPER_STRET // calls IMP
+ 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
+ mov superclass(%edx), %edx // edx = objc_super->class->super_class
+ CacheLookup STRET, LOOKUP // returns IMP on success
-LMsgSendSuper2StretExit:
- END_ENTRY _objc_msgSendSuper2_stret
+// cache miss: go search the method lists
+LCacheMiss:
+ // class still in edx
+ jmp __objc_msgLookup_stret_uncached
+
+ END_ENTRY _objc_msgLookupSuper2_stret
/********************************************************************
*
- * _objc_msgSend_uncached_impcache
* _objc_msgSend_uncached
* _objc_msgSend_stret_uncached
- *
- * Used to erase method cache entries in-place by
- * bouncing them to the uncached lookup.
+ * _objc_msgLookup_uncached
+ * _objc_msgLookup_stret_uncached
+ *
+ * The uncached method lookup.
*
********************************************************************/
-
- STATIC_ENTRY __objc_msgSend_uncached_impcache
- // Method cache version
+
+ STATIC_ENTRY __objc_msgSend_uncached
+ UNWIND __objc_msgSend_uncached, FrameWithNoSaves
// THIS IS NOT A CALLABLE C FUNCTION
- // Out-of-band condition register is NE for stret, EQ otherwise.
// Out-of-band edx is the searched class
- MESSENGER_START
- nop
- MESSENGER_END_SLOW
+ // edx is already the class to search
+ MethodTableLookup NORMAL
+ jmp *%eax // call imp
+
+ END_ENTRY __objc_msgSend_uncached
+
- jne __objc_msgSend_stret_uncached
- jmp __objc_msgSend_uncached
+ STATIC_ENTRY __objc_msgSend_stret_uncached
+ UNWIND __objc_msgSend_stret_uncached, FrameWithNoSaves
- END_ENTRY __objc_msgSend_uncached_impcache
+ // THIS IS NOT A CALLABLE C FUNCTION
+ // Out-of-band edx is the searched class
+ // edx is already the class to search
+ MethodTableLookup STRET
+ jmp *%eax // call imp
+
+ END_ENTRY __objc_msgSend_stret_uncached
- STATIC_ENTRY __objc_msgSend_uncached
+
+ STATIC_ENTRY __objc_msgLookup_uncached
+ UNWIND __objc_msgLookup_uncached, FrameWithNoSaves
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band edx is the searched class
// edx is already the class to search
- movl selector(%esp), %ecx
- MethodTableLookup NORMAL // calls IMP
+ MethodTableLookup NORMAL // eax = IMP
+ ret
- END_ENTRY __objc_msgSend_uncached
+ END_ENTRY __objc_msgLookup_uncached
- STATIC_ENTRY __objc_msgSend_stret_uncached
+ STATIC_ENTRY __objc_msgLookup_stret_uncached
+ UNWIND __objc_msgLookup_stret_uncached, FrameWithNoSaves
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band edx is the searched class
// edx is already the class to search
- movl selector_stret(%esp), %ecx
- MethodTableLookup STRET // calls IMP
+ MethodTableLookup STRET // eax = IMP
+ ret
- END_ENTRY __objc_msgSend_stret_uncached
+ END_ENTRY __objc_msgLookup_stret_uncached
-
/********************************************************************
*
* id _objc_msgForward(id self, SEL _cmd,...);
.indirect_symbol __objc_forward_stret_handler
.long 0
- STATIC_ENTRY __objc_msgForward_impcache
+ STATIC_ENTRY __objc_msgForward_impcache
// Method cache version
// THIS IS NOT A CALLABLE C FUNCTION
jne __objc_msgForward_stret
jmp __objc_msgForward
- END_ENTRY _objc_msgForward_impcache
+ END_ENTRY _objc_msgForward_impcache
- ENTRY __objc_msgForward
+ ENTRY __objc_msgForward
// Non-struct return version
call 1f
movl L_forward_handler-1b(%edx), %edx
jmp *(%edx)
- END_ENTRY __objc_msgForward
+ END_ENTRY __objc_msgForward
- ENTRY __objc_msgForward_stret
+ ENTRY __objc_msgForward_stret
// Struct return version
call 1f
movl L_forward_stret_handler-1b(%edx), %edx
jmp *(%edx)
- END_ENTRY __objc_msgForward_stret
+ END_ENTRY __objc_msgForward_stret
ENTRY _objc_msgSend_debug
jmp *%eax
END_ENTRY _method_invoke_stret
-
-#if DEBUG
- STATIC_ENTRY __objc_ignored_method
-
- movl self(%esp), %eax
- ret
-
- END_ENTRY __objc_ignored_method
-#endif
.section __DATA,__objc_msg_break
*/
#include <TargetConditionals.h>
-#if __x86_64__ && TARGET_IPHONE_SIMULATOR
+#if __x86_64__ && TARGET_OS_SIMULATOR
/********************************************************************
********************************************************************
.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
.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
#define NORMAL 0
#define FPRET 1
#define FP2RET 2
-#define GETIMP 3
-#define STRET 4
-#define SUPER 5
-#define SUPER_STRET 6
-#define SUPER2 7
-#define SUPER2_STRET 8
-
+#define STRET 3
+
+#define CALL 100
+#define GETIMP 101
+#define LOOKUP 102
+
/********************************************************************
*
.globl $0
.align 6, 0x90
$0:
- .cfi_startproc
.endmacro
.macro STATIC_ENTRY
.private_extern $0
.align 2, 0x90
$0:
- .cfi_startproc
.endmacro
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
.macro END_ENTRY
- .cfi_endproc
LExit$0:
.endmacro
-/////////////////////////////////////////////////////////////////////
-//
-// SaveRegisters
-//
-// Pushes a stack frame and saves all registers that might contain
-// parameter values.
-//
-// On entry:
-// stack = ret
-//
-// On exit:
-// %rsp is 16-byte aligned
-//
-/////////////////////////////////////////////////////////////////////
-
-.macro SaveRegisters
-
- push %rbp
- .cfi_def_cfa_offset 16
- .cfi_offset rbp, -16
-
- mov %rsp, %rbp
- .cfi_def_cfa_register rbp
-
- sub $$0x80+8, %rsp // +8 for alignment
-
- movdqa %xmm0, -0x80(%rbp)
- push %rax // might be xmm parameter count
- movdqa %xmm1, -0x70(%rbp)
- push %a1
- movdqa %xmm2, -0x60(%rbp)
- push %a2
- movdqa %xmm3, -0x50(%rbp)
- push %a3
- movdqa %xmm4, -0x40(%rbp)
- push %a4
- movdqa %xmm5, -0x30(%rbp)
- push %a5
- movdqa %xmm6, -0x20(%rbp)
- push %a6
- movdqa %xmm7, -0x10(%rbp)
-
+ /********************************************************************
+ * UNWIND name, flags
+ * Unwind info generation
+ ********************************************************************/
+.macro UNWIND
+ .section __LD,__compact_unwind,regular,debug
+ .quad $0
+ .set LUnwind$0, LExit$0 - $0
+ .long LUnwind$0
+ .long $1
+ .quad 0 /* no personality */
+ .quad 0 /* no LSDA */
+ .text
.endmacro
-/////////////////////////////////////////////////////////////////////
-//
-// RestoreRegisters
-//
-// Pops a stack frame pushed by SaveRegisters
-//
-// On entry:
-// %rbp unchanged since SaveRegisters
-//
-// On exit:
-// stack = ret
-//
-/////////////////////////////////////////////////////////////////////
-
-.macro RestoreRegisters
-
- movdqa -0x80(%rbp), %xmm0
- pop %a6
- movdqa -0x70(%rbp), %xmm1
- pop %a5
- movdqa -0x60(%rbp), %xmm2
- pop %a4
- movdqa -0x50(%rbp), %xmm3
- pop %a3
- movdqa -0x40(%rbp), %xmm4
- pop %a2
- movdqa -0x30(%rbp), %xmm5
- pop %a1
- movdqa -0x20(%rbp), %xmm6
- pop %rax
- movdqa -0x10(%rbp), %xmm7
-
- leave
- .cfi_def_cfa rsp, 8
- .cfi_same_value rbp
-
-.endmacro
+#define NoFrame 0x02010000 // no frame, no SP adjustment except return address
+#define FrameWithNoSaves 0x01000000 // frame, no non-volatile saves
/////////////////////////////////////////////////////////////////////
// Locate the implementation for a class in a selector's method cache.
//
// Takes:
-// $0 = NORMAL, FPRET, FP2RET, STRET, SUPER, SUPER_STRET, SUPER2, SUPER2_STRET, GETIMP
-// a2 or a3 (STRET) = selector a.k.a. cache
-// r11 = class to search
+// $0 = NORMAL, FPRET, FP2RET, STRET
+// $1 = CALL, LOOKUP, GETIMP
+// a1 or a2 (STRET) = receiver
+// a2 or a3 (STRET) = selector
+// r10 = class to search
//
// On exit: r10 clobbered
-// (found) calls or returns IMP, eq/ne/r11 set for forwarding
-// (not found) jumps to LCacheMiss, class still in r11
+// (found) calls or returns IMP in r11, eq/ne set for forwarding
+// (not found) jumps to LCacheMiss, class still in r10
//
/////////////////////////////////////////////////////////////////////
// CacheHit must always be preceded by a not-taken `jne` instruction
// in order to set the correct flags for _objc_msgForward_impcache.
- // r10 = found bucket
+ // r11 = found bucket
-.if $0 == GETIMP
- movq 8(%r10), %rax // return imp
- leaq __objc_msgSend_uncached_impcache(%rip), %r11
- cmpq %rax, %r11
- jne 4f
- xorl %eax, %eax // don't return msgSend_uncached
-4: ret
-.elseif $0 == NORMAL || $0 == FPRET || $0 == FP2RET
+.if $1 == GETIMP
+ movq 8(%r11), %rax // return imp
+ 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(%r10) // call imp
-
-.elseif $0 == SUPER
- movq receiver(%a1), %a1 // load real receiver
- cmp %r10, %r10 // set eq for non-stret forwarding
- MESSENGER_END_FAST
- jmp *8(%r10) // call imp
-
-.elseif $0 == SUPER2
- movq receiver(%a1), %a1 // load real receiver
- cmp %r10, %r10 // set eq for non-stret forwarding
- MESSENGER_END_FAST
- jmp *8(%r10) // call imp
-
-.elseif $0 == STRET
- test %r10, %r10 // set ne for stret forwarding
- MESSENGER_END_FAST
- jmp *8(%r10) // call imp
+ jmp *8(%r11) // call imp
-.elseif $0 == SUPER_STRET
- movq receiver(%a2), %a2 // load real receiver
- test %r10, %r10 // set ne for stret forwarding
- MESSENGER_END_FAST
- jmp *8(%r10) // call imp
+.elseif $1 == LOOKUP
+ movq 8(%r11), %r11 // return imp
+ ret
-.elseif $0 == SUPER2_STRET
- movq receiver(%a2), %a2 // load real receiver
- test %r10, %r10 // set ne for stret forwarding
- MESSENGER_END_FAST
- jmp *8(%r10) // call imp
.else
.abort oops
.endif
-
+
+.endif
+
.endmacro
.macro CacheLookup
-.if $0 != STRET && $0 != SUPER_STRET && $0 != SUPER2_STRET
- movq %a2, %r10 // r10 = _cmd
+.if $0 != STRET
+ movq %a2, %r11 // r11 = _cmd
.else
- movq %a3, %r10 // r10 = _cmd
+ movq %a3, %r11 // r11 = _cmd
.endif
- andl 24(%r11), %r10d // r10 = _cmd & class->cache.mask
- shlq $$4, %r10 // r10 = offset = (_cmd & mask)<<4
- addq 16(%r11), %r10 // r10 = class->cache.buckets + offset
+ andl 24(%r10), %r11d // r11 = _cmd & class->cache.mask
+ shlq $$4, %r11 // r11 = offset = (_cmd & mask)<<4
+ addq 16(%r10), %r11 // r11 = class->cache.buckets + offset
-.if $0 != STRET && $0 != SUPER_STRET && $0 != SUPER2_STRET
- cmpq (%r10), %a2 // if (bucket->sel != _cmd)
+.if $0 != STRET
+ cmpq (%r11), %a2 // if (bucket->sel != _cmd)
.else
- cmpq (%r10), %a3 // if (bucket->sel != _cmd)
+ cmpq (%r11), %a3 // if (bucket->sel != _cmd)
.endif
jne 1f // scan more
// CacheHit must always be preceded by a not-taken `jne` instruction
- CacheHit $0 // call or return imp
+ CacheHit $0, $1 // call or return imp
1:
// loop
- cmpq $$1, (%r10)
+ cmpq $$1, (%r11)
jbe 3f // if (bucket->sel <= 1) wrap or miss
- addq $$16, %r10 // bucket++
+ addq $$16, %r11 // bucket++
2:
-.if $0 != STRET && $0 != SUPER_STRET && $0 != SUPER2_STRET
- cmpq (%r10), %a2 // if (bucket->sel != _cmd)
+.if $0 != STRET
+ cmpq (%r11), %a2 // if (bucket->sel != _cmd)
.else
- cmpq (%r10), %a3 // if (bucket->sel != _cmd)
+ cmpq (%r11), %a3 // if (bucket->sel != _cmd)
.endif
jne 1b // scan more
// CacheHit must always be preceded by a not-taken `jne` instruction
- CacheHit $0 // call or return imp
+ CacheHit $0, $1 // call or return imp
3:
// wrap or miss
jb LCacheMiss_f // if (bucket->sel < 1) cache miss
// wrap
- movq 8(%r10), %r10 // bucket->imp is really first bucket
+ movq 8(%r11), %r11 // bucket->imp is really first bucket
jmp 2f
// Clone scanning loop to miss instead of hang when cache is corrupt.
1:
// loop
- cmpq $$1, (%r10)
+ cmpq $$1, (%r11)
jbe 3f // if (bucket->sel <= 1) wrap or miss
- addq $$16, %r10 // bucket++
+ addq $$16, %r11 // bucket++
2:
-.if $0 != STRET && $0 != SUPER_STRET && $0 != SUPER2_STRET
- cmpq (%r10), %a2 // if (bucket->sel != _cmd)
+.if $0 != STRET
+ cmpq (%r11), %a2 // if (bucket->sel != _cmd)
.else
- cmpq (%r10), %a3 // if (bucket->sel != _cmd)
+ cmpq (%r11), %a3 // if (bucket->sel != _cmd)
.endif
jne 1b // scan more
// CacheHit must always be preceded by a not-taken `jne` instruction
- CacheHit $0 // call or return imp
+ CacheHit $0, $1 // call or return imp
3:
// double wrap or miss
/////////////////////////////////////////////////////////////////////
//
-// MethodTableLookup classRegister, selectorRegister
+// MethodTableLookup NORMAL|STRET
//
-// Takes: $0 = class to search (a1 or a2 or r10 ONLY)
-// $1 = selector to search for (a2 or a3 ONLY)
-// r11 = class to search
+// Takes: a1 or a2 (STRET) = receiver
+// a2 or a3 (STRET) = selector to search for
+// r10 = class to search
//
-// On exit: imp in %r11
+// On exit: imp in %r11, eq/ne set for forwarding
//
/////////////////////////////////////////////////////////////////////
+
.macro MethodTableLookup
- MESSENGER_END_SLOW
+ push %rbp
+ mov %rsp, %rbp
- SaveRegisters
+ sub $$0x80+8, %rsp // +8 for alignment
+
+ movdqa %xmm0, -0x80(%rbp)
+ push %rax // might be xmm parameter count
+ movdqa %xmm1, -0x70(%rbp)
+ push %a1
+ movdqa %xmm2, -0x60(%rbp)
+ push %a2
+ movdqa %xmm3, -0x50(%rbp)
+ push %a3
+ movdqa %xmm4, -0x40(%rbp)
+ push %a4
+ movdqa %xmm5, -0x30(%rbp)
+ push %a5
+ movdqa %xmm6, -0x20(%rbp)
+ push %a6
+ movdqa %xmm7, -0x10(%rbp)
// _class_lookupMethodAndLoadCache3(receiver, selector, class)
- movq $0, %a1
- movq $1, %a2
- movq %r11, %a3
+.if $0 == NORMAL
+ // receiver already in a1
+ // selector already in a2
+.else
+ movq %a2, %a1
+ movq %a3, %a2
+.endif
+ movq %r10, %a3
call __class_lookupMethodAndLoadCache3
// IMP is now in %rax
movq %rax, %r11
- RestoreRegisters
+ movdqa -0x80(%rbp), %xmm0
+ pop %a6
+ movdqa -0x70(%rbp), %xmm1
+ pop %a5
+ movdqa -0x60(%rbp), %xmm2
+ pop %a4
+ movdqa -0x50(%rbp), %xmm3
+ pop %a3
+ movdqa -0x40(%rbp), %xmm4
+ pop %a2
+ movdqa -0x30(%rbp), %xmm5
+ pop %a1
+ movdqa -0x20(%rbp), %xmm6
+ pop %rax
+ movdqa -0x10(%rbp), %xmm7
+
+.if $0 == NORMAL
+ cmp %r11, %r11 // set eq for nonstret forwarding
+.else
+ test %r11, %r11 // set ne for stret forwarding
+.endif
+
+ leave
.endmacro
/////////////////////////////////////////////////////////////////////
//
// GetIsaCheckNil return-type
-// GetIsaSupport return-type
+// GetIsaSupport return-type
+// NilTestReturnZero return-type
+// NilTestReturnIMP return-type
//
-// Sets r11 = receiver->isa.
+// Sets r10 = obj->isa.
// Looks up the real class if receiver is a tagged pointer object.
-// Returns zero if obj is nil.
+// Returns zero or a zero-returning IMP if obj is nil.
//
// Takes: $0 = NORMAL or FPRET or FP2RET or STRET
// a1 or a2 (STRET) = receiver
//
-// On exit: r11 = receiver->isa
-// r10 is clobbered
+// On exit from GetIsaCheckNil:
+// r10 = receiver->isa
+// r11 is clobbered
//
/////////////////////////////////////////////////////////////////////
-.macro GetIsaCheckNil
-.if $0 == SUPER || $0 == SUPER_STRET
- error super dispatch does not test for nil
-.endif
+.macro ZeroReturn
+ xorl %eax, %eax
+ xorl %edx, %edx
+ xorps %xmm0, %xmm0
+ xorps %xmm1, %xmm1
+.endmacro
+
+.macro ZeroReturnFPRET
+ fldz
+ ZeroReturn
+.endmacro
+
+.macro ZeroReturnFP2RET
+ fldz
+ fldz
+ ZeroReturn
+.endmacro
+
+.macro ZeroReturnSTRET
+ // rax gets the struct-return address as passed in rdi
+ movq %rdi, %rax
+.endmacro
+
+ STATIC_ENTRY __objc_msgNil
+ ZeroReturn
+ ret
+ END_ENTRY __objc_msgNil
+
+ STATIC_ENTRY __objc_msgNil_fpret
+ ZeroReturnFPRET
+ ret
+ END_ENTRY __objc_msgNil_fpret
+ STATIC_ENTRY __objc_msgNil_fp2ret
+ ZeroReturnFP2RET
+ ret
+ END_ENTRY __objc_msgNil_fp2ret
+
+ STATIC_ENTRY __objc_msgNil_stret
+ ZeroReturnSTRET
+ ret
+ END_ENTRY __objc_msgNil_stret
+
+
+.macro GetIsaCheckNil
.if $0 != STRET
testq %a1, %a1
.else
jle LNilOrTagged_f // MSB tagged pointer looks negative
.if $0 != STRET
- movq (%a1), %r11 // r11 = isa
+ movq (%a1), %r10 // r10 = isa
.else
- movq (%a2), %r11 // r11 = isa
+ movq (%a2), %r10 // r10 = isa
.endif
LGetIsaDone:
.macro GetIsaSupport
.align 3
LNilOrTagged:
- jz LNil_f // flags set by NilOrTaggedTest
-
- // tagged
-
- leaq _objc_debug_taggedpointer_classes(%rip), %r11
+ jz LNil_f // flags set by GetIsaCheckNil
.if $0 != STRET
- movq %a1, %r10
+ movq %a1, %r11
.else
- movq %a2, %r10
+ movq %a2, %r11
.endif
- shrq $$60, %r10
- movq (%r11, %r10, 8), %r11 // read isa from table
+ shrq $$60, %r11
+ cmpl $$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:
+ // ext tagged
+.if $0 != STRET
+ movq %a1, %r11
+.else
+ movq %a2, %r11
+.endif
+ shrq $$52, %r11
+ andl $$0xff, %r11d
+ leaq _objc_debug_taggedpointer_ext_classes(%rip), %r10
+ movq (%r10, %r11, 8), %r10 // read isa from table
+ jmp LGetIsaDone_b
+.endmacro
-LNil:
- // nil
-.if $0 == FPRET
- fldz
+.macro NilTestReturnZero
+LNil:
+.if $0 == NORMAL
+ ZeroReturn
+.elseif $0 == FPRET
+ ZeroReturnFPRET
.elseif $0 == FP2RET
- fldz
- fldz
-.endif
-.if $0 == STRET
- movq %rdi, %rax
+ ZeroReturnFP2RET
+.elseif $0 == STRET
+ ZeroReturnSTRET
.else
- xorl %eax, %eax
- xorl %edx, %edx
- xorps %xmm0, %xmm0
- xorps %xmm1, %xmm1
+.abort oops
.endif
MESSENGER_END_NIL
ret
.endmacro
+.macro NilTestReturnIMP
+LNil:
+.if $0 == NORMAL
+ leaq __objc_msgNil(%rip), %r11
+.elseif $0 == FPRET
+ leaq __objc_msgNil_fpret(%rip), %r11
+.elseif $0 == FP2RET
+ leaq __objc_msgNil_fp2ret(%rip), %r11
+.elseif $0 == STRET
+ leaq __objc_msgNil_stret(%rip), %r11
+.else
+.abort oops
+.endif
+ ret
+.endmacro
+
+
/********************************************************************
* IMP cache_getImp(Class cls, SEL sel)
*
STATIC_ENTRY _cache_getImp
// do lookup
- movq %a1, %r11 // move class to r11 for CacheLookup
- CacheLookup GETIMP // returns IMP on success
+ movq %a1, %r10 // move class to r10 for CacheLookup
+ CacheLookup NORMAL, GETIMP // returns IMP on success
LCacheMiss:
// cache miss, return nil
xorl %eax, %eax
ret
-LGetImpExit:
- END_ENTRY _cache_getImp
+ END_ENTRY _cache_getImp
/********************************************************************
*
* id objc_msgSend(id self, SEL _cmd,...);
+ * IMP objc_msgLookup(id self, SEL _cmd, ...);
+ *
+ * objc_msgLookup ABI:
+ * IMP returned in r11
+ * Forwarding returned in Z flag
+ * r10 reserved for our use but not used
*
********************************************************************/
.globl _objc_debug_taggedpointer_classes
_objc_debug_taggedpointer_classes:
.fill 16, 8, 0
+ .globl _objc_debug_taggedpointer_ext_classes
+_objc_debug_taggedpointer_ext_classes:
+ .fill 256, 8, 0
- ENTRY _objc_msgSend
+ ENTRY _objc_msgSend
+ UNWIND _objc_msgSend, NoFrame
MESSENGER_START
- GetIsaCheckNil NORMAL // r11 = self->isa, or return zero
- CacheLookup NORMAL // calls IMP on success
+ GetIsaCheckNil NORMAL // r10 = self->isa, or return zero
+ CacheLookup NORMAL, CALL // calls IMP on success
- GetIsaSupport NORMAL
+ GetIsaSupport NORMAL
+ NilTestReturnZero NORMAL
// cache miss: go search the method lists
LCacheMiss:
- // isa still in r11
- MethodTableLookup %a1, %a2 // r11 = IMP
- cmp %r11, %r11 // set eq (nonstret) for forwarding
- jmp *%r11 // goto *imp
+ // isa still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_uncached
- END_ENTRY _objc_msgSend
+ END_ENTRY _objc_msgSend
+
+
+ ENTRY _objc_msgLookup
+
+ GetIsaCheckNil NORMAL // r10 = self->isa, or return zero IMP
+ CacheLookup NORMAL, LOOKUP // returns IMP on success
+
+ GetIsaSupport NORMAL
+ NilTestReturnIMP NORMAL
+
+// cache miss: go search the method lists
+LCacheMiss:
+ // isa still in r10
+ jmp __objc_msgLookup_uncached
+
+ END_ENTRY _objc_msgLookup
ENTRY _objc_msgSend_fixup
* };
********************************************************************/
- ENTRY _objc_msgSendSuper
+ ENTRY _objc_msgSendSuper
+ UNWIND _objc_msgSendSuper, NoFrame
MESSENGER_START
// search the cache (objc_super in %a1)
- movq class(%a1), %r11 // class = objc_super->class
- CacheLookup SUPER // calls IMP on success
+ movq class(%a1), %r10 // class = objc_super->class
+ movq receiver(%a1), %a1 // load real receiver
+ CacheLookup NORMAL, CALL // calls IMP on success
// cache miss: go search the method lists
LCacheMiss:
- // class still in r11
- movq receiver(%a1), %r10
- MethodTableLookup %r10, %a2 // r11 = IMP
- movq receiver(%a1), %a1 // load real receiver
- cmp %r11, %r11 // set eq (nonstret) for forwarding
- jmp *%r11 // goto *imp
+ // class still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_uncached
- END_ENTRY _objc_msgSendSuper
+ END_ENTRY _objc_msgSendSuper
/********************************************************************
********************************************************************/
ENTRY _objc_msgSendSuper2
+ UNWIND _objc_msgSendSuper2, NoFrame
MESSENGER_START
// objc_super->class is superclass of class to search
// search the cache (objc_super in %a1)
- movq class(%a1), %r11 // cls = objc_super->class
- movq 8(%r11), %r11 // cls = class->superclass
- CacheLookup SUPER2 // calls IMP on success
+ 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
// cache miss: go search the method lists
LCacheMiss:
- // superclass still in r11
- movq receiver(%a1), %r10
- MethodTableLookup %r10, %a2 // r11 = IMP
- movq receiver(%a1), %a1 // load real receiver
- cmp %r11, %r11 // set eq (nonstret) for forwarding
- jmp *%r11 // goto *imp
+ // superclass still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_uncached
- END_ENTRY _objc_msgSendSuper2
+ END_ENTRY _objc_msgSendSuper2
+
+ ENTRY _objc_msgLookupSuper2
+ // objc_super->class is superclass of class to search
+
+// search the cache (objc_super in %a1)
+ 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
+
+// cache miss: go search the method lists
+LCacheMiss:
+ // superclass still in r10
+ jmp __objc_msgLookup_uncached
+
+ END_ENTRY _objc_msgLookupSuper2
+
+
ENTRY _objc_msgSendSuper2_fixup
int3
END_ENTRY _objc_msgSendSuper2_fixup
*
********************************************************************/
- ENTRY _objc_msgSend_fpret
+ ENTRY _objc_msgSend_fpret
+ UNWIND _objc_msgSend_fpret, NoFrame
MESSENGER_START
- GetIsaCheckNil FPRET // r11 = self->isa, or return zero
- CacheLookup FPRET // calls IMP on success
+ GetIsaCheckNil FPRET // r10 = self->isa, or return zero
+ CacheLookup FPRET, CALL // calls IMP on success
- GetIsaSupport FPRET
+ GetIsaSupport FPRET
+ NilTestReturnZero FPRET
// cache miss: go search the method lists
LCacheMiss:
- // isa still in r11
- MethodTableLookup %a1, %a2 // r11 = IMP
- cmp %r11, %r11 // set eq (nonstret) for forwarding
- jmp *%r11 // goto *imp
+ // isa still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_uncached
+
+ END_ENTRY _objc_msgSend_fpret
- END_ENTRY _objc_msgSend_fpret
+
+ ENTRY _objc_msgLookup_fpret
+
+ GetIsaCheckNil FPRET // r10 = self->isa, or return zero IMP
+ CacheLookup FPRET, LOOKUP // returns IMP on success
+
+ GetIsaSupport FPRET
+ NilTestReturnIMP FPRET
+
+// cache miss: go search the method lists
+LCacheMiss:
+ // isa still in r10
+ jmp __objc_msgLookup_uncached
+
+ END_ENTRY _objc_msgLookup_fpret
ENTRY _objc_msgSend_fpret_fixup
*
********************************************************************/
- ENTRY _objc_msgSend_fp2ret
+ ENTRY _objc_msgSend_fp2ret
+ UNWIND _objc_msgSend_fp2ret, NoFrame
MESSENGER_START
- GetIsaCheckNil FP2RET // r11 = self->isa, or return zero
- CacheLookup FP2RET // calls IMP on success
+ GetIsaCheckNil FP2RET // r10 = self->isa, or return zero
+ CacheLookup FP2RET, CALL // calls IMP on success
- GetIsaSupport FP2RET
+ GetIsaSupport FP2RET
+ NilTestReturnZero FP2RET
// cache miss: go search the method lists
LCacheMiss:
- // isa still in r11
- MethodTableLookup %a1, %a2 // r11 = IMP
- cmp %r11, %r11 // set eq (nonstret) for forwarding
- jmp *%r11 // goto *imp
+ // isa still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_uncached
+
+ END_ENTRY _objc_msgSend_fp2ret
+
+
+ ENTRY _objc_msgLookup_fp2ret
+
+ GetIsaCheckNil FP2RET // r10 = self->isa, or return zero IMP
+ CacheLookup FP2RET, LOOKUP // returns IMP on success
+
+ GetIsaSupport FP2RET
+ NilTestReturnIMP FP2RET
+
+// cache miss: go search the method lists
+LCacheMiss:
+ // isa still in r10
+ jmp __objc_msgLookup_uncached
- END_ENTRY _objc_msgSend_fp2ret
+ END_ENTRY _objc_msgLookup_fp2ret
ENTRY _objc_msgSend_fp2ret_fixup
* %a3 is the selector
********************************************************************/
- ENTRY _objc_msgSend_stret
+ 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
+
+ GetIsaSupport STRET
+ NilTestReturnZero STRET
+
+// 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
+
+
+ ENTRY _objc_msgLookup_stret
- GetIsaCheckNil STRET // r11 = self->isa, or return zero
- CacheLookup STRET // calls IMP on success
+ GetIsaCheckNil STRET // r10 = self->isa, or return zero IMP
+ CacheLookup STRET, LOOKUP // returns IMP on success
- GetIsaSupport STRET
+ GetIsaSupport STRET
+ NilTestReturnIMP STRET
// cache miss: go search the method lists
LCacheMiss:
- // isa still in r11
- MethodTableLookup %a2, %a3 // r11 = IMP
- test %r11, %r11 // set ne (stret) for forward; r11!=0
- jmp *%r11 // goto *imp
+ // isa still in r10
+ jmp __objc_msgLookup_stret_uncached
- END_ENTRY _objc_msgSend_stret
+ END_ENTRY _objc_msgLookup_stret
ENTRY _objc_msgSend_stret_fixup
*
********************************************************************/
- ENTRY _objc_msgSendSuper_stret
+ ENTRY _objc_msgSendSuper_stret
+ UNWIND _objc_msgSendSuper_stret, NoFrame
MESSENGER_START
// search the cache (objc_super in %a2)
- movq class(%a2), %r11 // class = objc_super->class
- CacheLookup SUPER_STRET // calls IMP on success
+ movq class(%a2), %r10 // class = objc_super->class
+ movq receiver(%a2), %a2 // load real receiver
+ CacheLookup STRET, CALL // calls IMP on success
// cache miss: go search the method lists
LCacheMiss:
- // class still in r11
- movq receiver(%a2), %r10
- MethodTableLookup %r10, %a3 // r11 = IMP
- movq receiver(%a2), %a2 // load real receiver
- test %r11, %r11 // set ne (stret) for forward; r11!=0
- jmp *%r11 // goto *imp
-
- END_ENTRY _objc_msgSendSuper_stret
+ // class still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_stret_uncached
+
+ END_ENTRY _objc_msgSendSuper_stret
/********************************************************************
* id objc_msgSendSuper2_stret
********************************************************************/
- ENTRY _objc_msgSendSuper2_stret
+ ENTRY _objc_msgSendSuper2_stret
+ UNWIND _objc_msgSendSuper2_stret, NoFrame
MESSENGER_START
// search the cache (objc_super in %a2)
- movq class(%a2), %r11 // class = objc_super->class
- movq 8(%r11), %r11 // class = class->superclass
- CacheLookup SUPER2_STRET // calls IMP on success
+ 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
// cache miss: go search the method lists
LCacheMiss:
- // superclass still in r11
- movq receiver(%a2), %r10
- MethodTableLookup %r10, %a3 // r11 = IMP
+ // superclass still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_stret_uncached
+
+ END_ENTRY _objc_msgSendSuper2_stret
+
+
+ ENTRY _objc_msgLookupSuper2_stret
+
+// search the cache (objc_super in %a2)
+ movq class(%a2), %r10 // class = objc_super->class
movq receiver(%a2), %a2 // load real receiver
- test %r11, %r11 // set ne (stret) for forward; r11!=0
- jmp *%r11 // goto *imp
+ movq 8(%r10), %r10 // class = class->superclass
+ CacheLookup STRET, LOOKUP // returns IMP on success
+
+// cache miss: go search the method lists
+LCacheMiss:
+ // superclass still in r10
+ jmp __objc_msgLookup_stret_uncached
- END_ENTRY _objc_msgSendSuper2_stret
+ END_ENTRY _objc_msgLookupSuper2_stret
ENTRY _objc_msgSendSuper2_stret_fixup
/********************************************************************
*
- * _objc_msgSend_uncached_impcache
* _objc_msgSend_uncached
* _objc_msgSend_stret_uncached
- *
- * Used to erase method cache entries in-place by
- * bouncing them to the uncached lookup.
+ * The uncached method lookup.
*
********************************************************************/
-
- STATIC_ENTRY __objc_msgSend_uncached_impcache
- // Method cache version
-
- // THIS IS NOT A CALLABLE C FUNCTION
- // Out-of-band condition register is NE for stret, EQ otherwise.
- // Out-of-band r11 is the searched class
-
- MESSENGER_START
- nop
- MESSENGER_END_SLOW
-
- jne __objc_msgSend_stret_uncached
- jmp __objc_msgSend_uncached
-
- END_ENTRY __objc_msgSend_uncached_impcache
-
STATIC_ENTRY __objc_msgSend_uncached
-
+ UNWIND __objc_msgSend_uncached, FrameWithNoSaves
+
// THIS IS NOT A CALLABLE C FUNCTION
- // Out-of-band r11 is the searched class
+ // Out-of-band r10 is the searched class
- // r11 is already the class to search
- MethodTableLookup %a1, %a2 // r11 = IMP
- cmp %r11, %r11 // set eq (nonstret) for forwarding
+ // r10 is already the class to search
+ MethodTableLookup NORMAL // r11 = IMP
jmp *%r11 // goto *imp
END_ENTRY __objc_msgSend_uncached
STATIC_ENTRY __objc_msgSend_stret_uncached
+ UNWIND __objc_msgSend_stret_uncached, FrameWithNoSaves
+
// THIS IS NOT A CALLABLE C FUNCTION
- // Out-of-band r11 is the searched class
+ // Out-of-band r10 is the searched class
- // r11 is already the class to search
- MethodTableLookup %a2, %a3 // r11 = IMP
- test %r11, %r11 // set ne (stret) for forward; r11!=0
+ // r10 is already the class to search
+ MethodTableLookup STRET // r11 = IMP
jmp *%r11 // goto *imp
END_ENTRY __objc_msgSend_stret_uncached
+ STATIC_ENTRY __objc_msgLookup_uncached
+ UNWIND __objc_msgLookup_uncached, FrameWithNoSaves
+
+ // THIS IS NOT A CALLABLE C FUNCTION
+ // Out-of-band r10 is the searched class
+
+ // r10 is already the class to search
+ MethodTableLookup NORMAL // r11 = IMP
+ ret
+
+ END_ENTRY __objc_msgLookup_uncached
+
+
+ STATIC_ENTRY __objc_msgLookup_stret_uncached
+ UNWIND __objc_msgLookup_stret_uncached, FrameWithNoSaves
+
+ // THIS IS NOT A CALLABLE C FUNCTION
+ // Out-of-band r10 is the searched class
+
+ // r10 is already the class to search
+ MethodTableLookup STRET // r11 = IMP
+ ret
+
+ END_ENTRY __objc_msgLookup_stret_uncached
+
+
/********************************************************************
*
* id _objc_msgForward(id self, SEL _cmd,...);
*
********************************************************************/
- STATIC_ENTRY __objc_msgForward_impcache
+ STATIC_ENTRY __objc_msgForward_impcache
// Method cache version
// THIS IS NOT A CALLABLE C FUNCTION
jne __objc_msgForward_stret
jmp __objc_msgForward
- END_ENTRY __objc_msgForward_impcache
+ END_ENTRY __objc_msgForward_impcache
- ENTRY __objc_msgForward
+ ENTRY __objc_msgForward
// Non-stret version
movq __objc_forward_handler(%rip), %r11
jmp *%r11
- END_ENTRY __objc_msgForward
+ END_ENTRY __objc_msgForward
- ENTRY __objc_msgForward_stret
+ ENTRY __objc_msgForward_stret
// Struct-return version
-
+
movq __objc_forward_stret_handler(%rip), %r11
jmp *%r11
- END_ENTRY __objc_msgForward_stret
+ END_ENTRY __objc_msgForward_stret
ENTRY _objc_msgSend_debug
END_ENTRY _method_invoke_stret
- STATIC_ENTRY __objc_ignored_method
-
- movq %a1, %rax
- ret
-
- END_ENTRY __objc_ignored_method
-
-
.section __DATA,__objc_msg_break
.quad 0
.quad 0
mov ecx, SELECTOR\r
mov eax, SELF\r
\r
-#if SUPPORT_GC\r
- // check whether selector is ignored\r
-#error oops\r
-#endif\r
-\r
// check whether receiver is nil\r
test eax, eax\r
je NIL\r
mov ecx, SELECTOR\r
mov eax, SELF\r
\r
-#if SUPPORT_GC\r
- // check whether selector is ignored\r
-#error oops\r
-#endif\r
-\r
// check whether receiver is nil\r
test eax, eax\r
je NIL\r
mov ecx, SELECTOR\r
mov edx, super_class[eax]\r
\r
-#if SUPPORT_GC\r
- // check whether selector is ignored\r
-#error oops\r
-#endif\r
-\r
// search the cache (class in edx)\r
// CacheLookup WORD_RETURN, MSG_SENDSUPER\r
push edi\r
mov ecx, SELECTOR_STRET\r
mov eax, SELF_STRET\r
\r
-#if SUPPORT_GC\r
- // check whether selector is ignored\r
-#error oops\r
-#endif\r
-\r
// check whether receiver is nil\r
test eax, eax\r
je NIL\r
mov ecx, SELECTOR_STRET\r
mov edx, super_class[eax]\r
\r
-#if SUPPORT_GC\r
- // check whether selector is ignored\r
-#error oops\r
-#endif\r
-\r
// search the cache (class in edx)\r
// CacheLookup WORD_RETURN, MSG_SENDSUPER\r
push edi\r
jmp eax\r
}\r
}\r
-\r
-\r
-__declspec(naked) id _objc_ignored_method(id obj, SEL sel)\r
-{\r
- return obj;\r
-}\r
*/
#include <TargetConditionals.h>
-#if __x86_64__ && !TARGET_IPHONE_SIMULATOR
+#if __x86_64__ && !TARGET_OS_SIMULATOR
/********************************************************************
********************************************************************
********************************************************************
********************************************************************/
-/********************************************************************
-* Data used by the ObjC runtime.
-*
-********************************************************************/
-
.data
// _objc_entryPoints and _objc_exitPoints are used by objc
.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
.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
#define NORMAL 0
#define FPRET 1
#define FP2RET 2
-#define GETIMP 3
-#define STRET 4
-#define SUPER 5
-#define SUPER_STRET 6
-#define SUPER2 7
-#define SUPER2_STRET 8
-
+#define STRET 3
+
+#define CALL 100
+#define GETIMP 101
+#define LOOKUP 102
+
/********************************************************************
*
#define method_name 0
#define method_imp 16
-// typedef struct {
-// uint128_t floatingPointArgs[8]; // xmm0..xmm7
-// long linkageArea[4]; // r10, rax, ebp, ret
-// long registerArgs[6]; // a1..a6
-// long stackArgs[0]; // variable-size
-// } *marg_list;
-#define FP_AREA 0
-#define LINK_AREA (FP_AREA+8*16)
-#define REG_AREA (LINK_AREA+4*8)
-#define STACK_AREA (REG_AREA+6*8)
-
//////////////////////////////////////////////////////////////////////
//
.globl $0
.align 6, 0x90
$0:
- .cfi_startproc
.endmacro
.macro STATIC_ENTRY
.private_extern $0
.align 2, 0x90
$0:
- .cfi_startproc
.endmacro
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
.macro END_ENTRY
- .cfi_endproc
LExit$0:
.endmacro
-/////////////////////////////////////////////////////////////////////
-//
-// SaveRegisters
-//
-// Pushes a stack frame and saves all registers that might contain
-// parameter values.
-//
-// On entry:
-// stack = ret
-//
-// On exit:
-// %rsp is 16-byte aligned
-//
-/////////////////////////////////////////////////////////////////////
-
-.macro SaveRegisters
-
- push %rbp
- .cfi_def_cfa_offset 16
- .cfi_offset rbp, -16
-
- mov %rsp, %rbp
- .cfi_def_cfa_register rbp
-
- sub $$0x80+8, %rsp // +8 for alignment
-
- movdqa %xmm0, -0x80(%rbp)
- push %rax // might be xmm parameter count
- movdqa %xmm1, -0x70(%rbp)
- push %a1
- movdqa %xmm2, -0x60(%rbp)
- push %a2
- movdqa %xmm3, -0x50(%rbp)
- push %a3
- movdqa %xmm4, -0x40(%rbp)
- push %a4
- movdqa %xmm5, -0x30(%rbp)
- push %a5
- movdqa %xmm6, -0x20(%rbp)
- push %a6
- movdqa %xmm7, -0x10(%rbp)
-
+ /********************************************************************
+ * UNWIND name, flags
+ * Unwind info generation
+ ********************************************************************/
+.macro UNWIND
+ .section __LD,__compact_unwind,regular,debug
+ .quad $0
+ .set LUnwind$0, LExit$0 - $0
+ .long LUnwind$0
+ .long $1
+ .quad 0 /* no personality */
+ .quad 0 /* no LSDA */
+ .text
.endmacro
-/////////////////////////////////////////////////////////////////////
-//
-// RestoreRegisters
-//
-// Pops a stack frame pushed by SaveRegisters
-//
-// On entry:
-// %rbp unchanged since SaveRegisters
-//
-// On exit:
-// stack = ret
-//
-/////////////////////////////////////////////////////////////////////
-
-.macro RestoreRegisters
-
- movdqa -0x80(%rbp), %xmm0
- pop %a6
- movdqa -0x70(%rbp), %xmm1
- pop %a5
- movdqa -0x60(%rbp), %xmm2
- pop %a4
- movdqa -0x50(%rbp), %xmm3
- pop %a3
- movdqa -0x40(%rbp), %xmm4
- pop %a2
- movdqa -0x30(%rbp), %xmm5
- pop %a1
- movdqa -0x20(%rbp), %xmm6
- pop %rax
- movdqa -0x10(%rbp), %xmm7
-
- leave
- .cfi_def_cfa rsp, 8
- .cfi_same_value rbp
-
-.endmacro
+#define NoFrame 0x02010000 // no frame, no SP adjustment except return address
+#define FrameWithNoSaves 0x01000000 // frame, no non-volatile saves
/////////////////////////////////////////////////////////////////////
// Locate the implementation for a class in a selector's method cache.
//
// Takes:
-// $0 = NORMAL, FPRET, FP2RET, STRET, SUPER, SUPER_STRET, SUPER2, SUPER2_STRET, GETIMP
-// a2 or a3 (STRET) = selector a.k.a. cache
-// r11 = class to search
+// $0 = NORMAL, FPRET, FP2RET, STRET
+// $1 = CALL, LOOKUP, GETIMP
+// a1 or a2 (STRET) = receiver
+// a2 or a3 (STRET) = selector
+// r10 = class to search
//
// On exit: r10 clobbered
-// (found) calls or returns IMP, eq/ne/r11 set for forwarding
-// (not found) jumps to LCacheMiss, class still in r11
+// (found) calls or returns IMP in r11, eq/ne set for forwarding
+// (not found) jumps to LCacheMiss, class still in r10
//
/////////////////////////////////////////////////////////////////////
// CacheHit must always be preceded by a not-taken `jne` instruction
// in order to set the correct flags for _objc_msgForward_impcache.
- // r10 = found bucket
+ // r11 = found bucket
-.if $0 == GETIMP
- movq 8(%r10), %rax // return imp
- leaq __objc_msgSend_uncached_impcache(%rip), %r11
- cmpq %rax, %r11
- jne 4f
- xorl %eax, %eax // don't return msgSend_uncached
-4: ret
-.elseif $0 == NORMAL || $0 == FPRET || $0 == FP2RET
+.if $1 == GETIMP
+ movq 8(%r11), %rax // return imp
+ 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(%r10) // call imp
-
-.elseif $0 == SUPER
- movq receiver(%a1), %a1 // load real receiver
- cmp %r10, %r10 // set eq for non-stret forwarding
- MESSENGER_END_FAST
- jmp *8(%r10) // call imp
-
-.elseif $0 == SUPER2
- movq receiver(%a1), %a1 // load real receiver
- cmp %r10, %r10 // set eq for non-stret forwarding
- MESSENGER_END_FAST
- jmp *8(%r10) // call imp
-
-.elseif $0 == STRET
- test %r10, %r10 // set ne for stret forwarding
- MESSENGER_END_FAST
- jmp *8(%r10) // call imp
+ jmp *8(%r11) // call imp
-.elseif $0 == SUPER_STRET
- movq receiver(%a2), %a2 // load real receiver
- test %r10, %r10 // set ne for stret forwarding
- MESSENGER_END_FAST
- jmp *8(%r10) // call imp
+.elseif $1 == LOOKUP
+ movq 8(%r11), %r11 // return imp
+ ret
-.elseif $0 == SUPER2_STRET
- movq receiver(%a2), %a2 // load real receiver
- test %r10, %r10 // set ne for stret forwarding
- MESSENGER_END_FAST
- jmp *8(%r10) // call imp
.else
.abort oops
.endif
-
+
+.endif
+
.endmacro
.macro CacheLookup
-.if $0 != STRET && $0 != SUPER_STRET && $0 != SUPER2_STRET
- movq %a2, %r10 // r10 = _cmd
+.if $0 != STRET
+ movq %a2, %r11 // r11 = _cmd
.else
- movq %a3, %r10 // r10 = _cmd
+ movq %a3, %r11 // r11 = _cmd
.endif
- andl 24(%r11), %r10d // r10 = _cmd & class->cache.mask
- shlq $$4, %r10 // r10 = offset = (_cmd & mask)<<4
- addq 16(%r11), %r10 // r10 = class->cache.buckets + offset
+ andl 24(%r10), %r11d // r11 = _cmd & class->cache.mask
+ shlq $$4, %r11 // r11 = offset = (_cmd & mask)<<4
+ addq 16(%r10), %r11 // r11 = class->cache.buckets + offset
-.if $0 != STRET && $0 != SUPER_STRET && $0 != SUPER2_STRET
- cmpq (%r10), %a2 // if (bucket->sel != _cmd)
+.if $0 != STRET
+ cmpq (%r11), %a2 // if (bucket->sel != _cmd)
.else
- cmpq (%r10), %a3 // if (bucket->sel != _cmd)
+ cmpq (%r11), %a3 // if (bucket->sel != _cmd)
.endif
jne 1f // scan more
// CacheHit must always be preceded by a not-taken `jne` instruction
- CacheHit $0 // call or return imp
+ CacheHit $0, $1 // call or return imp
1:
// loop
- cmpq $$1, (%r10)
+ cmpq $$1, (%r11)
jbe 3f // if (bucket->sel <= 1) wrap or miss
- addq $$16, %r10 // bucket++
+ addq $$16, %r11 // bucket++
2:
-.if $0 != STRET && $0 != SUPER_STRET && $0 != SUPER2_STRET
- cmpq (%r10), %a2 // if (bucket->sel != _cmd)
+.if $0 != STRET
+ cmpq (%r11), %a2 // if (bucket->sel != _cmd)
.else
- cmpq (%r10), %a3 // if (bucket->sel != _cmd)
+ cmpq (%r11), %a3 // if (bucket->sel != _cmd)
.endif
jne 1b // scan more
// CacheHit must always be preceded by a not-taken `jne` instruction
- CacheHit $0 // call or return imp
+ CacheHit $0, $1 // call or return imp
3:
// wrap or miss
jb LCacheMiss_f // if (bucket->sel < 1) cache miss
// wrap
- movq 8(%r10), %r10 // bucket->imp is really first bucket
+ movq 8(%r11), %r11 // bucket->imp is really first bucket
jmp 2f
// Clone scanning loop to miss instead of hang when cache is corrupt.
1:
// loop
- cmpq $$1, (%r10)
+ cmpq $$1, (%r11)
jbe 3f // if (bucket->sel <= 1) wrap or miss
- addq $$16, %r10 // bucket++
+ addq $$16, %r11 // bucket++
2:
-.if $0 != STRET && $0 != SUPER_STRET && $0 != SUPER2_STRET
- cmpq (%r10), %a2 // if (bucket->sel != _cmd)
+.if $0 != STRET
+ cmpq (%r11), %a2 // if (bucket->sel != _cmd)
.else
- cmpq (%r10), %a3 // if (bucket->sel != _cmd)
+ cmpq (%r11), %a3 // if (bucket->sel != _cmd)
.endif
jne 1b // scan more
// CacheHit must always be preceded by a not-taken `jne` instruction
- CacheHit $0 // call or return imp
+ CacheHit $0, $1 // call or return imp
3:
// double wrap or miss
/////////////////////////////////////////////////////////////////////
//
-// MethodTableLookup classRegister, selectorRegister
+// MethodTableLookup NORMAL|STRET
//
-// Takes: $0 = class to search (a1 or a2 or r10 ONLY)
-// $1 = selector to search for (a2 or a3 ONLY)
-// r11 = class to search
+// Takes: a1 or a2 (STRET) = receiver
+// a2 or a3 (STRET) = selector to search for
+// r10 = class to search
//
-// On exit: imp in %r11
+// On exit: imp in %r11, eq/ne set for forwarding
//
/////////////////////////////////////////////////////////////////////
+
.macro MethodTableLookup
- MESSENGER_END_SLOW
+ push %rbp
+ mov %rsp, %rbp
- SaveRegisters
+ sub $$0x80+8, %rsp // +8 for alignment
+
+ movdqa %xmm0, -0x80(%rbp)
+ push %rax // might be xmm parameter count
+ movdqa %xmm1, -0x70(%rbp)
+ push %a1
+ movdqa %xmm2, -0x60(%rbp)
+ push %a2
+ movdqa %xmm3, -0x50(%rbp)
+ push %a3
+ movdqa %xmm4, -0x40(%rbp)
+ push %a4
+ movdqa %xmm5, -0x30(%rbp)
+ push %a5
+ movdqa %xmm6, -0x20(%rbp)
+ push %a6
+ movdqa %xmm7, -0x10(%rbp)
// _class_lookupMethodAndLoadCache3(receiver, selector, class)
- movq $0, %a1
- movq $1, %a2
- movq %r11, %a3
+.if $0 == NORMAL
+ // receiver already in a1
+ // selector already in a2
+.else
+ movq %a2, %a1
+ movq %a3, %a2
+.endif
+ movq %r10, %a3
call __class_lookupMethodAndLoadCache3
// IMP is now in %rax
movq %rax, %r11
- RestoreRegisters
+ movdqa -0x80(%rbp), %xmm0
+ pop %a6
+ movdqa -0x70(%rbp), %xmm1
+ pop %a5
+ movdqa -0x60(%rbp), %xmm2
+ pop %a4
+ movdqa -0x50(%rbp), %xmm3
+ pop %a3
+ movdqa -0x40(%rbp), %xmm4
+ pop %a2
+ movdqa -0x30(%rbp), %xmm5
+ pop %a1
+ movdqa -0x20(%rbp), %xmm6
+ pop %rax
+ movdqa -0x10(%rbp), %xmm7
+
+.if $0 == NORMAL
+ cmp %r11, %r11 // set eq for nonstret forwarding
+.else
+ test %r11, %r11 // set ne for stret forwarding
+.endif
+
+ leave
.endmacro
+
/////////////////////////////////////////////////////////////////////
//
// GetIsaFast return-type
// GetIsaSupport return-type
//
-// Sets r11 = obj->isa. Consults the tagged isa table if necessary.
+// Sets r10 = obj->isa. Consults the tagged isa table if necessary.
//
// Takes: $0 = NORMAL or FPRET or FP2RET or STRET
// a1 or a2 (STRET) = receiver
//
-// On exit: r11 = receiver->isa
-// r10 is clobbered
+// On exit: r10 = receiver->isa
+// r11 is clobbered
//
/////////////////////////////////////////////////////////////////////
testb $$1, %a1b
PN
jnz LGetIsaSlow_f
- movq $$0x00007ffffffffff8, %r11
- andq (%a1), %r11
+ movq $$0x00007ffffffffff8, %r10
+ andq (%a1), %r10
.else
testb $$1, %a2b
PN
jnz LGetIsaSlow_f
- movq $$0x00007ffffffffff8, %r11
- andq (%a2), %r11
+ movq $$0x00007ffffffffff8, %r10
+ andq (%a2), %r10
.endif
LGetIsaDone:
.endmacro
-.macro GetIsaSupport2
+.macro GetIsaSupport
LGetIsaSlow:
- leaq _objc_debug_taggedpointer_classes(%rip), %r11
.if $0 != STRET
- movl %a1d, %r10d
+ movl %a1d, %r11d
.else
- movl %a2d, %r10d
+ movl %a2d, %r11d
.endif
- andl $$0xF, %r10d
- movq (%r11, %r10, 8), %r11 // read isa from table
-.endmacro
-
-.macro GetIsaSupport
- GetIsaSupport2 $0
+ 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:
+ // extended tagged
+.if $0 != STRET
+ movl %a1d, %r11d
+.else
+ movl %a2d, %r11d
+.endif
+ shrl $$4, %r11d
+ andl $$0xFF, %r11d
+ leaq _objc_debug_taggedpointer_ext_classes(%rip), %r10
+ movq (%r10, %r11, 8), %r10 // read isa from table
jmp LGetIsaDone_b
-.endmacro
-
-.macro GetIsa
- GetIsaFast $0
- jmp LGetIsaDone_f
- GetIsaSupport2 $0
-LGetIsaDone:
.endmacro
// Takes: $0 = NORMAL or FPRET or FP2RET or STRET
// %a1 or %a2 (STRET) = receiver
//
-// On exit: Loads non-nil receiver in %a1 or %a2 (STRET), or returns zero.
+// On exit: Loads non-nil receiver in %a1 or %a2 (STRET)
+// or returns.
//
-// NilTestSupport return-type
+// NilTestReturnZero return-type
//
// Takes: $0 = NORMAL or FPRET or FP2RET or STRET
// %a1 or %a2 (STRET) = receiver
//
-// On exit: Loads non-nil receiver in %a1 or %a2 (STRET), or returns zero.
+// On exit: Loads non-nil receiver in %a1 or %a2 (STRET)
+// or returns zero.
+//
+// NilTestReturnIMP return-type
+//
+// Takes: $0 = NORMAL or FPRET or FP2RET or STRET
+// %a1 or %a2 (STRET) = receiver
+//
+// On exit: Loads non-nil receiver in %a1 or %a2 (STRET)
+// or returns an IMP in r11 that returns zero.
//
/////////////////////////////////////////////////////////////////////
-.macro NilTest
-.if $0 == SUPER || $0 == SUPER_STRET
- error super dispatch does not test for nil
-.endif
+.macro ZeroReturn
+ xorl %eax, %eax
+ xorl %edx, %edx
+ xorps %xmm0, %xmm0
+ xorps %xmm1, %xmm1
+.endmacro
+
+.macro ZeroReturnFPRET
+ fldz
+ ZeroReturn
+.endmacro
+.macro ZeroReturnFP2RET
+ fldz
+ fldz
+ ZeroReturn
+.endmacro
+
+.macro ZeroReturnSTRET
+ // rax gets the struct-return address as passed in rdi
+ movq %rdi, %rax
+.endmacro
+
+ STATIC_ENTRY __objc_msgNil
+ ZeroReturn
+ ret
+ END_ENTRY __objc_msgNil
+
+ STATIC_ENTRY __objc_msgNil_fpret
+ ZeroReturnFPRET
+ ret
+ END_ENTRY __objc_msgNil_fpret
+
+ STATIC_ENTRY __objc_msgNil_fp2ret
+ ZeroReturnFP2RET
+ ret
+ END_ENTRY __objc_msgNil_fp2ret
+
+ STATIC_ENTRY __objc_msgNil_stret
+ ZeroReturnSTRET
+ ret
+ END_ENTRY __objc_msgNil_stret
+
+
+.macro NilTest
.if $0 != STRET
testq %a1, %a1
.else
jz LNilTestSlow_f
.endmacro
-.macro NilTestSupport
+
+.macro NilTestReturnZero
.align 3
LNilTestSlow:
-.if $0 == FPRET
- fldz
+
+.if $0 == NORMAL
+ ZeroReturn
+.elseif $0 == FPRET
+ ZeroReturnFPRET
.elseif $0 == FP2RET
- fldz
- fldz
-.endif
-.if $0 == STRET
- movq %rdi, %rax
+ ZeroReturnFP2RET
+.elseif $0 == STRET
+ ZeroReturnSTRET
.else
- xorl %eax, %eax
- xorl %edx, %edx
- xorps %xmm0, %xmm0
- xorps %xmm1, %xmm1
+.abort oops
.endif
MESSENGER_END_NIL
+ ret
+.endmacro
+
+
+.macro NilTestReturnIMP
+ .align 3
+LNilTestSlow:
+
+.if $0 == NORMAL
+ leaq __objc_msgNil(%rip), %r11
+.elseif $0 == FPRET
+ leaq __objc_msgNil_fpret(%rip), %r11
+.elseif $0 == FP2RET
+ leaq __objc_msgNil_fp2ret(%rip), %r11
+.elseif $0 == STRET
+ leaq __objc_msgNil_stret(%rip), %r11
+.else
+.abort oops
+.endif
ret
.endmacro
STATIC_ENTRY _cache_getImp
// do lookup
- movq %a1, %r11 // move class to r11 for CacheLookup
- CacheLookup GETIMP // returns IMP on success
+ movq %a1, %r10 // move class to r10 for CacheLookup
+ CacheLookup NORMAL, GETIMP // returns IMP on success
LCacheMiss:
// cache miss, return nil
xorl %eax, %eax
ret
-LGetImpExit:
- END_ENTRY _cache_getImp
+ END_ENTRY _cache_getImp
/********************************************************************
*
* id objc_msgSend(id self, SEL _cmd,...);
+ * IMP objc_msgLookup(id self, SEL _cmd, ...);
+ *
+ * objc_msgLookup ABI:
+ * IMP returned in r11
+ * Forwarding returned in Z flag
+ * r10 reserved for our use but not used
*
********************************************************************/
.globl _objc_debug_taggedpointer_classes
_objc_debug_taggedpointer_classes:
.fill 16, 8, 0
+ .globl _objc_debug_taggedpointer_ext_classes
+_objc_debug_taggedpointer_ext_classes:
+ .fill 256, 8, 0
- ENTRY _objc_msgSend
+ ENTRY _objc_msgSend
+ UNWIND _objc_msgSend, NoFrame
MESSENGER_START
NilTest NORMAL
- GetIsaFast NORMAL // r11 = self->isa
- CacheLookup NORMAL // calls IMP on success
+ GetIsaFast NORMAL // r10 = self->isa
+ CacheLookup NORMAL, CALL // calls IMP on success
- NilTestSupport NORMAL
+ NilTestReturnZero NORMAL
- GetIsaSupport NORMAL
+ GetIsaSupport NORMAL
// cache miss: go search the method lists
LCacheMiss:
- // isa still in r11
- MethodTableLookup %a1, %a2 // r11 = IMP
- cmp %r11, %r11 // set eq (nonstret) for forwarding
- jmp *%r11 // goto *imp
+ // isa still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_uncached
- END_ENTRY _objc_msgSend
+ END_ENTRY _objc_msgSend
+
+
+ ENTRY _objc_msgLookup
+
+ NilTest NORMAL
+
+ GetIsaFast NORMAL // r10 = self->isa
+ CacheLookup NORMAL, LOOKUP // returns IMP on success
+
+ NilTestReturnIMP NORMAL
+
+ GetIsaSupport NORMAL
+
+// cache miss: go search the method lists
+LCacheMiss:
+ // isa still in r10
+ jmp __objc_msgLookup_uncached
+
+ END_ENTRY _objc_msgLookup
ENTRY _objc_msgSend_fixup
* };
********************************************************************/
- ENTRY _objc_msgSendSuper
+ ENTRY _objc_msgSendSuper
+ UNWIND _objc_msgSendSuper, NoFrame
MESSENGER_START
// search the cache (objc_super in %a1)
- movq class(%a1), %r11 // class = objc_super->class
- CacheLookup SUPER // calls IMP on success
+ movq class(%a1), %r10 // class = objc_super->class
+ movq receiver(%a1), %a1 // load real receiver
+ CacheLookup NORMAL, CALL // calls IMP on success
// cache miss: go search the method lists
LCacheMiss:
- // class still in r11
- movq receiver(%a1), %r10
- MethodTableLookup %r10, %a2 // r11 = IMP
- movq receiver(%a1), %a1 // load real receiver
- cmp %r11, %r11 // set eq (nonstret) for forwarding
- jmp *%r11 // goto *imp
+ // class still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_uncached
- END_ENTRY _objc_msgSendSuper
+ END_ENTRY _objc_msgSendSuper
/********************************************************************
********************************************************************/
ENTRY _objc_msgSendSuper2
+ UNWIND _objc_msgSendSuper2, NoFrame
MESSENGER_START
// objc_super->class is superclass of class to search
// search the cache (objc_super in %a1)
- movq class(%a1), %r11 // cls = objc_super->class
- movq 8(%r11), %r11 // cls = class->superclass
- CacheLookup SUPER2 // calls IMP on success
+ 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
// cache miss: go search the method lists
LCacheMiss:
- // superclass still in r11
- movq receiver(%a1), %r10
- MethodTableLookup %r10, %a2 // r11 = IMP
- movq receiver(%a1), %a1 // load real receiver
- cmp %r11, %r11 // set eq (nonstret) for forwarding
- jmp *%r11 // goto *imp
+ // superclass still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_uncached
+
+ END_ENTRY _objc_msgSendSuper2
+
+
+ ENTRY _objc_msgLookupSuper2
+
+ // objc_super->class is superclass of class to search
- END_ENTRY _objc_msgSendSuper2
+// search the cache (objc_super in %a1)
+ 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
+// cache miss: go search the method lists
+LCacheMiss:
+ // superclass still in r10
+ jmp __objc_msgLookup_uncached
+ END_ENTRY _objc_msgLookupSuper2
+
+
ENTRY _objc_msgSendSuper2_fixup
int3
END_ENTRY _objc_msgSendSuper2_fixup
*
********************************************************************/
- ENTRY _objc_msgSend_fpret
+ ENTRY _objc_msgSend_fpret
+ UNWIND _objc_msgSend_fpret, NoFrame
MESSENGER_START
NilTest FPRET
- GetIsaFast FPRET // r11 = self->isa
- CacheLookup FPRET // calls IMP on success
+ GetIsaFast FPRET // r10 = self->isa
+ CacheLookup FPRET, CALL // calls IMP on success
- NilTestSupport FPRET
+ NilTestReturnZero FPRET
- GetIsaSupport FPRET
+ GetIsaSupport FPRET
// cache miss: go search the method lists
LCacheMiss:
- // isa still in r11
- MethodTableLookup %a1, %a2 // r11 = IMP
- cmp %r11, %r11 // set eq (nonstret) for forwarding
- jmp *%r11 // goto *imp
+ // isa still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_uncached
+
+ END_ENTRY _objc_msgSend_fpret
- END_ENTRY _objc_msgSend_fpret
+
+ ENTRY _objc_msgLookup_fpret
+
+ NilTest FPRET
+
+ GetIsaFast FPRET // r10 = self->isa
+ CacheLookup FPRET, LOOKUP // returns IMP on success
+
+ NilTestReturnIMP FPRET
+
+ GetIsaSupport FPRET
+
+// cache miss: go search the method lists
+LCacheMiss:
+ // isa still in r10
+ jmp __objc_msgLookup_uncached
+
+ END_ENTRY _objc_msgLookup_fpret
ENTRY _objc_msgSend_fpret_fixup
*
********************************************************************/
- ENTRY _objc_msgSend_fp2ret
+ ENTRY _objc_msgSend_fp2ret
+ UNWIND _objc_msgSend_fp2ret, NoFrame
MESSENGER_START
NilTest FP2RET
- GetIsaFast FP2RET // r11 = self->isa
- CacheLookup FP2RET // calls IMP on success
+ GetIsaFast FP2RET // r10 = self->isa
+ CacheLookup FP2RET, CALL // calls IMP on success
- NilTestSupport FP2RET
+ NilTestReturnZero FP2RET
- GetIsaSupport FP2RET
+ GetIsaSupport FP2RET
// cache miss: go search the method lists
LCacheMiss:
- // isa still in r11
- MethodTableLookup %a1, %a2 // r11 = IMP
- cmp %r11, %r11 // set eq (nonstret) for forwarding
- jmp *%r11 // goto *imp
+ // isa still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_uncached
+
+ END_ENTRY _objc_msgSend_fp2ret
+
+
+ ENTRY _objc_msgLookup_fp2ret
+
+ NilTest FP2RET
- END_ENTRY _objc_msgSend_fp2ret
+ GetIsaFast FP2RET // r10 = self->isa
+ CacheLookup FP2RET, LOOKUP // returns IMP on success
+
+ NilTestReturnIMP FP2RET
+
+ GetIsaSupport FP2RET
+
+// cache miss: go search the method lists
+LCacheMiss:
+ // isa still in r10
+ jmp __objc_msgLookup_uncached
+
+ END_ENTRY _objc_msgLookup_fp2ret
ENTRY _objc_msgSend_fp2ret_fixup
* %a3 is the selector
********************************************************************/
- ENTRY _objc_msgSend_stret
+ ENTRY _objc_msgSend_stret
+ UNWIND _objc_msgSend_stret, NoFrame
MESSENGER_START
NilTest STRET
- GetIsaFast STRET // r11 = self->isa
- CacheLookup STRET // calls IMP on success
+ GetIsaFast STRET // r10 = self->isa
+ CacheLookup STRET, CALL // calls IMP on success
- NilTestSupport STRET
+ NilTestReturnZero STRET
- GetIsaSupport STRET
+ GetIsaSupport STRET
// cache miss: go search the method lists
LCacheMiss:
- // isa still in r11
- MethodTableLookup %a2, %a3 // r11 = IMP
- test %r11, %r11 // set ne (stret) for forward; r11!=0
- jmp *%r11 // goto *imp
+ // isa still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_stret_uncached
+
+ END_ENTRY _objc_msgSend_stret
+
- END_ENTRY _objc_msgSend_stret
+ ENTRY _objc_msgLookup_stret
+
+ NilTest STRET
+
+ GetIsaFast STRET // r10 = self->isa
+ CacheLookup STRET, LOOKUP // returns IMP on success
+
+ NilTestReturnIMP STRET
+
+ GetIsaSupport STRET
+
+// cache miss: go search the method lists
+LCacheMiss:
+ // isa still in r10
+ jmp __objc_msgLookup_stret_uncached
+
+ END_ENTRY _objc_msgLookup_stret
ENTRY _objc_msgSend_stret_fixup
*
********************************************************************/
- ENTRY _objc_msgSendSuper_stret
+ ENTRY _objc_msgSendSuper_stret
+ UNWIND _objc_msgSendSuper_stret, NoFrame
MESSENGER_START
// search the cache (objc_super in %a2)
- movq class(%a2), %r11 // class = objc_super->class
- CacheLookup SUPER_STRET // calls IMP on success
+ movq class(%a2), %r10 // class = objc_super->class
+ movq receiver(%a2), %a2 // load real receiver
+ CacheLookup STRET, CALL // calls IMP on success
// cache miss: go search the method lists
LCacheMiss:
- // class still in r11
- movq receiver(%a2), %r10
- MethodTableLookup %r10, %a3 // r11 = IMP
- movq receiver(%a2), %a2 // load real receiver
- test %r11, %r11 // set ne (stret) for forward; r11!=0
- jmp *%r11 // goto *imp
-
- END_ENTRY _objc_msgSendSuper_stret
+ // class still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_stret_uncached
+
+ END_ENTRY _objc_msgSendSuper_stret
/********************************************************************
* id objc_msgSendSuper2_stret
********************************************************************/
- ENTRY _objc_msgSendSuper2_stret
+ ENTRY _objc_msgSendSuper2_stret
+ UNWIND _objc_msgSendSuper2_stret, NoFrame
MESSENGER_START
// search the cache (objc_super in %a2)
- movq class(%a2), %r11 // class = objc_super->class
- movq 8(%r11), %r11 // class = class->superclass
- CacheLookup SUPER2_STRET // calls IMP on success
+ 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
// cache miss: go search the method lists
LCacheMiss:
- // superclass still in r11
- movq receiver(%a2), %r10
- MethodTableLookup %r10, %a3 // r11 = IMP
+ // superclass still in r10
+ MESSENGER_END_SLOW
+ jmp __objc_msgSend_stret_uncached
+
+ END_ENTRY _objc_msgSendSuper2_stret
+
+
+ ENTRY _objc_msgLookupSuper2_stret
+
+// search the cache (objc_super in %a2)
+ movq class(%a2), %r10 // class = objc_super->class
movq receiver(%a2), %a2 // load real receiver
- test %r11, %r11 // set ne (stret) for forward; r11!=0
- jmp *%r11 // goto *imp
+ movq 8(%r10), %r10 // class = class->superclass
+ CacheLookup STRET, LOOKUP // returns IMP on success
+
+// cache miss: go search the method lists
+LCacheMiss:
+ // superclass still in r10
+ jmp __objc_msgLookup_stret_uncached
- END_ENTRY _objc_msgSendSuper2_stret
+ END_ENTRY _objc_msgLookupSuper2_stret
ENTRY _objc_msgSendSuper2_stret_fixup
/********************************************************************
*
- * _objc_msgSend_uncached_impcache
* _objc_msgSend_uncached
* _objc_msgSend_stret_uncached
- *
- * Used to erase method cache entries in-place by
- * bouncing them to the uncached lookup.
+ * _objc_msgLookup_uncached
+ * _objc_msgLookup_stret_uncached
+ *
+ * The uncached method lookup.
*
********************************************************************/
-
- STATIC_ENTRY __objc_msgSend_uncached_impcache
- // Method cache version
-
- // THIS IS NOT A CALLABLE C FUNCTION
- // Out-of-band condition register is NE for stret, EQ otherwise.
- // Out-of-band r11 is the searched class
-
- MESSENGER_START
- nop
- MESSENGER_END_SLOW
-
- jne __objc_msgSend_stret_uncached
- jmp __objc_msgSend_uncached
-
- END_ENTRY __objc_msgSend_uncached_impcache
-
STATIC_ENTRY __objc_msgSend_uncached
-
+ UNWIND __objc_msgSend_uncached, FrameWithNoSaves
+
// THIS IS NOT A CALLABLE C FUNCTION
- // Out-of-band r11 is the searched class
+ // Out-of-band r10 is the searched class
- // r11 is already the class to search
- MethodTableLookup %a1, %a2 // r11 = IMP
- cmp %r11, %r11 // set eq (nonstret) for forwarding
+ // r10 is already the class to search
+ MethodTableLookup NORMAL // r11 = IMP
jmp *%r11 // goto *imp
END_ENTRY __objc_msgSend_uncached
STATIC_ENTRY __objc_msgSend_stret_uncached
+ UNWIND __objc_msgSend_stret_uncached, FrameWithNoSaves
+
// THIS IS NOT A CALLABLE C FUNCTION
- // Out-of-band r11 is the searched class
+ // Out-of-band r10 is the searched class
- // r11 is already the class to search
- MethodTableLookup %a2, %a3 // r11 = IMP
- test %r11, %r11 // set ne (stret) for forward; r11!=0
+ // r10 is already the class to search
+ MethodTableLookup STRET // r11 = IMP
jmp *%r11 // goto *imp
END_ENTRY __objc_msgSend_stret_uncached
+ STATIC_ENTRY __objc_msgLookup_uncached
+ UNWIND __objc_msgLookup_uncached, FrameWithNoSaves
+
+ // THIS IS NOT A CALLABLE C FUNCTION
+ // Out-of-band r10 is the searched class
+
+ // r10 is already the class to search
+ MethodTableLookup NORMAL // r11 = IMP
+ ret
+
+ END_ENTRY __objc_msgLookup_uncached
+
+
+ STATIC_ENTRY __objc_msgLookup_stret_uncached
+ UNWIND __objc_msgLookup_stret_uncached, FrameWithNoSaves
+
+ // THIS IS NOT A CALLABLE C FUNCTION
+ // Out-of-band r10 is the searched class
+
+ // r10 is already the class to search
+ MethodTableLookup STRET // r11 = IMP
+ ret
+
+ END_ENTRY __objc_msgLookup_stret_uncached
+
+
/********************************************************************
*
* id _objc_msgForward(id self, SEL _cmd,...);
*
********************************************************************/
- STATIC_ENTRY __objc_msgForward_impcache
+ STATIC_ENTRY __objc_msgForward_impcache
// Method cache version
// THIS IS NOT A CALLABLE C FUNCTION
jne __objc_msgForward_stret
jmp __objc_msgForward
- END_ENTRY __objc_msgForward_impcache
+ END_ENTRY __objc_msgForward_impcache
- ENTRY __objc_msgForward
+ ENTRY __objc_msgForward
// Non-stret version
movq __objc_forward_handler(%rip), %r11
jmp *%r11
- END_ENTRY __objc_msgForward
+ END_ENTRY __objc_msgForward
- ENTRY __objc_msgForward_stret
+ ENTRY __objc_msgForward_stret
// Struct-return version
movq __objc_forward_stret_handler(%rip), %r11
jmp *%r11
- END_ENTRY __objc_msgForward_stret
+ END_ENTRY __objc_msgForward_stret
ENTRY _objc_msgSend_debug
END_ENTRY _method_invoke_stret
- STATIC_ENTRY __objc_ignored_method
-
- movq %a1, %rax
- ret
-
- END_ENTRY __objc_ignored_method
-
-
.section __DATA,__objc_msg_break
.quad 0
.quad 0
@end
-__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ROOT_CLASS
OBJC_EXPORT
@interface NSObject <NSObject> {
+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
- (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer");
-- (void)finalize;
+- (void)finalize OBJC_DEPRECATED("Objective-C garbage collection is no longer supported");
- (id)copy;
- (id)mutableCopy;
+ (IMP)instanceMethodForSelector:(SEL)aSelector;
- (void)doesNotRecognizeSelector:(SEL)aSelector;
-- (id)forwardingTargetForSelector:(SEL)aSelector __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
+ (BOOL)isSubclassOfClass:(Class)aClass;
-+ (BOOL)resolveClassMethod:(SEL)sel __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
-+ (BOOL)resolveInstanceMethod:(SEL)sel __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
++ (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);
+ (NSUInteger)hash;
+ (Class)superclass;
NSOBJECT_ELSEWHERE_IN(2.2);
NSOBJECT_ELSEWHERE_IN(2.1);
NSOBJECT_ELSEWHERE_IN(2.0);
-#elif TARGET_OS_MAC && !TARGET_OS_IPHONE
+#elif TARGET_OS_OSX
NSOBJECT_ELSEWHERE_IN(10.7);
NSOBJECT_ELSEWHERE_IN(10.6);
NSOBJECT_ELSEWHERE_IN(10.5);
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
- bool trylock() { return slock.trylock(); }
// Address-ordered lock discipline for a pair of side tables.
}
+/*
+ Once upon a time we eagerly cleared *location if we saw the object
+ was deallocating. This confuses code like NSPointerFunctions which
+ tries to pre-flight the raw storage and assumes if the storage is
+ zero then the weak system is done interfering. That is false: the
+ weak system is still going to check and clear the storage later.
+ This can cause objc_weak_error complaints and crashes.
+ So we now don't touch the storage until deallocation completes.
+*/
+
id
objc_loadWeakRetained(id *location)
{
+ id obj;
id result;
+ Class cls;
SideTable *table;
retry:
- result = *location;
- if (!result) return nil;
+ // fixme std::atomic this load
+ obj = *location;
+ if (!obj) return nil;
+ if (obj->isTaggedPointer()) return obj;
- table = &SideTables()[result];
+ table = &SideTables()[obj];
table->lock();
- if (*location != result) {
+ if (*location != obj) {
table->unlock();
goto retry;
}
-
- result = weak_read_no_lock(&table->weak_table, location);
-
+
+ result = obj;
+
+ cls = obj->ISA();
+ if (! cls->hasCustomRR()) {
+ // Fast case. We know +initialize is complete because
+ // default-RR can never be set before then.
+ assert(cls->isInitialized());
+ if (! obj->rootTryRetain()) {
+ result = nil;
+ }
+ }
+ else {
+ // Slow case. We must check for +initialize and call it outside
+ // the lock if necessary in order to avoid deadlocks.
+ if (cls->isInitialized() || _thisThreadIsInitializingClass(cls)) {
+ BOOL (*tryRetain)(id, SEL) = (BOOL(*)(id, SEL))
+ class_getMethodImplementation(cls, SEL_retainWeakReference);
+ if ((IMP)tryRetain == _objc_msgForward) {
+ result = nil;
+ }
+ else if (! (*tryRetain)(obj, SEL_retainWeakReference)) {
+ result = nil;
+ }
+ }
+ else {
+ table->unlock();
+ _class_initialize(cls);
+ goto retry;
+ }
+ }
+
table->unlock();
return result;
}
Autorelease pool implementation
A thread's autorelease pool is a stack of pointers.
- Each pointer is either an object to release, or POOL_SENTINEL which is
+ 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_SENTINEL for that pool. When
+ 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.
objects are stored.
**********************************************************************/
+// 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)
+
BREAKPOINT_FUNCTION(void objc_autoreleaseNoPool(id obj));
+BREAKPOINT_FUNCTION(void objc_autoreleasePoolInvalid(const void *token));
namespace {
}
bool fastcheck() const {
-#if DEBUG
+#if CHECK_AUTORELEASEPOOL
return check();
#else
return (m[0] == M0);
};
-// Set this to 1 to mprotect() autorelease pool contents
-#define PROTECT_AUTORELEASEPOOL 0
-
class AutoreleasePoolPage
{
+ // EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is
+ // pushed and it has never contained any objects. This saves memory
+ // when the top level (i.e. libdispatch) pushes and pops pools but
+ // never uses them.
+# define EMPTY_POOL_PLACEHOLDER ((id*)1)
-#define POOL_SENTINEL nil
+# define POOL_BOUNDARY nil
static pthread_key_t const key = AUTORELEASE_POOL_KEY;
static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
static size_t const SIZE =
void fastcheck(bool die = true)
{
+#if CHECK_AUTORELEASEPOOL
+ check(die);
+#else
if (! magic.fastcheck()) {
busted(die);
}
+#endif
}
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
- if (obj != POOL_SENTINEL) {
+ if (obj != POOL_BOUNDARY) {
objc_release(obj);
}
}
static void tls_dealloc(void *p)
{
+ if (p == (void*)EMPTY_POOL_PLACEHOLDER) {
+ // No objects or pool pages to clean up here.
+ return;
+ }
+
// reinstate TLS value while we work
setHotPage((AutoreleasePoolPage *)p);
}
+ static inline bool haveEmptyPoolPlaceholder()
+ {
+ id *tls = (id *)tls_get_direct(key);
+ return (tls == EMPTY_POOL_PLACEHOLDER);
+ }
+
+ static inline id* setEmptyPoolPlaceholder()
+ {
+ assert(tls_get_direct(key) == nil);
+ tls_set_direct(key, (void *)EMPTY_POOL_PLACEHOLDER);
+ return EMPTY_POOL_PLACEHOLDER;
+ }
+
static inline AutoreleasePoolPage *hotPage()
{
AutoreleasePoolPage *result = (AutoreleasePoolPage *)
tls_get_direct(key);
+ if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
if (result) result->fastcheck();
return result;
}
static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
- // No pool in place.
+ // "No page" could mean no pool has been pushed
+ // or an empty placeholder pool has been pushed and has no contents yet
assert(!hotPage());
- if (obj != POOL_SENTINEL && DebugMissingPools) {
+ bool pushExtraBoundary = false;
+ if (haveEmptyPoolPlaceholder()) {
+ // We are pushing a second pool over the empty placeholder pool
+ // or pushing the first object into the empty placeholder pool.
+ // Before doing that, push a pool boundary on behalf of the pool
+ // that is currently represented by the empty placeholder.
+ pushExtraBoundary = true;
+ }
+ else if (obj != POOL_BOUNDARY && DebugMissingPools) {
// We are pushing an object with no pool in place,
// and no-pool debugging was requested by environment.
- _objc_inform("MISSING POOLS: Object %p of class %s "
+ _objc_inform("MISSING POOLS: (%p) Object %p of class %s "
"autoreleased with no pool in place - "
"just leaking - break on "
"objc_autoreleaseNoPool() to debug",
- (void*)obj, object_getClassName(obj));
+ pthread_self(), (void*)obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return nil;
}
+ else if (obj == POOL_BOUNDARY && !DebugPoolAllocation) {
+ // We are pushing a pool with no pool in place,
+ // and alloc-per-pool debugging was not requested.
+ // Install and return the empty pool placeholder.
+ return setEmptyPoolPlaceholder();
+ }
+
+ // We are pushing an object or a non-placeholder'd pool.
// Install the first page.
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
-
- // Push an autorelease pool boundary if it wasn't already requested.
- if (obj != POOL_SENTINEL) {
- page->add(POOL_SENTINEL);
+
+ // Push a boundary on behalf of the previously-placeholder'd pool.
+ if (pushExtraBoundary) {
+ page->add(POOL_BOUNDARY);
}
-
- // Push the requested object.
+
+ // Push the requested object or pool.
return page->add(obj);
}
assert(obj);
assert(!obj->isTaggedPointer());
id *dest __unused = autoreleaseFast(obj);
- assert(!dest || *dest == obj);
+ assert(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
return obj;
}
id *dest;
if (DebugPoolAllocation) {
// Each autorelease pool starts on a new pool page.
- dest = autoreleaseNewPage(POOL_SENTINEL);
+ dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
- dest = autoreleaseFast(POOL_SENTINEL);
+ dest = autoreleaseFast(POOL_BOUNDARY);
}
- assert(*dest == POOL_SENTINEL);
+ assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
+ static void badPop(void *token)
+ {
+ // Error. For bincompat purposes this is not
+ // fatal in executables built with old SDKs.
+
+ if (DebugPoolAllocation || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0)) {
+ // OBJC_DEBUG_POOL_ALLOCATION or new SDK. Bad pop is fatal.
+ _objc_fatal
+ ("Invalid or prematurely-freed autorelease pool %p.", token);
+ }
+
+ // Old SDK. Bad pop is warned once.
+ static bool complained = false;
+ if (!complained) {
+ complained = true;
+ _objc_inform_now_and_on_crash
+ ("Invalid or prematurely-freed autorelease pool %p. "
+ "Set a breakpoint on objc_autoreleasePoolInvalid to debug. "
+ "Proceeding anyway because the app is old "
+ "(SDK version " SDK_FORMAT "). Memory errors are likely.",
+ token, FORMAT_SDK(sdkVersion()));
+ }
+ objc_autoreleasePoolInvalid(token);
+ }
+
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
+ if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
+ // Popping the top-level placeholder pool.
+ if (hotPage()) {
+ // Pool was used. Pop its contents normally.
+ // Pool pages remain allocated for re-use as usual.
+ pop(coldPage()->begin());
+ } else {
+ // Pool was never used. Clear the placeholder.
+ setHotPage(nil);
+ }
+ return;
+ }
+
page = pageForPointer(token);
stop = (id *)token;
- if (DebugPoolAllocation && *stop != POOL_SENTINEL) {
- // This check is not valid with DebugPoolAllocation off
- // after an autorelease with a pool page but no pool in place.
- _objc_fatal("invalid or prematurely-freed autorelease pool %p; ",
- token);
+ if (*stop != POOL_BOUNDARY) {
+ if (stop == page->begin() && !page->parent) {
+ // Start of coldest page may correctly not be POOL_BOUNDARY:
+ // 1. top-level pool is popped, leaving the cold page in place
+ // 2. an object is autoreleased with no pool
+ } else {
+ // Error. For bincompat purposes this is not
+ // fatal in executables built with old SDKs.
+ return badPop(token);
+ }
}
if (PrintPoolHiwat) printHiwat();
this == coldPage() ? "(cold)" : "");
check(false);
for (id *p = begin(); p < next; p++) {
- if (*p == POOL_SENTINEL) {
+ if (*p == POOL_BOUNDARY) {
_objc_inform("[%p] ################ POOL %p", p, p);
} else {
_objc_inform("[%p] %#16lx %s",
}
_objc_inform("%llu releases pending.", (unsigned long long)objects);
- for (page = coldPage(); page; page = page->child) {
- page->print();
+ if (haveEmptyPoolPlaceholder()) {
+ _objc_inform("[%p] ................ PAGE (placeholder)",
+ EMPTY_POOL_PLACEHOLDER);
+ _objc_inform("[%p] ################ POOL (placeholder)",
+ EMPTY_POOL_PLACEHOLDER);
+ }
+ else {
+ for (page = coldPage(); page; page = page->child) {
+ page->print();
+ }
}
_objc_inform("##############");
}
_objc_inform("POOL HIGHWATER: new high water mark of %u "
- "pending autoreleases for thread %p:",
+ "pending releases for thread %p:",
mark, pthread_self());
void *stack[128];
}
}
-#undef POOL_SENTINEL
+#undef POOL_BOUNDARY
};
// anonymous namespace
// Slow path of clearDeallocating()
-// for objects with indexed isa
+// for objects with nonpointer isa
// that were ever weakly referenced
// or whose retain count ever overflowed to the side table.
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
- assert(isa.indexed && (isa.weakly_referenced || isa.has_sidetable_rc));
+ assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
bool isDeallocating,
bool weaklyReferenced)
{
- assert(!isa.indexed); // should already be changed to not-indexed
+ assert(!isa.nonpointer); // should already be changed to raw pointer
SideTable& table = SideTables()[this];
size_t& refcntStorage = table.refcnts[this];
bool
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
{
- assert(isa.indexed);
+ assert(isa.nonpointer);
SideTable& table = SideTables()[this];
size_t& refcntStorage = table.refcnts[this];
size_t
objc_object::sidetable_subExtraRC_nolock(size_t delta_rc)
{
- assert(isa.indexed);
+ assert(isa.nonpointer);
SideTable& table = SideTables()[this];
RefcountMap::iterator it = table.refcnts.find(this);
size_t
objc_object::sidetable_getExtraRC_nolock()
{
- assert(isa.indexed);
+ assert(isa.nonpointer);
SideTable& table = SideTables()[this];
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) return 0;
#endif
-__attribute__((used,noinline,nothrow))
id
-objc_object::sidetable_retain_slow(SideTable& table)
+objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
- assert(!isa.indexed);
+ assert(!isa.nonpointer);
#endif
-
+ SideTable& table = SideTables()[this];
+
table.lock();
size_t& refcntStorage = table.refcnts[this];
if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
}
-id
-objc_object::sidetable_retain()
-{
-#if SUPPORT_NONPOINTER_ISA
- assert(!isa.indexed);
-#endif
- SideTable& table = SideTables()[this];
-
- if (table.trylock()) {
- size_t& refcntStorage = table.refcnts[this];
- if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
- refcntStorage += SIDE_TABLE_RC_ONE;
- }
- table.unlock();
- return (id)this;
- }
- return sidetable_retain_slow(table);
-}
-
-
bool
objc_object::sidetable_tryRetain()
{
#if SUPPORT_NONPOINTER_ISA
- assert(!isa.indexed);
+ assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
objc_object::sidetable_setWeaklyReferenced_nolock()
{
#if SUPPORT_NONPOINTER_ISA
- assert(!isa.indexed);
+ assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
// rdar://20206767
// return uintptr_t instead of bool so that the various raw-isa
// -release paths all return zero in eax
-__attribute__((used,noinline,nothrow))
uintptr_t
-objc_object::sidetable_release_slow(SideTable& table, bool performDealloc)
+objc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
- assert(!isa.indexed);
+ assert(!isa.nonpointer);
#endif
+ SideTable& table = SideTables()[this];
+
bool do_dealloc = false;
table.lock();
}
-// rdar://20206767
-// return uintptr_t instead of bool so that the various raw-isa
-// -release paths all return zero in eax
-uintptr_t
-objc_object::sidetable_release(bool performDealloc)
-{
-#if SUPPORT_NONPOINTER_ISA
- assert(!isa.indexed);
-#endif
- SideTable& table = SideTables()[this];
-
- bool do_dealloc = false;
-
- if (table.trylock()) {
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it == table.refcnts.end()) {
- do_dealloc = true;
- table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
- } else if (it->second < SIDE_TABLE_DEALLOCATING) {
- // SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
- do_dealloc = true;
- it->second |= SIDE_TABLE_DEALLOCATING;
- } else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
- it->second -= SIDE_TABLE_RC_ONE;
- }
- table.unlock();
- if (do_dealloc && performDealloc) {
- ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
- }
- return do_dealloc;
- }
-
- return sidetable_release_slow(table, performDealloc);
-}
-
-
void
objc_object::sidetable_clearDeallocating()
{
objc_clear_deallocating(id obj)
{
assert(obj);
- assert(!UseGC);
if (obj->isTaggedPointer()) return;
obj->clearDeallocating();
_objc_rootAutorelease(id obj)
{
assert(obj);
- // assert(!UseGC);
- if (UseGC) return obj; // fixme CF calls this when GC is on
-
return obj->rootAutorelease();
}
(void)zone;
obj = class_createInstance(cls, 0);
#else
- if (!zone || UseGC) {
+ if (!zone) {
obj = class_createInstance(cls, 0);
}
else {
}
#endif
- if (!obj) obj = callBadAllocHandler(cls);
+ if (slowpath(!obj)) obj = callBadAllocHandler(cls);
return obj;
}
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
- if (checkNil && !cls) return nil;
+ if (slowpath(checkNil && !cls)) return nil;
#if __OBJC2__
- if (! cls->ISA()->hasCustomAWZ()) {
+ if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// No alloc/allocWithZone implementation. Go straight to the allocator.
// fixme store hasCustomAWZ in the non-meta class and
// add it to canAllocFast's summary
- if (cls->canAllocFast()) {
+ if (fastpath(cls->canAllocFast())) {
// No ctors, raw isa, etc. Go straight to the metal.
bool dtor = cls->hasCxxDtor();
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
- if (!obj) return callBadAllocHandler(cls);
+ if (slowpath(!obj)) return callBadAllocHandler(cls);
obj->initInstanceIsa(cls, dtor);
return obj;
}
else {
// Has ctor or raw isa or something. Use the slower path.
id obj = class_createInstance(cls, 0);
- if (!obj) return callBadAllocHandler(cls);
+ if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
}
}
_objc_rootFinalize(id obj __unused)
{
assert(obj);
- assert(UseGC);
-
- if (UseGC) {
- return;
- }
_objc_fatal("_objc_rootFinalize called with garbage collection off");
}
_objc_rootZone(id obj)
{
(void)obj;
- if (gc_zone) {
- return gc_zone;
- }
#if __OBJC2__
// allocWithZone under __OBJC2__ ignores the zone parameter
return malloc_default_zone();
uintptr_t
_objc_rootHash(id obj)
{
- if (UseGC) {
- return _object_getExternalHash(obj);
- }
return (uintptr_t)obj;
}
void *
objc_autoreleasePoolPush(void)
{
- if (UseGC) return nil;
return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt)
{
- if (UseGC) return;
AutoreleasePoolPage::pop(ctxt);
}
void
_objc_autoreleasePoolPrint(void)
{
- if (UseGC) return;
AutoreleasePoolPage::printAll();
}
[obj dealloc];
}
-#undef objc_retainedObject
-#undef objc_unretainedObject
-#undef objc_unretainedPointer
-
// convert objc_objectptr_t to id, callee must take ownership.
id objc_retainedObject(objc_objectptr_t pointer) { return (id)pointer; }
SideTableInit();
}
+
+#if SUPPORT_TAGGED_POINTERS
+
+// Placeholder for old debuggers. When they inspect an
+// extended tagged pointer object they will see this isa.
+
+@interface __NSUnrecognizedTaggedPointer : NSObject
+@end
+
+@implementation __NSUnrecognizedTaggedPointer
++(void) load { }
+-(id) retain { return self; }
+-(oneway void) release { }
+-(id) autorelease { return self; }
+@end
+
+#endif
+
+
@implementation NSObject
+ (void)load {
- if (UseGC) gc_init2();
}
+ (void)initialize {
_objc_rootDealloc(self);
}
-// Replaced by CF (throws an NSException)
-+ (void)finalize {
-}
-
-- (void)finalize {
- _objc_rootFinalize(self);
+// Previously used by GC. Now a placeholder for binary compatibility.
+- (void) finalize {
}
+ (struct _NSZone *)zone {
#if __OBJC__ && !__OBJC2__
-__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA)
+__OSX_AVAILABLE(10.0)
+__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE
OBJC_ROOT_CLASS
@interface Object
{
#if __OBJC2__
-__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_NA)
+__OSX_AVAILABLE(10.0)
+__IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE
OBJC_ROOT_CLASS
@interface Object {
Class isa;
#include "Object.h"
#include "Protocol.h"
#include "objc-runtime.h"
-#include "objc-auto.h"
// Error Messages
#ifndef _OBJC_LIST_H_
#define _OBJC_LIST_H_
-#if __OBJC__ && !__OBJC2__ && !__cplusplus
+#if __OBJC__ && !__OBJC2__ && !__cplusplus && !__has_feature(objc_arc)
#include <objc/Object.h>
#include <Availability.h>
// All methods of class Protocol are unavailable.
// Use the functions in objc/runtime.h instead.
-__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
@interface Protocol : NSObject
@end
#include <objc/Object.h>
-__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
@interface Protocol : Object
{
@private
/* Looking up information specific to a protocol */
- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_2_0,__IPHONE_2_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");
- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_2_0,__IPHONE_2_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");
@end
#include <mach-o/ldsyms.h>
#include "Protocol.h"
+#include "NSObject.h"
+
+// __IncompleteProtocol is used as the return type of objc_allocateProtocol().
+
+// Old ABI uses NSObject as the superclass even though Protocol uses Object
+// because the R/R implementation for class Protocol is added at runtime
+// by CF, so __IncompleteProtocol would be left without an R/R implementation
+// otherwise, which would break ARC.
-#if __OBJC2__
@interface __IncompleteProtocol : NSObject @end
@implementation __IncompleteProtocol
+#if __OBJC2__
// fixme hack - make __IncompleteProtocol a non-lazy class
+ (void) load { }
-@end
#endif
+@end
+
@implementation Protocol
#define _OBJC_LITTLE_HASHTABLE_H_
#ifndef _OBJC_PRIVATE_H_
-# define OBJC_HASH_AVAILABILITY __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_1, __IPHONE_NA,__IPHONE_NA);
+# define OBJC_HASH_AVAILABILITY \
+ __OSX_DEPRECATED(10.0, 10.1, "NXHashTable is deprecated") \
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE
#else
# define OBJC_HASH_AVAILABILITY
#endif
#define _OBJC_MAPTABLE_H_
#ifndef _OBJC_PRIVATE_H_
-# define OBJC_MAP_AVAILABILITY __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_1, __IPHONE_NA,__IPHONE_NA);
+# define OBJC_MAP_AVAILABILITY \
+ __OSX_DEPRECATED(10.0, 10.1, "NXMapTable is deprecated") \
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE
#else
# define OBJC_MAP_AVAILABILITY
#endif
// key DOES exist in table - use table's key for insertion
} else {
// key DOES NOT exist in table - copy the new key before insertion
- realKey = (void *)strdup((char *)key);
+ realKey = (void *)strdupIfMutable((char *)key);
}
return NXMapInsert(table, realKey, value);
}
if ((realKey = NXMapMember(table, key, &realValue)) != NX_MAPNOTAKEY) {
// key DOES exist in table - remove pair and free key
realValue = NXMapRemove(table, realKey);
- free(realKey); // the key from the table, not necessarily the one given
+ // free the key from the table, not necessarily the one given
+ freeIfMutable((char *)realKey);
return realValue;
} else {
// key DOES NOT exist in table - nothing to do
*/
#if !OBJC_OLD_DISPATCH_PROTOTYPES
OBJC_EXPORT void objc_msgSend(void /* id self, SEL op, ... */ )
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
#else
/**
* Sends a message with a simple return value to an instance of a class.
* are sent using \c objc_msgSendSuper_stret and \c objc_msgSend_stret.
*/
OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Sends a message with a simple return value to the superclass of an instance of a class.
*
* @see objc_msgSend
*/
OBJC_EXPORT id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
#endif
*/
#if !OBJC_OLD_DISPATCH_PROTOTYPES
OBJC_EXPORT void objc_msgSend_stret(void /* id self, SEL op, ... */ )
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARM64_UNAVAILABLE;
OBJC_EXPORT void objc_msgSendSuper_stret(void /* struct objc_super *super, SEL op, ... */ )
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARM64_UNAVAILABLE;
#else
/**
* @see objc_msgSend
*/
OBJC_EXPORT void objc_msgSend_stret(id self, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARM64_UNAVAILABLE;
/**
* @see objc_msgSendSuper
*/
OBJC_EXPORT void objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARM64_UNAVAILABLE;
#endif
# if defined(__i386__)
OBJC_EXPORT void objc_msgSend_fpret(void /* id self, SEL op, ... */ )
- __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.4, 2.0, 9.0, 1.0);
# elif defined(__x86_64__)
OBJC_EXPORT void objc_msgSend_fpret(void /* id self, SEL op, ... */ )
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT void objc_msgSend_fp2ret(void /* id self, SEL op, ... */ )
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
# endif
* \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, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.4, 2.0, 9.0, 1.0);
/* Use objc_msgSendSuper() for fp-returning messages to super. */
/* See also objc_msgSendv_fpret() below. */
* @see objc_msgSend
*/
OBJC_EXPORT long double objc_msgSend_fpret(id self, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
# if __STDC_VERSION__ >= 199901L
OBJC_EXPORT _Complex long double objc_msgSend_fp2ret(id self, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
# else
OBJC_EXPORT void objc_msgSend_fp2ret(id self, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
# endif
/* Use objc_msgSendSuper() for fp-returning messages to super. */
*/
#if !OBJC_OLD_DISPATCH_PROTOTYPES
OBJC_EXPORT void method_invoke(void /* id receiver, Method m, ... */ )
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT void method_invoke_stret(void /* id receiver, Method m, ... */ )
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0)
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0)
OBJC_ARM64_UNAVAILABLE;
#else
OBJC_EXPORT id method_invoke(id receiver, Method m, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT void method_invoke_stret(id receiver, Method m, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0)
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0)
OBJC_ARM64_UNAVAILABLE;
#endif
*/
#if !OBJC_OLD_DISPATCH_PROTOTYPES
OBJC_EXPORT void _objc_msgForward(void /* id receiver, SEL sel, ... */ )
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
OBJC_EXPORT void _objc_msgForward_stret(void /* id receiver, SEL sel, ... */ )
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0)
+ OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0)
OBJC_ARM64_UNAVAILABLE;
#else
OBJC_EXPORT id _objc_msgForward(id receiver, SEL sel, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
OBJC_EXPORT void _objc_msgForward_stret(id receiver, SEL sel, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0)
+ OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0)
OBJC_ARM64_UNAVAILABLE;
#endif
*/
#include <malloc/malloc.h>
+#include <TargetConditionals.h>
#include <objc/objc.h>
#include <objc/runtime.h>
#include <objc/message.h>
// Old static initializer. Used by old crt1.o and old bug workarounds.
OBJC_EXPORT void _objcInit(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/* Images */
typedef struct objc_image_info {
uint32_t version; // currently 0
uint32_t flags;
+
+#if __cplusplus >= 201103L
+ private:
+ enum : uint32_t {
+ IsReplacement = 1<<0, // used for Fix&Continue, now ignored
+ SupportsGC = 1<<1, // image supports GC
+ RequiresGC = 1<<2, // image requires GC
+ OptimizedByDyld = 1<<3, // image is from an optimized shared cache
+ 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
+
+ SwiftVersionMaskShift = 8,
+ SwiftVersionMask = 0xff << SwiftVersionMaskShift // Swift ABI version
+
+ };
+ public:
+ enum : uint32_t {
+ SwiftVersion1 = 1,
+ SwiftVersion1_2 = 2,
+ SwiftVersion2 = 3,
+ SwiftVersion3 = 4
+ };
+
+ public:
+ bool isReplacement() const { return flags & IsReplacement; }
+ bool supportsGC() const { return flags & SupportsGC; }
+ 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; }
+#endif
} objc_image_info;
-// Values for objc_image_info.flags
-#define OBJC_IMAGE_IS_REPLACEMENT (1<<0)
-#define OBJC_IMAGE_SUPPORTS_GC (1<<1)
-#define OBJC_IMAGE_REQUIRES_GC (1<<2)
-#define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3)
-#define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4) // might be re-assignable
+/*
+IsReplacement:
+ Once used for Fix&Continue in old OS X object files (not final linked images)
+ Not currently used.
+
+SupportsGC:
+ App: GC is required. Framework: GC is supported but not required.
+
+RequiresGC:
+ Framework: GC is required.
+
+OptimizedByDyld:
+ Assorted metadata precooked in the dyld shared cache.
+ Never set for images outside the shared cache file itself.
+
+CorrectedSynthesize:
+ Once used on old iOS to mark images that did not have a particular
+ miscompile. Not used by the runtime.
+
+IsSimulated:
+ Image was compiled for a simulator platform. Not used by the runtime.
+
+HasClassProperties:
+ New ABI: category_t.classProperties fields are present.
+ Old ABI: Set by some compilers. Not used by the runtime.
+*/
/* Properties */
// Read or write an object property. Not all object properties use these.
OBJC_EXPORT id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_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)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
- __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0)
- OBJC_GC_UNAVAILABLE;
+ OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
OBJC_EXPORT void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
- __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0)
- OBJC_GC_UNAVAILABLE;
+ OBJC_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)
- __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0)
- OBJC_GC_UNAVAILABLE;
+ 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)
- __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0)
- OBJC_GC_UNAVAILABLE;
+ OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
// Read or write a non-object property. Not all uses are C structs,
// and not all C struct properties use this.
OBJC_EXPORT void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
// Perform a copy of a C++ object using striped locks. Used by non-POD C++ typed atomic properties.
OBJC_EXPORT void objc_copyCppObjectAtomic(void *dest, const void *src, void (*copyHelper) (void *dest, const void *source))
- __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0);
+ OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
/* Classes. */
#if __OBJC2__
OBJC_EXPORT IMP _objc_empty_vtable
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
#endif
OBJC_EXPORT struct objc_cache _objc_empty_cache
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/* Messages */
#if __OBJC2__
// objc_msgSendSuper2() takes the current search class, not its superclass.
OBJC_EXPORT id objc_msgSendSuper2(struct objc_super *super, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.6, 2.0, 9.0, 1.0);
OBJC_EXPORT void objc_msgSendSuper2_stret(struct objc_super *super, SEL op,...)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_2_0)
+ OBJC_AVAILABLE(10.6, 2.0, 9.0, 1.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)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
#endif
#if __OBJC2__
// Old objc_msgSendSuper() does not have a debug version; this is OBJC2 only.
// *_fixup() do not have debug versions; use non-fixup only for debug mode.
OBJC_EXPORT id objc_msgSend_debug(id self, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT id objc_msgSendSuper2_debug(struct objc_super *super, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT void objc_msgSend_stret_debug(id self, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0)
OBJC_ARM64_UNAVAILABLE;
OBJC_EXPORT void objc_msgSendSuper2_stret_debug(struct objc_super *super, SEL op,...)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0)
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0)
OBJC_ARM64_UNAVAILABLE;
# if defined(__i386__)
OBJC_EXPORT double objc_msgSend_fpret_debug(id self, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
# elif defined(__x86_64__)
OBJC_EXPORT long double objc_msgSend_fpret_debug(id self, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
# if __STDC_VERSION__ >= 199901L
OBJC_EXPORT _Complex long double objc_msgSend_fp2ret_debug(id self, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
# else
OBJC_EXPORT void objc_msgSend_fp2ret_debug(id self, SEL op, ...)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
# endif
# endif
#endif
-#if defined(__x86_64__) && TARGET_OS_MAC && !TARGET_IPHONE_SIMULATOR
+#if __OBJC2__
+// Lookup messengers.
+// These are not callable C functions. Do not call them directly.
+// The caller should set the method parameters, call objc_msgLookup(),
+// then immediately call the returned IMP.
+//
+// Generic ABI:
+// - Callee-saved registers are preserved.
+// - Receiver and selector registers may be modified. These values must
+// be passed to the called IMP. Other parameter registers are preserved.
+// - Caller-saved non-parameter registers are not preserved. Some of
+// these registers are used to pass data from objc_msgLookup() to
+// the called IMP and must not be disturbed by the caller.
+// - 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_ARM64_UNAVAILABLE;
+OBJC_EXPORT void objc_msgLookupSuper2_stret(void)
+ OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0)
+ OBJC_ARM64_UNAVAILABLE;
+
+# if defined(__i386__)
+OBJC_EXPORT void objc_msgLookup_fpret(void)
+ OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.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);
+# 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_AVAILABLE_BUT_DEPRECATED(__MAC_10_5, __MAC_10_8, __IPHONE_NA, __IPHONE_NA);
+ __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_AVAILABLE_BUT_DEPRECATED(__MAC_10_5, __MAC_10_8, __IPHONE_NA, __IPHONE_NA);
+ __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_AVAILABLE_BUT_DEPRECATED(__MAC_10_5, __MAC_10_8, __IPHONE_NA, __IPHONE_NA);
+ __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_AVAILABLE_BUT_DEPRECATED(__MAC_10_5, __MAC_10_8, __IPHONE_NA, __IPHONE_NA);
+ __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_AVAILABLE_BUT_DEPRECATED(__MAC_10_5, __MAC_10_8, __IPHONE_NA, __IPHONE_NA);
+ __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_AVAILABLE_BUT_DEPRECATED(__MAC_10_5, __MAC_10_8, __IPHONE_NA, __IPHONE_NA);
+ __OSX_DEPRECATED(10.5, 10.8, "fixup dispatch is no longer optimized")
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
#endif
/* C++-compatible exception handling. */
// Vtable for C++ exception typeinfo for Objective-C types.
OBJC_EXPORT const void *objc_ehtype_vtable[]
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
// C++ exception typeinfo for type `id`.
OBJC_EXPORT struct objc_typeinfo OBJC_EHTYPE_id
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
#endif
uint64_t exceptionClass,
struct _Unwind_Exception *exceptionObject,
struct _Unwind_Context *context)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
#endif
-/* ARR */
+/* ARC */
OBJC_EXPORT id objc_retainBlock(id)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
+
+
+/* Non-pointer isa */
+
+#if __OBJC2__
+
+// Extract class pointer from an isa field.
+
+#if TARGET_OS_SIMULATOR
+ // No simulators use nonpointer isa yet.
+
+#elif __LP64__
+# define OBJC_HAVE_NONPOINTER_ISA 1
+# define OBJC_HAVE_PACKED_NONPOINTER_ISA 1
+
+// 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);
+
+#elif __ARM_ARCH_7K__ >= 2
+# define OBJC_HAVE_NONPOINTER_ISA 1
+# define OBJC_HAVE_INDEXED_NONPOINTER_ISA 1
+
+// Indexed-isa version.
+// if (isa & (uintptr_t)&objc_absolute_indexed_isa_magic_mask == (uintptr_t)&objc_absolute_indexed_isa_magic_value) {
+// uintptr_t index = (isa & (uintptr_t)&objc_absolute_indexed_isa_index_mask) >> (uintptr_t)&objc_absolute_indexed_isa_index_shift;
+// cls = objc_indexed_classes[index];
+// } else
+// 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_EXPORT const struct { char c; } objc_absolute_indexed_isa_magic_value
+ OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
+OBJC_EXPORT const struct { char c; } objc_absolute_indexed_isa_index_mask
+ OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
+OBJC_EXPORT const struct { char c; } objc_absolute_indexed_isa_index_shift
+ OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
+
+#endif
+
+// OBJC2
+#endif
+// _OBJC_ABI_H
#endif
+++ /dev/null
-/*
- * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef _OBJC_ACCESSORS_H_
-#define _OBJC_ACCESSORS_H_
-
-#include <objc/objc.h>
-#include <stddef.h>
-
-__BEGIN_DECLS
-
-#if SUPPORT_GC
-
-extern void objc_setProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy);
-extern id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic);
-
-extern void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy);
-extern id objc_getProperty_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic);
-
-#endif
-
-__END_DECLS
-
-#endif
#include <libkern/OSAtomic.h>
#include "objc-private.h"
-#include "objc-auto.h"
#include "runtime.h"
-#include "objc-accessors.h"
// stub interface declarations to make compiler happy.
- (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;
#define MUTABLE_COPY 2
-id objc_getProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
+id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
objc_release(oldValue);
}
-void objc_setProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
+void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
{
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
bool mutableCopy = (shouldCopy == MUTABLE_COPY);
}
-#if SUPPORT_GC
-
-id objc_getProperty_gc(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
- return *(id*) ((char*)self + offset);
-}
-
-void objc_setProperty_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) {
- if (shouldCopy) {
- newValue = (shouldCopy == MUTABLE_COPY ? [newValue mutableCopyWithZone:nil] : [newValue copyWithZone:nil]);
- }
- objc_assign_ivar(newValue, self, offset);
-}
-
-// objc_getProperty and objc_setProperty are resolver functions in objc-auto.mm
-
-#else
-
-id
-objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic)
-{
- return objc_getProperty_non_gc(self, _cmd, offset, atomic);
-}
-
-void
-objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue,
- BOOL atomic, signed char shouldCopy)
-{
- objc_setProperty_non_gc(self, _cmd, offset, newValue, atomic, shouldCopy);
-}
-
-#endif
-
-
// This entry point was designed wrong. When used as a getter, src needs to be locked so that
// if simultaneously used for a setter then there would be contention on src.
// So we need two locks - one of which will be contended.
-void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong) {
- static StripedMap<spinlock_t> StructLocks;
+void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong __unused) {
spinlock_t *srcLock = nil;
spinlock_t *dstLock = nil;
if (atomic) {
dstLock = &StructLocks[dest];
spinlock_t::lockTwo(srcLock, dstLock);
}
-#if SUPPORT_GC
- if (UseGC && hasStrong) {
- auto_zone_write_barrier_memmove(gc_zone, dest, src, size);
- } else
-#endif
- {
- memmove(dest, src, size);
- }
+
+ memmove(dest, src, size);
+
if (atomic) {
spinlock_t::unlockTwo(srcLock, dstLock);
}
}
void objc_copyCppObjectAtomic(void *dest, const void *src, void (*copyHelper) (void *dest, const void *source)) {
- static StripedMap<spinlock_t> CppObjectLocks;
spinlock_t *srcLock = &CppObjectLocks[src];
spinlock_t *dstLock = &CppObjectLocks[dest];
spinlock_t::lockTwo(srcLock, dstLock);
/*
* OBJC_NO_GC 1: GC is not supported
- * OBJC_NO_GC undef: GC is supported
+ * OBJC_NO_GC undef: GC is supported. This SDK no longer supports this mode.
*
* OBJC_NO_GC_API undef: Libraries must export any symbols that
* dual-mode code may links to.
* OBJC_NO_GC_API 1: Libraries need not export GC-related symbols.
*/
-#if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32
- /* GC is unsupported. GC API symbols are not exported. */
-# define OBJC_NO_GC 1
-# define OBJC_NO_GC_API 1
-#elif TARGET_OS_MAC && __x86_64h__
+#if defined(__OBJC_GC__)
+# error Objective-C garbage collection is not supported.
+#elif TARGET_OS_OSX
/* GC is unsupported. GC API symbols are exported. */
# define OBJC_NO_GC 1
# undef OBJC_NO_GC_API
#else
- /* GC is supported. */
-# undef OBJC_NO_GC
-# undef OBJC_GC_API
+ /* GC is unsupported. GC API symbols are not exported. */
+# define OBJC_NO_GC 1
+# define OBJC_NO_GC_API 1
#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)
+#endif
+
+
/* OBJC_ISA_AVAILABILITY: `isa` will be deprecated or unavailable
* in the future */
#if !defined(OBJC_ISA_AVAILABILITY)
# define OBJC2_UNAVAILABLE UNAVAILABLE_ATTRIBUTE
# else
/* plain C code also falls here, but this is close enough */
-# define OBJC2_UNAVAILABLE __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_5,__MAC_10_5, __IPHONE_2_0,__IPHONE_2_0)
+# 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
+# endif
+#endif
+
+/* OBJC_UNAVAILABLE: unavailable, with a message where supported */
+#if !defined(OBJC_UNAVAILABLE)
+# if __has_extension(attribute_unavailable_with_message)
+# define OBJC_UNAVAILABLE(_msg) __attribute__((unavailable(_msg)))
+# else
+# define OBJC_UNAVAILABLE(_msg) __attribute__((unavailable))
+# endif
+#endif
+
+/* OBJC_DEPRECATED: deprecated, with a message where supported */
+#if !defined(OBJC_DEPRECATED)
+# if __has_extension(attribute_deprecated_with_message)
+# define OBJC_DEPRECATED(_msg) __attribute__((deprecated(_msg)))
+# else
+# define OBJC_DEPRECATED(_msg) __attribute__((deprecated))
# endif
#endif
/* OBJC_ARC_UNAVAILABLE: unavailable with -fobjc-arc */
#if !defined(OBJC_ARC_UNAVAILABLE)
# if __has_feature(objc_arc)
-# if __has_extension(attribute_unavailable_with_message)
-# define OBJC_ARC_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
-# else
-# define OBJC_ARC_UNAVAILABLE __attribute__((unavailable))
-# endif
+# define OBJC_ARC_UNAVAILABLE OBJC_UNAVAILABLE("not available in automatic reference counting mode")
# else
# define OBJC_ARC_UNAVAILABLE
# endif
/* OBJC_ARM64_UNAVAILABLE: unavailable on arm64 (i.e. stret dispatch) */
#if !defined(OBJC_ARM64_UNAVAILABLE)
# if defined(__arm64__)
-# define OBJC_ARM64_UNAVAILABLE __attribute__((unavailable("not available in arm64")))
+# define OBJC_ARM64_UNAVAILABLE OBJC_UNAVAILABLE("not available in arm64")
# else
# define OBJC_ARM64_UNAVAILABLE
# endif
/* OBJC_GC_UNAVAILABLE: unavailable with -fobjc-gc or -fobjc-gc-only */
#if !defined(OBJC_GC_UNAVAILABLE)
-# if __OBJC_GC__
-# if __has_extension(attribute_unavailable_with_message)
-# define OBJC_GC_UNAVAILABLE __attribute__((unavailable("not available in garbage collecting mode")))
-# else
-# define OBJC_GC_UNAVAILABLE __attribute__((unavailable))
-# endif
-# else
-# define OBJC_GC_UNAVAILABLE
-# endif
+# define OBJC_GC_UNAVAILABLE
#endif
#if !defined(OBJC_EXTERN)
+++ /dev/null
-//
-// objc-auto-dump.h
-// objc
-// The raw dump file format
-// See objc-gdb.h for the primitive.
-//
-// Created by Blaine Garst on 12/8/08.
-// Copyright 2008 Apple, Inc. All rights reserved.
-//
-#ifndef _OBJC_AUTO_DUMP_H_
-#define _OBJC_AUTO_DUMP_H_
-
-/*
- * Raw file format definitions
- */
-
-// must be unique in first letter...
-// RAW FORMAT
-#define HEADER "dumpster"
-#define THREAD 't'
-#define LOCAL 'l'
-#define NODE 'n'
-#define REGISTER 'r'
-#define ROOT 'g'
-#define WEAK 'w'
-#define CLASS 'c'
-#define END 'e'
-
-#define SixtyFour 1
-#define Little 2
-
-/*
-
-Raw format, not that anyone should really care. Most programs should use the cooked file reader.
-
-<rawfile := <header> <arch> <middle>* <end>
-<header> := 'd' 'u' 'm' 'p' 's' 't' 'e' 'r' ; the HEADER string
-<arch> := SixtyFour? + Little? ; architecture
-<middle> := <thread> | <root> | <node> | <weak> | <class>
-<thread> := <register> <stack> <local>* ; the triple
-<register> := 'r' longLength [bytes] ; the register bank
-<stack> := 't' longLength [bytes] ; the stack
-<local> := 'l' [long] ; a thread local node
-<root> := 'g' longAddress longValue
-<node> := 'n' longAddress longSize intLayout longRefcount longIsa?
-<weak> := 'w' longAddress longValue
-<class> := 'c' longAddress <name> <strongLayout> <weakLayout>
-<name> := intLength [bytes] ; no null byte
-<strongLayout> := intLength [bytes] ; including 0 byte at end
-<weakLayout> := intLength [bytes] ; including 0 byte at end
-<end> := 'e'
-
- */
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2008 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include "objc-config.h"
-
-#if SUPPORT_GC
-
-#include "objc-private.h"
-#include "objc-auto-dump.h"
-
-#include <auto_zone.h>
-#include <objc/objc.h>
-#include <objc/runtime.h>
-#include <strings.h>
-
-/*
- * Utilities
- */
-
-static char myType() {
- char type = 0;
- if (sizeof(void *) == 8) type |= SixtyFour;
-#if __LITTLE_ENDIAN__
- type |= Little;
-#endif
- return type;
-}
-
-/*
- * Sigh, a mutable set.
- */
-
-typedef struct {
- long *items;
- long count;
- long capacity;
-} pointer_set_t;
-
-static pointer_set_t *new_pointer_set() {
- pointer_set_t *result = (pointer_set_t *)malloc(sizeof(pointer_set_t));
- result->items = (long *)calloc(64, sizeof(long));
- result->count = 0;
- result->capacity = 63; // last valid ptr, also mask
- return result;
-}
-
-static void pointer_set_grow(pointer_set_t *set);
-
-static void pointer_set_add(pointer_set_t *set, long ptr) {
- long hash = ptr & set->capacity;
- while (1) {
- if (!set->items[hash]) {
- set->items[hash] = ptr;
- ++set->count;
- if (set->count*3 > set->capacity*2)
- pointer_set_grow(set);
- return;
- }
- if (set->items[hash] == ptr) return;
- hash = (hash + 1) & set->capacity;
- }
-}
-
-static void pointer_set_grow(pointer_set_t *set) {
- long oldCapacity = set->capacity;
- long *oldItems = set->items;
- long i;
- set->count = 0;
- set->capacity = 2*(oldCapacity+1)-1;
- set->items = (long *)calloc(2*(oldCapacity+1), sizeof(long));
- for (i = 0; i < oldCapacity; ++i)
- if (oldItems[i]) pointer_set_add(set, oldItems[i]);
- free(oldItems);
-}
-
-static void pointer_set_iterate(pointer_set_t *set, void (^block)(long item)) {
- long i;
- for (i = 0; i < set->capacity; ++i)
- if (set->items[i]) block(set->items[i]);
-}
-
-static void pointer_set_dispose(pointer_set_t *set) {
- free(set->items);
- free(set);
-}
-
-/*
- Quickly dump heap to a named file in a pretty raw format.
- */
-bool _objc_dumpHeap(auto_zone_t *zone, const char *filename) {
- // just write interesting info to disk
- int fd = secure_open(filename, O_WRONLY|O_CREAT, geteuid());
- if (fd < 0) return NO;
- FILE *fp = fdopen(fd, "w");
- if (fp == NULL) {
- return NO;
- }
-
- fwrite(HEADER, strlen(HEADER), 1, fp);
- char type2 = myType();
- fwrite(&type2, 1, 1, fp);
-
- // for each thread...
-
- // do registers first
- auto_zone_register_dump dump_registers = ^(const void *base, unsigned long byte_size) {
- char type = REGISTER;
- fwrite(&type, 1, 1, fp);
- //fwrite(REGISTER, strlen(REGISTER), 1, fp);
- fwrite(&byte_size, sizeof(byte_size), 1, fp);
- fwrite(base, byte_size, 1, fp);
- };
-
- // then stacks
- auto_zone_stack_dump dump_stack = ^(const void *base, unsigned long byte_size) {
- char type = THREAD;
- fwrite(&type, 1, 1, fp);
- //fwrite(THREAD, strlen(THREAD), 1, fp);
- fwrite(&byte_size, sizeof(byte_size), 1, fp);
- fwrite(base, byte_size, 1, fp);
- };
-
- // then locals
- void (^dump_local)(const void *, unsigned long, unsigned int, unsigned long) =
- ^(const void *address, unsigned long size, unsigned int layout, unsigned long refcount) {
- // just write the value - rely on it showing up again as a node later
- char type = LOCAL;
- fwrite(&type, 1, 1, fp);
- fwrite(&address, sizeof(address), 1, fp);
- };
-
-
-
- // roots
- auto_zone_root_dump dump_root = ^(const void **address) {
- char type = ROOT;
- fwrite(&type, 1, 1, fp);
- // write the address so that we can catch misregistered globals
- fwrite(&address, sizeof(address), 1, fp);
- // write content, even (?) if zero
- fwrite(address, sizeof(*address), 1, fp);
- };
-
- // the nodes
- pointer_set_t *classes = new_pointer_set();
- auto_zone_node_dump dump_node = ^(const void *address, unsigned long size, unsigned int layout, unsigned long refcount) {
- char type = NODE;
- fwrite(&type, 1, 1, fp);
- fwrite(&address, sizeof(address), 1, fp);
- fwrite(&size, sizeof(size), 1, fp);
- fwrite(&layout, sizeof(layout), 1, fp);
- fwrite(&refcount, sizeof(refcount), 1, fp);
- if ((layout & AUTO_UNSCANNED) != AUTO_UNSCANNED) {
- // now the nodes unfiltered content
- fwrite(address, size, 1, fp);
- }
- if ((layout & AUTO_OBJECT) == AUTO_OBJECT) {
- long theClass = *(long *)address;
- if (theClass) pointer_set_add(classes, theClass);
- }
- };
-
- // weak
- auto_zone_weak_dump dump_weak = ^(const void **address, const void *item) {
- char type = WEAK;
- fwrite(&type, 1, 1, fp);
- fwrite(&address, sizeof(address), 1, fp);
- fwrite(&item, sizeof(item), 1, fp);
- };
-
- auto_zone_dump(zone, dump_stack, dump_registers, dump_local, dump_root, dump_node, dump_weak);
-
- pointer_set_iterate(classes, ^(long cls) {
- char type = CLASS;
- fwrite(&type, 1, 1, fp);
- fwrite(&cls, sizeof(cls), 1, fp); // write address so that we can map it from node isa's
- // classname (for grins)
- const char *className = class_getName((Class)cls);
- unsigned int length = (int)strlen(className);
- fwrite(&length, sizeof(length), 1, fp); // n
- fwrite(className, length, 1, fp); // n bytes
- // strong layout
- const uint8_t *layout = class_getIvarLayout((Class)cls);
- length = layout ? (int)strlen((char *)layout)+1 : 0; // format is <skipnibble><count nibble> ending with <0><0>
- fwrite(&length, sizeof(length), 1, fp); // n
- fwrite(layout, length, 1, fp); // n bytes
- // weak layout
- layout = class_getWeakIvarLayout((Class)cls);
- length = layout ? (int)strlen((char *)layout)+1 : 0; // format is <skipnibble><count nibble> ending with <0><0>
- fwrite(&length, sizeof(length), 1, fp); // n
- fwrite(layout, length, 1, fp); // n bytes
- });
-
- {
- // end
- char type = END;
- fwrite(&type, 1, 1, fp);
- fclose(fp);
- pointer_set_dispose(classes);
- }
- return YES;
-}
-
-#endif
#include <Availability.h>
#include <TargetConditionals.h>
-#if !TARGET_OS_WIN32
#include <sys/types.h>
#include <libkern/OSAtomic.h>
+
+
+// Define OBJC_SILENCE_GC_DEPRECATIONS=1 to temporarily
+// silence deprecation warnings for GC functions.
+
+#if OBJC_SILENCE_GC_DEPRECATIONS
+# define OBJC_GC_DEPRECATED(message)
+#elif __has_extension(attribute_deprecated_with_message)
+# define OBJC_GC_DEPRECATED(message) __attribute__((deprecated(message ". Define OBJC_SILENCE_GC_DEPRECATIONS=1 to temporarily silence this diagnostic.")))
#else
-# define WINVER 0x0501 // target Windows XP and later
-# define _WIN32_WINNT 0x0501 // target Windows XP and later
-# define WIN32_LEAN_AND_MEAN
-// workaround: windef.h typedefs BOOL as int
-# define BOOL WINBOOL
-# include <windows.h>
-# undef BOOL
+# define OBJC_GC_DEPRECATED(message) __attribute__((deprecated))
#endif
-/* objc_collect() options */
enum {
- // choose one
- OBJC_RATIO_COLLECTION = (0 << 0), // run "ratio" generational collections, then a full
- OBJC_GENERATIONAL_COLLECTION = (1 << 0), // run fast incremental collection
- OBJC_FULL_COLLECTION = (2 << 0), // run full collection.
- OBJC_EXHAUSTIVE_COLLECTION = (3 << 0), // run full collections until memory available stops improving
+ OBJC_RATIO_COLLECTION = (0 << 0),
+ OBJC_GENERATIONAL_COLLECTION = (1 << 0),
+ OBJC_FULL_COLLECTION = (2 << 0),
+ OBJC_EXHAUSTIVE_COLLECTION = (3 << 0),
- OBJC_COLLECT_IF_NEEDED = (1 << 3), // run collection only if needed (allocation threshold exceeded)
- OBJC_WAIT_UNTIL_DONE = (1 << 4), // wait (when possible) for collection to end before returning (when collector is running on dedicated thread)
+ OBJC_COLLECT_IF_NEEDED = (1 << 3),
+ OBJC_WAIT_UNTIL_DONE = (1 << 4),
};
-/* objc_clear_stack() options */
enum {
OBJC_CLEAR_RESIDENT_STACK = (1 << 0)
};
-#ifndef OBJC_NO_GC
+#ifndef OBJC_NO_GC
-/* GC declarations */
-/* Collection utilities */
+/* Out-of-line declarations */
OBJC_EXPORT void objc_collect(unsigned long options)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
+ __OSX_DEPRECATED(10.6, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT BOOL objc_collectingEnabled(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+ __OSX_DEPRECATED(10.5, 10.8, "it always returns NO") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT malloc_zone_t *objc_collectableZone(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);
-
-/* GC configuration */
-
-/* Tells collector to wait until specified bytes have been allocated before trying to collect again. */
+ __OSX_DEPRECATED(10.7, 10.8, "it always returns nil") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_setCollectionThreshold(size_t threshold)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
-
-/* Tells collector to run a full collection for every ratio generational collections. */
+ __OSX_DEPRECATED(10.5, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_setCollectionRatio(size_t ratio)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
-
-//
-// GC-safe compare-and-swap
-//
-
-/* Atomic update, with write barrier. */
+ __OSX_DEPRECATED(10.5, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA) OBJC_ARC_UNAVAILABLE;
-/* "Barrier" version also includes memory barrier. */
+ __OSX_DEPRECATED(10.6, 10.8, "use OSAtomicCompareAndSwapPtr instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT BOOL objc_atomicCompareAndSwapPtrBarrier(id predicate, id replacement, volatile id *objectLocation)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA) OBJC_ARC_UNAVAILABLE;
-
-// atomic update of a global variable
+ __OSX_DEPRECATED(10.6, 10.8, "use OSAtomicCompareAndSwapPtrBarrier instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT BOOL objc_atomicCompareAndSwapGlobal(id predicate, id replacement, volatile id *objectLocation)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA) OBJC_ARC_UNAVAILABLE;
+ __OSX_DEPRECATED(10.6, 10.8, "use OSAtomicCompareAndSwapPtr instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT BOOL objc_atomicCompareAndSwapGlobalBarrier(id predicate, id replacement, volatile id *objectLocation)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA) OBJC_ARC_UNAVAILABLE;
-// atomic update of an instance variable
+ __OSX_DEPRECATED(10.6, 10.8, "use OSAtomicCompareAndSwapPtrBarrier instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT BOOL objc_atomicCompareAndSwapInstanceVariable(id predicate, id replacement, volatile id *objectLocation)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA) OBJC_ARC_UNAVAILABLE;
+ __OSX_DEPRECATED(10.6, 10.8, "use OSAtomicCompareAndSwapPtr instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, id replacement, volatile id *objectLocation)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA) OBJC_ARC_UNAVAILABLE;
-
-
-//
-// Read and write barriers
-//
-
+ __OSX_DEPRECATED(10.6, 10.8, "use OSAtomicCompareAndSwapPtrBarrier instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT id objc_assign_strongCast(id val, id *dest)
- __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
+ __OSX_DEPRECATED(10.4, 10.8, "use a simple assignment instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT id objc_assign_global(id val, id *dest)
- __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
+ __OSX_DEPRECATED(10.4, 10.8, "use a simple assignment instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT id objc_assign_threadlocal(id val, id *dest)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);
+ __OSX_DEPRECATED(10.7, 10.8, "use a simple assignment instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT id objc_assign_ivar(id value, id dest, ptrdiff_t offset)
- __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
+ __OSX_DEPRECATED(10.4, 10.8, "use a simple assignment instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void *objc_memmove_collectable(void *dst, const void *src, size_t size)
- __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
-
+ __OSX_DEPRECATED(10.4, 10.8, "use memmove instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT id objc_read_weak(id *location)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+ __OSX_DEPRECATED(10.5, 10.8, "use a simple read instead, or convert to zeroing __weak") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT id objc_assign_weak(id value, id *location)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
-
-
-//
-// Thread management
-//
-
-/* Register the calling thread with the garbage collector. */
+ __OSX_DEPRECATED(10.5, 10.8, "use a simple assignment instead, or convert to zeroing __weak") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_registerThreadWithCollector(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
-
-/* Unregisters the calling thread with the garbage collector.
- Unregistration also happens automatically at thread exit. */
+ __OSX_DEPRECATED(10.6, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_unregisterThreadWithCollector(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
-
-/* To be called from code which must only execute on a registered thread. */
-/* If the calling thread is unregistered then an error message is emitted and the thread is implicitly registered. */
+ __OSX_DEPRECATED(10.6, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_assertRegisteredThreadWithCollector(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_NA);
-
-/* Erases any stale references in unused parts of the stack. */
+ __OSX_DEPRECATED(10.6, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_clear_stack(unsigned long options)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
-
-
-//
-// Finalization
-//
-
-/* Returns true if object has been scheduled for finalization. Can be used to avoid operations that may lead to resurrection, which are fatal. */
+ __OSX_DEPRECATED(10.5, 10.8, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT BOOL objc_is_finalized(void *ptr)
- __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
-
-// Deprcated. Tells runtime to issue finalize calls on the main thread only.
+ __OSX_DEPRECATED(10.4, 10.8, "it always returns NO") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_finalizeOnMainThread(Class cls)
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_5,__MAC_10_5, __IPHONE_NA,__IPHONE_NA);
-
-
-//
-// Deprecated names.
-//
-
-/* Deprecated. Use objc_collectingEnabled() instead. */
+ __OSX_DEPRECATED(10.5, 10.5, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT BOOL objc_collecting_enabled(void)
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4,__MAC_10_5, __IPHONE_NA,__IPHONE_NA);
-/* Deprecated. Use objc_setCollectionThreshold() instead. */
+ __OSX_DEPRECATED(10.4, 10.5, "it always returns NO") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_set_collection_threshold(size_t threshold)
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4,__MAC_10_5, __IPHONE_NA,__IPHONE_NA);
-/* Deprecated. Use objc_setCollectionRatio() instead. */
+ __OSX_DEPRECATED(10.4, 10.5, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_set_collection_ratio(size_t ratio)
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4,__MAC_10_5, __IPHONE_NA,__IPHONE_NA);
-/* Deprecated. Use objc_startCollectorThread() instead. */
+ __OSX_DEPRECATED(10.4, 10.5, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_start_collector_thread(void)
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4,__MAC_10_5, __IPHONE_NA,__IPHONE_NA);
-/* Deprecated. No replacement. Formerly told the collector to run using a dedicated background thread. */
+ __OSX_DEPRECATED(10.4, 10.5, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_startCollectorThread(void)
-__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_5,__MAC_10_7, __IPHONE_NA,__IPHONE_NA);
-
-
-/* Deprecated. Use class_createInstance() instead. */
+ __OSX_DEPRECATED(10.5, 10.7, "it does nothing") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT id objc_allocate_object(Class cls, int extra)
-__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_4,__MAC_10_4, __IPHONE_NA,__IPHONE_NA);
+ __OSX_DEPRECATED(10.4, 10.4, "use class_createInstance instead") __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
/* !defined(OBJC_NO_GC) */
/* defined(OBJC_NO_GC) */
-/* Non-GC declarations */
+/* Inline declarations */
+OBJC_GC_DEPRECATED("it does nothing")
static OBJC_INLINE void objc_collect(unsigned long options __unused) { }
+OBJC_GC_DEPRECATED("it always returns NO")
static OBJC_INLINE BOOL objc_collectingEnabled(void) { return NO; }
-#if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && !TARGET_IPHONE_SIMULATOR
+#if TARGET_OS_OSX
+OBJC_GC_DEPRECATED("it always returns nil")
static OBJC_INLINE malloc_zone_t *objc_collectableZone(void) { return nil; }
#endif
+OBJC_GC_DEPRECATED("it does nothing")
static OBJC_INLINE void objc_setCollectionThreshold(size_t threshold __unused) { }
+OBJC_GC_DEPRECATED("it does nothing")
static OBJC_INLINE void objc_setCollectionRatio(size_t ratio __unused) { }
+OBJC_GC_DEPRECATED("it does nothing")
static OBJC_INLINE void objc_startCollectorThread(void) { }
#if __has_feature(objc_arc)
#else
-#if TARGET_OS_WIN32
-static OBJC_INLINE BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation)
- { void *original = InterlockedCompareExchangePointer((void * volatile *)objectLocation, (void *)replacement, (void *)predicate); return (original == predicate); }
-
-static OBJC_INLINE 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_GC_DEPRECATED("use OSAtomicCompareAndSwapPtr instead")
static OBJC_INLINE BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation)
{ return OSAtomicCompareAndSwapPtr((void *)predicate, (void *)replacement, (void * volatile *)objectLocation); }
+OBJC_GC_DEPRECATED("use OSAtomicCompareAndSwapPtrBarrier instead")
static OBJC_INLINE BOOL objc_atomicCompareAndSwapPtrBarrier(id predicate, id replacement, volatile id *objectLocation)
{ return OSAtomicCompareAndSwapPtrBarrier((void *)predicate, (void *)replacement, (void * volatile *)objectLocation); }
-#endif
+OBJC_GC_DEPRECATED("use OSAtomicCompareAndSwapPtr instead")
static OBJC_INLINE BOOL objc_atomicCompareAndSwapGlobal(id predicate, id replacement, volatile id *objectLocation)
{ return objc_atomicCompareAndSwapPtr(predicate, replacement, objectLocation); }
+OBJC_GC_DEPRECATED("use OSAtomicCompareAndSwapPtrBarrier instead")
static OBJC_INLINE BOOL objc_atomicCompareAndSwapGlobalBarrier(id predicate, id replacement, volatile id *objectLocation)
{ return objc_atomicCompareAndSwapPtrBarrier(predicate, replacement, objectLocation); }
+OBJC_GC_DEPRECATED("use OSAtomicCompareAndSwapPtr instead")
static OBJC_INLINE BOOL objc_atomicCompareAndSwapInstanceVariable(id predicate, id replacement, volatile id *objectLocation)
{ return objc_atomicCompareAndSwapPtr(predicate, replacement, objectLocation); }
+OBJC_GC_DEPRECATED("use OSAtomicCompareAndSwapPtrBarrier instead")
static OBJC_INLINE BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, id replacement, volatile id *objectLocation)
{ return objc_atomicCompareAndSwapPtrBarrier(predicate, replacement, objectLocation); }
+OBJC_GC_DEPRECATED("use a simple assignment instead")
static OBJC_INLINE id objc_assign_strongCast(id val, id *dest)
{ return (*dest = val); }
+OBJC_GC_DEPRECATED("use a simple assignment instead")
static OBJC_INLINE id objc_assign_global(id val, id *dest)
{ return (*dest = val); }
+OBJC_GC_DEPRECATED("use a simple assignment instead")
static OBJC_INLINE id objc_assign_threadlocal(id val, id *dest)
{ return (*dest = val); }
+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); }
+OBJC_GC_DEPRECATED("use a simple read instead, or convert to zeroing __weak")
static OBJC_INLINE id objc_read_weak(id *location)
{ return *location; }
+OBJC_GC_DEPRECATED("use a simple assignment instead, or convert to zeroing __weak")
static OBJC_INLINE id objc_assign_weak(id value, id *location)
{ return (*location = value); }
/* MRC */
#endif
+OBJC_GC_DEPRECATED("use memmove instead")
static OBJC_INLINE void *objc_memmove_collectable(void *dst, const void *src, size_t size)
{ return memmove(dst, src, size); }
+OBJC_GC_DEPRECATED("it does nothing")
static OBJC_INLINE void objc_finalizeOnMainThread(Class cls __unused) { }
+OBJC_GC_DEPRECATED("it always returns NO")
static OBJC_INLINE BOOL objc_is_finalized(void *ptr __unused) { return NO; }
+OBJC_GC_DEPRECATED("it does nothing")
static OBJC_INLINE void objc_clear_stack(unsigned long options __unused) { }
-
+OBJC_GC_DEPRECATED("it always returns NO")
static OBJC_INLINE BOOL objc_collecting_enabled(void) { return NO; }
+OBJC_GC_DEPRECATED("it does nothing")
static OBJC_INLINE void objc_set_collection_threshold(size_t threshold __unused) { }
+OBJC_GC_DEPRECATED("it does nothing")
static OBJC_INLINE void objc_set_collection_ratio(size_t ratio __unused) { }
+OBJC_GC_DEPRECATED("it does nothing")
static OBJC_INLINE void objc_start_collector_thread(void) { }
#if __has_feature(objc_arc)
extern id objc_allocate_object(Class cls, int extra) UNAVAILABLE_ATTRIBUTE;
#else
OBJC_EXPORT id class_createInstance(Class cls, size_t extraBytes)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
+OBJC_GC_DEPRECATED("use class_createInstance instead")
static OBJC_INLINE id objc_allocate_object(Class cls, int extra)
{ return class_createInstance(cls, extra); }
#endif
+OBJC_GC_DEPRECATED("it does nothing")
static OBJC_INLINE void objc_registerThreadWithCollector() { }
+OBJC_GC_DEPRECATED("it does nothing")
static OBJC_INLINE void objc_unregisterThreadWithCollector() { }
+OBJC_GC_DEPRECATED("it does nothing")
static OBJC_INLINE void objc_assertRegisteredThreadWithCollector() { }
/* defined(OBJC_NO_GC) */
#endif
-#if TARGET_OS_EMBEDDED
-enum {
- OBJC_GENERATIONAL = (1 << 0)
-};
-static OBJC_INLINE void objc_collect_if_needed(unsigned long options) __attribute__((deprecated));
-static OBJC_INLINE void objc_collect_if_needed(unsigned long options __unused) { }
-#endif
-
#endif
#include "objc-private.h"
+// GC is no longer supported.
-#if OBJC_NO_GC && OBJC_NO_GC_API
+#if OBJC_NO_GC_API
// No GC and no GC symbols needed. We're done here.
+# if SUPPORT_GC_COMPAT
+# error inconsistent config settings
+# endif
-#elif OBJC_NO_GC && !OBJC_NO_GC_API
+#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 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)
{ return (*dest = val); }
OBJC_EXPORT BOOL objc_dumpHeap(char *filename __unused, unsigned long length __unused)
{ return NO; }
-// OBJC_NO_GC && !OBJC_NO_GC_API
-#else
-// !OBJC_NO_GC
-
-// Garbage collection.
-
-#include <stdint.h>
-#include <stdbool.h>
-#include <fcntl.h>
-#include <dlfcn.h>
-#include <mach/mach.h>
-#include <mach-o/dyld.h>
-#include <mach-o/nlist.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <libkern/OSAtomic.h>
-#include <auto_zone.h>
-
-#include <Block_private.h>
-#include <dispatch/private.h>
-
-#include "objc-private.h"
-#include "objc-config.h"
-#include "objc-accessors.h"
-#include "objc-auto.h"
-#include "objc-references.h"
-#include "maptable.h"
-#include "message.h"
-#include "objc-gdb.h"
-
-#if DEBUG && !__OBJC2__
-#include "objc-exception.h"
-#endif
-
-
-static auto_zone_t *gc_zone_init(void);
-static void gc_block_init(void);
-static void registeredClassTableInit(void);
-static bool objc_isRegisteredClass(Class candidate);
-
-int8_t UseGC = -1;
-static bool WantsMainThreadFinalization = NO;
-
-auto_zone_t *gc_zone = nil;
-
-
-/* Method prototypes */
-@interface DoesNotExist
-- (const char *)UTF8String;
-- (id)description;
-@end
-
-
-/***********************************************************************
-* Break-on-error functions
-**********************************************************************/
-
-BREAKPOINT_FUNCTION(
- void objc_assign_ivar_error(id base, ptrdiff_t offset)
-);
-
-BREAKPOINT_FUNCTION(
- void objc_assign_global_error(id value, id *slot)
-);
-
-BREAKPOINT_FUNCTION(
- void objc_exception_during_finalize_error(void)
-);
-
-/***********************************************************************
-* Utility exports
-* Called by various libraries.
-**********************************************************************/
-
-OBJC_EXPORT void objc_set_collection_threshold(size_t threshold) { // Old naming
- if (UseGC) {
- auto_collection_parameters(gc_zone)->collection_threshold = threshold;
- }
-}
-
-OBJC_EXPORT void objc_setCollectionThreshold(size_t threshold) {
- if (UseGC) {
- auto_collection_parameters(gc_zone)->collection_threshold = threshold;
- }
-}
-
-void objc_setCollectionRatio(size_t ratio) {
- if (UseGC) {
- auto_collection_parameters(gc_zone)->full_vs_gen_frequency = ratio;
- }
-}
-
-void objc_set_collection_ratio(size_t ratio) { // old naming
- if (UseGC) {
- auto_collection_parameters(gc_zone)->full_vs_gen_frequency = ratio;
- }
-}
-
-void objc_finalizeOnMainThread(Class cls) {
- if (UseGC) {
- WantsMainThreadFinalization = YES;
- cls->setShouldFinalizeOnMainThread();
- }
-}
-
-// stack based data structure queued if/when there is main-thread-only finalization work TBD
-typedef struct BatchFinalizeBlock {
- auto_zone_foreach_object_t foreach;
- auto_zone_cursor_t cursor;
- size_t cursor_size;
- volatile bool finished;
- volatile bool started;
- struct BatchFinalizeBlock *next;
-} BatchFinalizeBlock_t;
-
-// The Main Thread Finalization Work Queue Head
-static struct {
- pthread_mutex_t mutex;
- pthread_cond_t condition;
- BatchFinalizeBlock_t *head;
- BatchFinalizeBlock_t *tail;
-} MainThreadWorkQ;
-
-
-void objc_startCollectorThread(void) {
-}
-
-void objc_start_collector_thread(void) {
-}
-
-static void batchFinalizeOnMainThread(void);
-
-void objc_collect(unsigned long options) {
- if (!UseGC) return;
- bool onMainThread = pthread_main_np();
-
- // while we're here, sneak off and do some finalization work (if any)
- if (onMainThread) batchFinalizeOnMainThread();
- // now on with our normally scheduled programming
- auto_zone_options_t amode = AUTO_ZONE_COLLECT_NO_OPTIONS;
- if (!(options & OBJC_COLLECT_IF_NEEDED)) {
- switch (options & 0x3) {
- case OBJC_RATIO_COLLECTION: amode = AUTO_ZONE_COLLECT_RATIO_COLLECTION; break;
- case OBJC_GENERATIONAL_COLLECTION: amode = AUTO_ZONE_COLLECT_GENERATIONAL_COLLECTION; break;
- case OBJC_FULL_COLLECTION: amode = AUTO_ZONE_COLLECT_FULL_COLLECTION; break;
- case OBJC_EXHAUSTIVE_COLLECTION: amode = AUTO_ZONE_COLLECT_EXHAUSTIVE_COLLECTION; break;
- }
- amode |= AUTO_ZONE_COLLECT_COALESCE;
- amode |= AUTO_ZONE_COLLECT_LOCAL_COLLECTION;
- }
- if (options & OBJC_WAIT_UNTIL_DONE) {
- __block bool done = NO;
- // If executing on the main thread, use the main thread work queue condition to block,
- // so main thread finalization can complete. Otherwise, use a thread-local condition.
- pthread_mutex_t localMutex = PTHREAD_MUTEX_INITIALIZER, *mutex = &localMutex;
- pthread_cond_t localCondition = PTHREAD_COND_INITIALIZER, *condition = &localCondition;
- if (onMainThread) {
- mutex = &MainThreadWorkQ.mutex;
- condition = &MainThreadWorkQ.condition;
- }
- pthread_mutex_lock(mutex);
- auto_zone_collect_and_notify(gc_zone, amode, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- pthread_mutex_lock(mutex);
- done = YES;
- pthread_cond_signal(condition);
- pthread_mutex_unlock(mutex);
- });
- while (!done) {
- pthread_cond_wait(condition, mutex);
- if (onMainThread && MainThreadWorkQ.head) {
- pthread_mutex_unlock(mutex);
- batchFinalizeOnMainThread();
- pthread_mutex_lock(mutex);
- }
- }
- pthread_mutex_unlock(mutex);
- } else {
- auto_zone_collect(gc_zone, amode);
- }
-}
-
-
-// USED BY CF & ONE OTHER
-BOOL objc_isAuto(id object)
-{
- return UseGC && auto_zone_is_valid_pointer(gc_zone, object) != 0;
-}
-
-
-BOOL objc_collectingEnabled(void)
-{
- return UseGC;
-}
-
-BOOL objc_collecting_enabled(void) // Old naming
-{
- return UseGC;
-}
-
-malloc_zone_t *objc_collectableZone(void) {
- return gc_zone;
-}
-
-BOOL objc_dumpHeap(char *filenamebuffer, unsigned long length) {
- static int counter = 0;
- ++counter;
- char buffer[1024];
- sprintf(buffer, OBJC_HEAP_DUMP_FILENAME_FORMAT, getpid(), counter);
- if (!_objc_dumpHeap(gc_zone, buffer)) return NO;
- if (filenamebuffer) {
- unsigned long blen = strlen(buffer);
- if (blen < length)
- strncpy(filenamebuffer, buffer, blen+1);
- else if (length > 0)
- filenamebuffer[0] = 0; // give some answer
- }
- return YES;
-}
-
-
-/***********************************************************************
-* Memory management.
-* Called by CF and Foundation.
-**********************************************************************/
-
-// Allocate an object in the GC zone, with the given number of extra bytes.
-id objc_allocate_object(Class cls, int extra)
-{
- return class_createInstance(cls, extra);
-}
-
-
-/***********************************************************************
-* Write barrier implementations, optimized for when GC is known to be on
-* Called by the write barrier exports only.
-* These implementations assume GC is on. The exported function must
-* either perform the check itself or be conditionally stomped at
-* startup time.
-**********************************************************************/
-
-id objc_assign_strongCast_gc(id value, id *slot) {
- if (!auto_zone_set_write_barrier(gc_zone, (void*)slot, value)) { // stores & returns true if slot points into GC allocated memory
- auto_zone_root_write_barrier(gc_zone, slot, value); // always stores
- }
- return value;
-}
-
-id objc_assign_global_gc(id value, id *slot) {
- // use explicit root registration.
- if (value && auto_zone_is_valid_pointer(gc_zone, value)) {
- if (auto_zone_is_finalized(gc_zone, value)) {
- _objc_inform("GC: storing an already collected object %p into global memory at %p, break on objc_assign_global_error to debug\n", (void*)value, slot);
- objc_assign_global_error(value, slot);
- }
- auto_zone_add_root(gc_zone, slot, value);
- }
- else
- *slot = value;
-
- return value;
-}
-
-id objc_assign_threadlocal_gc(id value, id *slot)
-{
- if (value && auto_zone_is_valid_pointer(gc_zone, value)) {
- auto_zone_add_root(gc_zone, slot, value);
- }
- else {
- *slot = value;
- }
-
- return value;
-}
-
-id objc_assign_ivar_gc(id value, id base, ptrdiff_t offset)
-{
- id *slot = (id*) ((char *)base + offset);
-
- if (value) {
- if (!auto_zone_set_write_barrier(gc_zone, (char *)base + offset, value)) {
- _objc_inform("GC: %p + %tu isn't in the auto_zone, break on objc_assign_ivar_error to debug.\n", (void*)base, offset);
- objc_assign_ivar_error(base, offset);
- }
- }
- else
- *slot = value;
-
- return value;
-}
-
-id objc_assign_strongCast_non_gc(id value, id *slot) {
- return (*slot = value);
-}
-
-id objc_assign_global_non_gc(id value, id *slot) {
- return (*slot = value);
-}
-
-id objc_assign_threadlocal_non_gc(id value, id *slot) {
- return (*slot = value);
-}
-
-id objc_assign_ivar_non_gc(id value, id base, ptrdiff_t offset) {
- id *slot = (id*) ((char *)base + offset);
- return (*slot = value);
-}
-
-
-/***********************************************************************
-* Non-trivial write barriers
-**********************************************************************/
-
-void *objc_memmove_collectable(void *dst, const void *src, size_t size)
-{
- if (UseGC) {
- return auto_zone_write_barrier_memmove(gc_zone, dst, src, size);
- } else {
- return memmove(dst, src, size);
- }
-}
-
-BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation) {
- const BOOL issueMemoryBarrier = NO;
- if (UseGC)
- return auto_zone_atomicCompareAndSwapPtr(gc_zone, (void *)predicate, (void *)replacement, (void * volatile *)objectLocation, issueMemoryBarrier);
- else
- return OSAtomicCompareAndSwapPtr((void *)predicate, (void *)replacement, (void * volatile *)objectLocation);
-}
-
-BOOL objc_atomicCompareAndSwapPtrBarrier(id predicate, id replacement, volatile id *objectLocation) {
- const BOOL issueMemoryBarrier = YES;
- if (UseGC)
- return auto_zone_atomicCompareAndSwapPtr(gc_zone, (void *)predicate, (void *)replacement, (void * volatile *)objectLocation, issueMemoryBarrier);
- else
- return OSAtomicCompareAndSwapPtrBarrier((void *)predicate, (void *)replacement, (void * volatile *)objectLocation);
-}
-
-BOOL objc_atomicCompareAndSwapGlobal(id predicate, id replacement, volatile id *objectLocation) {
- const BOOL isGlobal = YES;
- const BOOL issueMemoryBarrier = NO;
- if (UseGC)
- return auto_zone_atomicCompareAndSwap(gc_zone, (void *)predicate, (void *)replacement, (void * volatile *)objectLocation, isGlobal, issueMemoryBarrier);
- else
- return OSAtomicCompareAndSwapPtr((void *)predicate, (void *)replacement, (void * volatile *)objectLocation);
-}
-
-BOOL objc_atomicCompareAndSwapGlobalBarrier(id predicate, id replacement, volatile id *objectLocation) {
- const BOOL isGlobal = YES;
- const BOOL issueMemoryBarrier = YES;
- if (UseGC)
- return auto_zone_atomicCompareAndSwap(gc_zone, (void *)predicate, (void *)replacement, (void * volatile *)objectLocation, isGlobal, issueMemoryBarrier);
- else
- return OSAtomicCompareAndSwapPtrBarrier((void *)predicate, (void *)replacement, (void * volatile *)objectLocation);
-}
-
-BOOL objc_atomicCompareAndSwapInstanceVariable(id predicate, id replacement, volatile id *objectLocation) {
- const BOOL isGlobal = NO;
- const BOOL issueMemoryBarrier = NO;
- if (UseGC)
- return auto_zone_atomicCompareAndSwap(gc_zone, (void *)predicate, (void *)replacement, (void * volatile *)objectLocation, isGlobal, issueMemoryBarrier);
- else
- return OSAtomicCompareAndSwapPtr((void *)predicate, (void *)replacement, (void * volatile *)objectLocation);
-}
-
-BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, id replacement, volatile id *objectLocation) {
- const BOOL isGlobal = NO;
- const BOOL issueMemoryBarrier = YES;
- if (UseGC)
- return auto_zone_atomicCompareAndSwap(gc_zone, (void *)predicate, (void *)replacement, (void * volatile *)objectLocation, isGlobal, issueMemoryBarrier);
- else
- return OSAtomicCompareAndSwapPtrBarrier((void *)predicate, (void *)replacement, (void * volatile *)objectLocation);
-}
-
-
-/***********************************************************************
-* Weak ivar support
-**********************************************************************/
-
-id objc_read_weak_gc(id *location) {
- id result = *location;
- if (result) {
- result = (id)auto_read_weak_reference(gc_zone, (void **)location);
- }
- return result;
-}
-
-id objc_read_weak_non_gc(id *location) {
- return *location;
-}
-
-id objc_assign_weak_gc(id value, id *location) {
- auto_assign_weak_reference(gc_zone, value, (const void **)location, nil);
- return value;
-}
-
-id objc_assign_weak_non_gc(id value, id *location) {
- return (*location = value);
-}
-
-
-void gc_fixup_weakreferences(id newObject, id oldObject) {
- // fix up weak references if any.
- const unsigned char *weakLayout = (const unsigned char *)class_getWeakIvarLayout(newObject->ISA());
- if (weakLayout) {
- void **newPtr = (void **)newObject, **oldPtr = (void **)oldObject;
- unsigned char byte;
- while ((byte = *weakLayout++)) {
- unsigned skips = (byte >> 4);
- unsigned weaks = (byte & 0x0F);
- newPtr += skips, oldPtr += skips;
- while (weaks--) {
- *newPtr = nil;
- auto_assign_weak_reference(gc_zone, auto_read_weak_reference(gc_zone, oldPtr), (const void **)newPtr, nil);
- ++newPtr, ++oldPtr;
- }
- }
- }
-}
-
-
-/***********************************************************************
-* dyld resolver functions for basic GC write barriers
-* dyld calls the resolver function to bind the symbol.
-* We return the GC or non-GC variant as appropriate.
-**********************************************************************/
-
-#define GC_RESOLVER(name) \
- OBJC_EXPORT void *name##_resolver(void) __asm__("_" #name); \
- void *name##_resolver(void) \
- { \
- __asm__(".symbol_resolver _" #name); \
- if (UseGC) return (void*)name##_gc; \
- else return (void*)name##_non_gc; \
- }
-
-GC_RESOLVER(objc_assign_ivar)
-GC_RESOLVER(objc_assign_strongCast)
-GC_RESOLVER(objc_assign_global)
-GC_RESOLVER(objc_assign_threadlocal)
-GC_RESOLVER(objc_read_weak)
-GC_RESOLVER(objc_assign_weak)
-GC_RESOLVER(objc_getProperty)
-GC_RESOLVER(objc_setProperty)
-GC_RESOLVER(objc_getAssociatedObject)
-GC_RESOLVER(objc_setAssociatedObject)
-GC_RESOLVER(_object_addExternalReference)
-GC_RESOLVER(_object_readExternalReference)
-GC_RESOLVER(_object_removeExternalReference)
-
-
-/***********************************************************************
-* Testing tools
-* Used to isolate resurrection of garbage objects during finalization.
-**********************************************************************/
-BOOL objc_is_finalized(void *ptr) {
- if (ptr != nil && UseGC) {
- return auto_zone_is_finalized(gc_zone, ptr);
- }
- return NO;
-}
-
-
-/***********************************************************************
-* Stack clearing.
-* Used by top-level thread loops to reduce false pointers from the stack.
-**********************************************************************/
-void objc_clear_stack(unsigned long options) {
- if (!UseGC) return;
- auto_zone_clear_stack(gc_zone, 0);
-}
-
-
-/***********************************************************************
-* Finalization support
-**********************************************************************/
-
-// Finalizer crash debugging
-static void *finalizing_object;
-
-// finalize a single object without fuss
-// When there are no main-thread-only classes this is used directly
-// Otherwise, it is used indirectly by smarter code that knows main-thread-affinity requirements
-static void finalizeOneObject(void *obj, void *ignored) {
- id object = (id)obj;
- finalizing_object = obj;
-
- Class cls = object->ISA();
- CRSetCrashLogMessage2(class_getName(cls));
-
- /// call -finalize method.
- ((void(*)(id, SEL))objc_msgSend)(object, @selector(finalize));
-
- // Call C++ destructors.
- // This would be objc_destructInstance() but for performance.
- if (cls->hasCxxDtor()) {
- object_cxxDestruct(object);
- }
-
- finalizing_object = nil;
- CRSetCrashLogMessage2(nil);
-}
-
-// finalize object only if it is a main-thread-only object.
-// Called only from the main thread.
-static void finalizeOneMainThreadOnlyObject(void *obj, void *arg) {
- id object = (id)obj;
- Class cls = object->ISA();
- if (cls == nil) {
- _objc_fatal("object with nil ISA passed to finalizeOneMainThreadOnlyObject: %p\n", obj);
- }
- if (cls->shouldFinalizeOnMainThread()) {
- finalizeOneObject(obj, nil);
- }
-}
-
-// finalize one object only if it is not a main-thread-only object
-// called from any other thread than the main thread
-// Important: if a main-thread-only object is passed, return that fact in the needsMain argument
-static void finalizeOneAnywhereObject(void *obj, void *needsMain) {
- id object = (id)obj;
- Class cls = object->ISA();
- bool *needsMainThreadWork = (bool *)needsMain;
- if (cls == nil) {
- _objc_fatal("object with nil ISA passed to finalizeOneAnywhereObject: %p\n", obj);
- }
- if (!cls->shouldFinalizeOnMainThread()) {
- finalizeOneObject(obj, nil);
- }
- else {
- *needsMainThreadWork = true;
- }
-}
-
-
-// Utility workhorse.
-// Set up the expensive @try block and ask the collector to hand the next object to
-// our finalizeAnObject function.
-// Track and return a boolean that records whether or not any main thread work is necessary.
-// (When we know that there are no main thread only objects then the boolean isn't even computed)
-static bool batchFinalize(auto_zone_t *zone,
- auto_zone_foreach_object_t foreach,
- auto_zone_cursor_t cursor,
- size_t cursor_size,
- void (*finalizeAnObject)(void *, void*))
-{
-#if DEBUG && !__OBJC2__
- // debug: don't call try/catch before exception handlers are installed
- objc_exception_functions_t table = {};
- objc_exception_get_functions(&table);
- assert(table.throw_exc);
-#endif
-
- bool needsMainThreadWork = false;
- for (;;) {
- @try {
- foreach(cursor, finalizeAnObject, &needsMainThreadWork);
- // non-exceptional return means finalization is complete.
- break;
- }
- @catch (id exception) {
- // whoops, note exception, then restart at cursor's position
- _objc_inform("GC: -finalize resulted in an exception (%p) being thrown, break on objc_exception_during_finalize_error to debug\n\t%s", exception, (const char*)[[exception description] UTF8String]);
- objc_exception_during_finalize_error();
- }
- @catch (...) {
- // whoops, note exception, then restart at cursor's position
- _objc_inform("GC: -finalize resulted in an exception being thrown, break on objc_exception_during_finalize_error to debug");
- objc_exception_during_finalize_error();
- }
- }
- return needsMainThreadWork;
-}
-
-// Called on main thread-only.
-// Pick up work from global queue.
-// called parasitically by anyone requesting a collection
-// called explicitly when there is known to be main thread only finalization work
-// In both cases we are on the main thread
-// Guard against recursion by something called from a finalizer
-static void batchFinalizeOnMainThread() {
- pthread_mutex_lock(&MainThreadWorkQ.mutex);
- if (!MainThreadWorkQ.head || MainThreadWorkQ.head->started) {
- // No work or we're already here
- pthread_mutex_unlock(&MainThreadWorkQ.mutex);
- return;
- }
- while (MainThreadWorkQ.head) {
- BatchFinalizeBlock_t *bfb = MainThreadWorkQ.head;
- bfb->started = YES;
- pthread_mutex_unlock(&MainThreadWorkQ.mutex);
-
- batchFinalize(gc_zone, bfb->foreach, bfb->cursor, bfb->cursor_size, finalizeOneMainThreadOnlyObject);
- // signal the collector thread(s) that finalization has finished.
- pthread_mutex_lock(&MainThreadWorkQ.mutex);
- bfb->finished = YES;
- pthread_cond_broadcast(&MainThreadWorkQ.condition);
- MainThreadWorkQ.head = bfb->next;
- }
- MainThreadWorkQ.tail = nil;
- pthread_mutex_unlock(&MainThreadWorkQ.mutex);
-}
-
-
-// Knowing that we possibly have main thread only work to do, first process everything
-// that is not main-thread-only. If we discover main thread only work, queue a work block
-// to the main thread that will do just the main thread only work. Wait for it.
-// Called from a non main thread.
-static void batchFinalizeOnTwoThreads(auto_zone_t *zone,
- auto_zone_foreach_object_t foreach,
- auto_zone_cursor_t cursor,
- size_t cursor_size)
-{
- // First, lets get rid of everything we can on this thread, then ask main thread to help if needed
- char cursor_copy[cursor_size];
- memcpy(cursor_copy, cursor, cursor_size);
- bool needsMainThreadFinalization = batchFinalize(zone, foreach, (auto_zone_cursor_t)cursor_copy, cursor_size, finalizeOneAnywhereObject);
-
- if (! needsMainThreadFinalization)
- return; // no help needed
-
- // set up the control block. Either our ping of main thread with _callOnMainThread will get to it, or
- // an objc_collect(if_needed) will get to it. Either way, this block will be processed on the main thread.
- BatchFinalizeBlock_t bfb;
- bfb.foreach = foreach;
- bfb.cursor = cursor;
- bfb.cursor_size = cursor_size;
- bfb.started = NO;
- bfb.finished = NO;
- bfb.next = nil;
- pthread_mutex_lock(&MainThreadWorkQ.mutex);
- if (MainThreadWorkQ.tail) {
-
- // link to end so that ordering of finalization is preserved.
- MainThreadWorkQ.tail->next = &bfb;
- MainThreadWorkQ.tail = &bfb;
- }
- else {
- MainThreadWorkQ.head = &bfb;
- MainThreadWorkQ.tail = &bfb;
- }
- pthread_mutex_unlock(&MainThreadWorkQ.mutex);
-
- //printf("----->asking main thread to finalize\n");
- dispatch_async(dispatch_get_main_queue(), ^{ batchFinalizeOnMainThread(); });
-
- // wait for the main thread to finish finalizing instances of classes marked CLS_FINALIZE_ON_MAIN_THREAD.
- pthread_mutex_lock(&MainThreadWorkQ.mutex);
- while (!bfb.finished) {
- // the main thread might be blocked waiting for a synchronous collection to complete, so wake it here
- pthread_cond_signal(&MainThreadWorkQ.condition);
- pthread_cond_wait(&MainThreadWorkQ.condition, &MainThreadWorkQ.mutex);
- }
- pthread_mutex_unlock(&MainThreadWorkQ.mutex);
- //printf("<------ main thread finalize done\n");
-
-}
-
-
-
-// collector calls this with garbage ready
-// thread collectors, too, so this needs to be thread-safe
-static void BatchInvalidate(auto_zone_t *zone,
- auto_zone_foreach_object_t foreach,
- auto_zone_cursor_t cursor,
- size_t cursor_size)
-{
- if (pthread_main_np() || !WantsMainThreadFinalization) {
- // Collect all objects. We're either pre-multithreaded on main thread or we're on the collector thread
- // but no main-thread-only objects have been allocated.
- batchFinalize(zone, foreach, cursor, cursor_size, finalizeOneObject);
- }
- else {
- // We're on the dedicated thread. Collect some on main thread, the rest here.
- batchFinalizeOnTwoThreads(zone, foreach, cursor, cursor_size);
- }
-
-}
-
-
-/*
- * Zombie support
- * Collector calls into this system when it finds resurrected objects.
- * This keeps them pitifully alive and leaked, even if they reference garbage.
- */
-
-// idea: keep a side table mapping resurrected object pointers to their original Class, so we don't
-// need to smash anything. alternatively, could use associative references to track against a secondary
-// object with information about the resurrection, such as a stack crawl, etc.
-
-static Class _NSResurrectedObjectClass;
-static NXMapTable *_NSResurrectedObjectMap = nil;
-static pthread_mutex_t _NSResurrectedObjectLock = PTHREAD_MUTEX_INITIALIZER;
-
-static Class resurrectedObjectOriginalClass(id object) {
- Class originalClass;
- pthread_mutex_lock(&_NSResurrectedObjectLock);
- originalClass = (Class) NXMapGet(_NSResurrectedObjectMap, object);
- pthread_mutex_unlock(&_NSResurrectedObjectLock);
- return originalClass;
-}
-
-static id _NSResurrectedObject_classMethod(id self, SEL selector) { return self; }
-
-static id _NSResurrectedObject_instanceMethod(id self, SEL name) {
- _objc_inform("**resurrected** object %p of class %s being sent message '%s'\n", (void*)self, class_getName(resurrectedObjectOriginalClass(self)), sel_getName(name));
- return self;
-}
-
-static void _NSResurrectedObject_finalize(id self, SEL _cmd) {
- Class originalClass;
- pthread_mutex_lock(&_NSResurrectedObjectLock);
- originalClass = (Class) NXMapRemove(_NSResurrectedObjectMap, self);
- pthread_mutex_unlock(&_NSResurrectedObjectLock);
- if (originalClass) _objc_inform("**resurrected** object %p of class %s being finalized\n", (void*)self, class_getName(originalClass));
- _objc_rootFinalize(self);
-}
-
-static bool _NSResurrectedObject_resolveInstanceMethod(id self, SEL _cmd, SEL name) {
- class_addMethod((Class)self, name, (IMP)_NSResurrectedObject_instanceMethod, "@@:");
- return YES;
-}
-
-static bool _NSResurrectedObject_resolveClassMethod(id self, SEL _cmd, SEL name) {
- class_addMethod(self->ISA(), name, (IMP)_NSResurrectedObject_classMethod, "@@:");
- return YES;
-}
-
-static void _NSResurrectedObject_initialize() {
- _NSResurrectedObjectMap = NXCreateMapTable(NXPtrValueMapPrototype, 128);
- _NSResurrectedObjectClass = objc_allocateClassPair(objc_getClass("NSObject"), "_NSResurrectedObject", 0);
- class_addMethod(_NSResurrectedObjectClass, @selector(finalize), (IMP)_NSResurrectedObject_finalize, "v@:");
- Class metaClass = _NSResurrectedObjectClass->ISA();
- class_addMethod(metaClass, @selector(resolveInstanceMethod:), (IMP)_NSResurrectedObject_resolveInstanceMethod, "c@::");
- class_addMethod(metaClass, @selector(resolveClassMethod:), (IMP)_NSResurrectedObject_resolveClassMethod, "c@::");
- objc_registerClassPair(_NSResurrectedObjectClass);
-}
-
-static void resurrectZombie(auto_zone_t *zone, void *ptr) {
- id object = (id) ptr;
- Class cls = object->ISA();
- if (cls != _NSResurrectedObjectClass) {
- // remember the original class for this instance.
- pthread_mutex_lock(&_NSResurrectedObjectLock);
- NXMapInsert(_NSResurrectedObjectMap, ptr, cls);
- pthread_mutex_unlock(&_NSResurrectedObjectLock);
- object_setClass(object, _NSResurrectedObjectClass);
- }
-}
-
-/***********************************************************************
-* Pretty printing support
-* For development purposes.
-**********************************************************************/
-
-
-static char *name_for_address(auto_zone_t *zone, vm_address_t base, vm_address_t offset, int withRetainCount);
-
-static char* objc_name_for_address(auto_zone_t *zone, vm_address_t base, vm_address_t offset)
-{
- return name_for_address(zone, base, offset, false);
-}
-
-static const char* objc_name_for_object(auto_zone_t *zone, void *object) {
- Class cls = *(Class *)object;
- if (!objc_isRegisteredClass(cls)) return "";
- return class_getName(cls);
-}
-
-/***********************************************************************
-* Collection support
-**********************************************************************/
-
-static bool objc_isRegisteredClass(Class candidate);
-
-static const unsigned char *objc_layout_for_address(auto_zone_t *zone, void *address) {
- id object = (id)address;
- volatile void *clsptr = (void*)object->ISA();
- Class cls = (Class)clsptr;
- return objc_isRegisteredClass(cls) ? _object_getIvarLayout(cls, object) : nil;
-}
-
-static const unsigned char *objc_weak_layout_for_address(auto_zone_t *zone, void *address) {
- id object = (id)address;
- volatile void *clsptr = (void*)object->ISA();
- Class cls = (Class)clsptr;
- return objc_isRegisteredClass(cls) ? class_getWeakIvarLayout(cls) : nil;
-}
-
-void gc_register_datasegment(uintptr_t base, size_t size) {
- auto_zone_register_datasegment(gc_zone, (void*)base, size);
-}
-
-void gc_unregister_datasegment(uintptr_t base, size_t size) {
- auto_zone_unregister_datasegment(gc_zone, (void*)base, size);
-}
-
-
-/***********************************************************************
-* Initialization
-**********************************************************************/
-
-static void objc_will_grow(auto_zone_t *zone, auto_heap_growth_info_t info) {
- if (auto_zone_is_collecting(gc_zone)) {
- ;
- }
- else {
- auto_zone_collect(gc_zone, AUTO_ZONE_COLLECT_COALESCE|AUTO_ZONE_COLLECT_RATIO_COLLECTION);
- }
-}
-
-
-static auto_zone_t *gc_zone_init(void)
-{
- auto_zone_t *result;
- static int didOnce = 0;
- if (!didOnce) {
- didOnce = 1;
-
- // initialize the batch finalization queue
- MainThreadWorkQ.head = nil;
- MainThreadWorkQ.tail = nil;
- pthread_mutex_init(&MainThreadWorkQ.mutex, nil);
- pthread_cond_init(&MainThreadWorkQ.condition, nil);
- }
-
- result = auto_zone_create("auto_zone");
-
- auto_zone_disable_compaction(result);
-
- auto_collection_control_t *control = auto_collection_parameters(result);
-
- // set up the magic control parameters
- control->batch_invalidate = BatchInvalidate;
- control->will_grow = objc_will_grow;
- control->resurrect = resurrectZombie;
- control->layout_for_address = objc_layout_for_address;
- control->weak_layout_for_address = objc_weak_layout_for_address;
- control->name_for_address = objc_name_for_address;
-
- if (control->version >= sizeof(auto_collection_control_t)) {
- control->name_for_object = objc_name_for_object;
- }
-
- return result;
-}
-
-
-/* should be defined in /usr/local/include/libdispatch_private.h. */
-extern void (*dispatch_begin_thread_4GC)(void);
-extern void (*dispatch_end_thread_4GC)(void);
-
-static void objc_reapThreadLocalBlocks()
-{
- if (UseGC) auto_zone_reap_all_local_blocks(gc_zone);
-}
-
-void objc_registerThreadWithCollector()
-{
- if (UseGC) auto_zone_register_thread(gc_zone);
-}
-
-void objc_unregisterThreadWithCollector()
-{
- if (UseGC) auto_zone_unregister_thread(gc_zone);
-}
-
-void objc_assertRegisteredThreadWithCollector()
-{
- if (UseGC) auto_zone_assert_thread_registered(gc_zone);
-}
-
-// Always called by _objcInit, even if GC is off.
-void gc_init(bool wantsGC)
-{
- assert(UseGC == -1);
- UseGC = wantsGC;
-
- if (PrintGC) {
- _objc_inform("GC: is %s", wantsGC ? "ON" : "OFF");
- }
-
- if (UseGC) {
- // Set up the GC zone
- gc_zone = gc_zone_init();
-
- // tell libdispatch to register its threads with the GC.
- dispatch_begin_thread_4GC = objc_registerThreadWithCollector;
- dispatch_end_thread_4GC = objc_reapThreadLocalBlocks;
-
- // set up the registered classes list
- registeredClassTableInit();
-
- // tell Blocks to use collectable memory. CF will cook up the classes separately.
- gc_block_init();
-
- // Add GC state to crash log reports
- _objc_inform_on_crash("garbage collection is ON");
- }
-}
-
-
-// Called by NSObject +load to perform late GC setup
-// This work must wait until after all of libSystem initializes.
-void gc_init2(void)
-{
- assert(UseGC);
-
- // create the _NSResurrectedObject class used to track resurrections.
- _NSResurrectedObject_initialize();
-
- // tell libauto to set up its dispatch queues
- auto_collect_multithreaded(gc_zone);
-}
-
-// Called by Foundation.
-// This function used to initialize NSObject stuff, but now does nothing.
-malloc_zone_t *objc_collect_init(int (*callback)(void) __unused)
-{
- return (malloc_zone_t *)gc_zone;
-}
-
-/*
- * Support routines for the Block implementation
- */
-
-
-// The Block runtime now needs to sometimes allocate a Block that is an Object - namely
-// when it neesd to have a finalizer which, for now, is only if there are C++ destructors
-// in the helper function. Hence the isObject parameter.
-// Under GC a -copy message should allocate a refcount 0 block, ergo the isOne parameter.
-static void *block_gc_alloc5(const unsigned long size, const bool isOne, const bool isObject) {
- auto_memory_type_t type = isObject ? (AUTO_OBJECT|AUTO_MEMORY_SCANNED) : AUTO_MEMORY_SCANNED;
- return auto_zone_allocate_object(gc_zone, size, type, isOne, false);
-}
-
-// The Blocks runtime keeps track of everything above 1 and so it only calls
-// up to the collector to tell it about the 0->1 transition and then the 1->0 transition
-static void block_gc_setHasRefcount(const void *block, const bool hasRefcount) {
- if (hasRefcount)
- auto_zone_retain(gc_zone, (void *)block);
- else
- auto_zone_release(gc_zone, (void *)block);
-}
-
-static void block_gc_memmove(void *dst, void *src, unsigned long size) {
- auto_zone_write_barrier_memmove(gc_zone, dst, src, (size_t)size);
-}
-
-static void gc_block_init(void) {
- _Block_use_GC(
- block_gc_alloc5,
- block_gc_setHasRefcount,
- (void (*)(void *, void **))objc_assign_strongCast_gc,
- (void (*)(const void *, void *))objc_assign_weak,
- block_gc_memmove
- );
-}
-
-
-/***********************************************************************
-* Track classes.
-* In addition to the global class hashtable (set) indexed by name, we
-* also keep one based purely by pointer when running under Garbage Collection.
-* This allows the background collector to race against objects recycled from TLC.
-* Specifically, the background collector can read the admin byte and see that
-* a thread local object is an object, get scheduled out, and the TLC recovers it,
-* linking it into the cache, then the background collector reads the isa field and
-* finds linkage info. By qualifying all isa fields read we avoid this.
-**********************************************************************/
-
-// This is a self-contained hash table of all classes. The first two elements contain the (size-1) and count.
-static volatile Class *AllClasses = nil;
-
-#define SHIFT 3
-#define INITIALSIZE 512
-#define REMOVED ~0ul
-
-// Allocate the side table.
-static void registeredClassTableInit() {
- assert(UseGC);
- // allocate a collectable (refcount 0) zeroed hunk of unscanned memory
- uintptr_t *table = (uintptr_t *)auto_zone_allocate_object(gc_zone, INITIALSIZE*sizeof(void *), AUTO_MEMORY_UNSCANNED, true, true);
- // set initial capacity (as mask)
- table[0] = INITIALSIZE - 1;
- // set initial count
- table[1] = 0;
- AllClasses = (Class *)table;
-}
-
-// Verify that a particular pointer is to a class.
-// Safe from any thread anytime
-static bool objc_isRegisteredClass(Class candidate) {
- assert(UseGC);
- // nil is never a valid ISA.
- if (candidate == nil) return NO;
- // We don't care about a race with another thread adding a class to which we randomly might have a pointer
- // Get local copy of classes so that we're immune from updates.
- // We keep the size of the list as the first element so there is no race as the list & size get updated.
- uintptr_t *allClasses = (uintptr_t *)AllClasses;
- // Slot 0 is always the size of the list in log 2 masked terms (e.g. size - 1) where size is always power of 2
- // Slot 1 is count
- uintptr_t slot = (((uintptr_t)candidate) >> SHIFT) & allClasses[0];
- // avoid slot 0 and 1
- if (slot < 2) slot = 2;
- for(;;) {
- long int slotValue = allClasses[slot];
- if (slotValue == (long int)candidate) {
- return YES;
- }
- if (slotValue == 0) {
- return NO;
- }
- ++slot;
- if (slot > allClasses[0])
- slot = 2; // skip size, count
- }
-}
-
-// Utility used when growing
-// Assumes lock held
-static void addClassHelper(uintptr_t *table, uintptr_t candidate) {
- uintptr_t slot = (((long int)candidate) >> SHIFT) & table[0];
- if (slot < 2) slot = 2;
- for(;;) {
- uintptr_t slotValue = table[slot];
- if (slotValue == 0) {
- table[slot] = candidate;
- ++table[1];
- return;
- }
- ++slot;
- if (slot > table[0])
- slot = 2; // skip size, count
- }
-}
-
-// lock held by callers
-void objc_addRegisteredClass(Class candidate) {
- if (!UseGC) return;
- uintptr_t *table = (uintptr_t *)AllClasses;
- // Slot 0 is always the size of the list in log 2 masked terms (e.g. size - 1) where size is always power of 2
- // Slot 1 is count - always non-zero
- uintptr_t slot = (((long int)candidate) >> SHIFT) & table[0];
- if (slot < 2) slot = 2;
- for(;;) {
- uintptr_t slotValue = table[slot];
- assert(slotValue != (uintptr_t)candidate);
- if (slotValue == REMOVED) {
- table[slot] = (long)candidate;
- return;
- }
- else if (slotValue == 0) {
- table[slot] = (long)candidate;
- if (2*++table[1] > table[0]) { // add to count; check if we cross 50% utilization
- // grow
- uintptr_t oldSize = table[0]+1;
- uintptr_t *newTable = (uintptr_t *)auto_zone_allocate_object(gc_zone, oldSize*2*sizeof(void *), AUTO_MEMORY_UNSCANNED, true, true);
- uintptr_t i;
- newTable[0] = 2*oldSize - 1;
- newTable[1] = 0;
- for (i = 2; i < oldSize; ++i) {
- if (table[i] && table[i] != REMOVED)
- addClassHelper(newTable, table[i]);
- }
- AllClasses = (Class *)newTable;
- // let the old table be collected when other threads are no longer reading it.
- auto_zone_release(gc_zone, (void *)table);
- }
- return;
- }
- ++slot;
- if (slot > table[0])
- slot = 2; // skip size, count
- }
-}
-
-// lock held by callers
-void objc_removeRegisteredClass(Class candidate) {
- if (!UseGC) return;
- uintptr_t *table = (uintptr_t *)AllClasses;
- // Slot 0 is always the size of the list in log 2 masked terms (e.g. size - 1) where size is always power of 2
- // Slot 1 is count - always non-zero
- uintptr_t slot = (((uintptr_t)candidate) >> SHIFT) & table[0];
- if (slot < 2) slot = 2;
- for(;;) {
- uintptr_t slotValue = table[slot];
- if (slotValue == (uintptr_t)candidate) {
- table[slot] = REMOVED; // if next slot == 0 we could set to 0 here and decr count
- return;
- }
- assert(slotValue != 0);
- ++slot;
- if (slot > table[0])
- slot = 2; // skip size, count
- }
-}
-
-
-/***********************************************************************
-* Debugging - support for smart printouts when errors occur
-**********************************************************************/
-
-
-static malloc_zone_t *objc_debug_zone(void)
-{
- static malloc_zone_t *z = nil;
- if (!z) {
- z = malloc_create_zone(PAGE_MAX_SIZE, 0);
- malloc_set_zone_name(z, "objc-auto debug");
- }
- return z;
-}
-
-static char *_malloc_append_unsigned(uintptr_t value, unsigned base, char *head) {
- if (!value) {
- head[0] = '0';
- } else {
- if (value >= base) head = _malloc_append_unsigned(value / base, base, head);
- value = value % base;
- head[0] = (value < 10) ? '0' + value : 'a' + value - 10;
- }
- return head+1;
-}
-
-static void strlcati(char *str, uintptr_t value, size_t bufSize)
-{
- if ( (bufSize - strlen(str)) < 30)
- return;
- str = _malloc_append_unsigned(value, 10, str + strlen(str));
- str[0] = '\0';
-}
-
-
-static Ivar ivar_for_offset(Class cls, vm_address_t offset)
-{
- unsigned i;
- vm_address_t ivar_offset;
- Ivar super_ivar, result;
- Ivar *ivars;
- unsigned int ivar_count;
-
- if (!cls) return nil;
-
- // scan base classes FIRST
- super_ivar = ivar_for_offset(cls->superclass, offset);
- // result is best-effort; our ivars may be closer
-
- ivars = class_copyIvarList(cls, &ivar_count);
- if (ivars && ivar_count) {
- // Try our first ivar. If it's too big, use super's best ivar.
- // (lose 64-bit precision)
- ivar_offset = ivar_getOffset(ivars[0]);
- if (ivar_offset > offset) result = super_ivar;
- else if (ivar_offset == offset) result = ivars[0];
- else result = nil;
-
- // Try our other ivars. If any is too big, use the previous.
- for (i = 1; result == nil && i < ivar_count; i++) {
- ivar_offset = ivar_getOffset(ivars[i]);
- if (ivar_offset == offset) {
- result = ivars[i];
- } else if (ivar_offset > offset) {
- result = ivars[i - 1];
- }
- }
-
- // Found nothing. Return our last ivar.
- if (result == nil)
- result = ivars[ivar_count - 1];
-
- free(ivars);
- } else {
- result = super_ivar;
- }
-
- return result;
-}
-
-static void append_ivar_at_offset(char *buf, Class cls, vm_address_t offset, size_t bufSize)
-{
- Ivar ivar = nil;
-
- if (offset == 0) return; // don't bother with isa
- if (offset >= class_getInstanceSize(cls)) {
- strlcat(buf, ".<extra>+", bufSize);
- strlcati(buf, offset, bufSize);
- return;
- }
-
- ivar = ivar_for_offset(cls, offset);
- if (!ivar) {
- strlcat(buf, ".<?>", bufSize);
- return;
- }
-
- // fixme doesn't handle structs etc.
-
- strlcat(buf, ".", bufSize);
- const char *ivar_name = ivar_getName(ivar);
- if (ivar_name) strlcat(buf, ivar_name, bufSize);
- else strlcat(buf, "<anonymous ivar>", bufSize);
-
- offset -= ivar_getOffset(ivar);
- if (offset > 0) {
- strlcat(buf, "+", bufSize);
- strlcati(buf, offset, bufSize);
- }
-}
-
-
-static const char *cf_class_for_object(void *cfobj)
-{
- // ick - we don't link against CF anymore
-
- struct fake_cfclass {
- size_t version;
- const char *className;
- // don't care about the rest
- };
-
- const char *result;
- void *dlh;
- size_t (*CFGetTypeID)(void *);
- fake_cfclass * (*_CFRuntimeGetClassWithTypeID)(size_t);
-
- result = "anonymous_NSCFType";
-
- dlh = dlopen("/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation", RTLD_LAZY | RTLD_NOLOAD | RTLD_FIRST);
- if (!dlh) return result;
-
- CFGetTypeID = (size_t(*)(void*)) dlsym(dlh, "CFGetTypeID");
- _CFRuntimeGetClassWithTypeID = (fake_cfclass*(*)(size_t)) dlsym(dlh, "_CFRuntimeGetClassWithTypeID");
-
- if (CFGetTypeID && _CFRuntimeGetClassWithTypeID) {
- size_t cfid = (*CFGetTypeID)(cfobj);
- result = (*_CFRuntimeGetClassWithTypeID)(cfid)->className;
- }
-
- dlclose(dlh);
- return result;
-}
-
-
-static char *name_for_address(auto_zone_t *zone, vm_address_t base, vm_address_t offset, int withRetainCount)
-{
-#define APPEND_SIZE(s) \
- strlcat(buf, "[", sizeof(buf)); \
- strlcati(buf, s, sizeof(buf)); \
- strlcat(buf, "]", sizeof(buf));
-
- char buf[1500];
- char *result;
-
- buf[0] = '\0';
-
- size_t size =
- auto_zone_size(zone, (void *)base);
- auto_memory_type_t type = size ?
- auto_zone_get_layout_type(zone, (void *)base) : AUTO_TYPE_UNKNOWN;
- unsigned int refcount = size ?
- auto_zone_retain_count(zone, (void *)base) : 0;
-
- switch (type) {
- case AUTO_OBJECT_SCANNED:
- case AUTO_OBJECT_UNSCANNED:
- case AUTO_OBJECT_ALL_POINTERS: {
- const char *class_name = object_getClassName((id)base);
- if ((0 == strcmp(class_name, "__NSCFType")) || (0 == strcmp(class_name, "NSCFType"))) {
- strlcat(buf, cf_class_for_object((void *)base), sizeof(buf));
- } else {
- strlcat(buf, class_name, sizeof(buf));
- }
- if (offset) {
- append_ivar_at_offset(buf, ((id)base)->ISA(), offset, sizeof(buf));
- }
- APPEND_SIZE(size);
- break;
- }
- case AUTO_MEMORY_SCANNED:
- strlcat(buf, "{conservative-block}", sizeof(buf));
- APPEND_SIZE(size);
- break;
- case AUTO_MEMORY_UNSCANNED:
- strlcat(buf, "{no-pointers-block}", sizeof(buf));
- APPEND_SIZE(size);
- break;
- case AUTO_MEMORY_ALL_POINTERS:
- strlcat(buf, "{all-pointers-block}", sizeof(buf));
- APPEND_SIZE(size);
- break;
- case AUTO_MEMORY_ALL_WEAK_POINTERS:
- strlcat(buf, "{all-weak-pointers-block}", sizeof(buf));
- APPEND_SIZE(size);
- break;
- case AUTO_TYPE_UNKNOWN:
- strlcat(buf, "{uncollectable-memory}", sizeof(buf));
- break;
- default:
- strlcat(buf, "{unknown-memory-type}", sizeof(buf));
- }
-
- if (withRetainCount && refcount > 0) {
- strlcat(buf, " [[refcount=", sizeof(buf));
- strlcati(buf, refcount, sizeof(buf));
- strlcat(buf, "]]", sizeof(buf));
- }
-
- size_t len = 1 + strlen(buf);
- result = (char *)malloc_zone_malloc(objc_debug_zone(), len);
- memcpy(result, buf, len);
- return result;
-
-#undef APPEND_SIZE
-}
-
-
-// not OBJC_NO_GC
+// not OBJC_NO_GC_API
#endif
}
-/***********************************************************************
-* _cache_addIgnoredEntry
-* Add an entry for the ignored selector to cls's method cache.
-* Does nothing if the cache addition fails for any reason.
-* Returns the ignored IMP.
-* Cache locks: cacheUpdateLock must not be held.
-**********************************************************************/
-#if SUPPORT_GC && !SUPPORT_IGNORED_SELECTOR_CONSTANT
-static cache_entry *alloc_ignored_entries(void)
-{
- cache_entry *e = (cache_entry *)malloc(5 * sizeof(cache_entry));
- e[0] = (cache_entry){ @selector(retain), 0,(IMP)&_objc_ignored_method};
- e[1] = (cache_entry){ @selector(release), 0,(IMP)&_objc_ignored_method};
- e[2] = (cache_entry){ @selector(autorelease),0,(IMP)&_objc_ignored_method};
- e[3] = (cache_entry){ @selector(retainCount),0,(IMP)&_objc_ignored_method};
- e[4] = (cache_entry){ @selector(dealloc), 0,(IMP)&_objc_ignored_method};
- return e;
-}
-#endif
-
-IMP _cache_addIgnoredEntry(Class cls, SEL sel)
-{
- cache_entry *entryp = NULL;
-
-#if !SUPPORT_GC
- _objc_fatal("selector ignored with GC off");
-#elif SUPPORT_IGNORED_SELECTOR_CONSTANT
- static cache_entry entry = { (SEL)kIgnore, 0, (IMP)&_objc_ignored_method };
- entryp = &entry;
- assert(sel == (SEL)kIgnore);
-#else
- // hack
- int i;
- static cache_entry *entries;
- INIT_ONCE_PTR(entries, alloc_ignored_entries(), free(v));
-
- assert(ignoreSelector(sel));
- for (i = 0; i < 5; i++) {
- if (sel == entries[i].name) {
- entryp = &entries[i];
- break;
- }
- }
- if (!entryp) _objc_fatal("selector %s (%p) is not ignored",
- sel_getName(sel), sel);
-#endif
-
- _cache_fill(cls, (Method)entryp, sel);
- return entryp->imp;
-}
-
-
/***********************************************************************
* _cache_flush. Invalidate all valid entries in the given class' cache.
*
#endif
-#if SUPPORT_IGNORED_SELECTOR_CONSTANT
-#error sorry not implemented
-#endif
-
-
// copied from dispatch_atomic_maximally_synchronizing_barrier
// fixme verify that this barrier hack does in fact work here
#if __x86_64__
_objc_inform_now_and_on_crash
("isa '%s'", isa->nameForLogging());
_objc_fatal
- ("Method cache corrupted.");
+ ("Method cache corrupted. This may be a message to an "
+ "invalid object, or a memory error somewhere else.");
}
}
// Dispose all refs now in the garbage
+ // Erase each entry so debugging tools don't see stale pointers.
while (garbage_count--) {
- free(garbage_refs[garbage_count]);
+ auto dead = garbage_refs[garbage_count];
+ garbage_refs[garbage_count] = nil;
+ free(dead);
}
// Clear the garbage count and total size indicator
static inline old_method *_findNamedMethodInList(old_method_list * mlist, const char *meth_name) {
int i;
if (!mlist) return nil;
- if (ignoreSelectorNamed(meth_name)) return nil;
for (i = 0; i < mlist->method_count; i++) {
old_method *m = &mlist->method_list[i];
if (0 == strcmp((const char *)(m->method_name), meth_name)) {
/***********************************************************************
* fixupSelectorsInMethodList
* Uniques selectors in the given method list.
-* Also replaces imps for GC-ignored selectors
* The given method list must be non-nil and not already fixed-up.
* If the class was loaded from a bundle:
* fixes up the given list in place with heap-allocated selector strings
method = &mlist->method_list[i];
method->method_name =
sel_registerNameNoLock((const char *)method->method_name, isBundle); // Always copy selector data from bundles.
-
- if (ignoreSelector(method->method_name)) {
- method->method_imp = (IMP)&_objc_ignored_method;
- }
}
sel_unlock();
mlist->obsolete = fixed_up_method_list;
}
-// fixme for gc debugging temporary use
+// called by a debugging check in _objc_insertMethods
IMP findIMPInClass(Class cls, SEL sel)
{
old_method *m = _findMethodInClass(cls, sel);
retry:
methodListLock.lock();
- // Ignore GC selectors
- if (ignoreSelector(sel)) {
- methodPC = _cache_addIgnoredEntry(cls, sel);
- goto done;
- }
-
// Try this class's cache.
methodPC = _cache_getImp(cls, sel);
done:
methodListLock.unlock();
- // paranoia: look for ignored selectors with non-ignored implementations
- assert(!(ignoreSelector(sel) && methodPC != (IMP)&_objc_ignored_method));
-
return methodPC;
}
}
+/***********************************************************************
+* _class_getClassForIvar
+* Given a class and an ivar that is in it or one of its superclasses,
+* find the actual class that defined the ivar.
+**********************************************************************/
+Class _class_getClassForIvar(Class cls, Ivar ivar)
+{
+ for ( ; cls; cls = cls->superclass) {
+ if (auto ivars = cls->ivars) {
+ if (ivar >= &ivars->ivar_list[0] &&
+ ivar < &ivars->ivar_list[ivars->ivar_count])
+ {
+ return cls;
+ }
+ }
+ }
+
+ return nil;
+}
+
+
/***********************************************************************
* class_getVariable. Return the named instance variable.
**********************************************************************/
-Ivar _class_getVariable(Class cls, const char *name, Class *memberOf)
+Ivar _class_getVariable(Class cls, const char *name)
{
for (; cls != Nil; cls = cls->superclass) {
int i;
// (e.g. for anonymous bit fields).
old_ivar *ivar = &cls->ivars->ivar_list[i];
if (ivar->ivar_name && 0 == strcmp(name, ivar->ivar_name)) {
- if (memberOf) *memberOf = cls;
return (Ivar)ivar;
}
}
// Replace the original with the imposter in all class refs
// Major loop - process all headers
- for (hInfo = FirstHeader; hInfo != nil; hInfo = hInfo->next)
+ for (hInfo = FirstHeader; hInfo != nil; hInfo = hInfo->getNext())
{
Class *cls_refs;
size_t refCount;
NXHashRemove (class_hash, original);
NXHashInsert (class_hash, copy);
- objc_addRegisteredClass(copy); // imposter & original will rejoin later, just track the new guy
// Mark the imposter as such
imposter->setInfo(CLS_POSING);
return nil;
}
-BOOL _class_usesAutomaticRetainRelease(Class cls)
-{
- return NO;
-}
-
-uint32_t _class_getInstanceStart(Class cls)
-{
- _objc_fatal("_class_getInstanceStart() unimplemented for fragile instance variables");
- return 0; // PCB: never used just provided for ARR consistency.
-}
-
ptrdiff_t ivar_getOffset(Ivar ivar)
{
return oldivar(ivar)->ivar_offset;
old_method *m = oldmethod(m_gen);
if (!m) return nil;
if (!imp) return nil;
-
- if (ignoreSelector(m->method_name)) {
- // Ignored methods stay ignored
- return m->method_imp;
- }
impLock.lock();
old = m->method_imp;
old_method *m2 = oldmethod(m2_gen);
if (!m1 || !m2) return;
- if (ignoreSelector(m1->method_name) || ignoreSelector(m2->method_name)) {
- // Ignored methods stay ignored. Now they're both ignored.
- m1->method_imp = (IMP)&_objc_ignored_method;
- m2->method_imp = (IMP)&_objc_ignored_method;
- return;
- }
-
impLock.lock();
m1_imp = m1->method_imp;
m1->method_imp = m2->method_imp;
mlist->method_count = 1;
mlist->method_list[0].method_name = name;
mlist->method_list[0].method_types = strdup(types);
- if (!ignoreSelector(name)) {
- mlist->method_list[0].method_imp = imp;
- } else {
- mlist->method_list[0].method_imp = (IMP)&_objc_ignored_method;
- }
+ mlist->method_list[0].method_imp = imp;
_objc_insertMethods(cls, mlist, nil);
if (!(cls->info & CLS_CONSTRUCTING)) {
while ((mlist = nextMethodList(cls, &iterator))) {
int i;
for (i = 0; i < mlist->method_count; i++) {
- Method aMethod = (Method)&mlist->method_list[i];
- if (ignoreSelector(method_getName(aMethod))) {
- count--;
- continue;
- }
- result[m++] = aMethod;
+ result[m++] = (Method)&mlist->method_list[i];
}
}
result[m] = nil;
mutex_locker_t lock(classLock);
- // Build ivar layouts
- if (UseGC) {
- if (cls->ivar_layout != &UnsetLayout) {
- // Class builder already called class_setIvarLayout.
- }
- else if (!cls->superclass) {
- // Root class. Scan conservatively (should be isa ivar only).
- cls->ivar_layout = nil;
- }
- else if (cls->ivars == nil) {
- // No local ivars. Use superclass's layout.
- cls->ivar_layout =
- ustrdupMaybeNil(cls->superclass->ivar_layout);
- }
- else {
- // Has local ivars. Build layout based on superclass.
- Class supercls = cls->superclass;
- const uint8_t *superlayout =
- class_getIvarLayout(supercls);
- layout_bitmap bitmap =
- layout_bitmap_create(superlayout, supercls->instance_size,
- cls->instance_size, NO);
- int i;
- for (i = 0; i < cls->ivars->ivar_count; i++) {
- old_ivar *iv = &cls->ivars->ivar_list[i];
- layout_bitmap_set_ivar(bitmap, iv->ivar_type, iv->ivar_offset);
- }
- cls->ivar_layout = layout_string_create(bitmap);
- layout_bitmap_free(bitmap);
- }
-
- if (cls->ext->weak_ivar_layout != &UnsetLayout) {
- // Class builder already called class_setWeakIvarLayout.
- }
- else if (!cls->superclass) {
- // Root class. No weak ivars (should be isa ivar only)
- cls->ext->weak_ivar_layout = nil;
- }
- else if (cls->ivars == nil) {
- // No local ivars. Use superclass's layout.
- const uint8_t *weak =
- class_getWeakIvarLayout(cls->superclass);
- cls->ext->weak_ivar_layout = ustrdupMaybeNil(weak);
- }
- else {
- // Has local ivars. Build layout based on superclass.
- // No way to add weak ivars yet.
- const uint8_t *weak =
- class_getWeakIvarLayout(cls->superclass);
- cls->ext->weak_ivar_layout = ustrdupMaybeNil(weak);
- }
- }
-
// Clear "under construction" bit, set "done constructing" bit
cls->info &= ~CLS_CONSTRUCTING;
cls->ISA()->info &= ~CLS_CONSTRUCTING;
cls->ISA()->info |= CLS_CONSTRUCTED;
NXHashInsertIfAbsent(class_hash, cls);
- objc_addRegisteredClass(cls);
- //objc_addRegisteredClass(cls->ISA()); if we ever allocate classes from GC
}
mutex_locker_t lock(classLock);
NXHashInsert(class_hash, duplicate);
- objc_addRegisteredClass(duplicate);
return duplicate;
}
mutex_locker_t lock(classLock);
NXHashRemove(class_hash, cls);
- objc_removeRegisteredClass(cls);
unload_class(cls->ISA());
unload_class(cls);
}
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
-#if SUPPORT_GC
- if (UseGC) {
- bytes = auto_zone_allocate_object(gc_zone, size,
- AUTO_OBJECT_SCANNED, 0, 1);
- } else
-#endif
if (zone) {
bytes = malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
size = oldObj->ISA()->alignedInstanceSize() + extraBytes;
// fixme need C++ copy constructor
- objc_memmove_collectable(obj, oldObj, size);
+ memmove(obj, oldObj, size);
-#if SUPPORT_GC
- if (UseGC) gc_fixup_weakreferences(obj, oldObj);
-#endif
+ fixupCopiedIvars(obj, oldObj);
return obj;
}
* Calls C++ destructors.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
-* Be warned that GC DOES NOT CALL THIS. If you edit this, also edit finalize.
* CoreFoundation and other clients do call this under GC.
**********************************************************************/
void *objc_destructInstance(id obj)
_object_remove_assocations(obj);
}
- if (!UseGC) objc_clear_deallocating(obj);
+ objc_clear_deallocating(obj);
}
return obj;
objc_destructInstance(anObject);
-#if SUPPORT_GC
- if (UseGC) {
- auto_zone_retain(gc_zone, anObject); // gc free expects rc==1
- } else
-#endif
- {
- // only clobber isa for non-gc
- anObject->initIsa(_objc_getFreedObjectClass ());
- }
+ anObject->initIsa(_objc_getFreedObjectClass ());
+
free(anObject);
return nil;
}
id class_createInstance(Class cls, size_t extraBytes)
{
- if (UseGC) {
- return _class_createInstance(cls, extraBytes);
- } else {
- return (*_alloc)(cls, extraBytes);
- }
+ return (*_alloc)(cls, extraBytes);
}
id class_createInstanceFromZone(Class cls, size_t extraBytes, void *z)
{
OBJC_WARN_DEPRECATED;
- if (UseGC) {
- return _class_createInstanceFromZone(cls, extraBytes, z);
- } else {
- return (*_zoneAlloc)(cls, extraBytes, z);
- }
+ return (*_zoneAlloc)(cls, extraBytes, z);
}
unsigned class_createInstances(Class cls, size_t extraBytes,
id *results, unsigned num_requested)
{
- if (UseGC || _alloc == &_class_createInstance) {
+ if (_alloc == &_class_createInstance) {
return _class_createInstancesFromZone(cls, extraBytes, nil,
results, num_requested);
} else {
id object_copy(id obj, size_t extraBytes)
{
- if (UseGC) return _object_copy(obj, extraBytes);
- else return (*_copy)(obj, extraBytes);
+ return (*_copy)(obj, extraBytes);
}
id object_copyFromZone(id obj, size_t extraBytes, void *z)
{
OBJC_WARN_DEPRECATED;
- if (UseGC) return _object_copyFromZone(obj, extraBytes, z);
- else return (*_zoneCopy)(obj, extraBytes, z);
+ return (*_zoneCopy)(obj, extraBytes, z);
}
id object_dispose(id obj)
{
- if (UseGC) return _object_dispose(obj);
- else return (*_dealloc)(obj);
+ return (*_dealloc)(obj);
}
id object_realloc(id obj, size_t nBytes)
{
OBJC_WARN_DEPRECATED;
- if (UseGC) return _object_realloc(obj, nBytes);
- else return (*_realloc)(obj, nBytes);
+ return (*_realloc)(obj, nBytes);
}
id object_reallocFromZone(id obj, size_t nBytes, void *z)
{
OBJC_WARN_DEPRECATED;
- if (UseGC) return _object_reallocFromZone(obj, nBytes, z);
- else return (*_zoneRealloc)(obj, nBytes, z);
+ return (*_zoneRealloc)(obj, nBytes, z);
}
#include "objc-private.h"
#include "objc-abi.h"
-#include "objc-auto.h"
#include <objc/message.h>
#endif
-Ivar object_setInstanceVariable(id obj, const char *name, void *value)
-{
- Ivar ivar = nil;
-
- if (obj && name && !obj->isTaggedPointer()) {
- if ((ivar = class_getInstanceVariable(obj->ISA(), name))) {
- object_setIvar(obj, ivar, (id)value);
- }
- }
- return ivar;
-}
-
-Ivar object_getInstanceVariable(id obj, const char *name, void **value)
+static bool isScanned(ptrdiff_t ivar_offset, const uint8_t *layout)
{
- if (obj && name && !obj->isTaggedPointer()) {
- Ivar ivar;
- if ((ivar = class_getInstanceVariable(obj->ISA(), name))) {
- if (value) *value = (void *)object_getIvar(obj, ivar);
- return ivar;
- }
- }
- if (value) *value = nil;
- return nil;
-}
+ if (!layout) return NO;
-static bool is_scanned_offset(ptrdiff_t ivar_offset, const uint8_t *layout) {
ptrdiff_t index = 0, ivar_index = ivar_offset / sizeof(void*);
uint8_t byte;
while ((byte = *layout++)) {
unsigned skips = (byte >> 4);
unsigned scans = (byte & 0x0F);
index += skips;
- while (scans--) {
- if (index == ivar_index) return YES;
- if (index > ivar_index) return NO;
- ++index;
- }
+ if (index > ivar_index) return NO;
+ index += scans;
+ if (index > ivar_index) return YES;
}
return NO;
}
-// FIXME: this could be optimized.
-
-static Class _ivar_getClass(Class cls, Ivar ivar) {
- Class ivar_class = nil;
- const char *ivar_name = ivar_getName(ivar);
- Ivar named_ivar = _class_getVariable(cls, ivar_name, &ivar_class);
- if (named_ivar) {
- // the same ivar name can appear multiple times along the superclass chain.
- while (named_ivar != ivar && ivar_class != nil) {
- ivar_class = ivar_class->superclass;
- named_ivar = _class_getVariable(cls, ivar_getName(ivar), &ivar_class);
+
+/***********************************************************************
+* _class_lookUpIvar
+* Given an object and an ivar in it, look up some data about that ivar:
+* - its offset
+* - its memory management behavior
+* The ivar is assumed to be word-aligned and of of object type.
+**********************************************************************/
+static void
+_class_lookUpIvar(Class cls, Ivar ivar, ptrdiff_t& ivarOffset,
+ objc_ivar_memory_management_t& memoryManagement)
+{
+ ivarOffset = ivar_getOffset(ivar);
+
+ // Look for ARC variables and ARC-style weak.
+
+ // Preflight the hasAutomaticIvars check
+ // because _class_getClassForIvar() may need to take locks.
+ bool hasAutomaticIvars = NO;
+ for (Class c = cls; c; c = c->superclass) {
+ if (c->hasAutomaticIvars()) {
+ hasAutomaticIvars = YES;
+ break;
}
}
- return ivar_class;
-}
-void object_setIvar(id obj, Ivar ivar, id value)
-{
- if (obj && ivar && !obj->isTaggedPointer()) {
- Class cls = _ivar_getClass(obj->ISA(), ivar);
- ptrdiff_t ivar_offset = ivar_getOffset(ivar);
- id *location = (id *)((char *)obj + ivar_offset);
- // if this ivar is a member of an ARR compiled class, then issue the correct barrier according to the layout.
- if (_class_usesAutomaticRetainRelease(cls)) {
- // for ARR, layout strings are relative to the instance start.
- uint32_t instanceStart = _class_getInstanceStart(cls);
- const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
- if (weak_layout && is_scanned_offset(ivar_offset - instanceStart, weak_layout)) {
- // use the weak system to write to this variable.
- objc_storeWeak(location, value);
+ if (hasAutomaticIvars) {
+ Class ivarCls = _class_getClassForIvar(cls, ivar);
+ if (ivarCls->hasAutomaticIvars()) {
+ // ARC layout bitmaps encode the class's own ivars only.
+ // Use alignedInstanceStart() because unaligned bytes at the start
+ // of this class's ivars are not represented in the layout bitmap.
+ ptrdiff_t localOffset =
+ ivarOffset - ivarCls->alignedInstanceStart();
+
+ if (isScanned(localOffset, class_getIvarLayout(ivarCls))) {
+ memoryManagement = objc_ivar_memoryStrong;
return;
}
- const uint8_t *strong_layout = class_getIvarLayout(cls);
- if (strong_layout && is_scanned_offset(ivar_offset - instanceStart, strong_layout)) {
- objc_storeStrong(location, value);
+
+ if (isScanned(localOffset, class_getWeakIvarLayout(ivarCls))) {
+ memoryManagement = objc_ivar_memoryWeak;
return;
}
- }
-#if SUPPORT_GC
- if (UseGC) {
- // for GC, check for weak references.
- const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
- if (weak_layout && is_scanned_offset(ivar_offset, weak_layout)) {
- objc_assign_weak(value, location);
+
+ // Unretained is only for true ARC classes.
+ if (ivarCls->isARC()) {
+ memoryManagement = objc_ivar_memoryUnretained;
+ return;
}
}
- objc_assign_ivar(value, obj, ivar_offset);
-#else
- *location = value;
-#endif
}
+
+ memoryManagement = objc_ivar_memoryUnknown;
+}
+
+
+/***********************************************************************
+* _class_getIvarMemoryManagement
+* SPI for KVO and others to decide what memory management to use
+* when setting instance variables directly.
+**********************************************************************/
+objc_ivar_memory_management_t
+_class_getIvarMemoryManagement(Class cls, Ivar ivar)
+{
+ ptrdiff_t offset;
+ objc_ivar_memory_management_t memoryManagement;
+ _class_lookUpIvar(cls, ivar, offset, memoryManagement);
+ return memoryManagement;
+}
+
+
+static ALWAYS_INLINE
+void _object_setIvar(id obj, Ivar ivar, id value, bool assumeStrong)
+{
+ if (!obj || !ivar || obj->isTaggedPointer()) return;
+
+ ptrdiff_t offset;
+ objc_ivar_memory_management_t memoryManagement;
+ _class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);
+
+ if (memoryManagement == objc_ivar_memoryUnknown) {
+ if (assumeStrong) memoryManagement = objc_ivar_memoryStrong;
+ else memoryManagement = objc_ivar_memoryUnretained;
+ }
+
+ id *location = (id *)((char *)obj + offset);
+
+ switch (memoryManagement) {
+ case objc_ivar_memoryWeak: objc_storeWeak(location, value); break;
+ case objc_ivar_memoryStrong: objc_storeStrong(location, value); break;
+ case objc_ivar_memoryUnretained: *location = value; break;
+ case objc_ivar_memoryUnknown: _objc_fatal("impossible");
+ }
+}
+
+void object_setIvar(id obj, Ivar ivar, id value)
+{
+ return _object_setIvar(obj, ivar, value, false /*not strong default*/);
+}
+
+void object_setIvarWithStrongDefault(id obj, Ivar ivar, id value)
+{
+ return _object_setIvar(obj, ivar, value, true /*strong default*/);
}
id object_getIvar(id obj, Ivar ivar)
{
- if (obj && ivar && !obj->isTaggedPointer()) {
- Class cls = obj->ISA();
- ptrdiff_t ivar_offset = ivar_getOffset(ivar);
- if (_class_usesAutomaticRetainRelease(cls)) {
- // for ARR, layout strings are relative to the instance start.
- uint32_t instanceStart = _class_getInstanceStart(cls);
- const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
- if (weak_layout && is_scanned_offset(ivar_offset - instanceStart, weak_layout)) {
- // use the weak system to read this variable.
- id *location = (id *)((char *)obj + ivar_offset);
- return objc_loadWeak(location);
- }
+ if (!obj || !ivar || obj->isTaggedPointer()) return nil;
+
+ ptrdiff_t offset;
+ objc_ivar_memory_management_t memoryManagement;
+ _class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);
+
+ id *location = (id *)((char *)obj + offset);
+
+ if (memoryManagement == objc_ivar_memoryWeak) {
+ return objc_loadWeak(location);
+ } else {
+ return *location;
+ }
+}
+
+
+static ALWAYS_INLINE
+Ivar _object_setInstanceVariable(id obj, const char *name, void *value,
+ bool assumeStrong)
+{
+ Ivar ivar = nil;
+
+ if (obj && name && !obj->isTaggedPointer()) {
+ if ((ivar = _class_getVariable(obj->ISA(), name))) {
+ _object_setIvar(obj, ivar, (id)value, assumeStrong);
}
- id *idx = (id *)((char *)obj + ivar_offset);
-#if SUPPORT_GC
- if (UseGC) {
- const uint8_t *weak_layout = class_getWeakIvarLayout(cls);
- if (weak_layout && is_scanned_offset(ivar_offset, weak_layout)) {
- return objc_read_weak(idx);
- }
+ }
+ return ivar;
+}
+
+Ivar object_setInstanceVariable(id obj, const char *name, void *value)
+{
+ return _object_setInstanceVariable(obj, name, value, false);
+}
+
+Ivar object_setInstanceVariableWithStrongDefault(id obj, const char *name,
+ void *value)
+{
+ return _object_setInstanceVariable(obj, name, value, true);
+}
+
+
+Ivar object_getInstanceVariable(id obj, const char *name, void **value)
+{
+ if (obj && name && !obj->isTaggedPointer()) {
+ Ivar ivar;
+ if ((ivar = class_getInstanceVariable(obj->ISA(), name))) {
+ if (value) *value = (void *)object_getIvar(obj, ivar);
+ return ivar;
}
-#endif
- return *idx;
}
+ if (value) *value = nil;
return nil;
}
}
+/***********************************************************************
+* fixupCopiedIvars
+* Fix up ARC strong and ARC-style weak variables
+* after oldObject was memcpy'd to newObject.
+**********************************************************************/
+void fixupCopiedIvars(id newObject, id oldObject)
+{
+ for (Class cls = oldObject->ISA(); cls; cls = cls->superclass) {
+ if (cls->hasAutomaticIvars()) {
+ // Use alignedInstanceStart() because unaligned bytes at the start
+ // of this class's ivars are not represented in the layout bitmap.
+ size_t instanceStart = cls->alignedInstanceStart();
+
+ const uint8_t *strongLayout = class_getIvarLayout(cls);
+ if (strongLayout) {
+ id *newPtr = (id *)((char*)newObject + instanceStart);
+ unsigned char byte;
+ while ((byte = *strongLayout++)) {
+ unsigned skips = (byte >> 4);
+ unsigned scans = (byte & 0x0F);
+ newPtr += skips;
+ while (scans--) {
+ // ensure strong references are properly retained.
+ id value = *newPtr++;
+ if (value) objc_retain(value);
+ }
+ }
+ }
+
+ const uint8_t *weakLayout = class_getWeakIvarLayout(cls);
+ // fix up weak references if any.
+ if (weakLayout) {
+ id *newPtr = (id *)((char*)newObject + instanceStart), *oldPtr = (id *)((char*)oldObject + instanceStart);
+ unsigned char byte;
+ while ((byte = *weakLayout++)) {
+ unsigned skips = (byte >> 4);
+ unsigned weaks = (byte & 0x0F);
+ newPtr += skips, oldPtr += skips;
+ while (weaks--) {
+ objc_copyWeak(newPtr, oldPtr);
+ ++newPtr, ++oldPtr;
+ }
+ }
+ }
+ }
+ }
+}
+
+
/***********************************************************************
* _class_resolveClassMethod
* Call +resolveClassMethod, looking for a method to be added to class cls.
{
if (!cls || !name) return nil;
- return _class_getVariable(cls, name, nil);
+ return _class_getVariable(cls, name);
}
bool objcMsgLogEnabled = false;
static int objcMsgLogFD = -1;
+static spinlock_t objcMsgLogLock;
bool logMessageSend(bool isClassMethod,
const char *objectsClass,
implementingClass,
sel_getName(selector));
- static spinlock_t lock;
- lock.lock();
+ objcMsgLogLock.lock();
write (objcMsgLogFD, buf, strlen(buf));
- lock.unlock();
+ objcMsgLogLock.unlock();
// Tell caller to not cache the method
return false;
Class _calloc_class(size_t size)
{
-#if SUPPORT_GC
- if (UseGC) return (Class) malloc_zone_calloc(gc_zone, 1, size);
-#endif
return (Class) calloc(1, size);
}
assert(cls->hasCxxCtor()); // for performance, not correctness
id obj = object_cxxConstructFromClass(bytes, cls);
- if (!obj) {
-#if SUPPORT_GC
- if (UseGC) {
- auto_zone_retain(gc_zone, bytes); // gc free expects rc==1
- }
-#endif
- free(bytes);
- }
+ if (!obj) free(bytes);
return obj;
}
size_t size = cls->instanceSize(extraBytes);
-#if SUPPORT_GC
- if (UseGC) {
- num_allocated =
- auto_zone_batch_allocate(gc_zone, size, AUTO_OBJECT_SCANNED, 0, 1,
- (void**)results, num_requested);
- } else
-#endif
- {
- unsigned i;
- num_allocated =
- malloc_zone_batch_malloc((malloc_zone_t *)(zone ? zone : malloc_default_zone()),
- size, (void**)results, num_requested);
- for (i = 0; i < num_allocated; i++) {
- bzero(results[i], size);
- }
+ num_allocated =
+ malloc_zone_batch_malloc((malloc_zone_t *)(zone ? zone : malloc_default_zone()),
+ size, (void**)results, num_requested);
+ for (unsigned i = 0; i < num_allocated; i++) {
+ bzero(results[i], size);
}
// Construct each object, and delete any that fail construction.
unsigned shift = 0;
- unsigned i;
bool ctor = cls->hasCxxCtor();
- for (i = 0; i < num_allocated; i++) {
+ for (unsigned i = 0; i < num_allocated; i++) {
id obj = results[i];
- obj->initIsa(cls); // fixme allow indexed
+ obj->initIsa(cls); // fixme allow nonpointer
if (ctor) obj = _objc_constructOrFree(obj, cls);
if (obj) {
* inform_duplicate. Complain about duplicate class implementations.
**********************************************************************/
void
-inform_duplicate(const char *name, Class oldCls, Class cls)
+inform_duplicate(const char *name, Class oldCls, Class newCls)
{
#if TARGET_OS_WIN32
(DebugDuplicateClasses ? _objc_fatal : _objc_inform)
("Class %s is implemented in two different images.", name);
#else
const header_info *oldHeader = _headerForClass(oldCls);
- const header_info *newHeader = _headerForClass(cls);
- const char *oldName = oldHeader ? oldHeader->fname : "??";
- const char *newName = newHeader ? newHeader->fname : "??";
+ const header_info *newHeader = _headerForClass(newCls);
+ const char *oldName = oldHeader ? oldHeader->fname() : "??";
+ const char *newName = newHeader ? newHeader->fname() : "??";
(DebugDuplicateClasses ? _objc_fatal : _objc_inform)
- ("Class %s is implemented in both %s and %s. "
+ ("Class %s is implemented in both %s (%p) and %s (%p). "
"One of the two will be used. Which one is undefined.",
- name, oldName, newName);
+ name, oldName, oldCls, newName, newCls);
#endif
}
# define DEBUG 0
#endif
-// Define SUPPORT_GC=1 to enable garbage collection.
-// Be sure to edit OBJC_NO_GC and OBJC_NO_GC_API in objc-api.h as well.
-#if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32 || (TARGET_OS_MAC && __x86_64h__)
-# define SUPPORT_GC 0
+// Define SUPPORT_GC_COMPAT=1 to enable compatibility where GC once was.
+// OBJC_NO_GC and OBJC_NO_GC_API in objc-api.h mean something else.
+#if !TARGET_OS_OSX
+# define SUPPORT_GC_COMPAT 0
#else
-# define SUPPORT_GC 1
+# define SUPPORT_GC_COMPAT 1
#endif
// Define SUPPORT_ZONES=1 to enable malloc zone support in NXHashTable.
-#if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
+#if !TARGET_OS_OSX
# define SUPPORT_ZONES 0
#else
# define SUPPORT_ZONES 1
#endif
// Define SUPPORT_PREOPT=1 to enable dyld shared cache optimizations
-#if TARGET_OS_WIN32 || TARGET_IPHONE_SIMULATOR
+#if TARGET_OS_WIN32 || TARGET_OS_SIMULATOR
# define SUPPORT_PREOPT 0
#else
# define SUPPORT_PREOPT 1
# define SUPPORT_MSB_TAGGED_POINTERS 1
#endif
-// Define SUPPORT_NONPOINTER_ISA=1 to enable extra data in the isa field.
-#if !__LP64__ || TARGET_OS_WIN32 || TARGET_IPHONE_SIMULATOR
+// Define SUPPORT_INDEXED_ISA=1 on platforms that store the class in the isa
+// 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
+# define SUPPORT_INDEXED_ISA 1
+#else
+# define SUPPORT_INDEXED_ISA 0
+#endif
+
+// 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)
+# define SUPPORT_PACKED_ISA 0
+#else
+# define SUPPORT_PACKED_ISA 1
+#endif
+
+// Define SUPPORT_NONPOINTER_ISA=1 on any platform that may store something
+// in the isa field that is not a raw pointer.
+#if !SUPPORT_INDEXED_ISA && !SUPPORT_PACKED_ISA
# define SUPPORT_NONPOINTER_ISA 0
#else
# define SUPPORT_NONPOINTER_ISA 1
// Define SUPPORT_FIXUP=1 to repair calls sites for fixup dispatch.
// Fixup messaging itself is no longer supported.
// Be sure to edit objc-abi.h as well (objc_msgSend*_fixup)
-// Note TARGET_OS_MAC is also set for iOS simulator.
-#if !__x86_64__ || !TARGET_OS_MAC
+#if !(defined(__x86_64__) && (TARGET_OS_OSX || TARGET_OS_SIMULATOR))
# define SUPPORT_FIXUP 0
#else
# define SUPPORT_FIXUP 1
#endif
-// Define SUPPORT_IGNORED_SELECTOR_CONSTANT to remap GC-ignored selectors.
-// Good: fast ignore in objc_msgSend. Bad: disable shared cache optimizations
-// Now used only for old-ABI GC.
-// This is required for binary compatibility on 32-bit Mac: rdar://13757938
-#if __OBJC2__ || !SUPPORT_GC
-# define SUPPORT_IGNORED_SELECTOR_CONSTANT 0
-#else
-# define SUPPORT_IGNORED_SELECTOR_CONSTANT 1
-# if defined(__i386__)
-# define kIgnore 0xfffeb010
-# else
-# error unknown architecture
-# endif
-#endif
-
// Define SUPPORT_ZEROCOST_EXCEPTIONS to use "zero-cost" exceptions for OBJC2.
// Be sure to edit objc-exception.h as well (objc_add/removeExceptionHandler)
#if !__OBJC2__ || (defined(__arm__) && __USING_SJLJ_EXCEPTIONS__)
#endif
// Define SUPPORT_RETURN_AUTORELEASE to optimize autoreleased return values
-#if !__OBJC2__ || TARGET_OS_WIN32
+#if TARGET_OS_WIN32
# define SUPPORT_RETURN_AUTORELEASE 0
#else
# define SUPPORT_RETURN_AUTORELEASE 1
OPTION( PrintVtableImages, OBJC_PRINT_VTABLE_IMAGES, "print vtable images showing overridden methods")
OPTION( PrintCaches, OBJC_PRINT_CACHE_SETUP, "log processing of method caches")
OPTION( PrintFuture, OBJC_PRINT_FUTURE_CLASSES, "log use of future classes for toll-free bridging")
-OPTION( PrintGC, OBJC_PRINT_GC, "log some GC operations")
OPTION( PrintPreopt, OBJC_PRINT_PREOPTIMIZATION, "log preoptimization courtesy of dyld shared cache")
OPTION( PrintCxxCtors, OBJC_PRINT_CXX_CTORS, "log calls to C++ ctors and dtors for instance variables")
OPTION( PrintExceptions, OBJC_PRINT_EXCEPTIONS, "log exception handling")
OPTION( DebugUnload, OBJC_DEBUG_UNLOAD, "warn about poorly-behaving bundles when unloaded")
OPTION( DebugFragileSuperclasses, OBJC_DEBUG_FRAGILE_SUPERCLASSES, "warn about subclasses that may have been broken by subsequent changes to superclasses")
-OPTION( DebugFinalizers, OBJC_DEBUG_FINALIZERS, "warn about classes that implement -dealloc but not -finalize")
OPTION( DebugNilSync, OBJC_DEBUG_NIL_SYNC, "warn about @synchronized(nil), which does no synchronization")
OPTION( DebugNonFragileIvars, OBJC_DEBUG_NONFRAGILE_IVARS, "capriciously rearrange non-fragile ivars")
OPTION( DebugAltHandlers, OBJC_DEBUG_ALT_HANDLERS, "record more info about bad alt handler use")
OPTION( DebugMissingPools, OBJC_DEBUG_MISSING_POOLS, "warn about autorelease with no pool in place, which may be a leak")
OPTION( DebugPoolAllocation, OBJC_DEBUG_POOL_ALLOCATION, "halt when autorelease pools are popped out of order, and allow heap debuggers to track autorelease pools")
OPTION( DebugDuplicateClasses, OBJC_DEBUG_DUPLICATE_CLASSES, "halt when multiple classes with the same name are present")
+OPTION( DebugDontCrash, OBJC_DEBUG_DONT_CRASH, "halt the process by exiting instead of crashing")
-OPTION( DisableGC, OBJC_DISABLE_GC, "force GC OFF, even if the executable wants it on")
OPTION( DisableVtables, OBJC_DISABLE_VTABLES, "disable vtable dispatch")
OPTION( DisablePreopt, OBJC_DISABLE_PREOPTIMIZATION, "disable preoptimization courtesy of dyld shared cache")
OPTION( DisableTaggedPointers, OBJC_DISABLE_TAGGED_POINTERS, "disable tagged pointer optimization of NSNumber et al.")
-OPTION( DisableIndexedIsa, OBJC_DISABLE_NONPOINTER_ISA, "disable non-pointer isa fields")
+OPTION( DisableNonpointerIsa, OBJC_DISABLE_NONPOINTER_ISA, "disable non-pointer isa fields")
#else
-#include <vproc_priv.h>
#include <_simple.h>
OBJC_EXPORT void (*_error)(id, const char *, va_list);
-static void _objc_trap(void) __attribute__((noreturn));
-
// Return true if c is a UTF8 continuation byte
static bool isUTF8Continuation(char c)
{
}
// Add "message" to any forthcoming crash log.
+static mutex_t crashlog_lock;
static void _objc_crashlog(const char *message)
{
char *newmsg;
}
#endif
- static mutex_t crashlog_lock;
mutex_locker_t lock(crashlog_lock);
char *oldmsg = (char *)CRGetCrashLogMessage();
/*
* _objc_error is the default *_error handler.
*/
-#if __OBJC2__
-__attribute__((noreturn))
-#else
+#if !__OBJC2__
// used by ExceptionHandling.framework
#endif
+__attribute__((noreturn))
void _objc_error(id self, const char *fmt, va_list ap)
{
- char *buf1;
- char *buf2;
-
- vasprintf(&buf1, fmt, ap);
- asprintf(&buf2, "objc[%d]: %s: %s\n",
- getpid(), object_getClassName(self), buf1);
- _objc_syslog(buf2);
- _objc_crashlog(buf2);
-
- _objc_trap();
+ char *buf;
+ vasprintf(&buf, fmt, ap);
+ _objc_fatal("%s: %s", object_getClassName(self), buf);
}
/*
va_end(vp);
}
-/*
- * this routine handles severe runtime errors...like not being able
- * to read the mach headers, allocate space, etc...very uncommon.
- */
-void _objc_fatal(const char *fmt, ...)
+static __attribute__((noreturn))
+void _objc_fatalv(uint64_t reason, uint64_t flags, const char *fmt, va_list ap)
{
- va_list ap;
char *buf1;
- char *buf2;
-
- va_start(ap,fmt);
vasprintf(&buf1, fmt, ap);
- va_end (ap);
+ char *buf2;
asprintf(&buf2, "objc[%d]: %s\n", getpid(), buf1);
_objc_syslog(buf2);
- _objc_crashlog(buf2);
- _objc_trap();
+ if (DebugDontCrash) {
+ char *buf3;
+ asprintf(&buf3, "objc[%d]: HALTED\n", getpid());
+ _objc_syslog(buf3);
+ _Exit(1);
+ }
+ else {
+ abort_with_reason(OS_REASON_OBJC, reason, buf1, flags);
+ }
+}
+
+void _objc_fatal_with_reason(uint64_t reason, uint64_t flags,
+ const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ _objc_fatalv(reason, flags, fmt, ap);
+}
+
+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,
+ fmt, ap);
}
/*
free(buf1);
}
-
-/* Kill the process in a way that generates a crash log.
- * This is better than calling exit(). */
-static void _objc_trap(void)
-{
- __builtin_trap();
-}
-
-/* Try to keep _objc_warn_deprecated out of crash logs
- * caused by _objc_trap(). rdar://4546883 */
-__attribute__((used))
-static void _objc_trap2(void)
-{
- __builtin_trap();
-}
-
#endif
// compiler reserves a setjmp buffer + 4 words as localExceptionData
OBJC_EXPORT void objc_exception_throw(id exception)
- __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
+ __OSX_AVAILABLE(10.3)
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_exception_try_enter(void *localExceptionData)
- __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
+ __OSX_AVAILABLE(10.3)
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_exception_try_exit(void *localExceptionData)
- __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
+ __OSX_AVAILABLE(10.3)
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT id objc_exception_extract(void *localExceptionData)
- __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
+ __OSX_AVAILABLE(10.3)
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT int objc_exception_match(Class exceptionClass, id exception)
- __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
+ __OSX_AVAILABLE(10.3)
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
typedef struct {
// get table; version tells how many
OBJC_EXPORT void objc_exception_get_functions(objc_exception_functions_t *table)
- __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
+ __OSX_AVAILABLE(10.3)
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
// set table
OBJC_EXPORT void objc_exception_set_functions(objc_exception_functions_t *table)
- __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_NA);
+ __OSX_AVAILABLE(10.3)
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
// !__OBJC2__
* @param exception The exception to be thrown.
*/
OBJC_EXPORT void objc_exception_throw(id exception)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT void objc_exception_rethrow(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT id objc_begin_catch(void *exc_buf)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT void objc_end_catch(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT void objc_terminate(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0);
+ OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
OBJC_EXPORT objc_exception_preprocessor objc_setExceptionPreprocessor(objc_exception_preprocessor fn)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT objc_exception_matcher objc_setExceptionMatcher(objc_exception_matcher fn)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
// Not for iOS.
OBJC_EXPORT uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+ __OSX_AVAILABLE(10.5)
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT void objc_removeExceptionHandler(uintptr_t token)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_NA);
+ __OSX_AVAILABLE(10.5)
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
// __OBJC2__
#endif
exc, obj, object_getClassName(obj));
}
-#if SUPPORT_GC
- if (UseGC) {
- if (auto_zone_is_valid_pointer(gc_zone, obj)) {
- auto_zone_release(gc_zone, exc->obj);
- }
- }
- else
-#endif
- {
- [obj release];
- }
+ [obj release];
}
obj = (*exception_preprocessor)(obj);
- // Retain the exception object during unwinding.
- // GC: because `exc` is unscanned memory
- // Non-GC: because otherwise an autorelease pool pop can cause a crash
-#if SUPPORT_GC
- if (UseGC) {
- if (auto_zone_is_valid_pointer(gc_zone, obj)) {
- auto_zone_retain(gc_zone, obj);
- }
- }
- else
-#endif
- {
- [obj retain];
- }
+ // Retain the exception object during unwinding
+ // because otherwise an autorelease pool pop can cause a crash
+ [obj retain];
exc->obj = obj;
exc->tinfo.vtable = objc_ehtype_vtable+2;
static struct alt_handler_list *DebugLists;
static uintptr_t DebugCounter;
-void alt_handler_error(uintptr_t token) __attribute__((noinline));
+__attribute__((noinline, noreturn))
+void alt_handler_error(uintptr_t token);
static struct alt_handler_list *
fetch_handler_list(bool create)
if (!list || !list->handlers) {
// no alt handlers active
alt_handler_error(token);
- __builtin_trap();
}
uintptr_t i = token-1;
if (i >= list->allocated) {
// token out of range
alt_handler_error(token);
- __builtin_trap();
}
struct alt_handler_data *data = &list->handlers[i];
if (data->frame.ip_start == 0 && data->frame.ip_end == 0 && data->frame.cfa == 0) {
// token in range, but invalid
alt_handler_error(token);
- __builtin_trap();
}
if (PrintAltHandlers) {
list->used--;
}
-void objc_alt_handler_error(void) __attribute__((noinline));
+BREAKPOINT_FUNCTION(
+void objc_alt_handler_error(void));
+
+__attribute__((noinline, noreturn))
void alt_handler_error(uintptr_t token)
{
- if (!DebugAltHandlers) {
- _objc_inform_now_and_on_crash
- ("objc_removeExceptionHandler() called with unknown alt handler; "
- "this is probably a bug in multithreaded AppKit use. "
- "Set environment variable OBJC_DEBUG_ALT_HANDLERS=YES "
- "or break in objc_alt_handler_error() to debug.");
- objc_alt_handler_error();
- }
+ _objc_inform
+ ("objc_removeExceptionHandler() called with unknown alt handler; "
+ "this is probably a bug in multithreaded AppKit use. "
+ "Set environment variable OBJC_DEBUG_ALT_HANDLERS=YES "
+ "or break in objc_alt_handler_error() to debug.");
- DebugLock.lock();
-
- // Search other threads' alt handler lists for this handler.
- struct alt_handler_list *list;
- for (list = DebugLists; list; list = list->next_DEBUGONLY) {
- unsigned h;
- for (h = 0; h < list->allocated; h++) {
- struct alt_handler_data *data = &list->handlers[h];
- if (data->debug && data->debug->token == token) {
- // found it
- int i;
-
- // Build a string from the recorded backtrace
- char *symbolString;
- char **symbols =
- backtrace_symbols(data->debug->backtrace,
- data->debug->backtraceSize);
- size_t len = 1;
- for (i = 0; i < data->debug->backtraceSize; i++){
- len += 4 + strlen(symbols[i]) + 1;
- }
- symbolString = (char *)calloc(len, 1);
- for (i = 0; i < data->debug->backtraceSize; i++){
- strcat(symbolString, " ");
- strcat(symbolString, symbols[i]);
- strcat(symbolString, "\n");
+ if (DebugAltHandlers) {
+ DebugLock.lock();
+
+ // Search other threads' alt handler lists for this handler.
+ struct alt_handler_list *list;
+ for (list = DebugLists; list; list = list->next_DEBUGONLY) {
+ unsigned h;
+ for (h = 0; h < list->allocated; h++) {
+ struct alt_handler_data *data = &list->handlers[h];
+ if (data->debug && data->debug->token == token) {
+ // found it
+ int i;
+
+ // Build a string from the recorded backtrace
+ char *symbolString;
+ char **symbols =
+ backtrace_symbols(data->debug->backtrace,
+ data->debug->backtraceSize);
+ size_t len = 1;
+ for (i = 0; i < data->debug->backtraceSize; i++){
+ len += 4 + strlen(symbols[i]) + 1;
+ }
+ symbolString = (char *)calloc(len, 1);
+ for (i = 0; i < data->debug->backtraceSize; i++){
+ strcat(symbolString, " ");
+ strcat(symbolString, symbols[i]);
+ strcat(symbolString, "\n");
+ }
+
+ free(symbols);
+
+ _objc_inform_now_and_on_crash
+ ("The matching objc_addExceptionHandler() was called "
+ "by:\nThread '%s': Dispatch queue: '%s': \n%s",
+ data->debug->thread, data->debug->queue, symbolString);
+
+ goto done;
}
-
- free(symbols);
-
- _objc_inform_now_and_on_crash
- ("objc_removeExceptionHandler() called with "
- "unknown alt handler; this is probably a bug in "
- "multithreaded AppKit use. \n"
- "The matching objc_addExceptionHandler() was called by:\n"
- "Thread '%s': Dispatch queue: '%s': \n%s",
- data->debug->thread, data->debug->queue, symbolString);
-
- DebugLock.unlock();
- free(symbolString);
-
- objc_alt_handler_error();
}
}
+ done:
+ DebugLock.unlock();
}
- DebugLock.unlock();
- // not found
- _objc_inform_now_and_on_crash
- ("objc_removeExceptionHandler() called with unknown alt handler; "
- "this is probably a bug in multithreaded AppKit use");
objc_alt_handler_error();
-}
-
-void objc_alt_handler_error(void)
-{
- __builtin_trap();
+
+ _objc_fatal
+ ("objc_removeExceptionHandler() called with unknown alt handler; "
+ "this is probably a bug in multithreaded AppKit use. ");
}
// called in order registered, to match 32-bit _NSAddAltHandler2
+++ /dev/null
-/*
- * Copyright (c) 2010 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include "objc-private.h"
-
-#include <malloc/malloc.h>
-#include <assert.h>
-#include "runtime.h"
-#include "objc-os.h"
-#include "message.h"
-#if SUPPORT_GC
-#include "auto_zone.h"
-#endif
-
-enum {
- // external references to data segment objects all use this type
- OBJC_XREF_TYPE_STATIC = 3,
-
- OBJC_XREF_TYPE_MASK = 3
-};
-
-// Macros to encode/decode reference values and types.
-#define encode_pointer_and_type(pointer, type) (~((uintptr_t)(pointer) | type))
-#define decode_pointer(encoded) ((id)((~(encoded)) & (~OBJC_XREF_TYPE_MASK)))
-#define decode_type(encoded) ((~(encoded)) & OBJC_XREF_TYPE_MASK)
-#define encode_index_and_type(index, type) (~((index<<3) | type))
-#define decode_index(encoded) ((~encoded)>>3)
-
-#if SUPPORT_GC
-
-typedef struct {
- objc_xref_type_t _type; // type of list.
- dispatch_queue_t _synchronizer; // a reader/write lock
- __strong void **_buffer; // a retained all pointers block
- size_t _size; // number of pointers that fit in _list (buffer size)
- size_t _count; // count of pointers in _list (in use count)
- size_t _search; // lowest index in list which *might* be unused
-} external_ref_list;
-
-static external_ref_list _xref_lists[2];
-
-#define is_strong(list) (list->_type == OBJC_XREF_STRONG)
-#define is_weak(list) (list->_type == OBJC_XREF_WEAK)
-
-inline static size_t _index_for_type(objc_xref_type_t ref_type) {
- assert(ref_type == OBJC_XREF_STRONG || ref_type == OBJC_XREF_WEAK);
- return (ref_type - 1);
-}
-
-static void _initialize_gc() {
- static dispatch_once_t init_guard;
- dispatch_once(&init_guard, ^{
- external_ref_list *_strong_list = &_xref_lists[_index_for_type(OBJC_XREF_STRONG)];
- _strong_list->_type = OBJC_XREF_STRONG;
- _strong_list->_synchronizer = dispatch_queue_create("OBJC_XREF_STRONG synchronizer", DISPATCH_QUEUE_CONCURRENT);
-
- external_ref_list *_weak_list = &_xref_lists[_index_for_type(OBJC_XREF_WEAK)];
- _weak_list->_type = OBJC_XREF_WEAK;
- _weak_list->_synchronizer = dispatch_queue_create("OBJC_XREF_WEAK synchronizer", DISPATCH_QUEUE_CONCURRENT);
- });
-}
-
-#define EMPTY_SLOT ((void*)0x1)
-
-// grow the buffer by one page
-static bool _grow_list(external_ref_list *list) {
- auto_memory_type_t memory_type = (is_strong(list) ? AUTO_MEMORY_ALL_POINTERS : AUTO_MEMORY_ALL_WEAK_POINTERS);
- size_t new_size = list->_size + PAGE_MAX_SIZE / sizeof(void *);
- // auto_realloc() has been enhanced to handle strong and weak memory.
- void **new_list = (void **)(list->_buffer ? malloc_zone_realloc(gc_zone, list->_buffer, new_size * sizeof(void *)) : auto_zone_allocate_object(gc_zone, new_size * sizeof(void *), memory_type, false, false));
- if (!new_list) _objc_fatal("unable to allocate, size = %ld\n", new_size);
-
- list->_search = list->_size;
- // Fill the newly allocated space with empty slot tokens.
- for (size_t index = list->_size; index < new_size; ++index)
- new_list[index] = EMPTY_SLOT;
- list->_size = new_size;
- auto_zone_root_write_barrier(gc_zone, &list->_buffer, new_list);
- return true;
-}
-
-
-// find an unused slot in the list, growing the list if necessary
-static size_t _find_unused_index(external_ref_list *list) {
- size_t index;
- if (list->_size == list->_count) {
- _grow_list(list);
- }
- // find the lowest unused index in _list
- index = list->_search;
- while (list->_buffer[index] != EMPTY_SLOT)
- index++;
- // mark the slot as no longer empty, good form for weak slots.
- list->_buffer[index] = NULL;
- return index;
-}
-
-
-// return the strong or weak list
-inline static external_ref_list *_list_for_type(objc_xref_type_t ref_type) {
- return &_xref_lists[_index_for_type(ref_type)];
-}
-
-
-// create a GC external reference
-objc_xref_t _object_addExternalReference_gc(id obj, objc_xref_type_t ref_type) {
- _initialize_gc();
- __block size_t index;
- objc_xref_t xref;
-
- if (auto_zone_is_valid_pointer(gc_zone, obj)) {
- external_ref_list *list = _list_for_type(ref_type);
-
- // writer lock
- dispatch_barrier_sync(list->_synchronizer, (dispatch_block_t)^{
- index = _find_unused_index(list);
- if (ref_type == OBJC_XREF_STRONG) {
- auto_zone_set_write_barrier(gc_zone, &list->_buffer[index], obj);
- } else {
- auto_assign_weak_reference(gc_zone, obj, (const void **)&list->_buffer[index], NULL);
- }
- list->_count++;
- });
- xref = encode_index_and_type(index, ref_type);
- } else {
- // data segment object
- xref = encode_pointer_and_type(obj, OBJC_XREF_TYPE_STATIC);
- }
- return xref;
-}
-
-
-id _object_readExternalReference_gc(objc_xref_t ref) {
- _initialize_gc();
- __block id result;
- objc_xref_type_t ref_type = decode_type(ref);
- if (ref_type != OBJC_XREF_TYPE_STATIC) {
- size_t index = decode_index(ref);
- external_ref_list *list = _list_for_type(ref_type);
-
- dispatch_sync(list->_synchronizer, ^{
- if (index >= list->_size) {
- _objc_fatal("attempted to resolve invalid external reference\n");
- }
- if (ref_type == OBJC_XREF_STRONG)
- result = (id)list->_buffer[index];
- else
- result = (id)auto_read_weak_reference(gc_zone, &list->_buffer[index]);
- if (result == (id)EMPTY_SLOT)
- _objc_fatal("attempted to resolve unallocated external reference\n");
- });
- } else {
- // data segment object
- result = decode_pointer(ref);
- }
- return result;
-}
-
-
-void _object_removeExternalReference_gc(objc_xref_t ref) {
- _initialize_gc();
- objc_xref_type_t ref_type = decode_type(ref);
- if (ref_type != OBJC_XREF_TYPE_STATIC) {
- size_t index = decode_index(ref);
- external_ref_list *list = _list_for_type(ref_type);
-
- dispatch_barrier_sync(list->_synchronizer, ^{
- if (index >= list->_size) {
- _objc_fatal("attempted to destroy invalid external reference\n");
- }
- id old_value;
- if (ref_type == OBJC_XREF_STRONG) {
- old_value = (id)list->_buffer[index];
- } else {
- old_value = (id)auto_read_weak_reference(gc_zone, &list->_buffer[index]);
- auto_assign_weak_reference(gc_zone, NULL, (const void **)&list->_buffer[index], NULL);
- }
- list->_buffer[index] = EMPTY_SLOT;
- if (old_value == (id)EMPTY_SLOT)
- _objc_fatal("attempted to destroy unallocated external reference\n");
- list->_count--;
- if (list->_search > index)
- list->_search = index;
- });
- } else {
- // nothing for data segment object
- }
-}
-
-
-// SUPPORT_GC
-#endif
-
-
-objc_xref_t _object_addExternalReference_non_gc(id obj, objc_xref_type_t ref_type) {
- switch (ref_type) {
- case OBJC_XREF_STRONG:
- ((id(*)(id, SEL))objc_msgSend)(obj, SEL_retain);
- break;
- case OBJC_XREF_WEAK:
- break;
- default:
- _objc_fatal("invalid external reference type: %d", (int)ref_type);
- break;
- }
- return encode_pointer_and_type(obj, ref_type);
-}
-
-
-id _object_readExternalReference_non_gc(objc_xref_t ref) {
- id obj = decode_pointer(ref);
- return obj;
-}
-
-
-void _object_removeExternalReference_non_gc(objc_xref_t ref) {
- id obj = decode_pointer(ref);
- objc_xref_type_t ref_type = decode_type(ref);
- switch (ref_type) {
- case OBJC_XREF_STRONG:
- ((void(*)(id, SEL))objc_msgSend)(obj, SEL_release);
- break;
- case OBJC_XREF_WEAK:
- break;
- default:
- _objc_fatal("invalid external reference type: %d", (int)ref_type);
- break;
- }
-}
-
-
-uintptr_t _object_getExternalHash(id object) {
- return (uintptr_t)object;
-}
-
-
-#if SUPPORT_GC
-
-// These functions are resolver functions in objc-auto.mm.
-
-#else
-
-objc_xref_t
-_object_addExternalReference(id obj, objc_xref_t type)
-{
- return _object_addExternalReference_non_gc(obj, type);
-}
-
-
-id
-_object_readExternalReference(objc_xref_t ref)
-{
- return _object_readExternalReference_non_gc(ref);
-}
-
-
-void
-_object_removeExternalReference(objc_xref_t ref)
-{
- _object_removeExternalReference_non_gc(ref);
-}
-
-#endif
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);
+
__END_DECLS
#endif
#else
-#define GETSECT(name, type, sectname) \
- type *name(const header_info *hi, size_t *outCount) \
+#define GETSECT(name, type, segname, sectname) \
+ type *name(const headerType *mhdr, size_t *outCount) \
{ \
unsigned long byteCount = 0; \
type *data = (type *) \
- getsectiondata(hi->mhdr, SEG_OBJC, sectname, &byteCount); \
+ getsectiondata(mhdr, segname, sectname, &byteCount); \
*outCount = byteCount / sizeof(type); \
return data; \
+ } \
+ type *name(const header_info *hi, size_t *outCount) \
+ { \
+ return name(hi->mhdr(), outCount); \
}
-GETSECT(_getObjcModules, struct objc_module, "__module_info");
-GETSECT(_getObjcSelectorRefs, SEL, "__message_refs");
-GETSECT(_getObjcClassRefs, Class, "__cls_refs");
-GETSECT(_getObjcClassNames, const char, "__class_names");
+GETSECT(_getObjcModules, objc_module, "__OBJC", "__module_info");
+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");
objc_image_info *
{
unsigned long size = 0;
struct old_protocol *protos = (struct old_protocol *)
- getsectiondata(hi->mhdr, SEG_OBJC, "__protocol", &size);
+ getsectiondata(hi->mhdr(), SEG_OBJC, "__protocol", &size);
*nprotos = size / sizeof(struct old_protocol);
if (!hi->proto_refs && *nprotos) {
_hasObjcContents(const header_info *hi)
{
// Look for an __OBJC,* section other than __OBJC,__image_info
- const segmentType *seg = getsegbynamefromheader(hi->mhdr, "__OBJC");
+ const segmentType *seg = getsegbynamefromheader(hi->mhdr(), "__OBJC");
const sectionType *sect;
uint32_t i;
for (i = 0; i < seg->nsects; i++) {
#include "objc-private.h"
#include "objc-file.h"
-// Segment and section names are 16 bytes and may be un-terminated.
-bool segnameEquals(const char *lhs, const char *rhs) {
- return 0 == strncmp(lhs, rhs, 16);
-}
-
-bool segnameStartsWith(const char *segname, const char *prefix) {
- return 0 == strncmp(segname, prefix, strlen(prefix));
-}
-
-bool sectnameEquals(const char *lhs, const char *rhs) {
- return segnameEquals(lhs, rhs);
-}
-
-bool sectnameStartsWith(const char *sectname, const char *prefix) {
- return segnameStartsWith(sectname, prefix);
-}
-
// Look for a __DATA or __DATA_CONST or __DATA_DIRTY section
// with the given name that stores an array of T.
return getDataSection<type>(mhdr, sectname, nil, outCount); \
} \
type *name(const header_info *hi, size_t *outCount) { \
- return getDataSection<type>(hi->mhdr, sectname, nil, outCount); \
+ return getDataSection<type>(hi->mhdr(), sectname, nil, outCount); \
}
// function name content type section name
GETSECT(_getObjc2NonlazyCategoryList, category_t *, "__objc_nlcatlist");
GETSECT(_getObjc2ProtocolList, protocol_t *, "__objc_protolist");
GETSECT(_getObjc2ProtocolRefs, protocol_t *, "__objc_protorefs");
-GETSECT(getLibobjcInitializers, Initializer, "__objc_init_func");
+GETSECT(getLibobjcInitializers, Initializer, "__objc_init_func");
objc_image_info *
_hasObjcContents(const header_info *hi)
{
const segmentType *data =
- getsegbynamefromheader(hi->mhdr, "__DATA");
+ getsegbynamefromheader(hi->mhdr(), "__DATA");
const segmentType *data_const =
- getsegbynamefromheader(hi->mhdr, "__DATA_CONST");
+ getsegbynamefromheader(hi->mhdr(), "__DATA_CONST");
const segmentType *data_dirty =
- getsegbynamefromheader(hi->mhdr, "__DATA_CONST");
+ getsegbynamefromheader(hi->mhdr(), "__DATA_DIRTY");
return segmentHasObjcContents(data)
|| segmentHasObjcContents(data_const)
|| segmentHasObjcContents(data_dirty);
}
+
+// OBJC2
#endif
// Return cls if it's a valid class, or crash.
OBJC_EXPORT Class gdb_class_getClass(Class cls)
#if __OBJC2__
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
+ OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
#else
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_3_1);
+ OBJC_AVAILABLE(10.7, 3.1, 9.0, 1.0);
#endif
// Same as gdb_class_getClass(object_getClass(cls)).
OBJC_EXPORT Class gdb_object_getClass(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
/***********************************************************************
// Maps class name to Class, for in-use classes only. NXStrValueMapPrototype.
OBJC_EXPORT NXMapTable *gdb_objc_realized_classes
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
+ OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
#else
// Hashes Classes, for all known classes. Custom prototype.
OBJC_EXPORT NXHashTable *_objc_debug_class_hash
- __OSX_AVAILABLE_STARTING(__MAC_10_2, __IPHONE_NA);
+ __OSX_AVAILABLE(10.2)
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
#endif
// Extract isa pointer from an isa field.
// (Class)(isa & mask) == class pointer
OBJC_EXPORT const uintptr_t objc_debug_isa_class_mask
- __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_7_0);
+ OBJC_AVAILABLE(10.10, 7.0, 9.0, 1.0);
// Extract magic cookie from an isa field.
// (isa & magic_mask) == magic_value
OBJC_EXPORT const uintptr_t objc_debug_isa_magic_mask
- __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_7_0);
+ OBJC_AVAILABLE(10.10, 7.0, 9.0, 1.0);
OBJC_EXPORT const uintptr_t objc_debug_isa_magic_value
- __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_7_0);
+ OBJC_AVAILABLE(10.10, 7.0, 9.0, 1.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.
+OBJC_EXPORT const uintptr_t objc_debug_indexed_isa_magic_mask;
+OBJC_EXPORT const uintptr_t objc_debug_indexed_isa_magic_value;
+
+// Then these are used to extract the index from the ISA.
+OBJC_EXPORT const uintptr_t objc_debug_indexed_isa_index_mask;
+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[];
+
+// 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.
+// Note, an index of 0 is illegal.
+OBJC_EXPORT uintptr_t objc_indexed_classes_count;
+
+// Absolute symbols for some of the above values are in objc-abi.h.
#endif
**********************************************************************/
#if __OBJC2__
+// Basic tagged pointers (7 classes, 60-bit payload).
+
// if (obj & mask) obj is a tagged pointer object
OBJC_EXPORT uintptr_t objc_debug_taggedpointer_mask
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
// tag_slot = (obj >> slot_shift) & slot_mask
OBJC_EXPORT unsigned int objc_debug_taggedpointer_slot_shift
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
OBJC_EXPORT uintptr_t objc_debug_taggedpointer_slot_mask
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
// class = classes[tag_slot]
OBJC_EXPORT Class objc_debug_taggedpointer_classes[]
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
// payload = (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
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
OBJC_EXPORT unsigned int objc_debug_taggedpointer_payload_rshift
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
+
+
+// Extended tagged pointers (255 classes, 52-bit payload).
+
+// If you interrogate an extended tagged pointer using the basic
+// 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)
+// 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);
+
+// 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_EXPORT uintptr_t objc_debug_taggedpointer_ext_slot_mask
+ OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.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);
+
+// payload = (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_EXPORT unsigned int objc_debug_taggedpointer_ext_payload_rshift
+ OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
#endif
OBJC_EXPORT struct objc_messenger_breakpoint
gdb_objc_messenger_breakpoints[]
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
#endif
-#ifndef OBJC_NO_GC
-
-/***********************************************************************
- * Garbage Collector heap dump
-**********************************************************************/
-
-/* Dump GC heap; if supplied the name is returned in filenamebuffer. Returns YES on success. */
-OBJC_EXPORT BOOL objc_dumpHeap(char *filenamebuffer, unsigned long length)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);
-
-#define OBJC_HEAP_DUMP_FILENAME_FORMAT "/tmp/objc-gc-heap-dump-%d-%d"
-
-#endif
-
__END_DECLS
#endif
extern void _destroyInitializingClassList(struct _objc_initializing_classes *list);
+extern bool _thisThreadIsInitializingClass(Class cls);
+
__END_DECLS
#endif
* If create == YES, create the list when no classes are being initialized by this thread.
* If create == NO, return nil when no classes are being initialized by this thread.
**********************************************************************/
-static _objc_initializing_classes *_fetchInitializingClassList(BOOL create)
+static _objc_initializing_classes *_fetchInitializingClassList(bool create)
{
_objc_pthread_data *data;
_objc_initializing_classes *list;
* _thisThreadIsInitializingClass
* Return TRUE if this thread is currently initializing the given class.
**********************************************************************/
-static BOOL _thisThreadIsInitializingClass(Class cls)
+bool _thisThreadIsInitializingClass(Class cls)
{
int i;
cls->nameForLogging());
}
- // propagate finalization affinity.
- if (UseGC && supercls && supercls->shouldFinalizeOnMainThread()) {
- cls->setShouldFinalizeOnMainThread();
- }
-
// mark this class as fully +initialized
cls->setInitialized();
classInitLock.notifyAll();
}
+// Provide helpful messages in stack traces.
+OBJC_EXTERN __attribute__((noinline, used, visibility("hidden")))
+void waitForInitializeToComplete(Class cls)
+ asm("_WAITING_FOR_ANOTHER_THREAD_TO_FINISH_CALLING_+initialize");
+OBJC_EXTERN __attribute__((noinline, used, visibility("hidden")))
+void callInitialize(Class cls)
+ asm("_CALLING_SOME_+initialize_METHOD");
+
+
+void waitForInitializeToComplete(Class cls)
+{
+ monitor_locker_t lock(classInitLock);
+ while (!cls->isInitialized()) {
+ classInitLock.wait();
+ }
+ asm("");
+}
+
+
+void callInitialize(Class cls)
+{
+ ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
+ asm("");
+}
+
+
/***********************************************************************
* class_initialize. Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
assert(!cls->isMetaClass());
Class supercls;
- BOOL reallyInitialize = NO;
+ bool reallyInitialize = NO;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
cls->nameForLogging());
}
- ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
+ // Exceptions: A +initialize call that throws an exception
+ // is deemed to be a complete and successful +initialize.
+ @try {
+ callInitialize(cls);
- if (PrintInitializing) {
- _objc_inform("INITIALIZE: finished +[%s initialize]",
- cls->nameForLogging());
- }
-
- // 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);
+ if (PrintInitializing) {
+ _objc_inform("INITIALIZE: finished +[%s initialize]",
+ cls->nameForLogging());
+ }
+ }
+ @catch (...) {
+ if (PrintInitializing) {
+ _objc_inform("INITIALIZE: +[%s initialize] threw an exception",
+ 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);
+ }
}
return;
}
if (_thisThreadIsInitializingClass(cls)) {
return;
} else {
- monitor_locker_t lock(classInitLock);
- while (!cls->isInitialized()) {
- classInitLock.wait();
- }
+ waitForInitializeToComplete(cls);
return;
}
}
__BEGIN_DECLS
+// Termination reasons in the OS_REASON_OBJC namespace.
+#define OBJC_EXIT_REASON_UNSPECIFIED 1
+#define OBJC_EXIT_REASON_GC_NOT_SUPPORTED 2
+
// This is the allocation size required for each of the class and the metaclass
// with objc_initializeClassPair() and objc_readClassPair().
// The runtime's class structure will never grow beyond this.
// 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)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0);
+ OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0);
// Class and metaclass construction from a compiler-generated memory image.
// cls and cls->isa must each be OBJC_MAX_CLASS_SIZE bytes.
struct objc_image_info;
OBJC_EXPORT Class objc_readClassPair(Class cls,
const struct objc_image_info *info)
- __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0);
+ OBJC_AVAILABLE(10.10, 8.0, 9.0, 1.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)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3)
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
// Get the isa pointer written into objects just before being freed.
OBJC_EXPORT Class _objc_getFreedObjectClass(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
-
-// Return YES if GC is on and `object` is a GC allocation.
-OBJC_EXPORT BOOL objc_isAuto(id object)
- __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
// env NSObjCMessageLoggingEnabled
OBJC_EXPORT void instrumentObjcMessageSends(BOOL flag)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
// Initializer called by libSystem
-#if __OBJC2__
OBJC_EXPORT void _objc_init(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0);
+#if __OBJC2__
+ OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
+#else
+ OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
#endif
-#ifndef OBJC_NO_GC
+// 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;
+
// GC startup callback from Foundation
OBJC_EXPORT malloc_zone_t *objc_collect_init(int (*callback)(void))
- __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_NA);
-#endif
+ __OSX_DEPRECATED(10.4, 10.8, "it does nothing")
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
// Plainly-implemented GC barriers. Rosetta used to use these.
OBJC_EXPORT id objc_assign_strongCast_generic(id value, id *dest)
OBJC_EXPORT id objc_assign_ivar_generic(id value, id 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)
+ __OSX_AVAILABLE(10.11)
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
+
// Install missing-class callback. Used by the late unlamented ZeroLink.
OBJC_EXPORT void _objc_setClassLoader(BOOL (*newClassLoader)(const char *)) OBJC2_UNAVAILABLE;
// Install handler for allocation failures.
// Handler may abort, or throw, or provide an object to return.
OBJC_EXPORT void _objc_setBadAllocHandler(id (*newHandler)(Class isa))
- __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0);
+ 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_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_NA,__IPHONE_NA);
+ __OSX_DEPRECATED(10.0, 10.5, "multithreading is always available")
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
#endif
// Used by ExceptionHandling.framework
#if !__OBJC2__
OBJC_EXPORT void _objc_error(id rcv, const char *fmt, va_list args)
__attribute__((noreturn))
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_NA,__IPHONE_NA);
+ __OSX_DEPRECATED(10.0, 10.5, "use other logging facilities instead")
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
#endif
#if OBJC_HAVE_TAGGED_POINTERS
-// Tagged pointer layout and usage is subject to change
-// on different OS versions. The current layout is:
-// (MSB)
-// 60 bits payload
-// 3 bits tag index
-// 1 bit 1 for tagged pointer objects, 0 for ordinary objects
-// (LSB)
+// Tagged pointer layout and usage is subject to change on different OS versions.
+
+// Tag indexes 0..<7 have a 60-bit payload.
+// Tag index 7 is reserved.
+// Tag indexes 8..<264 have a 52-bit payload.
+// Tag index 264 is reserved.
#if __has_feature(objc_fixed_enum) || __cplusplus >= 201103L
-enum objc_tag_index_t : uint8_t
+enum objc_tag_index_t : uint16_t
#else
-typedef uint8_t objc_tag_index_t;
+typedef uint16_t objc_tag_index_t;
enum
#endif
{
OBJC_TAG_NSIndexPath = 4,
OBJC_TAG_NSManagedObjectID = 5,
OBJC_TAG_NSDate = 6,
- OBJC_TAG_7 = 7
+ OBJC_TAG_RESERVED_7 = 7,
+
+ OBJC_TAG_First60BitPayload = 0,
+ OBJC_TAG_Last60BitPayload = 6,
+ OBJC_TAG_First52BitPayload = 8,
+ OBJC_TAG_Last52BitPayload = 263,
+
+ OBJC_TAG_RESERVED_264 = 264
};
#if __has_feature(objc_fixed_enum) && !defined(__cplusplus)
typedef enum objc_tag_index_t objc_tag_index_t;
#endif
-OBJC_EXPORT void _objc_registerTaggedPointerClass(objc_tag_index_t tag, Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
-
-OBJC_EXPORT Class _objc_getClassForTag(objc_tag_index_t tag)
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
+// Returns true if tagged pointers are enabled.
+// The other functions below must not be called if tagged pointers are disabled.
static inline bool
-_objc_taggedPointersEnabled(void)
-{
- extern uintptr_t objc_debug_taggedpointer_mask;
- return (objc_debug_taggedpointer_mask != 0);
-}
+_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);
-#if TARGET_OS_IPHONE
-// tagged pointer marker is MSB
+// 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);
+// 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 *
-_objc_makeTaggedPointer(objc_tag_index_t tag, uintptr_t value)
-{
- // assert(_objc_taggedPointersEnabled());
- // assert((unsigned int)tag < 8);
- // assert(((value << 4) >> 4) == value);
- return (void*)((1UL << 63) | ((uintptr_t)tag << 60) | (value & ~(0xFUL << 60)));
-}
+_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)
-{
- return (intptr_t)ptr < 0; // a.k.a. ptr & 0x8000000000000000
-}
+_objc_isTaggedPointer(const void *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)
-{
- // assert(_objc_isTaggedPointer(ptr));
- return (objc_tag_index_t)(((uintptr_t)ptr >> 60) & 0x7);
-}
+_objc_getTaggedPointerTag(const void *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)
-{
- // assert(_objc_isTaggedPointer(ptr));
- return (uintptr_t)ptr & 0x0fffffffffffffff;
-}
+_objc_getTaggedPointerValue(const void *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)
-{
- // assert(_objc_isTaggedPointer(ptr));
- return ((intptr_t)ptr << 4) >> 4;
-}
+_objc_getTaggedPointerSignedValue(const void *ptr);
+
+// Don't use the values below. Use the declarations above.
-// TARGET_OS_IPHONE
+#if TARGET_OS_OSX && __x86_64__
+ // 64-bit Mac - tag bit is LSB
+# define OBJC_MSB_TAGGED_POINTERS 0
#else
-// not TARGET_OS_IPHONE
-// tagged pointer marker is LSB
+ // Everything else - tag bit is MSB
+# define OBJC_MSB_TAGGED_POINTERS 1
+#endif
+
+#define _OBJC_TAG_INDEX_MASK 0x7
+// array slot includes the tag bit itself
+#define _OBJC_TAG_SLOT_COUNT 16
+#define _OBJC_TAG_SLOT_MASK 0xf
+
+#define _OBJC_TAG_EXT_INDEX_MASK 0xff
+// array slot has no extra bits
+#define _OBJC_TAG_EXT_SLOT_COUNT 256
+#define _OBJC_TAG_EXT_SLOT_MASK 0xff
+
+#if OBJC_MSB_TAGGED_POINTERS
+# define _OBJC_TAG_MASK (1ULL<<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_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_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_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
+
+static inline bool
+_objc_taggedPointersEnabled(void)
+{
+ extern uintptr_t objc_debug_taggedpointer_mask;
+ return (objc_debug_taggedpointer_mask != 0);
+}
static inline void *
_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((unsigned int)tag < 8);
- // assert(((value << 4) >> 4) == value);
- return (void *)((value << 4) | ((uintptr_t)tag << 1) | 1);
+ if (tag <= OBJC_TAG_Last60BitPayload) {
+ // assert(((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT) == value);
+ return (void*)
+ (_OBJC_TAG_MASK |
+ ((uintptr_t)tag << _OBJC_TAG_INDEX_SHIFT) |
+ ((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT));
+ } 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*)
+ (_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));
+ }
}
static inline bool
_objc_isTaggedPointer(const void *ptr)
{
- return (uintptr_t)ptr & 1;
+ return ((intptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
static inline objc_tag_index_t
_objc_getTaggedPointerTag(const void *ptr)
{
// assert(_objc_isTaggedPointer(ptr));
- return (objc_tag_index_t)(((uintptr_t)ptr & 0xe) >> 1);
+ 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;
+ if (basicTag == _OBJC_TAG_INDEX_MASK) {
+ return (objc_tag_index_t)(extTag + OBJC_TAG_First52BitPayload);
+ } else {
+ return (objc_tag_index_t)basicTag;
+ }
}
static inline uintptr_t
_objc_getTaggedPointerValue(const void *ptr)
{
// assert(_objc_isTaggedPointer(ptr));
- return (uintptr_t)ptr >> 4;
+ uintptr_t basicTag = ((uintptr_t)ptr >> _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;
+ } else {
+ return ((uintptr_t)ptr << _OBJC_TAG_PAYLOAD_LSHIFT) >> _OBJC_TAG_PAYLOAD_RSHIFT;
+ }
}
static inline intptr_t
_objc_getTaggedPointerSignedValue(const void *ptr)
{
// assert(_objc_isTaggedPointer(ptr));
- return (intptr_t)ptr >> 4;
+ uintptr_t basicTag = ((uintptr_t)ptr >> _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;
+ } else {
+ return ((intptr_t)ptr << _OBJC_TAG_PAYLOAD_LSHIFT) >> _OBJC_TAG_PAYLOAD_RSHIFT;
+ }
}
-// not TARGET_OS_IPHONE
-#endif
-
-
-OBJC_EXPORT void _objc_insert_tagged_isa(unsigned char slotNumber, Class isa)
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_7,__MAC_10_9, __IPHONE_4_3,__IPHONE_7_0);
-
+// OBJC_HAVE_TAGGED_POINTERS
#endif
-// External Reference support. Used to support compaction.
-
-enum {
- OBJC_XREF_STRONG = 1,
- OBJC_XREF_WEAK = 2
-};
-typedef uintptr_t objc_xref_type_t;
-typedef uintptr_t objc_xref_t;
-
-OBJC_EXPORT objc_xref_t _object_addExternalReference(id object, objc_xref_type_t type)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
-OBJC_EXPORT void _object_removeExternalReference(objc_xref_t xref)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
-OBJC_EXPORT id _object_readExternalReference(objc_xref_t xref)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
-
-OBJC_EXPORT uintptr_t _object_getExternalHash(id object)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
-
/**
* Returns the method implementation of an object.
*
* class_getMethodImplementation(object_getClass(obj), name);
*/
OBJC_EXPORT IMP object_getMethodImplementation(id obj, SEL name)
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
OBJC_EXPORT IMP object_getMethodImplementation_stret(id obj, SEL name)
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0)
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0)
OBJC_ARM64_UNAVAILABLE;
// Instance-specific instance variable layout.
OBJC_EXPORT void _class_setIvarLayoutAccessor(Class cls_gen, const uint8_t* (*accessor) (id object))
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);
+ __OSX_AVAILABLE(10.7)
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
OBJC_EXPORT const uint8_t *_object_getIvarLayout(Class cls_gen, id object)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);
+ __OSX_AVAILABLE(10.7)
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE;
-OBJC_EXPORT BOOL _class_usesAutomaticRetainRelease(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+/*
+ "Unknown" includes non-object ivars and non-ARC non-__weak ivars
+ "Strong" includes ARC __strong ivars
+ "Weak" includes ARC and new MRC __weak ivars
+ "Unretained" includes ARC __unsafe_unretained and old GC+MRC __weak ivars
+*/
+typedef enum {
+ objc_ivar_memoryUnknown, // unknown / unknown
+ objc_ivar_memoryStrong, // direct access / objc_storeStrong
+ objc_ivar_memoryWeak, // objc_loadWeak[Retained] / objc_storeWeak
+ 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 BOOL _class_isFutureClass(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
-
-
-// Obsolete ARC conversions.
-
-// hack - remove and reinstate objc.h's definitions
-#undef objc_retainedObject
-#undef objc_unretainedObject
-#undef objc_unretainedPointer
-OBJC_EXPORT id objc_retainedObject(objc_objectptr_t pointer)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
-OBJC_EXPORT id objc_unretainedObject(objc_objectptr_t pointer)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
-OBJC_EXPORT objc_objectptr_t objc_unretainedPointer(id object)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
-#if __has_feature(objc_arc)
-# define objc_retainedObject(o) ((__bridge_transfer id)(objc_objectptr_t)(o))
-# define objc_unretainedObject(o) ((__bridge id)(objc_objectptr_t)(o))
-# define objc_unretainedPointer(o) ((__bridge objc_objectptr_t)(id)(o))
-#else
-# define objc_retainedObject(o) ((id)(objc_objectptr_t)(o))
-# define objc_unretainedObject(o) ((id)(objc_objectptr_t)(o))
-# define objc_unretainedPointer(o) ((objc_objectptr_t)(id)(o))
-#endif
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
+
// API to only be called by root classes like NSObject or NSProxy
OBJC_EXPORT
id
_objc_rootRetain(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
void
_objc_rootRelease(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
bool
_objc_rootReleaseWasZero(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
bool
_objc_rootTryRetain(id obj)
-__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
bool
_objc_rootIsDeallocating(id obj)
-__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
id
_objc_rootAutorelease(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
uintptr_t
_objc_rootRetainCount(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
id
_objc_rootInit(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
id
_objc_rootAlloc(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
void
_objc_rootDealloc(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
void
_objc_rootFinalize(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
malloc_zone_t *
_objc_rootZone(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
uintptr_t
_objc_rootHash(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
void *
objc_autoreleasePoolPush(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
void
objc_autoreleasePoolPop(void *context)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT id objc_alloc(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
OBJC_EXPORT id objc_allocWithZone(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);
+ OBJC_AVAILABLE(10.9, 7.0, 9.0, 1.0);
OBJC_EXPORT id objc_retain(id obj)
__asm__("_objc_retain")
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT void objc_release(id obj)
__asm__("_objc_release")
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT id objc_autorelease(id obj)
__asm__("_objc_autorelease")
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
// Prepare a value at +1 for return through a +0 autoreleasing convention.
OBJC_EXPORT
id
objc_autoreleaseReturnValue(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
// Prepare a value at +0 for return through a +0 autoreleasing convention.
OBJC_EXPORT
id
objc_retainAutoreleaseReturnValue(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
// Accept a value returned through a +0 autoreleasing convention for use at +1.
OBJC_EXPORT
id
objc_retainAutoreleasedReturnValue(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
// Accept a value returned through a +0 autoreleasing convention for use at +0.
OBJC_EXPORT
id
objc_unsafeClaimAutoreleasedReturnValue(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_11, __IPHONE_9_0);
+ OBJC_AVAILABLE(10.11, 9.0, 9.0, 1.0);
OBJC_EXPORT
void
objc_storeStrong(id *location, id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
id
objc_retainAutorelease(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
// obsolete.
OBJC_EXPORT id objc_retain_autorelease(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
id
objc_loadWeakRetained(id *location)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
id
objc_initWeak(id *location, id val)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
// Like objc_storeWeak, but stores nil if the new object is deallocating
// or the new object's class does not support weak references.
OBJC_EXPORT
id
objc_storeWeakOrNil(id *location, id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_11, __IPHONE_9_0);
+ OBJC_AVAILABLE(10.11, 9.0, 9.0, 1.0);
// Like objc_initWeak, but stores nil if the new object is deallocating
// or the new object's class does not support weak references.
OBJC_EXPORT
id
objc_initWeakOrNil(id *location, id val)
- __OSX_AVAILABLE_STARTING(__MAC_10_11, __IPHONE_9_0);
+ OBJC_AVAILABLE(10.11, 9.0, 9.0, 1.0);
OBJC_EXPORT
void
objc_destroyWeak(id *location)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
void
objc_copyWeak(id *to, id *from)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
void
objc_moveWeak(id *to, id *from)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
void
_objc_autoreleasePoolPrint(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT BOOL objc_should_deallocate(id object)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT void objc_clear_deallocating(id object)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
// to make CF link for now
OBJC_EXPORT
void *
_objc_autoreleasePoolPush(void)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
OBJC_EXPORT
void
_objc_autoreleasePoolPop(void *context)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
// Extra @encode data for XPC, or NULL
OBJC_EXPORT const char *_protocol_getMethodTypeEncoding(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod)
- __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_6_0);
+ OBJC_AVAILABLE(10.8, 6.0, 9.0, 1.0);
// API to only be called by classes that provide their own reference count storage
OBJC_EXPORT
void
_objc_deallocOnMainThreadHelper(void *context)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
// On async versus sync deallocation and the _dealloc2main flag
//
* Mutex checking
**********************************************************************/
+#if !TARGET_OS_SIMULATOR
+// Non-simulator platforms have lock debugging built into os_unfair_lock.
+
+
+void
+lockdebug_mutex_lock(mutex_t *lock)
+{
+ // empty
+}
+
+void
+lockdebug_mutex_unlock(mutex_t *lock)
+{
+ // empty
+}
+
+void
+lockdebug_mutex_assert_locked(mutex_t *lock)
+{
+ os_unfair_lock_assert_owner((os_unfair_lock *)lock);
+}
+
+void
+lockdebug_mutex_assert_unlocked(mutex_t *lock)
+{
+ os_unfair_lock_assert_not_owner((os_unfair_lock *)lock);
+}
+
+
+// !TARGET_OS_SIMULATOR
+#else
+// TARGET_OS_SIMULATOR
+
+// Simulator platforms have no built-in lock debugging in os_unfair_lock.
+
+
void
lockdebug_mutex_lock(mutex_t *lock)
{
}
+// TARGET_OS_SIMULATOR
+#endif
+
/***********************************************************************
* Recursive mutex checking
**********************************************************************/
#if SUPPORT_TAGGED_POINTERS
-#define TAG_COUNT 8
-#define TAG_SLOT_MASK 0xf
-
-#if SUPPORT_MSB_TAGGED_POINTERS
-# define TAG_MASK (1ULL<<63)
-# define TAG_SLOT_SHIFT 60
-# define TAG_PAYLOAD_LSHIFT 4
-# define TAG_PAYLOAD_RSHIFT 4
-#else
-# define TAG_MASK 1
-# define TAG_SLOT_SHIFT 0
-# define TAG_PAYLOAD_LSHIFT 0
-# define TAG_PAYLOAD_RSHIFT 4
+extern "C" {
+ extern Class objc_debug_taggedpointer_classes[_OBJC_TAG_SLOT_COUNT*2];
+ extern Class objc_debug_taggedpointer_ext_classes[_OBJC_TAG_EXT_SLOT_COUNT];
+}
+#define objc_tag_classes objc_debug_taggedpointer_classes
+#define objc_tag_ext_classes objc_debug_taggedpointer_ext_classes
+
#endif
-extern "C" { extern Class objc_debug_taggedpointer_classes[TAG_COUNT*2]; }
-#define objc_tag_classes objc_debug_taggedpointer_classes
+#if SUPPORT_INDEXED_ISA
+
+ALWAYS_INLINE Class &
+classForIndex(uintptr_t index) {
+ assert(index > 0);
+ assert(index < (uintptr_t)objc_indexed_classes_count);
+ return objc_indexed_classes[index];
+}
#endif
return ISA()->isMetaClass();
}
-#if SUPPORT_NONPOINTER_ISA
-
-# if !SUPPORT_TAGGED_POINTERS
-# error sorry
-# endif
+#if SUPPORT_TAGGED_POINTERS
inline Class
-objc_object::ISA()
+objc_object::getIsa()
{
- assert(!isTaggedPointer());
- return (Class)(isa.bits & ISA_MASK);
+ if (!isTaggedPointer()) return ISA();
+
+ uintptr_t ptr = (uintptr_t)this;
+ if (isExtTaggedPointer()) {
+ uintptr_t slot =
+ (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
+ return objc_tag_ext_classes[slot];
+ } else {
+ uintptr_t slot =
+ (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
+ return objc_tag_classes[slot];
+ }
}
inline bool
-objc_object::hasIndexedIsa()
+objc_object::isTaggedPointer()
{
- return isa.indexed;
+ return _objc_isTaggedPointer(this);
}
+inline bool
+objc_object::isBasicTaggedPointer()
+{
+ return isTaggedPointer() && !isExtTaggedPointer();
+}
+
+inline bool
+objc_object::isExtTaggedPointer()
+{
+ return ((uintptr_t)this & _OBJC_TAG_EXT_MASK) == _OBJC_TAG_EXT_MASK;
+}
+
+
+// SUPPORT_TAGGED_POINTERS
+#else
+// not SUPPORT_TAGGED_POINTERS
+
+
inline Class
objc_object::getIsa()
{
- if (isTaggedPointer()) {
- uintptr_t slot = ((uintptr_t)this >> TAG_SLOT_SHIFT) & TAG_SLOT_MASK;
- return objc_tag_classes[slot];
- }
return ISA();
}
+inline bool
+objc_object::isTaggedPointer()
+{
+ return false;
+}
+
+inline bool
+objc_object::isBasicTaggedPointer()
+{
+ return false;
+}
+
+inline bool
+objc_object::isExtTaggedPointer()
+{
+ return false;
+}
+
+
+// not SUPPORT_TAGGED_POINTERS
+#endif
+
+
+#if SUPPORT_NONPOINTER_ISA
+
+inline Class
+objc_object::ISA()
+{
+ assert(!isTaggedPointer());
+#if SUPPORT_INDEXED_ISA
+ if (isa.nonpointer) {
+ uintptr_t slot = isa.indexcls;
+ return classForIndex((unsigned)slot);
+ }
+ return (Class)isa.bits;
+#else
+ return (Class)(isa.bits & ISA_MASK);
+#endif
+}
+
+
+inline bool
+objc_object::hasNonpointerIsa()
+{
+ return isa.nonpointer;
+}
+
+
inline void
objc_object::initIsa(Class cls)
{
inline void
objc_object::initClassIsa(Class cls)
{
- if (DisableIndexedIsa) {
- initIsa(cls, false, false);
+ if (DisableNonpointerIsa || cls->instancesRequireRawIsa()) {
+ initIsa(cls, false/*not nonpointer*/, false);
} else {
- initIsa(cls, true, false);
+ initIsa(cls, true/*nonpointer*/, false);
}
}
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
- assert(!UseGC);
- assert(!cls->requiresRawIsa());
+ assert(!cls->instancesRequireRawIsa());
assert(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
inline void
-objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor)
+objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
assert(!isTaggedPointer());
- if (!indexed) {
+ if (!nonpointer) {
isa.cls = cls;
} else {
- assert(!DisableIndexedIsa);
- isa.bits = ISA_MAGIC_VALUE;
+ assert(!DisableNonpointerIsa);
+ assert(!cls->instancesRequireRawIsa());
+
+ isa_t newisa(0);
+
+#if SUPPORT_INDEXED_ISA
+ assert(cls->classArrayIndex() > 0);
+ newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
- // isa.indexed is part of ISA_MAGIC_VALUE
- isa.has_cxx_dtor = hasCxxDtor;
- isa.shiftcls = (uintptr_t)cls >> 3;
+ // isa.nonpointer is part of ISA_MAGIC_VALUE
+ newisa.has_cxx_dtor = hasCxxDtor;
+ newisa.indexcls = (uintptr_t)cls->classArrayIndex();
+#else
+ newisa.bits = ISA_MAGIC_VALUE;
+ // isa.magic is part of ISA_MAGIC_VALUE
+ // isa.nonpointer is part of ISA_MAGIC_VALUE
+ newisa.has_cxx_dtor = hasCxxDtor;
+ newisa.shiftcls = (uintptr_t)cls >> 3;
+#endif
+
+ // This write must be performed in a single store in some cases
+ // (for example when realizing a class because other threads
+ // may simultaneously try to use the class).
+ // fixme use atomics here to guarantee single-store and to
+ // guarantee memory order w.r.t. the class index table
+ // ...but not too atomic because we don't want to hurt instantiation
+ isa = newisa;
}
}
inline Class
objc_object::changeIsa(Class newCls)
{
- // This is almost always rue but there are
+ // This is almost always true but there are
// enough edge cases that we can't assert it.
// assert(newCls->isFuture() ||
// newCls->isInitializing() || newCls->isInitialized());
do {
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits);
- if ((oldisa.bits == 0 || oldisa.indexed) &&
- !newCls->isFuture() && newCls->canAllocIndexed())
+ if ((oldisa.bits == 0 || oldisa.nonpointer) &&
+ !newCls->isFuture() && newCls->canAllocNonpointer())
{
- // 0 -> indexed
- // indexed -> indexed
+ // 0 -> nonpointer
+ // nonpointer -> nonpointer
+#if SUPPORT_INDEXED_ISA
+ if (oldisa.bits == 0) newisa.bits = ISA_INDEX_MAGIC_VALUE;
+ else newisa = oldisa;
+ // isa.magic is part of ISA_MAGIC_VALUE
+ // isa.nonpointer is part of ISA_MAGIC_VALUE
+ newisa.has_cxx_dtor = newCls->hasCxxDtor();
+ assert(newCls->classArrayIndex() > 0);
+ newisa.indexcls = (uintptr_t)newCls->classArrayIndex();
+#else
if (oldisa.bits == 0) newisa.bits = ISA_MAGIC_VALUE;
else newisa = oldisa;
// isa.magic is part of ISA_MAGIC_VALUE
- // isa.indexed is part of ISA_MAGIC_VALUE
+ // isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = newCls->hasCxxDtor();
newisa.shiftcls = (uintptr_t)newCls >> 3;
+#endif
}
- else if (oldisa.indexed) {
- // indexed -> not indexed
+ else if (oldisa.nonpointer) {
+ // nonpointer -> raw pointer
// Need to copy retain count et al to side table.
// Acquire side table lock before setting isa to
// prevent races such as concurrent -release.
newisa.cls = newCls;
}
else {
- // not indexed -> not indexed
+ // raw pointer -> raw pointer
newisa.cls = newCls;
}
} while (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits));
if (transcribeToSideTable) {
// Copy oldisa's retain count et al to side table.
- // oldisa.weakly_referenced: nothing to do
// oldisa.has_assoc: nothing to do
// oldisa.has_cxx_dtor: nothing to do
sidetable_moveExtraRC_nolock(oldisa.extra_rc,
if (sideTableLocked) sidetable_unlock();
- Class oldCls;
- if (oldisa.indexed) oldCls = (Class)((uintptr_t)oldisa.shiftcls << 3);
- else oldCls = oldisa.cls;
-
- return oldCls;
-}
-
-
-inline bool
-objc_object::isTaggedPointer()
-{
- return ((uintptr_t)this & TAG_MASK);
+ if (oldisa.nonpointer) {
+#if SUPPORT_INDEXED_ISA
+ return classForIndex(oldisa.indexcls);
+#else
+ return (Class)((uintptr_t)oldisa.shiftcls << 3);
+#endif
+ }
+ else {
+ return oldisa.cls;
+ }
}
objc_object::hasAssociatedObjects()
{
if (isTaggedPointer()) return true;
- if (isa.indexed) return isa.has_assoc;
+ if (isa.nonpointer) return isa.has_assoc;
return true;
}
retry:
isa_t oldisa = LoadExclusive(&isa.bits);
isa_t newisa = oldisa;
- if (!newisa.indexed) return;
- if (newisa.has_assoc) return;
+ if (!newisa.nonpointer || newisa.has_assoc) {
+ ClearExclusive(&isa.bits);
+ return;
+ }
newisa.has_assoc = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
}
objc_object::isWeaklyReferenced()
{
assert(!isTaggedPointer());
- if (isa.indexed) return isa.weakly_referenced;
+ if (isa.nonpointer) return isa.weakly_referenced;
else return sidetable_isWeaklyReferenced();
}
retry:
isa_t oldisa = LoadExclusive(&isa.bits);
isa_t newisa = oldisa;
- if (!newisa.indexed) return sidetable_setWeaklyReferenced_nolock();
- if (newisa.weakly_referenced) return;
+ if (slowpath(!newisa.nonpointer)) {
+ ClearExclusive(&isa.bits);
+ sidetable_setWeaklyReferenced_nolock();
+ return;
+ }
+ if (newisa.weakly_referenced) {
+ ClearExclusive(&isa.bits);
+ return;
+ }
newisa.weakly_referenced = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
}
objc_object::hasCxxDtor()
{
assert(!isTaggedPointer());
- if (isa.indexed) return isa.has_cxx_dtor;
+ if (isa.nonpointer) return isa.has_cxx_dtor;
else return isa.cls->hasCxxDtor();
}
inline bool
objc_object::rootIsDeallocating()
{
- assert(!UseGC);
-
if (isTaggedPointer()) return false;
- if (isa.indexed) return isa.deallocating;
+ if (isa.nonpointer) return isa.deallocating;
return sidetable_isDeallocating();
}
inline void
objc_object::clearDeallocating()
{
- if (!isa.indexed) {
+ if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
- else if (isa.weakly_referenced || isa.has_sidetable_rc) {
+ else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
inline void
objc_object::rootDealloc()
{
- assert(!UseGC);
- if (isTaggedPointer()) return;
+ if (isTaggedPointer()) return; // fixme necessary?
- if (isa.indexed &&
- !isa.weakly_referenced &&
- !isa.has_assoc &&
- !isa.has_cxx_dtor &&
- !isa.has_sidetable_rc)
+ if (fastpath(isa.nonpointer &&
+ !isa.weakly_referenced &&
+ !isa.has_assoc &&
+ !isa.has_cxx_dtor &&
+ !isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
inline id
objc_object::retain()
{
- // UseGC is allowed here, but requires hasCustomRR.
- assert(!UseGC || ISA()->hasCustomRR());
assert(!isTaggedPointer());
- if (! ISA()->hasCustomRR()) {
+ if (fastpath(!ISA()->hasCustomRR())) {
return rootRetain();
}
ALWAYS_INLINE id
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
- assert(!UseGC);
if (isTaggedPointer()) return (id)this;
bool sideTableLocked = false;
transcribeToSideTable = false;
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
- if (!newisa.indexed) goto unindexed;
+ if (slowpath(!newisa.nonpointer)) {
+ ClearExclusive(&isa.bits);
+ if (!tryRetain && sideTableLocked) sidetable_unlock();
+ if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
+ else return sidetable_retain();
+ }
// don't check newisa.fast_rr; we already called any RR overrides
- if (tryRetain && newisa.deallocating) goto tryfail;
+ if (slowpath(tryRetain && newisa.deallocating)) {
+ ClearExclusive(&isa.bits);
+ if (!tryRetain && sideTableLocked) sidetable_unlock();
+ return nil;
+ }
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
- if (carry) {
+ if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
- if (!handleOverflow) return rootRetain_overflow(tryRetain);
+ if (!handleOverflow) {
+ ClearExclusive(&isa.bits);
+ return rootRetain_overflow(tryRetain);
+ }
// Leave half of the retain counts inline and
// prepare to copy the other half to the side table.
if (!tryRetain && !sideTableLocked) sidetable_lock();
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true;
}
- } while (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits));
+ } while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
- if (transcribeToSideTable) {
+ if (slowpath(transcribeToSideTable)) {
// Copy the other half of the retain counts to the side table.
sidetable_addExtraRC_nolock(RC_HALF);
}
- if (!tryRetain && sideTableLocked) sidetable_unlock();
+ if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
return (id)this;
-
- tryfail:
- if (!tryRetain && sideTableLocked) sidetable_unlock();
- return nil;
-
- unindexed:
- if (!tryRetain && sideTableLocked) sidetable_unlock();
- if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
- else return sidetable_retain();
}
inline void
objc_object::release()
{
- // UseGC is allowed here, but requires hasCustomRR.
- assert(!UseGC || ISA()->hasCustomRR());
assert(!isTaggedPointer());
- if (! ISA()->hasCustomRR()) {
+ if (fastpath(!ISA()->hasCustomRR())) {
rootRelease();
return;
}
ALWAYS_INLINE bool
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
- assert(!UseGC);
if (isTaggedPointer()) return false;
bool sideTableLocked = false;
do {
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
- if (!newisa.indexed) goto unindexed;
+ if (slowpath(!newisa.nonpointer)) {
+ ClearExclusive(&isa.bits);
+ if (sideTableLocked) sidetable_unlock();
+ return sidetable_release(performDealloc);
+ }
// don't check newisa.fast_rr; we already called any RR overrides
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
- if (carry) goto underflow;
- } while (!StoreReleaseExclusive(&isa.bits, oldisa.bits, newisa.bits));
+ if (slowpath(carry)) {
+ // don't ClearExclusive()
+ goto underflow;
+ }
+ } while (slowpath(!StoreReleaseExclusive(&isa.bits,
+ oldisa.bits, newisa.bits)));
- if (sideTableLocked) sidetable_unlock();
+ if (slowpath(sideTableLocked)) sidetable_unlock();
return false;
underflow:
// abandon newisa to undo the decrement
newisa = oldisa;
- if (newisa.has_sidetable_rc) {
+ if (slowpath(newisa.has_sidetable_rc)) {
if (!handleUnderflow) {
+ ClearExclusive(&isa.bits);
return rootRelease_underflow(performDealloc);
}
// Transfer retain count from side table to inline storage.
if (!sideTableLocked) {
+ ClearExclusive(&isa.bits);
sidetable_lock();
sideTableLocked = true;
- if (!isa.indexed) {
- // Lost a race vs the indexed -> not indexed transition
- // before we got the side table lock. Stop now to avoid
- // breaking the safety checks in the sidetable ExtraRC code.
- goto unindexed;
- }
+ // Need to start over to avoid a race against
+ // the nonpointer -> raw pointer transition.
+ goto retry;
}
// Try to remove some retain counts from the side table.
// Side table retain count decreased.
// Try to add them to the inline count.
newisa.extra_rc = borrowed - 1; // redo the original decrement too
- bool stored = StoreExclusive(&isa.bits, oldisa.bits, newisa.bits);
+ bool stored = StoreReleaseExclusive(&isa.bits,
+ oldisa.bits, newisa.bits);
if (!stored) {
// Inline update failed.
// Try it again right now. This prevents livelock on LL/SC
// dropped the reservation.
isa_t oldisa2 = LoadExclusive(&isa.bits);
isa_t newisa2 = oldisa2;
- if (newisa2.indexed) {
+ if (newisa2.nonpointer) {
uintptr_t overflow;
newisa2.bits =
addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
// Really deallocate.
- if (sideTableLocked) sidetable_unlock();
-
- if (newisa.deallocating) {
+ if (slowpath(newisa.deallocating)) {
+ ClearExclusive(&isa.bits);
+ if (sideTableLocked) sidetable_unlock();
return overrelease_error();
+ // does not actually return
}
newisa.deallocating = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
+
+ if (slowpath(sideTableLocked)) sidetable_unlock();
+
__sync_synchronize();
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return true;
-
- unindexed:
- if (sideTableLocked) sidetable_unlock();
- return sidetable_release(performDealloc);
}
inline id
objc_object::autorelease()
{
- // UseGC is allowed here, but requires hasCustomRR.
- assert(!UseGC || ISA()->hasCustomRR());
-
if (isTaggedPointer()) return (id)this;
- if (! ISA()->hasCustomRR()) return rootAutorelease();
+ if (fastpath(!ISA()->hasCustomRR())) return rootAutorelease();
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, SEL_autorelease);
}
inline id
objc_object::rootAutorelease()
{
- assert(!UseGC);
-
if (isTaggedPointer()) return (id)this;
if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
inline uintptr_t
objc_object::rootRetainCount()
{
- assert(!UseGC);
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
- if (bits.indexed) {
+ ClearExclusive(&isa.bits);
+ if (bits.nonpointer) {
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
inline bool
-objc_object::hasIndexedIsa()
+objc_object::hasNonpointerIsa()
{
return false;
}
-inline Class
-objc_object::getIsa()
-{
-#if SUPPORT_TAGGED_POINTERS
- if (isTaggedPointer()) {
- uintptr_t slot = ((uintptr_t)this >> TAG_SLOT_SHIFT) & TAG_SLOT_MASK;
- return objc_tag_classes[slot];
- }
-#endif
- return ISA();
-}
-
-
inline void
objc_object::initIsa(Class cls)
{
}
-inline bool
-objc_object::isTaggedPointer()
-{
-#if SUPPORT_TAGGED_POINTERS
- return ((uintptr_t)this & TAG_MASK);
-#else
- return false;
-#endif
-}
-
-
inline bool
objc_object::hasAssociatedObjects()
{
- assert(!UseGC);
-
return getIsa()->instancesHaveAssociatedObjects();
}
inline void
objc_object::setHasAssociatedObjects()
{
- assert(!UseGC);
-
getIsa()->setInstancesHaveAssociatedObjects();
}
objc_object::isWeaklyReferenced()
{
assert(!isTaggedPointer());
- assert(!UseGC);
return sidetable_isWeaklyReferenced();
}
objc_object::setWeaklyReferenced_nolock()
{
assert(!isTaggedPointer());
- assert(!UseGC);
sidetable_setWeaklyReferenced_nolock();
}
inline bool
objc_object::rootIsDeallocating()
{
- assert(!UseGC);
-
if (isTaggedPointer()) return false;
return sidetable_isDeallocating();
}
inline id
objc_object::retain()
{
- // UseGC is allowed here, but requires hasCustomRR.
- assert(!UseGC || ISA()->hasCustomRR());
assert(!isTaggedPointer());
- if (! ISA()->hasCustomRR()) {
+ if (fastpath(!ISA()->hasCustomRR())) {
return sidetable_retain();
}
inline id
objc_object::rootRetain()
{
- assert(!UseGC);
-
if (isTaggedPointer()) return (id)this;
return sidetable_retain();
}
inline void
objc_object::release()
{
- // UseGC is allowed here, but requires hasCustomRR.
- assert(!UseGC || ISA()->hasCustomRR());
assert(!isTaggedPointer());
- if (! ISA()->hasCustomRR()) {
+ if (fastpath(!ISA()->hasCustomRR())) {
sidetable_release();
return;
}
inline bool
objc_object::rootRelease()
{
- assert(!UseGC);
-
if (isTaggedPointer()) return false;
return sidetable_release(true);
}
inline id
objc_object::autorelease()
{
- // UseGC is allowed here, but requires hasCustomRR.
- assert(!UseGC || ISA()->hasCustomRR());
-
if (isTaggedPointer()) return (id)this;
- if (! ISA()->hasCustomRR()) return rootAutorelease();
+ if (fastpath(!ISA()->hasCustomRR())) return rootAutorelease();
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, SEL_autorelease);
}
inline id
objc_object::rootAutorelease()
{
- assert(!UseGC);
-
if (isTaggedPointer()) return (id)this;
if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
inline bool
objc_object::rootTryRetain()
{
- assert(!UseGC);
-
if (isTaggedPointer()) return true;
return sidetable_tryRetain();
}
inline uintptr_t
objc_object::rootRetainCount()
{
- assert(!UseGC);
-
if (isTaggedPointer()) return (uintptr_t)this;
return sidetable_retainCount();
}
Caller sees the TLS, clears it, and accepts the result at +1 as-is.
The callee's recognition of the optimized caller is architecture-dependent.
- i386 and x86_64: Callee looks for `mov rax, rdi` followed by a call or
+ x86_64: Callee looks for `mov rax, rdi` followed by a call or
jump instruction to objc_retainAutoreleasedReturnValue or
objc_unsafeClaimAutoreleasedReturnValue.
+ i386: Callee looks for a magic nop `movl %ebp, %ebp` (frame pointer register)
armv7: Callee looks for a magic nop `mov r7, r7` (frame pointer register).
arm64: Callee looks for a magic nop `mov x29, x29` (frame pointer register).
callerAcceptsOptimizedReturn(const void * const ra0)
{
const uint8_t *ra1 = (const uint8_t *)ra0;
- const uint16_t *ra2;
- const uint32_t *ra4 = (const uint32_t *)ra1;
+ const unaligned_uint16_t *ra2;
+ const unaligned_uint32_t *ra4 = (const unaligned_uint32_t *)ra1;
const void **sym;
#define PREFER_GOTPCREL 0
if (*ra4 != 0xe8c78948) {
return false;
}
- ra1 += (long)*(const int32_t *)(ra1 + 4) + 8l;
- ra2 = (const uint16_t *)ra1;
+ ra1 += (long)*(const unaligned_int32_t *)(ra1 + 4) + 8l;
+ ra2 = (const unaligned_uint16_t *)ra1;
// ff 25 jmpq *symbol@DYLDMAGIC(%rip)
if (*ra2 != 0x25ff) {
return false;
}
#endif
- ra1 += 6l + (long)*(const int32_t *)(ra1 + 2);
+ ra1 += 6l + (long)*(const unaligned_int32_t *)(ra1 + 2);
sym = (const void **)ra1;
if (*sym != objc_retainAutoreleasedReturnValue &&
*sym != objc_unsafeClaimAutoreleasedReturnValue)
if ((uintptr_t)ra & 1) {
// 3f 46 mov r7, r7
// we mask off the low bit via subtraction
+ // 16-bit instructions are well-aligned
if (*(uint16_t *)((uint8_t *)ra - 1) == 0x463f) {
return true;
}
} else {
// 07 70 a0 e1 mov r7, r7
- if (*(uint32_t *)ra == 0xe1a07007) {
+ // 32-bit instructions may be only 16-bit aligned
+ if (*(unaligned_uint32_t *)ra == 0xe1a07007) {
return true;
}
}
callerAcceptsOptimizedReturn(const void *ra)
{
// fd 03 1d aa mov fp, fp
+ // arm64 instructions are well-aligned
if (*(uint32_t *)ra == 0xaa1d03fd) {
return true;
}
}
// __arm64__
-# elif __i386__ && TARGET_IPHONE_SIMULATOR
+# elif __i386__
-static inline bool
+static ALWAYS_INLINE bool
callerAcceptsOptimizedReturn(const void *ra)
{
+ // 89 ed movl %ebp, %ebp
+ if (*(unaligned_uint16_t *)ra == 0xed89) {
+ return true;
+ }
return false;
}
-// __i386__ && TARGET_IPHONE_SIMULATOR
+// __i386__
# else
#warning unknown architecture
return false;
}
+bool noMissingWeakSuperclasses(void)
+{
+ return false;
+}
+
bool header_info::isPreoptimized() const
{
return false;
return nil;
}
+header_info_rw *getPreoptimizedHeaderRW(const struct header_info *const hdr)
+{
+ return nil;
+}
+
void preopt_init(void)
{
disableSharedCacheOptimizations();
using objc_opt::objc_stringhash_offset_t;
using objc_opt::objc_protocolopt_t;
using objc_opt::objc_clsopt_t;
-using objc_opt::objc_headeropt_t;
+using objc_opt::objc_headeropt_ro_t;
+using objc_opt::objc_headeropt_rw_t;
using objc_opt::objc_opt_t;
__BEGIN_DECLS
extern const objc_opt_t _objc_opt_data; // in __TEXT, __objc_opt_ro
+/***********************************************************************
+* Return YES if we have a valid optimized shared cache.
+**********************************************************************/
bool isPreoptimized(void)
{
return preoptimized;
}
+/***********************************************************************
+* Return YES if the shared cache does not have any classes with
+* missing weak superclasses.
+**********************************************************************/
+bool noMissingWeakSuperclasses(void)
+{
+ if (!preoptimized) return NO; // might have missing weak superclasses
+ return opt->flags & objc_opt::NoMissingWeakSuperclasses;
+}
+
+
/***********************************************************************
* Return YES if this image's dyld shared cache optimizations are valid.
**********************************************************************/
if (!preoptimized) return NO;
// image not from shared cache, or not fixed inside shared cache
- if (!_objcHeaderOptimizedByDyld(this)) return NO;
+ if (!info()->optimizedByDyld()) return NO;
return YES;
}
}
namespace objc_opt {
-struct objc_headeropt_t {
+struct objc_headeropt_ro_t {
uint32_t count;
uint32_t entsize;
header_info headers[0]; // sorted by mhdr address
while (start <= end) {
int32_t i = (start+end)/2;
header_info *hi = headers+i;
- if (mhdr == hi->mhdr) return hi;
- else if (mhdr < hi->mhdr) end = i-1;
+ if (mhdr == hi->mhdr()) return hi;
+ else if (mhdr < hi->mhdr()) end = i-1;
else start = i+1;
}
#if DEBUG
for (uint32_t i = 0; i < count; i++) {
header_info *hi = headers+i;
- if (mhdr == hi->mhdr) {
+ if (mhdr == hi->mhdr()) {
_objc_fatal("failed to find header %p (%d/%d)",
mhdr, i, count);
}
return nil;
}
};
+
+struct objc_headeropt_rw_t {
+ uint32_t count;
+ uint32_t entsize;
+ header_info_rw headers[0]; // sorted by mhdr address
+};
};
header_info *preoptimizedHinfoForHeader(const headerType *mhdr)
{
- objc_headeropt_t *hinfos = opt ? opt->headeropt() : nil;
+#if !__OBJC2__
+ // fixme old ABI shared cache doesn't prepare these properly
+ return nil;
+#endif
+
+ objc_headeropt_ro_t *hinfos = opt ? opt->headeropt_ro() : nil;
if (hinfos) return hinfos->get(mhdr);
else return nil;
}
+header_info_rw *getPreoptimizedHeaderRW(const struct header_info *const hdr)
+{
+#if !__OBJC2__
+ // fixme old ABI shared cache doesn't prepare these properly
+ return nil;
+#endif
+
+ objc_headeropt_ro_t *hinfoRO = opt ? opt->headeropt_ro() : nil;
+ objc_headeropt_rw_t *hinfoRW = opt ? opt->headeropt_rw() : nil;
+ if (!hinfoRO || !hinfoRW) {
+ _objc_fatal("preoptimized header_info missing for %s (%p %p %p)",
+ hdr->fname(), hdr, hinfoRO, hinfoRW);
+ }
+ int32_t index = (int32_t)(hdr - hinfoRO->headers);
+ assert(hinfoRW->entsize == sizeof(header_info_rw));
+ return &hinfoRW->headers[index];
+}
+
+
void preopt_init(void)
{
// `opt` not set at compile time in order to detect too-early usage
_objc_fatal("bad objc preopt version (want %d, got %d)",
objc_opt::VERSION, opt->version);
}
- else if (!opt->selopt() || !opt->headeropt()) {
+ else if (!opt->selopt() || !opt->headeropt_ro()) {
// One of the tables is missing.
failure = "(dyld shared cache is absent or out of date)";
}
-#if SUPPORT_IGNORED_SELECTOR_CONSTANT
- else if (UseGC) {
- // GC is on, which renames some selectors
- // Non-selector optimizations are still valid, but we don't have
- // any of those yet
- failure = "(GC is on)";
- }
-#endif
if (failure) {
// All preoptimized selector references are invalid.
#if TARGET_OS_MAC
+# define OS_UNFAIR_LOCK_INLINE 1
+
# ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS
# endif
# include <sys/time.h>
# include <sys/stat.h>
# include <sys/param.h>
+# include <sys/reason.h>
# include <mach/mach.h>
# include <mach/vm_param.h>
# include <mach/mach_time.h>
#define ALWAYS_INLINE inline __attribute__((always_inline))
#define NEVER_INLINE inline __attribute__((noinline))
+#define fastpath(x) (__builtin_expect(bool(x), 1))
+#define slowpath(x) (__builtin_expect(bool(x), 0))
static ALWAYS_INLINE uintptr_t
return !result;
}
+static ALWAYS_INLINE
+void
+ClearExclusive(uintptr_t *dst)
+{
+ // pretend it writes to *dst for instruction ordering purposes
+ asm("clrex" : "=m" (*dst));
+}
+
#elif __arm__
(void **)dst);
}
+static ALWAYS_INLINE
+void
+ClearExclusive(uintptr_t *dst __unused)
+{
+}
+
#elif __x86_64__ || __i386__
return StoreExclusive(dst, oldvalue, value);
}
+static ALWAYS_INLINE
+void
+ClearExclusive(uintptr_t *dst __unused)
+{
+}
+
+
#else
# error unknown architecture
#endif
-class spinlock_t {
- os_lock_handoff_s mLock;
- public:
- spinlock_t() : mLock(OS_LOCK_HANDOFF_INIT) { }
-
- void lock() { os_lock_lock(&mLock); }
- void unlock() { os_lock_unlock(&mLock); }
- bool trylock() { return os_lock_trylock(&mLock); }
-
-
- // Address-ordered lock discipline for a pair of locks.
-
- static void lockTwo(spinlock_t *lock1, spinlock_t *lock2) {
- if (lock1 > lock2) {
- lock1->lock();
- lock2->lock();
- } else {
- lock2->lock();
- if (lock2 != lock1) lock1->lock();
- }
- }
-
- static void unlockTwo(spinlock_t *lock1, spinlock_t *lock2) {
- lock1->unlock();
- if (lock2 != lock1) lock2->unlock();
- }
-};
-
-
#if !TARGET_OS_IPHONE
# include <CrashReporterClient.h>
#else
__BEGIN_DECLS
extern const char *CRSetCrashLogMessage(const char *msg);
extern const char *CRGetCrashLogMessage(void);
- extern const char *CRSetCrashLogMessage2(const char *msg);
__END_DECLS
#endif
#include <objc/objc.h>
#include <objc/objc-api.h>
-extern void _objc_fatal(const char *fmt, ...) __attribute__((noreturn, format (printf, 1, 2)));
+extern void _objc_fatal(const char *fmt, ...)
+ __attribute__((noreturn, format (printf, 1, 2)));
+extern void _objc_fatal_with_reason(uint64_t reason, uint64_t flags,
+ const char *fmt, ...)
+ __attribute__((noreturn, format (printf, 3, 4)));
#define INIT_ONCE_PTR(var, create, delete) \
do { \
template <bool Debug> class rwlock_tt;
template <bool Debug> class recursive_mutex_tt;
+using spinlock_t = mutex_tt<DEBUG>;
+using mutex_t = mutex_tt<DEBUG>;
+using monitor_t = monitor_tt<DEBUG>;
+using rwlock_t = rwlock_tt<DEBUG>;
+using recursive_mutex_t = recursive_mutex_tt<DEBUG>;
+
#include "objc-lockdebug.h"
template <bool Debug>
class mutex_tt : nocopy_t {
- pthread_mutex_t mLock;
-
- public:
- mutex_tt() : mLock(PTHREAD_MUTEX_INITIALIZER) { }
+ os_unfair_lock mLock;
+ public:
+ mutex_tt() : mLock(OS_UNFAIR_LOCK_INIT) { }
- void lock()
- {
+ void lock() {
lockdebug_mutex_lock(this);
- int err = pthread_mutex_lock(&mLock);
- if (err) _objc_fatal("pthread_mutex_lock failed (%d)", err);
- }
-
- bool tryLock()
- {
- int err = pthread_mutex_trylock(&mLock);
- if (err == 0) {
- lockdebug_mutex_try_lock_success(this);
- return true;
- } else if (err == EBUSY) {
- return false;
- } else {
- _objc_fatal("pthread_mutex_trylock failed (%d)", err);
- }
+ os_unfair_lock_lock_with_options_inline
+ (&mLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
}
- void unlock()
- {
+ void unlock() {
lockdebug_mutex_unlock(this);
- int err = pthread_mutex_unlock(&mLock);
- if (err) _objc_fatal("pthread_mutex_unlock failed (%d)", err);
+ os_unfair_lock_unlock_inline(&mLock);
}
-
void assertLocked() {
lockdebug_mutex_assert_locked(this);
}
void assertUnlocked() {
lockdebug_mutex_assert_unlocked(this);
}
-};
-using mutex_t = mutex_tt<DEBUG>;
+
+ // Address-ordered lock discipline for a pair of locks.
+
+ static void lockTwo(mutex_tt *lock1, mutex_tt *lock2) {
+ if (lock1 > lock2) {
+ lock1->lock();
+ lock2->lock();
+ } else {
+ lock2->lock();
+ if (lock2 != lock1) lock1->lock();
+ }
+ }
+
+ static void unlockTwo(mutex_tt *lock1, mutex_tt *lock2) {
+ lock1->unlock();
+ if (lock2 != lock1) lock2->unlock();
+ }
+};
template <bool Debug>
if (err) _objc_fatal("pthread_mutex_lock failed (%d)", err);
}
- bool tryLock()
- {
- int err = pthread_mutex_trylock(&mLock);
- if (err == 0) {
- lockdebug_recursive_mutex_lock(this);
- return true;
- } else if (err == EBUSY) {
- return false;
- } else {
- _objc_fatal("pthread_mutex_trylock failed (%d)", err);
- }
- }
-
-
void unlock()
{
lockdebug_recursive_mutex_unlock(this);
}
};
-using recursive_mutex_t = recursive_mutex_tt<DEBUG>;
-
template <bool Debug>
class monitor_tt {
}
};
-using monitor_t = monitor_tt<DEBUG>;
-
// semaphore_create formatted for INIT_ONCE use
static inline semaphore_t create_semaphore(void)
}
};
-using rwlock_t = rwlock_tt<DEBUG>;
-
#ifndef __LP64__
typedef struct mach_header headerType;
typedef struct segment_command_64 segmentType;
typedef struct section_64 sectionType;
#endif
-#define headerIsBundle(hi) (hi->mhdr->filetype == MH_BUNDLE)
+#define headerIsBundle(hi) (hi->mhdr()->filetype == MH_BUNDLE)
#define libobjc_header ((headerType *)&_mh_dylib_header)
// Prototypes
return dup;
}
-// unsigned strdup
-static inline uint8_t *
-ustrdup(const uint8_t *str)
+// strdup that doesn't copy read-only memory
+static inline char *
+strdupIfMutable(const char *str)
{
- return (uint8_t *)strdup((char *)str);
+ size_t size = strlen(str) + 1;
+ if (_dyld_is_memory_immutable(str, size)) {
+ return (char *)str;
+ } else {
+ return (char *)memdup(str, size);
+ }
}
-// nil-checking strdup
-static inline uint8_t *
-strdupMaybeNil(const uint8_t *str)
+// free strdupIfMutable() result
+static inline void
+freeIfMutable(char *str)
{
- if (!str) return nil;
- return (uint8_t *)strdup((char *)str);
+ size_t size = strlen(str) + 1;
+ if (_dyld_is_memory_immutable(str, size)) {
+ // nothing
+ } else {
+ free(str);
+ }
}
// nil-checking unsigned strdup
ustrdupMaybeNil(const uint8_t *str)
{
if (!str) return nil;
- return (uint8_t *)strdup((char *)str);
+ return (uint8_t *)strdupIfMutable((char *)str);
}
+// OS version checking:
+//
+// sdkVersion()
+// DYLD_OS_VERSION(mac, ios, tv, watch)
+// sdkIsOlderThan(mac, ios, tv, watch)
+// sdkIsAtLeast(mac, ios, tv, watch)
+//
+// This version order matches OBJC_AVAILABLE.
+
+#if TARGET_OS_OSX
+# define DYLD_OS_VERSION(x, i, t, w) DYLD_MACOSX_VERSION_##x
+# define sdkVersion() dyld_get_program_sdk_version()
+
+#elif TARGET_OS_IOS
+# define DYLD_OS_VERSION(x, i, t, w) DYLD_IOS_VERSION_##i
+# define sdkVersion() dyld_get_program_sdk_version()
+
+#elif TARGET_OS_TV
+ // dyld does not currently have distinct constants for tvOS
+# define DYLD_OS_VERSION(x, i, t, w) DYLD_IOS_VERSION_##t
+# define sdkVersion() dyld_get_program_sdk_version()
+
+#elif TARGET_OS_WATCH
+# define DYLD_OS_VERSION(x, i, t, w) DYLD_WATCHOS_VERSION_##w
+ // watchOS has its own API for compatibility reasons
+# define sdkVersion() dyld_get_program_sdk_watch_os_version()
+
+#else
+# error unknown OS
+#endif
+
+
+#define sdkIsOlderThan(x, i, t, w) \
+ (sdkVersion() < DYLD_OS_VERSION(x, i, t, w))
+#define sdkIsAtLeast(x, i, t, w) \
+ (sdkVersion() >= DYLD_OS_VERSION(x, i, t, w))
+
+// Allow bare 0 to be used in DYLD_OS_VERSION() and sdkIsOlderThan()
+#define DYLD_MACOSX_VERSION_0 0
+#define DYLD_IOS_VERSION_0 0
+#define DYLD_TVOS_VERSION_0 0
+#define DYLD_WATCHOS_VERSION_0 0
+
+// Pretty-print a DYLD_*_VERSION_* constant.
+#define SDK_FORMAT "%hu.%hhu.%hhu"
+#define FORMAT_SDK(v) \
+ (unsigned short)(((uint32_t)(v))>>16), \
+ (unsigned char)(((uint32_t)(v))>>8), \
+ (unsigned char)(((uint32_t)(v))>>0)
+
#endif
environ_init();
tls_init();
lock_init();
- sel_init(NO, 3500); // old selector heuristic
+ sel_init(3500); // old selector heuristic
exception_init();
break;
appendHeader(hi);
if (PrintImages) {
- _objc_inform("IMAGES: loading image for %s%s%s\n",
- hi->fname,
- headerIsBundle(hi) ? " (bundle)" : "",
- _objcHeaderIsReplacement(hi) ? " (replacement)":"");
+ _objc_inform("IMAGES: loading image for %s%s%s%s\n",
+ hi->fname,
+ headerIsBundle(hi) ? " (bundle)" : "",
+ hi->info->isReplacement() ? " (replacement)":"",
+ hi->info->hasCategoryClassProperties() ? " (has class properties)":"");
+ }
+
+ // Count classes. Size various table based on the total.
+ int total = 0;
+ int unoptimizedTotal = 0;
+ {
+ if (_getObjc2ClassList(hi, &count)) {
+ total += (int)count;
+ if (!hi->getInSharedCache()) unoptimizedTotal += count;
+ }
}
- _read_images(&hi, 1);
+ _read_images(&hi, 1, total, unoptimizedTotal);
return hi;
}
}
-bool crashlog_header_name(header_info *hi)
-{
- return true;
-}
-
-
// TARGET_OS_WIN32
#elif TARGET_OS_MAC
#include "objc-file.h"
+/***********************************************************************
+* libobjc must never run static destructors.
+* Cover libc's __cxa_atexit with our own definition that runs nothing.
+* rdar://21734598 ER: Compiler option to suppress C++ static destructors
+**********************************************************************/
+extern "C" int __cxa_atexit();
+extern "C" int __cxa_atexit() { return 0; }
+
+
/***********************************************************************
* bad_magic.
* Return YES if the header has invalid Mach-o magic.
}
-static header_info * addHeader(const headerType *mhdr)
+static header_info * addHeader(const headerType *mhdr, const char *path, int &totalClasses, int &unoptimizedTotalClasses)
{
header_info *hi;
if (bad_magic(mhdr)) return NULL;
-#if __OBJC2__
+ bool inSharedCache = false;
+
// Look for hinfo from the dyld shared cache.
hi = preoptimizedHinfoForHeader(mhdr);
if (hi) {
// Found an hinfo in the dyld shared cache.
// Weed out duplicates.
- if (hi->loaded) {
+ if (hi->isLoaded()) {
return NULL;
}
+ inSharedCache = true;
+
// Initialize fields not set by the shared cache
// hi->next is set by appendHeader
- hi->fname = dyld_image_path_containing_address(hi->mhdr);
- hi->loaded = true;
- hi->inSharedCache = true;
+ hi->setLoaded(true);
if (PrintPreopt) {
- _objc_inform("PREOPTIMIZATION: honoring preoptimized header info at %p for %s", hi, hi->fname);
+ _objc_inform("PREOPTIMIZATION: honoring preoptimized header info at %p for %s", hi, hi->fname());
}
-# if DEBUG
+#if !__OBJC2__
+ _objc_fatal("shouldn't be here");
+#endif
+#if DEBUG
// Verify image_info
size_t info_size = 0;
const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size);
- assert(image_info == hi->info);
-# endif
+ assert(image_info == hi->info());
+#endif
}
else
-#endif
{
// Didn't find an hinfo in the dyld shared cache.
// Weed out duplicates
- for (hi = FirstHeader; hi; hi = hi->next) {
- if (mhdr == hi->mhdr) return NULL;
+ for (hi = FirstHeader; hi; hi = hi->getNext()) {
+ if (mhdr == hi->mhdr()) return NULL;
}
// Locate the __OBJC segment
if (!objc_segment && !image_info) return NULL;
// Allocate a header_info entry.
- hi = (header_info *)calloc(sizeof(header_info), 1);
+ // Note we also allocate space for a single header_info_rw in the
+ // rw_data[] inside header_info.
+ hi = (header_info *)calloc(sizeof(header_info) + sizeof(header_info_rw), 1);
// Set up the new header_info entry.
- hi->mhdr = mhdr;
+ hi->setmhdr(mhdr);
#if !__OBJC2__
// mhdr must already be set
hi->mod_count = 0;
hi->mod_ptr = _getObjcModules(hi, &hi->mod_count);
#endif
- hi->info = image_info;
- hi->fname = dyld_image_path_containing_address(hi->mhdr);
- hi->loaded = true;
- hi->inSharedCache = false;
- hi->allClassesRealized = NO;
+ // Install a placeholder image_info if absent to simplify code elsewhere
+ static const objc_image_info emptyInfo = {0, 0};
+ hi->setinfo(image_info ?: &emptyInfo);
+
+ hi->setLoaded(true);
+ hi->setAllClassesRealized(NO);
}
- // dylibs are not allowed to unload
- // ...except those with image_info and nothing else (5359412)
- if (hi->mhdr->filetype == MH_DYLIB && _hasObjcContents(hi)) {
- dlopen(hi->fname, RTLD_NOLOAD);
+#if __OBJC2__
+ {
+ size_t count = 0;
+ if (_getObjc2ClassList(hi, &count)) {
+ totalClasses += (int)count;
+ if (!inSharedCache) unoptimizedTotalClasses += count;
+ }
}
+#endif
appendHeader(hi);
}
-#if !SUPPORT_GC
-
-const char *_gcForHInfo(const header_info *hinfo)
-{
- return "";
-}
-const char *_gcForHInfo2(const header_info *hinfo)
-{
- return "";
-}
-
-#else
-
-/***********************************************************************
-* _gcForHInfo.
-**********************************************************************/
-const char *_gcForHInfo(const header_info *hinfo)
-{
- if (_objcHeaderRequiresGC(hinfo)) {
- return "requires GC";
- } else if (_objcHeaderSupportsGC(hinfo)) {
- return "supports GC";
- } else {
- return "does not support GC";
- }
-}
-const char *_gcForHInfo2(const header_info *hinfo)
-{
- if (_objcHeaderRequiresGC(hinfo)) {
- return "(requires GC)";
- } else if (_objcHeaderSupportsGC(hinfo)) {
- return "(supports GC)";
- }
- return "";
-}
-
-
/***********************************************************************
* linksToLibrary
* Returns true if the image links directly to a dylib whose install name
const struct dylib_command *cmd;
unsigned long i;
- cmd = (const struct dylib_command *) (hi->mhdr + 1);
- for (i = 0; i < hi->mhdr->ncmds; i++) {
+ cmd = (const struct dylib_command *) (hi->mhdr() + 1);
+ for (i = 0; i < hi->mhdr()->ncmds; i++) {
if (cmd->cmd == LC_LOAD_DYLIB || cmd->cmd == LC_LOAD_UPWARD_DYLIB ||
cmd->cmd == LC_LOAD_WEAK_DYLIB || cmd->cmd == LC_REEXPORT_DYLIB)
{
}
+#if SUPPORT_GC_COMPAT
+
/***********************************************************************
-* check_gc
-* Check whether the executable supports or requires GC, and make sure
-* all already-loaded libraries support the executable's GC mode.
-* Returns TRUE if the executable wants GC on.
+* shouldRejectGCApp
+* Return YES if the executable requires GC.
**********************************************************************/
-static void check_wants_gc(bool *appWantsGC)
+static bool shouldRejectGCApp(const header_info *hi)
{
- const header_info *hi;
+ assert(hi->mhdr()->filetype == MH_EXECUTE);
- // Environment variables can override the following.
- if (DisableGC) {
- _objc_inform_on_crash("GC: forcing GC OFF because OBJC_DISABLE_GC is set");
- *appWantsGC = NO;
+ if (!hi->info()->supportsGC()) {
+ // App does not use GC. Don't reject it.
+ return NO;
}
- else {
- // Find the executable and check its GC bits.
- // If the executable cannot be found, default to NO.
- // (The executable will not be found if the executable contains
- // no Objective-C code.)
- *appWantsGC = NO;
- for (hi = FirstHeader; hi != NULL; hi = hi->next) {
- if (hi->mhdr->filetype == MH_EXECUTE) {
- *appWantsGC = _objcHeaderSupportsGC(hi);
-
- if (PrintGC) {
- _objc_inform("GC: executable '%s' %s",
- hi->fname, _gcForHInfo(hi));
- }
-
- if (*appWantsGC) {
- // Exception: AppleScriptObjC apps run without GC in 10.9+
- // 1. executable defines no classes
- // 2. executable references NSBundle only
- // 3. executable links to AppleScriptObjC.framework
- size_t classcount = 0;
- size_t refcount = 0;
+
+ // Exception: Trivial AppleScriptObjC apps can run without GC.
+ // 1. executable defines no classes
+ // 2. executable references NSBundle only
+ // 3. executable links to AppleScriptObjC.framework
+ // Note that objc_appRequiresGC() also knows about this.
+ size_t classcount = 0;
+ size_t refcount = 0;
#if __OBJC2__
- _getObjc2ClassList(hi, &classcount);
- _getObjc2ClassRefs(hi, &refcount);
+ _getObjc2ClassList(hi, &classcount);
+ _getObjc2ClassRefs(hi, &refcount);
#else
- if (hi->mod_count == 0 || (hi->mod_count == 1 && !hi->mod_ptr[0].symtab)) classcount = 0;
- else classcount = 1;
- _getObjcClassRefs(hi, &refcount);
+ if (hi->mod_count == 0 || (hi->mod_count == 1 && !hi->mod_ptr[0].symtab)) classcount = 0;
+ else classcount = 1;
+ _getObjcClassRefs(hi, &refcount);
#endif
- if (classcount == 0 && refcount == 1 &&
- linksToLibrary(hi, "/System/Library/Frameworks"
- "/AppleScriptObjC.framework/Versions/A"
- "/AppleScriptObjC"))
- {
- *appWantsGC = NO;
- if (PrintGC) {
- _objc_inform("GC: forcing GC OFF because this is "
- "a trivial AppleScriptObjC app");
- }
- }
- }
- }
- }
- }
-}
-
-
-/***********************************************************************
-* verify_gc_readiness
-* if we want gc, verify that every header describes files compiled
-* and presumably ready for gc.
-************************************************************************/
-static void verify_gc_readiness(bool wantsGC,
- header_info **hList, uint32_t hCount)
-{
- bool busted = NO;
- uint32_t i;
-
- // Find the libraries and check their GC bits against the app's request
- for (i = 0; i < hCount; i++) {
- header_info *hi = hList[i];
- if (hi->mhdr->filetype == MH_EXECUTE) {
- continue;
- }
- else if (hi->mhdr == &_mh_dylib_header) {
- // libobjc itself works with anything even though it is not
- // compiled with -fobjc-gc (fixme should it be?)
- }
- else if (wantsGC && ! _objcHeaderSupportsGC(hi)) {
- // App wants GC but library does not support it - bad
- _objc_inform_now_and_on_crash
- ("'%s' was not compiled with -fobjc-gc or -fobjc-gc-only, "
- "but the application requires GC",
- hi->fname);
- busted = YES;
- }
- else if (!wantsGC && _objcHeaderRequiresGC(hi)) {
- // App doesn't want GC but library requires it - bad
- _objc_inform_now_and_on_crash
- ("'%s' was compiled with -fobjc-gc-only, "
- "but the application does not support GC",
- hi->fname);
- busted = YES;
- }
-
- if (PrintGC) {
- _objc_inform("GC: library '%s' %s",
- hi->fname, _gcForHInfo(hi));
- }
- }
-
- if (busted) {
- // GC state is not consistent.
- // Kill the process unless one of the forcing flags is set.
- if (!DisableGC) {
- _objc_fatal("*** GC capability of application and some libraries did not match");
- }
+ if (classcount == 0 && refcount == 1 &&
+ linksToLibrary(hi, "/System/Library/Frameworks"
+ "/AppleScriptObjC.framework/Versions/A"
+ "/AppleScriptObjC"))
+ {
+ // It's AppleScriptObjC. Don't reject it.
+ return NO;
+ }
+ else {
+ // GC and not trivial AppleScriptObjC. Reject it.
+ return YES;
}
}
/***********************************************************************
-* gc_enforcer
-* Make sure that images about to be loaded by dyld are GC-acceptable.
-* Images linked to the executable are always permitted; they are
-* enforced inside map_images() itself.
+* rejectGCImage
+* Halt if an image requires GC.
+* Testing of the main executable should use rejectGCApp() instead.
**********************************************************************/
-static bool InitialDyldRegistration = NO;
-static const char *gc_enforcer(enum dyld_image_states state,
- uint32_t infoCount,
- const struct dyld_image_info info[])
+static bool shouldRejectGCImage(const headerType *mhdr)
{
- uint32_t i;
-
- // Linked images get a free pass
- if (InitialDyldRegistration) return NULL;
-
- if (PrintImages) {
- _objc_inform("IMAGES: checking %d images for compatibility...",
- infoCount);
- }
-
- for (i = 0; i < infoCount; i++) {
- crashlog_header_name_string(info[i].imageFilePath);
-
- const headerType *mhdr = (const headerType *)info[i].imageLoadAddress;
- if (bad_magic(mhdr)) continue;
-
- objc_image_info *image_info;
- size_t size;
-
- if (mhdr == &_mh_dylib_header) {
- // libobjc itself - OK
- continue;
- }
+ assert(mhdr->filetype != MH_EXECUTE);
+ objc_image_info *image_info;
+ size_t size;
+
#if !__OBJC2__
- unsigned long seg_size;
- // 32-bit: __OBJC seg but no image_info means no GC support
- if (!getsegmentdata(mhdr, "__OBJC", &seg_size)) {
- // not objc - assume OK
- continue;
- }
- image_info = _getObjcImageInfo(mhdr, &size);
- if (!image_info) {
- // No image_info - assume GC unsupported
- if (!UseGC) {
- // GC is OFF - ok
- continue;
- } else {
- // GC is ON - bad
- if (PrintImages || PrintGC) {
- _objc_inform("IMAGES: rejecting %d images because %s doesn't support GC (no image_info)", infoCount, info[i].imageFilePath);
- }
- goto reject;
- }
- }
+ unsigned long seg_size;
+ // 32-bit: __OBJC seg but no image_info means no GC support
+ if (!getsegmentdata(mhdr, "__OBJC", &seg_size)) {
+ // Not objc, therefore not GC. Don't reject it.
+ return NO;
+ }
+ image_info = _getObjcImageInfo(mhdr, &size);
+ if (!image_info) {
+ // No image_info, therefore not GC. Don't reject it.
+ return NO;
+ }
#else
- // 64-bit: no image_info means no objc at all
- image_info = _getObjcImageInfo(mhdr, &size);
- if (!image_info) {
- // not objc - assume OK
- continue;
- }
-#endif
-
- if (UseGC && !_objcInfoSupportsGC(image_info)) {
- // GC is ON, but image does not support GC
- if (PrintImages || PrintGC) {
- _objc_inform("IMAGES: rejecting %d images because %s doesn't support GC", infoCount, info[i].imageFilePath);
- }
- goto reject;
- }
- if (!UseGC && _objcInfoRequiresGC(image_info)) {
- // GC is OFF, but image requires GC
- if (PrintImages || PrintGC) {
- _objc_inform("IMAGES: rejecting %d images because %s requires GC", infoCount, info[i].imageFilePath);
- }
- goto reject;
- }
+ // 64-bit: no image_info means no objc at all
+ image_info = _getObjcImageInfo(mhdr, &size);
+ if (!image_info) {
+ // Not objc, therefore not GC. Don't reject it.
+ return NO;
}
+#endif
- crashlog_header_name_string(NULL);
- return NULL;
-
- reject:
- crashlog_header_name_string(NULL);
- return "GC capability mismatch";
+ return image_info->requiresGC();
}
-// SUPPORT_GC
+// SUPPORT_GC_COMPAT
#endif
#include "objc-file-old.h"
#endif
-const char *
-map_images_nolock(enum dyld_image_states state, uint32_t infoCount,
- const struct dyld_image_info infoList[])
+void
+map_images_nolock(unsigned mhCount, const char * const mhPaths[],
+ const struct mach_header * const mhdrs[])
{
static bool firstTime = YES;
- static bool wantsGC = NO;
- uint32_t i;
- header_info *hi;
- header_info *hList[infoCount];
+ header_info *hList[mhCount];
uint32_t hCount;
size_t selrefCount = 0;
// fixme defer initialization until an objc-using image is found?
if (firstTime) {
preopt_init();
-#if SUPPORT_GC
- InitialDyldRegistration = YES;
- dyld_register_image_state_change_handler(dyld_image_state_mapped, 0 /* batch */, &gc_enforcer);
- InitialDyldRegistration = NO;
-#endif
}
if (PrintImages) {
- _objc_inform("IMAGES: processing %u newly-mapped images...\n", infoCount);
+ _objc_inform("IMAGES: processing %u newly-mapped images...\n", mhCount);
}
// Find all images with Objective-C metadata.
hCount = 0;
- i = infoCount;
- while (i--) {
- const headerType *mhdr = (headerType *)infoList[i].imageLoadAddress;
-
- hi = addHeader(mhdr);
- if (!hi) {
- // no objc data in this entry
- continue;
- }
- if (mhdr->filetype == MH_EXECUTE) {
- // Size some data structures based on main executable's size
+ // Count classes. Size various table based on the total.
+ int totalClasses = 0;
+ int unoptimizedTotalClasses = 0;
+ {
+ uint32_t i = mhCount;
+ while (i--) {
+ const headerType *mhdr = (const headerType *)mhdrs[i];
+
+ auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
+ if (!hi) {
+ // no objc data in this entry
+ continue;
+ }
+
+ if (mhdr->filetype == MH_EXECUTE) {
+ // Size some data structures based on main executable's size
#if __OBJC2__
- size_t count;
- _getObjc2SelectorRefs(hi, &count);
- selrefCount += count;
- _getObjc2MessageRefs(hi, &count);
- selrefCount += count;
+ size_t count;
+ _getObjc2SelectorRefs(hi, &count);
+ selrefCount += count;
+ _getObjc2MessageRefs(hi, &count);
+ selrefCount += count;
#else
- _getObjcSelectorRefs(hi, &selrefCount);
+ _getObjcSelectorRefs(hi, &selrefCount);
#endif
- }
-
- hList[hCount++] = hi;
-
-
- if (PrintImages) {
- _objc_inform("IMAGES: loading image for %s%s%s%s%s\n",
- hi->fname,
- mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
- _objcHeaderIsReplacement(hi) ? " (replacement)" : "",
- _objcHeaderOptimizedByDyld(hi)?" (preoptimized)" : "",
- _gcForHInfo2(hi));
+
+#if SUPPORT_GC_COMPAT
+ // Halt if this is a GC app.
+ if (shouldRejectGCApp(hi)) {
+ _objc_fatal_with_reason
+ (OBJC_EXIT_REASON_GC_NOT_SUPPORTED,
+ OS_REASON_FLAG_CONSISTENT_FAILURE,
+ "Objective-C garbage collection "
+ "is no longer supported.");
+ }
+#endif
+ }
+
+ hList[hCount++] = hi;
+
+ if (PrintImages) {
+ _objc_inform("IMAGES: loading image for %s%s%s%s%s\n",
+ hi->fname(),
+ mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
+ hi->info()->isReplacement() ? " (replacement)" : "",
+ hi->info()->hasCategoryClassProperties() ? " (has class properties)" : "",
+ hi->info()->optimizedByDyld()?" (preoptimized)":"");
+ }
}
}
// further initialization.
// (The executable may not be present in this infoList if the
// executable does not contain Objective-C code but Objective-C
- // is dynamically loaded later. In that case, check_wants_gc()
- // will do the right thing.)
-#if SUPPORT_GC
+ // is dynamically loaded later.
if (firstTime) {
- check_wants_gc(&wantsGC);
-
- verify_gc_readiness(wantsGC, hList, hCount);
-
- gc_init(wantsGC); // needs executable for GC decision
- } else {
- verify_gc_readiness(wantsGC, hList, hCount);
- }
-
- if (wantsGC) {
- // tell the collector about the data segment ranges.
- for (i = 0; i < hCount; ++i) {
- uint8_t *seg;
- unsigned long seg_size;
- hi = hList[i];
-
- seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size);
- if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
-
- seg = getsegmentdata(hi->mhdr, "__DATA_CONST", &seg_size);
- if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
-
- seg = getsegmentdata(hi->mhdr, "__DATA_DIRTY", &seg_size);
- if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
+ sel_init(selrefCount);
+ arr_init();
- seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
- if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
- // __OBJC contains no GC data, but pointers to it are
- // used as associated reference values (rdar://6953570)
+#if SUPPORT_GC_COMPAT
+ // Reject any GC images linked to the main executable.
+ // We already rejected the app itself above.
+ // Images loaded after launch will be rejected by dyld.
+
+ for (uint32_t i = 0; i < hCount; i++) {
+ auto hi = hList[i];
+ auto mh = hi->mhdr();
+ if (mh->filetype != MH_EXECUTE && shouldRejectGCImage(mh)) {
+ _objc_fatal_with_reason
+ (OBJC_EXIT_REASON_GC_NOT_SUPPORTED,
+ OS_REASON_FLAG_CONSISTENT_FAILURE,
+ "%s requires Objective-C garbage collection "
+ "which is no longer supported.", hi->fname());
+ }
}
- }
#endif
-
- if (firstTime) {
- sel_init(wantsGC, selrefCount);
- arr_init();
}
- _read_images(hList, hCount);
-
- firstTime = NO;
-
- return NULL;
-}
-
-
-/***********************************************************************
-* load_images_nolock
-* Prepares +load in the given images which are being mapped in by dyld.
-* Returns YES if there are now +load methods to be called by call_load_methods.
-*
-* Locking: loadMethodLock(both) and runtimeLock(new) acquired by load_images
-**********************************************************************/
-bool
-load_images_nolock(enum dyld_image_states state,uint32_t infoCount,
- const struct dyld_image_info infoList[])
-{
- bool found = NO;
- uint32_t i;
-
- i = infoCount;
- while (i--) {
- const headerType *mhdr = (headerType*)infoList[i].imageLoadAddress;
- if (!hasLoadMethods(mhdr)) continue;
-
- prepare_load_methods(mhdr);
- found = YES;
+ if (hCount > 0) {
+ _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
- return found;
+ firstTime = NO;
}
header_info *hi;
// Find the runtime's header_info struct for the image
- for (hi = FirstHeader; hi != NULL; hi = hi->next) {
- if (hi->mhdr == (const headerType *)mh) {
+ for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
+ if (hi->mhdr() == (const headerType *)mh) {
break;
}
}
if (!hi) return;
- if (PrintImages) {
- _objc_inform("IMAGES: unloading image for %s%s%s%s\n",
- hi->fname,
- hi->mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
- _objcHeaderIsReplacement(hi) ? " (replacement)" : "",
- _gcForHInfo2(hi));
- }
-
-#if SUPPORT_GC
- if (UseGC) {
- uint8_t *seg;
- unsigned long seg_size;
-
- seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size);
- if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
-
- seg = getsegmentdata(hi->mhdr, "__DATA_CONST", &seg_size);
- if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
-
- seg = getsegmentdata(hi->mhdr, "__DATA_DIRTY", &seg_size);
- if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
-
- seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
- if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
+ if (PrintImages) {
+ _objc_inform("IMAGES: unloading image for %s%s%s\n",
+ hi->fname(),
+ hi->mhdr()->filetype == MH_BUNDLE ? " (bundle)" : "",
+ hi->info()->isReplacement() ? " (replacement)" : "");
}
-#endif
_unload_image(hi);
**********************************************************************/
static void static_init()
{
-#if __OBJC2__
size_t count;
Initializer *inits = getLibobjcInitializers(&_mh_dylib_header, &count);
for (size_t i = 0; i < count; i++) {
inits[i]();
}
-#endif
}
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
-* Old ABI: called by dyld as a library initializer
-* New ABI: called by libSystem BEFORE library initialization time
+* Called by libSystem BEFORE library initialization time
**********************************************************************/
-#if !__OBJC2__
-static __attribute__((constructor))
-#endif
void _objc_init(void)
{
static bool initialized = false;
static_init();
lock_init();
exception_init();
-
- // Register for unmap first, in case some +load unmaps something
- _dyld_register_func_for_remove_image(&unmap_image);
- dyld_register_image_state_change_handler(dyld_image_state_bound,
- 1/*batch*/, &map_2_images);
- dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images);
+
+ _dyld_objc_notify_register(&map_2_images, load_images, unmap_image);
}
#endif
header_info *hi;
- for (hi = FirstHeader; hi != NULL; hi = hi->next) {
+ for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
for (size_t i = 0; i < sizeof(segnames)/sizeof(segnames[0]); i++) {
unsigned long seg_size;
- uint8_t *seg = getsegmentdata(hi->mhdr, segnames[i], &seg_size);
+ uint8_t *seg = getsegmentdata(hi->mhdr(), segnames[i], &seg_size);
if (!seg) continue;
// Is the class in this header?
}
-bool crashlog_header_name(header_info *hi)
-{
- return crashlog_header_name_string(hi ? hi->fname : NULL);
-}
-
-bool crashlog_header_name_string(const char *name)
-{
- CRSetCrashLogMessage2(name);
- return true;
-}
-
-
#if TARGET_OS_IPHONE
const char *__crashreporter_info__ = NULL;
return __crashreporter_info__;
}
-const char *CRSetCrashLogMessage2(const char *msg)
-{
- // sorry
- return msg;
-}
-
#endif
// TARGET_OS_MAC
};
+#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
+
+
union isa_t
{
isa_t() { }
Class cls;
uintptr_t bits;
-#if SUPPORT_NONPOINTER_ISA
+#if SUPPORT_PACKED_ISA
// extra_rc must be the MSB-most field (so it matches carry/overflow flags)
- // indexed must be the LSB (fixme or get rid of it)
+ // 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)
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
- uintptr_t indexed : 1;
+ uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
struct {
- uintptr_t indexed : 1;
+ uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000
};
# else
- // Available bits in isa field are architecture-specific.
-# error unknown architecture
+# error unknown architecture for packed isa
+# endif
+
+// SUPPORT_PACKED_ISA
+#endif
+
+
+#if SUPPORT_INDEXED_ISA
+
+# if __ARM_ARCH_7K__ >= 2
+
+# 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
+ struct {
+ 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_NONPOINTER_ISA
+// SUPPORT_INDEXED_ISA
#endif
};
// initClassIsa(): class objects
// initProtocolIsa(): protocol objects
// initIsa(): other objects
- void initIsa(Class cls /*indexed=false*/);
- void initClassIsa(Class cls /*indexed=maybe*/);
- void initProtocolIsa(Class cls /*indexed=maybe*/);
+ void initIsa(Class cls /*nonpointer=false*/);
+ void initClassIsa(Class cls /*nonpointer=maybe*/);
+ void initProtocolIsa(Class cls /*nonpointer=maybe*/);
void initInstanceIsa(Class cls, bool hasCxxDtor);
// changeIsa() should be used to change the isa of existing objects.
// If this is a new object, use initIsa() for performance.
Class changeIsa(Class newCls);
- bool hasIndexedIsa();
+ bool hasNonpointerIsa();
bool isTaggedPointer();
+ bool isBasicTaggedPointer();
+ bool isExtTaggedPointer();
bool isClass();
// object may have associated objects?
void rootDealloc();
private:
- void initIsa(Class newCls, bool indexed, bool hasCxxDtor);
+ void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);
// Slow paths for inline control
id rootAutorelease2();
#include "maptable.h"
#include "hashtable2.h"
-#if SUPPORT_GC
-#include "objc-auto.h"
-#endif
-
/* Do not include message.h here. */
/* #include "message.h" */
#endif
+#define STRINGIFY(x) #x
+#define STRINGIFY2(x) STRINGIFY(x)
+
__BEGIN_DECLS
+struct header_info;
-#if (defined(OBJC_NO_GC) && SUPPORT_GC) || \
- (!defined(OBJC_NO_GC) && !SUPPORT_GC)
-# error OBJC_NO_GC and SUPPORT_GC inconsistent
-#endif
+// Split out the rw data from header info. For now put it in a huge array
+// that more than exceeds the space needed. In future we'll just allocate
+// this in the shared cache builder.
+typedef struct header_info_rw {
-#if SUPPORT_GC
-# include <auto_zone.h>
- // PRIVATE_EXTERN is needed to help the compiler know "how" extern these are
- PRIVATE_EXTERN extern int8_t UseGC; // equivalent to calling objc_collecting_enabled()
- PRIVATE_EXTERN extern auto_zone_t *gc_zone; // the GC zone, or NULL if no GC
- extern void objc_addRegisteredClass(Class c);
- extern void objc_removeRegisteredClass(Class c);
-#else
-# define UseGC NO
-# define gc_zone NULL
-# define objc_addRegisteredClass(c) do {} while(0)
-# define objc_removeRegisteredClass(c) do {} while(0)
- /* Uses of the following must be protected with UseGC. */
- extern id gc_unsupported_dont_call();
-# define auto_zone_allocate_object gc_unsupported_dont_call
-# define auto_zone_retain gc_unsupported_dont_call
-# define auto_zone_release gc_unsupported_dont_call
-# define auto_zone_is_valid_pointer gc_unsupported_dont_call
-# define auto_zone_write_barrier_memmove gc_unsupported_dont_call
-# define AUTO_OBJECT_SCANNED 0
-#endif
+ bool getLoaded() const {
+ return isLoaded;
+ }
+ void setLoaded(bool v) {
+ isLoaded = v ? 1: 0;
+ }
-#define _objcHeaderIsReplacement(h) ((h)->info && ((h)->info->flags & OBJC_IMAGE_IS_REPLACEMENT))
+ bool getAllClassesRealized() const {
+ return allClassesRealized;
+ }
-/* OBJC_IMAGE_IS_REPLACEMENT:
- Don't load any classes
- Don't load any categories
- Do fix up selector refs (@selector points to them)
- Do fix up class refs (@class and objc_msgSend points to them)
- Do fix up protocols (@protocol points to them)
- Do fix up superclass pointers in classes ([super ...] points to them)
- Future: do load new classes?
- Future: do load new categories?
- Future: do insert new methods on existing classes?
- Future: do insert new methods on existing categories?
-*/
+ void setAllClassesRealized(bool v) {
+ allClassesRealized = v ? 1: 0;
+ }
-#define _objcInfoSupportsGC(info) (((info)->flags & OBJC_IMAGE_SUPPORTS_GC) ? 1 : 0)
-#define _objcInfoRequiresGC(info) (((info)->flags & OBJC_IMAGE_REQUIRES_GC) ? 1 : 0)
-#define _objcHeaderSupportsGC(h) ((h)->info && _objcInfoSupportsGC((h)->info))
-#define _objcHeaderRequiresGC(h) ((h)->info && _objcInfoRequiresGC((h)->info))
+ header_info *getNext() const {
+ return (header_info *)(next << 2);
+ }
-/* OBJC_IMAGE_SUPPORTS_GC:
- was compiled with -fobjc-gc flag, regardless of whether write-barriers were issued
- if executable image compiled this way, then all subsequent libraries etc. must also be this way
-*/
+ void setNext(header_info *v) {
+ next = ((uintptr_t)v) >> 2;
+ }
-#define _objcHeaderOptimizedByDyld(h) ((h)->info && ((h)->info->flags & OBJC_IMAGE_OPTIMIZED_BY_DYLD))
+private:
+#ifdef __LP64__
+ uintptr_t isLoaded : 1;
+ uintptr_t allClassesRealized : 1;
+ uintptr_t next : 62;
+#else
+ uintptr_t isLoaded : 1;
+ uintptr_t allClassesRealized : 1;
+ uintptr_t next : 30;
+#endif
+} header_info_rw;
-/* OBJC_IMAGE_OPTIMIZED_BY_DYLD:
- Assorted metadata precooked in the dyld shared cache.
- Never set for images outside the shared cache file itself.
-*/
-
+struct header_info_rw* getPreoptimizedHeaderRW(const struct header_info *const hdr);
typedef struct header_info {
- struct header_info *next;
- const headerType *mhdr;
- const objc_image_info *info;
- const char *fname; // same as Dl_info.dli_fname
- bool loaded;
- bool inSharedCache;
- bool allClassesRealized;
+private:
+ // Note, this is no longer a pointer, but instead an offset to a pointer
+ // from this location.
+ intptr_t mhdr_offset;
+
+ // Note, this is no longer a pointer, but instead an offset to a pointer
+ // from this location.
+ intptr_t info_offset;
// Do not add fields without editing ObjCModernAbstraction.hpp
+public:
+
+ header_info_rw *getHeaderInfoRW() {
+ header_info_rw *preopt =
+ isPreoptimized() ? getPreoptimizedHeaderRW(this) : nil;
+ if (preopt) return preopt;
+ else return &rw_data[0];
+ }
+
+ const headerType *mhdr() const {
+ return (const headerType *)(((intptr_t)&mhdr_offset) + mhdr_offset);
+ }
+
+ void setmhdr(const headerType *mhdr) {
+ mhdr_offset = (intptr_t)mhdr - (intptr_t)&mhdr_offset;
+ }
+
+ const objc_image_info *info() const {
+ return (const objc_image_info *)(((intptr_t)&info_offset) + info_offset);
+ }
+
+ void setinfo(const objc_image_info *info) {
+ info_offset = (intptr_t)info - (intptr_t)&info_offset;
+ }
bool isLoaded() {
- return loaded;
+ return getHeaderInfoRW()->getLoaded();
+ }
+
+ void setLoaded(bool v) {
+ getHeaderInfoRW()->setLoaded(v);
+ }
+
+ bool areAllClassesRealized() {
+ return getHeaderInfoRW()->getAllClassesRealized();
+ }
+
+ void setAllClassesRealized(bool v) {
+ getHeaderInfoRW()->setAllClassesRealized(v);
+ }
+
+ header_info *getNext() {
+ return getHeaderInfoRW()->getNext();
+ }
+
+ void setNext(header_info *v) {
+ getHeaderInfoRW()->setNext(v);
}
bool isBundle() {
- return mhdr->filetype == MH_BUNDLE;
+ return mhdr()->filetype == MH_BUNDLE;
+ }
+
+ const char *fname() const {
+ return dyld_image_path_containing_address(mhdr());
}
bool isPreoptimized() const;
TCHAR *moduleName;
# endif
#endif
+
+private:
+ // Images in the shared cache will have an empty array here while those
+ // allocated at run time will allocate a single entry.
+ header_info_rw rw_data[];
} header_info;
extern header_info *FirstHeader;
extern bool _hasObjcContents(const header_info *hi);
+// Mach-O segment and section names are 16 bytes and may be un-terminated.
+
+static inline bool segnameEquals(const char *lhs, const char *rhs) {
+ return 0 == strncmp(lhs, rhs, 16);
+}
+
+static inline bool segnameStartsWith(const char *segname, const char *prefix) {
+ return 0 == strncmp(segname, prefix, strlen(prefix));
+}
+
+static inline bool sectnameEquals(const char *lhs, const char *rhs) {
+ return segnameEquals(lhs, rhs);
+}
+
+static inline bool sectnameStartsWith(const char *sectname, const char *prefix){
+ return segnameStartsWith(sectname, prefix);
+}
+
+
/* selectors */
-extern void sel_init(bool gc, size_t selrefCount);
+extern void sel_init(size_t selrefCount);
extern SEL sel_registerNameNoLock(const char *str, bool copy);
extern void sel_lock(void);
extern void sel_unlock(void);
extern SEL SEL_dealloc;
extern SEL SEL_copy;
extern SEL SEL_new;
-extern SEL SEL_finalize;
extern SEL SEL_forwardInvocation;
extern SEL SEL_tryRetain;
extern SEL SEL_isDeallocating;
extern void preopt_init(void);
extern void disableSharedCacheOptimizations(void);
extern bool isPreoptimized(void);
+extern bool noMissingWeakSuperclasses(void);
extern header_info *preoptimizedHinfoForHeader(const headerType *mhdr);
extern objc_selopt_t *preoptimizedSelectors(void);
#if !OBJC_OLD_DISPATCH_PROTOTYPES
extern void _objc_msgForward_impcache(void);
-extern void _objc_ignored_method(void);
-extern void _objc_msgSend_uncached_impcache(void);
#else
extern id _objc_msgForward_impcache(id, SEL, ...);
-extern id _objc_ignored_method(id, SEL, ...);
-extern id _objc_msgSend_uncached_impcache(id, SEL, ...);
#endif
/* errors */
extern void _objc_inform_now_and_on_crash(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
extern void _objc_inform_deprecated(const char *oldname, const char *newname) __attribute__((noinline));
extern void inform_duplicate(const char *name, Class oldCls, Class cls);
-extern bool crashlog_header_name(header_info *hi);
-extern bool crashlog_header_name_string(const char *name);
/* magic */
extern Class _objc_getFreedObjectClass (void);
~rwlock_writer_t() { lock.unlockWrite(); }
};
-/* ignored selector support */
-
-/* Non-GC: no ignored selectors
- GC (i386 Mac): some selectors ignored, remapped to kIgnore
- GC (others): some selectors ignored, but not remapped
-*/
-
-static inline int ignoreSelector(SEL sel)
-{
-#if !SUPPORT_GC
- return NO;
-#elif SUPPORT_IGNORED_SELECTOR_CONSTANT
- return UseGC && sel == (SEL)kIgnore;
-#else
- return UseGC &&
- (sel == @selector(retain) ||
- sel == @selector(release) ||
- sel == @selector(autorelease) ||
- sel == @selector(retainCount) ||
- sel == @selector(dealloc));
-#endif
-}
-
-static inline int ignoreSelectorNamed(const char *sel)
-{
-#if !SUPPORT_GC
- return NO;
-#else
- // release retain retainCount dealloc autorelease
- return (UseGC &&
- ( (sel[0] == 'r' && sel[1] == 'e' &&
- (strcmp(&sel[2], "lease") == 0 ||
- strcmp(&sel[2], "tain") == 0 ||
- strcmp(&sel[2], "tainCount") == 0 ))
- ||
- (strcmp(sel, "dealloc") == 0)
- ||
- (sel[0] == 'a' && sel[1] == 'u' &&
- strcmp(&sel[2], "torelease") == 0)));
-#endif
-}
-
-/* GC startup */
-extern void gc_init(bool wantsGC);
-extern void gc_init2(void);
/* Exceptions */
struct alt_handler_list;
extern void gdb_objc_class_changed(Class cls, unsigned long changes, const char *classname)
__attribute__((noinline));
-#if SUPPORT_GC
-
-/* Write barrier implementations */
-extern id objc_getAssociatedObject_non_gc(id object, const void *key);
-extern void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy);
-
-extern id objc_getAssociatedObject_gc(id object, const void *key);
-extern void objc_setAssociatedObject_gc(id object, const void *key, id value, objc_AssociationPolicy policy);
-
-/* xrefs */
-extern objc_xref_t _object_addExternalReference_non_gc(id obj, objc_xref_t type);
-extern id _object_readExternalReference_non_gc(objc_xref_t ref);
-extern void _object_removeExternalReference_non_gc(objc_xref_t ref);
-
-extern objc_xref_t _object_addExternalReference_gc(id obj, objc_xref_t type);
-extern id _object_readExternalReference_gc(objc_xref_t ref);
-extern void _object_removeExternalReference_gc(objc_xref_t ref);
-
-/* GC weak reference fixup. */
-extern void gc_fixup_weakreferences(id newObject, id oldObject);
-
-/* GC datasegment registration. */
-extern void gc_register_datasegment(uintptr_t base, size_t size);
-extern void gc_unregister_datasegment(uintptr_t base, size_t size);
-
-/* objc_dumpHeap implementation */
-extern bool _objc_dumpHeap(auto_zone_t *zone, const char *filename);
-
-#endif
-
// Settings from environment variables
#define OPTION(var, env, help) extern bool var;
// fixme runtime
extern Class look_up_class(const char *aClassName, bool includeUnconnected, bool includeClassHandler);
-extern "C" const char *map_2_images(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]);
-extern const char *map_images_nolock(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]);
-extern const char * load_images(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]);
-extern bool load_images_nolock(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]);
-extern void unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide);
+extern "C" void map_2_images(unsigned count, const char * const paths[],
+ const struct mach_header * const mhdrs[]);
+extern void map_images_nolock(unsigned count, const char * const paths[],
+ const struct mach_header * const mhdrs[]);
+extern void load_images(const char *path, const struct mach_header *mh);
+extern void unmap_image(const char *path, const struct mach_header *mh);
extern void unmap_image_nolock(const struct mach_header *mh);
-extern void _read_images(header_info **hList, uint32_t hCount);
-extern void prepare_load_methods(const headerType *mhdr);
-extern bool hasLoadMethods(const headerType *mhdr);
+extern void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClass);
extern void _unload_image(header_info *hi);
extern const char ** _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount);
extern Class _class_remap(Class cls);
extern Class _class_getNonMetaClass(Class cls, id obj);
-extern Ivar _class_getVariable(Class cls, const char *name, Class *memberOf);
-extern uint32_t _class_getInstanceStart(Class cls);
+extern Ivar _class_getVariable(Class cls, const char *name);
extern unsigned _class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone, id *results, unsigned num_requested);
extern id _objc_constructOrFree(id bytes, Class cls);
extern void _class_resolveMethod(Class cls, SEL sel, id inst);
+extern void fixupCopiedIvars(id newObject, id oldObject);
+extern Class _class_getClassForIvar(Class cls, Ivar ivar);
+
+
#define OBJC_WARN_DEPRECATED \
do { \
static int warned = 0; \
#endif
+// Misalignment-safe integer types
+__attribute__((aligned(1))) typedef uintptr_t unaligned_uintptr_t;
+__attribute__((aligned(1))) typedef intptr_t unaligned_intptr_t;
+__attribute__((aligned(1))) typedef uint64_t unaligned_uint64_t;
+__attribute__((aligned(1))) typedef int64_t unaligned_int64_t;
+__attribute__((aligned(1))) typedef uint32_t unaligned_uint32_t;
+__attribute__((aligned(1))) typedef int32_t unaligned_int32_t;
+__attribute__((aligned(1))) typedef uint16_t unaligned_uint16_t;
+__attribute__((aligned(1))) typedef int16_t unaligned_int16_t;
+
// Global operator new and delete. We must not use any app overrides.
// This ALSO REQUIRES each of these be in libobjc's unexported symbol list.
// DisguisedPtr<T> acts like pointer type T*, except the
// stored value is disguised to hide it from tools like `leaks`.
// nil is disguised as itself so zero-filled memory works as expected,
-// which means 0x80..00 is also diguised as itself but we don't care
+// which means 0x80..00 is also disguised as itself but we don't care.
+// Note that weak_entry_t knows about this encoding.
template <typename T>
class DisguisedPtr {
uintptr_t value;
};
struct ivar_list_t : entsize_list_tt<ivar_t, ivar_list_t, 0> {
+ bool containsIvar(Ivar ivar) const {
+ return (ivar >= (Ivar)&*begin() && ivar < (Ivar)&*end());
+ }
};
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
// Values for protocol_t->flags
#define PROTOCOL_FIXED_UP_2 (1<<31) // must never be set by compiler
#define PROTOCOL_FIXED_UP_1 (1<<30) // must never be set by compiler
+// Bits 0..15 are reserved for Swift's use.
#define PROTOCOL_FIXED_UP_MASK (PROTOCOL_FIXED_UP_1 | PROTOCOL_FIXED_UP_2)
uint32_t size; // sizeof(protocol_t)
uint32_t flags;
// Fields below this point are not always present on disk.
- const char **extendedMethodTypes;
+ const char **_extendedMethodTypes;
const char *_demangledName;
+ property_list_t *_classProperties;
const char *demangledName();
bool isFixedUp() const;
void setFixedUp();
+# define HAS_FIELD(f) (size >= offsetof(protocol_t, f) + sizeof(f))
+
bool hasExtendedMethodTypesField() const {
- return size >= (offsetof(protocol_t, extendedMethodTypes)
- + sizeof(extendedMethodTypes));
+ return HAS_FIELD(_extendedMethodTypes);
+ }
+ bool hasDemangledNameField() const {
+ return HAS_FIELD(_demangledName);
+ }
+ bool hasClassPropertiesField() const {
+ return HAS_FIELD(_classProperties);
+ }
+
+# undef HAS_FIELD
+
+ const char **extendedMethodTypes() const {
+ return hasExtendedMethodTypesField() ? _extendedMethodTypes : nil;
}
- bool hasExtendedMethodTypes() const {
- return hasExtendedMethodTypesField() && extendedMethodTypes;
+
+ property_list_t *classProperties() const {
+ return hasClassPropertiesField() ? _classProperties : nil;
}
};
// The extra bits are optimized for the retain/release and alloc/dealloc paths.
// Values for class_ro_t->flags
-// These are emitted by the compiler and are part of the ABI.
+// These are emitted by the compiler and are part of the ABI.
+// Note: See CGObjCNonFragileABIMac::BuildClassRoTInitializer in clang
// class is a metaclass
#define RO_META (1<<0)
// class is a root class
#define RO_EXCEPTION (1<<5)
// this bit is available for reassignment
// #define RO_REUSE_ME (1<<6)
-// class compiled with -fobjc-arc (automatic retain/release)
-#define RO_IS_ARR (1<<7)
+// class compiled with ARC
+#define RO_IS_ARC (1<<7)
// class has .cxx_destruct but no .cxx_construct (with RO_HAS_CXX_STRUCTORS)
#define RO_HAS_CXX_DTOR_ONLY (1<<8)
+// class is not ARC but has ARC-style weak ivar layout
+#define RO_HAS_WEAK_WITHOUT_ARC (1<<9)
// class is in an unloadable bundle - must never be set by compiler
#define RO_FROM_BUNDLE (1<<29)
#define RW_CONSTRUCTING (1<<26)
// class allocated and registered
#define RW_CONSTRUCTED (1<<25)
-// GC: class has unsafe finalize method
-#define RW_FINALIZE_ON_MAIN_THREAD (1<<24)
+// available for use; was RW_FINALIZE_ON_MAIN_THREAD
+// #define RW_24 (1<<24)
// class +load has been called
#define RW_LOADED (1<<23)
#if !SUPPORT_NONPOINTER_ISA
// Note this is is stored in the metaclass.
#define RW_HAS_DEFAULT_AWZ (1<<16)
// class's instances requires raw isa
-// not tracked for 32-bit because it only applies to non-pointer isa
-// #define RW_REQUIRES_RAW_ISA
+#if SUPPORT_NONPOINTER_ISA
+#define RW_REQUIRES_RAW_ISA (1<<15)
+#endif
// class is a Swift class
#define FAST_IS_SWIFT (1UL<<0)
// _tryRetain/_isDeallocating/retainWeakReference/allowsWeakReference
#define FAST_HAS_DEFAULT_RR (1UL<<49)
// summary bit for fast alloc path: !hasCxxCtor and
-// !requiresRawIsa and instanceSize fits into shiftedSize
+// !instancesRequireRawIsa and instanceSize fits into shiftedSize
#define FAST_ALLOC (1UL<<50)
// instance size in units of 16 bytes
// or 0 if the instance size is too big in this field
struct class_rw_t {
+ // Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
char *demangledName;
+#if SUPPORT_INDEXED_ISA
+ uint32_t index;
+#endif
+
void setFlags(uint32_t set)
{
OSAtomicOr32Barrier(set, &flags);
#endif
#if FAST_REQUIRES_RAW_ISA
- bool requiresRawIsa() {
+ bool instancesRequireRawIsa() {
return getBit(FAST_REQUIRES_RAW_ISA);
}
- void setRequiresRawIsa() {
+ void setInstancesRequireRawIsa() {
setBits(FAST_REQUIRES_RAW_ISA);
}
+#elif SUPPORT_NONPOINTER_ISA
+ bool instancesRequireRawIsa() {
+ return data()->flags & RW_REQUIRES_RAW_ISA;
+ }
+ void setInstancesRequireRawIsa() {
+ data()->setFlags(RW_REQUIRES_RAW_ISA);
+ }
#else
-# if SUPPORT_NONPOINTER_ISA
-# error oops
-# endif
- bool requiresRawIsa() {
+ bool instancesRequireRawIsa() {
return true;
}
- void setRequiresRawIsa() {
+ void setInstancesRequireRawIsa() {
// nothing
}
#endif
}
#endif
+ void setClassArrayIndex(unsigned Idx) {
+#if SUPPORT_INDEXED_ISA
+ // 0 is unused as then we can rely on zero-initialisation from calloc.
+ assert(Idx > 0);
+ data()->index = Idx;
+#endif
+ }
+
+ unsigned classArrayIndex() {
+#if SUPPORT_INDEXED_ISA
+ return data()->index;
+#else
+ return 0;
+#endif
+ }
+
bool isSwift() {
return getBit(FAST_IS_SWIFT);
}
void setHasCustomAWZ(bool inherited = false);
void printCustomAWZ(bool inherited);
- bool requiresRawIsa() {
- return bits.requiresRawIsa();
+ bool instancesRequireRawIsa() {
+ return bits.instancesRequireRawIsa();
}
- void setRequiresRawIsa(bool inherited = false);
- void printRequiresRawIsa(bool inherited);
+ void setInstancesRequireRawIsa(bool inherited = false);
+ void printInstancesRequireRawIsa(bool inherited);
- bool canAllocIndexed() {
+ bool canAllocNonpointer() {
assert(!isFuture());
- return !requiresRawIsa();
+ return !instancesRequireRawIsa();
}
bool canAllocFast() {
assert(!isFuture());
}
+ // Return YES if the class's ivars are managed by ARC,
+ // or the class is MRC but has ARC-style weak ivars.
+ bool hasAutomaticIvars() {
+ return data()->ro->flags & (RO_IS_ARC | RO_HAS_WEAK_WITHOUT_ARC);
+ }
+
+ // Return YES if the class's ivars are managed by ARC.
+ bool isARC() {
+ return data()->ro->flags & RO_IS_ARC;
+ }
+
+
#if SUPPORT_NONPOINTER_ISA
// Tracked in non-pointer isas; not tracked otherwise
#else
// fixme good or bad for memory use?
}
- bool shouldFinalizeOnMainThread() {
- // finishInitializing() propagates this flag from the superclass.
- assert(isRealized());
- return data()->flags & RW_FINALIZE_ON_MAIN_THREAD;
- }
-
- void setShouldFinalizeOnMainThread() {
- assert(isRealized());
- setInfo(RW_FINALIZE_ON_MAIN_THREAD);
- }
-
bool isInitializing() {
return getMeta()->data()->flags & RW_INITIALIZING;
}
const char *demangledName(bool realize = false);
const char *nameForLogging();
+ // May be unaligned depending on class's ivars.
+ uint32_t unalignedInstanceStart() {
+ assert(isRealized());
+ return data()->ro->instanceStart;
+ }
+
+ // Class's instance start rounded up to a pointer-size boundary.
+ // This is used for ARC layout bitmaps.
+ uint32_t alignedInstanceStart() {
+ return word_align(unalignedInstanceStart());
+ }
+
// May be unaligned depending on class's ivars.
uint32_t unalignedInstanceSize() {
assert(isRealized());
}
bits.setFastInstanceSize(newSize);
}
+
+ void chooseClassArrayIndex();
+
+ void setClassArrayIndex(unsigned Idx) {
+ bits.setClassArrayIndex(Idx);
+ }
+
+ unsigned classArrayIndex() {
+ return bits.classArrayIndex();
+ }
+
};
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
+ // Fields below this point are not always present on disk.
+ struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
- property_list_t *propertiesForMeta(bool isMeta) {
- if (isMeta) return nil; // classProperties;
- else return instanceProperties;
- }
+ property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
struct objc_super2 {
}
}
+// Enumerates a class and all of its realized subclasses.
static inline void
foreach_realized_class_and_subclass(Class top, void (^code)(Class))
{
});
}
+// Enumerates all realized classes and metaclasses.
+extern Class firstRealizedClass();
+static inline void
+foreach_realized_class_and_metaclass(void (^code)(Class))
+{
+ for (Class top = firstRealizedClass();
+ top != nil;
+ top = top->data()->nextSiblingClass)
+ {
+ foreach_realized_class_and_subclass_2(top, ^bool(Class cls) {
+ code(cls); return true;
+ });
+ }
+
+}
+
#endif
static method_t *getMethodNoSuper_nolock(Class cls, SEL sel);
static method_t *getMethod_nolock(Class cls, SEL sel);
static IMP addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace);
-static NXHashTable *realizedClasses(void);
static bool isRRSelector(SEL sel);
static bool isAWZSelector(SEL sel);
static bool methodListImplementsRR(const method_list_t *mlist);
static bool MetaclassNSObjectAWZSwizzled;
static bool ClassNSObjectRRSwizzled;
-#define SDK_FORMAT "%hu.%hhu.%hhu"
-#define FORMAT_SDK(v) \
- (unsigned short)(((uint32_t)(v))>>16), \
- (unsigned char)(((uint32_t)(v))>>8), \
- (unsigned char)(((uint32_t)(v))>>0)
-
-
id objc_noop_imp(id self, SEL _cmd __unused) {
return self;
}
/***********************************************************************
* Non-pointer isa decoding
**********************************************************************/
-#if SUPPORT_NONPOINTER_ISA
+#if SUPPORT_INDEXED_ISA
+
+// Indexed non-pointer isa.
+
+// These are used to mask the ISA and see if its got an index or not.
+const uintptr_t objc_debug_indexed_isa_magic_mask = ISA_INDEX_MAGIC_MASK;
+const uintptr_t objc_debug_indexed_isa_magic_value = ISA_INDEX_MAGIC_VALUE;
+
+// die if masks overlap
+STATIC_ASSERT((ISA_INDEX_MASK & ISA_INDEX_MAGIC_MASK) == 0);
+
+// die if magic is wrong
+STATIC_ASSERT((~ISA_INDEX_MAGIC_MASK & ISA_INDEX_MAGIC_VALUE) == 0);
+
+// Then these are used to extract the index from the ISA.
+const uintptr_t objc_debug_indexed_isa_index_mask = ISA_INDEX_MASK;
+const uintptr_t objc_debug_indexed_isa_index_shift = ISA_INDEX_SHIFT;
+
+asm("\n .globl _objc_absolute_indexed_isa_magic_mask" \
+ "\n _objc_absolute_indexed_isa_magic_mask = " STRINGIFY2(ISA_INDEX_MAGIC_MASK));
+asm("\n .globl _objc_absolute_indexed_isa_magic_value" \
+ "\n _objc_absolute_indexed_isa_magic_value = " STRINGIFY2(ISA_INDEX_MAGIC_VALUE));
+asm("\n .globl _objc_absolute_indexed_isa_index_mask" \
+ "\n _objc_absolute_indexed_isa_index_mask = " STRINGIFY2(ISA_INDEX_MASK));
+asm("\n .globl _objc_absolute_indexed_isa_index_shift" \
+ "\n _objc_absolute_indexed_isa_index_shift = " STRINGIFY2(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.
+// Defined in the objc-msg-*.s files
+// const Class 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.
+// Note, an index of 0 is illegal.
+uintptr_t objc_indexed_classes_count = 0;
+
+// SUPPORT_INDEXED_ISA
+#else
+// not SUPPORT_INDEXED_ISA
+
+// These variables exist but are all set to 0 so that they are ignored.
+const uintptr_t objc_debug_indexed_isa_magic_mask = 0;
+const uintptr_t objc_debug_indexed_isa_magic_value = 0;
+const uintptr_t objc_debug_indexed_isa_index_mask = 0;
+const uintptr_t objc_debug_indexed_isa_index_shift = 0;
+Class objc_indexed_classes[1] = { nil };
+uintptr_t objc_indexed_classes_count = 0;
+
+// not SUPPORT_INDEXED_ISA
+#endif
+
+
+#if SUPPORT_PACKED_ISA
+
+// Packed non-pointer isa.
+
+asm("\n .globl _objc_absolute_packed_isa_class_mask" \
+ "\n _objc_absolute_packed_isa_class_mask = " STRINGIFY2(ISA_MASK));
const uintptr_t objc_debug_isa_class_mask = ISA_MASK;
const uintptr_t objc_debug_isa_magic_mask = ISA_MAGIC_MASK;
STATIC_ASSERT((~ISA_MASK & MACH_VM_MAX_ADDRESS) == 0 ||
ISA_MASK + sizeof(void*) == MACH_VM_MAX_ADDRESS);
+// SUPPORT_PACKED_ISA
#else
+// not SUPPORT_PACKED_ISA
// These variables exist but enforce pointer alignment only.
const uintptr_t objc_debug_isa_class_mask = (~WORD_MASK);
const uintptr_t objc_debug_isa_magic_mask = WORD_MASK;
const uintptr_t objc_debug_isa_magic_value = 0;
+// not SUPPORT_PACKED_ISA
#endif
for (const auto& meth : *mlist) {
SEL s = sel_registerName(sel_cname(meth.name));
- // Don't warn about GC-ignored selectors
- if (ignoreSelector(s)) continue;
-
// Search for replaced methods in method lookup order.
// Complain about the first duplicate only.
// Unique selectors in list.
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name);
-
- SEL sel = sel_registerNameNoLock(name, bundleCopy);
- meth.name = sel;
-
- if (ignoreSelector(sel)) {
- meth.imp = (IMP)&_objc_ignored_method;
- }
+ meth.name = sel_registerNameNoLock(name, bundleCopy);
}
sel_unlock();
if (addedCount == 0) return;
// Don't scan redundantly
- bool scanForCustomRR = !UseGC && !cls->hasCustomRR();
- bool scanForCustomAWZ = !UseGC && !cls->hasCustomAWZ();
+ bool scanForCustomRR = !cls->hasCustomRR();
+ bool scanForCustomAWZ = !cls->hasCustomAWZ();
// There exist RR/AWZ special cases for some class's base methods.
// But this code should never need to scan base methods for RR/AWZ:
fromBundle |= entry.hi->isBundle();
}
- property_list_t *proplist = entry.cat->propertiesForMeta(isMeta);
+ property_list_t *proplist =
+ entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
_objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
cls->nameForLogging(), sel_getName(meth.name));
}
- assert(ignoreSelector(meth.name) ||
- sel_registerName(sel_getName(meth.name)) == meth.name);
+ assert(sel_registerName(sel_getName(meth.name)) == meth.name);
}
#endif
}
// Module name.
const char *prefix;
int prefixLength;
- if (strncmp(string, "Ss", 2) == 0) {
+ if (string[0] == 's') {
+ // "s" is the Swift module.
prefix = "Swift";
prefixLength = 5;
- string += 2;
+ string += 1;
} else {
if (! scanMangledField(string, end, prefix, prefixLength)) return nil;
}
char *name;
if (prefixLength == 5 && memcmp(prefix, "Swift", 5) == 0) {
- asprintf(&name, "_Tt%cSs%zu%.*s%s",
+ asprintf(&name, "_Tt%cs%zu%.*s%s",
isProtocol ? 'P' : 'C',
suffixLength, (int)suffixLength, suffix,
isProtocol ? "_" : "");
}
-/***********************************************************************
-* realizedClasses
-* Returns the class list for realized non-meta classes.
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static NXHashTable *realized_class_hash = nil;
-
-static NXHashTable *realizedClasses(void)
-{
- runtimeLock.assertLocked();
-
- // allocated in _read_images
- assert(realized_class_hash);
-
- return realized_class_hash;
-}
-
-
-/***********************************************************************
-* realizedMetaclasses
-* Returns the class list for realized metaclasses.
-* Locking: runtimeLock must be read- or write-locked by the caller
-**********************************************************************/
-static NXHashTable *realized_metaclass_hash = nil;
-static NXHashTable *realizedMetaclasses(void)
-{
- runtimeLock.assertLocked();
-
- // allocated in _read_images
- assert(realized_metaclass_hash);
-
- return realized_metaclass_hash;
-}
-
-
-/***********************************************************************
-* addRealizedClass
-* Adds cls to the realized non-meta class hash.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void addRealizedClass(Class cls)
-{
- runtimeLock.assertWriting();
- void *old;
- old = NXHashInsert(realizedClasses(), cls);
- objc_addRegisteredClass(cls);
- assert(!cls->isMetaClass());
- assert(!old);
-}
-
-
-/***********************************************************************
-* removeRealizedClass
-* Removes cls from the realized non-meta class hash.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void removeRealizedClass(Class cls)
-{
- runtimeLock.assertWriting();
- if (cls->isRealized()) {
- assert(!cls->isMetaClass());
- NXHashRemove(realizedClasses(), cls);
- objc_removeRegisteredClass(cls);
- }
-}
-
-
-/***********************************************************************
-* addRealizedMetaclass
-* Adds cls to the realized metaclass hash.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void addRealizedMetaclass(Class cls)
-{
- runtimeLock.assertWriting();
- void *old;
- old = NXHashInsert(realizedMetaclasses(), cls);
- assert(cls->isMetaClass());
- assert(!old);
-}
-
-
-/***********************************************************************
-* removeRealizedMetaclass
-* Removes cls from the realized metaclass hash.
-* Locking: runtimeLock must be held by the caller
-**********************************************************************/
-static void removeRealizedMetaclass(Class cls)
-{
- runtimeLock.assertWriting();
- if (cls->isRealized()) {
- assert(cls->isMetaClass());
- NXHashRemove(realizedMetaclasses(), cls);
- }
-}
-
-
/***********************************************************************
* futureNamedClasses
* Returns the classname => future class map for unrealized future classes.
}
+static bool haveFutureNamedClasses() {
+ return future_named_class_map && NXCountMapTable(future_named_class_map);
+}
+
+
/***********************************************************************
* addFutureNamedClass
* Installs cls as the class structure to use for the named class if it appears.
class_rw_t *rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
class_ro_t *ro = (class_ro_t *)calloc(sizeof(class_ro_t), 1);
- ro->name = strdup(name);
+ ro->name = strdupIfMutable(name);
rw->ro = ro;
cls->setData(rw);
cls->data()->flags = RO_FUTURE;
}
+/***********************************************************************
+* addRootClass
+* Adds cls as a new realized root class.
+* Locking: runtimeLock must be held by the caller.
+**********************************************************************/
+static Class _firstRealizedClass = nil;
+Class firstRealizedClass()
+{
+ runtimeLock.assertLocked();
+ return _firstRealizedClass;
+}
+
+static void addRootClass(Class cls)
+{
+ runtimeLock.assertWriting();
+
+ assert(cls->isRealized());
+ cls->data()->nextSiblingClass = _firstRealizedClass;
+ _firstRealizedClass = cls;
+}
+
+static void removeRootClass(Class cls)
+{
+ runtimeLock.assertWriting();
+
+ Class *classp;
+ for (classp = &_firstRealizedClass;
+ *classp != cls;
+ classp = &(*classp)->data()->nextSiblingClass)
+ { }
+
+ *classp = (*classp)->data()->nextSiblingClass;
+}
+
+
/***********************************************************************
* addSubclass
* Adds subcls as a subclass of supercls.
subcls->setHasCustomAWZ(true);
}
- if (supercls->requiresRawIsa()) {
- subcls->setRequiresRawIsa(true);
+ // Special case: instancesRequireRawIsa does not propagate
+ // from root class to root metaclass
+ if (supercls->instancesRequireRawIsa() && supercls->superclass) {
+ subcls->setInstancesRequireRawIsa(true);
}
}
}
/***********************************************************************
* moveIvars
* Slides a class's ivars to accommodate the given superclass size.
-* Also slides ivar and weak GC layouts if provided.
* Ivars are NOT compacted to compensate for a superclass that shrunk.
* Locking: runtimeLock must be held by the caller.
**********************************************************************/
-static void moveIvars(class_ro_t *ro, uint32_t superSize,
- layout_bitmap *ivarBitmap, layout_bitmap *weakBitmap)
+static void moveIvars(class_ro_t *ro, uint32_t superSize)
{
runtimeLock.assertWriting();
// Compute a slide value that preserves that alignment
uint32_t alignMask = maxAlignment - 1;
- if (diff & alignMask) diff = (diff + alignMask) & ~alignMask;
+ diff = (diff + alignMask) & ~alignMask;
// Slide all of this class's ivars en masse
for (const auto& ivar : *ro->ivars) {
ivar.size, ivar.alignment());
}
}
-
- // Slide GC layouts
- uint32_t oldOffset = ro->instanceStart;
- uint32_t newOffset = ro->instanceStart + diff;
-
- if (ivarBitmap) {
- layout_bitmap_slide(ivarBitmap,
- oldOffset >> WORD_SHIFT,
- newOffset >> WORD_SHIFT);
- }
- if (weakBitmap) {
- layout_bitmap_slide(weakBitmap,
- oldOffset >> WORD_SHIFT,
- newOffset >> WORD_SHIFT);
- }
}
*(uint32_t *)&ro->instanceStart += diff;
*(uint32_t *)&ro->instanceSize += diff;
-
- if (!ro->ivars) {
- // No ivars slid, but superclass changed size.
- // Expand bitmap in preparation for layout_bitmap_splat().
- if (ivarBitmap) layout_bitmap_grow(ivarBitmap, ro->instanceSize >> WORD_SHIFT);
- if (weakBitmap) layout_bitmap_grow(weakBitmap, ro->instanceSize >> WORD_SHIFT);
- }
-}
-
-
-/***********************************************************************
-* getIvar
-* Look up an ivar by name.
-* Locking: runtimeLock must be read- or write-locked by the caller.
-**********************************************************************/
-static ivar_t *getIvar(Class cls, const char *name)
-{
- runtimeLock.assertLocked();
-
- const ivar_list_t *ivars;
- assert(cls->isRealized());
- if ((ivars = cls->data()->ro->ivars)) {
- for (auto& ivar : *ivars) {
- if (!ivar.offset) continue; // anonymous bitfield
-
- // ivar.name may be nil for anonymous bitfields etc.
- if (ivar.name && 0 == strcmp(name, ivar.name)) {
- return &ivar;
- }
- }
- }
-
- return nil;
}
*/
// Non-fragile ivars - reconcile this class with its superclass
- layout_bitmap ivarBitmap;
- layout_bitmap weakBitmap;
- bool layoutsChanged = NO;
- bool mergeLayouts = UseGC;
const class_ro_t *super_ro = supercls->data()->ro;
if (DebugNonFragileIvars) {
0 != strcmp(clsname, "NSSimpleCString"))
{
uint32_t oldStart = ro->instanceStart;
- uint32_t oldSize = ro->instanceSize;
class_ro_t *ro_w = make_ro_writeable(rw);
ro = rw->ro;
*ivar.offset -= delta;
}
}
-
- if (mergeLayouts) {
- layout_bitmap layout;
- if (ro->ivarLayout) {
- layout = layout_bitmap_create(ro->ivarLayout,
- oldSize, oldSize, NO);
- layout_bitmap_slide_anywhere(&layout,
- delta >> WORD_SHIFT, 0);
- ro_w->ivarLayout = layout_string_create(layout);
- layout_bitmap_free(layout);
- }
- if (ro->weakIvarLayout) {
- layout = layout_bitmap_create(ro->weakIvarLayout,
- oldSize, oldSize, YES);
- layout_bitmap_slide_anywhere(&layout,
- delta >> WORD_SHIFT, 0);
- ro_w->weakIvarLayout = layout_string_create(layout);
- layout_bitmap_free(layout);
- }
- }
}
}
- if (ro->instanceStart >= super_ro->instanceSize && !mergeLayouts) {
- // Superclass has not overgrown its space, and we don't
- // need to rebuild GC layouts. We're done here.
+ if (ro->instanceStart >= super_ro->instanceSize) {
+ // Superclass has not overgrown its space. We're done here.
return;
}
// fixme can optimize for "class has no new ivars", etc
- if (mergeLayouts) {
- // WARNING: gcc c++ sets instanceStart/Size=0 for classes with
- // no local ivars, but does provide a layout bitmap.
- // Handle that case specially so layout_bitmap_create doesn't die
- // The other ivar sliding code below still works fine, and
- // the final result is a good class.
- if (ro->instanceStart == 0 && ro->instanceSize == 0) {
- // We can't use ro->ivarLayout because we don't know
- // how long it is. Force a new layout to be created.
- if (PrintIvars) {
- _objc_inform("IVARS: instanceStart/Size==0 for class %s; "
- "disregarding ivar layout", cls->nameForLogging());
- }
- ivarBitmap = layout_bitmap_create_empty(super_ro->instanceSize, NO);
- weakBitmap = layout_bitmap_create_empty(super_ro->instanceSize, YES);
- layoutsChanged = YES;
- }
- else {
- ivarBitmap =
- layout_bitmap_create(ro->ivarLayout,
- ro->instanceSize,
- ro->instanceSize, NO);
- weakBitmap =
- layout_bitmap_create(ro->weakIvarLayout,
- ro->instanceSize,
- ro->instanceSize, YES);
- }
- }
-
if (ro->instanceStart < super_ro->instanceSize) {
// Superclass has changed size. This class's ivars must move.
// Also slide layout bits in parallel.
}
class_ro_t *ro_w = make_ro_writeable(rw);
ro = rw->ro;
- moveIvars(ro_w, super_ro->instanceSize,
- mergeLayouts ? &ivarBitmap : nil,
- mergeLayouts ? &weakBitmap : nil);
+ moveIvars(ro_w, super_ro->instanceSize);
gdb_objc_class_changed(cls, OBJC_CLASS_IVARS_CHANGED, ro->name);
- layoutsChanged = YES;
}
-
- if (mergeLayouts) {
- // Check superclass's layout against this class's layout.
- // This needs to be done even if the superclass is not bigger.
- layout_bitmap superBitmap;
-
- superBitmap = layout_bitmap_create(super_ro->ivarLayout,
- super_ro->instanceSize,
- super_ro->instanceSize, NO);
- layoutsChanged |= layout_bitmap_splat(ivarBitmap, superBitmap,
- ro->instanceStart);
- layout_bitmap_free(superBitmap);
-
- // check the superclass' weak layout.
- superBitmap = layout_bitmap_create(super_ro->weakIvarLayout,
- super_ro->instanceSize,
- super_ro->instanceSize, YES);
- layoutsChanged |= layout_bitmap_splat(weakBitmap, superBitmap,
- ro->instanceStart);
- layout_bitmap_free(superBitmap);
-
- // Rebuild layout strings if necessary.
- if (layoutsChanged) {
- if (PrintIvars) {
- _objc_inform("IVARS: gc layout changed for class %s",
- cls->nameForLogging());
- }
- class_ro_t *ro_w = make_ro_writeable(rw);
- ro = rw->ro;
- if (DebugNonFragileIvars) {
- try_free(ro_w->ivarLayout);
- try_free(ro_w->weakIvarLayout);
- }
- ro_w->ivarLayout = layout_string_create(ivarBitmap);
- ro_w->weakIvarLayout = layout_string_create(weakBitmap);
- }
-
- layout_bitmap_free(ivarBitmap);
- layout_bitmap_free(weakBitmap);
- }
}
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
+
+ // Choose an index for this class.
+ // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
+ cls->chooseClassArrayIndex();
+
if (PrintConnecting) {
- _objc_inform("CLASS: realizing class '%s' %s %p %p",
- cls->nameForLogging(), isMeta ? "(meta)" : "",
- (void*)cls, ro);
+ _objc_inform("CLASS: realizing class '%s'%s %p %p #%u",
+ cls->nameForLogging(), isMeta ? " (meta)" : "",
+ (void*)cls, ro, cls->classArrayIndex());
}
// Realize superclass and metaclass, if they aren't already.
// This needs to be done after RW_REALIZED is set above, for root classes.
+ // This needs to be done after class index is chosen, for root metaclasses.
supercls = realizeClass(remapClass(cls->superclass));
metacls = realizeClass(remapClass(cls->ISA()));
+#if SUPPORT_NONPOINTER_ISA
+ // Disable non-pointer isa for some classes and/or platforms.
+ // Set instancesRequireRawIsa.
+ bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
+ bool rawIsaIsInherited = false;
+ static bool hackedDispatch = false;
+
+ if (DisableNonpointerIsa) {
+ // Non-pointer isa disabled by environment or app SDK version
+ instancesRequireRawIsa = true;
+ }
+ else if (!hackedDispatch && !(ro->flags & RO_META) &&
+ 0 == strcmp(ro->name, "OS_object"))
+ {
+ // hack for libdispatch et al - isa also acts as vtable pointer
+ hackedDispatch = true;
+ instancesRequireRawIsa = true;
+ }
+ else if (supercls && supercls->superclass &&
+ supercls->instancesRequireRawIsa())
+ {
+ // This is also propagated by addSubclass()
+ // but nonpointer isa setup needs it earlier.
+ // Special case: instancesRequireRawIsa does not propagate
+ // from root class to root metaclass
+ instancesRequireRawIsa = true;
+ rawIsaIsInherited = true;
+ }
+
+ if (instancesRequireRawIsa) {
+ cls->setInstancesRequireRawIsa(rawIsaIsInherited);
+ }
+// SUPPORT_NONPOINTER_ISA
+#endif
+
// Update superclass and metaclass in case of remapping
cls->superclass = supercls;
cls->initClassIsa(metacls);
}
}
- // Disable non-pointer isa for some classes and/or platforms.
-#if SUPPORT_NONPOINTER_ISA
- {
- bool disable = false;
- static bool hackedDispatch = false;
-
- if (DisableIndexedIsa) {
- // Non-pointer isa disabled by environment or GC or app SDK version
- disable = true;
- }
- else if (!hackedDispatch && !(ro->flags & RO_META) &&
- 0 == strcmp(ro->name, "OS_object"))
- {
- // hack for libdispatch et al - isa also acts as vtable pointer
- hackedDispatch = true;
- disable = true;
- }
-
- if (disable) {
- cls->setRequiresRawIsa(false/*inherited*/);
- }
- }
-#endif
-
// Connect this class to its superclass's subclass lists
if (supercls) {
addSubclass(supercls, cls);
+ } else {
+ addRootClass(cls);
}
// Attach categories
methodizeClass(cls);
- if (!isMeta) {
- addRealizedClass(cls);
- } else {
- addRealizedMetaclass(cls);
- }
-
return cls;
}
size_t count, i;
classref_t *classlist;
- if (hi->allClassesRealized) return;
+ if (hi->areAllClassesRealized()) return;
classlist = _getObjc2ClassList(hi, &count);
realizeClass(remapClass(classlist[i]));
}
- hi->allClassesRealized = YES;
+ hi->setAllClassesRealized(YES);
}
runtimeLock.assertWriting();
header_info *hi;
- for (hi = FirstHeader; hi; hi = hi->next) {
+ for (hi = FirstHeader; hi; hi = hi->getNext()) {
realizeAllClassesInImage(hi);
}
}
foreach_realized_class_and_subclass(cls, ^(Class c){
cache_erase_nolock(c);
});
-
- if (!cls->superclass) {
- // root; metaclasses are subclasses and were flushed above
- } else {
- foreach_realized_class_and_subclass(cls->ISA(), ^(Class c){
- cache_erase_nolock(c);
- });
- }
}
else {
- Class c;
- NXHashTable *classes = realizedClasses();
- NXHashState state = NXInitHashState(classes);
- while (NXNextHashState(classes, &state, (void **)&c)) {
- cache_erase_nolock(c);
- }
- classes = realizedMetaclasses();
- state = NXInitHashState(classes);
- while (NXNextHashState(classes, &state, (void **)&c)) {
+ foreach_realized_class_and_metaclass(^(Class c){
cache_erase_nolock(c);
- }
+ });
}
}
{
rwlock_writer_t lock(runtimeLock);
flushCaches(cls);
+ if (cls && cls->superclass && cls != cls->getIsa()) {
+ flushCaches(cls->getIsa());
+ } else {
+ // cls is a root class or root metaclass. Its metaclass is itself
+ // or a subclass so the metaclass caches were already flushed.
+ }
}
if (!cls) {
*
* Locking: write-locks runtimeLock
**********************************************************************/
-const char *
-map_2_images(enum dyld_image_states state, uint32_t infoCount,
- const struct dyld_image_info infoList[])
+void
+map_2_images(unsigned count, const char * const paths[],
+ const struct mach_header * const mhdrs[])
{
rwlock_writer_t lock(runtimeLock);
- return map_images_nolock(state, infoCount, infoList);
+ return map_images_nolock(count, paths, mhdrs);
}
/***********************************************************************
* load_images
* Process +load in the given images which are being mapped in by dyld.
-* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock and loadMethodLock
**********************************************************************/
-const char *
-load_images(enum dyld_image_states state, uint32_t infoCount,
- const struct dyld_image_info infoList[])
-{
- bool found;
+extern bool hasLoadMethods(const headerType *mhdr);
+extern void prepare_load_methods(const headerType *mhdr);
+void
+load_images(const char *path __unused, const struct mach_header *mh)
+{
// Return without taking locks if there are no +load methods here.
- found = false;
- for (uint32_t i = 0; i < infoCount; i++) {
- if (hasLoadMethods((const headerType *)infoList[i].imageLoadAddress)) {
- found = true;
- break;
- }
- }
- if (!found) return nil;
+ if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
rwlock_writer_t lock2(runtimeLock);
- found = load_images_nolock(state, infoCount, infoList);
+ prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
- if (found) {
- call_load_methods();
- }
-
- return nil;
+ call_load_methods();
}
/***********************************************************************
* unmap_image
* Process the given image which is about to be unmapped by dyld.
-* mh is mach_header instead of headerType because that's what
-* dyld_priv.h says even for 64-bit.
*
* Locking: write-locks runtimeLock and loadMethodLock
**********************************************************************/
void
-unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide)
+unmap_image(const char *path __unused, const struct mach_header *mh)
{
recursive_mutex_locker_t lock(loadMethodLock);
rwlock_writer_t lock2(runtimeLock);
+/***********************************************************************
+* mustReadClasses
+* Preflight check in advance of readClass() from an image.
+**********************************************************************/
+bool mustReadClasses(header_info *hi)
+{
+ const char *reason;
+
+ // If the image is not preoptimized then we must read classes.
+ if (!hi->isPreoptimized()) {
+ reason = nil; // Don't log this one because it is noisy.
+ goto readthem;
+ }
+
+ // If iOS simulator then we must read classes.
+#if TARGET_OS_SIMULATOR
+ reason = "the image is for iOS simulator";
+ goto readthem;
+#endif
+
+ assert(!hi->isBundle()); // no MH_BUNDLE in shared cache
+
+ // If the image may have missing weak superclasses then we must read classes
+ if (!noMissingWeakSuperclasses()) {
+ reason = "the image may contain classes with missing weak superclasses";
+ goto readthem;
+ }
+
+ // If there are unresolved future classes then we must read classes.
+ if (haveFutureNamedClasses()) {
+ reason = "there are unresolved future classes pending";
+ goto readthem;
+ }
+
+ // readClass() does not need to do anything.
+ return NO;
+
+ readthem:
+ if (PrintPreopt && reason) {
+ _objc_inform("PREOPTIMIZATION: reading classes manually from %s "
+ "because %s", hi->fname(), reason);
+ }
+ return YES;
+}
+
+
/***********************************************************************
* readClass
* Read a class and metaclass as written by a compiler.
* - nil (cls has a missing weak-linked superclass)
* - something else (space for this class was reserved by a future class)
*
+* Note that all work performed by this function is preflighted by
+* mustReadClasses(). Do not change this function without updating that one.
+*
* Locking: runtimeLock acquired by map_images or objc_readClassPair
**********************************************************************/
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
// does not bind shared cache absolute symbols as expected.
// This (and the __ARCLite__ hack below) can be removed
// once the simulator drops 10.8 support.
-#if TARGET_IPHONE_SIMULATOR
+#if TARGET_OS_SIMULATOR
if (cls->cache._mask) cls->cache._mask = 0;
if (cls->cache._occupied) cls->cache._occupied = 0;
if (cls->ISA()->cache._mask) cls->ISA()->cache._mask = 0;
memcpy(newCls, cls, sizeof(objc_class));
rw->ro = (class_ro_t *)newCls->data();
newCls->setData(rw);
- free((void *)old_ro->name);
+ freeIfMutable((char *)old_ro->name);
free((void *)old_ro);
addRemappedClass(cls, newCls);
*
* Locking: runtimeLock acquired by map_images
**********************************************************************/
-void _read_images(header_info **hList, uint32_t hCount)
+void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
header_info *hi;
uint32_t hIndex;
#define EACH_HEADER \
hIndex = 0; \
- crashlog_header_name(nil) && hIndex < hCount && (hi = hList[hIndex]) && crashlog_header_name(hi); \
+ hIndex < hCount && (hi = hList[hIndex]); \
hIndex++
if (!doneOnce) {
doneOnce = YES;
#if SUPPORT_NONPOINTER_ISA
+ // Disable non-pointer isa under some conditions.
+
+# if SUPPORT_INDEXED_ISA
+ // Disable nonpointer isa if any image contains old Swift code
+ for (EACH_HEADER) {
+ if (hi->info()->containsSwift() &&
+ hi->info()->swiftVersion() < objc_image_info::SwiftVersion3)
+ {
+ DisableNonpointerIsa = true;
+ if (PrintRawIsa) {
+ _objc_inform("RAW ISA: disabling non-pointer isa because "
+ "the app or a framework contains Swift code "
+ "older than Swift 3.0");
+ }
+ break;
+ }
+ }
+# endif
-# if TARGET_OS_MAC && !TARGET_OS_IPHONE
+# if TARGET_OS_OSX
// Disable non-pointer isa if the app is too old
// (linked before OS X 10.11)
if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_11) {
- DisableIndexedIsa = true;
+ DisableNonpointerIsa = true;
if (PrintRawIsa) {
_objc_inform("RAW ISA: disabling non-pointer isa because "
"the app is too old (SDK version " SDK_FORMAT ")",
// Disable non-pointer isa if the app has a __DATA,__objc_rawisa section
// New apps that load old extensions may need this.
for (EACH_HEADER) {
- if (hi->mhdr->filetype != MH_EXECUTE) continue;
+ if (hi->mhdr()->filetype != MH_EXECUTE) continue;
unsigned long size;
- if (getsectiondata(hi->mhdr, "__DATA", "__objc_rawisa", &size)) {
- DisableIndexedIsa = true;
+ if (getsectiondata(hi->mhdr(), "__DATA", "__objc_rawisa", &size)) {
+ DisableNonpointerIsa = true;
if (PrintRawIsa) {
_objc_inform("RAW ISA: disabling non-pointer isa because "
"the app has a __DATA,__objc_rawisa section");
}
# endif
- // Disable non-pointer isa for all GC apps.
- if (UseGC) {
- DisableIndexedIsa = true;
- if (PrintRawIsa) {
- _objc_inform("RAW ISA: disabling non-pointer isa because "
- "the app is GC");
- }
- }
-
#endif
if (DisableTaggedPointers) {
disableTaggedPointers();
}
- // Count classes. Size various table based on the total.
- int total = 0;
- int unoptimizedTotal = 0;
- for (EACH_HEADER) {
- if (_getObjc2ClassList(hi, &count)) {
- total += (int)count;
- if (!hi->inSharedCache) unoptimizedTotal += count;
- }
- }
-
if (PrintConnecting) {
- _objc_inform("CLASS: found %d classes during launch", total);
+ _objc_inform("CLASS: found %d classes during launch", totalClasses);
}
- // namedClasses (NOT realizedClasses)
+ // namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
int namedClassesSize =
- (isPreoptimized() ? unoptimizedTotal : total) * 4 / 3;
+ (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
-
- // realizedClasses and realizedMetaclasses - less than the full total
- realized_class_hash =
- NXCreateHashTable(NXPtrPrototype, total / 8, nil);
- realized_metaclass_hash =
- NXCreateHashTable(NXPtrPrototype, total / 8, nil);
ts.log("IMAGE TIMES: first time tasks");
}
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
for (EACH_HEADER) {
+ if (! mustReadClasses(hi)) {
+ // Image is sufficiently optimized that we need not call readClass()
+ continue;
+ }
+
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->isPreoptimized();
// Non-lazily realize the class below.
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
- (resolvedFutureClassCount+1)
- * sizeof(Class));
+ (resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
if (PrintVtables) {
_objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
- "call sites in %s", count, hi->fname);
+ "call sites in %s", count, hi->fname());
}
for (i = 0; i < count; i++) {
fixupMessageRef(refs+i);
if (!cls) continue;
// hack for class __ARCLite__, which didn't get this above
-#if TARGET_IPHONE_SIMULATOR
+#if TARGET_OS_SIMULATOR
if (cls->cache._buckets == (void*)&_objc_empty_cache &&
(cls->cache._mask || cls->cache._occupied))
{
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
realizeClass(resolvedFutureClasses[i]);
- resolvedFutureClasses[i]->setRequiresRawIsa(false/*inherited*/);
+ resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
+ bool hasClassProperties = hi->info()->hasCategoryClassProperties();
+
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
}
if (cat->classMethods || cat->protocols
- /* || cat->classProperties */)
+ || (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
for (EACH_HEADER) {
if (hi->isPreoptimized()) {
_objc_inform("PREOPTIMIZATION: honoring preoptimized selectors "
- "in %s", hi->fname);
+ "in %s", hi->fname());
}
- else if (_objcHeaderOptimizedByDyld(hi)) {
+ else if (hi->info()->optimizedByDyld()) {
_objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors "
- "in %s", hi->fname);
+ "in %s", hi->fname());
}
classref_t *classlist = _getObjc2ClassList(hi, &count);
// Unload classes.
- classref_t *classlist = _getObjc2ClassList(hi, &count);
+ // Gather classes from both __DATA,__objc_clslist
+ // and __DATA,__objc_nlclslist. arclite's hack puts a class in the latter
+ // only, and we need to unload that class if we unload an arclite image.
- // First detach classes from each other. Then free each class.
- // This avoid bugs where this loop unloads a subclass before its superclass
+ NXHashTable *classes = NXCreateHashTable(NXPtrPrototype, 0, nil);
+ classref_t *classlist;
+ classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
- if (cls) {
- remove_class_from_loadable_list(cls);
- detach_class(cls->ISA(), YES);
- detach_class(cls, NO);
- }
+ if (cls) NXHashInsert(classes, cls);
}
-
+
+ classlist = _getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
- if (cls) {
- free_class(cls->ISA());
- free_class(cls);
- }
+ if (cls) NXHashInsert(classes, cls);
}
+
+ // First detach classes from each other. Then free each class.
+ // This avoid bugs where this loop unloads a subclass before its superclass
+
+ NXHashState hs;
+ Class cls;
+
+ hs = NXInitHashState(classes);
+ while (NXNextHashState(classes, &hs, (void**)&cls)) {
+ remove_class_from_loadable_list(cls);
+ detach_class(cls->ISA(), YES);
+ detach_class(cls, NO);
+ }
+ hs = NXInitHashState(classes);
+ while (NXNextHashState(classes, &hs, (void**)&cls)) {
+ free_class(cls->ISA());
+ free_class(cls);
+ }
+
+ NXFreeHashTable(classes);
// XXX FIXME -- Clean up protocols:
// <rdar://problem/9033191> Support unloading protocols at dylib/image unload time
if (!m) return nil;
if (!imp) return nil;
- if (ignoreSelector(m->name)) {
- // Ignored methods stay ignored
- return m->imp;
- }
-
IMP old = m->imp;
m->imp = imp;
rwlock_writer_t lock(runtimeLock);
- if (ignoreSelector(m1->name) || ignoreSelector(m2->name)) {
- // Ignored methods stay ignored. Now they're both ignored.
- m1->imp = (IMP)&_objc_ignored_method;
- m2->imp = (IMP)&_objc_ignored_method;
- return;
- }
-
IMP m1_imp = m1->imp;
m1->imp = m2->imp;
m2->imp = m1_imp;
if (!mlist) return;
if (mlist->isFixedUp()) return;
- bool hasExtendedMethodTypes = proto->hasExtendedMethodTypes();
+ const char **extTypes = proto->extendedMethodTypes();
fixupMethodList(mlist, true/*always copy for simplicity*/,
- !hasExtendedMethodTypes/*sort if no ext*/);
+ !extTypes/*sort if no extended method types*/);
- if (hasExtendedMethodTypes) {
+ if (extTypes) {
// Sort method list and extended method types together.
// fixupMethodList() can't do this.
// fixme COW stomp
uint32_t junk;
getExtendedTypesIndexesForMethod(proto, &mlist->get(0),
required, instance, prefix, junk);
- const char **types = proto->extendedMethodTypes;
for (uint32_t i = 0; i < count; i++) {
for (uint32_t j = i+1; j < count; j++) {
method_t& mi = mlist->get(i);
method_t& mj = mlist->get(j);
if (mi.name > mj.name) {
std::swap(mi, mj);
- std::swap(types[prefix+i], types[prefix+j]);
+ std::swap(extTypes[prefix+i], extTypes[prefix+j]);
}
}
}
runtimeLock.assertLocked();
if (!proto) return nil;
- if (!proto->hasExtendedMethodTypes()) return nil;
+ if (!proto->extendedMethodTypes()) return nil;
assert(proto->isFixedUp());
uint32_t i = getExtendedTypesIndexForMethod(proto, m,
isRequiredMethod,
isInstanceMethod);
- return proto->extendedMethodTypes[i];
+ return proto->extendedMethodTypes()[i];
}
// No method with that name. Search incorporated protocols.
const char *
protocol_t::demangledName()
{
- assert(size >= offsetof(protocol_t, _demangledName)+sizeof(_demangledName));
+ assert(hasDemangledNameField());
if (! _demangledName) {
char *de = copySwiftV1DemangledName(mangledName, true/*isProtocol*/);
{
runtimeLock.assertLocked();
- if (!isRequiredProperty || !isInstanceProperty) {
- // Only required instance properties are currently supported
+ if (!isRequiredProperty) {
+ // Only required properties are currently supported.
return nil;
}
- property_list_t *plist;
- if ((plist = proto->instanceProperties)) {
+ property_list_t *plist = isInstanceProperty ?
+ proto->instanceProperties : proto->classProperties();
+ if (plist) {
for (auto& prop : *plist) {
if (0 == strcmp(name, prop.name)) {
return ∝
/***********************************************************************
* protocol_copyPropertyList
+* protocol_copyPropertyList2
* fixme
* Locking: acquires runtimeLock
**********************************************************************/
return result;
}
-objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
+objc_property_t *
+protocol_copyPropertyList2(Protocol *proto, unsigned int *outCount,
+ BOOL isRequiredProperty, BOOL isInstanceProperty)
{
- if (!proto) {
+ if (!proto || !isRequiredProperty) {
+ // Optional properties are not currently supported.
if (outCount) *outCount = 0;
return nil;
}
rwlock_reader_t lock(runtimeLock);
- property_list_t *plist = newprotocol(proto)->instanceProperties;
+ property_list_t *plist = isInstanceProperty
+ ? newprotocol(proto)->instanceProperties
+ : newprotocol(proto)->classProperties();
return (objc_property_t *)copyPropertyList(plist, outCount);
}
+objc_property_t *
+protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
+{
+ return protocol_copyPropertyList2(proto, outCount,
+ YES/*required*/, YES/*instance*/);
+}
+
/***********************************************************************
* protocol_copyProtocolList
result->initProtocolIsa(cls);
result->size = sizeof(protocol_t);
// fixme mangle the name if it looks swift-y?
- result->mangledName = strdup(name);
+ result->mangledName = strdupIfMutable(name);
// fixme reserve name without installing
method_t& meth = list->get(list->count++);
meth.name = name;
- meth.types = strdup(types ? types : "");
+ meth.types = types ? strdupIfMutable(types) : "";
meth.imp = nil;
}
}
property_t& prop = plist->get(plist->count++);
- prop.name = strdup(name);
+ prop.name = strdupIfMutable(name);
prop.attributes = copyPropertyAttributeString(attrs, count);
}
if (isRequiredProperty && isInstanceProperty) {
protocol_addProperty_nolock(proto->instanceProperties, name, attrs, count);
}
- //else if (isRequiredProperty && !isInstanceProperty) {
- // protocol_addProperty_nolock(proto->classProperties, name, attrs, count);
- //} else if (!isRequiredProperty && isInstanceProperty) {
+ else if (isRequiredProperty && !isInstanceProperty) {
+ protocol_addProperty_nolock(proto->_classProperties, name, attrs, count);
+ }
+ //else if (!isRequiredProperty && isInstanceProperty) {
// protocol_addProperty_nolock(proto->optionalInstanceProperties, name, attrs, count);
- //} else /* !isRequiredProperty && !isInstanceProperty) */ {
+ //}
+ //else /* !isRequiredProperty && !isInstanceProperty) */ {
// protocol_addProperty_nolock(proto->optionalClassProperties, name, attrs, count);
//}
}
realizeAllClasses();
- int count;
- Class cls;
- NXHashState state;
- NXHashTable *classes = realizedClasses();
- int allCount = NXCountHashTable(classes);
-
- if (!buffer) {
- return allCount;
- }
+ __block int count = 0;
+ foreach_realized_class_and_metaclass(^(Class cls) {
+ if (!cls->isMetaClass()) count++;
+ });
- count = 0;
- state = NXInitHashState(classes);
- while (count < bufferLen &&
- NXNextHashState(classes, &state, (void **)&cls))
- {
- buffer[count++] = cls;
+ if (buffer) {
+ __block int c = 0;
+ foreach_realized_class_and_metaclass(^(Class cls) {
+ if (c < bufferLen && !cls->isMetaClass()) {
+ buffer[c++] = cls;
+ }
+ });
}
- return allCount;
+ return count;
}
realizeAllClasses();
Class *result = nil;
- NXHashTable *classes = realizedClasses();
- unsigned int count = NXCountHashTable(classes);
+
+ __block unsigned int count = 0;
+ foreach_realized_class_and_metaclass(^(Class cls) {
+ if (!cls->isMetaClass()) count++;
+ });
if (count > 0) {
- Class cls;
- NXHashState state = NXInitHashState(classes);
result = (Class *)malloc((1+count) * sizeof(Class));
- count = 0;
- while (NXNextHashState(classes, &state, (void **)&cls)) {
- result[count++] = cls;
- }
- result[count] = nil;
+ __block unsigned int c = 0;
+ foreach_realized_class_and_metaclass(^(Class cls) {
+ if (!cls->isMetaClass()) {
+ result[c++] = cls;
+ }
+ });
+ result[c] = nil;
}
-
+
if (outCount) *outCount = count;
return result;
}
count = 0;
for (auto& meth : cls->data()->methods) {
- if (! ignoreSelector(meth.name)) {
- result[count++] = &meth;
- }
+ result[count++] = &meth;
}
result[count] = nil;
}
}
+/***********************************************************************
+* category_t::propertiesForMeta
+* Return a category's instance or class properties.
+* hi is the image containing the category.
+**********************************************************************/
+property_list_t *
+category_t::propertiesForMeta(bool isMeta, struct header_info *hi)
+{
+ if (!isMeta) return instanceProperties;
+ else if (hi->info()->hasCategoryClassProperties()) return _classProperties;
+ else return nil;
+}
+
+
/***********************************************************************
* class_copyProtocolList
* fixme
}
-/***********************************************************************
- * _class_getInstanceStart
- * Uses alignedInstanceStart() to ensure that ARR layout strings are
- * interpreted relative to the first word aligned ivar of an object.
- * Locking: none
- **********************************************************************/
-
-static uint32_t
-alignedInstanceStart(Class cls)
-{
- assert(cls);
- assert(cls->isRealized());
- return (uint32_t)word_align(cls->data()->ro->instanceStart);
-}
-
-uint32_t _class_getInstanceStart(Class cls) {
- return alignedInstanceStart(cls);
-}
-
-
/***********************************************************************
* saveTemporaryString
* Save a string in a thread-local FIFO buffer.
* If realize=false, the class must already be realized or future.
* Locking: If realize=true, runtimeLock must be held for writing by the caller.
**********************************************************************/
+static mutex_t DemangleCacheLock;
+static NXHashTable *DemangleCache;
const char *
objc_class::demangledName(bool realize)
{
// Class is not yet realized and name is mangled. Realize the class.
// Only objc_copyClassNamesForImage() should get here.
- runtimeLock.assertWriting();
- assert(realize);
+
+ // fixme lldb's calls to class_getName() can also get here when
+ // interrogating the dyld shared cache. (rdar://27258517)
+ // fixme runtimeLock.assertWriting();
+ // fixme assert(realize);
+
if (realize) {
+ runtimeLock.assertWriting();
realizeClass((Class)this);
data()->demangledName = de;
return de;
- } else {
- return de; // bug - just leak
+ }
+ else {
+ // Save the string to avoid leaks.
+ char *cached;
+ {
+ mutex_locker_t lock(DemangleCacheLock);
+ if (!DemangleCache) {
+ DemangleCache = NXCreateHashTable(NXStrPrototype, 0, nil);
+ }
+ cached = (char *)NXHashInsertIfAbsent(DemangleCache, de);
+ }
+ if (cached != de) free(de);
+ return cached;
}
}
const char *class_getName(Class cls)
{
if (!cls) return "nil";
- assert(cls->isRealized() || cls->isFuture());
+ // fixme lldb calls class_getName() on unrealized classes (rdar://27258517)
+ // assert(cls->isRealized() || cls->isFuture());
return cls->demangledName();
}
retry:
runtimeLock.read();
- // Ignore GC selectors
- if (ignoreSelector(sel)) {
- imp = _objc_ignored_method;
- cache_fill(cls, sel, imp, inst);
- goto done;
- }
-
// Try this class's cache.
imp = cache_getImp(cls, sel);
done:
runtimeLock.unlockRead();
- // paranoia: look for ignored selectors with non-ignored implementations
- assert(!(ignoreSelector(sel) && imp != (IMP)&_objc_ignored_method));
-
- // paranoia: never let uncached leak out
- assert(imp != _objc_msgSend_uncached_impcache);
-
return imp;
}
Method meth;
IMP imp;
- // fixme this is incomplete - no resolver, +initialize, GC -
+ // fixme this is incomplete - no resolver, +initialize -
// but it's only used for .cxx_construct/destruct so we don't care
assert(sel == SEL_cxx_construct || sel == SEL_cxx_destruct);
// Also print custom RR/AWZ because we probably haven't done it yet.
// Special cases:
- // GC's RR and AWZ are never default.
// NSObject AWZ class methods are default.
// NSObject RR instance methods are default.
// updateCustomRR_AWZ() also knows these special cases.
bool inherited;
bool metaCustomAWZ = NO;
- if (UseGC) {
- // GC is always custom AWZ
- metaCustomAWZ = YES;
- inherited = NO;
- }
- else if (MetaclassNSObjectAWZSwizzled) {
+ if (MetaclassNSObjectAWZSwizzled) {
// Somebody already swizzled NSObject's methods
metaCustomAWZ = YES;
inherited = NO;
bool clsCustomRR = NO;
- if (UseGC) {
- // GC is always custom RR
- clsCustomRR = YES;
- inherited = NO;
- }
- else if (ClassNSObjectRRSwizzled) {
+ if (ClassNSObjectRRSwizzled) {
// Somebody already swizzled NSObject's methods
clsCustomRR = YES;
inherited = NO;
}
-/***********************************************************************
- * _class_usesAutomaticRetainRelease
- * Returns YES if class was compiled with -fobjc-arc
- **********************************************************************/
-BOOL _class_usesAutomaticRetainRelease(Class cls)
-{
- return bool(cls->data()->ro->flags & RO_IS_ARR);
-}
-
-
/***********************************************************************
* Return YES if sel is used by retain/release implementors
**********************************************************************/
}
void
-objc_class::printRequiresRawIsa(bool inherited)
+objc_class::printInstancesRequireRawIsa(bool inherited)
{
assert(PrintRawIsa);
- assert(requiresRawIsa());
+ assert(instancesRequireRawIsa());
_objc_inform("RAW ISA: %s%s%s", nameForLogging(),
isMetaClass() ? " (meta)" : "",
inherited ? " (inherited)" : "");
/***********************************************************************
* Mark this class and all of its subclasses as requiring raw isa pointers
**********************************************************************/
-void objc_class::setRequiresRawIsa(bool inherited)
+void objc_class::setInstancesRequireRawIsa(bool inherited)
{
Class cls = (Class)this;
runtimeLock.assertWriting();
- if (requiresRawIsa()) return;
+ if (instancesRequireRawIsa()) return;
foreach_realized_class_and_subclass(cls, ^(Class c){
- if (c->isInitialized()) {
- _objc_fatal("too late to require raw isa");
- return;
- }
- if (c->requiresRawIsa()) {
+ if (c->instancesRequireRawIsa()) {
// fixme short circuit recursion?
return;
}
- c->bits.setRequiresRawIsa();
+ c->bits.setInstancesRequireRawIsa();
- if (PrintRawIsa) c->printRequiresRawIsa(inherited || c != cls);
+ if (PrintRawIsa) c->printInstancesRequireRawIsa(inherited || c != cls);
});
}
+/***********************************************************************
+* Choose a class index.
+* Set instancesRequireRawIsa if no more class indexes are available.
+**********************************************************************/
+void objc_class::chooseClassArrayIndex()
+{
+#if SUPPORT_INDEXED_ISA
+ Class cls = (Class)this;
+ runtimeLock.assertWriting();
+
+ if (objc_indexed_classes_count >= ISA_INDEX_COUNT) {
+ // No more indexes available.
+ assert(cls->classArrayIndex() == 0);
+ cls->setInstancesRequireRawIsa(false/*not inherited*/);
+ return;
+ }
+
+ unsigned index = objc_indexed_classes_count++;
+ if (index == 0) index = objc_indexed_classes_count++; // index 0 is unused
+ classForIndex(index) = cls;
+ cls->setClassArrayIndex(index);
+#endif
+}
+
+
/***********************************************************************
* Update custom RR and AWZ when a method changes its IMP
**********************************************************************/
/***********************************************************************
* class_setIvarLayout
-* Changes the class's GC scan layout.
+* Changes the class's ivar layout.
* nil layout means no unscanned ivars
* The class must be under construction.
* fixme: sanity-check layout vs instance size?
// Can only change layout of in-construction classes.
// note: if modifications to post-construction classes were
- // allowed, there would be a race below (us vs. concurrent GC scan)
+ // allowed, there would be a race below (us vs. concurrent object_setIvar)
if (!(cls->data()->flags & RW_CONSTRUCTING)) {
_objc_inform("*** Can't set ivar layout for already-registered "
"class '%s'", cls->nameForLogging());
/***********************************************************************
* class_setWeakIvarLayout
-* Changes the class's GC weak layout.
+* Changes the class's weak ivar layout.
* nil layout means no weak ivars
* The class must be under construction.
* fixme: sanity-check layout vs instance size?
// Can only change layout of in-construction classes.
// note: if modifications to post-construction classes were
- // allowed, there would be a race below (us vs. concurrent GC scan)
+ // allowed, there would be a race below (us vs. concurrent object_setIvar)
if (!(cls->data()->flags & RW_CONSTRUCTING)) {
_objc_inform("*** Can't set weak ivar layout for already-registered "
"class '%s'", cls->nameForLogging());
}
+/***********************************************************************
+* getIvar
+* Look up an ivar by name.
+* Locking: runtimeLock must be read- or write-locked by the caller.
+**********************************************************************/
+static ivar_t *getIvar(Class cls, const char *name)
+{
+ runtimeLock.assertLocked();
+
+ const ivar_list_t *ivars;
+ assert(cls->isRealized());
+ if ((ivars = cls->data()->ro->ivars)) {
+ for (auto& ivar : *ivars) {
+ if (!ivar.offset) continue; // anonymous bitfield
+
+ // ivar.name may be nil for anonymous bitfields etc.
+ if (ivar.name && 0 == strcmp(name, ivar.name)) {
+ return &ivar;
+ }
+ }
+ }
+
+ return nil;
+}
+
+
+/***********************************************************************
+* _class_getClassForIvar
+* Given a class and an ivar that is in it or one of its superclasses,
+* find the actual class that defined the ivar.
+**********************************************************************/
+Class _class_getClassForIvar(Class cls, Ivar ivar)
+{
+ rwlock_reader_t lock(runtimeLock);
+
+ for ( ; cls; cls = cls->superclass) {
+ if (auto ivars = cls->data()->ro->ivars) {
+ if (ivars->containsIvar(ivar)) {
+ return cls;
+ }
+ }
+ }
+
+ return nil;
+}
+
+
/***********************************************************************
* _class_getVariable
* fixme
* Locking: read-locks runtimeLock
**********************************************************************/
Ivar
-_class_getVariable(Class cls, const char *name, Class *memberOf)
+_class_getVariable(Class cls, const char *name)
{
rwlock_reader_t lock(runtimeLock);
for ( ; cls; cls = cls->superclass) {
ivar_t *ivar = getIvar(cls, name);
if (ivar) {
- if (memberOf) *memberOf = cls;
return ivar;
}
}
(uint32_t)sizeof(method_t) | fixed_up_method_list;
newlist->count = 1;
newlist->first.name = name;
- newlist->first.types = strdup(types);
- if (!ignoreSelector(name)) {
- newlist->first.imp = imp;
- } else {
- newlist->first.imp = (IMP)&_objc_ignored_method;
- }
+ newlist->first.types = strdupIfMutable(types);
+ newlist->first.imp = imp;
prepareMethodLists(cls, &newlist, 1, NO, NO);
cls->data()->methods.attachLists(&newlist, 1);
ivar.offset = (int32_t *)malloc(sizeof(int32_t));
#endif
*ivar.offset = offset;
- ivar.name = name ? strdup(name) : nil;
- ivar.type = strdup(type);
+ ivar.name = name ? strdupIfMutable(name) : nil;
+ ivar.type = strdupIfMutable(type);
ivar.alignment_raw = alignment;
ivar.size = (uint32_t)size;
malloc(sizeof(*proplist));
proplist->count = 1;
proplist->entsizeAndFlags = sizeof(proplist->first);
- proplist->first.name = strdup(name);
+ proplist->first.name = strdupIfMutable(name);
proplist->first.attributes = copyPropertyAttributeString(attrs, count);
cls->data()->properties.attachLists(&proplist, 1);
rw->ro = (class_ro_t *)
memdup(original->data()->ro, sizeof(*original->data()->ro));
- *(char **)&rw->ro->name = strdup(name);
+ *(char **)&rw->ro->name = strdupIfMutable(name);
rw->methods = original->data()->methods.duplicate();
rw->properties = original->data()->properties;
rw->protocols = original->data()->protocols;
+ duplicate->chooseClassArrayIndex();
+
if (duplicate->superclass) {
addSubclass(duplicate->superclass, duplicate);
+ // duplicate->isa == original->isa so don't addSubclass() for it
+ } else {
+ addRootClass(duplicate);
}
// Don't methodize class - construction above is correct
addNamedClass(duplicate, duplicate->data()->ro->name);
- addRealizedClass(duplicate);
- // no: duplicate->ISA == original->ISA
- // addRealizedMetaclass(duplicate->ISA);
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s' (duplicate of %s) %p %p",
runtimeLock.assertWriting();
class_ro_t *cls_ro_w, *meta_ro_w;
-
- cls->cache.initializeToEmpty();
- meta->cache.initializeToEmpty();
cls->setData((class_rw_t *)calloc(sizeof(class_rw_t), 1));
meta->setData((class_rw_t *)calloc(sizeof(class_rw_t), 1));
meta->setInstanceSize(meta_ro_w->instanceStart);
}
- cls_ro_w->name = strdup(name);
- meta_ro_w->name = strdup(name);
+ cls_ro_w->name = strdupIfMutable(name);
+ meta_ro_w->name = strdupIfMutable(name);
cls_ro_w->ivarLayout = &UnsetLayout;
cls_ro_w->weakIvarLayout = &UnsetLayout;
+ meta->chooseClassArrayIndex();
+ cls->chooseClassArrayIndex();
+
// Connect to superclasses and metaclasses
cls->initClassIsa(meta);
if (superclass) {
meta->initClassIsa(meta);
cls->superclass = Nil;
meta->superclass = cls;
+ addRootClass(cls);
addSubclass(cls, meta);
}
+
+ cls->cache.initializeToEmpty();
+ meta->cache.initializeToEmpty();
}
return;
}
- // Build ivar layouts
- if (UseGC) {
- Class supercls = cls->superclass;
- class_ro_t *ro_w = (class_ro_t *)cls->data()->ro;
-
- if (ro_w->ivarLayout != &UnsetLayout) {
- // Class builder already called class_setIvarLayout.
- }
- else if (!supercls) {
- // Root class. Scan conservatively (should be isa ivar only).
- ro_w->ivarLayout = nil;
- }
- else if (ro_w->ivars == nil) {
- // No local ivars. Use superclass's layouts.
- ro_w->ivarLayout =
- ustrdupMaybeNil(supercls->data()->ro->ivarLayout);
- }
- else {
- // Has local ivars. Build layouts based on superclass.
- layout_bitmap bitmap =
- layout_bitmap_create(supercls->data()->ro->ivarLayout,
- supercls->unalignedInstanceSize(),
- cls->unalignedInstanceSize(), NO);
- for (const auto& ivar : *ro_w->ivars) {
- if (!ivar.offset) continue; // anonymous bitfield
-
- layout_bitmap_set_ivar(bitmap, ivar.type, *ivar.offset);
- }
- ro_w->ivarLayout = layout_string_create(bitmap);
- layout_bitmap_free(bitmap);
- }
-
- if (ro_w->weakIvarLayout != &UnsetLayout) {
- // Class builder already called class_setWeakIvarLayout.
- }
- else if (!supercls) {
- // Root class. No weak ivars (should be isa ivar only).
- ro_w->weakIvarLayout = nil;
- }
- else if (ro_w->ivars == nil) {
- // No local ivars. Use superclass's layout.
- ro_w->weakIvarLayout =
- ustrdupMaybeNil(supercls->data()->ro->weakIvarLayout);
- }
- else {
- // Has local ivars. Build layout based on superclass.
- // No way to add weak ivars yet.
- ro_w->weakIvarLayout =
- ustrdupMaybeNil(supercls->data()->ro->weakIvarLayout);
- }
- }
-
// Clear "under construction" bit, set "done constructing" bit
cls->ISA()->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
cls->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
- // Add to named and realized classes
+ // Add to named class table.
addNamedClass(cls, cls->data()->ro->name);
- addRealizedClass(cls);
- addRealizedMetaclass(cls->ISA());
}
Class supercls = cls->superclass;
if (supercls) {
removeSubclass(supercls, cls);
+ } else {
+ removeRootClass(cls);
}
}
// class tables and +load queue
if (!isMeta) {
removeNamedClass(cls, cls->mangledName());
- removeRealizedClass(cls);
- } else {
- removeRealizedMetaclass(cls);
}
}
// Read class's info bits all at once for performance
bool hasCxxCtor = cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
- bool fast = cls->canAllocIndexed();
+ bool fast = cls->canAllocNonpointer();
- if (!UseGC && fast) {
+ if (fast) {
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
obj->initIsa(cls);
// Read class's info bits all at once for performance
bool hasCxxCtor = cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
- bool fast = cls->canAllocIndexed();
+ bool fast = cls->canAllocNonpointer();
size_t size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
- if (!UseGC && !zone && fast) {
+ if (!zone && fast) {
obj = (id)calloc(1, size);
if (!obj) return nil;
obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
-#if SUPPORT_GC
- if (UseGC) {
- obj = (id)auto_zone_allocate_object(gc_zone, size,
- AUTO_OBJECT_SCANNED, 0, 1);
- } else
-#endif
if (zone) {
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
}
if (!obj) return nil;
- // Use non-indexed isa on the assumption that they might be
+ // Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
results, num_requested);
}
-static bool classOrSuperClassesUseARR(Class cls) {
- while (cls) {
- if (_class_usesAutomaticRetainRelease(cls)) return true;
- cls = cls->superclass;
- }
- return false;
-}
-
-static void arr_fixup_copied_references(id newObject, id oldObject)
-{
- // use ARR layouts to correctly copy the references from old object to new, both strong and weak.
- Class cls = oldObject->ISA();
- for ( ; cls; cls = cls->superclass) {
- if (_class_usesAutomaticRetainRelease(cls)) {
- // FIXME: align the instance start to nearest id boundary. This currently handles the case where
- // the the compiler folds a leading BOOL (char, short, etc.) into the alignment slop of a superclass.
- size_t instanceStart = _class_getInstanceStart(cls);
- const uint8_t *strongLayout = class_getIvarLayout(cls);
- if (strongLayout) {
- id *newPtr = (id *)((char*)newObject + instanceStart);
- unsigned char byte;
- while ((byte = *strongLayout++)) {
- unsigned skips = (byte >> 4);
- unsigned scans = (byte & 0x0F);
- newPtr += skips;
- while (scans--) {
- // ensure strong references are properly retained.
- id value = *newPtr++;
- if (value) objc_retain(value);
- }
- }
- }
- const uint8_t *weakLayout = class_getWeakIvarLayout(cls);
- // fix up weak references if any.
- if (weakLayout) {
- id *newPtr = (id *)((char*)newObject + instanceStart), *oldPtr = (id *)((char*)oldObject + instanceStart);
- unsigned char byte;
- while ((byte = *weakLayout++)) {
- unsigned skips = (byte >> 4);
- unsigned weaks = (byte & 0x0F);
- newPtr += skips, oldPtr += skips;
- while (weaks--) {
- *newPtr = nil;
- objc_storeWeak(newPtr, objc_loadWeak(oldPtr));
- ++newPtr, ++oldPtr;
- }
- }
- }
- }
- }
-}
-
/***********************************************************************
* object_copyFromZone
* fixme
uint8_t *copyDst = (uint8_t *)obj + sizeof(Class);
uint8_t *copySrc = (uint8_t *)oldObj + sizeof(Class);
size_t copySize = size - sizeof(Class);
-#if SUPPORT_GC
- objc_memmove_collectable(copyDst, copySrc, copySize);
-#else
memmove(copyDst, copySrc, copySize);
-#endif
-#if SUPPORT_GC
- if (UseGC)
- gc_fixup_weakreferences(obj, oldObj);
- else
-#endif
- if (classOrSuperClassesUseARR(cls))
- arr_fixup_copied_references(obj, oldObj);
+ fixupCopiedIvars(obj, oldObj);
return obj;
}
* objc_destructInstance
* Destroys an instance without freeing memory.
* Calls C++ destructors.
-* Calls ARR ivar cleanup.
+* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
-* Be warned that GC DOES NOT CALL THIS. If you edit this, also edit finalize.
-* CoreFoundation and other clients do call this under GC.
**********************************************************************/
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
- bool assoc = !UseGC && obj->hasAssociatedObjects();
- bool dealloc = !UseGC;
+ bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
- if (dealloc) obj->clearDeallocating();
+ obj->clearDeallocating();
}
return obj;
{
if (!obj) return nil;
- objc_destructInstance(obj);
-
-#if SUPPORT_GC
- if (UseGC) {
- auto_zone_retain(gc_zone, obj); // gc free expects rc==1
- }
-#endif
-
+ objc_destructInstance(obj);
free(obj);
return nil;
* The tag index defines the object's class.
* The payload format is defined by the object's class.
*
+* If the tag index is 0b111, the tagged pointer object uses an
+* "extended" representation, allowing more classes but with smaller payloads:
+* (LSB)
+* 1 bit set if tagged, clear if ordinary object pointer
+* 3 bits 0b111
+* 8 bits extended tag index
+* 52 bits payload
+* (MSB)
+*
+* Some architectures reverse the MSB and LSB in these representations.
+*
* This representation is subject to change. Representation-agnostic SPI is:
* objc-internal.h for class implementers.
* objc-gdb.h for debuggers.
unsigned objc_debug_taggedpointer_payload_rshift = 0;
Class objc_debug_taggedpointer_classes[1] = { nil };
+uintptr_t objc_debug_taggedpointer_ext_mask = 0;
+unsigned objc_debug_taggedpointer_ext_slot_shift = 0;
+uintptr_t objc_debug_taggedpointer_ext_slot_mask = 0;
+unsigned objc_debug_taggedpointer_ext_payload_lshift = 0;
+unsigned objc_debug_taggedpointer_ext_payload_rshift = 0;
+Class objc_debug_taggedpointer_ext_classes[1] = { nil };
+
static void
disableTaggedPointers() { }
// The "slot" used in the class table and given to the debugger
// includes the is-tagged bit. This makes objc_msgSend faster.
+// The "ext" representation doesn't do that.
-uintptr_t objc_debug_taggedpointer_mask = TAG_MASK;
-unsigned objc_debug_taggedpointer_slot_shift = TAG_SLOT_SHIFT;
-uintptr_t objc_debug_taggedpointer_slot_mask = TAG_SLOT_MASK;
-unsigned objc_debug_taggedpointer_payload_lshift = TAG_PAYLOAD_LSHIFT;
-unsigned objc_debug_taggedpointer_payload_rshift = TAG_PAYLOAD_RSHIFT;
+uintptr_t objc_debug_taggedpointer_mask = _OBJC_TAG_MASK;
+unsigned objc_debug_taggedpointer_slot_shift = _OBJC_TAG_SLOT_SHIFT;
+uintptr_t objc_debug_taggedpointer_slot_mask = _OBJC_TAG_SLOT_MASK;
+unsigned objc_debug_taggedpointer_payload_lshift = _OBJC_TAG_PAYLOAD_LSHIFT;
+unsigned objc_debug_taggedpointer_payload_rshift = _OBJC_TAG_PAYLOAD_RSHIFT;
// objc_debug_taggedpointer_classes is defined in objc-msg-*.s
+uintptr_t objc_debug_taggedpointer_ext_mask = _OBJC_TAG_EXT_MASK;
+unsigned objc_debug_taggedpointer_ext_slot_shift = _OBJC_TAG_EXT_SLOT_SHIFT;
+uintptr_t objc_debug_taggedpointer_ext_slot_mask = _OBJC_TAG_EXT_SLOT_MASK;
+unsigned objc_debug_taggedpointer_ext_payload_lshift = _OBJC_TAG_EXT_PAYLOAD_LSHIFT;
+unsigned objc_debug_taggedpointer_ext_payload_rshift = _OBJC_TAG_EXT_PAYLOAD_RSHIFT;
+// objc_debug_taggedpointer_ext_classes is defined in objc-msg-*.s
+
static void
disableTaggedPointers()
{
objc_debug_taggedpointer_slot_mask = 0;
objc_debug_taggedpointer_payload_lshift = 0;
objc_debug_taggedpointer_payload_rshift = 0;
+
+ objc_debug_taggedpointer_ext_mask = 0;
+ objc_debug_taggedpointer_ext_slot_shift = 0;
+ objc_debug_taggedpointer_ext_slot_mask = 0;
+ objc_debug_taggedpointer_ext_payload_lshift = 0;
+ objc_debug_taggedpointer_ext_payload_rshift = 0;
}
-static int
-tagSlotForTagIndex(objc_tag_index_t tag)
+
+// Returns a pointer to the class's storage in the tagged class arrays.
+// Assumes the tag is a valid basic tag.
+static Class *
+classSlotForBasicTagIndex(objc_tag_index_t tag)
{
+ // Array index in objc_tag_classes includes the tagged bit itself
#if SUPPORT_MSB_TAGGED_POINTERS
- return 0x8 | tag;
+ return &objc_tag_classes[0x8 | tag];
#else
- return (tag << 1) | 1;
+ return &objc_tag_classes[(tag << 1) | 1];
#endif
}
+// Returns a pointer to the class's storage in the tagged class arrays,
+// or nil if the tag is out of range.
+static Class *
+classSlotForTagIndex(objc_tag_index_t tag)
+{
+ if (tag >= OBJC_TAG_First60BitPayload && tag <= OBJC_TAG_Last60BitPayload) {
+ return classSlotForBasicTagIndex(tag);
+ }
+
+ if (tag >= OBJC_TAG_First52BitPayload && tag <= OBJC_TAG_Last52BitPayload) {
+ return &objc_tag_ext_classes[tag - OBJC_TAG_First52BitPayload];
+ }
+
+ return nil;
+}
+
+
/***********************************************************************
* _objc_registerTaggedPointerClass
* Set the class to use for the given tagged pointer index.
**********************************************************************/
void
_objc_registerTaggedPointerClass(objc_tag_index_t tag, Class cls)
-{
+{
if (objc_debug_taggedpointer_mask == 0) {
_objc_fatal("tagged pointers are disabled");
}
- if ((unsigned int)tag >= TAG_COUNT) {
- _objc_fatal("tag index %u is too large.", tag);
+ Class *slot = classSlotForTagIndex(tag);
+ if (!slot) {
+ _objc_fatal("tag index %u is invalid", (unsigned int)tag);
}
- int slot = tagSlotForTagIndex(tag);
- Class oldCls = objc_tag_classes[slot];
+ Class oldCls = *slot;
if (cls && oldCls && cls != oldCls) {
_objc_fatal("tag index %u used for two different classes "
cls, cls->nameForLogging());
}
- objc_tag_classes[slot] = cls;
-}
-
+ *slot = cls;
-// Deprecated name.
-void _objc_insert_tagged_isa(unsigned char slotNumber, Class isa)
-{
- return _objc_registerTaggedPointerClass((objc_tag_index_t)slotNumber, isa);
+ // Store a placeholder class in the basic tag slot that is
+ // reserved for the extended tag space, if it isn't set already.
+ // Do this lazily when the first extended tag is registered so
+ // that old debuggers characterize bogus pointers correctly more often.
+ if (tag < OBJC_TAG_First60BitPayload || tag > OBJC_TAG_Last60BitPayload) {
+ Class *extSlot = classSlotForBasicTagIndex(OBJC_TAG_RESERVED_7);
+ if (*extSlot == nil) {
+ extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
+ *extSlot = (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
+ }
+ }
}
Class
_objc_getClassForTag(objc_tag_index_t tag)
{
- if ((unsigned int)tag >= TAG_COUNT) return nil;
- return objc_tag_classes[tagSlotForTagIndex(tag)];
+ Class *slot = classSlotForTagIndex(tag);
+ if (slot) return *slot;
+ else return nil;
}
#endif
{
msg->sel = sel_registerName((const char *)msg->sel);
- if (ignoreSelector(msg->sel)) {
- // ignored selector - bypass dispatcher
- msg->imp = (IMP)&_objc_ignored_method;
- }
- else if (msg->imp == &objc_msgSend_fixup) {
+ if (msg->imp == &objc_msgSend_fixup) {
if (msg->sel == SEL_alloc) {
msg->imp = (IMP)&objc_alloc;
} else if (msg->sel == SEL_allocWithZone) {
// Flush subclass's method caches.
flushCaches(cls);
+ flushCaches(cls->ISA());
return oldSuper;
}
#define CLS_CONSTRUCTING 0x10000
// visibility=hidden
#define CLS_HIDDEN 0x20000
-// GC: class has unsafe finalize method
-#define CLS_FINALIZE_ON_MAIN_THREAD 0x40000
+// available for use; was CLS_FINALIZE_ON_MAIN_THREAD
+#define CLS_40000 0x40000
// Lazy property list arrays
#define CLS_NO_PROPERTY_ARRAY 0x80000
// +load implementation
#define CLS_INSTANCES_HAVE_ASSOCIATED_OBJECTS 0x1000000
// class has instance-specific GC layout
#define CLS_HAS_INSTANCE_SPECIFIC_LAYOUT 0x2000000
+// class compiled with ARC
+#define CLS_IS_ARC 0x4000000
+// class is not ARC but has ARC-style weak ivar layout
+#define CLS_HAS_WEAK_WITHOUT_ARC 0x8000000
// Terminator for array of method lists
#define GETMETA(cls) (ISMETA(cls) ? (cls) : (cls)->ISA())
+struct old_class_ext {
+ uint32_t size;
+ const uint8_t *weak_ivar_layout;
+ struct old_property_list **propertyLists;
+};
+
+struct old_category {
+ char *category_name;
+ char *class_name;
+ struct old_method_list *instance_methods;
+ struct old_method_list *class_methods;
+ struct old_protocol_list *protocols;
+ // Fields below this point are in version 7 or later only.
+ uint32_t size;
+ struct old_property_list *instance_properties;
+ // Check size for fields below this point.
+ struct old_property_list *class_properties;
+
+ bool hasClassPropertiesField() const {
+ return size >= offsetof(old_category, class_properties) + sizeof(class_properties);
+ }
+};
+
+struct old_ivar {
+ char *ivar_name;
+ char *ivar_type;
+ int ivar_offset;
+#ifdef __LP64__
+ int space;
+#endif
+};
+
+struct old_ivar_list {
+ int ivar_count;
+#ifdef __LP64__
+ int space;
+#endif
+ /* variable length structure */
+ struct old_ivar ivar_list[1];
+};
+
+
+struct old_method {
+ SEL method_name;
+ char *method_types;
+ IMP method_imp;
+};
+
+struct old_method_list {
+ void *obsolete;
+
+ int method_count;
+#ifdef __LP64__
+ int space;
+#endif
+ /* variable length structure */
+ struct old_method method_list[1];
+};
+
+struct old_protocol {
+ Class isa;
+ const char *protocol_name;
+ struct old_protocol_list *protocol_list;
+ struct objc_method_description_list *instance_methods;
+ struct objc_method_description_list *class_methods;
+};
+
+struct old_protocol_list {
+ struct old_protocol_list *next;
+ long count;
+ struct old_protocol *list[1];
+};
+
+struct old_protocol_ext {
+ uint32_t size;
+ struct objc_method_description_list *optional_instance_methods;
+ struct objc_method_description_list *optional_class_methods;
+ struct old_property_list *instance_properties;
+ const char **extendedMethodTypes;
+ struct old_property_list *class_properties;
+
+ bool hasClassPropertiesField() const {
+ return size >= offsetof(old_protocol_ext, class_properties) + sizeof(class_properties);
+ }
+};
+
+
+struct old_property {
+ const char *name;
+ const char *attributes;
+};
+
+struct old_property_list {
+ uint32_t entsize;
+ uint32_t count;
+ struct old_property first;
+};
+
+
struct objc_class : objc_object {
Class superclass;
const char *name;
return hasCxxCtor(); // one bit for both ctor and dtor
}
+ // Return YES if the class's ivars are managed by ARC,
+ // or the class is MRC but has ARC-style weak ivars.
+ bool hasAutomaticIvars() {
+ return info & (CLS_IS_ARC | CLS_HAS_WEAK_WITHOUT_ARC);
+ }
+
+ // Return YES if the class's ivars are managed by ARC.
+ bool isARC() {
+ return info & CLS_IS_ARC;
+ }
+
bool hasCustomRR() {
return true;
}
else clearInfo(CLS_GROW_CACHE);
}
- bool shouldFinalizeOnMainThread() {
- return info & CLS_FINALIZE_ON_MAIN_THREAD;
- }
-
- void setShouldFinalizeOnMainThread() {
- setInfo(CLS_FINALIZE_ON_MAIN_THREAD);
- }
-
// +initialize bits are stored on the metaclass only
bool isInitializing() {
return getMeta()->info & CLS_INITIALIZING;
else return this->ISA();
}
+ // May be unaligned depending on class's ivars.
+ uint32_t unalignedInstanceStart() {
+ // This is not simply superclass->instance_size.
+ // superclass->instance_size is padded to its sizeof() boundary,
+ // which may envelop one of this class's ivars.
+ // That in turn would break ARC-style ivar layouts.
+ // Instead, we use the address of this class's first ivar when possible.
+ if (!superclass) return 0;
+ if (!ivars || ivars->ivar_count == 0) return superclass->instance_size;
+ return ivars->ivar_list[0].ivar_offset;
+ }
+
+ // Class's instance start rounded up to a pointer-size boundary.
+ // This is used for ARC layout bitmaps.
+ uint32_t alignedInstanceStart() {
+ return word_align(unalignedInstanceStart());
+ }
+
+
// May be unaligned depending on class's ivars.
uint32_t unalignedInstanceSize() {
return instance_size;
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
- return (unalignedInstanceSize() + WORD_MASK) & ~WORD_MASK;
+ return word_align(unalignedInstanceSize());
}
size_t instanceSize(size_t extraBytes) {
};
-struct old_class_ext {
- uint32_t size;
- const uint8_t *weak_ivar_layout;
- struct old_property_list **propertyLists;
-};
-
-struct old_category {
- char *category_name;
- char *class_name;
- struct old_method_list *instance_methods;
- struct old_method_list *class_methods;
- struct old_protocol_list *protocols;
- uint32_t size;
- struct old_property_list *instance_properties;
-};
-
-struct old_ivar {
- char *ivar_name;
- char *ivar_type;
- int ivar_offset;
-#ifdef __LP64__
- int space;
-#endif
-};
-
-struct old_ivar_list {
- int ivar_count;
-#ifdef __LP64__
- int space;
-#endif
- /* variable length structure */
- struct old_ivar ivar_list[1];
-};
-
-
-struct old_method {
- SEL method_name;
- char *method_types;
- IMP method_imp;
-};
-
-struct old_method_list {
- void *obsolete;
-
- int method_count;
-#ifdef __LP64__
- int space;
-#endif
- /* variable length structure */
- struct old_method method_list[1];
-};
-
-struct old_protocol {
- Class isa;
- const char *protocol_name;
- struct old_protocol_list *protocol_list;
- struct objc_method_description_list *instance_methods;
- struct objc_method_description_list *class_methods;
-};
-
-struct old_protocol_list {
- struct old_protocol_list *next;
- long count;
- struct old_protocol *list[1];
-};
-
-struct old_protocol_ext {
- uint32_t size;
- struct objc_method_description_list *optional_instance_methods;
- struct objc_method_description_list *optional_class_methods;
- struct old_property_list *instance_properties;
- const char **extendedMethodTypes;
-};
-
-
-struct old_property {
- const char *name;
- const char *attributes;
-};
-
-struct old_property_list {
- uint32_t entsize;
- uint32_t count;
- struct old_property first;
-};
-
#include "hashtable2.h"
// Connect superclass pointers.
set_superclass(cls, supercls, YES);
- // Update GC layouts
- // For paranoia, this is a conservative update:
- // only non-strong -> strong and weak -> strong are corrected.
- if (UseGC && supercls &&
- (cls->info & CLS_EXT) && (supercls->info & CLS_EXT))
- {
- bool layoutChanged;
- layout_bitmap ivarBitmap =
- layout_bitmap_create(cls->ivar_layout,
- cls->instance_size,
- cls->instance_size, NO);
-
- layout_bitmap superBitmap =
- layout_bitmap_create(supercls->ivar_layout,
- supercls->instance_size,
- supercls->instance_size, NO);
-
- // non-strong -> strong: bits set in super should be set in sub
- layoutChanged = layout_bitmap_or(ivarBitmap, superBitmap, cls->name);
- layout_bitmap_free(superBitmap);
-
- if (layoutChanged) {
- layout_bitmap weakBitmap = {};
- bool weakLayoutChanged = NO;
-
- if (cls->ext && cls->ext->weak_ivar_layout) {
- // weak -> strong: strong bits should be cleared in weak layout
- // This is a subset of non-strong -> strong
- weakBitmap =
- layout_bitmap_create(cls->ext->weak_ivar_layout,
- cls->instance_size,
- cls->instance_size, YES);
-
- weakLayoutChanged =
- layout_bitmap_clear(weakBitmap, ivarBitmap, cls->name);
- } else {
- // no existing weak ivars, so no weak -> strong changes
- }
-
- // Rebuild layout strings.
- if (PrintIvars) {
- _objc_inform("IVARS: gc layout changed "
- "for class %s (super %s)",
- cls->name, supercls->name);
- if (weakLayoutChanged) {
- _objc_inform("IVARS: gc weak layout changed "
- "for class %s (super %s)",
- cls->name, supercls->name);
- }
- }
- cls->ivar_layout = layout_string_create(ivarBitmap);
- if (weakLayoutChanged) {
- cls->ext->weak_ivar_layout = layout_string_create(weakBitmap);
- }
-
- layout_bitmap_free(weakBitmap);
- }
-
- layout_bitmap_free(ivarBitmap);
- }
-
// Done!
cls->info |= CLS_CONNECTED;
// Update hash tables.
NXHashRemove(unconnected_class_hash, cls);
oldCls = (Class)NXHashInsert(class_hash, cls);
- objc_addRegisteredClass(cls);
// Delete unconnected_class_hash if it is now empty.
if (NXCountHashTable(unconnected_class_hash) == 0) {
// Connect newly-connectable subclasses
resolve_subclasses_of_class(cls);
- // GC debugging: make sure all classes with -dealloc also have -finalize
- if (DebugFinalizers) {
- extern IMP findIMPInClass(Class cls, SEL sel);
- if (findIMPInClass(cls, sel_getUid("dealloc")) &&
- ! findIMPInClass(cls, sel_getUid("finalize")))
- {
- _objc_inform("GC: class '%s' implements -dealloc but not -finalize", cls->name);
- }
- }
-
// Debugging: if this class has ivars, make sure this class's ivars don't
// overlap with its super's. This catches some broken fragile base classes.
// Do not use super->instance_size vs. self->ivar[0] to check this.
size_t midx;
bool needFlush = NO;
- if (_objcHeaderIsReplacement(hi)) {
+ if (hi->info()->isReplacement()) {
// Ignore any categories in this image
return NO;
}
-
// Major loop - process all modules in the header
mods = hi->mod_ptr;
Module mods;
int isBundle = headerIsBundle(hi);
- if (_objcHeaderIsReplacement(hi)) {
+ if (hi->info()->isReplacement()) {
// Ignore any classes in this image
return;
}
// to add lots of classes.
{
mutex_locker_t lock(classLock);
- if (hi->mhdr != libobjc_header && _NXHashCapacity(class_hash) < 1024) {
+ if (hi->mhdr() != libobjc_header && _NXHashCapacity(class_hash) < 1024) {
_NXHashRehashToCapacity(class_hash, 1024);
}
}
unsigned int index;
unsigned int midx;
Module mods;
- bool replacement = _objcHeaderIsReplacement(hi);
+ bool replacement = hi->info()->isReplacement();
// Major loop - process all modules in the image
mods = hi->mod_ptr;
}
-objc_property_t protocol_getProperty(Protocol *p, const char *name,
- BOOL isRequiredProperty, BOOL isInstanceProperty)
+objc_property_t
+protocol_getProperty(Protocol *p, const char *name,
+ BOOL isRequiredProperty, BOOL isInstanceProperty)
{
old_protocol *proto = oldprotocol(p);
old_protocol_ext *ext;
if (!proto || !name) return nil;
- if (!isRequiredProperty || !isInstanceProperty) {
- // Only required instance properties are currently supported
+ if (!isRequiredProperty) {
+ // Only required properties are currently supported
return nil;
}
if ((ext = ext_for_protocol(proto))) {
old_property_list *plist;
- if ((plist = ext->instance_properties)) {
+ if (isInstanceProperty) plist = ext->instance_properties;
+ else if (ext->hasClassPropertiesField()) plist = ext->class_properties;
+ else plist = nil;
+
+ if (plist) {
uint32_t i;
for (i = 0; i < plist->count; i++) {
old_property *prop = property_list_nth(plist, i);
}
-objc_property_t *protocol_copyPropertyList(Protocol *p, unsigned int *outCount)
+objc_property_t *
+protocol_copyPropertyList2(Protocol *p, unsigned int *outCount,
+ BOOL isRequiredProperty, BOOL isInstanceProperty)
{
old_property **result = nil;
old_protocol_ext *ext;
old_property_list *plist;
old_protocol *proto = oldprotocol(p);
- if (! (ext = ext_for_protocol(proto))) {
+ if (! (ext = ext_for_protocol(proto)) || !isRequiredProperty) {
+ // Only required properties are currently supported.
if (outCount) *outCount = 0;
return nil;
}
- plist = ext->instance_properties;
+ if (isInstanceProperty) plist = ext->instance_properties;
+ else if (ext->hasClassPropertiesField()) plist = ext->class_properties;
+ else plist = nil;
+
result = copyPropertyList(plist, outCount);
return (objc_property_t *)result;
}
+objc_property_t *protocol_copyPropertyList(Protocol *p, unsigned int *outCount)
+{
+ return protocol_copyPropertyList2(p, outCount, YES, YES);
+}
+
/***********************************************************************
* protocol_copyProtocolList
objc_allocateProtocol(const char *name)
{
Class cls = objc_getClass("__IncompleteProtocol");
+ assert(cls);
mutex_locker_t lock(classLock);
if (isRequiredProperty && isInstanceProperty) {
_protocol_addProperty(&ext->instance_properties, name, attrs, count);
}
- //else if (isRequiredProperty && !isInstanceProperty) {
- // _protocol_addProperty(&ext->class_properties, name, attrs, count);
- //} else if (!isRequiredProperty && isInstanceProperty) {
+ else if (isRequiredProperty && !isInstanceProperty) {
+ _protocol_addProperty(&ext->class_properties, name, attrs, count);
+ }
+ // else if (!isRequiredProperty && isInstanceProperty) {
// _protocol_addProperty(&ext->optional_instance_properties, name, attrs, count);
- //} else /* !isRequiredProperty && !isInstanceProperty) */ {
+ //}
+ // else /* !isRequiredProperty && !isInstanceProperty) */ {
// _protocol_addProperty(&ext->optional_class_properties, name, attrs, count);
//}
}
SEL *sels;
bool preoptimized = hi->isPreoptimized();
-# if SUPPORT_IGNORED_SELECTOR_CONSTANT
- // shared cache can't fix constant ignored selectors
- if (UseGC) preoptimized = NO;
-# endif
if (PrintPreopt) {
if (preoptimized) {
_objc_inform("PREOPTIMIZATION: honoring preoptimized selectors in %s",
- hi->fname);
+ hi->fname());
}
- else if (_objcHeaderOptimizedByDyld(hi)) {
+ else if (hi->info()->optimizedByDyld()) {
_objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors in %s",
- hi->fname);
+ hi->fname());
}
}
* dyld_priv.h says even for 64-bit.
**********************************************************************/
void
-unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide)
+unmap_image(const char *path __unused, const struct mach_header *mh)
{
recursive_mutex_locker_t lock(loadMethodLock);
unmap_image_nolock(mh);
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
**********************************************************************/
-const char *
-map_2_images(enum dyld_image_states state, uint32_t infoCount,
- const struct dyld_image_info infoList[])
+void
+map_2_images(unsigned count, const char * const paths[],
+ const struct mach_header * const mhdrs[])
{
recursive_mutex_locker_t lock(loadMethodLock);
- return map_images_nolock(state, infoCount, infoList);
+ map_images_nolock(count, paths, mhdrs);
}
/***********************************************************************
* load_images
* Process +load in the given images which are being mapped in by dyld.
-* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: acquires classLock and loadMethodLock
**********************************************************************/
-const char *
-load_images(enum dyld_image_states state, uint32_t infoCount,
- const struct dyld_image_info infoList[])
-{
- bool found;
+extern void prepare_load_methods(const headerType *mhdr);
+void
+load_images(const char *path __unused, const struct mach_header *mh)
+{
recursive_mutex_locker_t lock(loadMethodLock);
// Discover +load methods
- found = load_images_nolock(state, infoCount, infoList);
+ prepare_load_methods((const headerType *)mh);
// Call +load methods (without classLock - re-entrant)
- if (found) {
- call_load_methods();
- }
-
- return nil;
+ call_load_methods();
}
#endif
* _read_images
* Perform metadata processing for hCount images starting with firstNewHeader
**********************************************************************/
-void _read_images(header_info **hList, uint32_t hCount)
+void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClass)
{
uint32_t i;
bool categoriesLoaded = NO;
cls->info |= CLS_LOADED;
}
-bool hasLoadMethods(const headerType *mhdr)
-{
- return true;
-}
-
void prepare_load_methods(const headerType *mhdr)
{
Module mods;
unsigned int midx;
header_info *hi;
- for (hi = FirstHeader; hi; hi = hi->next) {
- if (mhdr == hi->mhdr) break;
+ for (hi = FirstHeader; hi; hi = hi->getNext()) {
+ if (mhdr == hi->mhdr()) break;
}
if (!hi) return;
- if (_objcHeaderIsReplacement(hi)) {
+ if (hi->info()->isReplacement()) {
// Ignore any classes in this image
return;
}
// Remove from class_hash
NXHashRemove(class_hash, cls);
- objc_removeRegisteredClass(cls);
// Free heap memory pointed to by the class
unload_class(cls->ISA());
// Get the location of the dying image's __OBJC segment
uintptr_t seg;
unsigned long seg_size;
- seg = (uintptr_t)getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
+ seg = (uintptr_t)getsegmentdata(hi->mhdr(), "__OBJC", &seg_size);
header_info *other_hi;
- for (other_hi = FirstHeader; other_hi != nil; other_hi = other_hi->next) {
+ for (other_hi = FirstHeader; other_hi != nil; other_hi = other_hi->getNext()) {
Class *other_refs;
size_t count;
if (other_hi == hi) continue; // skip the image being unloaded
// Get the location of the dying image's __OBJC segment
uintptr_t seg;
unsigned long seg_size;
- seg = (uintptr_t)getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
+ seg = (uintptr_t)getsegmentdata(hi->mhdr(), "__OBJC", &seg_size);
_objc_inform("UNLOAD DEBUG: unloading image '%s' [%p..%p]",
- hi->fname, (void *)seg, (void*)(seg+seg_size));
+ hi->fname(), (void *)seg, (void*)(seg+seg_size));
mutex_locker_t lock(classLock);
_objc_remove_classes_in_image(hi);
_objc_remove_categories_in_image(hi);
_objc_remove_pending_class_refs_in_image(hi);
-
+ if (hi->proto_refs) try_free(hi->proto_refs);
+
// Perform various debugging checks if requested.
if (DebugUnload) unload_paranoia(hi);
}
// Add the class to the table
(void) NXHashInsert (class_hash, cls);
- objc_addRegisteredClass(cls);
// Superclass is no longer a leaf for cache flushing
if (cls->superclass && (cls->superclass->info & CLS_LEAF)) {
}
}
- // Augment properties
+ // Augment instance properties
if (version >= 7 && category->instance_properties) {
if (cls->ISA()->version >= 6) {
_class_addProperties(cls, category->instance_properties);
} else {
- _objc_inform ("unable to add properties from category %s...\n", category->category_name);
+ _objc_inform ("unable to add instance properties from category %s...\n", category->category_name);
+ _objc_inform ("class `%s' must be recompiled\n", category->class_name);
+ }
+ }
+
+ // Augment class properties
+ if (version >= 7 && category->hasClassPropertiesField() &&
+ category->class_properties)
+ {
+ if (cls->ISA()->version >= 6) {
+ _class_addProperties(cls->ISA(), category->class_properties);
+ } else {
+ _objc_inform ("unable to add class properties from category %s...\n", category->category_name);
_objc_inform ("class `%s' must be recompiled\n", category->class_name);
}
}
SEL SEL_dealloc = NULL;
SEL SEL_copy = NULL;
SEL SEL_new = NULL;
-SEL SEL_finalize = NULL;
SEL SEL_forwardInvocation = NULL;
SEL SEL_tryRetain = NULL;
SEL SEL_isDeallocating = NULL;
// Add the header to the header list.
// The header is appended to the list, to preserve the bottom-up order.
HeaderCount++;
- hi->next = NULL;
+ hi->setNext(NULL);
if (!FirstHeader) {
// list is empty
FirstHeader = LastHeader = hi;
if (!LastHeader) {
// list is not empty, but LastHeader is invalid - recompute it
LastHeader = FirstHeader;
- while (LastHeader->next) LastHeader = LastHeader->next;
+ while (LastHeader->getNext()) LastHeader = LastHeader->getNext();
}
// LastHeader is now valid
- LastHeader->next = hi;
+ LastHeader->setNext(hi);
LastHeader = hi;
}
}
**********************************************************************/
void removeHeader(header_info *hi)
{
- header_info **hiP;
-
- for (hiP = &FirstHeader; *hiP != NULL; hiP = &(**hiP).next) {
- if (*hiP == hi) {
- header_info *deadHead = *hiP;
-
- // Remove from the linked list (updating FirstHeader if necessary).
- *hiP = (**hiP).next;
+ header_info *prev = NULL;
+ header_info *current = NULL;
+
+ for (current = FirstHeader; current != NULL; current = current->getNext()) {
+ if (current == hi) {
+ header_info *deadHead = current;
+
+ // Remove from the linked list.
+ if (prev)
+ prev->setNext(current->getNext());
+ else
+ FirstHeader = current->getNext(); // no prev so removing head
// Update LastHeader if necessary.
if (LastHeader == deadHead) {
HeaderCount--;
break;
}
+ prev = current;
}
}
const char **names = (const char **)calloc(max+1, sizeof(char *));
#endif
- for (hi = FirstHeader; hi != NULL && count < max; hi = hi->next) {
+ for (hi = FirstHeader; hi != NULL && count < max; hi = hi->getNext()) {
#if TARGET_OS_WIN32
if (hi->moduleName) {
names[count++] = hi->moduleName;
}
#else
- if (hi->fname) {
- names[count++] = hi->fname;
+ const char *fname = hi->fname();
+ if (fname) {
+ names[count++] = fname;
}
#endif
}
}
// Find the image.
- for (hi = FirstHeader; hi != NULL; hi = hi->next) {
+ for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
#if TARGET_OS_WIN32
if (0 == wcscmp((TCHAR *)image, hi->moduleName)) break;
#else
- if (0 == strcmp(image, hi->fname)) break;
+ if (0 == strcmp(image, hi->fname())) break;
#endif
}
* Associative Reference Support
**********************************************************************/
-id objc_getAssociatedObject_non_gc(id object, const void *key) {
+id objc_getAssociatedObject(id object, const void *key) {
return _object_get_associative_reference(object, (void *)key);
}
-void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy) {
+void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
-#if SUPPORT_GC
+void objc_removeAssociatedObjects(id object)
+{
+ if (object && object->hasAssociatedObjects()) {
+ _object_remove_assocations(object);
+ }
+}
-id objc_getAssociatedObject_gc(id object, const void *key) {
- // auto_zone doesn't handle tagged pointer objects. Track it ourselves.
- if (object->isTaggedPointer()) return objc_getAssociatedObject_non_gc(object, key);
- return (id)auto_zone_get_associative_ref(gc_zone, object, (void *)key);
-}
-void objc_setAssociatedObject_gc(id object, const void *key, id value, objc_AssociationPolicy policy) {
- // auto_zone doesn't handle tagged pointer objects. Track it ourselves.
- if (object->isTaggedPointer()) return objc_setAssociatedObject_non_gc(object, key, value, policy);
+#if SUPPORT_GC_COMPAT
+
+#include <mach-o/fat.h>
+
+// GC preflight for an app executable.
+
+enum GCness {
+ WithGC = 1,
+ WithoutGC = 0,
+ Error = -1
+};
+
+// Overloaded template wrappers around clang's overflow-checked arithmetic.
+
+template <typename T> bool uadd_overflow(T x, T y, T* sum);
+template <typename T> bool usub_overflow(T x, T y, T* diff);
+template <typename T> bool umul_overflow(T x, T y, T* prod);
+
+template <typename T> bool sadd_overflow(T x, T y, T* sum);
+template <typename T> bool ssub_overflow(T x, T y, T* diff);
+template <typename T> bool smul_overflow(T x, T y, T* prod);
+
+template <> bool uadd_overflow(unsigned x, unsigned y, unsigned* sum) { return __builtin_uadd_overflow(x, y, sum); }
+template <> bool uadd_overflow(unsigned long x, unsigned long y, unsigned long* sum) { return __builtin_uaddl_overflow(x, y, sum); }
+template <> bool uadd_overflow(unsigned long long x, unsigned long long y, unsigned long long* sum) { return __builtin_uaddll_overflow(x, y, sum); }
+
+template <> bool usub_overflow(unsigned x, unsigned y, unsigned* diff) { return __builtin_usub_overflow(x, y, diff); }
+template <> bool usub_overflow(unsigned long x, unsigned long y, unsigned long* diff) { return __builtin_usubl_overflow(x, y, diff); }
+template <> bool usub_overflow(unsigned long long x, unsigned long long y, unsigned long long* diff) { return __builtin_usubll_overflow(x, y, diff); }
+
+template <> bool umul_overflow(unsigned x, unsigned y, unsigned* prod) { return __builtin_umul_overflow(x, y, prod); }
+template <> bool umul_overflow(unsigned long x, unsigned long y, unsigned long* prod) { return __builtin_umull_overflow(x, y, prod); }
+template <> bool umul_overflow(unsigned long long x, unsigned long long y, unsigned long long* prod) { return __builtin_umulll_overflow(x, y, prod); }
+
+template <> bool sadd_overflow(signed x, signed y, signed* sum) { return __builtin_sadd_overflow(x, y, sum); }
+template <> bool sadd_overflow(signed long x, signed long y, signed long* sum) { return __builtin_saddl_overflow(x, y, sum); }
+template <> bool sadd_overflow(signed long long x, signed long long y, signed long long* sum) { return __builtin_saddll_overflow(x, y, sum); }
+
+template <> bool ssub_overflow(signed x, signed y, signed* diff) { return __builtin_ssub_overflow(x, y, diff); }
+template <> bool ssub_overflow(signed long x, signed long y, signed long* diff) { return __builtin_ssubl_overflow(x, y, diff); }
+template <> bool ssub_overflow(signed long long x, signed long long y, signed long long* diff) { return __builtin_ssubll_overflow(x, y, diff); }
+
+template <> bool smul_overflow(signed x, signed y, signed* prod) { return __builtin_smul_overflow(x, y, prod); }
+template <> bool smul_overflow(signed long x, signed long y, signed long* prod) { return __builtin_smull_overflow(x, y, prod); }
+template <> bool smul_overflow(signed long long x, signed long long y, signed long long* prod) { return __builtin_smulll_overflow(x, y, prod); }
+
+
+// Range-checking subview of a file.
+class FileSlice {
+ int fd;
+ uint64_t sliceOffset;
+ uint64_t sliceSize;
+
+public:
+ FileSlice() : fd(-1), sliceOffset(0), sliceSize(0) { }
+
+ FileSlice(int newfd, uint64_t newOffset, uint64_t newSize)
+ : fd(newfd) , sliceOffset(newOffset) , sliceSize(newSize) { }
+
+ // Read bytes from this slice.
+ // Returns YES if all bytes were read successfully.
+ bool pread(void *buf, uint64_t readSize, uint64_t readOffset = 0) {
+ uint64_t readEnd;
+ if (uadd_overflow(readOffset, readSize, &readEnd)) return NO;
+ if (readEnd > sliceSize) return NO;
- if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) {
- value = ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
+ uint64_t preadOffset;
+ if (uadd_overflow(sliceOffset, readOffset, &preadOffset)) return NO;
+
+ int64_t readed = ::pread(fd, buf, (size_t)readSize, preadOffset);
+ if (readed < 0 || (uint64_t)readed != readSize) return NO;
+ return YES;
}
- auto_zone_set_associative_ref(gc_zone, object, (void *)key, value);
-}
-// objc_setAssociatedObject and objc_getAssociatedObject are
-// resolver functions in objc-auto.mm.
+ // Create a new slice that is a subset of this slice.
+ // Returnes YES if successful.
+ bool slice(uint64_t newOffset, uint64_t newSize, FileSlice& result) {
+ // fixme arithmetic overflow
+ uint64_t newEnd;
+ if (uadd_overflow(newOffset, newSize, &newEnd)) return NO;
+ if (newEnd > sliceSize) return NO;
-#else
+ if (uadd_overflow(sliceOffset, newOffset, &result.sliceOffset)) {
+ return NO;
+ }
+ result.sliceSize = newSize;
+ result.fd = fd;
+ return YES;
+ }
+
+ // Shorten this slice in place by removing a range from the start.
+ bool advance(uint64_t distance) {
+ if (distance > sliceSize) return NO;
+ if (uadd_overflow(sliceOffset, distance, &sliceOffset)) return NO;
+ if (usub_overflow(sliceSize, distance, &sliceSize)) return NO;
+ return YES;
+ }
+};
+
+
+// Arch32 and Arch64 are used to specialize sliceRequiresGC()
+// to interrogate old-ABI i386 and new-ABI x86_64 files.
+
+struct Arch32 {
+ using mh_t = struct mach_header;
+ using segment_command_t = struct segment_command;
+ using section_t = struct section;
+
+ enum : cpu_type_t { cputype = CPU_TYPE_X86 };
+ enum : int { segment_cmd = LC_SEGMENT };
+
+ static bool isObjCSegment(const char *segname) {
+ return segnameEquals(segname, "__OBJC");
+ }
+
+ static bool isImageInfoSection(const char *sectname) {
+ return sectnameEquals(sectname, "__image_info");
+ }
+
+ static bool countClasses(FileSlice file, section_t& sect,
+ int& classCount, int& classrefCount)
+ {
+ if (sectnameEquals(sect.sectname, "__cls_refs")) {
+ classrefCount += sect.size / 4;
+ }
+ else if (sectnameEquals(sect.sectname, "__module_info")) {
+ struct module_t {
+ uint32_t version;
+ uint32_t size;
+ uint32_t name; // not bound
+ uint32_t symtab; // not bound
+ };
+ size_t mod_count = sect.size / sizeof(module_t);
+ if (mod_count == 0) {
+ // no classes defined
+ } else if (mod_count > 1) {
+ // AppleScriptObjC apps only have one module.
+ // Disqualify this app by setting classCount to non-zero.
+ // We don't actually need an accurate count.
+ classCount = 1;
+ } else if (mod_count == 1) {
+ FileSlice moduleSlice;
+ if (!file.slice(sect.offset, sect.size, moduleSlice)) return NO;
+ module_t module;
+ if (!moduleSlice.pread(&module, sizeof(module))) return NO;
+ if (module.symtab) {
+ // AppleScriptObjC apps only have a module with no symtab.
+ // Disqualify this app by setting classCount to non-zero.
+ // We don't actually need an accurate count.
+ classCount = 1;
+ }
+ }
+
+ }
+ return YES;
+ }
+
+};
-id
-objc_getAssociatedObject(id object, const void *key)
+struct Arch64 {
+ using mh_t = struct mach_header_64;
+ using segment_command_t = struct segment_command_64;
+ using section_t = struct section_64;
+
+ enum : cpu_type_t { cputype = CPU_TYPE_X86_64 };
+ enum : int { segment_cmd = LC_SEGMENT_64 };
+
+ static bool isObjCSegment(const char *segname) {
+ return
+ segnameEquals(segname, "__DATA") ||
+ segnameEquals(segname, "__DATA_CONST") ||
+ segnameEquals(segname, "__DATA_DIRTY");
+ }
+
+ static bool isImageInfoSection(const char *sectname) {
+ return sectnameEquals(sectname, "__objc_imageinfo");
+ }
+
+ static bool countClasses(FileSlice, section_t& sect,
+ int& classCount, int& classrefCount)
+ {
+ if (sectnameEquals(sect.sectname, "__objc_classlist")) {
+ classCount += sect.size / 8;
+ }
+ else if (sectnameEquals(sect.sectname, "__objc_classrefs")) {
+ classrefCount += sect.size / 8;
+ }
+ return YES;
+ }
+};
+
+
+#define SANE_HEADER_SIZE (32*1024)
+
+template <typename Arch>
+static int sliceRequiresGC(typename Arch::mh_t mh, FileSlice file)
{
- return objc_getAssociatedObject_non_gc(object, key);
+ // We assume there is only one arch per pointer size that can support GC.
+ // (i386 and x86_64)
+ if (mh.cputype != Arch::cputype) return 0;
+
+ // We only check the main executable.
+ if (mh.filetype != MH_EXECUTE) return 0;
+
+ // Look for ObjC segment.
+ // Look for AppleScriptObjC linkage.
+ FileSlice cmds;
+ if (!file.slice(sizeof(mh), mh.sizeofcmds, cmds)) return Error;
+
+ // Exception: Some AppleScriptObjC apps built for GC can run without GC.
+ // 1. executable defines no classes
+ // 2. executable references NSBundle only
+ // 3. executable links to AppleScriptObjC.framework
+ // Note that shouldRejectGCApp() also knows about this.
+ bool wantsGC = NO;
+ bool linksToAppleScriptObjC = NO;
+ int classCount = 0;
+ int classrefCount = 0;
+
+ // Disallow abusively-large executables that could hang this checker.
+ // dyld performs similar checks (MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE)
+ if (mh.sizeofcmds > SANE_HEADER_SIZE) return Error;
+ if (mh.ncmds > mh.sizeofcmds / sizeof(struct load_command)) return Error;
+
+ for (uint32_t cmdindex = 0; cmdindex < mh.ncmds; cmdindex++) {
+ struct load_command lc;
+ if (!cmds.pread(&lc, sizeof(lc))) return Error;
+
+ // Disallow abusively-small load commands that could hang this checker.
+ // dyld performs a similar check.
+ if (lc.cmdsize < sizeof(lc)) return Error;
+
+ if (lc.cmd == LC_LOAD_DYLIB || lc.cmd == LC_LOAD_UPWARD_DYLIB ||
+ lc.cmd == LC_LOAD_WEAK_DYLIB || lc.cmd == LC_REEXPORT_DYLIB)
+ {
+ // Look for AppleScriptObjC linkage.
+ FileSlice dylibSlice;
+ if (!cmds.slice(0, lc.cmdsize, dylibSlice)) return Error;
+ struct dylib_command dylib;
+ if (!dylibSlice.pread(&dylib, sizeof(dylib))) return Error;
+
+ const char *asoFramework =
+ "/System/Library/Frameworks/AppleScriptObjC.framework"
+ "/Versions/A/AppleScriptObjC";
+ size_t asoLen = strlen(asoFramework);
+
+ FileSlice nameSlice;
+ if (dylibSlice.slice(dylib.dylib.name.offset, asoLen, nameSlice)) {
+ char name[asoLen];
+ if (!nameSlice.pread(name, asoLen)) return Error;
+ if (0 == memcmp(name, asoFramework, asoLen)) {
+ linksToAppleScriptObjC = YES;
+ }
+ }
+ }
+ else if (lc.cmd == Arch::segment_cmd) {
+ typename Arch::segment_command_t seg;
+ if (!cmds.pread(&seg, sizeof(seg))) return Error;
+
+ if (Arch::isObjCSegment(seg.segname)) {
+ // ObjC segment.
+ // Look for image info section.
+ // Look for class implementations and class references.
+ FileSlice sections;
+ if (!cmds.slice(0, seg.cmdsize, sections)) return Error;
+ if (!sections.advance(sizeof(seg))) return Error;
+
+ for (uint32_t segindex = 0; segindex < seg.nsects; segindex++) {
+ typename Arch::section_t sect;
+ if (!sections.pread(§, sizeof(sect))) return Error;
+ if (!Arch::isObjCSegment(sect.segname)) return Error;
+
+ if (!Arch::countClasses(file, sect,
+ classCount, classrefCount))
+ {
+ return Error;
+ }
+
+ if ((sect.flags & SECTION_TYPE) == S_REGULAR &&
+ Arch::isImageInfoSection(sect.sectname))
+ {
+ // ObjC image info section.
+ // Check its contents.
+ FileSlice section;
+ if (!file.slice(sect.offset, sect.size, section)) {
+ return Error;
+ }
+ // The subset of objc_image_info that was in use for GC.
+ struct {
+ uint32_t version;
+ uint32_t flags;
+ } ii;
+ if (!section.pread(&ii, sizeof(ii))) return Error;
+ if (ii.flags & (1<<1)) {
+ // App wants GC.
+ // Don't return yet because we need to
+ // check the AppleScriptObjC exception.
+ wantsGC = YES;
+ }
+ }
+
+ if (!sections.advance(sizeof(sect))) return Error;
+ }
+ }
+ }
+
+ if (!cmds.advance(lc.cmdsize)) return Error;
+ }
+
+ if (!wantsGC) {
+ // No GC bit set.
+ return WithoutGC;
+ }
+ else if (linksToAppleScriptObjC && classCount == 0 && classrefCount == 1) {
+ // Has GC bit but falls under the AppleScriptObjC exception.
+ return WithoutGC;
+ }
+ else {
+ // Has GC bit and is not AppleScriptObjC.
+ return WithGC;
+ }
}
-void
-objc_setAssociatedObject(id object, const void *key, id value,
- objc_AssociationPolicy policy)
+
+static int sliceRequiresGC(FileSlice file)
{
- objc_setAssociatedObject_non_gc(object, key, value, policy);
+ // Read mach-o header.
+ struct mach_header_64 mh;
+ if (!file.pread(&mh, sizeof(mh))) return Error;
+
+ // Check header magic. We assume only host-endian slices can support GC.
+ switch (mh.magic) {
+ case MH_MAGIC:
+ return sliceRequiresGC<Arch32>(*(struct mach_header *)&mh, file);
+ case MH_MAGIC_64:
+ return sliceRequiresGC<Arch64>(mh, file);
+ default:
+ return WithoutGC;
+ }
}
-#endif
-
-void objc_removeAssociatedObjects(id object)
+// Returns 1 if any slice requires GC.
+// Returns 0 if no slice requires GC.
+// Returns -1 on any I/O or file format error.
+int objc_appRequiresGC(int fd)
{
-#if SUPPORT_GC
- if (UseGC) {
- auto_zone_erase_associative_refs(gc_zone, object);
- } else
-#endif
- {
- if (object && object->hasAssociatedObjects()) {
- _object_remove_assocations(object);
+ struct stat st;
+ if (fstat(fd, &st) < 0) return Error;
+
+ FileSlice file(fd, 0, st.st_size);
+
+ // Read fat header, if any.
+ struct fat_header fh;
+
+ if (! file.pread(&fh, sizeof(fh))) return Error;
+
+ int result;
+
+ if (OSSwapBigToHostInt32(fh.magic) == FAT_MAGIC) {
+ // Fat header.
+
+ size_t nfat_arch = OSSwapBigToHostInt32(fh.nfat_arch);
+ // Disallow abusively-large files that could hang this checker.
+ if (nfat_arch > SANE_HEADER_SIZE/sizeof(struct fat_arch)) return Error;
+
+ size_t fat_size;
+ if (umul_overflow(nfat_arch, sizeof(struct fat_arch), &fat_size)) {
+ return Error;
+ }
+
+ FileSlice archlist;
+ if (!file.slice(sizeof(fh), fat_size, archlist)) return Error;
+
+ result = WithoutGC;
+ for (size_t i = 0; i < nfat_arch; i++) {
+ struct fat_arch fa;
+ if (!archlist.pread(&fa, sizeof(fa))) return Error;
+ if (!archlist.advance(sizeof(fa))) return Error;
+
+ FileSlice thin;
+ if (!file.slice(OSSwapBigToHostInt32(fa.offset),
+ OSSwapBigToHostInt32(fa.size), thin))
+ {
+ return Error;
+ }
+ switch (sliceRequiresGC(thin)) {
+ case WithoutGC: break; // no change
+ case WithGC: if (result != Error) result = WithGC; break;
+ case Error: result = Error; break;
+ }
}
}
+ else {
+ // Thin header or not a header.
+ result = sliceRequiresGC(file);
+ }
+
+ return result;
}
+// SUPPORT_GC_COMPAT
+#endif
#endif
if (!key) return (SEL)0;
-#if SUPPORT_IGNORED_SELECTOR_CONSTANT
- if ((uintptr_t)key == kIgnore) return (SEL)kIgnore;
- if (ignoreSelectorNamed(key)) return (SEL)kIgnore;
-#endif
if ('\0' == *key) return (SEL)_objc_empty_selector;
#if SUPPORT_PREOPT
const char *sel_getName(SEL sel) {
-#if SUPPORT_IGNORED_SELECTOR_CONSTANT
- if ((uintptr_t)sel == kIgnore) return "<ignored selector>";
-#endif
return sel ? (const char *)sel : "<null selector>";
}
SEL sel;
if (!name) return NO;
-#if SUPPORT_IGNORED_SELECTOR_CONSTANT
- if ((uintptr_t)name == kIgnore) return YES;
-#endif
sel = _objc_search_builtins((const char *)name);
if (sel) return YES;
* sel_init
* Initialize selector tables and register selectors used internally.
**********************************************************************/
-void sel_init(bool wantsGC, size_t selrefCount)
+void sel_init(size_t selrefCount)
{
// save this value for later
SelrefCount = selrefCount;
// Register selectors used by libobjc
- if (wantsGC) {
- // Registering retain/release/autorelease requires GC decision first.
- // sel_init doesn't actually need the wantsGC parameter, it just
- // helps enforce the initialization order.
- }
-
#define s(x) SEL_##x = sel_registerNameNoLock(#x, NO)
#define t(x,y) SEL_##y = sel_registerNameNoLock(#x, NO)
s(dealloc);
s(copy);
s(new);
- s(finalize);
t(forwardInvocation:, forwardInvocation);
t(_tryRetain, tryRetain);
t(_isDeallocating, isDeallocating);
.align 3
.private_extern __objc_opt_data
__objc_opt_data:
-.long 13 /* table.version */
+.long 15 /* table.version */
+.long 0 /* table.flags */
.long 0 /* table.selopt_offset */
-.long 0 /* table.headeropt_offset */
-.long 0 /* table.clsopt_offset */
-.space PAGE_MAX_SIZE-16
+.long 0 /* table.headeropt_ro_offset */
+.long 0 /* table.clsopt_offset */
+.long 0 /* table.protocolopt_offset */
+.long 0 /* table.headeropt_rw_offset */
+.space PAGE_MAX_SIZE-28
-/* space for selopt, smax/capacity=262144, blen/mask=262143+1 */
+/* space for selopt, smax/capacity=524288, blen/mask=262143+1 */
.space 262144 /* mask tab */
.space 524288 /* checkbytes */
.space 524288*4 /* offsets */
-/* space for clsopt, smax/capacity=32768, blen/mask=16383+1 */
+/* space for clsopt, smax/capacity=65536, blen/mask=16383+1 */
.space 16384 /* mask tab */
-.space 32768 /* checkbytes */
-.space 32768*12 /* offsets to name and class and header_info */
+.space 65536 /* checkbytes */
+.space 65536*12 /* offsets to name and class and header_info */
.space PAGE_MAX_SIZE /* some duplicate classes */
/* space for protocolopt, smax/capacity=8192, blen/mask=4095+1 */
.space 8192 /* checkbytes */
.space 8192*4 /* offsets */
+/* space for header_info (RO) structures */
+.space 16384
.section __DATA,__objc_opt_rw
.align 3
.private_extern __objc_opt_rw_data
__objc_opt_rw_data:
-/* space for header_info structures */
-.space 32768
+/* space for header_info (RW) structures */
+.space 16384
/* space for 8192 protocols */
#if __LP64__
.section __DATA,__objc_opt_ptrs
.align 3
-#if TARGET_OS_MAC && !TARGET_OS_IPHONE && __i386__
+#if TARGET_OS_OSX && __i386__
// old ABI
.globl .objc_class_name_Protocol
PTR(.objc_class_name_Protocol)
static const objc_selopt_t *builtins = NULL;
#endif
-#if SUPPORT_IGNORED_SELECTOR_CONSTANT
-#error sorry
-#endif
-
static size_t SelrefCount = 0;
* sel_init
* Initialize selector tables and register selectors used internally.
**********************************************************************/
-void sel_init(bool wantsGC, size_t selrefCount)
+void sel_init(size_t selrefCount)
{
// save this value for later
SelrefCount = selrefCount;
// Register selectors used by libobjc
- if (wantsGC) {
- // Registering retain/release/autorelease requires GC decision first.
- // sel_init doesn't actually need the wantsGC parameter, it just
- // helps enforce the initialization order.
- }
-
#define s(x) SEL_##x = sel_registerNameNoLock(#x, NO)
#define t(x,y) SEL_##y = sel_registerNameNoLock(#x, NO)
s(dealloc);
s(copy);
s(new);
- s(finalize);
t(forwardInvocation:, forwardInvocation);
t(_tryRetain, tryRetain);
t(_isDeallocating, isDeallocating);
static SEL sel_alloc(const char *name, bool copy)
{
selLock.assertWriting();
- return (SEL)(copy ? strdup(name) : name);
+ return (SEL)(copy ? strdupIfMutable(name) : name);
}
* @return OBJC_SYNC_SUCCESS once lock is acquired.
*/
OBJC_EXPORT int objc_sync_enter(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.3, 2.0, 9.0, 1.0);
/**
* End synchronizing on 'obj'.
* @return OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERROR
*/
OBJC_EXPORT int objc_sync_exit(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_3, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.3, 2.0, 9.0, 1.0);
// The wait/notify functions have never worked correctly and no longer exist.
OBJC_EXPORT int objc_sync_wait(id obj, long long milliSecondsMaxWait)
So, in the hash table, indexed by the weakly referenced item, is a list of
all locations where this address is currently being stored.
-For ARR, we also keep track of whether an arbitrary object is being
+For ARC, we also keep track of whether an arbitrary object is being
deallocated by briefly placing it in the table just prior to invoking
dealloc, and removing it via objc_clear_deallocating just prior to memory
reclamation.
*/
-/// The address of a __weak object reference
-typedef objc_object ** weak_referrer_t;
+// The address of a __weak variable.
+// These pointers are stored disguised so memory analysis tools
+// don't see lots of interior pointers from the weak table into objects.
+typedef DisguisedPtr<objc_object *> weak_referrer_t;
#if __LP64__
-#define PTR_MINUS_1 63
+#define PTR_MINUS_2 62
#else
-#define PTR_MINUS_1 31
+#define PTR_MINUS_2 30
#endif
/**
* The internal structure stored in the weak references table.
* It maintains and stores
* a hash set of weak references pointing to an object.
- * If out_of_line==0, the set is instead a small inline array.
+ * If out_of_line_ness != REFERRERS_OUT_OF_LINE then the set
+ * is instead a small inline array.
*/
#define WEAK_INLINE_COUNT 4
+
+// out_of_line_ness field overlaps with the low two bits of inline_referrers[1].
+// inline_referrers[1] is a DisguisedPtr of a pointer-aligned address.
+// The low two bits of a pointer-aligned DisguisedPtr will always be 0b00
+// (disguised nil or 0x80..00) or 0b11 (any other address).
+// Therefore out_of_line_ness == 0b10 is used to mark the out-of-line state.
+#define REFERRERS_OUT_OF_LINE 2
+
struct weak_entry_t {
DisguisedPtr<objc_object> referent;
union {
struct {
weak_referrer_t *referrers;
- uintptr_t out_of_line : 1;
- uintptr_t num_refs : PTR_MINUS_1;
+ uintptr_t out_of_line_ness : 2;
+ uintptr_t num_refs : PTR_MINUS_2;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
struct {
- // out_of_line=0 is LSB of one of these (don't care which)
+ // out_of_line_ness field is low bits of inline_referrers[1]
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
+
+ bool out_of_line() {
+ return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
+ }
+
+ weak_entry_t& operator=(const weak_entry_t& other) {
+ memcpy(this, &other, sizeof(other));
+ return *this;
+ }
+
+ weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
+ : referent(newReferent)
+ {
+ inline_referrers[0] = newReferrer;
+ for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
+ inline_referrers[i] = nil;
+ }
+ }
};
/**
bool weak_is_registered_no_lock(weak_table_t *weak_table, id referent);
#endif
-/// Assert a weak pointer is valid and retain the object during its use.
-id weak_read_no_lock(weak_table_t *weak_table, id *referrer);
-
/// Called on object destruction. Sets all remaining weak pointers to nil.
void weak_clear_no_lock(weak_table_t *weak_table, id referent);
void objc_weak_error(void)
);
+static void bad_weak_table(weak_entry_t *entries)
+{
+ _objc_fatal("bad weak table at %p. This may be a runtime bug or a "
+ "memory error somewhere else.", entries);
+}
+
/**
* Unique hash function for object pointers only.
*
static void grow_refs_and_insert(weak_entry_t *entry,
objc_object **new_referrer)
{
- assert(entry->out_of_line);
+ assert(entry->out_of_line());
size_t old_size = TABLE_SIZE(entry);
size_t new_size = old_size ? old_size * 2 : 8;
*/
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
- if (! entry->out_of_line) {
+ if (! entry->out_of_line()) {
// Try to insert inline.
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == nil) {
}
entry->referrers = new_referrers;
entry->num_refs = WEAK_INLINE_COUNT;
- entry->out_of_line = 1;
+ entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
entry->mask = WEAK_INLINE_COUNT-1;
entry->max_hash_displacement = 0;
}
- assert(entry->out_of_line);
+ assert(entry->out_of_line());
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
return grow_refs_and_insert(entry, new_referrer);
}
- size_t index = w_hash_pointer(new_referrer) & (entry->mask);
+ size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
+ size_t index = begin;
size_t hash_displacement = 0;
- while (entry->referrers[index] != NULL) {
- index = (index+1) & entry->mask;
+ while (entry->referrers[index] != nil) {
hash_displacement++;
+ index = (index+1) & entry->mask;
+ if (index == begin) bad_weak_table(entry);
}
if (hash_displacement > entry->max_hash_displacement) {
entry->max_hash_displacement = hash_displacement;
*/
static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
- if (! entry->out_of_line) {
+ if (! entry->out_of_line()) {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == old_referrer) {
entry->inline_referrers[i] = nil;
return;
}
- size_t index = w_hash_pointer(old_referrer) & (entry->mask);
+ size_t begin = w_hash_pointer(old_referrer) & (entry->mask);
+ size_t index = begin;
size_t hash_displacement = 0;
while (entry->referrers[index] != old_referrer) {
index = (index+1) & entry->mask;
+ if (index == begin) bad_weak_table(entry);
hash_displacement++;
if (hash_displacement > entry->max_hash_displacement) {
_objc_inform("Attempted to unregister unknown __weak variable "
weak_entry_t *weak_entries = weak_table->weak_entries;
assert(weak_entries != nil);
- size_t index = hash_pointer(new_entry->referent) & (weak_table->mask);
+ size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);
+ size_t index = begin;
size_t hash_displacement = 0;
while (weak_entries[index].referent != nil) {
index = (index+1) & weak_table->mask;
+ if (index == begin) bad_weak_table(weak_entries);
hash_displacement++;
}
static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
{
// remove entry
- if (entry->out_of_line) free(entry->referrers);
+ if (entry->out_of_line()) free(entry->referrers);
bzero(entry, sizeof(*entry));
weak_table->num_entries--;
if (!weak_entries) return nil;
- size_t index = hash_pointer(referent) & weak_table->mask;
+ size_t begin = hash_pointer(referent) & weak_table->mask;
+ size_t index = begin;
size_t hash_displacement = 0;
while (weak_table->weak_entries[index].referent != referent) {
index = (index+1) & weak_table->mask;
+ if (index == begin) bad_weak_table(weak_table->weak_entries);
hash_displacement++;
if (hash_displacement > weak_table->max_hash_displacement) {
return nil;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
remove_referrer(entry, referrer);
bool empty = true;
- if (entry->out_of_line && entry->num_refs != 0) {
+ if (entry->out_of_line() && entry->num_refs != 0) {
empty = false;
}
else {
append_referrer(entry, referrer);
}
else {
- weak_entry_t new_entry;
- new_entry.referent = referent;
- new_entry.out_of_line = 0;
- new_entry.inline_referrers[0] = referrer;
- for (size_t i = 1; i < WEAK_INLINE_COUNT; i++) {
- new_entry.inline_referrers[i] = nil;
- }
-
+ weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
}
weak_referrer_t *referrers;
size_t count;
- if (entry->out_of_line) {
+ if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
weak_entry_remove(weak_table, entry);
}
-
-/**
- * This function gets called when the value of a weak pointer is being
- * used in an expression. Called by objc_loadWeakRetained() which is
- * ultimately called by objc_loadWeak(). The objective is to assert that
- * there is in fact a weak pointer(s) entry for this particular object being
- * stored in the weak-table, and to retain that object so it is not deallocated
- * during the weak pointer's usage.
- *
- * @param weak_table
- * @param referrer The weak pointer address.
- */
-/*
- Once upon a time we eagerly cleared *referrer if we saw the referent
- was deallocating. This confuses code like NSPointerFunctions which
- tries to pre-flight the raw storage and assumes if the storage is
- zero then the weak system is done interfering. That is false: the
- weak system is still going to check and clear the storage later.
- This can cause objc_weak_error complaints and crashes.
- So we now don't touch the storage until deallocation completes.
-*/
-id
-weak_read_no_lock(weak_table_t *weak_table, id *referrer_id)
-{
- objc_object **referrer = (objc_object **)referrer_id;
- objc_object *referent = *referrer;
- if (referent->isTaggedPointer()) return (id)referent;
-
- weak_entry_t *entry;
- if (referent == nil ||
- !(entry = weak_entry_for_referent(weak_table, referent)))
- {
- return nil;
- }
-
- if (! referent->ISA()->hasCustomRR()) {
- if (! referent->rootTryRetain()) {
- return nil;
- }
- }
- else {
- BOOL (*tryRetain)(objc_object *, SEL) = (BOOL(*)(objc_object *, SEL))
- object_getMethodImplementation((id)referent,
- SEL_retainWeakReference);
- if ((IMP)tryRetain == _objc_msgForward) {
- return nil;
- }
- if (! (*tryRetain)(referent, SEL_retainWeakReference)) {
- return nil;
- }
- }
-
- return (id)referent;
-}
-
# endif
#endif
-#if ! (defined(__OBJC_GC__) || __has_feature(objc_arc))
-#define __strong /* empty */
+#ifndef __strong
+# if !__has_feature(objc_arc)
+# define __strong /* empty */
+# endif
#endif
-#if !__has_feature(objc_arc)
-#define __unsafe_unretained /* empty */
-#define __autoreleasing /* empty */
+#ifndef __unsafe_unretained
+# if !__has_feature(objc_arc)
+# define __unsafe_unretained /* empty */
+# endif
+#endif
+
+#ifndef __autoreleasing
+# if !__has_feature(objc_arc)
+# define __autoreleasing /* empty */
+# endif
#endif
* @return A C string indicating the name of the selector.
*/
OBJC_EXPORT const char *sel_getName(SEL sel)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Registers a method with the Objective-C runtime system, maps the method
* has already been registered, this function simply returns the selector.
*/
OBJC_EXPORT SEL sel_registerName(const char *str)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Returns the class name of a given object.
* @return The name of the class of which \e obj is an instance.
*/
OBJC_EXPORT const char *object_getClassName(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Returns a pointer to any extra bytes allocated with an instance given object.
* @note In a garbage-collected environment, the memory is scanned conservatively.
*/
OBJC_EXPORT void *object_getIndexedIvars(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Identifies a selector as being valid or invalid.
* a crash.
*/
OBJC_EXPORT BOOL sel_isMapped(SEL sel)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Registers a method name with the Objective-C runtime system.
* observed that many of the callers of this function did not check the return value for \c NULL.
*/
OBJC_EXPORT SEL sel_getUid(const char *str)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
+typedef const void* objc_objectptr_t;
-// Obsolete ARC conversions. Deprecation forthcoming.
-// Use CFBridgingRetain, CFBridgingRelease, and __bridge casts instead.
-typedef const void* objc_objectptr_t;
+// Obsolete ARC conversions.
-#if __has_feature(objc_arc)
-# define objc_retainedObject(o) ((__bridge_transfer id)(objc_objectptr_t)(o))
-# define objc_unretainedObject(o) ((__bridge id)(objc_objectptr_t)(o))
-# define objc_unretainedPointer(o) ((__bridge objc_objectptr_t)(id)(o))
-#else
-# define objc_retainedObject(o) ((id)(objc_objectptr_t)(o))
-# define objc_unretainedObject(o) ((id)(objc_objectptr_t)(o))
-# define objc_unretainedPointer(o) ((objc_objectptr_t)(id)(o))
-#endif
+OBJC_EXPORT id objc_retainedObject(objc_objectptr_t obj)
+ OBJC_UNAVAILABLE("use CFBridgingRelease() or a (__bridge_transfer id) cast instead");
+OBJC_EXPORT id objc_unretainedObject(objc_objectptr_t obj)
+ OBJC_UNAVAILABLE("use a (__bridge id) cast instead");
+OBJC_EXPORT objc_objectptr_t objc_unretainedPointer(id obj)
+ OBJC_UNAVAILABLE("use a __bridge cast instead");
#if !__OBJC2__
* @return A copy of \e obj.
*/
OBJC_EXPORT id object_copy(id obj, size_t size)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
/**
* @return nil
*/
OBJC_EXPORT id object_dispose(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
/**
* or \c Nil if \e object is \c nil.
*/
OBJC_EXPORT Class object_getClass(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Sets the class of an object.
* @return The previous value of \e object's class, or \c Nil if \e object is \c nil.
*/
OBJC_EXPORT Class object_setClass(id obj, Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* @return true if the object is a class or metaclass, false otherwise.
*/
OBJC_EXPORT BOOL object_isClass(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0);
+ OBJC_AVAILABLE(10.10, 8.0, 9.0, 1.0);
/**
* @return The name of the class of which \e obj is an instance.
*/
OBJC_EXPORT const char *object_getClassName(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Returns a pointer to any extra bytes allocated with an instance given object.
* @note In a garbage-collected environment, the memory is scanned conservatively.
*/
OBJC_EXPORT void *object_getIndexedIvars(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
/**
* for the instance variable is already known.
*/
OBJC_EXPORT id object_getIvar(id obj, Ivar ivar)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Sets the value of an instance variable in an object.
* @param ivar The Ivar describing the instance variable whose value you want to set.
* @param value The new value for the instance variable.
*
+ * @note Instance variables with known memory management (such as ARC strong and weak)
+ * use that memory management. Instance variables with unknown memory management
+ * are assigned as if they were unsafe_unretained.
* @note \c object_setIvar is faster than \c object_setInstanceVariable if the Ivar
* for the instance variable is already known.
*/
OBJC_EXPORT void object_setIvar(id obj, Ivar ivar, id value)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
+
+/**
+ * Sets the value of an instance variable in an object.
+ *
+ * @param obj The object containing the instance variable whose value you want to set.
+ * @param ivar The Ivar describing the instance variable whose value you want to set.
+ * @param value The new value for the instance variable.
+ *
+ * @note Instance variables with known memory management (such as ARC strong and weak)
+ * use that memory management. Instance variables with unknown memory management
+ * are assigned as if they were strong.
+ * @note \c object_setIvar is faster than \c object_setInstanceVariable if the Ivar
+ * for the instance variable is already known.
+ */
+OBJC_EXPORT void object_setIvarWithStrongDefault(id obj, Ivar ivar, id value)
+ OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
/**
* Changes the value of an instance variable of a class instance.
*
* @return A pointer to the \c Ivar data structure that defines the type and
* name of the instance variable specified by \e name.
+ *
+ * @note Instance variables with known memory management (such as ARC strong and weak)
+ * use that memory management. Instance variables with unknown memory management
+ * are assigned as if they were unsafe_unretained.
*/
OBJC_EXPORT Ivar object_setInstanceVariable(id obj, const char *name, void *value)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
+ OBJC_ARC_UNAVAILABLE;
+
+/**
+ * Changes the value of an instance variable of a class instance.
+ *
+ * @param obj A pointer to an instance of a class. Pass the object containing
+ * the instance variable whose value you wish to modify.
+ * @param name A C string. Pass the name of the instance variable whose value you wish to modify.
+ * @param value The new value for the instance variable.
+ *
+ * @return A pointer to the \c Ivar data structure that defines the type and
+ * name of the instance variable specified by \e name.
+ *
+ * @note Instance variables with known memory management (such as ARC strong and weak)
+ * use that memory management. Instance variables with unknown memory management
+ * are assigned as if they were strong.
+ */
+OBJC_EXPORT Ivar object_setInstanceVariableWithStrongDefault(id obj, const char *name, void *value)
+ OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0)
OBJC_ARC_UNAVAILABLE;
/**
* the instance variable specified by \e name.
*/
OBJC_EXPORT Ivar object_getInstanceVariable(id obj, const char *name, void **outValue)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
* terminate the program if the class does not exist.
*/
OBJC_EXPORT Class objc_getClass(const char *name)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Returns the metaclass definition of a specified class.
* whether it’s valid or not.
*/
OBJC_EXPORT Class objc_getMetaClass(const char *name)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Returns the class definition of a specified class.
* time to see whether the class is registered. This function does not call the class handler callback.
*/
OBJC_EXPORT Class objc_lookUpClass(const char *name)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Returns the class definition of a specified class.
* @note This function is used by ZeroLink, where failing to find a class would be a compile-time link error without ZeroLink.
*/
OBJC_EXPORT Class objc_getRequiredClass(const char *name)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Obtains the list of registered class definitions.
* so you cannot safely call any methods on such classes without detecting that the method is implemented first.
*/
OBJC_EXPORT int objc_getClassList(Class *buffer, int bufferCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Creates and returns a list of pointers to all registered class definitions.
* @see objc_getClassList
*/
OBJC_EXPORT Class *objc_copyClassList(unsigned int *outCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_3_1);
+ OBJC_AVAILABLE(10.7, 3.1, 9.0, 1.0);
/* Working with Classes */
* @return The name of the class, or the empty string if \e cls is \c Nil.
*/
OBJC_EXPORT const char *class_getName(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns a Boolean value that indicates whether a class object is a metaclass.
* \c NO if \e cls is \c Nil.
*/
OBJC_EXPORT BOOL class_isMetaClass(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns the superclass of a class.
* @note You should usually use \c NSObject's \c superclass method instead of this function.
*/
OBJC_EXPORT Class class_getSuperclass(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Sets the superclass of a given class.
* @warning You should not use this function.
*/
OBJC_EXPORT Class class_setSuperclass(Class cls, Class newSuper)
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_5,__MAC_10_5, __IPHONE_2_0,__IPHONE_2_0);
+ __OSX_DEPRECATED(10.5, 10.5, "not recommended")
+ __IOS_DEPRECATED(2.0, 2.0, "not recommended")
+ __TVOS_DEPRECATED(9.0, 9.0, "not recommended")
+ __WATCHOS_DEPRECATED(1.0, 1.0, "not recommended");
/**
* Returns the version number of a class definition.
* @see class_setVersion
*/
OBJC_EXPORT int class_getVersion(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Sets the version number of a class definition.
* version number using the \c setVersion: class method, which is implemented using the \c class_setVersion function.
*/
OBJC_EXPORT void class_setVersion(Class cls, int version)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Returns the size of instances of a class.
* @return The size in bytes of instances of the class \e cls, or \c 0 if \e cls is \c Nil.
*/
OBJC_EXPORT size_t class_getInstanceSize(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns the \c Ivar for a specified instance variable of a given class.
* the instance variable specified by \e name.
*/
OBJC_EXPORT Ivar class_getInstanceVariable(Class cls, const char *name)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Returns the Ivar for a specified class variable of a given class.
* @return A pointer to an \c Ivar data structure containing information about the class variable specified by \e name.
*/
OBJC_EXPORT Ivar class_getClassVariable(Class cls, const char *name)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Describes the instance variables declared by a class.
* If the class declares no instance variables, or cls is Nil, NULL is returned and *outCount is 0.
*/
OBJC_EXPORT Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns a specified instance method for a given class.
* @note This function searches superclasses for implementations, whereas \c class_copyMethodList does not.
*/
OBJC_EXPORT Method class_getInstanceMethod(Class cls, SEL name)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Returns a pointer to the data structure describing a given class method for a given class.
* whereas \c class_copyMethodList does not.
*/
OBJC_EXPORT Method class_getClassMethod(Class cls, SEL name)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Returns the function pointer that would be called if a
* the selector, the function pointer returned will be part of the runtime's message forwarding machinery.
*/
OBJC_EXPORT IMP class_getMethodImplementation(Class cls, SEL name)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns the function pointer that would be called if a particular
* with an instance of the class, or \c NULL if \e cls is \c Nil.
*/
OBJC_EXPORT IMP class_getMethodImplementation_stret(Class cls, SEL name)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0)
- OBJC_ARM64_UNAVAILABLE;
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0)
+ OBJC_ARM64_UNAVAILABLE;
/**
* Returns a Boolean value that indicates whether instances of a class respond to a particular selector.
* methods instead of this function.
*/
OBJC_EXPORT BOOL class_respondsToSelector(Class cls, SEL sel)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Describes the instance methods implemented by a class.
* use \c class_getInstanceMethod or \c class_getClassMethod.
*/
OBJC_EXPORT Method *class_copyMethodList(Class cls, unsigned int *outCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns a Boolean value that indicates whether a class conforms to a given protocol.
* @note You should usually use NSObject's conformsToProtocol: method instead of this function.
*/
OBJC_EXPORT BOOL class_conformsToProtocol(Class cls, Protocol *protocol)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Describes the protocols adopted by a class.
* If cls adopts no protocols, or cls is Nil, returns NULL and *outCount is 0.
*/
OBJC_EXPORT Protocol * __unsafe_unretained *class_copyProtocolList(Class cls, unsigned int *outCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns a property with a given name of a given class.
* or \c NULL if \e cls is \c Nil.
*/
OBJC_EXPORT objc_property_t class_getProperty(Class cls, const char *name)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Describes the properties declared by a class.
* If \e cls declares no properties, or \e cls is \c Nil, returns \c NULL and \c *outCount is \c 0.
*/
OBJC_EXPORT objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns a description of the \c Ivar layout for a given class.
* @return A description of the \c Ivar layout for \e cls.
*/
OBJC_EXPORT const uint8_t *class_getIvarLayout(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns a description of the layout of weak Ivars for a given class.
* @return A description of the layout of the weak \c Ivars for \e cls.
*/
OBJC_EXPORT const uint8_t *class_getWeakIvarLayout(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Adds a new method to a class with a given name and implementation.
*/
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp,
const char *types)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Replaces the implementation of a method for a given class.
*/
OBJC_EXPORT IMP class_replaceMethod(Class cls, SEL name, IMP imp,
const char *types)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Adds a new instance variable to a class.
*/
OBJC_EXPORT BOOL class_addIvar(Class cls, const char *name, size_t size,
uint8_t alignment, const char *types)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Adds a protocol to a class.
* (for example, the class already conforms to that protocol).
*/
OBJC_EXPORT BOOL class_addProtocol(Class cls, Protocol *protocol)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Adds a property to a class.
* (for example, the class already has that property).
*/
OBJC_EXPORT BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
/**
* Replace a property of a class.
* @param attributeCount The number of attributes in \e attributes.
*/
OBJC_EXPORT void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
/**
* Sets the Ivar layout for a given class.
* @param layout The layout of the \c Ivars for \e cls.
*/
OBJC_EXPORT void class_setIvarLayout(Class cls, const uint8_t *layout)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Sets the layout for weak Ivars for a given class.
* @param layout The layout of the weak Ivars for \e cls.
*/
OBJC_EXPORT void class_setWeakIvarLayout(Class cls, const uint8_t *layout)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Used by CoreFoundation's toll-free bridging.
* @warning Do not call this function yourself.
*/
OBJC_EXPORT Class objc_getFutureClass(const char *name)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0)
- OBJC_ARC_UNAVAILABLE;
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0)
+ OBJC_ARC_UNAVAILABLE;
/* Instantiating Classes */
* @return An instance of the class \e cls.
*/
OBJC_EXPORT id class_createInstance(Class cls, size_t extraBytes)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
/**
* @see class_createInstance
*/
OBJC_EXPORT id objc_constructInstance(Class cls, void *bytes)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0)
+ OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
/**
*
* @return \e obj. Does nothing if \e obj is nil.
*
- * @warning GC does not call this. If you edit this, also edit finalize.
- *
* @note CF and other clients do call this under GC.
*/
OBJC_EXPORT void *objc_destructInstance(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0)
+ OBJC_AVAILABLE(10.6, 3.0, 9.0, 1.0)
OBJC_ARC_UNAVAILABLE;
*/
OBJC_EXPORT Class objc_allocateClassPair(Class superclass, const char *name,
size_t extraBytes)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Registers a class that was allocated using \c objc_allocateClassPair.
* @param cls The class you want to register.
*/
OBJC_EXPORT void objc_registerClassPair(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Used by Foundation's Key-Value Observing.
* @warning Do not call this function yourself.
*/
OBJC_EXPORT Class objc_duplicateClass(Class original, const char *name, size_t extraBytes)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Destroy a class and its associated metaclass.
* @warning Do not call if instances of this class or a subclass exist.
*/
OBJC_EXPORT void objc_disposeClassPair(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/* Working with Methods */
* @note To get the method name as a C string, call \c sel_getName(method_getName(method)).
*/
OBJC_EXPORT SEL method_getName(Method m)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns the implementation of a method.
* @return A function pointer of type IMP.
*/
OBJC_EXPORT IMP method_getImplementation(Method m)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns a string describing a method's parameter and return types.
* @return A C string. The string may be \c NULL.
*/
OBJC_EXPORT const char *method_getTypeEncoding(Method m)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns the number of arguments accepted by a method.
* @return An integer containing the number of arguments accepted by the given method.
*/
OBJC_EXPORT unsigned int method_getNumberOfArguments(Method m)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Returns a string describing a method's return type.
* @return A C string describing the return type. You must free the string with \c free().
*/
OBJC_EXPORT char *method_copyReturnType(Method m)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns a string describing a single parameter type of a method.
* if method has no parameter index \e index. You must free the string with \c free().
*/
OBJC_EXPORT char *method_copyArgumentType(Method m, unsigned int index)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns by reference a string describing a method's return type.
* \e dst is filled as if \c strncpy(dst, parameter_type, dst_len) were called.
*/
OBJC_EXPORT void method_getReturnType(Method m, char *dst, size_t dst_len)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns by reference a string describing a single parameter type of a method.
*/
OBJC_EXPORT void method_getArgumentType(Method m, unsigned int index,
char *dst, size_t dst_len)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT struct objc_method_description *method_getDescription(Method m)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Sets the implementation of a method.
* @return The previous implementation of the method.
*/
OBJC_EXPORT IMP method_setImplementation(Method m, IMP imp)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Exchanges the implementations of two methods.
* \endcode
*/
OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/* Working with Instance Variables */
* @return A C string containing the instance variable's name.
*/
OBJC_EXPORT const char *ivar_getName(Ivar v)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns the type string of an instance variable.
* @note For possible values, see Objective-C Runtime Programming Guide > Type Encodings.
*/
OBJC_EXPORT const char *ivar_getTypeEncoding(Ivar v)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns the offset of an instance variable.
* and \c object_setIvar instead of using this offset to access the instance variable data directly.
*/
OBJC_EXPORT ptrdiff_t ivar_getOffset(Ivar v)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/* Working with Properties */
* @return A C string containing the property's name.
*/
OBJC_EXPORT const char *property_getName(objc_property_t property)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns the attribute string of a property.
* @note The format of the attribute string is described in Declared Properties in Objective-C Runtime Programming Guide.
*/
OBJC_EXPORT const char *property_getAttributes(objc_property_t property)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns an array of property attributes for a property.
* @return An array of property attributes; must be free'd() by the caller.
*/
OBJC_EXPORT objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
/**
* Returns the value of a property attribute given the attribute name.
* \e property, \c nil otherwise.
*/
OBJC_EXPORT char *property_copyAttributeValue(objc_property_t property, const char *attributeName)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
/* Working with Protocols */
* @note This function acquires the runtime lock.
*/
OBJC_EXPORT Protocol *objc_getProtocol(const char *name)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns an array of all the protocols known to the runtime.
* @note This function acquires the runtime lock.
*/
OBJC_EXPORT Protocol * __unsafe_unretained *objc_copyProtocolList(unsigned int *outCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns a Boolean value that indicates whether one protocol conforms to another protocol.
* All the protocols listed between angle brackets are considered part of the ProtocolName protocol.
*/
OBJC_EXPORT BOOL protocol_conformsToProtocol(Protocol *proto, Protocol *other)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns a Boolean value that indicates whether two protocols are equal.
* @return \c YES if \e proto is the same as \e other, otherwise \c NO.
*/
OBJC_EXPORT BOOL protocol_isEqual(Protocol *proto, Protocol *other)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns the name of a protocol.
* @return The name of the protocol \e p as a C string.
*/
OBJC_EXPORT const char *protocol_getName(Protocol *p)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns a method description structure for a specified method of a given protocol.
* @note This function recursively searches any protocols that this protocol conforms to.
*/
OBJC_EXPORT struct objc_method_description protocol_getMethodDescription(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns an array of method descriptions of methods meeting a given specification for a given protocol.
* @note Methods in other protocols adopted by this protocol are not included.
*/
OBJC_EXPORT struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns the specified property of a given protocol.
*
* @param proto A protocol.
* @param name The name of a property.
- * @param isRequiredProperty A Boolean value that indicates whether name is a required property.
- * @param isInstanceProperty A Boolean value that indicates whether name is a required property.
+ * @param isRequiredProperty \c YES searches for a required property, \c NO searches for an optional property.
+ * @param isInstanceProperty \c YES searches for an instance property, \c NO searches for a class property.
*
* @return The property specified by \e name, \e isRequiredProperty, and \e isInstanceProperty for \e proto,
* or \c NULL if none of \e proto's properties meets the specification.
*/
OBJC_EXPORT objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
+
+/**
+ * Returns an array of the required instance properties declared by a protocol.
+ *
+ * @note Identical to
+ * \code
+ * protocol_copyPropertyList2(proto, outCount, YES, YES);
+ * \endcode
+ */
+OBJC_EXPORT objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
- * Returns an array of the properties declared by a protocol.
+ * Returns an array of properties declared by a protocol.
*
* @param proto A protocol.
* @param outCount Upon return, contains the number of elements in the returned array.
+ * @param isRequiredProperty \c YES returns required properties, \c NO returns optional properties.
+ * @param isInstanceProperty \c YES returns instance properties, \c NO returns class properties.
*
* @return A C array of pointers of type \c objc_property_t describing the properties declared by \e proto.
* Any properties declared by other protocols adopted by this protocol are not included. The array contains
* \c *outCount pointers followed by a \c NULL terminator. You must free the array with \c free().
- * If the protocol declares no properties, \c NULL is returned and \c *outCount is \c 0.
+ * If the protocol declares no matching properties, \c NULL is returned and \c *outCount is \c 0.
*/
-OBJC_EXPORT objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+OBJC_EXPORT objc_property_t *protocol_copyPropertyList2(Protocol *proto, unsigned int *outCount, BOOL isRequiredProperty, BOOL isInstanceProperty)
+ OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0);
/**
* Returns an array of the protocols adopted by a protocol.
* If the protocol declares no properties, \c NULL is returned and \c *outCount is \c 0.
*/
OBJC_EXPORT Protocol * __unsafe_unretained *protocol_copyProtocolList(Protocol *proto, unsigned int *outCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Creates a new protocol instance that cannot be used until registered with
* @note There is no dispose method for this.
*/
OBJC_EXPORT Protocol *objc_allocateProtocol(const char *name)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
/**
* Registers a newly constructed protocol with the runtime. The protocol
* @param proto The protocol you want to register.
*/
OBJC_EXPORT void objc_registerProtocol(Protocol *proto)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
/**
* Adds a method to a protocol. The protocol must be under construction.
* @param isInstanceMethod YES if the method is an instance method.
*/
OBJC_EXPORT void protocol_addMethodDescription(Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
/**
* Adds an incorporated protocol to another protocol. The protocol being
* @param addition The protocol you want to incorporate into \e proto, it must be registered.
*/
OBJC_EXPORT void protocol_addProtocol(Protocol *proto, Protocol *addition)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
/**
* Adds a property to a protocol. The protocol must be under construction.
* not add the property to the protocol at all.
*/
OBJC_EXPORT void protocol_addProperty(Protocol *proto, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount, BOOL isRequiredProperty, BOOL isInstanceProperty)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
/* Working with Libraries */
* @return An array of C strings of names. Must be free()'d by caller.
*/
OBJC_EXPORT const char **objc_copyImageNames(unsigned int *outCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns the dynamic library name a class originated from.
* @return The name of the library containing this class.
*/
OBJC_EXPORT const char *class_getImageName(Class cls)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Returns the names of all the classes within a library.
*/
OBJC_EXPORT const char **objc_copyClassNamesForImage(const char *image,
unsigned int *outCount)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/* Working with Selectors */
* @return A C string indicating the name of the selector.
*/
OBJC_EXPORT const char *sel_getName(SEL sel)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Registers a method name with the Objective-C runtime system.
* observed that many of the callers of this function did not check the return value for \c NULL.
*/
OBJC_EXPORT SEL sel_getUid(const char *str)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Registers a method with the Objective-C runtime system, maps the method
* has already been registered, this function simply returns the selector.
*/
OBJC_EXPORT SEL sel_registerName(const char *str)
- __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
/**
* Returns a Boolean value that indicates whether two selectors are equal.
* @param lhs The selector to compare with rhs.
* @param rhs The selector to compare with lhs.
*
- * @return \c YES if \e rhs and \e rhs are equal, otherwise \c NO.
+ * @return \c YES if \e lhs and \e rhs are equal, otherwise \c NO.
*
* @note sel_isEqual is equivalent to ==.
*/
OBJC_EXPORT BOOL sel_isEqual(SEL lhs, SEL rhs)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/* Objective-C Language Features */
*
*/
OBJC_EXPORT void objc_enumerationMutation(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Sets the current mutation handler.
* @param handler Function pointer to the new mutation handler.
*/
OBJC_EXPORT void objc_setEnumerationMutationHandler(void (*handler)(id))
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Set the function to be called by objc_msgForward.
* @see message.h::_objc_msgForward
*/
OBJC_EXPORT void objc_setForwardHandler(void *fwd, void *fwd_stret)
- __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
/**
* Creates a pointer to a function that will call the block
* \c imp_removeBlock.
*/
OBJC_EXPORT IMP imp_implementationWithBlock(id block)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
/**
* Return the block associated with an IMP that was created using
* @return The block called by \e anImp.
*/
OBJC_EXPORT id imp_getBlock(IMP anImp)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
/**
* Disassociates a block from an IMP that was created using
* (For example, the block might not have been used to create an IMP previously).
*/
OBJC_EXPORT BOOL imp_removeBlock(IMP anImp)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
+ OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);
/**
* This loads the object referenced by a weak pointer and returns it, after
* @return The object pointed to by \e location, or \c nil if \e location is \c nil.
*/
OBJC_EXPORT id objc_loadWeak(id *location)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
/**
* This function stores a new value into a __weak variable. It would
* @return The value stored into \e location, i.e. \e obj
*/
OBJC_EXPORT id objc_storeWeak(id *location, id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
+ OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0);
/* Associative References */
* @see objc_removeAssociatedObjects
*/
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
+ OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
/**
* Returns the value associated with a given object for a given key.
* @see objc_setAssociatedObject
*/
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
+ OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
/**
* Removes all associations for a given object.
* @see objc_getAssociatedObject
*/
OBJC_EXPORT void objc_removeAssociatedObjects(id object)
- __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
+ OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
#define _C_ID '@'
struct objc_protocol_list {
struct objc_protocol_list *next;
long count;
- Protocol *list[1];
+ __unsafe_unretained Protocol *list[1];
};
/* Obsolete functions */
OBJC_EXPORT IMP class_lookupMethod(Class cls, SEL sel)
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_2_0,__IPHONE_2_0);
+ __OSX_DEPRECATED(10.0, 10.5, "use class_getMethodImplementation instead")
+ __IOS_DEPRECATED(2.0, 2.0, "use class_getMethodImplementation instead")
+ __TVOS_DEPRECATED(9.0, 9.0, "use class_getMethodImplementation instead")
+ __WATCHOS_DEPRECATED(1.0, 1.0, "use class_getMethodImplementation instead");
OBJC_EXPORT BOOL class_respondsToMethod(Class cls, SEL sel)
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_2_0,__IPHONE_2_0);
+ __OSX_DEPRECATED(10.0, 10.5, "use class_respondsToSelector instead")
+ __IOS_DEPRECATED(2.0, 2.0, "use class_respondsToSelector instead")
+ __TVOS_DEPRECATED(9.0, 9.0, "use class_respondsToSelector instead")
+ __WATCHOS_DEPRECATED(1.0, 1.0, "use class_respondsToSelector instead");
OBJC_EXPORT void _objc_flush_caches(Class cls)
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_2_0,__IPHONE_2_0);
+ __OSX_DEPRECATED(10.0, 10.5, "not recommended")
+ __IOS_DEPRECATED(2.0, 2.0, "not recommended")
+ __TVOS_DEPRECATED(9.0, 9.0, "not recommended")
+ __WATCHOS_DEPRECATED(1.0, 1.0, "not recommended");
OBJC_EXPORT id object_copyFromZone(id anObject, size_t nBytes, void *z)
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_NA,__IPHONE_NA)
+ __OSX_DEPRECATED(10.0, 10.5, "use object_copy instead")
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE
OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT id object_realloc(id anObject, size_t nBytes) OBJC2_UNAVAILABLE;
OBJC_EXPORT id object_reallocFromZone(id anObject, size_t nBytes, void *z) OBJC2_UNAVAILABLE;
OBJC_EXPORT void objc_setMultithreaded (BOOL flag) OBJC2_UNAVAILABLE;
OBJC_EXPORT id class_createInstanceFromZone(Class, size_t idxIvars, void *z)
- __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_0,__MAC_10_5, __IPHONE_NA,__IPHONE_NA)
+ __OSX_DEPRECATED(10.0, 10.5, "use class_createInstance instead")
+ __IOS_UNAVAILABLE __TVOS_UNAVAILABLE __WATCHOS_UNAVAILABLE
OBJC_ARC_UNAVAILABLE;
OBJC_EXPORT void class_addMethods(Class, struct objc_method_list *) OBJC2_UNAVAILABLE;
+++ /dev/null
-//
-// ARRBase.h
-// TestARRLayouts
-//
-// Created by Patrick Beard on 3/8/11.
-// Copyright 2011 __MyCompanyName__. All rights reserved.
-//
-
-#import <Foundation/NSObject.h>
-
-@interface ARRBase : NSObject
-@property long number;
-@property(retain) id object;
-@property void *pointer;
-@property(weak) __weak id delegate;
-@end
+++ /dev/null
-//
-// ARRBase.m
-// TestARRLayouts
-//
-// Created by Patrick Beard on 3/8/11.
-// Copyright 2011 __MyCompanyName__. All rights reserved.
-//
-
-#import "ARRBase.h"
-
-#if 1
-@interface ARRBase () {
-@private
- long number;
- id object;
- void *pointer;
- __weak id delegate;
-}
-@end
-#endif
-
-@implementation ARRBase
-@synthesize number, object, pointer, delegate;
-@end
+++ /dev/null
-/*
-TEST_CONFIG MEM=arc CC=clang
-TEST_BUILD
- $C{COMPILE_NOLINK_NOMEM} -c $DIR/MRRBase.m
- $C{COMPILE_NOLINK_NOMEM} -c $DIR/MRRARR.m
- $C{COMPILE_NOLINK} -c $DIR/ARRBase.m
- $C{COMPILE_NOLINK} -c $DIR/ARRMRR.m
- $C{COMPILE} -fobjc-arc $DIR/ARRLayouts.m -x none MRRBase.o MRRARR.o ARRBase.o ARRMRR.o -framework Foundation -o ARRLayouts.out
-END
-*/
-
-#include "test.h"
-#import <stdio.h>
-#import <Foundation/Foundation.h>
-#import <objc/runtime.h>
-
-#import "ARRMRR.h"
-#import "MRRARR.h"
-
-@interface NSObject (Layouts)
-+ (const char *)strongLayout;
-+ (const char *)weakLayout;
-@end
-
-void printlayout(const char *name, const uint8_t *layout)
-{
- if (! getenv("VERBOSE")) return;
-
- testprintf("%s: ", name);
-
- if (!layout) {
- fprintf(stderr, "NULL\n");
- return;
- }
-
- const uint8_t *c;
- for (c = layout; *c; c++) {
- fprintf(stderr, "%02x ", *c);
- }
-
- fprintf(stderr, "00\n");
-}
-
-@implementation NSObject (Layouts)
-
-+ (const char *)strongLayout {
- const uint8_t *layout = class_getIvarLayout(self);
- printlayout("strong", layout);
- return (const char *)layout;
-}
-
-+ (const char *)weakLayout {
- const uint8_t *weakLayout = class_getWeakIvarLayout(self);
- printlayout("weak", weakLayout);
- return (const char *)weakLayout;
-}
-
-+ (Ivar)instanceVariable:(const char *)name {
- return class_getInstanceVariable(self, name);
-}
-
-@end
-
-int main (int argc __unused, const char * argv[] __unused) {
- // Under ARR, layout strings are relative to the class' own ivars.
- testassert(strcmp([ARRBase strongLayout], "\x11\x20") == 0);
- testassert(strcmp([ARRBase weakLayout], "\x31") == 0);
- testassert([MRRBase strongLayout] == NULL);
- testassert([MRRBase weakLayout] == NULL);
- testassert(strcmp([ARRMRR strongLayout], "\x01") == 0);
- testassert([ARRMRR weakLayout] == NULL);
- testassert([MRRARR strongLayout] == NULL);
- testassert([MRRARR weakLayout] == NULL);
-
- // now check consistency between dynamic accessors and KVC, etc.
- ARRMRR *am = [ARRMRR new];
- MRRARR *ma = [MRRARR new];
-
- NSString *am_description = [[NSString alloc] initWithFormat:@"%s %p", "ARRMRR", am];
- NSString *ma_description = [[NSString alloc] initWithFormat:@"%s %p", "MRRARR", ma];
-
- am.number = M_PI;
- object_setIvar(am, [ARRMRR instanceVariable:"object"], am_description);
- testassert(CFGetRetainCount(objc_unretainedPointer(am_description)) == 1);
- am.pointer = @selector(ARRMRR);
- object_setIvar(am, [ARRMRR instanceVariable:"delegate"], ma);
- testassert(CFGetRetainCount(objc_unretainedPointer(ma)) == 1);
-
- ma.number = M_E;
- object_setIvar(ma, [MRRARR instanceVariable:"object"], ma_description);
- testassert(CFGetRetainCount(objc_unretainedPointer(ma_description)) == 2);
- ma.pointer = @selector(MRRARR);
- ma.delegate = am;
- object_setIvar(ma, [MRRARR instanceVariable:"delegate"], am);
- testassert(CFGetRetainCount(objc_unretainedPointer(am)) == 1);
-
- succeed(__FILE__);
- return 0;
-}
+++ /dev/null
-//
-// ARRMRR.h
-// TestARRLayouts
-//
-// Created by Patrick Beard on 3/8/11.
-// Copyright 2011 __MyCompanyName__. All rights reserved.
-//
-
-#import "MRRBase.h"
-
-@interface ARRMRR : MRRBase
-@property(retain) id dataSource;
-@end
+++ /dev/null
-//
-// ARRMRR.m
-//
-
-#import "ARRMRR.h"
-
-@implementation ARRMRR
-
-@synthesize dataSource;
-
-@end
+++ /dev/null
-//
-// MRRARR.h
-// TestARRLayouts
-//
-// Created by Patrick Beard on 3/8/11.
-// Copyright 2011 __MyCompanyName__. All rights reserved.
-//
-
-#import "ARRBase.h"
-
-@interface MRRARR : ARRBase
-@property(retain) id dataSource;
-@end
+++ /dev/null
-//
-// MRRARR.m
-//
-
-#import "MRRARR.h"
-
-@implementation MRRARR
-
-@synthesize dataSource;
-
-@end
+++ /dev/null
-//
-// MRRBase.h
-// TestARRLayouts
-//
-// Created by Patrick Beard on 3/8/11.
-// Copyright 2011 __MyCompanyName__. All rights reserved.
-//
-
-#import <Foundation/NSObject.h>
-
-@interface MRRBase : NSObject
-@property double number;
-@property(retain) id object;
-@property void *pointer;
-@property(weak) __weak id delegate;
-@end
+++ /dev/null
-//
-// MRRBase.m
-// TestARRLayouts
-//
-// Created by Patrick Beard on 3/8/11.
-// Copyright 2011 __MyCompanyName__. All rights reserved.
-//
-
-#import "MRRBase.h"
-
-#if 1
-@interface MRRBase () {
-@private
- double number;
- id object;
- void *pointer;
- __weak id delegate;
-}
-@end
-#endif
-
-@implementation MRRBase
-@synthesize number, object, pointer, delegate;
-@end
+++ /dev/null
-# quick test
-all:
- perl test.pl $(MAKEFLAGS)
-
-# default-arch but otherwise comprehensive test for buildbot
-buildbot:
- perl test.pl $(MAKEFLAGS) MEM=mrc,arc,gc CC=clang LANGUAGE=objc,objc++
-
-# comprehensive tests
-mac macos macosx:
- perl test.pl $(MAKEFLAGS) ARCH=x86_64,i386 MEM=mrc,arc,gc CC=clang LANGUAGE=objc,objc++
-
-iphonesimulator:
- perl test.pl $(MAKEFLAGS) ARCH=i386 SDK=iphonesimulator MEM=mrc,arc CC=clang LANGUAGE=objc,objc++
-
-iphoneos:
- perl test.pl $(MAKEFLAGS) ARCH=armv6,armv7 SDK=iphoneos MEM=mrc,arc CC=clang LANGUAGE=objc,objc++
-
-clean:
- @ perl test.pl clean
+++ /dev/null
-// TEST_CFLAGS -framework Foundation
-
-#import <Foundation/Foundation.h>
-#import <objc/runtime.h>
-#import <objc/objc-abi.h>
-#include "test.h"
-
-@interface Test : NSObject {
- NSString *_value;
- // _object is at the last optimized property offset
- id _object __attribute__((aligned(64)));
-}
-@property(readonly) Class cls;
-@property(copy) NSString *value;
-@property(assign) id object;
-@end
-
-typedef struct {
- void *isa;
- void *_value;
- // _object is at the last optimized property offset
- void *_object __attribute__((aligned(64)));
-} TestDefs;
-
-@implementation Test
-
-// Question: why can't this code be automatically generated?
-
-#if !__has_feature(objc_arc)
-- (void)dealloc {
- self.value = nil;
- self.object = nil;
- [super dealloc];
-}
-#endif
-
-- (Class)cls { return objc_getProperty(self, _cmd, 0, YES); }
-
-- (NSString*)value { return (NSString*) objc_getProperty(self, _cmd, offsetof(TestDefs, _value), YES); }
-- (void)setValue:(NSString*)inValue { objc_setProperty(self, _cmd, offsetof(TestDefs, _value), inValue, YES, YES); }
-
-- (id)object { return objc_getProperty(self, _cmd, offsetof(TestDefs, _object), YES); }
-- (void)setObject:(id)inObject { objc_setProperty(self, _cmd, offsetof(TestDefs, _object), inObject, YES, NO); }
-
-- (NSString *)description {
- return [NSString stringWithFormat:@"value = %@, object = %@", self.value, self.object];
-}
-
-@end
-
-int main() {
- PUSH_POOL {
-
- NSMutableString *value = [NSMutableString stringWithUTF8String:"test"];
- id object = [NSNumber numberWithInt:11];
- Test *t = AUTORELEASE([Test new]);
- t.value = value;
- [value setString:@"yuck"]; // mutate the string.
- testassert(t.value != value); // must copy, since it was mutable.
- testassert([t.value isEqualToString:@"test"]);
-
- Class testClass = [Test class];
- Class cls = t.cls;
- testassert(testClass == cls);
- cls = t.cls;
- testassert(testClass == cls);
-
- t.object = object;
- t.object = object;
-
- // NSLog(@"t.object = %@, t.value = %@", t.object, t.value);
- // NSLog(@"t.object = %@, t.value = %@", t.object, t.value); // second call will optimized getters.
-
- } POP_POOL;
-
- succeed(__FILE__);
-
- return 0;
-}
+++ /dev/null
-// TEST_CONFIG MEM=mrc,arc
-
-#include "test.h"
-#include <objc/runtime.h>
-#include <objc/objc-abi.h>
-
-#include "testroot.i"
-
-@interface Base : TestRoot {
- @public
- id ivar;
-}
-@end
-@implementation Base @end
-
-int main()
-{
- SEL _cmd = @selector(foo);
- Base *o = [Base new];
- ptrdiff_t offset = ivar_getOffset(class_getInstanceVariable([Base class], "ivar"));
- testassert(offset == sizeof(id));
-
- TestRoot *value = [TestRoot new];
-
- // fixme test atomicity
-
- // Original setter API
-
- testprintf("original nonatomic retain\n");
- o->ivar = nil;
- TestRootRetain = 0;
- TestRootCopyWithZone = 0;
- TestRootMutableCopyWithZone = 0;
- objc_setProperty(o, _cmd, offset, value, NO/*atomic*/, NO/*copy*/);
- testassert(TestRootRetain == 1);
- testassert(TestRootCopyWithZone == 0);
- testassert(TestRootMutableCopyWithZone == 0);
- testassert(o->ivar == value);
-
- testprintf("original atomic retain\n");
- o->ivar = nil;
- TestRootRetain = 0;
- TestRootCopyWithZone = 0;
- TestRootMutableCopyWithZone = 0;
- objc_setProperty(o, _cmd, offset, value, YES/*atomic*/, NO/*copy*/);
- testassert(TestRootRetain == 1);
- testassert(TestRootCopyWithZone == 0);
- testassert(TestRootMutableCopyWithZone == 0);
- testassert(o->ivar == value);
-
- testprintf("original nonatomic copy\n");
- o->ivar = nil;
- TestRootRetain = 0;
- TestRootCopyWithZone = 0;
- TestRootMutableCopyWithZone = 0;
- objc_setProperty(o, _cmd, offset, value, NO/*atomic*/, YES/*copy*/);
- testassert(TestRootRetain == 0);
- testassert(TestRootCopyWithZone == 1);
- testassert(TestRootMutableCopyWithZone == 0);
- testassert(o->ivar && o->ivar != value);
-
- testprintf("original atomic copy\n");
- o->ivar = nil;
- TestRootRetain = 0;
- TestRootCopyWithZone = 0;
- TestRootMutableCopyWithZone = 0;
- objc_setProperty(o, _cmd, offset, value, YES/*atomic*/, YES/*copy*/);
- testassert(TestRootRetain == 0);
- testassert(TestRootCopyWithZone == 1);
- testassert(TestRootMutableCopyWithZone == 0);
- testassert(o->ivar && o->ivar != value);
-
- testprintf("original nonatomic mutablecopy\n");
- o->ivar = nil;
- TestRootRetain = 0;
- TestRootCopyWithZone = 0;
- TestRootMutableCopyWithZone = 0;
- objc_setProperty(o, _cmd, offset, value, NO/*atomic*/, 2/*copy*/);
- testassert(TestRootRetain == 0);
- testassert(TestRootCopyWithZone == 0);
- testassert(TestRootMutableCopyWithZone == 1);
- testassert(o->ivar && o->ivar != value);
-
- testprintf("original atomic mutablecopy\n");
- o->ivar = nil;
- TestRootRetain = 0;
- TestRootCopyWithZone = 0;
- TestRootMutableCopyWithZone = 0;
- objc_setProperty(o, _cmd, offset, value, YES/*atomic*/, 2/*copy*/);
- testassert(TestRootRetain == 0);
- testassert(TestRootCopyWithZone == 0);
- testassert(TestRootMutableCopyWithZone == 1);
- testassert(o->ivar && o->ivar != value);
-
-
- // Optimized setter API
-
- testprintf("optimized nonatomic retain\n");
- o->ivar = nil;
- TestRootRetain = 0;
- TestRootCopyWithZone = 0;
- TestRootMutableCopyWithZone = 0;
- objc_setProperty_nonatomic(o, _cmd, value, offset);
- testassert(TestRootRetain == 1);
- testassert(TestRootCopyWithZone == 0);
- testassert(TestRootMutableCopyWithZone == 0);
- testassert(o->ivar == value);
-
- testprintf("optimized atomic retain\n");
- o->ivar = nil;
- TestRootRetain = 0;
- TestRootCopyWithZone = 0;
- TestRootMutableCopyWithZone = 0;
- objc_setProperty_atomic(o, _cmd, value, offset);
- testassert(TestRootRetain == 1);
- testassert(TestRootCopyWithZone == 0);
- testassert(TestRootMutableCopyWithZone == 0);
- testassert(o->ivar == value);
-
- testprintf("optimized nonatomic copy\n");
- o->ivar = nil;
- TestRootRetain = 0;
- TestRootCopyWithZone = 0;
- TestRootMutableCopyWithZone = 0;
- objc_setProperty_nonatomic_copy(o, _cmd, value, offset);
- testassert(TestRootRetain == 0);
- testassert(TestRootCopyWithZone == 1);
- testassert(TestRootMutableCopyWithZone == 0);
- testassert(o->ivar && o->ivar != value);
-
- testprintf("optimized atomic copy\n");
- o->ivar = nil;
- TestRootRetain = 0;
- TestRootCopyWithZone = 0;
- TestRootMutableCopyWithZone = 0;
- objc_setProperty_atomic_copy(o, _cmd, value, offset);
- testassert(TestRootRetain == 0);
- testassert(TestRootCopyWithZone == 1);
- testassert(TestRootMutableCopyWithZone == 0);
- testassert(o->ivar && o->ivar != value);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include "testroot.i"
-#include <objc/runtime.h>
-
-@interface Super : TestRoot @end
-@implementation Super
--(int)superMethod { return 0; }
--(int)bothMethod { return 0; }
-@end
-
-@interface Sub : Super @end
-@implementation Sub
--(int)subMethod { return 0; }
--(int)bothMethod { return 0; }
-@end
-
-@interface Sub2 : Super @end
-@implementation Sub2
--(int)subMethod { return 0; }
--(int)bothMethod { return 0; }
-@end
-
-
-id fn(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) { return nil; }
-
-int main()
-{
- IMP superMethodFromSuper = class_getMethodImplementation([Super class], @selector(superMethod));
- IMP bothMethodFromSuper = class_getMethodImplementation([Super class], @selector(bothMethod));
- IMP subMethodFromSub = class_getMethodImplementation([Sub class], @selector(subMethod));
- IMP bothMethodFromSub = class_getMethodImplementation([Sub class], @selector(bothMethod));
- IMP subMethodFromSub2 = class_getMethodImplementation([Sub2 class], @selector(subMethod));
- IMP bothMethodFromSub2 = class_getMethodImplementation([Sub2 class], @selector(bothMethod));
-
- testassert(superMethodFromSuper);
- testassert(bothMethodFromSuper);
- testassert(subMethodFromSub);
- testassert(bothMethodFromSub);
- testassert(subMethodFromSub2);
- testassert(bothMethodFromSub2);
-
- BOOL ok;
- IMP imp;
-
- // class_addMethod doesn't replace existing implementations
- ok = class_addMethod([Super class], @selector(superMethod), (IMP)fn, NULL);
- testassert(!ok);
- testassert(class_getMethodImplementation([Super class], @selector(superMethod)) == superMethodFromSuper);
-
- // class_addMethod does override superclass implementations
- ok = class_addMethod([Sub class], @selector(superMethod), (IMP)fn, NULL);
- testassert(ok);
- testassert(class_getMethodImplementation([Sub class], @selector(superMethod)) == (IMP)fn);
-
- // class_addMethod does add root implementations
- ok = class_addMethod([Super class], @selector(superMethodNew2), (IMP)fn, NULL);
- testassert(ok);
- testassert(class_getMethodImplementation([Super class], @selector(superMethodNew2)) == (IMP)fn);
- testassert(class_getMethodImplementation([Sub class], @selector(superMethodNew2)) == (IMP)fn);
-
-
- // class_replaceMethod does add new implementations,
- // returning NULL if super has an implementation
- imp = class_replaceMethod([Sub2 class], @selector(superMethod), (IMP)fn, NULL);
- testassert(imp == NULL);
- testassert(class_getMethodImplementation([Sub2 class], @selector(superMethod)) == (IMP)fn);
-
- // class_replaceMethod does add new implementations,
- // returning NULL if super has no implementation
- imp = class_replaceMethod([Sub2 class], @selector(subMethodNew), (IMP)fn, NULL);
- testassert(imp == NULL);
- testassert(class_getMethodImplementation([Sub2 class], @selector(subMethodNew)) == (IMP)fn);
-
- // class_replaceMethod does add new implemetations
- // returning NULL if there is no super class
- imp = class_replaceMethod([Super class], @selector(superMethodNew), (IMP)fn, NULL);
- testassert(imp == NULL);
- testassert(class_getMethodImplementation([Super class], @selector(superMethodNew)) == (IMP)fn);
-
-
- // class_replaceMethod does replace existing implementations,
- // returning existing implementation (regardless of super)
- imp = class_replaceMethod([Sub2 class], @selector(subMethod), (IMP)fn, NULL);
- testassert(imp == subMethodFromSub2);
- testassert(class_getMethodImplementation([Sub2 class], @selector(subMethod)) == (IMP)fn);
-
- // class_replaceMethod does replace existing implemetations,
- // returning existing implementation (regardless of super)
- imp = class_replaceMethod([Sub2 class], @selector(bothMethod), (IMP)fn, NULL);
- testassert(imp == bothMethodFromSub2);
- testassert(class_getMethodImplementation([Sub2 class], @selector(bothMethod)) == (IMP)fn);
-
- // class_replaceMethod does replace existing implemetations,
- // returning existing implementation (regardless of super)
- imp = class_replaceMethod([Super class], @selector(superMethod), (IMP)fn, NULL);
- testassert(imp == superMethodFromSuper);
- testassert(class_getMethodImplementation([Super class], @selector(superMethod)) == (IMP)fn);
-
- // fixme actually try calling them
-
- succeed(__FILE__);
-}
+++ /dev/null
-/*
-TEST_RUN_OUTPUT
-objc\[\d+\]: protocol_addProtocol: added protocol 'EmptyProto' is still under construction!
-objc\[\d+\]: objc_registerProtocol: protocol 'Proto1' was already registered!
-objc\[\d+\]: protocol_addProtocol: modified protocol 'Proto1' is not under construction!
-objc\[\d+\]: protocol_addMethodDescription: protocol 'Proto1' is not under construction!
-objc\[\d+\]: objc_registerProtocol: protocol 'SuperProto' was already registered!
-objc\[\d+\]: protocol_addProtocol: modified protocol 'SuperProto' is not under construction!
-objc\[\d+\]: protocol_addMethodDescription: protocol 'SuperProto' is not under construction!
-OK: addProtocol.m
-END
-*/
-
-#include "test.h"
-
-#include <objc/runtime.h>
-
-@protocol SuperProto @end
-@protocol SuperProto2 @end
-@protocol UnrelatedProto @end
-
-
-void Crash(id self, SEL _cmd)
-{
- fail("%c[%s %s] called unexpectedly",
- class_isMetaClass(object_getClass(self)) ? '+' : '-',
- object_getClassName(self), sel_getName(_cmd));
-}
-
-
-int main()
-{
- Protocol *proto, *proto2;
- Protocol * __unsafe_unretained *protolist;
- struct objc_method_description *desclist;
- objc_property_t *proplist;
- unsigned int count;
-
- // If objc_registerProtocol() fails to preserve the retain count
- // then ARC will deallocate Protocol objects too early.
- class_replaceMethod(objc_getClass("Protocol"),
- sel_registerName("dealloc"), (IMP)Crash, "v@:");
- class_replaceMethod(objc_getClass("__IncompleteProtocol"),
- sel_registerName("dealloc"), (IMP)Crash, "v@:");
-
- // make sure binary contains hard copies of these protocols
- proto = @protocol(SuperProto);
- proto = @protocol(SuperProto2);
-
- // Adding a protocol
-
- char *name = strdup("Proto1");
- proto = objc_allocateProtocol(name);
- testassert(proto);
- testassert(!objc_getProtocol(name));
-
- protocol_addProtocol(proto, @protocol(SuperProto));
- protocol_addProtocol(proto, @protocol(SuperProto2));
- // no inheritance cycles
- proto2 = objc_allocateProtocol("EmptyProto");
- protocol_addProtocol(proto, proto2); // fails
- objc_registerProtocol(proto2);
- protocol_addProtocol(proto, proto2); // succeeds
-
- char *types = strdup("@:");
- protocol_addMethodDescription(proto, @selector(ReqInst0), types, YES, YES);
- protocol_addMethodDescription(proto, @selector(ReqInst1), types, YES, YES);
- protocol_addMethodDescription(proto, @selector(ReqInst2), types, YES, YES);
- protocol_addMethodDescription(proto, @selector(ReqInst3), types, YES, YES);
-
- protocol_addMethodDescription(proto, @selector(ReqClas0), types, YES, NO);
- protocol_addMethodDescription(proto, @selector(ReqClas1), types, YES, NO);
- protocol_addMethodDescription(proto, @selector(ReqClas2), types, YES, NO);
- protocol_addMethodDescription(proto, @selector(ReqClas3), types, YES, NO);
-
- protocol_addMethodDescription(proto, @selector(OptInst0), types, NO, YES);
- protocol_addMethodDescription(proto, @selector(OptInst1), types, NO, YES);
- protocol_addMethodDescription(proto, @selector(OptInst2), types, NO, YES);
- protocol_addMethodDescription(proto, @selector(OptInst3), types, NO, YES);
-
- protocol_addMethodDescription(proto, @selector(OptClas0), types, NO, NO);
- protocol_addMethodDescription(proto, @selector(OptClas1), types, NO, NO);
- protocol_addMethodDescription(proto, @selector(OptClas2), types, NO, NO);
- protocol_addMethodDescription(proto, @selector(OptClas3), types, NO, NO);
-
- char *name0 = strdup("ReqInst0");
- char *name1 = strdup("ReqInst1");
- char *name2 = strdup("ReqInst2");
- char *name3 = strdup("ReqInst3");
- char *attrname = strdup("T");
- char *attrvalue = strdup("i");
- objc_property_attribute_t attrs[] = {{attrname, attrvalue}};
- int attrcount = sizeof(attrs) / sizeof(attrs[0]);
- protocol_addProperty(proto, name0, attrs, attrcount, YES, YES);
- protocol_addProperty(proto, name1, attrs, attrcount, YES, YES);
- protocol_addProperty(proto, name2, attrs, attrcount, YES, YES);
- protocol_addProperty(proto, name3, attrs, attrcount, YES, YES);
-
- objc_registerProtocol(proto);
- testassert(0 == strcmp(protocol_getName(proto), "Proto1"));
-
- // Use of added protocols
-
- testassert(proto == objc_getProtocol("Proto1"));
- strcpy(name, "XXXXXX"); // name is copied
- testassert(0 == strcmp(protocol_getName(proto), "Proto1"));
-
- protolist = protocol_copyProtocolList(proto, &count);
- testassert(protolist);
- testassert(count == 3);
- // note this order is not required
- testassert(protolist[0] == @protocol(SuperProto) &&
- protolist[1] == @protocol(SuperProto2) &&
- protolist[2] == proto2);
- free(protolist);
-
- testassert(protocol_conformsToProtocol(proto, proto2));
- testassert(protocol_conformsToProtocol(proto, @protocol(SuperProto)));
- testassert(!protocol_conformsToProtocol(proto, @protocol(UnrelatedProto)));
-
- strcpy(types, "XX"); // types is copied
- desclist = protocol_copyMethodDescriptionList(proto, YES, YES, &count);
- testassert(desclist && count == 4);
- testprintf("%p %p\n", desclist[0].name, @selector(ReqInst0));
- // testassert(desclist[0].name == @selector(ReqInst0));
- testassert(0 == strcmp(desclist[0].types, "@:"));
- free(desclist);
- desclist = protocol_copyMethodDescriptionList(proto, YES, NO, &count);
- testassert(desclist && count == 4);
- testassert(desclist[1].name == @selector(ReqClas1));
- testassert(0 == strcmp(desclist[1].types, "@:"));
- free(desclist);
- desclist = protocol_copyMethodDescriptionList(proto, NO, YES, &count);
- testassert(desclist && count == 4);
- testassert(desclist[2].name == @selector(OptInst2));
- testassert(0 == strcmp(desclist[2].types, "@:"));
- free(desclist);
- desclist = protocol_copyMethodDescriptionList(proto, NO, NO, &count);
- testassert(desclist && count == 4);
- testassert(desclist[3].name == @selector(OptClas3));
- testassert(0 == strcmp(desclist[3].types, "@:"));
- free(desclist);
-
- strcpy(name0, "XXXXXXXX"); // name is copied
- strcpy(name1, "XXXXXXXX"); // name is copied
- strcpy(name2, "XXXXXXXX"); // name is copied
- strcpy(name3, "XXXXXXXX"); // name is copied
- strcpy(attrname, "X"); // description is copied
- strcpy(attrvalue, "X"); // description is copied
- memset(attrs, 'X', sizeof(attrs)); // description is copied
- proplist = protocol_copyPropertyList(proto, &count);
- testassert(proplist);
- testassert(count == 4);
- // note this order is not required
- testassert(0 == strcmp(property_getName(proplist[0]), "ReqInst0"));
- testassert(0 == strcmp(property_getName(proplist[1]), "ReqInst1"));
- testassert(0 == strcmp(property_getName(proplist[2]), "ReqInst2"));
- testassert(0 == strcmp(property_getName(proplist[3]), "ReqInst3"));
- testassert(0 == strcmp(property_getAttributes(proplist[0]), "Ti"));
- testassert(0 == strcmp(property_getAttributes(proplist[1]), "Ti"));
- testassert(0 == strcmp(property_getAttributes(proplist[2]), "Ti"));
- testassert(0 == strcmp(property_getAttributes(proplist[3]), "Ti"));
- free(proplist);
-
-
- testassert(proto2 == objc_getProtocol("EmptyProto"));
- testassert(0 == strcmp(protocol_getName(proto2), "EmptyProto"));
-
- protolist = protocol_copyProtocolList(proto2, &count);
- testassert(!protolist);
- testassert(count == 0);
-
- testassert(!protocol_conformsToProtocol(proto2, proto));
- testassert(!protocol_conformsToProtocol(proto2,@protocol(SuperProto)));
- testassert(!protocol_conformsToProtocol(proto2,@protocol(UnrelatedProto)));
-
- desclist = protocol_copyMethodDescriptionList(proto2, YES, YES, &count);
- testassert(!desclist && count == 0);
- desclist = protocol_copyMethodDescriptionList(proto2, YES, NO, &count);
- testassert(!desclist && count == 0);
- desclist = protocol_copyMethodDescriptionList(proto2, NO, YES, &count);
- testassert(!desclist && count == 0);
- desclist = protocol_copyMethodDescriptionList(proto2, NO, NO, &count);
- testassert(!desclist && count == 0);
-
- // Immutability of existing protocols
-
- objc_registerProtocol(proto);
- protocol_addProtocol(proto, @protocol(SuperProto2));
- protocol_addMethodDescription(proto, @selector(foo), "", YES, YES);
-
- objc_registerProtocol(@protocol(SuperProto));
- protocol_addProtocol(@protocol(SuperProto), @protocol(SuperProto2));
- protocol_addMethodDescription(@protocol(SuperProto), @selector(foo), "", YES, YES);
-
- // No duplicates
-
- proto = objc_allocateProtocol("SuperProto");
- testassert(!proto);
- proto = objc_allocateProtocol("Proto1");
- testassert(!proto);
-
- // NULL protocols ignored
-
- protocol_addProtocol((Protocol *)objc_unretainedObject((void*)1), NULL);
- protocol_addProtocol(NULL, (Protocol *)objc_unretainedObject((void*)1));
- protocol_addProtocol(NULL, NULL);
- protocol_addMethodDescription(NULL, @selector(foo), "", YES, YES);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG OS=macosx
-// TEST_CFLAGS -framework AppleScriptObjC -framework Foundation
-
-// Verify that trivial AppleScriptObjC apps run with GC off.
-
-#include <Foundation/Foundation.h>
-#include "test.h"
-
-int main()
-{
- [NSBundle class];
- testassert(!objc_collectingEnabled());
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -framework AppleScriptObjC -framework Foundation
-// TEST_CONFIG MEM=gc
-
-// Verify that non-trivial AppleScriptObjC apps run with GC ON.
-
-#include <Foundation/Foundation.h>
-#include "test.h"
-
-@interface NonTrivial : NSObject @end
-@implementation NonTrivial @end
-
-int main()
-{
- [NSBundle class];
- testassert(objc_collectingEnabled());
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-
-// objc.h redefines these calls into bridge casts.
-// This test verifies that the function implementations are exported.
-__BEGIN_DECLS
-extern void *retainedObject(void *arg) __asm__("_objc_retainedObject");
-extern void *unretainedObject(void *arg) __asm__("_objc_unretainedObject");
-extern void *unretainedPointer(void *arg) __asm__("_objc_unretainedPointer");
-__END_DECLS
-
-int main()
-{
- void *p = (void*)&main;
- testassert(p == retainedObject(p));
- testassert(p == unretainedObject(p));
- testassert(p == unretainedPointer(p));
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG MEM=mrc
-// TEST_CRASHES
-/*
-TEST_RUN_OUTPUT
-objc\[\d+\]: Cannot form weak reference to instance \(0x[0-9a-f]+\) of class Crash. It is possible that this object was over-released, or is in the process of deallocation.
-CRASHED: SIG(ILL|TRAP)
-END
-*/
-
-#include "test.h"
-
-#include <Foundation/NSObject.h>
-
-static id weak;
-static id weak2;
-static id weak3;
-static id weak4;
-static bool did_dealloc;
-
-static int state;
-
-@interface NSObject (WeakInternals)
--(BOOL)_tryRetain;
--(BOOL)_isDeallocating;
-@end
-
-@interface Test : NSObject @end
-@implementation Test
--(void)dealloc {
- testprintf("Weak storeOrNil does not crash while deallocating\n");
- weak4 = (id)0x100; // old value must not be used
- id result = objc_initWeakOrNil(&weak4, self);
- testassert(result == nil);
- testassert(weak4 == nil);
- result = objc_storeWeakOrNil(&weak4, self);
- testassert(result == nil);
- testassert(weak4 == nil);
-
- // The value returned by objc_loadWeak() is now nil,
- // but the storage is not yet cleared.
- testassert(weak == self);
- testassert(weak2 == self);
-
- // objc_loadWeak() does not eagerly clear the storage.
- testassert(objc_loadWeakRetained(&weak) == nil);
- testassert(weak != nil);
-
- // dealloc clears the storage.
- testprintf("Weak references clear during super dealloc\n");
- testassert(weak2 != nil);
- [super dealloc];
- testassert(weak == nil);
- testassert(weak2 == nil);
-
- did_dealloc = true;
-}
-@end
-
-@interface CustomTryRetain : Test @end
-@implementation CustomTryRetain
--(BOOL)_tryRetain { state++; return [super _tryRetain]; }
-@end
-
-@interface CustomIsDeallocating : Test @end
-@implementation CustomIsDeallocating
--(BOOL)_isDeallocating { state++; return [super _isDeallocating]; }
-@end
-
-@interface CustomAllowsWeakReference : Test @end
-@implementation CustomAllowsWeakReference
--(BOOL)allowsWeakReference { state++; return [super allowsWeakReference]; }
-@end
-
-@interface CustomRetainWeakReference : Test @end
-@implementation CustomRetainWeakReference
--(BOOL)retainWeakReference { state++; return [super retainWeakReference]; }
-@end
-
-@interface Crash : NSObject @end
-@implementation Crash
--(void)dealloc {
- testassert(weak == self);
- testassert(weak2 == self);
- testassert(objc_loadWeakRetained(&weak) == nil);
- testassert(objc_loadWeakRetained(&weak2) == nil);
-
- testprintf("Weak storeOrNil does not crash while deallocating\n");
- id result = objc_storeWeakOrNil(&weak, self);
- testassert(result == nil);
-
- testprintf("Weak store crashes while deallocating\n");
- objc_storeWeak(&weak, self);
- fail("objc_storeWeak of deallocating value should have crashed");
- [super dealloc];
-}
-@end
-
-
-void cycle(Class cls, Test *obj, Test *obj2, bool storeOrNil)
-{
- testprintf("Cycling class %s\n", class_getName(cls));
-
- id result;
-
- id (*storeWeak)(id *location, id obj);
- id (*initWeak)(id *location, id obj);
- if (storeOrNil) {
- testprintf("Using objc_storeWeakOrNil\n");
- storeWeak = objc_storeWeakOrNil;
- initWeak = objc_initWeakOrNil;
- } else {
- testprintf("Using objc_storeWeak\n");
- storeWeak = objc_storeWeak;
- initWeak = objc_initWeak;
- }
-
- // state counts calls to custom weak methods
- // Difference test classes have different expected values.
- int storeTarget;
- int loadTarget;
- if (cls == [Test class]) {
- storeTarget = 0;
- loadTarget = 0;
- }
- else if (cls == [CustomTryRetain class] ||
- cls == [CustomRetainWeakReference class])
- {
- storeTarget = 0;
- loadTarget = 1;
- }
- else if (cls == [CustomIsDeallocating class] ||
- cls == [CustomAllowsWeakReference class])
- {
- storeTarget = 1;
- loadTarget = 0;
- }
- else fail("wut");
-
- testprintf("Weak assignment\n");
- state = 0;
- result = storeWeak(&weak, obj);
- testassert(state == storeTarget);
- testassert(result == obj);
- testassert(weak == obj);
-
- testprintf("Weak assignment to the same value\n");
- state = 0;
- result = storeWeak(&weak, obj);
- testassert(state == storeTarget);
- testassert(result == obj);
- testassert(weak == obj);
-
- testprintf("Weak load\n");
- state = 0;
- result = objc_loadWeakRetained(&weak);
- if (state != loadTarget) testprintf("state %d target %d\n", state, loadTarget);
- testassert(state == loadTarget);
- testassert(result == obj);
- testassert(result == weak);
- [result release];
-
- testprintf("Weak assignment to different value\n");
- state = 0;
- result = storeWeak(&weak, obj2);
- testassert(state == storeTarget);
- testassert(result == obj2);
- testassert(weak == obj2);
-
- testprintf("Weak assignment to NULL\n");
- state = 0;
- result = storeWeak(&weak, NULL);
- testassert(state == 0);
- testassert(result == NULL);
- testassert(weak == NULL);
-
- testprintf("Weak re-assignment to NULL\n");
- state = 0;
- result = storeWeak(&weak, NULL);
- testassert(state == 0);
- testassert(result == NULL);
- testassert(weak == NULL);
-
- testprintf("Weak move\n");
- state = 0;
- result = storeWeak(&weak, obj);
- testassert(state == storeTarget);
- testassert(result == obj);
- testassert(weak == obj);
- weak2 = (id)(PAGE_MAX_SIZE-16);
- objc_moveWeak(&weak2, &weak);
- testassert(weak == nil);
- testassert(weak2 == obj);
- storeWeak(&weak2, NULL);
-
- testprintf("Weak copy\n");
- state = 0;
- result = storeWeak(&weak, obj);
- testassert(state == storeTarget);
- testassert(result == obj);
- testassert(weak == obj);
- weak2 = (id)(PAGE_MAX_SIZE-16);
- objc_copyWeak(&weak2, &weak);
- testassert(weak == obj);
- testassert(weak2 == obj);
- storeWeak(&weak, NULL);
- storeWeak(&weak2, NULL);
-
- testprintf("Weak clear\n");
-
- id obj3 = [cls new];
-
- state = 0;
- result = storeWeak(&weak, obj3);
- testassert(state == storeTarget);
- testassert(result == obj3);
- testassert(weak == obj3);
-
- state = 0;
- result = storeWeak(&weak2, obj3);
- testassert(state == storeTarget);
- testassert(result == obj3);
- testassert(weak2 == obj3);
-
- did_dealloc = false;
- [obj3 release];
- testassert(did_dealloc);
- testassert(weak == NULL);
- testassert(weak2 == NULL);
-
-
- testprintf("Weak init and destroy\n");
-
- id obj4 = [cls new];
-
- state = 0;
- weak = (id)0x100; // old value must not be used
- result = initWeak(&weak, obj4);
- testassert(state == storeTarget);
- testassert(result == obj4);
- testassert(weak == obj4);
-
- state = 0;
- weak2 = (id)0x100; // old value must not be used
- result = initWeak(&weak2, obj4);
- testassert(state == storeTarget);
- testassert(result == obj4);
- testassert(weak2 == obj4);
-
- state = 0;
- weak3 = (id)0x100; // old value must not be used
- result = initWeak(&weak3, obj4);
- testassert(state == storeTarget);
- testassert(result == obj4);
- testassert(weak3 == obj4);
-
- state = 0;
- objc_destroyWeak(&weak3);
- testassert(state == 0);
- testassert(weak3 == obj4); // storage is unchanged
-
- did_dealloc = false;
- [obj4 release];
- testassert(did_dealloc);
- testassert(weak == NULL); // not destroyed earlier so cleared now
- testassert(weak2 == NULL); // not destroyed earlier so cleared now
- testassert(weak3 == obj4); // destroyed earlier so not cleared now
-
- objc_destroyWeak(&weak);
- objc_destroyWeak(&weak2);
-}
-
-
-void test_class(Class cls)
-{
- // prime strong and weak side tables before leak checking
- Test *prime[256] = {nil};
- for (size_t i = 0; i < sizeof(prime)/sizeof(prime[0]); i++) {
- objc_storeWeak(&prime[i], [cls new]);
- }
-
- Test *obj = [cls new];
- Test *obj2 = [cls new];
-
- for (int i = 0; i < 100000; i++) {
- cycle(cls, obj, obj2, false);
- cycle(cls, obj, obj2, true);
- }
- leak_mark();
- for (int i = 0; i < 100000; i++) {
- cycle(cls, obj, obj2, false);
- cycle(cls, obj, obj2, true);
- }
- // allow some slop for side table expansion
- // 5120 is common with this configuration
- leak_check(6000);
-
- // rdar://14105994
- id weaks[8];
- for (size_t i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) {
- objc_storeWeak(&weaks[i], obj);
- }
- for (size_t i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) {
- objc_storeWeak(&weaks[i], nil);
- }
-}
-
-int main()
-{
- test_class([Test class]);
- test_class([CustomTryRetain class]);
- test_class([CustomIsDeallocating class]);
- test_class([CustomAllowsWeakReference class]);
- test_class([CustomRetainWeakReference class]);
-
-
- id result;
-
- Crash *obj3 = [Crash new];
- result = objc_storeWeak(&weak, obj3);
- testassert(result == obj3);
- testassert(weak == obj3);
-
- result = objc_storeWeak(&weak2, obj3);
- testassert(result == obj3);
- testassert(weak2 == obj3);
-
- [obj3 release];
- fail("should have crashed in -[Crash dealloc]");
-}
+++ /dev/null
-// TEST_CFLAGS -framework CoreFoundation
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <objc/runtime.h>
-
-#include "test.h"
-
-#if __has_feature(objc_arc)
-
-int main()
-{
- testwarn("rdar://11368528 confused by Foundation");
- succeed(__FILE__);
-}
-
-#else
-
-int main()
-{
- // rdar://6164781 setAssociatedObject on pure-CF object crashes LP64
-
- id obj;
- id array = objc_retainedObject(CFArrayCreate(0, 0, 0, 0));
- testassert(array);
-
- testassert(! objc_getClass("NSCFArray"));
-
- objc_setAssociatedObject(array, (void*)1, array, OBJC_ASSOCIATION_ASSIGN);
-
- obj = objc_getAssociatedObject(array, (void*)1);
- testassert(obj == array);
-
- RELEASE_VAR(array);
-
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include <Foundation/NSObject.h>
-#include <objc/runtime.h>
-
-static int values;
-static int supers;
-static int subs;
-
-static const char *key = "key";
-
-
-@interface Value : NSObject @end
-@interface Super : NSObject @end
-@interface Sub : NSObject @end
-
-@interface Super2 : NSObject @end
-@interface Sub2 : NSObject @end
-
-@implementation Super
--(id) init
-{
- // rdar://8270243 don't lose associations after isa swizzling
-
- id value = [Value new];
- objc_setAssociatedObject(self, &key, value, OBJC_ASSOCIATION_RETAIN);
- RELEASE_VAR(value);
-
- object_setClass(self, [Sub class]);
-
- return self;
-}
-
--(void) dealloc
-{
- supers++;
- SUPER_DEALLOC();
-}
--(void) finalize
-{
- supers++;
- [super finalize];
-}
-
-@end
-
-@implementation Sub
--(void) dealloc
-{
- subs++;
- SUPER_DEALLOC();
-}
--(void) finalize
-{
- subs++;
- [super finalize];
-}
-@end
-
-@implementation Super2
--(id) init
-{
- // rdar://9617109 don't lose associations after isa swizzling
-
- id value = [Value new];
- object_setClass(self, [Sub2 class]);
- objc_setAssociatedObject(self, &key, value, OBJC_ASSOCIATION_RETAIN);
- RELEASE_VAR(value);
- object_setClass(self, [Super2 class]);
-
- return self;
-}
-
--(void) dealloc
-{
- supers++;
- SUPER_DEALLOC();
-}
--(void) finalize
-{
- supers++;
- [super finalize];
-}
-
-@end
-
-@implementation Sub2
--(void) dealloc
-{
- subs++;
- SUPER_DEALLOC();
-}
--(void) finalize
-{
- subs++;
- [super finalize];
-}
-@end
-
-@implementation Value
--(void) dealloc {
- values++;
- SUPER_DEALLOC();
-}
--(void) finalize {
- values++;
- [super finalize];
-}
-@end
-
-
-int main()
-{
- testonthread(^{
- int i;
- for (i = 0; i < 100; i++) {
- RELEASE_VALUE([[Super alloc] init]);
- }
- });
- testcollect();
-
- testassert(supers == 0);
- testassert(subs > 0);
- testassert(subs == values);
-
-
- supers = 0;
- subs = 0;
- values = 0;
-
- testonthread(^{
- int i;
- for (i = 0; i < 100; i++) {
- RELEASE_VALUE([[Super2 alloc] init]);
- }
- });
- testcollect();
-
- testassert(supers > 0);
- testassert(subs == 0);
- testassert(supers == values);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG CC=clang
-
-#include "test.h"
-#include <objc/runtime.h>
-#include <objc/objc-internal.h>
-#import <Foundation/NSObject.h>
-
-class SerialNumber {
- size_t _number;
-public:
- SerialNumber() : _number(42) {}
- SerialNumber(const SerialNumber &number) : _number(number._number + 1) {}
- SerialNumber &operator=(const SerialNumber &number) { _number = number._number + 1; return *this; }
-
- int operator==(const SerialNumber &number) { return _number == number._number; }
- int operator!=(const SerialNumber &number) { return _number != number._number; }
-};
-
-@interface TestAtomicProperty : NSObject {
- SerialNumber number;
-}
-@property(atomic) SerialNumber number;
-@end
-
-@implementation TestAtomicProperty
-
-@synthesize number;
-
-@end
-
-int main()
-{
- PUSH_POOL {
- SerialNumber number;
- TestAtomicProperty *test = [TestAtomicProperty new];
- test.number = number;
- testassert(test.number != number);
- } POP_POOL;
-
- succeed(__FILE__);
-}
+++ /dev/null
-// for OBJC2 mac only
-/* TEST_CONFIG OS=macosx ARCH=x86_64
- TEST_CRASHES
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: objc_removeExceptionHandler\(\) called with unknown alt handler; this is probably a bug in multithreaded AppKit use. Set environment variable OBJC_DEBUG_ALT_HANDLERS=YES or break in objc_alt_handler_error\(\) to debug.
-CRASHED: SIGILL
-END
-*/
-
-#include "test.h"
-
-#include <objc/objc-exception.h>
-
-/*
- rdar://6888838
- Mail installs an alt handler on one thread and deletes it on another.
- This confuses the alt handler machinery, which halts the process.
-*/
-
-uintptr_t Token;
-
-void handler(id unused __unused, void *context __unused)
-{
-}
-
-int main()
-{
-#if __clang__ && __cplusplus
- // alt handlers need the objc personality
- // catch (id) workaround forces the objc personality
- @try {
- testwarn("rdar://9183014 clang uses wrong exception personality");
- } @catch (id e __unused) {
- }
-#endif
-
- @try {
- // Install 4 alt handlers
- uintptr_t t1, t2, t3, t4;
- t1 = objc_addExceptionHandler(&handler, NULL);
- t2 = objc_addExceptionHandler(&handler, NULL);
- t3 = objc_addExceptionHandler(&handler, NULL);
- t4 = objc_addExceptionHandler(&handler, NULL);
-
- // Remove 3 of them.
- objc_removeExceptionHandler(t1);
- objc_removeExceptionHandler(t2);
- objc_removeExceptionHandler(t3);
-
- // Create an alt handler on another thread
- // that collides with one of the removed handlers
- testonthread(^{
- @try {
- Token = objc_addExceptionHandler(&handler, NULL);
- } @catch (...) {
- }
- });
-
- // Incorrectly remove the other thread's handler
- objc_removeExceptionHandler(Token);
- // Remove the 4th handler
- objc_removeExceptionHandler(t4);
-
- // Install 8 more handlers.
- // If the other thread's handler was not ignored,
- // this will fail.
- objc_addExceptionHandler(&handler, NULL);
- objc_addExceptionHandler(&handler, NULL);
- objc_addExceptionHandler(&handler, NULL);
- objc_addExceptionHandler(&handler, NULL);
- objc_addExceptionHandler(&handler, NULL);
- objc_addExceptionHandler(&handler, NULL);
- objc_addExceptionHandler(&handler, NULL);
- objc_addExceptionHandler(&handler, NULL);
- } @catch (...) {
- }
-
- // This should have crashed earlier.
- fail(__FILE__);
-}
+++ /dev/null
-/*
-TEST_CRASHES
-TEST_RUN_OUTPUT
-objc1
-OK: badCache.m
-OR
-crash now
-objc\[\d+\]: Method cache corrupted.*
-objc\[\d+\]: .*
-objc\[\d+\]: .*
-objc\[\d+\]: .*
-objc\[\d+\]: .*
-objc\[\d+\]: Method cache corrupted\.
-CRASHED: SIG(ILL|TRAP)
-END
-*/
-
-
-#include "test.h"
-
-#if !__OBJC2__ || __arm__
-
-int main()
-{
- fprintf(stderr, "objc1\n");
- succeed(__FILE__);
-}
-
-#else
-
-#include "testroot.i"
-
-#if __LP64__
-typedef uint32_t mask_t;
-#else
-typedef uint16_t mask_t;
-#endif
-
-struct bucket_t {
- uintptr_t sel;
- uintptr_t imp;
-};
-
-struct cache_t {
- struct bucket_t *buckets;
- mask_t mask;
- mask_t occupied;
-};
-
-struct class_t {
- void *isa;
- void *supercls;
- struct cache_t cache;
-};
-
-@interface Subclass : TestRoot @end
-@implementation Subclass @end
-
-int main()
-{
- Class cls = [TestRoot class];
- id obj = [cls new];
- [obj self];
-
- struct cache_t *cache = &((__bridge struct class_t *)cls)->cache;
-
-# define COUNT 4
- struct bucket_t *buckets = calloc(sizeof(struct bucket_t), COUNT+1);
- for (int i = 0; i < COUNT; i++) {
- buckets[i].sel = ~0;
- buckets[i].imp = ~0;
- }
- buckets[COUNT].sel = 1;
- buckets[COUNT].imp = (uintptr_t)buckets;
-
- cache->mask = COUNT-1;
- cache->occupied = 0;
- cache->buckets = buckets;
-
- fprintf(stderr, "crash now\n");
- [obj self];
-
- fail("should have crashed");
-}
-
-#endif
+++ /dev/null
-/*
-TEST_CRASHES
-TEST_RUN_OUTPUT
-objc\[\d+\]: tag index 7 used for two different classes \(was 0x[0-9a-fA-F]+ NSObject, now 0x[0-9a-fA-F]+ TestRoot\)
-CRASHED: SIG(ILL|TRAP)
-OR
-no tagged pointers
-OK: badTagClass.m
-END
-*/
-
-#include "test.h"
-#include "testroot.i"
-
-#include <objc/objc-internal.h>
-#include <objc/Protocol.h>
-
-#if OBJC_HAVE_TAGGED_POINTERS
-
-int main()
-{
- // re-registration and nil registration allowed
- _objc_registerTaggedPointerClass(OBJC_TAG_7, [NSObject class]);
- _objc_registerTaggedPointerClass(OBJC_TAG_7, [NSObject class]);
- _objc_registerTaggedPointerClass(OBJC_TAG_7, nil);
- _objc_registerTaggedPointerClass(OBJC_TAG_7, [NSObject class]);
-
- // colliding registration disallowed
- _objc_registerTaggedPointerClass(OBJC_TAG_7, [TestRoot class]);
-
- fail(__FILE__);
-}
-
-#else
-
-int main()
-{
- fprintf(stderr, "no tagged pointers\n");
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-/*
-TEST_CRASHES
-TEST_RUN_OUTPUT
-objc\[\d+\]: tag index 8 is too large.
-CRASHED: SIG(ILL|TRAP)
-OR
-no tagged pointers
-OK: badTagIndex.m
-END
-*/
-
-#include "test.h"
-
-#include <objc/objc-internal.h>
-#include <objc/NSObject.h>
-
-#if OBJC_HAVE_TAGGED_POINTERS
-
-int main()
-{
- _objc_registerTaggedPointerClass((objc_tag_index_t)8, [NSObject class]);
- fail(__FILE__);
-}
-
-#else
-
-int main()
-{
- fprintf(stderr, "no tagged pointers\n");
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-// TEST_CONFIG MEM=mrc
-/*
-TEST_RUN_OUTPUT
-objc\[\d+\]: Deallocator object 0x[0-9a-fA-F]+ overreleased while already deallocating; break on objc_overrelease_during_dealloc_error to debug
-OK: bigrc.m
-OR
-no overrelease enforcement
-OK: bigrc.m
-END
- */
-
-#include "test.h"
-#include "testroot.i"
-
-static size_t LOTS;
-
-@interface Deallocator : TestRoot @end
-@implementation Deallocator
-
--(void)dealloc
-{
- id o = self;
- size_t rc = 1;
-
-
- testprintf("Retain a lot during dealloc\n");
-
- testassert(rc == 1);
- testassert([o retainCount] == rc);
- do {
- [o retain];
- if (rc % 0x100000 == 0) testprintf("%zx/%zx ++\n", rc, LOTS);
- } while (++rc < LOTS);
-
- testassert([o retainCount] == rc);
-
- do {
- [o release];
- if (rc % 0x100000 == 0) testprintf("%zx/%zx --\n", rc, LOTS);
- } while (--rc > 1);
-
- testassert(rc == 1);
- testassert([o retainCount] == rc);
-
-
- testprintf("Overrelease during dealloc\n");
-
- // Not all architectures enforce this.
-#if !SUPPORT_NONPOINTER_ISA
- testwarn("no overrelease enforcement");
- fprintf(stderr, "no overrelease enforcement\n");
-#endif
- [o release];
-
- [super dealloc];
-}
-
-@end
-
-
-int main()
-{
- Deallocator *o = [Deallocator new];
- size_t rc = 1;
-
- [o retain];
-
- uintptr_t isa = *(uintptr_t *)o;
- if (isa & 1) {
- // Assume refcount in high bits.
- LOTS = 1 << (4 + __builtin_clzll(isa));
- testprintf("LOTS %zu via cntlzw\n", LOTS);
- } else {
- LOTS = 0x1000000;
- testprintf("LOTS %zu via guess\n", LOTS);
- }
-
- [o release];
-
-
- testprintf("Retain a lot\n");
-
- testassert(rc == 1);
- testassert([o retainCount] == rc);
- do {
- [o retain];
- if (rc % 0x100000 == 0) testprintf("%zx/%zx ++\n", rc, LOTS);
- } while (++rc < LOTS);
-
- testassert([o retainCount] == rc);
-
- do {
- [o release];
- if (rc % 0x100000 == 0) testprintf("%zx/%zx --\n", rc, LOTS);
- } while (--rc > 1);
-
- testassert(rc == 1);
- testassert([o retainCount] == rc);
-
-
- testprintf("tryRetain a lot\n");
-
- id w;
- objc_storeWeak(&w, o);
- testassert(w == o);
-
- testassert(rc == 1);
- testassert([o retainCount] == rc);
- do {
- objc_loadWeakRetained(&w);
- if (rc % 0x100000 == 0) testprintf("%zx/%zx ++\n", rc, LOTS);
- } while (++rc < LOTS);
-
- testassert([o retainCount] == rc);
-
- do {
- [o release];
- if (rc % 0x100000 == 0) testprintf("%zx/%zx --\n", rc, LOTS);
- } while (--rc > 1);
-
- testassert(rc == 1);
- testassert([o retainCount] == rc);
-
- testprintf("dealloc\n");
-
- testassert(TestRootDealloc == 0);
- testassert(w != nil);
- [o release];
- testassert(TestRootDealloc == 1);
- testassert(w == nil);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -framework Foundation
-
-#include "test.h"
-#include <objc/runtime.h>
-#import <Foundation/Foundation.h>
-
-#include <Block_private.h>
-
-#if !__has_feature(objc_arc)
-# define __bridge
-#endif
-
-#if !__clang__
- // gcc and llvm-gcc will never support struct-return marking
-# define STRET_OK 0
-# define STRET_SPECIAL 0
-#elif __arm64__
- // stret supported, but is identical to non-stret
-# define STRET_OK 1
-# define STRET_SPECIAL 0
-#else
- // stret supported and distinct from non-stret
-# define STRET_OK 1
-# define STRET_SPECIAL 1
-#endif
-
-typedef struct BigStruct {
- uintptr_t datums[200];
-} BigStruct;
-
-@interface Foo:NSObject
-@end
-@implementation Foo
-- (BigStruct) methodThatReturnsBigStruct: (BigStruct) b
-{
- return b;
-}
-@end
-
-@interface Foo(bar)
-- (int) boo: (int) a;
-- (BigStruct) structThatIsBig: (BigStruct) b;
-- (BigStruct) methodThatReturnsBigStruct: (BigStruct) b;
-- (float) methodThatReturnsFloat: (float) aFloat;
-@end
-
-// This is void* instead of id to prevent interference from ARC.
-typedef uintptr_t (*FuncPtr)(void *, SEL);
-typedef BigStruct (*BigStructFuncPtr)(id, SEL, BigStruct);
-typedef float (*FloatFuncPtr)(id, SEL, float);
-
-BigStruct bigfunc(BigStruct a) {
- return a;
-}
-
-@interface Deallocator : NSObject @end
-@implementation Deallocator
--(void) methodThatNobodyElseCalls1 { }
--(void) methodThatNobodyElseCalls2 { }
-id retain_imp(Deallocator *self, SEL _cmd) {
- _objc_flush_caches([Deallocator class]);
- [self methodThatNobodyElseCalls1];
- struct objc_super sup = { self, [[Deallocator class] superclass] };
- return ((id(*)(struct objc_super *, SEL))objc_msgSendSuper)(&sup, _cmd);
-}
-void dealloc_imp(Deallocator *self, SEL _cmd) {
- _objc_flush_caches([Deallocator class]);
- [self methodThatNobodyElseCalls2];
- struct objc_super sup = { self, [[Deallocator class] superclass] };
- ((void(*)(struct objc_super *, SEL))objc_msgSendSuper)(&sup, _cmd);
-}
-+(void) load {
- class_addMethod(self, sel_registerName("retain"), (IMP)retain_imp, "");
- class_addMethod(self, sel_registerName("dealloc"), (IMP)dealloc_imp, "");
-}
-@end
-
-/* Code copied from objc-block-trampolines.m to test Block innards */
-typedef enum {
- ReturnValueInRegisterArgumentMode,
-#if STRET_SPECIAL
- ReturnValueOnStackArgumentMode,
-#endif
-
- ArgumentModeMax
-} ArgumentMode;
-
-static ArgumentMode _argumentModeForBlock(id block) {
- ArgumentMode aMode = ReturnValueInRegisterArgumentMode;
-#if STRET_SPECIAL
- if ( _Block_use_stret((__bridge void *)block) )
- aMode = ReturnValueOnStackArgumentMode;
-#else
- testassert(!_Block_use_stret((__bridge void *)block));
-#endif
-
- return aMode;
-}
-/* End copied code */
-
-int main () {
- // make sure the bits are in place
- int (^registerReturn)() = ^(){ return 42; };
- ArgumentMode aMode;
-
- aMode = _argumentModeForBlock(registerReturn);
- testassert(aMode == ReturnValueInRegisterArgumentMode);
-
-#if STRET_OK
- BigStruct (^stackReturn)() = ^() { BigStruct k; return k; };
- aMode = _argumentModeForBlock(stackReturn);
-# if STRET_SPECIAL
- testassert(aMode == ReturnValueOnStackArgumentMode);
-# else
- testassert(aMode == ReturnValueInRegisterArgumentMode);
-# endif
-#endif
-
-#define TEST_QUANTITY 100000
- static FuncPtr funcArray[TEST_QUANTITY];
-
- uintptr_t i;
- for(i = 0; i<TEST_QUANTITY; i++) {
- uintptr_t (^block)(void *self) = ^uintptr_t(void *self) {
- testassert(i == (uintptr_t)self);
- return i;
- };
- block = (__bridge id)_Block_copy((__bridge void *)block);
-
- funcArray[i] = (FuncPtr) imp_implementationWithBlock(block);
-
- testassert(block((void *)i) == i);
-
- id blockFromIMPResult = imp_getBlock((IMP)funcArray[i]);
- testassert(blockFromIMPResult == (id)block);
-
- _Block_release((__bridge void *)block);
- }
-
- for(i = 0; i<TEST_QUANTITY; i++) {
- uintptr_t result = funcArray[i]((void *)i, 0);
- testassert(i == result);
- }
-
- for(i = 0; i < TEST_QUANTITY; i= i + 3) {
- imp_removeBlock((IMP)funcArray[i]);
- id shouldBeNull = imp_getBlock((IMP)funcArray[i]);
- testassert(shouldBeNull == NULL);
- }
-
- for(i = 0; i < TEST_QUANTITY; i= i + 3) {
- uintptr_t j = i * i;
-
- uintptr_t (^block)(void *) = ^uintptr_t(void *self) {
- testassert(j == (uintptr_t)self);
- return j;
- };
- funcArray[i] = (FuncPtr) imp_implementationWithBlock(block);
-
- testassert(block((void *)j) == j);
- testassert(funcArray[i]((void *)j, 0) == j);
- }
-
- for(i = 0; i < TEST_QUANTITY; i= i + 3) {
- uintptr_t j = i * i;
- uintptr_t result = funcArray[i]((void *)j, 0);
- testassert(j == result);
- }
-
- int (^implBlock)(id, int);
-
- implBlock = ^(id self __attribute__((unused)), int a){
- return -1 * a;
- };
-
- PUSH_POOL {
-
- IMP methodImp = imp_implementationWithBlock(implBlock);
-
- BOOL success = class_addMethod([Foo class], @selector(boo:), methodImp, "i@:i");
- testassert(success);
-
- Foo *f = [Foo new];
- int (*impF)(id self, SEL _cmd, int x) = (int(*)(id, SEL, int)) [Foo instanceMethodForSelector: @selector(boo:)];
-
- int x = impF(f, @selector(boo:), -42);
-
- testassert(x == 42);
- testassert([f boo: -42] == 42);
-
-#if STRET_OK
- BigStruct a;
- for(i=0; i<200; i++)
- a.datums[i] = i;
-
- // slightly more straightforward here
- __block unsigned int state = 0;
- BigStruct (^structBlock)(id, BigStruct) = ^BigStruct(id self __attribute__((unused)), BigStruct c) {
- state++;
- return c;
- };
- BigStruct blockDirect = structBlock(nil, a);
- testassert(!memcmp(&a, &blockDirect, sizeof(BigStruct)));
- testassert(state==1);
-
- IMP bigStructIMP = imp_implementationWithBlock(structBlock);
-
- class_addMethod([Foo class], @selector(structThatIsBig:), bigStructIMP, "oh, type strings, how I hate thee. Fortunately, the runtime doesn't generally care.");
-
- BigStruct b;
-
- BigStructFuncPtr bFunc;
-
- b = bigfunc(a);
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
- b = bigfunc(a);
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
-
- bFunc = (BigStructFuncPtr) [Foo instanceMethodForSelector: @selector(methodThatReturnsBigStruct:)];
-
- b = bFunc(f, @selector(methodThatReturnsBigStruct:), a);
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
-
- b = [f methodThatReturnsBigStruct: a];
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
-
- bFunc = (BigStructFuncPtr) [Foo instanceMethodForSelector: @selector(structThatIsBig:)];
-
- b = bFunc(f, @selector(structThatIsBig:), a);
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
- testassert(state==2);
-
- b = [f structThatIsBig: a];
- testassert(!memcmp(&a, &b, sizeof(BigStruct)));
- testassert(state==3);
- // STRET_OK
-#endif
-
-
- IMP floatIMP = imp_implementationWithBlock(^float (id self __attribute__((unused)), float aFloat ) {
- return aFloat;
- });
- class_addMethod([Foo class], @selector(methodThatReturnsFloat:), floatIMP, "ooh.. type string unspecified again... oh noe... runtime might punish. not.");
-
- float e = (float)0.001;
- float retF = (float)[f methodThatReturnsFloat: 37.1212f];
- testassert( ((retF - e) < 37.1212) && ((retF + e) > 37.1212) );
-
-
-#if !__has_feature(objc_arc)
- // Make sure imp_implementationWithBlock() and imp_removeBlock()
- // don't deadlock while calling Block_copy() and Block_release()
- Deallocator *dead = [[Deallocator alloc] init];
- IMP deadlockImp = imp_implementationWithBlock(^{ [dead self]; });
- [dead release];
- imp_removeBlock(deadlockImp);
-#endif
-
- } POP_POOL;
-
- succeed(__FILE__);
-}
-
+++ /dev/null
-#include <objc/objc.h>
-#include "test.h"
-
-@interface TestRoot(cat)
-+(int)classMethod;
--(int)instanceMethod;
-@end
+++ /dev/null
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/cacheflush0.m -o cacheflush0.dylib -dynamiclib
- $C{COMPILE} $DIR/cacheflush2.m -x none cacheflush0.dylib -o cacheflush2.dylib -dynamiclib
- $C{COMPILE} $DIR/cacheflush3.m -x none cacheflush0.dylib -o cacheflush3.dylib -dynamiclib
- $C{COMPILE} $DIR/cacheflush.m -x none cacheflush0.dylib -o cacheflush.out
-END
-*/
-
-#include "test.h"
-#include <objc/runtime.h>
-#include <dlfcn.h>
-
-#include "cacheflush.h"
-
-@interface Sub : TestRoot @end
-@implementation Sub @end
-
-
-int main()
-{
- TestRoot *sup = [TestRoot new];
- Sub *sub = [Sub new];
-
- // Fill method cache
- testassert(1 == [TestRoot classMethod]);
- testassert(1 == [sup instanceMethod]);
- testassert(1 == [TestRoot classMethod]);
- testassert(1 == [sup instanceMethod]);
-
- testassert(1 == [Sub classMethod]);
- testassert(1 == [sub instanceMethod]);
- testassert(1 == [Sub classMethod]);
- testassert(1 == [sub instanceMethod]);
-
- // Dynamically load a category
- dlopen("cacheflush2.dylib", 0);
-
- // Make sure old cache results are gone
- testassert(2 == [TestRoot classMethod]);
- testassert(2 == [sup instanceMethod]);
-
- testassert(2 == [Sub classMethod]);
- testassert(2 == [sub instanceMethod]);
-
- // Dynamically load another category
- dlopen("cacheflush3.dylib", 0);
-
- // Make sure old cache results are gone
- testassert(3 == [TestRoot classMethod]);
- testassert(3 == [sup instanceMethod]);
-
- testassert(3 == [Sub classMethod]);
- testassert(3 == [sub instanceMethod]);
-
- // fixme test subclasses
-
- // fixme test objc_flush_caches(), class_addMethod(), class_addMethods()
-
- succeed(__FILE__);
-}
+++ /dev/null
-#include "cacheflush.h"
-#include "testroot.i"
-
-@implementation TestRoot(cat)
-+(int)classMethod { return 1; }
--(int)instanceMethod { return 1; }
-@end
+++ /dev/null
-#include "cacheflush.h"
-
-@implementation TestRoot (Category2)
-+(int)classMethod { return 2; }
--(int)instanceMethod { return 2; }
-@end
+++ /dev/null
-#include "cacheflush.h"
-
-@implementation TestRoot (Category3)
-+(int)classMethod { return 3; }
--(int)instanceMethod { return 3; }
-@end
+++ /dev/null
-// TEST_CFLAGS -Wl,-no_objc_category_merging
-
-#include "test.h"
-#include "testroot.i"
-#include <string.h>
-#include <objc/runtime.h>
-
-static int state = 0;
-
-@interface Super : TestRoot @end
-@implementation Super
--(void)instancemethod { fail("-instancemethod not overridden by category"); }
-+(void)method { fail("+method not overridden by category"); }
-@end
-
-@interface Super (Category) @end
-@implementation Super (Category)
-+(void)method {
- testprintf("in [Super(Category) method]\n");
- testassert(self == [Super class]);
- testassert(state == 0);
- state = 1;
-}
--(void)instancemethod {
- testprintf("in [Super(Category) instancemethod]\n");
- testassert(object_getClass(self) == [Super class]);
- testassert(state == 1);
- state = 2;
-}
-@end
-
-@interface Super (PropertyCategory)
-@property int i;
-@end
-@implementation Super (PropertyCategory)
-- (int)i { return 0; }
-- (void)setI:(int)value { (void)value; }
-@end
-
-// rdar://5086110 memory smasher in category with class method and property
-@interface Super (r5086110)
-@property int property5086110;
-@end
-@implementation Super (r5086110)
-+(void)method5086110 {
- fail("method method5086110 called!");
-}
-- (int)property5086110 { fail("property5086110 called!"); return 0; }
-- (void)setProperty5086110:(int)value { fail("setProperty5086110 called!"); (void)value; }
-@end
-
-
-@interface PropertyClass : Super {
- int q;
-}
-@property(readonly) int q;
-@end
-@implementation PropertyClass
-@synthesize q;
-@end
-
-@interface PropertyClass (PropertyCategory)
-@property int q;
-@end
-@implementation PropertyClass (PropertyCategory)
-@dynamic q;
-@end
-
-
-int main()
-{
- // methods introduced by category
- state = 0;
- [Super method];
- [[Super new] instancemethod];
- testassert(state == 2);
-
- // property introduced by category
- objc_property_t p = class_getProperty([Super class], "i");
- testassert(p);
- testassert(0 == strcmp(property_getName(p), "i"));
- testassert(property_getAttributes(p));
-
- // methods introduced by category's property
- Method m;
- m = class_getInstanceMethod([Super class], @selector(i));
- testassert(m);
- m = class_getInstanceMethod([Super class], @selector(setI:));
- testassert(m);
-
- // class's property shadowed by category's property
- objc_property_t *plist = class_copyPropertyList([PropertyClass class], NULL);
- testassert(plist);
- testassert(plist[0]);
- testassert(0 == strcmp(property_getName(plist[0]), "q"));
- testassert(0 == strcmp(property_getAttributes(plist[0]), "Ti,D"));
- testassert(plist[1]);
- testassert(0 == strcmp(property_getName(plist[1]), "q"));
- testassert(0 == strcmp(property_getAttributes(plist[1]), "Ti,R,Vq"));
- testassert(!plist[2]);
- free(plist);
-
- succeed(__FILE__);
-}
-
+++ /dev/null
-// TEST_CONFIG
-
-#if USE_FOUNDATION
-#include <Foundation/Foundation.h>
-#define SUPERCLASS NSObject
-#define FILENAME "nscdtors.mm"
-#else
-#define SUPERCLASS TestRoot
-#define FILENAME "cdtors.mm"
-#endif
-
-#include "test.h"
-
-#include <pthread.h>
-#include "objc/objc-internal.h"
-#include "testroot.i"
-
-static unsigned ctors1 = 0;
-static unsigned dtors1 = 0;
-static unsigned ctors2 = 0;
-static unsigned dtors2 = 0;
-
-class cxx1 {
- unsigned & ctors;
- unsigned& dtors;
-
- public:
- cxx1() : ctors(ctors1), dtors(dtors1) { ctors++; }
-
- ~cxx1() { dtors++; }
-};
-class cxx2 {
- unsigned& ctors;
- unsigned& dtors;
-
- public:
- cxx2() : ctors(ctors2), dtors(dtors2) { ctors++; }
-
- ~cxx2() { dtors++; }
-};
-
-/*
- Class hierarchy:
- TestRoot
- CXXBase
- NoCXXSub
- CXXSub
-
- This has two cxx-wielding classes, and a class in between without cxx.
-*/
-
-
-@interface CXXBase : SUPERCLASS {
- cxx1 baseIvar;
-}
-@end
-@implementation CXXBase @end
-
-@interface NoCXXSub : CXXBase {
- int nocxxIvar;
-}
-@end
-@implementation NoCXXSub @end
-
-@interface CXXSub : NoCXXSub {
- cxx2 subIvar;
-}
-@end
-@implementation CXXSub @end
-
-
-void test_single(void)
-{
- // Single allocation
-
- ctors1 = dtors1 = ctors2 = dtors2 = 0;
- testonthread(^{
- id o = [TestRoot new];
- testassert(ctors1 == 0 && dtors1 == 0 &&
- ctors2 == 0 && dtors2 == 0);
- testassert([o class] == [TestRoot class]);
- RELEASE_VAR(o);
- });
- testcollect();
- testassert(ctors1 == 0 && dtors1 == 0 &&
- ctors2 == 0 && dtors2 == 0);
-
- ctors1 = dtors1 = ctors2 = dtors2 = 0;
- testonthread(^{
- id o = [CXXBase new];
- testassert(ctors1 == 1 && dtors1 == 0 &&
- ctors2 == 0 && dtors2 == 0);
- testassert([o class] == [CXXBase class]);
- RELEASE_VAR(o);
- });
- testcollect();
- testassert(ctors1 == 1 && dtors1 == 1 &&
- ctors2 == 0 && dtors2 == 0);
-
- ctors1 = dtors1 = ctors2 = dtors2 = 0;
- testonthread(^{
- id o = [NoCXXSub new];
- testassert(ctors1 == 1 && dtors1 == 0 &&
- ctors2 == 0 && dtors2 == 0);
- testassert([o class] == [NoCXXSub class]);
- RELEASE_VAR(o);
- });
- testcollect();
- testassert(ctors1 == 1 && dtors1 == 1 &&
- ctors2 == 0 && dtors2 == 0);
-
- ctors1 = dtors1 = ctors2 = dtors2 = 0;
- testonthread(^{
- id o = [CXXSub new];
- testassert(ctors1 == 1 && dtors1 == 0 &&
- ctors2 == 1 && dtors2 == 0);
- testassert([o class] == [CXXSub class]);
- RELEASE_VAR(o);
- });
- testcollect();
- testassert(ctors1 == 1 && dtors1 == 1 &&
- ctors2 == 1 && dtors2 == 1);
-}
-
-void test_inplace(void)
-{
- __unsafe_unretained volatile id o;
- char o2[64];
-
- id (*objc_constructInstance_fn)(Class, void*) = (id(*)(Class, void*))dlsym(RTLD_DEFAULT, "objc_constructInstance");
- void (*objc_destructInstance_fn)(id) = (void(*)(id))dlsym(RTLD_DEFAULT, "objc_destructInstance");
-
- // In-place allocation
-
- ctors1 = dtors1 = ctors2 = dtors2 = 0;
- o = objc_constructInstance_fn([TestRoot class], o2);
- testassert(ctors1 == 0 && dtors1 == 0 &&
- ctors2 == 0 && dtors2 == 0);
- testassert([o class] == [TestRoot class]);
- objc_destructInstance_fn(o), o = nil;
- testcollect();
- testassert(ctors1 == 0 && dtors1 == 0 &&
- ctors2 == 0 && dtors2 == 0);
-
- ctors1 = dtors1 = ctors2 = dtors2 = 0;
- o = objc_constructInstance_fn([CXXBase class], o2);
- testassert(ctors1 == 1 && dtors1 == 0 &&
- ctors2 == 0 && dtors2 == 0);
- testassert([o class] == [CXXBase class]);
- objc_destructInstance_fn(o), o = nil;
- testcollect();
- testassert(ctors1 == 1 && dtors1 == 1 &&
- ctors2 == 0 && dtors2 == 0);
-
- ctors1 = dtors1 = ctors2 = dtors2 = 0;
- o = objc_constructInstance_fn([NoCXXSub class], o2);
- testassert(ctors1 == 1 && dtors1 == 0 &&
- ctors2 == 0 && dtors2 == 0);
- testassert([o class] == [NoCXXSub class]);
- objc_destructInstance_fn(o), o = nil;
- testcollect();
- testassert(ctors1 == 1 && dtors1 == 1 &&
- ctors2 == 0 && dtors2 == 0);
-
- ctors1 = dtors1 = ctors2 = dtors2 = 0;
- o = objc_constructInstance_fn([CXXSub class], o2);
- testassert(ctors1 == 1 && dtors1 == 0 &&
- ctors2 == 1 && dtors2 == 0);
- testassert([o class] == [CXXSub class]);
- objc_destructInstance_fn(o), o = nil;
- testcollect();
- testassert(ctors1 == 1 && dtors1 == 1 &&
- ctors2 == 1 && dtors2 == 1);
-}
-
-
-#if __has_feature(objc_arc)
-
-void test_batch(void)
-{
- // not converted to ARC yet
- return;
-}
-
-#else
-
-// Like class_createInstances(), but refuses to accept zero allocations
-static unsigned
-reallyCreateInstances(Class cls, size_t extraBytes, id *dst, unsigned want)
-{
- unsigned count;
- while (0 == (count = class_createInstances(cls, extraBytes, dst, want))) {
- testprintf("class_createInstances created nothing; retrying\n");
- RELEASE_VALUE([[TestRoot alloc] init]);
- }
- return count;
-}
-
-void test_batch(void)
-{
- id o2[100];
- unsigned int count, i;
-
- // Batch allocation
-
- for (i = 0; i < 100; i++) {
- o2[i] = (id)malloc(class_getInstanceSize([TestRoot class]));
- }
- for (i = 0; i < 100; i++) {
- free(o2[i]);
- }
-
- ctors1 = dtors1 = ctors2 = dtors2 = 0;
- count = reallyCreateInstances([TestRoot class], 0, o2, 10);
- testassert(count > 0);
- testassert(ctors1 == 0 && dtors1 == 0 &&
- ctors2 == 0 && dtors2 == 0);
- for (i = 0; i < count; i++) testassert([o2[i] class] == [TestRoot class]);
- for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
- testcollect();
- testassert(ctors1 == 0 && dtors1 == 0 &&
- ctors2 == 0 && dtors2 == 0);
-
- for (i = 0; i < 100; i++) {
- // prime batch allocator
- free(malloc(class_getInstanceSize([TestRoot class])));
- }
-
- ctors1 = dtors1 = ctors2 = dtors2 = 0;
- count = reallyCreateInstances([CXXBase class], 0, o2, 10);
- testassert(count > 0);
- testassert(ctors1 == count && dtors1 == 0 &&
- ctors2 == 0 && dtors2 == 0);
- for (i = 0; i < count; i++) testassert([o2[i] class] == [CXXBase class]);
- for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
- testcollect();
- testassert(ctors1 == count && dtors1 == count &&
- ctors2 == 0 && dtors2 == 0);
-
- for (i = 0; i < 100; i++) {
- // prime batch allocator
- free(malloc(class_getInstanceSize([TestRoot class])));
- }
-
- ctors1 = dtors1 = ctors2 = dtors2 = 0;
- count = reallyCreateInstances([NoCXXSub class], 0, o2, 10);
- testassert(count > 0);
- testassert(ctors1 == count && dtors1 == 0 &&
- ctors2 == 0 && dtors2 == 0);
- for (i = 0; i < count; i++) testassert([o2[i] class] == [NoCXXSub class]);
- for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
- testcollect();
- testassert(ctors1 == count && dtors1 == count &&
- ctors2 == 0 && dtors2 == 0);
-
- for (i = 0; i < 100; i++) {
- // prime batch allocator
- free(malloc(class_getInstanceSize([TestRoot class])));
- }
-
- ctors1 = dtors1 = ctors2 = dtors2 = 0;
- count = reallyCreateInstances([CXXSub class], 0, o2, 10);
- testassert(count > 0);
- testassert(ctors1 == count && dtors1 == 0 &&
- ctors2 == count && dtors2 == 0);
- for (i = 0; i < count; i++) testassert([o2[i] class] == [CXXSub class]);
- for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
- testcollect();
- testassert(ctors1 == count && dtors1 == count &&
- ctors2 == count && dtors2 == count);
-}
-
-// not ARC
-#endif
-
-
-int main()
-{
- if (objc_collectingEnabled()) {
- testwarn("rdar://19042235 test disabled in GC because it is slow");
- succeed(FILENAME);
- }
-
- for (int i = 0; i < 1000; i++) {
- testonthread(^{ test_single(); });
- testonthread(^{ test_inplace(); });
- testonthread(^{ test_batch(); });
- }
-
- testonthread(^{ test_single(); });
- testonthread(^{ test_inplace(); });
- testonthread(^{ test_batch(); });
-
- leak_mark();
-
- for (int i = 0; i < 1000; i++) {
- testonthread(^{ test_single(); });
- testonthread(^{ test_inplace(); });
- testonthread(^{ test_batch(); });
- }
-
- leak_check(0);
-
- // fixme ctor exceptions aren't caught inside .cxx_construct ?
- // Single allocation, ctors fail
- // In-place allocation, ctors fail
- // Batch allocation, ctors fail for every object
- // Batch allocation, ctors fail for every other object
-
- succeed(FILENAME);
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include <objc/objc-runtime.h>
-#include <objc/objc-gdb.h>
-#import <Foundation/NSObject.h>
-
-@interface Foo:NSObject
-@end
-@implementation Foo
-@end
-
-int main()
-{
-#if __OBJC2__
- testassert(gdb_class_getClass([Foo class]) == [Foo class]);
-#endif
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include "testroot.i"
-#include <string.h>
-#include <objc/runtime.h>
-
-@interface Fake : TestRoot @end
-@implementation Fake @end
-
-int main()
-{
- TestRoot *obj = [TestRoot new];
- Class __unsafe_unretained * buf = (Class *)objc_unretainedPointer(obj);
- *buf = [Fake class];
-
- testassert(object_getClass(obj) == [Fake class]);
- testassert(object_setClass(obj, [TestRoot class]) == [Fake class]);
- testassert(object_getClass(obj) == [TestRoot class]);
- testassert(object_setClass(nil, [TestRoot class]) == nil);
-
- testassert(malloc_size(buf) >= sizeof(id));
- bzero(buf, malloc_size(buf));
- testassert(object_setClass(obj, [TestRoot class]) == nil);
-
- testassert(object_getClass(obj) == [TestRoot class]);
- testassert(object_getClass([TestRoot class]) == object_getClass([TestRoot class]));
- testassert(object_getClass(nil) == Nil);
-
- testassert(0 == strcmp(object_getClassName(obj), "TestRoot"));
- testassert(0 == strcmp(object_getClassName([TestRoot class]), "TestRoot"));
- testassert(0 == strcmp(object_getClassName(nil), "nil"));
-
- testassert(0 == strcmp(class_getName([TestRoot class]), "TestRoot"));
- testassert(0 == strcmp(class_getName(object_getClass([TestRoot class])), "TestRoot"));
- testassert(0 == strcmp(class_getName(nil), "nil"));
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -Wno-deprecated-declarations
-
-#include "test.h"
-
-#include "testroot.i"
-#include <objc/runtime.h>
-#include <string.h>
-#ifndef OBJC_NO_GC
-#include <objc/objc-auto.h>
-#include <auto_zone.h>
-#endif
-
-@protocol Proto
--(void) instanceMethod;
-+(void) classMethod;
-@optional
--(void) instanceMethod2;
-+(void) classMethod2;
-@end
-
-@protocol Proto2
--(void) instanceMethod;
-+(void) classMethod;
-@optional
--(void) instanceMethod2;
-+(void) classMethod_that_does_not_exist;
-@end
-
-@protocol Proto3
--(void) instanceMethod;
-+(void) classMethod_that_does_not_exist;
-@optional
--(void) instanceMethod2;
-+(void) classMethod2;
-@end
-
-static int super_initialize;
-
-@interface Super : TestRoot
-@property int superProp;
-@end
-@implementation Super
-@dynamic superProp;
-+(void)initialize { super_initialize++; }
-
-+(void) classMethod { fail("+[Super classMethod] called"); }
-+(void) classMethod2 { fail("+[Super classMethod2] called"); }
--(void) instanceMethod { fail("-[Super instanceMethod] called"); }
--(void) instanceMethod2 { fail("-[Super instanceMethod2] called"); }
-@end
-
-@interface WeakSuper : Super { __weak id weakIvar; } @end
-@implementation WeakSuper @end
-
-static int state;
-
-static void instance_fn(id self, SEL _cmd __attribute__((unused)))
-{
- testassert(!class_isMetaClass(object_getClass(self)));
- state++;
-}
-
-static void class_fn(id self, SEL _cmd __attribute__((unused)))
-{
- testassert(class_isMetaClass(object_getClass(self)));
- state++;
-}
-
-static void fail_fn(id self __attribute__((unused)), SEL _cmd)
-{
- fail("fail_fn '%s' called", sel_getName(_cmd));
-}
-
-
-static void cycle(void)
-{
- Class cls;
- BOOL ok;
- objc_property_t prop;
- char namebuf[256];
-
- testassert(!objc_getClass("Sub"));
- testassert([Super class]);
-
- // Test subclass with bells and whistles
-
- cls = objc_allocateClassPair([Super class], "Sub", 0);
- testassert(cls);
-#ifndef OBJC_NO_GC
- if (objc_collectingEnabled()) {
- testassert(auto_zone_size(objc_collectableZone(), objc_unretainedPointer(cls)));
- testassert(auto_zone_size(objc_collectableZone(), objc_unretainedPointer(object_getClass(cls))));
- }
-#endif
-
- class_addMethod(cls, @selector(instanceMethod),
- (IMP)&instance_fn, "v@:");
- class_addMethod(object_getClass(cls), @selector(classMethod),
- (IMP)&class_fn, "v@:");
- class_addMethod(object_getClass(cls), @selector(initialize),
- (IMP)&class_fn, "v@:");
- class_addMethod(object_getClass(cls), @selector(load),
- (IMP)&fail_fn, "v@:");
-
- ok = class_addProtocol(cls, @protocol(Proto));
- testassert(ok);
- ok = class_addProtocol(cls, @protocol(Proto));
- testassert(!ok);
-
- char attrname[2];
- char attrvalue[2];
- objc_property_attribute_t attrs[1];
- unsigned int attrcount = sizeof(attrs) / sizeof(attrs[0]);
-
- attrs[0].name = attrname;
- attrs[0].value = attrvalue;
- strcpy(attrname, "T");
- strcpy(attrvalue, "x");
-
- strcpy(namebuf, "subProp");
- ok = class_addProperty(cls, namebuf, attrs, attrcount);
- testassert(ok);
- strcpy(namebuf, "subProp");
- ok = class_addProperty(cls, namebuf, attrs, attrcount);
- testassert(!ok);
- strcpy(attrvalue, "i");
- class_replaceProperty(cls, namebuf, attrs, attrcount);
- strcpy(namebuf, "superProp");
- ok = class_addProperty(cls, namebuf, attrs, attrcount);
- testassert(!ok);
- bzero(namebuf, sizeof(namebuf));
- bzero(attrs, sizeof(attrs));
- bzero(attrname, sizeof(attrname));
- bzero(attrvalue, sizeof(attrvalue));
-
-#ifndef __LP64__
-# define size 4
-# define align 2
-#else
-#define size 8
-# define align 3
-#endif
-
- /*
- {
- int ivar;
- id ivarid;
- id* ivaridstar;
- Block_t ivarblock;
- }
- */
- ok = class_addIvar(cls, "ivar", 4, 2, "i");
- testassert(ok);
- ok = class_addIvar(cls, "ivarid", size, align, "@");
- testassert(ok);
- ok = class_addIvar(cls, "ivaridstar", size, align, "^@");
- testassert(ok);
- ok = class_addIvar(cls, "ivarblock", size, align, "@?");
- testassert(ok);
-
- ok = class_addIvar(cls, "ivar", 4, 2, "i");
- testassert(!ok);
- ok = class_addIvar(object_getClass(cls), "classvar", 4, 2, "i");
- testassert(!ok);
-
- objc_registerClassPair(cls);
-
- // should call cls's +initialize, not super's
- // Provoke +initialize using class_getMethodImplementation(class method)
- // in order to test getNonMetaClass's slow case
- super_initialize = 0;
- state = 0;
- class_getMethodImplementation(object_getClass(cls), @selector(class));
- testassert(super_initialize == 0);
- testassert(state == 1);
-
- testassert(cls == [cls class]);
- testassert(cls == objc_getClass("Sub"));
-
- testassert(!class_isMetaClass(cls));
- testassert(class_isMetaClass(object_getClass(cls)));
-
- testassert(class_getSuperclass(cls) == [Super class]);
- testassert(class_getSuperclass(object_getClass(cls)) == object_getClass([Super class]));
-
- testassert(class_getInstanceSize(cls) >= sizeof(Class) + 4 + 3*size);
- testassert(class_conformsToProtocol(cls, @protocol(Proto)));
-
- if (objc_collectingEnabled()) {
- testassert(0 == strcmp((char *)class_getIvarLayout(cls), "\x01\x13"));
- testassert(NULL == class_getWeakIvarLayout(cls));
- }
-
- class_addMethod(cls, @selector(instanceMethod2),
- (IMP)&instance_fn, "v@:");
- class_addMethod(object_getClass(cls), @selector(classMethod2),
- (IMP)&class_fn, "v@:");
-
- ok = class_addIvar(cls, "ivar2", 4, 4, "i");
- testassert(!ok);
- ok = class_addIvar(object_getClass(cls), "classvar2", 4, 4, "i");
- testassert(!ok);
-
- ok = class_addProtocol(cls, @protocol(Proto2));
- testassert(ok);
- ok = class_addProtocol(cls, @protocol(Proto2));
- testassert(!ok);
- ok = class_addProtocol(cls, @protocol(Proto));
- testassert(!ok);
-
- attrs[0].name = attrname;
- attrs[0].value = attrvalue;
- strcpy(attrname, "T");
- strcpy(attrvalue, "i");
-
- strcpy(namebuf, "subProp2");
- ok = class_addProperty(cls, namebuf, attrs, attrcount);
- testassert(ok);
- strcpy(namebuf, "subProp");
- ok = class_addProperty(cls, namebuf, attrs, attrcount);
- testassert(!ok);
- strcpy(namebuf, "superProp");
- ok = class_addProperty(cls, namebuf, attrs, attrcount);
- testassert(!ok);
- bzero(namebuf, sizeof(namebuf));
- bzero(attrs, sizeof(attrs));
- bzero(attrname, sizeof(attrname));
- bzero(attrvalue, sizeof(attrvalue));
-
- prop = class_getProperty(cls, "subProp");
- testassert(prop);
- testassert(0 == strcmp(property_getName(prop), "subProp"));
- testassert(0 == strcmp(property_getAttributes(prop), "Ti"));
- prop = class_getProperty(cls, "subProp2");
- testassert(prop);
- testassert(0 == strcmp(property_getName(prop), "subProp2"));
- testassert(0 == strcmp(property_getAttributes(prop), "Ti"));
-
- // note: adding more methods here causes a false leak check failure
- state = 0;
- [cls classMethod];
- [cls classMethod2];
- testassert(state == 2);
-
- // put instance tests on a separate thread so they
- // are reliably GC'd before class destruction
- testonthread(^{
- id obj = [cls new];
- state = 0;
- [obj instanceMethod];
- [obj instanceMethod2];
- testassert(state == 2);
- RELEASE_VAR(obj);
- });
-
- // Test ivar layouts of sub-subclass
- Class cls2 = objc_allocateClassPair(cls, "SubSub", 0);
- testassert(cls2);
-
- /*
- {
- id ivarid2;
- id idarray[16];
- void* ptrarray[16];
- char a;
- char b;
- char c;
- }
- */
- ok = class_addIvar(cls2, "ivarid2", size, align, "@");
- testassert(ok);
- ok = class_addIvar(cls2, "idarray", 16*sizeof(id), align, "[16@]");
- testassert(ok);
- ok = class_addIvar(cls2, "ptrarray", 16*sizeof(void*), align, "[16^]");
- testassert(ok);
- ok = class_addIvar(cls2, "a", 1, 0, "c");
- testassert(ok);
- ok = class_addIvar(cls2, "b", 1, 0, "c");
- testassert(ok);
- ok = class_addIvar(cls2, "c", 1, 0, "c");
- testassert(ok);
-
- objc_registerClassPair(cls2);
-
- if (objc_collectingEnabled()) {
- testassert(0 == strcmp((char *)class_getIvarLayout(cls2), "\x01\x1f\x05\xf0\x10"));
- testassert(NULL == class_getWeakIvarLayout(cls2));
- }
-
- // 1-byte ivars should be well packed
- testassert(ivar_getOffset(class_getInstanceVariable(cls2, "b")) ==
- ivar_getOffset(class_getInstanceVariable(cls2, "a")) + 1);
- testassert(ivar_getOffset(class_getInstanceVariable(cls2, "c")) ==
- ivar_getOffset(class_getInstanceVariable(cls2, "b")) + 1);
-
- testcollect(); // GC: finalize "obj" above before disposing its class
- objc_disposeClassPair(cls2);
- objc_disposeClassPair(cls);
-
- testassert(!objc_getClass("Sub"));
-
-
- // Test unmodified ivar layouts
-
- cls = objc_allocateClassPair([Super class], "Sub2", 0);
- testassert(cls);
- objc_registerClassPair(cls);
- if (objc_collectingEnabled()) {
- const char *l1, *l2;
- l1 = (char *)class_getIvarLayout([Super class]);
- l2 = (char *)class_getIvarLayout(cls);
- testassert(l1 == l2 || 0 == strcmp(l1, l2));
- l1 = (char *)class_getWeakIvarLayout([Super class]);
- l2 = (char *)class_getWeakIvarLayout(cls);
- testassert(l1 == l2 || 0 == strcmp(l1, l2));
- }
- objc_disposeClassPair(cls);
-
- cls = objc_allocateClassPair([WeakSuper class], "Sub3", 0);
- testassert(cls);
- objc_registerClassPair(cls);
- if (objc_collectingEnabled()) {
- const char *l1, *l2;
- l1 = (char *)class_getIvarLayout([WeakSuper class]);
- l2 = (char *)class_getIvarLayout(cls);
- testassert(l1 == l2 || 0 == strcmp(l1, l2));
- l1 = (char *)class_getWeakIvarLayout([WeakSuper class]);
- l2 = (char *)class_getWeakIvarLayout(cls);
- testassert(l1 == l2 || 0 == strcmp(l1, l2));
- }
- objc_disposeClassPair(cls);
-
- // Test layout setters
- if (objc_collectingEnabled()) {
- cls = objc_allocateClassPair([Super class], "Sub4", 0);
- testassert(cls);
- class_setIvarLayout(cls, (uint8_t *)"foo");
- class_setWeakIvarLayout(cls, NULL);
- objc_registerClassPair(cls);
- testassert(0 == strcmp("foo", (char *)class_getIvarLayout(cls)));
- testassert(NULL == class_getWeakIvarLayout(cls));
- objc_disposeClassPair(cls);
-
- cls = objc_allocateClassPair([Super class], "Sub5", 0);
- testassert(cls);
- class_setIvarLayout(cls, NULL);
- class_setWeakIvarLayout(cls, (uint8_t *)"bar");
- objc_registerClassPair(cls);
- testassert(NULL == class_getIvarLayout(cls));
- testassert(0 == strcmp("bar", (char *)class_getWeakIvarLayout(cls)));
- objc_disposeClassPair(cls);
- }
-}
-
-int main()
-{
- int count = 1000;
-
- testonthread(^{ cycle(); });
- testonthread(^{ cycle(); });
- testonthread(^{ cycle(); });
-
- leak_mark();
- while (count--) {
- testonthread(^{ cycle(); });
- }
-#if __OBJC_GC__
- testwarn("rdar://19042235 possible leaks suppressed under GC");
- leak_check(16000);
-#else
- leak_check(0);
-#endif
-
- succeed(__FILE__);
-}
-
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include "testroot.i"
-#include <objc/runtime.h>
-
-int main()
-{
- Class cls = [TestRoot class];
- testassert(class_getVersion(cls) == 0);
- testassert(class_getVersion(object_getClass(cls)) > 5);
- class_setVersion(cls, 100);
- testassert(class_getVersion(cls) == 100);
-
- testassert(class_getVersion(Nil) == 0);
- class_setVersion(Nil, 100);
-
- succeed(__FILE__);
-}
+++ /dev/null
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/concurrentcat.m -o concurrentcat.out -framework Foundation
-
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc1 -o cc1.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc2 -o cc2.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc3 -o cc3.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc4 -o cc4.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc5 -o cc5.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc6 -o cc6.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc7 -o cc7.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc8 -o cc8.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc9 -o cc9.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc10 -o cc10.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc11 -o cc11.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc12 -o cc12.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc13 -o cc13.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc14 -o cc14.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/concurrentcat_category.m -DTN=cc15 -o cc15.dylib
-END
-*/
-
-#include "test.h"
-#include <objc/runtime.h>
-#include <objc/objc-auto.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#include <pthread.h>
-#include <Foundation/Foundation.h>
-
-@interface TargetClass : NSObject
-@end
-
-@interface TargetClass(LoadedMethods)
-- (void) m0;
-- (void) m1;
-- (void) m2;
-- (void) m3;
-- (void) m4;
-- (void) m5;
-- (void) m6;
-- (void) m7;
-- (void) m8;
-- (void) m9;
-- (void) m10;
-- (void) m11;
-- (void) m12;
-- (void) m13;
-- (void) m14;
-- (void) m15;
-@end
-
-@implementation TargetClass
-- (void) m0 { fail("shoulda been loaded!"); }
-- (void) m1 { fail("shoulda been loaded!"); }
-- (void) m2 { fail("shoulda been loaded!"); }
-- (void) m3 { fail("shoulda been loaded!"); }
-- (void) m4 { fail("shoulda been loaded!"); }
-- (void) m5 { fail("shoulda been loaded!"); }
-- (void) m6 { fail("shoulda been loaded!"); }
-@end
-
-void *threadFun(void *aTargetClassName) {
- const char *className = (const char *)aTargetClassName;
-
- objc_registerThreadWithCollector();
-
- PUSH_POOL {
-
- Class targetSubclass = objc_getClass(className);
- testassert(targetSubclass);
-
- id target = [targetSubclass new];
- testassert(target);
-
- while(1) {
- [target m0];
- RETAIN(target);
- [target addObserver: target forKeyPath: @"m3" options: 0 context: NULL];
- [target addObserver: target forKeyPath: @"m4" options: 0 context: NULL];
- [target m1];
- RELEASE_VALUE(target);
- [target m2];
- AUTORELEASE(target);
- [target m3];
- RETAIN(target);
- [target removeObserver: target forKeyPath: @"m4"];
- [target addObserver: target forKeyPath: @"m5" options: 0 context: NULL];
- [target m4];
- RETAIN(target);
- [target m5];
- AUTORELEASE(target);
- [target m6];
- [target m7];
- [target m8];
- [target m9];
- [target m10];
- [target m11];
- [target m12];
- [target m13];
- [target m14];
- [target m15];
- [target removeObserver: target forKeyPath: @"m3"];
- [target removeObserver: target forKeyPath: @"m5"];
- }
- } POP_POOL;
- return NULL;
-}
-
-int main()
-{
- int i;
-
- void *dylib;
-
- for(i=1; i<16; i++) {
- pthread_t t;
- char dlName[100];
- sprintf(dlName, "cc%d.dylib", i);
- dylib = dlopen(dlName, RTLD_LAZY);
- char className[100];
- sprintf(className, "cc%d", i);
- pthread_create(&t, NULL, threadFun, strdup(className));
- testassert(dylib);
- }
- sleep(1);
-
- succeed(__FILE__);
-}
+++ /dev/null
-#include <stdio.h>
-#include <objc/runtime.h>
-#import <Foundation/Foundation.h>
-
-@interface TargetClass : NSObject
-@end
-
-@interface TargetClass(LoadedMethods)
-- (void) m0;
-- (void) m1;
-- (void) m2;
-- (void) m3;
-- (void) m4;
-- (void) m5;
-- (void) m6;
-- (void) m7;
-- (void) m8;
-- (void) m9;
-- (void) m10;
-- (void) m11;
-- (void) m12;
-- (void) m13;
-- (void) m14;
-- (void) m15;
-@end
-
-@interface TN:TargetClass
-@end
-
-@implementation TN
-- (void) m1 { [super m1]; }
-- (void) m3 { [self m1]; }
-
-- (void) m2
-{
- [self willChangeValueForKey: @"m4"];
- [self didChangeValueForKey: @"m4"];
-}
-
-- (void)observeValueForKeyPath:(NSString *) keyPath
- ofObject:(id)object
- change:(NSDictionary *)change
- context:(void *)context
-{
- // suppress warning
- keyPath = nil;
- object = nil;
- change = nil;
- context = NULL;
-}
-@end
-
-@implementation TargetClass(LoadedMethods)
-- (void) m0 { ; }
-- (void) m1 { ; }
-- (void) m2 { ; }
-- (void) m3 { ; }
-- (void) m4 { ; }
-- (void) m5 { ; }
-- (void) m6 { ; }
-- (void) m7 { ; }
-- (void) m8 { ; }
-- (void) m9 { ; }
-- (void) m10 { ; }
-- (void) m11 { ; }
-- (void) m12 { ; }
-- (void) m13 { ; }
-- (void) m14 { ; }
-- (void) m15 { ; }
-@end
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include <string.h>
-#include <malloc/malloc.h>
-#include <objc/objc-runtime.h>
-
-OBJC_ROOT_CLASS
-@interface SuperIvars {
- id isa;
- int ivar1;
- int ivar2;
-} @end
-@implementation SuperIvars @end
-
-@interface SubIvars : SuperIvars {
- int ivar3;
- int ivar4;
-} @end
-@implementation SubIvars @end
-
-OBJC_ROOT_CLASS
-@interface FourIvars {
- int ivar1;
- int ivar2;
- int ivar3;
- int ivar4;
-} @end
-@implementation FourIvars @end
-
-OBJC_ROOT_CLASS
-@interface NoIvars { } @end
-@implementation NoIvars @end
-
-static int isNamed(Ivar iv, const char *name)
-{
- return (0 == strcmp(name, ivar_getName(iv)));
-}
-
-int main()
-{
- Ivar *ivars;
- unsigned int count;
- Class cls;
-
- cls = objc_getClass("SubIvars");
- testassert(cls);
-
- count = 100;
- ivars = class_copyIvarList(cls, &count);
- testassert(ivars);
- testassert(count == 2);
- testassert(isNamed(ivars[0], "ivar3"));
- testassert(isNamed(ivars[1], "ivar4"));
- // ivars[] should be null-terminated
- testassert(ivars[2] == NULL);
- free(ivars);
-
- cls = objc_getClass("SuperIvars");
- testassert(cls);
-
- count = 100;
- ivars = class_copyIvarList(cls, &count);
- testassert(ivars);
- testassert(count == 3);
- testassert(isNamed(ivars[0], "isa"));
- testassert(isNamed(ivars[1], "ivar1"));
- testassert(isNamed(ivars[2], "ivar2"));
- // ivars[] should be null-terminated
- testassert(ivars[3] == NULL);
- free(ivars);
-
- // Check null-termination - this ivar list block would be 16 bytes
- // if it weren't for the terminator
- cls = objc_getClass("FourIvars");
- testassert(cls);
-
- count = 100;
- ivars = class_copyIvarList(cls, &count);
- testassert(ivars);
- testassert(count == 4);
- testassert(malloc_size(ivars) >= 5 * sizeof(Ivar));
- testassert(ivars[3] != NULL);
- testassert(ivars[4] == NULL);
- free(ivars);
-
- // Check NULL count parameter
- ivars = class_copyIvarList(cls, NULL);
- testassert(ivars);
- testassert(ivars[4] == NULL);
- testassert(ivars[3] != NULL);
- free(ivars);
-
- // Check NULL class parameter
- count = 100;
- ivars = class_copyIvarList(NULL, &count);
- testassert(!ivars);
- testassert(count == 0);
-
- // Check NULL class and count
- ivars = class_copyIvarList(NULL, NULL);
- testassert(!ivars);
-
- // Check class with no ivars
- cls = objc_getClass("NoIvars");
- testassert(cls);
-
- count = 100;
- ivars = class_copyIvarList(cls, &count);
- testassert(!ivars);
- testassert(count == 0);
-
- succeed(__FILE__);
- return 0;
-}
+++ /dev/null
-// TEST_CFLAGS -Wl,-no_objc_category_merging
-
-#include "test.h"
-#include "testroot.i"
-#include <malloc/malloc.h>
-#include <objc/runtime.h>
-
-@interface SuperMethods : TestRoot { } @end
-@implementation SuperMethods
-+(BOOL)SuperMethodClass { return NO; }
-+(BOOL)SuperMethodClass2 { return NO; }
--(BOOL)SuperMethodInstance { return NO; }
--(BOOL)SuperMethodInstance2 { return NO; }
-@end
-
-@interface SubMethods : SuperMethods { } @end
-@implementation SubMethods
-+(BOOL)SubMethodClass { return NO; }
-+(BOOL)SubMethodClass2 { return NO; }
--(BOOL)SubMethodInstance { return NO; }
--(BOOL)SubMethodInstance2 { return NO; }
-@end
-
-@interface SuperMethods (Category) @end
-@implementation SuperMethods (Category)
-+(BOOL)SuperMethodClass { return YES; }
-+(BOOL)SuperMethodClass2 { return YES; }
--(BOOL)SuperMethodInstance { return YES; }
--(BOOL)SuperMethodInstance2 { return YES; }
-@end
-
-@interface SubMethods (Category) @end
-@implementation SubMethods (Category)
-+(BOOL)SubMethodClass { return YES; }
-+(BOOL)SubMethodClass2 { return YES; }
--(BOOL)SubMethodInstance { return YES; }
--(BOOL)SubMethodInstance2 { return YES; }
-@end
-
-
-@interface FourMethods : TestRoot @end
-@implementation FourMethods
--(void)one { }
--(void)two { }
--(void)three { }
--(void)four { }
-@end
-
-@interface NoMethods : TestRoot @end
-@implementation NoMethods @end
-
-static void checkReplacement(Method *list, const char *name)
-{
- Method first = NULL, second = NULL;
- SEL sel = sel_registerName(name);
- int i;
-
- testassert(list);
-
- // Find the methods. There should be two.
- for (i = 0; list[i]; i++) {
- if (method_getName(list[i]) == sel) {
- if (!first) first = list[i];
- else if (!second) second = list[i];
- else testassert(0);
- }
- }
-
- // Call the methods. The first should be the category (returns YES).
- BOOL isCat;
- isCat = ((BOOL(*)(id, Method))method_invoke)(NULL, first);
- testassert(isCat);
- isCat = ((BOOL(*)(id, Method))method_invoke)(NULL, second);
- testassert(! isCat);
-}
-
-int main()
-{
- // Class SubMethods has not yet been touched, so runtime must attach
- // the lazy categories
- Method *methods;
- unsigned int count;
- Class cls;
-
- cls = objc_getClass("SubMethods");
- testassert(cls);
-
- testprintf("calling class_copyMethodList(SubMethods) (should be unmethodized)\n");
-
- count = 100;
- methods = class_copyMethodList(cls, &count);
- testassert(methods);
- testassert(count == 4);
- // methods[] should be null-terminated
- testassert(methods[4] == NULL);
- // Class and category methods may be mixed in the method list thanks
- // to linker / shared cache sorting, but a category's replacement should
- // always precede the class's implementation.
- checkReplacement(methods, "SubMethodInstance");
- checkReplacement(methods, "SubMethodInstance2");
- free(methods);
-
- testprintf("calling class_copyMethodList(SubMethods(meta)) (should be unmethodized)\n");
-
- count = 100;
- methods = class_copyMethodList(object_getClass(cls), &count);
- testassert(methods);
- testassert(count == 4);
- // methods[] should be null-terminated
- testassert(methods[4] == NULL);
- // Class and category methods may be mixed in the method list thanks
- // to linker / shared cache sorting, but a category's replacement should
- // always precede the class's implementation.
- checkReplacement(methods, "SubMethodClass");
- checkReplacement(methods, "SubMethodClass2");
- free(methods);
-
- // Check null-termination - this method list block would be 16 bytes
- // if it weren't for the terminator
- count = 100;
- cls = objc_getClass("FourMethods");
- methods = class_copyMethodList(cls, &count);
- testassert(methods);
- testassert(count == 4);
- testassert(malloc_size(methods) >= (4+1) * sizeof(Method));
- testassert(methods[3] != NULL);
- testassert(methods[4] == NULL);
- free(methods);
-
- // Check NULL count parameter
- methods = class_copyMethodList(cls, NULL);
- testassert(methods);
- testassert(methods[4] == NULL);
- testassert(methods[3] != NULL);
- free(methods);
-
- // Check NULL class parameter
- count = 100;
- methods = class_copyMethodList(NULL, &count);
- testassert(!methods);
- testassert(count == 0);
-
- // Check NULL class and count
- methods = class_copyMethodList(NULL, NULL);
- testassert(!methods);
-
- // Check class with no methods
- count = 100;
- cls = objc_getClass("NoMethods");
- methods = class_copyMethodList(cls, &count);
- testassert(!methods);
- testassert(count == 0);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include <string.h>
-#include <malloc/malloc.h>
-#include <objc/objc-runtime.h>
-
-OBJC_ROOT_CLASS
-@interface SuperProps { id isa; int prop1; int prop2; }
-@property int prop1;
-@property int prop2;
-@end
-@implementation SuperProps
-@synthesize prop1;
-@synthesize prop2;
-@end
-
-@interface SubProps : SuperProps { int prop3; int prop4; }
-@property int prop3;
-@property int prop4;
-@end
-@implementation SubProps
-@synthesize prop3;
-@synthesize prop4;
-@end
-
-OBJC_ROOT_CLASS
-@interface FourProps { int prop1; int prop2; int prop3; int prop4; }
-@property int prop1;
-@property int prop2;
-@property int prop3;
-@property int prop4;
-@end
-@implementation FourProps
-@synthesize prop1;
-@synthesize prop2;
-@synthesize prop3;
-@synthesize prop4;
-@end
-
-OBJC_ROOT_CLASS
-@interface NoProps @end
-@implementation NoProps @end
-
-static int isNamed(objc_property_t p, const char *name)
-{
- return (0 == strcmp(name, property_getName(p)));
-}
-
-int main()
-{
- objc_property_t *props;
- unsigned int count;
- Class cls;
-
- cls = objc_getClass("SubProps");
- testassert(cls);
-
- count = 100;
- props = class_copyPropertyList(cls, &count);
- testassert(props);
- testassert(count == 2);
- testassert((isNamed(props[0], "prop3") && isNamed(props[1], "prop4")) ||
- (isNamed(props[1], "prop3") && isNamed(props[0], "prop4")));
- // props[] should be null-terminated
- testassert(props[2] == NULL);
- free(props);
-
- cls = objc_getClass("SuperProps");
- testassert(cls);
-
- count = 100;
- props = class_copyPropertyList(cls, &count);
- testassert(props);
- testassert(count == 2);
- testassert((isNamed(props[0], "prop1") && isNamed(props[1], "prop2")) ||
- (isNamed(props[1], "prop1") && isNamed(props[0], "prop2")));
- // props[] should be null-terminated
- testassert(props[2] == NULL);
- free(props);
-
- // Check null-termination - this property list block would be 16 bytes
- // if it weren't for the terminator
- cls = objc_getClass("FourProps");
- testassert(cls);
-
- count = 100;
- props = class_copyPropertyList(cls, &count);
- testassert(props);
- testassert(count == 4);
- testassert(malloc_size(props) >= 5 * sizeof(objc_property_t));
- testassert(props[3] != NULL);
- testassert(props[4] == NULL);
- free(props);
-
- // Check NULL count parameter
- props = class_copyPropertyList(cls, NULL);
- testassert(props);
- testassert(props[4] == NULL);
- testassert(props[3] != NULL);
- free(props);
-
- // Check NULL class parameter
- count = 100;
- props = class_copyPropertyList(NULL, &count);
- testassert(!props);
- testassert(count == 0);
-
- // Check NULL class and count
- props = class_copyPropertyList(NULL, NULL);
- testassert(!props);
-
- // Check class with no properties
- cls = objc_getClass("NoProps");
- testassert(cls);
-
- count = 100;
- props = class_copyPropertyList(cls, &count);
- testassert(!props);
- testassert(count == 0);
-
- succeed(__FILE__);
- return 0;
-}
+++ /dev/null
-// TEST_CONFIG MEM=mrc,gc
-// TEST_CFLAGS -Wno-deprecated-declarations
-
-#import <objc/runtime.h>
-#import <objc/objc-auto.h>
-#ifndef OBJC_NO_GC
-#include <auto_zone.h>
-#else
-static BOOL auto_zone_is_valid_pointer(void *a, void *b) { return a||b; }
-#endif
-#include "test.h"
-
-OBJC_ROOT_CLASS
-@interface Super { @public id isa; } @end
-@implementation Super
-+(void) initialize { }
-+(Class) class { return self; }
-@end
-
-@interface Sub : Super { int array[128]; } @end
-@implementation Sub @end
-
-int main()
-{
- Super *s;
-
- s = class_createInstance([Super class], 0);
- testassert(s);
- testassert(object_getClass(s) == [Super class]);
- testassert(malloc_size(s) >= class_getInstanceSize([Super class]));
- if (objc_collectingEnabled()) testassert(auto_zone_is_valid_pointer(objc_collectableZone(), s));
-
- object_dispose(s);
-
- s = class_createInstance([Sub class], 0);
- testassert(s);
- testassert(object_getClass(s) == [Sub class]);
- testassert(malloc_size(s) >= class_getInstanceSize([Sub class]));
- if (objc_collectingEnabled()) testassert(auto_zone_is_valid_pointer(objc_collectableZone(), s));
-
- object_dispose(s);
-
- s = class_createInstance([Super class], 100);
- testassert(s);
- testassert(object_getClass(s) == [Super class]);
- testassert(malloc_size(s) >= class_getInstanceSize([Super class]) + 100);
- if (objc_collectingEnabled()) testassert(auto_zone_is_valid_pointer(objc_collectableZone(), s));
-
- object_dispose(s);
-
- s = class_createInstance([Sub class], 100);
- testassert(s);
- testassert(object_getClass(s) == [Sub class]);
- testassert(malloc_size(s) >= class_getInstanceSize([Sub class]) + 100);
- if (objc_collectingEnabled()) testassert(auto_zone_is_valid_pointer(objc_collectableZone(), s));
-
- object_dispose(s);
-
- s = class_createInstance(Nil, 0);
- testassert(!s);
-
- succeed(__FILE__);
-}
+++ /dev/null
-@interface InheritingSubCat @end
-
-@interface InheritingSubCat (NonClobberingCategory) @end
-
-@implementation InheritingSubCat (NonClobberingCategory)
--(id) unrelatedMethod { return self; }
-@end
+++ /dev/null
-@interface InheritingSubCat @end
-
-@interface InheritingSubCat (ClobberingCategory) @end
-
-@implementation InheritingSubCat (ClobberingCategory)
--(int) retainCount { return 1; }
-@end
+++ /dev/null
-/*
-
-TEST_CONFIG MEM=mrc
-TEST_ENV OBJC_PRINT_CUSTOM_RR=YES OBJC_PRINT_CUSTOM_AWZ=YES
-
-TEST_BUILD
- $C{COMPILE} $DIR/customrr-nsobject.m -o customrr-nsobject-awz.out -DSWIZZLE_AWZ=1
-END
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: CUSTOM AWZ: NSObject \(meta\)
-OK: customrr-nsobject-awz.out
-END
-
-*/
-
+++ /dev/null
-/*
-
-TEST_CONFIG MEM=mrc
-TEST_ENV OBJC_PRINT_CUSTOM_RR=YES OBJC_PRINT_CUSTOM_AWZ=YES
-
-TEST_BUILD
- $C{COMPILE} $DIR/customrr-nsobject.m -o customrr-nsobject-none.out
-END
-
-TEST_RUN_OUTPUT
-OK: customrr-nsobject-none.out
-END
-
-*/
-
+++ /dev/null
-/*
-
-TEST_CONFIG MEM=mrc
-TEST_ENV OBJC_PRINT_CUSTOM_RR=YES OBJC_PRINT_CUSTOM_AWZ=YES
-
-TEST_BUILD
- $C{COMPILE} $DIR/customrr-nsobject.m -o customrr-nsobject-rr.out -DSWIZZLE_RELEASE=1
-END
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: CUSTOM RR: NSObject
-OK: customrr-nsobject-rr.out
-END
-
-*/
-
+++ /dev/null
-/*
-
-TEST_CONFIG MEM=mrc
-TEST_ENV OBJC_PRINT_CUSTOM_RR=YES OBJC_PRINT_CUSTOM_AWZ=YES
-
-TEST_BUILD
- $C{COMPILE} $DIR/customrr-nsobject.m -o customrr-nsobject-rrawz.out -DSWIZZLE_RELEASE=1 -DSWIZZLE_AWZ=1
-END
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: CUSTOM AWZ: NSObject \(meta\)
-objc\[\d+\]: CUSTOM RR: NSObject
-OK: customrr-nsobject-rrawz.out
-END
-
-*/
-
+++ /dev/null
-// This file is used in the customrr-nsobject-*.m tests
-
-#include "test.h"
-#include <objc/NSObject.h>
-
-#if __OBJC2__
-# define BYPASS 1
-#else
-// old ABI does not implement the optimization
-# define BYPASS 0
-#endif
-
-static int Retains;
-static int Releases;
-static int Autoreleases;
-static int PlusInitializes;
-static int Allocs;
-static int AllocWithZones;
-
-id (*RealRetain)(id self, SEL _cmd);
-void (*RealRelease)(id self, SEL _cmd);
-id (*RealAutorelease)(id self, SEL _cmd);
-id (*RealAlloc)(id self, SEL _cmd);
-id (*RealAllocWithZone)(id self, SEL _cmd, void *zone);
-
-id HackRetain(id self, SEL _cmd) { Retains++; return RealRetain(self, _cmd); }
-void HackRelease(id self, SEL _cmd) { Releases++; return RealRelease(self, _cmd); }
-id HackAutorelease(id self, SEL _cmd) { Autoreleases++; return RealAutorelease(self, _cmd); }
-
-id HackAlloc(Class self, SEL _cmd) { Allocs++; return RealAlloc(self, _cmd); }
-id HackAllocWithZone(Class self, SEL _cmd, void *zone) { AllocWithZones++; return RealAllocWithZone(self, _cmd, zone); }
-
-void HackPlusInitialize(id self __unused, SEL _cmd __unused) { PlusInitializes++; }
-
-
-int main(int argc __unused, char **argv)
-{
- Class cls = objc_getClass("NSObject");
- Method meth;
-
- meth = class_getClassMethod(cls, @selector(initialize));
- method_setImplementation(meth, (IMP)HackPlusInitialize);
-
- // We either swizzle the method normally (testing that it properly
- // disables optimizations), or we hack the implementation into place
- // behind objc's back (so we can see whether it got called with the
- // optimizations still enabled).
-
- meth = class_getClassMethod(cls, @selector(allocWithZone:));
- RealAllocWithZone = (typeof(RealAllocWithZone))method_getImplementation(meth);
-#if SWIZZLE_AWZ
- method_setImplementation(meth, (IMP)HackAllocWithZone);
-#else
- ((IMP *)meth)[2] = (IMP)HackAllocWithZone;
-#endif
-
- meth = class_getInstanceMethod(cls, @selector(release));
- RealRelease = (typeof(RealRelease))method_getImplementation(meth);
-#if SWIZZLE_RELEASE
- method_setImplementation(meth, (IMP)HackRelease);
-#else
- ((IMP *)meth)[2] = (IMP)HackRelease;
-#endif
-
- // These other methods get hacked for counting purposes only
-
- meth = class_getInstanceMethod(cls, @selector(retain));
- RealRetain = (typeof(RealRetain))method_getImplementation(meth);
- ((IMP *)meth)[2] = (IMP)HackRetain;
-
- meth = class_getInstanceMethod(cls, @selector(autorelease));
- RealAutorelease = (typeof(RealAutorelease))method_getImplementation(meth);
- ((IMP *)meth)[2] = (IMP)HackAutorelease;
-
- meth = class_getClassMethod(cls, @selector(alloc));
- RealAlloc = (typeof(RealAlloc))method_getImplementation(meth);
- ((IMP *)meth)[2] = (IMP)HackAlloc;
-
- // Verify that the swizzles occurred before +initialize by provoking it now
- testassert(PlusInitializes == 0);
- [NSObject self];
- testassert(PlusInitializes == 1);
-
-#if !__OBJC2__
- // hack: fool the expected output because old ABI doesn't optimize this
-# if SWIZZLE_AWZ
- fprintf(stderr, "objc[1234]: CUSTOM AWZ: NSObject (meta)\n");
-# endif
-# if SWIZZLE_RELEASE
- fprintf(stderr, "objc[1234]: CUSTOM RR: NSObject\n");
-# endif
-#endif
-
- id obj;
-
- Allocs = 0;
- AllocWithZones = 0;
- obj = objc_alloc(cls);
-#if SWIZZLE_AWZ || !BYPASS
- testprintf("swizzled AWZ should be called\n");
- testassert(Allocs == 1);
- testassert(AllocWithZones == 1);
-#else
- testprintf("unswizzled AWZ should be bypassed\n");
- testassert(Allocs == 0);
- testassert(AllocWithZones == 0);
-#endif
-
- Allocs = 0;
- AllocWithZones = 0;
- obj = [NSObject alloc];
-#if SWIZZLE_AWZ || !BYPASS
- testprintf("swizzled AWZ should be called\n");
- testassert(Allocs == 1);
- testassert(AllocWithZones == 1);
-#else
- testprintf("unswizzled AWZ should be bypassed\n");
- testassert(Allocs == 1);
- testassert(AllocWithZones == 0);
-#endif
-
- Retains = 0;
- objc_retain(obj);
-#if SWIZZLE_RELEASE || !BYPASS
- testprintf("swizzled release should force retain\n");
- testassert(Retains == 1);
-#else
- testprintf("unswizzled release should bypass retain\n");
- testassert(Retains == 0);
-#endif
-
- Releases = 0;
- Autoreleases = 0;
- PUSH_POOL {
- objc_autorelease(obj);
-#if SWIZZLE_RELEASE || !BYPASS
- testprintf("swizzled release should force autorelease\n");
- testassert(Autoreleases == 1);
-#else
- testprintf("unswizzled release should bypass autorelease\n");
- testassert(Autoreleases == 0);
-#endif
- } POP_POOL
-
-#if SWIZZLE_RELEASE || !BYPASS
- testprintf("swizzled release should be called\n");
- testassert(Releases == 1);
-#else
- testprintf("unswizzled release should be bypassed\n");
- testassert(Releases == 0);
-#endif
-
- succeed(basename(argv[0]));
-}
+++ /dev/null
-// These options must match customrr2.m
-// TEST_CONFIG MEM=mrc
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/customrr.m -fvisibility=default -o customrr.out
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/customrr-cat1.m -o customrr-cat1.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/customrr-cat2.m -o customrr-cat2.dylib
-END
-*/
-
-
-#include "test.h"
-#include <dlfcn.h>
-#include <Foundation/NSObject.h>
-
-#if !__OBJC2__
-
-// pacify exported symbols list
-OBJC_ROOT_CLASS
-@interface InheritingSubCat @end
-@implementation InheritingSubCat @end
-
-int main(int argc __unused, char **argv)
-{
- succeed(basename(argv[0]));
-}
-
-#else
-
-static int Retains;
-static int Releases;
-static int Autoreleases;
-static int RetainCounts;
-static int PlusRetains;
-static int PlusReleases;
-static int PlusAutoreleases;
-static int PlusRetainCounts;
-static int Allocs;
-static int AllocWithZones;
-
-static int SubRetains;
-static int SubReleases;
-static int SubAutoreleases;
-static int SubRetainCounts;
-static int SubPlusRetains;
-static int SubPlusReleases;
-static int SubPlusAutoreleases;
-static int SubPlusRetainCounts;
-static int SubAllocs;
-static int SubAllocWithZones;
-
-static int Imps;
-
-static id imp_fn(id self, SEL _cmd __unused, ...)
-{
- Imps++;
- return self;
-}
-
-static void zero(void) {
- Retains = 0;
- Releases = 0;
- Autoreleases = 0;
- RetainCounts = 0;
- PlusRetains = 0;
- PlusReleases = 0;
- PlusAutoreleases = 0;
- PlusRetainCounts = 0;
- Allocs = 0;
- AllocWithZones = 0;
-
- SubRetains = 0;
- SubReleases = 0;
- SubAutoreleases = 0;
- SubRetainCounts = 0;
- SubPlusRetains = 0;
- SubPlusReleases = 0;
- SubPlusAutoreleases = 0;
- SubPlusRetainCounts = 0;
- SubAllocs = 0;
- SubAllocWithZones = 0;
-
- Imps = 0;
-}
-
-
-id HackRetain(id self, SEL _cmd __unused) { Retains++; return self; }
-void HackRelease(id self __unused, SEL _cmd __unused) { Releases++; }
-id HackAutorelease(id self, SEL _cmd __unused) { Autoreleases++; return self; }
-NSUInteger HackRetainCount(id self __unused, SEL _cmd __unused) { RetainCounts++; return 1; }
-id HackPlusRetain(id self, SEL _cmd __unused) { PlusRetains++; return self; }
-void HackPlusRelease(id self __unused, SEL _cmd __unused) { PlusReleases++; }
-id HackPlusAutorelease(id self, SEL _cmd __unused) { PlusAutoreleases++; return self; }
-NSUInteger HackPlusRetainCount(id self __unused, SEL _cmd __unused) { PlusRetainCounts++; return 1; }
-id HackAlloc(Class self, SEL _cmd __unused) { Allocs++; return class_createInstance(self, 0); }
-id HackAllocWithZone(Class self, SEL _cmd __unused) { AllocWithZones++; return class_createInstance(self, 0); }
-
-
-@interface OverridingSub : NSObject @end
-@implementation OverridingSub
-
--(id) retain { SubRetains++; return self; }
-+(id) retain { SubPlusRetains++; return self; }
--(oneway void) release { SubReleases++; }
-+(oneway void) release { SubPlusReleases++; }
--(id) autorelease { SubAutoreleases++; return self; }
-+(id) autorelease { SubPlusAutoreleases++; return self; }
--(NSUInteger) retainCount { SubRetainCounts++; return 1; }
-+(NSUInteger) retainCount { SubPlusRetainCounts++; return 1; }
-
-@end
-
-@interface OverridingASub : NSObject @end
-@implementation OverridingASub
-+(id) alloc { SubAllocs++; return class_createInstance(self, 0); }
-@end
-
-@interface OverridingAWZSub : NSObject @end
-@implementation OverridingAWZSub
-+(id) allocWithZone:(NSZone * __unused)z { SubAllocWithZones++; return class_createInstance(self, 0); }
-@end
-
-@interface OverridingAAWZSub : NSObject @end
-@implementation OverridingAAWZSub
-+(id) alloc { SubAllocs++; return class_createInstance(self, 0); }
-+(id) allocWithZone:(NSZone * __unused)z { SubAllocWithZones++; return class_createInstance(self, 0); }
-@end
-
-
-@interface InheritingSub : NSObject @end
-@implementation InheritingSub @end
-
-@interface InheritingSub2 : NSObject @end
-@implementation InheritingSub2 @end
-@interface InheritingSub2_2 : InheritingSub2 @end
-@implementation InheritingSub2_2 @end
-
-@interface InheritingSub3 : NSObject @end
-@implementation InheritingSub3 @end
-@interface InheritingSub3_2 : InheritingSub3 @end
-@implementation InheritingSub3_2 @end
-
-@interface InheritingSub4 : NSObject @end
-@implementation InheritingSub4 @end
-@interface InheritingSub4_2 : InheritingSub4 @end
-@implementation InheritingSub4_2 @end
-
-@interface InheritingSub5 : NSObject @end
-@implementation InheritingSub5 @end
-@interface InheritingSub5_2 : InheritingSub5 @end
-@implementation InheritingSub5_2 @end
-
-@interface InheritingSub6 : NSObject @end
-@implementation InheritingSub6 @end
-@interface InheritingSub6_2 : InheritingSub6 @end
-@implementation InheritingSub6_2 @end
-
-@interface InheritingSub7 : NSObject @end
-@implementation InheritingSub7 @end
-@interface InheritingSub7_2 : InheritingSub7 @end
-@implementation InheritingSub7_2 @end
-
-@interface InheritingSubCat : NSObject @end
-@implementation InheritingSubCat @end
-@interface InheritingSubCat_2 : InheritingSubCat @end
-@implementation InheritingSubCat_2 @end
-
-
-extern uintptr_t OBJC_CLASS_$_UnrealizedSubA1;
-@interface UnrealizedSubA1 : NSObject @end
-@implementation UnrealizedSubA1 @end
-extern uintptr_t OBJC_CLASS_$_UnrealizedSubA2;
-@interface UnrealizedSubA2 : NSObject @end
-@implementation UnrealizedSubA2 @end
-extern uintptr_t OBJC_CLASS_$_UnrealizedSubA3;
-@interface UnrealizedSubA3 : NSObject @end
-@implementation UnrealizedSubA3 @end
-
-extern uintptr_t OBJC_CLASS_$_UnrealizedSubB1;
-@interface UnrealizedSubB1 : NSObject @end
-@implementation UnrealizedSubB1 @end
-extern uintptr_t OBJC_CLASS_$_UnrealizedSubB2;
-@interface UnrealizedSubB2 : NSObject @end
-@implementation UnrealizedSubB2 @end
-extern uintptr_t OBJC_CLASS_$_UnrealizedSubB3;
-@interface UnrealizedSubB3 : NSObject @end
-@implementation UnrealizedSubB3 @end
-
-extern uintptr_t OBJC_CLASS_$_UnrealizedSubC1;
-@interface UnrealizedSubC1 : NSObject @end
-@implementation UnrealizedSubC1 @end
-extern uintptr_t OBJC_CLASS_$_UnrealizedSubC2;
-@interface UnrealizedSubC2 : NSObject @end
-@implementation UnrealizedSubC2 @end
-extern uintptr_t OBJC_CLASS_$_UnrealizedSubC3;
-@interface UnrealizedSubC3 : NSObject @end
-@implementation UnrealizedSubC3 @end
-
-
-int main(int argc __unused, char **argv)
-{
- objc_autoreleasePoolPush();
-
- // Hack NSObject's RR methods.
- // Don't use runtime functions to do this -
- // we want the runtime to think that these are NSObject's real code
- {
- Class cls = [NSObject class];
- IMP *m;
- IMP imp;
- imp = class_getMethodImplementation(cls, @selector(retain));
- m = (IMP *)class_getInstanceMethod(cls, @selector(retain));
- testassert(m[2] == imp); // verify Method struct is as we expect
-
- m = (IMP *)class_getInstanceMethod(cls, @selector(retain));
- m[2] = (IMP)HackRetain;
- m = (IMP *)class_getInstanceMethod(cls, @selector(release));
- m[2] = (IMP)HackRelease;
- m = (IMP *)class_getInstanceMethod(cls, @selector(autorelease));
- m[2] = (IMP)HackAutorelease;
- m = (IMP *)class_getInstanceMethod(cls, @selector(retainCount));
- m[2] = (IMP)HackRetainCount;
- m = (IMP *)class_getClassMethod(cls, @selector(retain));
- m[2] = (IMP)HackPlusRetain;
- m = (IMP *)class_getClassMethod(cls, @selector(release));
- m[2] = (IMP)HackPlusRelease;
- m = (IMP *)class_getClassMethod(cls, @selector(autorelease));
- m[2] = (IMP)HackPlusAutorelease;
- m = (IMP *)class_getClassMethod(cls, @selector(retainCount));
- m[2] = (IMP)HackPlusRetainCount;
- m = (IMP *)class_getClassMethod(cls, @selector(alloc));
- m[2] = (IMP)HackAlloc;
- m = (IMP *)class_getClassMethod(cls, @selector(allocWithZone:));
- m[2] = (IMP)HackAllocWithZone;
-
- _objc_flush_caches(cls);
-
- imp = class_getMethodImplementation(cls, @selector(retain));
- testassert(imp == (IMP)HackRetain); // verify hack worked
- }
-
- Class cls = [NSObject class];
- Class icl = [InheritingSub class];
- Class ocl = [OverridingSub class];
- /*
- Class oa1 = [OverridingASub class];
- Class oa2 = [OverridingAWZSub class];
- Class oa3 = [OverridingAAWZSub class];
- */
- NSObject *obj = [NSObject new];
- InheritingSub *inh = [InheritingSub new];
- OverridingSub *ovr = [OverridingSub new];
-
- Class ccc;
- id ooo;
- Class cc2;
- id oo2;
-
- void *dlh;
-
-
-#if __x86_64__
- // vtable dispatch can introduce bypass just like the ARC entrypoints
-#else
- testprintf("method dispatch does not bypass\n");
- zero();
-
- [obj retain];
- testassert(Retains == 1);
- [obj release];
- testassert(Releases == 1);
- [obj autorelease];
- testassert(Autoreleases == 1);
-
- [cls retain];
- testassert(PlusRetains == 1);
- [cls release];
- testassert(PlusReleases == 1);
- [cls autorelease];
- testassert(PlusAutoreleases == 1);
-
- [inh retain];
- testassert(Retains == 2);
- [inh release];
- testassert(Releases == 2);
- [inh autorelease];
- testassert(Autoreleases == 2);
-
- [icl retain];
- testassert(PlusRetains == 2);
- [icl release];
- testassert(PlusReleases == 2);
- [icl autorelease];
- testassert(PlusAutoreleases == 2);
-
- [ovr retain];
- testassert(SubRetains == 1);
- [ovr release];
- testassert(SubReleases == 1);
- [ovr autorelease];
- testassert(SubAutoreleases == 1);
-
- [ocl retain];
- testassert(SubPlusRetains == 1);
- [ocl release];
- testassert(SubPlusReleases == 1);
- [ocl autorelease];
- testassert(SubPlusAutoreleases == 1);
-
- [UnrealizedSubA1 retain];
- testassert(PlusRetains == 3);
- [UnrealizedSubA2 release];
- testassert(PlusReleases == 3);
- [UnrealizedSubA3 autorelease];
- testassert(PlusAutoreleases == 3);
-#endif
-
-
- testprintf("objc_msgSend() does not bypass\n");
- zero();
-
- id (*retain_fn)(id, SEL) = (id(*)(id, SEL))objc_msgSend;
- void (*release_fn)(id, SEL) = (void(*)(id, SEL))objc_msgSend;
- id (*autorelease_fn)(id, SEL) = (id(*)(id, SEL))objc_msgSend;
-
- retain_fn(obj, @selector(retain));
- testassert(Retains == 1);
- release_fn(obj, @selector(release));
- testassert(Releases == 1);
- autorelease_fn(obj, @selector(autorelease));
- testassert(Autoreleases == 1);
-
- retain_fn(cls, @selector(retain));
- testassert(PlusRetains == 1);
- release_fn(cls, @selector(release));
- testassert(PlusReleases == 1);
- autorelease_fn(cls, @selector(autorelease));
- testassert(PlusAutoreleases == 1);
-
- retain_fn(inh, @selector(retain));
- testassert(Retains == 2);
- release_fn(inh, @selector(release));
- testassert(Releases == 2);
- autorelease_fn(inh, @selector(autorelease));
- testassert(Autoreleases == 2);
-
- retain_fn(icl, @selector(retain));
- testassert(PlusRetains == 2);
- release_fn(icl, @selector(release));
- testassert(PlusReleases == 2);
- autorelease_fn(icl, @selector(autorelease));
- testassert(PlusAutoreleases == 2);
-
- retain_fn(ovr, @selector(retain));
- testassert(SubRetains == 1);
- release_fn(ovr, @selector(release));
- testassert(SubReleases == 1);
- autorelease_fn(ovr, @selector(autorelease));
- testassert(SubAutoreleases == 1);
-
- retain_fn(ocl, @selector(retain));
- testassert(SubPlusRetains == 1);
- release_fn(ocl, @selector(release));
- testassert(SubPlusReleases == 1);
- autorelease_fn(ocl, @selector(autorelease));
- testassert(SubPlusAutoreleases == 1);
-
-#if __OBJC2__
- retain_fn((Class)&OBJC_CLASS_$_UnrealizedSubB1, @selector(retain));
- testassert(PlusRetains == 3);
- release_fn((Class)&OBJC_CLASS_$_UnrealizedSubB2, @selector(release));
- testassert(PlusReleases == 3);
- autorelease_fn((Class)&OBJC_CLASS_$_UnrealizedSubB3, @selector(autorelease));
- testassert(PlusAutoreleases == 3);
-#endif
-
-
- testprintf("arc function bypasses instance but not class or override\n");
- zero();
-
- objc_retain(obj);
- testassert(Retains == 0);
- objc_release(obj);
- testassert(Releases == 0);
- objc_autorelease(obj);
- testassert(Autoreleases == 0);
-
- objc_retain(cls);
- testassert(PlusRetains == 1);
- objc_release(cls);
- testassert(PlusReleases == 1);
- objc_autorelease(cls);
- testassert(PlusAutoreleases == 1);
-
- objc_retain(inh);
- testassert(Retains == 0);
- objc_release(inh);
- testassert(Releases == 0);
- objc_autorelease(inh);
- testassert(Autoreleases == 0);
-
- objc_retain(icl);
- testassert(PlusRetains == 2);
- objc_release(icl);
- testassert(PlusReleases == 2);
- objc_autorelease(icl);
- testassert(PlusAutoreleases == 2);
-
- objc_retain(ovr);
- testassert(SubRetains == 1);
- objc_release(ovr);
- testassert(SubReleases == 1);
- objc_autorelease(ovr);
- testassert(SubAutoreleases == 1);
-
- objc_retain(ocl);
- testassert(SubPlusRetains == 1);
- objc_release(ocl);
- testassert(SubPlusReleases == 1);
- objc_autorelease(ocl);
- testassert(SubPlusAutoreleases == 1);
-
-#if __OBJC2__
- objc_retain((Class)&OBJC_CLASS_$_UnrealizedSubC1);
- testassert(PlusRetains == 3);
- objc_release((Class)&OBJC_CLASS_$_UnrealizedSubC2);
- testassert(PlusReleases == 3);
- objc_autorelease((Class)&OBJC_CLASS_$_UnrealizedSubC3);
- testassert(PlusAutoreleases == 3);
-#endif
-
-
- testprintf("unrelated addMethod does not clobber\n");
- zero();
-
- class_addMethod(cls, @selector(unrelatedMethod), (IMP)imp_fn, "");
-
- objc_retain(obj);
- testassert(Retains == 0);
- objc_release(obj);
- testassert(Releases == 0);
- objc_autorelease(obj);
- testassert(Autoreleases == 0);
-
-
- testprintf("add class method does not clobber\n");
- zero();
-
- objc_retain(obj);
- testassert(Retains == 0);
- objc_release(obj);
- testassert(Releases == 0);
- objc_autorelease(obj);
- testassert(Autoreleases == 0);
-
- class_addMethod(object_getClass(cls), @selector(retain), (IMP)imp_fn, "");
-
- objc_retain(obj);
- testassert(Retains == 0);
- objc_release(obj);
- testassert(Releases == 0);
- objc_autorelease(obj);
- testassert(Autoreleases == 0);
-
-
- testprintf("addMethod clobbers (InheritingSub2, retain)\n");
- zero();
-
- ccc = [InheritingSub2 class];
- ooo = [ccc new];
- cc2 = [InheritingSub2_2 class];
- oo2 = [cc2 new];
-
- objc_retain(ooo);
- testassert(Retains == 0);
- objc_release(ooo);
- testassert(Releases == 0);
- objc_autorelease(ooo);
- testassert(Autoreleases == 0);
-
- objc_retain(oo2);
- testassert(Retains == 0);
- objc_release(oo2);
- testassert(Releases == 0);
- objc_autorelease(oo2);
- testassert(Autoreleases == 0);
-
- class_addMethod(ccc, @selector(retain), (IMP)imp_fn, "");
-
- objc_retain(ooo);
- testassert(Retains == 0);
- testassert(Imps == 1);
- objc_release(ooo);
- testassert(Releases == 1);
- objc_autorelease(ooo);
- testassert(Autoreleases == 1);
-
- objc_retain(oo2);
- testassert(Retains == 0);
- testassert(Imps == 2);
- objc_release(oo2);
- testassert(Releases == 2);
- objc_autorelease(oo2);
- testassert(Autoreleases == 2);
-
-
- testprintf("addMethod clobbers (InheritingSub3, release)\n");
- zero();
-
- ccc = [InheritingSub3 class];
- ooo = [ccc new];
- cc2 = [InheritingSub3_2 class];
- oo2 = [cc2 new];
-
- objc_retain(ooo);
- testassert(Retains == 0);
- objc_release(ooo);
- testassert(Releases == 0);
- objc_autorelease(ooo);
- testassert(Autoreleases == 0);
-
- objc_retain(oo2);
- testassert(Retains == 0);
- objc_release(oo2);
- testassert(Releases == 0);
- objc_autorelease(oo2);
- testassert(Autoreleases == 0);
-
- class_addMethod(ccc, @selector(release), (IMP)imp_fn, "");
-
- objc_retain(ooo);
- testassert(Retains == 1);
- objc_release(ooo);
- testassert(Releases == 0);
- testassert(Imps == 1);
- objc_autorelease(ooo);
- testassert(Autoreleases == 1);
-
- objc_retain(oo2);
- testassert(Retains == 2);
- objc_release(oo2);
- testassert(Releases == 0);
- testassert(Imps == 2);
- objc_autorelease(oo2);
- testassert(Autoreleases == 2);
-
-
- testprintf("addMethod clobbers (InheritingSub4, autorelease)\n");
- zero();
-
- ccc = [InheritingSub4 class];
- ooo = [ccc new];
- cc2 = [InheritingSub4_2 class];
- oo2 = [cc2 new];
-
- objc_retain(ooo);
- testassert(Retains == 0);
- objc_release(ooo);
- testassert(Releases == 0);
- objc_autorelease(ooo);
- testassert(Autoreleases == 0);
-
- objc_retain(oo2);
- testassert(Retains == 0);
- objc_release(oo2);
- testassert(Releases == 0);
- objc_autorelease(oo2);
- testassert(Autoreleases == 0);
-
- class_addMethod(ccc, @selector(autorelease), (IMP)imp_fn, "");
-
- objc_retain(ooo);
- testassert(Retains == 1);
- objc_release(ooo);
- testassert(Releases == 1);
- objc_autorelease(ooo);
- testassert(Autoreleases == 0);
- testassert(Imps == 1);
-
- objc_retain(oo2);
- testassert(Retains == 2);
- objc_release(oo2);
- testassert(Releases == 2);
- objc_autorelease(oo2);
- testassert(Autoreleases == 0);
- testassert(Imps == 2);
-
-
- testprintf("addMethod clobbers (InheritingSub5, retainCount)\n");
- zero();
-
- ccc = [InheritingSub5 class];
- ooo = [ccc new];
- cc2 = [InheritingSub5_2 class];
- oo2 = [cc2 new];
-
- objc_retain(ooo);
- testassert(Retains == 0);
- objc_release(ooo);
- testassert(Releases == 0);
- objc_autorelease(ooo);
- testassert(Autoreleases == 0);
-
- objc_retain(oo2);
- testassert(Retains == 0);
- objc_release(oo2);
- testassert(Releases == 0);
- objc_autorelease(oo2);
- testassert(Autoreleases == 0);
-
- class_addMethod(ccc, @selector(retainCount), (IMP)imp_fn, "");
-
- objc_retain(ooo);
- testassert(Retains == 1);
- objc_release(ooo);
- testassert(Releases == 1);
- objc_autorelease(ooo);
- testassert(Autoreleases == 1);
- // no bypassing call for -retainCount
-
- objc_retain(oo2);
- testassert(Retains == 2);
- objc_release(oo2);
- testassert(Releases == 2);
- objc_autorelease(oo2);
- testassert(Autoreleases == 2);
- // no bypassing call for -retainCount
-
-
- testprintf("setSuperclass to clean super does not clobber (InheritingSub6)\n");
- zero();
-
- ccc = [InheritingSub6 class];
- ooo = [ccc new];
- cc2 = [InheritingSub6_2 class];
- oo2 = [cc2 new];
-
- objc_retain(ooo);
- testassert(Retains == 0);
- objc_release(ooo);
- testassert(Releases == 0);
- objc_autorelease(ooo);
- testassert(Autoreleases == 0);
-
- objc_retain(oo2);
- testassert(Retains == 0);
- objc_release(oo2);
- testassert(Releases == 0);
- objc_autorelease(oo2);
- testassert(Autoreleases == 0);
-
- class_setSuperclass(ccc, [InheritingSub class]);
-
- objc_retain(ooo);
- testassert(Retains == 0);
- objc_release(ooo);
- testassert(Releases == 0);
- objc_autorelease(ooo);
- testassert(Autoreleases == 0);
-
- objc_retain(oo2);
- testassert(Retains == 0);
- objc_release(oo2);
- testassert(Releases == 0);
- objc_autorelease(oo2);
- testassert(Autoreleases == 0);
-
-
- testprintf("setSuperclass to dirty super clobbers (InheritingSub7)\n");
- zero();
-
- ccc = [InheritingSub7 class];
- ooo = [ccc new];
- cc2 = [InheritingSub7_2 class];
- oo2 = [cc2 new];
-
- objc_retain(ooo);
- testassert(Retains == 0);
- objc_release(ooo);
- testassert(Releases == 0);
- objc_autorelease(ooo);
- testassert(Autoreleases == 0);
-
- objc_retain(oo2);
- testassert(Retains == 0);
- objc_release(oo2);
- testassert(Releases == 0);
- objc_autorelease(oo2);
- testassert(Autoreleases == 0);
-
- class_setSuperclass(ccc, [OverridingSub class]);
-
- objc_retain(ooo);
- testassert(SubRetains == 1);
- objc_release(ooo);
- testassert(SubReleases == 1);
- objc_autorelease(ooo);
- testassert(SubAutoreleases == 1);
-
- objc_retain(oo2);
- testassert(SubRetains == 2);
- objc_release(oo2);
- testassert(SubReleases == 2);
- objc_autorelease(oo2);
- testassert(SubAutoreleases == 2);
-
-
- testprintf("category replacement of unrelated method does not clobber (InheritingSubCat)\n");
- zero();
-
- ccc = [InheritingSubCat class];
- ooo = [ccc new];
- cc2 = [InheritingSubCat_2 class];
- oo2 = [cc2 new];
-
- objc_retain(ooo);
- testassert(Retains == 0);
- objc_release(ooo);
- testassert(Releases == 0);
- objc_autorelease(ooo);
- testassert(Autoreleases == 0);
-
- objc_retain(oo2);
- testassert(Retains == 0);
- objc_release(oo2);
- testassert(Releases == 0);
- objc_autorelease(oo2);
- testassert(Autoreleases == 0);
-
- dlh = dlopen("customrr-cat1.dylib", RTLD_LAZY);
- testassert(dlh);
-
- objc_retain(ooo);
- testassert(Retains == 0);
- objc_release(ooo);
- testassert(Releases == 0);
- objc_autorelease(ooo);
- testassert(Autoreleases == 0);
-
- objc_retain(oo2);
- testassert(Retains == 0);
- objc_release(oo2);
- testassert(Releases == 0);
- objc_autorelease(oo2);
- testassert(Autoreleases == 0);
-
-
- testprintf("category replacement clobbers (InheritingSubCat)\n");
- zero();
-
- ccc = [InheritingSubCat class];
- ooo = [ccc new];
- cc2 = [InheritingSubCat_2 class];
- oo2 = [cc2 new];
-
- objc_retain(ooo);
- testassert(Retains == 0);
- objc_release(ooo);
- testassert(Releases == 0);
- objc_autorelease(ooo);
- testassert(Autoreleases == 0);
-
- objc_retain(oo2);
- testassert(Retains == 0);
- objc_release(oo2);
- testassert(Releases == 0);
- objc_autorelease(oo2);
- testassert(Autoreleases == 0);
-
- dlh = dlopen("customrr-cat2.dylib", RTLD_LAZY);
- testassert(dlh);
-
- objc_retain(ooo);
- testassert(Retains == 1);
- objc_release(ooo);
- testassert(Releases == 1);
- objc_autorelease(ooo);
- testassert(Autoreleases == 1);
-
- objc_retain(oo2);
- testassert(Retains == 2);
- objc_release(oo2);
- testassert(Releases == 2);
- objc_autorelease(oo2);
- testassert(Autoreleases == 2);
-
-
- testprintf("allocateClassPair with clean super does not clobber\n");
- zero();
-
- objc_retain(inh);
- testassert(Retains == 0);
- objc_release(inh);
- testassert(Releases == 0);
- objc_autorelease(inh);
- testassert(Autoreleases == 0);
-
- ccc = objc_allocateClassPair([InheritingSub class], "CleanClassPair", 0);
- objc_registerClassPair(ccc);
- ooo = [ccc new];
-
- objc_retain(inh);
- testassert(Retains == 0);
- objc_release(inh);
- testassert(Releases == 0);
- objc_autorelease(inh);
- testassert(Autoreleases == 0);
-
- objc_retain(ooo);
- testassert(Retains == 0);
- objc_release(ooo);
- testassert(Releases == 0);
- objc_autorelease(ooo);
- testassert(Autoreleases == 0);
-
-
- testprintf("allocateClassPair with clobbered super clobbers\n");
- zero();
-
- ccc = objc_allocateClassPair([OverridingSub class], "DirtyClassPair", 0);
- objc_registerClassPair(ccc);
- ooo = [ccc new];
-
- objc_retain(ooo);
- testassert(SubRetains == 1);
- objc_release(ooo);
- testassert(SubReleases == 1);
- objc_autorelease(ooo);
- testassert(SubAutoreleases == 1);
-
-
- testprintf("allocateClassPair with clean super and override clobbers\n");
- zero();
-
- ccc = objc_allocateClassPair([InheritingSub class], "Dirty2ClassPair", 0);
- class_addMethod(ccc, @selector(autorelease), (IMP)imp_fn, "");
- objc_registerClassPair(ccc);
- ooo = [ccc new];
-
- objc_retain(ooo);
- testassert(Retains == 1);
- objc_release(ooo);
- testassert(Releases == 1);
- objc_autorelease(ooo);
- testassert(Autoreleases == 0);
- testassert(Imps == 1);
-
-
- // method_setImplementation and method_exchangeImplementations only
- // clobber when manipulating NSObject. We can only test one at a time.
- // To test both, we need two tests: customrr and customrr2.
-
- // These tests also check recursive clobber.
-
-#if TEST_EXCHANGEIMPLEMENTATIONS
- testprintf("exchangeImplementations clobbers (recursive)\n");
-#else
- testprintf("setImplementation clobbers (recursive)\n");
-#endif
- zero();
-
- objc_retain(obj);
- testassert(Retains == 0);
- objc_release(obj);
- testassert(Releases == 0);
- objc_autorelease(obj);
- testassert(Autoreleases == 0);
-
- objc_retain(inh);
- testassert(Retains == 0);
- objc_release(inh);
- testassert(Releases == 0);
- objc_autorelease(inh);
- testassert(Autoreleases == 0);
-
- Method meth = class_getInstanceMethod(cls, @selector(retainCount));
- testassert(meth);
-#if TEST_EXCHANGEIMPLEMENTATIONS
- method_exchangeImplementations(meth, meth);
-#else
- method_setImplementation(meth, (IMP)imp_fn);
-#endif
-
- objc_retain(obj);
- testassert(Retains == 1);
- objc_release(obj);
- testassert(Releases == 1);
- objc_autorelease(obj);
- testassert(Autoreleases == 1);
-
- objc_retain(inh);
- testassert(Retains == 2);
- objc_release(inh);
- testassert(Releases == 2);
- objc_autorelease(inh);
- testassert(Autoreleases == 2);
-
-
- // do not add more tests here - the recursive test must be LAST
-
- succeed(basename(argv[0]));
-}
-
-#endif
+++ /dev/null
-// These options must match customrr.m
-// TEST_CONFIG MEM=mrc
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/customrr.m -fvisibility=default -o customrr2.out -DTEST_EXCHANGEIMPLEMENTATIONS=1
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/customrr-cat1.m -o customrr-cat1.dylib
- $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/customrr-cat2.m -o customrr-cat2.dylib
-END
-*/
+++ /dev/null
-// TEST_CONFIG
-
-// DO NOT include anything else here
-#include <objc/objc.h>
-// DO NOT include anything else here
-Class c = Nil;
-SEL s;
-IMP i;
-id o = nil;
-BOOL b = YES;
-BOOL b2 = NO;
-#if !__has_feature(objc_arc)
-__strong void *p;
-#endif
-id __unsafe_unretained u;
-id __weak w;
-
-void fn(void) __unused;
-void fn(void) {
- id __autoreleasing a __unused;
-}
-
-#if __llvm__ && !__clang__
-// llvm-gcc defines _NSConcreteGlobalBlock wrong
-#else
-// rdar://10118972 wrong type inference for blocks returning YES and NO
-BOOL (^block1)(void) = ^{ return YES; };
-BOOL (^block2)(void) = ^{ return NO; };
-#endif
-
-#include "test.h"
-
-int main()
-{
- testassert(YES);
- testassert(!NO);
-#if __cplusplus
- testwarn("rdar://12371870 -Wnull-conversion");
- testassert(!(bool)nil);
- testassert(!(bool)Nil);
-#else
- testassert(!nil);
- testassert(!Nil);
-#endif
-
-#if __has_feature(objc_bool)
- // YES[array] is disallowed for objc just as true[array] is for C++
-#else
- // this will fail if YES and NO do not have enough parentheses
- int array[2] = { 888, 999 };
- testassert(NO[array] == 888);
- testassert(YES[array] == 999);
-#endif
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-/* TEST_BUILD_OUTPUT
-.*designatedinit.m:\d+:\d+: warning: designated initializer should only invoke a designated initializer on 'super'.*
-.*designatedinit.m:\d+:\d+: note: .*
-.*designatedinit.m:\d+:\d+: warning: method override for the designated initializer of the superclass '-init' not found.*
-.*NSObject.h:\d+:\d+: note: .*
-END */
-
-#define NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER 1
-#include "test.h"
-#include <objc/NSObject.h>
-
-@interface C : NSObject
--(id) initWithInt:(int)i NS_DESIGNATED_INITIALIZER;
-@end
-
-@implementation C
--(id) initWithInt:(int)__unused i {
- return [self init];
-}
-@end
-
-int main()
-{
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -Wno-deprecated-declarations -Wl,-no_objc_category_merging
-
-#include "test.h"
-#include "testroot.i"
-#include <objc/runtime.h>
-#ifndef OBJC_NO_GC
-#include <objc/objc-auto.h>
-#include <auto_zone.h>
-#endif
-
-static int state;
-
-@protocol Proto
-+(void)classMethod;
--(void)instanceMethod;
-@end
-
-@interface Super : TestRoot <Proto> {
- int i;
-}
-@property int i;
-@end
-
-@implementation Super
-@synthesize i;
-
-+(void)classMethod {
- state = 1;
-}
-
--(void)instanceMethod {
- state = 3;
-}
-
-@end
-
-
-#if __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
-#endif
-
-@implementation Super (Category)
-
-+(void)classMethod {
- state = 2;
-}
-
--(void)instanceMethod {
- state = 4;
-}
-
-@end
-
-#if __clang__
-#pragma clang diagnostic pop
-#endif
-
-
-int main()
-{
- Class clone;
- Class cls;
- Method *m1, *m2;
- int i;
-
- cls = [Super class];
- clone = objc_duplicateClass(cls, "Super_copy", 0);
-#ifndef OBJC_NO_GC
- if (objc_collectingEnabled()) {
- testassert(auto_zone_size(objc_collectableZone(), objc_unretainedPointer(clone)));
- // objc_duplicateClass() doesn't duplicate the metaclass
- // no: testassert(auto_zone_size(objc_collectableZone(), clone->isa));
- }
-#endif
-
- testassert(clone != cls);
- testassert(object_getClass(clone) == object_getClass(cls));
- testassert(class_getSuperclass(clone) == class_getSuperclass(cls));
- testassert(class_getVersion(clone) == class_getVersion(cls));
- testassert(class_isMetaClass(clone) == class_isMetaClass(cls));
- testassert(class_getIvarLayout(clone) == class_getIvarLayout(cls));
- testassert(class_getWeakIvarLayout(clone) == class_getWeakIvarLayout(cls));
-#if !__OBJC2__
- testassert((clone->info & (CLS_CLASS|CLS_META)) == (cls->info & (CLS_CLASS|CLS_META)));
-#endif
-
- // Check method list
-
- m1 = class_copyMethodList(cls, NULL);
- m2 = class_copyMethodList(clone, NULL);
- testassert(m1);
- testassert(m2);
- for (i = 0; m1[i] && m2[i]; i++) {
- testassert(m1[i] != m2[i]); // method list must be deep-copied
- testassert(method_getName(m1[i]) == method_getName(m2[i]));
- testassert(method_getImplementation(m1[i]) == method_getImplementation(m2[i]));
- testassert(method_getTypeEncoding(m1[i]) == method_getTypeEncoding(m2[i]));
- }
- testassert(m1[i] == NULL && m2[i] == NULL);
- free(m1);
- free(m2);
-
- // Check ivar list
- Ivar *i1 = class_copyIvarList(cls, NULL);
- Ivar *i2 = class_copyIvarList(clone, NULL);
- testassert(i1);
- testassert(i2);
- for (i = 0; i1[i] && i2[i]; i++) {
- testassert(i1[i] == i2[i]); // ivars are not deep-copied
- }
- testassert(i1[i] == NULL && i2[i] == NULL);
- free(i1);
- free(i2);
-
- // Check protocol list
- Protocol * __unsafe_unretained *p1 = class_copyProtocolList(cls, NULL);
- Protocol * __unsafe_unretained *p2 = class_copyProtocolList(clone, NULL);
- testassert(p1);
- testassert(p2);
- for (i = 0; p1[i] && p2[i]; i++) {
- testassert(p1[i] == p2[i]); // protocols are not deep-copied
- }
- testassert(p1[i] == NULL && p2[i] == NULL);
- free(p1);
- free(p2);
-
- // Check property list
- objc_property_t *o1 = class_copyPropertyList(cls, NULL);
- objc_property_t *o2 = class_copyPropertyList(clone, NULL);
- testassert(o1);
- testassert(o2);
- for (i = 0; o1[i] && o2[i]; i++) {
- testassert(o1[i] == o2[i]); // properties are not deep-copied
- }
- testassert(o1[i] == NULL && o2[i] == NULL);
- free(o1);
- free(o2);
-
- // Check method calls
-
- state = 0;
- [cls classMethod];
- testassert(state == 2);
- state = 0;
- [clone classMethod];
- testassert(state == 2);
-
- // #4511660 Make sure category implementation is still the preferred one
- id obj;
- obj = [cls new];
- state = 0;
- [obj instanceMethod];
- testassert(state == 4);
- RELEASE_VAR(obj);
-
- obj = [clone new];
- state = 0;
- [obj instanceMethod];
- testassert(state == 4);
- RELEASE_VAR(obj);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_ENV OBJC_DEBUG_DUPLICATE_CLASSES=YES
-// TEST_CRASHES
-/*
-TEST_RUN_OUTPUT
-objc\[\d+\]: Class GKScore is implemented in both [^\s]+ and [^\s]+ One of the two will be used. Which one is undefined.
-CRASHED: SIG(ILL|TRAP)
-OR
-OK: duplicatedClasses.m
-END
- */
-
-#include "test.h"
-#include "testroot.i"
-
-@interface GKScore : TestRoot @end
-@implementation GKScore @end
-
-int main()
-{
- if (objc_collectingEnabled()) {
- testwarn("rdar://19042235 test disabled because GameKit is not GC");
- succeed(__FILE__);
- }
- void *dl = dlopen("/System/Library/Frameworks/GameKit.framework/GameKit", RTLD_LAZY);
- if (!dl) fail("couldn't open GameKit");
- fail("should have crashed already");
-}
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-category-0.m -dynamiclib -o libevil.dylib
- $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-category-0.out
-END
-*/
-
-// NOT EVIL version
-
-#define EVIL_INSTANCE_METHOD 0
-#define EVIL_CLASS_METHOD 0
-
-#define OMIT_CAT 0
-#define OMIT_NL_CAT 0
-
-#include "evil-category-def.m"
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_CONFIG OS=iphoneos
-TEST_CRASHES
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-category-00.m $DIR/evil-main.m -o evil-category-00.out
-END
-
-TEST_RUN_OUTPUT
-CRASHED: SIGSEGV
-END
-*/
-
-// NOT EVIL version: apps are allowed through (then crash in +load)
-
-#define EVIL_INSTANCE_METHOD 1
-#define EVIL_CLASS_METHOD 1
-
-#define OMIT_CAT 0
-#define OMIT_NL_CAT 0
-
-#include "evil-category-def.m"
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-category-000.m -dynamiclib -o libevil.dylib
- $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-category-000.out
-END
-*/
-
-// NOT EVIL version: category omitted from all lists
-
-#define EVIL_INSTANCE_METHOD 1
-#define EVIL_CLASS_METHOD 1
-
-#define OMIT_CAT 1
-#define OMIT_NL_CAT 1
-
-#include "evil-category-def.m"
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_CONFIG OS=iphoneos
-TEST_CRASHES
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-category-1.m -dynamiclib -o libevil.dylib
- $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-1.out
-END
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
-CRASHED: SIG(ILL|TRAP)
-END
-*/
-
-#define EVIL_INSTANCE_METHOD 1
-#define EVIL_CLASS_METHOD 0
-
-#define OMIT_CAT 0
-#define OMIT_NL_CAT 0
-
-#include "evil-category-def.m"
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_CONFIG OS=iphoneos
-TEST_CRASHES
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-category-2.m -dynamiclib -o libevil.dylib
- $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-2.out
-END
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
-CRASHED: SIG(ILL|TRAP)
-END
-*/
-
-#define EVIL_INSTANCE_METHOD 0
-#define EVIL_CLASS_METHOD 1
-
-#define OMIT_CAT 0
-#define OMIT_NL_CAT 0
-
-#include "evil-category-def.m"
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_CONFIG OS=iphoneos
-TEST_CRASHES
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-category-3.m -dynamiclib -o libevil.dylib
- $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-3.out
-END
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
-CRASHED: SIG(ILL|TRAP)
-END
-*/
-
-#define EVIL_INSTANCE_METHOD 0
-#define EVIL_CLASS_METHOD 1
-
-#define OMIT_CAT 1
-#define OMIT_NL_CAT 0
-
-#include "evil-category-def.m"
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_CONFIG OS=iphoneos
-TEST_CRASHES
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-category-4.m -dynamiclib -o libevil.dylib
- $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-category-4.out
-END
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
-CRASHED: SIG(ILL|TRAP)
-END
-*/
-
-#define EVIL_INSTANCE_METHOD 0
-#define EVIL_CLASS_METHOD 1
-
-#define OMIT_CAT 0
-#define OMIT_NL_CAT 1
-
-#include "evil-category-def.m"
+++ /dev/null
-
-#if __OBJC2__
-
-#include <mach/shared_region.h>
-
-#if __LP64__
-# define PTR " .quad "
-#else
-# define PTR " .long "
-#endif
-
-#define str(x) #x
-#define str2(x) str(x)
-
-__BEGIN_DECLS
-void nop(void) { }
-__END_DECLS
-
-asm(
- ".section __DATA,__objc_data \n"
- ".align 3 \n"
- "L_category: \n"
- PTR "L_cat_name \n"
- PTR "_OBJC_CLASS_$_NSObject \n"
-#if EVIL_INSTANCE_METHOD
- PTR "L_evil_methods \n"
-#else
- PTR "L_good_methods \n"
-#endif
-#if EVIL_CLASS_METHOD
- PTR "L_evil_methods \n"
-#else
- PTR "L_good_methods \n"
-#endif
- PTR "0 \n"
- PTR "0 \n"
-
- "L_evil_methods: \n"
- ".long 24 \n"
- ".long 1 \n"
- PTR "L_load \n"
- PTR "L_load \n"
- PTR str2(SHARED_REGION_BASE+SHARED_REGION_SIZE-PAGE_MAX_SIZE) " \n"
-
- "L_good_methods: \n"
- ".long 24 \n"
- ".long 1 \n"
- PTR "L_load \n"
- PTR "L_load \n"
- PTR "_nop \n"
-
- ".cstring \n"
- "L_cat_name: .ascii \"Evil\\0\" \n"
- "L_load: .ascii \"load\\0\" \n"
-
- ".section __DATA,__objc_catlist \n"
-#if !OMIT_CAT
- PTR "L_category \n"
-#endif
-
- ".section __DATA,__objc_nlcatlist \n"
-#if !OMIT_NL_CAT
- PTR "L_category \n"
-#endif
-
- ".text \n"
- );
-
-// __OBJC2__
-#endif
-
-void fn(void) { }
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-class-0.m -dynamiclib -o libevil.dylib
- $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-class-0.out
-END
-*/
-
-// NOT EVIL version
-
-#define EVIL_SUPER 0
-#define EVIL_SUPER_META 0
-#define EVIL_SUB 0
-#define EVIL_SUB_META 0
-
-#define OMIT_SUPER 0
-#define OMIT_NL_SUPER 0
-#define OMIT_SUB 0
-#define OMIT_NL_SUB 0
-
-#include "evil-class-def.m"
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_CONFIG OS=iphoneos
-TEST_CRASHES
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-class-00.m $DIR/evil-main.m -o evil-class-00.out
-END
-
-TEST_RUN_OUTPUT
-CRASHED: SIGSEGV
-END
-*/
-
-// NOT EVIL version: apps are allowed through (then crash in +load)
-
-#define EVIL_SUPER 0
-#define EVIL_SUPER_META 1
-#define EVIL_SUB 0
-#define EVIL_SUB_META 0
-
-#define OMIT_SUPER 1
-#define OMIT_NL_SUPER 1
-#define OMIT_SUB 1
-#define OMIT_NL_SUB 0
-
-#include "evil-class-def.m"
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-class-000.m -dynamiclib -o libevil.dylib
- $C{COMPILE} $DIR/evil-main.m -x none -DNOT_EVIL libevil.dylib -o evil-class-000.out
-END
-*/
-
-// NOT EVIL version: all classes omitted from all lists
-
-#define EVIL_SUPER 1
-#define EVIL_SUPER_META 1
-#define EVIL_SUB 1
-#define EVIL_SUB_META 1
-
-#define OMIT_SUPER 1
-#define OMIT_NL_SUPER 1
-#define OMIT_SUB 1
-#define OMIT_NL_SUB 1
-
-#include "evil-class-def.m"
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_CONFIG OS=iphoneos
-TEST_CRASHES
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-class-1.m -dynamiclib -o libevil.dylib
- $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-1.out
-END
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
-CRASHED: SIG(ILL|TRAP)
-END
-*/
-
-#define EVIL_SUPER 1
-#define EVIL_SUPER_META 0
-#define EVIL_SUB 0
-#define EVIL_SUB_META 0
-
-#define OMIT_SUPER 0
-#define OMIT_NL_SUPER 0
-#define OMIT_SUB 0
-#define OMIT_NL_SUB 0
-
-#include "evil-class-def.m"
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_CONFIG OS=iphoneos
-TEST_CRASHES
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-class-2.m -dynamiclib -o libevil.dylib
- $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-2.out
-END
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
-CRASHED: SIG(ILL|TRAP)
-END
-*/
-
-#define EVIL_SUPER 0
-#define EVIL_SUPER_META 1
-#define EVIL_SUB 0
-#define EVIL_SUB_META 0
-
-#define OMIT_SUPER 0
-#define OMIT_NL_SUPER 0
-#define OMIT_SUB 0
-#define OMIT_NL_SUB 0
-
-#include "evil-class-def.m"
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_CONFIG OS=iphoneos
-TEST_CRASHES
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-class-3.m -dynamiclib -o libevil.dylib
- $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-3.out
-END
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
-CRASHED: SIG(ILL|TRAP)
-END
-*/
-
-#define EVIL_SUPER 0
-#define EVIL_SUPER_META 0
-#define EVIL_SUB 1
-#define EVIL_SUB_META 0
-
-#define OMIT_SUPER 0
-#define OMIT_NL_SUPER 0
-#define OMIT_SUB 0
-#define OMIT_NL_SUB 0
-
-#include "evil-class-def.m"
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_CONFIG OS=iphoneos
-TEST_CRASHES
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-class-4.m -dynamiclib -o libevil.dylib
- $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-4.out
-END
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
-CRASHED: SIG(ILL|TRAP)
-END
-*/
-
-#define EVIL_SUPER 0
-#define EVIL_SUPER_META 0
-#define EVIL_SUB 0
-#define EVIL_SUB_META 1
-
-#define OMIT_SUPER 0
-#define OMIT_NL_SUPER 0
-#define OMIT_SUB 0
-#define OMIT_NL_SUB 0
-
-#include "evil-class-def.m"
+++ /dev/null
-/*
-rdar://8553305
-
-TEST_DISABLED rdar://19200100
-
-TEST_CONFIG OS=iphoneos
-TEST_CRASHES
-
-TEST_BUILD
- $C{COMPILE} $DIR/evil-class-5.m -dynamiclib -o libevil.dylib
- $C{COMPILE} $DIR/evil-main.m -x none libevil.dylib -o evil-class-5.out
-END
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: bad method implementation \(0x[0-9a-f]+ at 0x[0-9a-f]+\)
-CRASHED: SIG(ILL|TRAP)
-END
-*/
-
-#define EVIL_SUPER 0
-#define EVIL_SUPER_META 1
-#define EVIL_SUB 0
-#define EVIL_SUB_META 0
-
-#define OMIT_SUPER 1
-#define OMIT_NL_SUPER 1
-#define OMIT_SUB 1
-#define OMIT_NL_SUB 0
-
-#include "evil-class-def.m"
+++ /dev/null
-#if __OBJC2__
-
-#include <mach/shared_region.h>
-
-#if __LP64__
-# define PTR " .quad "
-# define PTRSIZE "8"
-# define LOGPTRSIZE "3"
-#else
-# define PTR " .long "
-# define PTRSIZE "4"
-# define LOGPTRSIZE "2"
-#endif
-
-#define str(x) #x
-#define str2(x) str(x)
-
-__BEGIN_DECLS
-// not id to avoid ARC operations because the class doesn't implement RR methods
-void* nop(void* self) { return self; }
-__END_DECLS
-
-asm(
- ".globl _OBJC_CLASS_$_Super \n"
- ".section __DATA,__objc_data \n"
- ".align 3 \n"
- "_OBJC_CLASS_$_Super: \n"
- PTR "_OBJC_METACLASS_$_Super \n"
- PTR "0 \n"
- PTR "__objc_empty_cache \n"
- PTR "0 \n"
- PTR "L_ro \n"
- // pad to OBJC_MAX_CLASS_SIZE
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- ""
- "_OBJC_METACLASS_$_Super: \n"
- PTR "_OBJC_METACLASS_$_Super \n"
- PTR "_OBJC_CLASS_$_Super \n"
- PTR "__objc_empty_cache \n"
- PTR "0 \n"
- PTR "L_meta_ro \n"
- // pad to OBJC_MAX_CLASS_SIZE
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- ""
- "L_ro: \n"
- ".long 2 \n"
- ".long 0 \n"
- ".long "PTRSIZE" \n"
-#if __LP64__
- ".long 0 \n"
-#endif
- PTR "0 \n"
- PTR "L_super_name \n"
-#if EVIL_SUPER
- PTR "L_evil_methods \n"
-#else
- PTR "L_good_methods \n"
-#endif
- PTR "0 \n"
- PTR "L_super_ivars \n"
- PTR "0 \n"
- PTR "0 \n"
- ""
- "L_meta_ro: \n"
- ".long 3 \n"
- ".long 40 \n"
- ".long 40 \n"
-#if __LP64__
- ".long 0 \n"
-#endif
- PTR "0 \n"
- PTR "L_super_name \n"
-#if EVIL_SUPER_META
- PTR "L_evil_methods \n"
-#else
- PTR "L_good_methods \n"
-#endif
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
-
- ".globl _OBJC_CLASS_$_Sub \n"
- ".section __DATA,__objc_data \n"
- ".align 3 \n"
- "_OBJC_CLASS_$_Sub: \n"
- PTR "_OBJC_METACLASS_$_Sub \n"
- PTR "_OBJC_CLASS_$_Super \n"
- PTR "__objc_empty_cache \n"
- PTR "0 \n"
- PTR "L_sub_ro \n"
- // pad to OBJC_MAX_CLASS_SIZE
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- ""
- "_OBJC_METACLASS_$_Sub: \n"
- PTR "_OBJC_METACLASS_$_Super \n"
- PTR "_OBJC_METACLASS_$_Super \n"
- PTR "__objc_empty_cache \n"
- PTR "0 \n"
- PTR "L_sub_meta_ro \n"
- // pad to OBJC_MAX_CLASS_SIZE
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- ""
- "L_sub_ro: \n"
- ".long 2 \n"
- ".long 0 \n"
- ".long "PTRSIZE" \n"
-#if __LP64__
- ".long 0 \n"
-#endif
- PTR "0 \n"
- PTR "L_sub_name \n"
-#if EVIL_SUB
- PTR "L_evil_methods \n"
-#else
- PTR "L_good_methods \n"
-#endif
- PTR "0 \n"
- PTR "L_sub_ivars \n"
- PTR "0 \n"
- PTR "0 \n"
- ""
- "L_sub_meta_ro: \n"
- ".long 3 \n"
- ".long 40 \n"
- ".long 40 \n"
-#if __LP64__
- ".long 0 \n"
-#endif
- PTR "0 \n"
- PTR "L_sub_name \n"
-#if EVIL_SUB_META
- PTR "L_evil_methods \n"
-#else
- PTR "L_good_methods \n"
-#endif
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
- PTR "0 \n"
-
- "L_evil_methods: \n"
- ".long 3*"PTRSIZE" \n"
- ".long 1 \n"
- PTR "L_load \n"
- PTR "L_load \n"
- PTR str2(SHARED_REGION_BASE+SHARED_REGION_SIZE-PAGE_MAX_SIZE) " \n"
-
- "L_good_methods: \n"
- ".long 3*"PTRSIZE" \n"
- ".long 2 \n"
- PTR "L_load \n"
- PTR "L_load \n"
- PTR "_nop \n"
- PTR "L_self \n"
- PTR "L_self \n"
- PTR "_nop \n"
-
- "L_super_ivars: \n"
- ".long 4*"PTRSIZE" \n"
- ".long 1 \n"
- PTR "L_super_ivar_offset \n"
- PTR "L_super_ivar_name \n"
- PTR "L_super_ivar_type \n"
- ".long "LOGPTRSIZE" \n"
- ".long "PTRSIZE" \n"
-
- "L_sub_ivars: \n"
- ".long 4*"PTRSIZE" \n"
- ".long 1 \n"
- PTR "L_sub_ivar_offset \n"
- PTR "L_sub_ivar_name \n"
- PTR "L_sub_ivar_type \n"
- ".long "LOGPTRSIZE" \n"
- ".long "PTRSIZE" \n"
-
- "L_super_ivar_offset: \n"
- ".long 0 \n"
- "L_sub_ivar_offset: \n"
- ".long "PTRSIZE" \n"
-
- ".cstring \n"
- "L_super_name: .ascii \"Super\\0\" \n"
- "L_sub_name: .ascii \"Sub\\0\" \n"
- "L_load: .ascii \"load\\0\" \n"
- "L_self: .ascii \"self\\0\" \n"
- "L_super_ivar_name: .ascii \"super_ivar\\0\" \n"
- "L_super_ivar_type: .ascii \"c\\0\" \n"
- "L_sub_ivar_name: .ascii \"sub_ivar\\0\" \n"
- "L_sub_ivar_type: .ascii \"@\\0\" \n"
-
-
- ".section __DATA,__objc_classlist \n"
-#if !OMIT_SUPER
- PTR "_OBJC_CLASS_$_Super \n"
-#endif
-#if !OMIT_SUB
- PTR "_OBJC_CLASS_$_Sub \n"
-#endif
-
- ".section __DATA,__objc_nlclslist \n"
-#if !OMIT_NL_SUPER
- PTR "_OBJC_CLASS_$_Super \n"
-#endif
-#if !OMIT_NL_SUB
- PTR "_OBJC_CLASS_$_Sub \n"
-#endif
-
- ".text \n"
-);
-
-// __OBJC2__
-#endif
-
-void fn(void) { }
+++ /dev/null
-#include "test.h"
-
-extern void fn(void);
-
-int main(int argc __unused, char **argv)
-{
- fn();
-
-#if TARGET_OS_EMBEDDED && !defined(NOT_EVIL)
-#pragma unused (argv)
- fail("All that is necessary for the triumph of evil is that good men do nothing.");
-#else
- succeed(basename(argv[0]));
-#endif
-}
+++ /dev/null
-/*
-need exception-safe ARC for exception deallocation tests
-need F/CF for testonthread() in GC mode
-TEST_CFLAGS -fobjc-arc-exceptions -framework Foundation
-
-llvm-gcc unavoidably warns about our deliberately out-of-order handlers
-
-TEST_BUILD_OUTPUT
-.*exc.m: In function .*
-.*exc.m:\d+: warning: exception of type .* will be caught
-.*exc.m:\d+: warning: by earlier handler for .*
-.*exc.m:\d+: warning: exception of type .* will be caught
-.*exc.m:\d+: warning: by earlier handler for .*
-.*exc.m:\d+: warning: exception of type .* will be caught
-.*exc.m:\d+: warning: by earlier handler for .*
-OR
-END
-*/
-
-#include "test.h"
-#include "testroot.i"
-#include <objc/runtime.h>
-#include <objc/objc-exception.h>
-
-static volatile int state = 0;
-static volatile int dealloced = 0;
-#define BAD 1000000
-
-#if defined(USE_FOUNDATION)
-
-#include <Foundation/Foundation.h>
-
-@interface Super : NSException @end
-@implementation Super
-+(id)exception { return AUTORELEASE([[self alloc] initWithName:@"Super" reason:@"reason" userInfo:nil]); }
--(void)check { state++; }
-+(void)check { testassert(!"caught class object, not instance"); }
--(void)dealloc { dealloced++; SUPER_DEALLOC(); }
--(void)finalize { dealloced++; [super finalize]; }
-@end
-
-#define FILENAME "nsexc.m"
-
-#else
-
-@interface Super : TestRoot @end
-@implementation Super
-+(id)exception { return AUTORELEASE([self new]); }
--(void)check { state++; }
-+(void)check { testassert(!"caught class object, not instance"); }
--(void)dealloc { dealloced++; SUPER_DEALLOC(); }
--(void)finalize { dealloced++; [super finalize]; }
-@end
-
-#define FILENAME "exc.m"
-
-#endif
-
-@interface Sub : Super @end
-@implementation Sub
-@end
-
-
-#if __OBJC2__ && !TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE
-void altHandlerFail(id unused __unused, void *context __unused)
-{
- fail("altHandlerFail called");
-}
-
-#define ALT_HANDLER(n) \
- void altHandler##n(id unused __unused, void *context) \
- { \
- testassert(context == (void*)&altHandler##n); \
- testassert(state == n); \
- state++; \
- }
-
-ALT_HANDLER(1)
-ALT_HANDLER(2)
-ALT_HANDLER(3)
-ALT_HANDLER(4)
-ALT_HANDLER(5)
-ALT_HANDLER(6)
-ALT_HANDLER(7)
-
-
-static void throwWithAltHandler(void) __attribute__((noinline));
-static void throwWithAltHandler(void)
-{
- @try {
- state++;
- uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3);
- // state++ inside alt handler
- @throw [Super exception];
- state = BAD;
- objc_removeExceptionHandler(token);
- }
- @catch (Sub *e) {
- state = BAD;
- }
- state = BAD;
-}
-
-
-static void throwWithAltHandlerAndRethrow(void) __attribute__((noinline));
-static void throwWithAltHandlerAndRethrow(void)
-{
- @try {
- state++;
- uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3);
- // state++ inside alt handler
- @throw [Super exception];
- state = BAD;
- objc_removeExceptionHandler(token);
- }
- @catch (...) {
- testassert(state == 4);
- state++;
- @throw;
- }
- state = BAD;
-}
-
-#endif
-
-#if __cplusplus && __OBJC2__
-#include <exception>
-void terminator() {
- succeed(FILENAME);
-}
-#endif
-
-
-#define TEST(code) \
- do { \
- testonthread(^{ PUSH_POOL { code } POP_POOL; }); \
- testcollect(); \
- } while (0)
-
-
-
-int main()
-{
- testprintf("try-catch-finally, exception caught exactly\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- @throw [Super exception];
- state = BAD;
- }
- @catch (Super *e) {
- state++;
- [e check]; // state++
- }
- @finally {
- state++;
- }
- state++;
- }
- @catch (...) {
- state = BAD;
- }
- });
- testassert(state == 6);
- testassert(dealloced == 1);
-
-
- testprintf("try-finally, no exception thrown\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- }
- @finally {
- state++;
- }
- state++;
- }
- @catch (...) {
- state = BAD;
- }
- });
- testassert(state == 4);
- testassert(dealloced == 0);
-
-
- testprintf("try-finally, with exception\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- @throw [Super exception];
- state = BAD;
- }
- @finally {
- state++;
- }
- state = BAD;
- }
- @catch (id e) {
- state++;
- [e check]; // state++
- }
- });
- testassert(state == 5);
- testassert(dealloced == 1);
-
-
-#if __OBJC2__
- testprintf("try-finally, with autorelease pool pop during unwind\n");
- // Popping an autorelease pool during unwind used to deallocate the
- // exception object, but now we retain them while in flight.
-
- // This use-after-free is undetected without MallocScribble or guardmalloc.
- if (!getenv("MallocScribble") &&
- (!getenv("DYLD_INSERT_LIBRARIES") ||
- !strstr(getenv("DYLD_INSERT_LIBRARIES"), "libgmalloc")))
- {
- testwarn("MallocScribble not set");
- }
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- void *pool2 = objc_autoreleasePoolPush();
- state++;
- @try {
- state++;
- @throw [Super exception];
- state = BAD;
- }
- @finally {
- state++;
- objc_autoreleasePoolPop(pool2);
- }
- state = BAD;
- }
- @catch (id e) {
- state++;
- [e check]; // state++
- }
- });
- testassert(state == 5);
- testassert(dealloced == 1);
-#endif
-
-
- testprintf("try-catch-finally, no exception\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- }
- @catch (...) {
- state = BAD;
- }
- @finally {
- state++;
- }
- state++;
- } @catch (...) {
- state = BAD;
- }
- });
- testassert(state == 4);
- testassert(dealloced == 0);
-
-
- testprintf("try-catch-finally, exception not caught\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- @throw [Super exception];
- state = BAD;
- }
- @catch (Sub *e) {
- state = BAD;
- }
- @finally {
- state++;
- }
- state = BAD;
- }
- @catch (id e) {
- state++;
- [e check]; // state++
- }
- });
- testassert(state == 5);
- testassert(dealloced == 1);
-
-
- testprintf("try-catch-finally, exception caught exactly, rethrown\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- @throw [Super exception];
- state = BAD;
- }
- @catch (Super *e) {
- state++;
- [e check]; // state++
- @throw;
- state = BAD;
- }
- @finally {
- state++;
- }
- state = BAD;
- }
- @catch (id e) {
- state++;
- [e check]; // state++
- }
- });
- testassert(state == 7);
- testassert(dealloced == 1);
-
-
- testprintf("try-catch, no exception\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- }
- @catch (...) {
- state = BAD;
- }
- state++;
- } @catch (...) {
- state = BAD;
- }
- });
- testassert(state == 3);
- testassert(dealloced == 0);
-
-
- testprintf("try-catch, exception not caught\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- @throw [Super exception];
- state = BAD;
- }
- @catch (Sub *e) {
- state = BAD;
- }
- state = BAD;
- }
- @catch (id e) {
- state++;
- [e check]; // state++
- }
- });
- testassert(state == 4);
- testassert(dealloced == 1);
-
-
- testprintf("try-catch, exception caught exactly\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- @throw [Super exception];
- state = BAD;
- }
- @catch (Super *e) {
- state++;
- [e check]; // state++
- }
- state++;
- }
- @catch (...) {
- state = BAD;
- }
- });
- testassert(state == 5);
- testassert(dealloced == 1);
-
-
- testprintf("try-catch, exception caught exactly, rethrown\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- @throw [Super exception];
- state = BAD;
- }
- @catch (Super *e) {
- state++;
- [e check]; // state++
- @throw;
- state = BAD;
- }
- state = BAD;
- }
- @catch (id e) {
- state++;
- [e check]; // state++
- }
- });
- testassert(state == 6);
- testassert(dealloced == 1);
-
-
- testprintf("try-catch, exception caught exactly, thrown again explicitly\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- @throw [Super exception];
- state = BAD;
- }
- @catch (Super *e) {
- state++;
- [e check]; // state++
- @throw e;
- state = BAD;
- }
- state = BAD;
- }
- @catch (id e) {
- state++;
- [e check]; // state++
- }
- });
- testassert(state == 6);
- testassert(dealloced == 1);
-
-
- testprintf("try-catch, default catch, rethrown\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- @throw [Super exception];
- state = BAD;
- }
- @catch (...) {
- state++;
- @throw;
- state = BAD;
- }
- state = BAD;
- }
- @catch (id e) {
- state++;
- [e check]; // state++
- }
- });
- testassert(state == 5);
- testassert(dealloced == 1);
-
-
- testprintf("try-catch, default catch, rethrown and caught inside nested handler\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- @throw [Super exception];
- state = BAD;
- }
- @catch (...) {
- state++;
-
- @try {
- state++;
- @throw;
- state = BAD;
- } @catch (Sub *e) {
- state = BAD;
- } @catch (Super *e) {
- state++;
- [e check]; // state++
- } @catch (...) {
- state = BAD;
- } @finally {
- state++;
- }
-
- state++;
- }
- state++;
- }
- @catch (...) {
- state = BAD;
- }
- });
- testassert(state == 9);
- testassert(dealloced == 1);
-
-
- testprintf("try-catch, default catch, rethrown inside nested handler but not caught\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- @try {
- state++;
- @try {
- state++;
- @throw [Super exception];
- state = BAD;
- }
- @catch (...) {
- state++;
-
- @try {
- state++;
- @throw;
- state = BAD;
- }
- @catch (Sub *e) {
- state = BAD;
- }
- @finally {
- state++;
- }
-
- state = BAD;
- }
- state = BAD;
- }
- @catch (id e) {
- state++;
- [e check]; // state++
- }
- });
- testassert(state == 7);
- testassert(dealloced == 1);
-
-
-#if __cplusplus && __OBJC2__
- testprintf("C++ try/catch, Objective-C exception superclass\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- try {
- state++;
- try {
- state++;
- try {
- state++;
- @throw [Super exception];
- state = BAD;
- } catch (...) {
- state++;
- throw;
- state = BAD;
- }
- state = BAD;
- } catch (void *e) {
- state = BAD;
- } catch (int e) {
- state = BAD;
- } catch (Sub *e) {
- state = BAD;
- } catch (Super *e) {
- state++;
- [e check]; // state++
- throw;
- } catch (...) {
- state = BAD;
- }
- } catch (id e) {
- state++;
- [e check]; // state++;
- }
- });
- testassert(state == 8);
- testassert(dealloced == 1);
-
-
- testprintf("C++ try/catch, Objective-C exception subclass\n");
-
- TEST({
- state = 0;
- dealloced = 0;
- try {
- state++;
- try {
- state++;
- try {
- state++;
- @throw [Sub exception];
- state = BAD;
- } catch (...) {
- state++;
- throw;
- state = BAD;
- }
- state = BAD;
- } catch (void *e) {
- state = BAD;
- } catch (int e) {
- state = BAD;
- } catch (Super *e) {
- state++;
- [e check]; // state++
- throw;
- } catch (Sub *e) {
- state = BAD;
- } catch (...) {
- state = BAD;
- }
- } catch (id e) {
- state++;
- [e check]; // state++;
- }
- });
- testassert(state == 8);
- testassert(dealloced == 1);
-
-#endif
-
-
-#if !__OBJC2__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
- // alt handlers for modern Mac OS only
-
-#else
- {
- // alt handlers
- // run a lot to catch failed unregistration (runtime complains at 1000)
-#define ALT_HANDLER_REPEAT 2000
-
- testprintf("alt handler, no exception\n");
-
- TEST({
- dealloced = 0;
- for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
- state = 0;
- @try {
- state++;
- @try {
- uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0);
- state++;
- objc_removeExceptionHandler(token);
- }
- @catch (...) {
- state = BAD;
- }
- state++;
- } @catch (...) {
- state = BAD;
- }
- testassert(state == 3);
- }
- });
- testassert(dealloced == 0);
-
-
- testprintf("alt handler, exception thrown through\n");
-
- TEST({
- dealloced = 0;
- for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
- state = 0;
- @try {
- state++;
- @try {
- state++;
- uintptr_t token = objc_addExceptionHandler(altHandler2, (void*)altHandler2);
- // state++ inside alt handler
- @throw [Super exception];
- state = BAD;
- objc_removeExceptionHandler(token);
- }
- @catch (Sub *e) {
- state = BAD;
- }
- state = BAD;
- }
- @catch (id e) {
- testassert(state == 3);
- state++;
- [e check]; // state++
- }
- testassert(state == 5);
- }
- });
- testassert(dealloced == ALT_HANDLER_REPEAT);
-
-
- testprintf("alt handler, nested\n");
-
- TEST({
- dealloced = 0;
- for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
- state = 0;
- @try {
- state++;
- @try {
- state++;
- // same-level handlers called in FIFO order (not stack-like)
- uintptr_t token = objc_addExceptionHandler(altHandler4, (void*)altHandler4);
- // state++ inside alt handler
- uintptr_t token2 = objc_addExceptionHandler(altHandler5, (void*)altHandler5);
- // state++ inside alt handler
- throwWithAltHandler(); // state += 2 inside
- state = BAD;
- objc_removeExceptionHandler(token);
- objc_removeExceptionHandler(token2);
- }
- @catch (id e) {
- testassert(state == 6);
- state++;
- [e check]; // state++;
- }
- state++;
- }
- @catch (...) {
- state = BAD;
- }
- testassert(state == 9);
- }
- });
- testassert(dealloced == ALT_HANDLER_REPEAT);
-
-
- testprintf("alt handler, nested, rethrows in between\n");
-
- TEST({
- dealloced = 0;
- for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
- state = 0;
- @try {
- state++;
- @try {
- state++;
- // same-level handlers called in FIFO order (not stack-like)
- uintptr_t token = objc_addExceptionHandler(altHandler5, (void*)altHandler5);
- // state++ inside alt handler
- uintptr_t token2 = objc_addExceptionHandler(altHandler6, (void*)altHandler6);
- // state++ inside alt handler
- throwWithAltHandlerAndRethrow(); // state += 3 inside
- state = BAD;
- objc_removeExceptionHandler(token);
- objc_removeExceptionHandler(token2);
- }
- @catch (...) {
- testassert(state == 7);
- state++;
- @throw;
- }
- state = BAD;
- }
- @catch (id e) {
- testassert(state == 8);
- state++;
- [e check]; // state++
- }
- testassert(state == 10);
- }
- });
- testassert(dealloced == ALT_HANDLER_REPEAT);
-
-
- testprintf("alt handler, exception thrown and caught inside\n");
-
- TEST({
- dealloced = 0;
- for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
- state = 0;
- @try {
- state++;
- uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0);
- @try {
- state++;
- @throw [Super exception];
- state = BAD;
- }
- @catch (Super *e) {
- state++;
- [e check]; // state++
- }
- state++;
- objc_removeExceptionHandler(token);
- }
- @catch (...) {
- state = BAD;
- }
- testassert(state == 5);
- }
- });
- testassert(dealloced == ALT_HANDLER_REPEAT);
-
-
-#if defined(USE_FOUNDATION)
- testprintf("alt handler, rdar://10055775\n");
-
- TEST({
- dealloced = 0;
- for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
- state = 0;
- @try {
- uintptr_t token = objc_addExceptionHandler(altHandler1, (void*)altHandler1);
- {
- id x = [NSArray array];
- x = [NSArray array];
- }
- state++;
- // state++ inside alt handler
- [Super raise:@"foo" format:@"bar"];
- state = BAD;
- objc_removeExceptionHandler(token);
- } @catch (id e) {
- state++;
- testassert(state == 3);
- }
- testassert(state == 3);
- }
- });
- testassert(dealloced == ALT_HANDLER_REPEAT);
-
-// defined(USE_FOUNDATION)
-#endif
-
- }
-// alt handlers
-#endif
-
-#if __cplusplus && __OBJC2__
- std::set_terminate(terminator);
- objc_terminate();
- fail("should not have returned from objc_terminate()");
-#else
- succeed(FILENAME);
-#endif
-}
-
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include "testroot.i"
-#include <objc/runtime.h>
-
-static int state;
-
-#define ONE 1
-#define TWO 2
-#define LENGTH 3
-#define COUNT 4
-
-@interface Super : TestRoot @end
-@implementation Super
-+(void) one { state = ONE; }
-+(void) two { state = TWO; }
-+(void) length { state = LENGTH; }
-+(void) count { state = COUNT; }
-@end
-
-#define checkExchange(s1, v1, s2, v2) \
- do { \
- Method m1, m2; \
- \
- testprintf("Check unexchanged version\n"); \
- state = 0; \
- [Super s1]; \
- testassert(state == v1); \
- state = 0; \
- [Super s2]; \
- testassert(state == v2); \
- \
- testprintf("Exchange\n"); \
- m1 = class_getClassMethod([Super class], @selector(s1)); \
- m2 = class_getClassMethod([Super class], @selector(s2)); \
- testassert(m1); \
- testassert(m2); \
- method_exchangeImplementations(m1, m2); \
- \
- testprintf("Check exchanged version\n"); \
- state = 0; \
- [Super s1]; \
- testassert(state == v2); \
- state = 0; \
- [Super s2]; \
- testassert(state == v1); \
- \
- testprintf("NULL should do nothing\n"); \
- method_exchangeImplementations(m1, NULL); \
- method_exchangeImplementations(NULL, m2); \
- method_exchangeImplementations(NULL, NULL); \
- \
- testprintf("Make sure NULL did nothing\n"); \
- state = 0; \
- [Super s1]; \
- testassert(state == v2); \
- state = 0; \
- [Super s2]; \
- testassert(state == v1); \
- \
- testprintf("Put them back\n"); \
- method_exchangeImplementations(m1, m2); \
- \
- testprintf("Check restored version\n"); \
- state = 0; \
- [Super s1]; \
- testassert(state == v1); \
- state = 0; \
- [Super s2]; \
- testassert(state == v2); \
- } while (0)
-
-int main()
-{
- // Check ordinary selectors
- checkExchange(one, ONE, two, TWO);
-
- // Check vtable selectors
- checkExchange(length, LENGTH, count, COUNT);
-
- // Check ordinary<->vtable and vtable<->ordinary
- checkExchange(count, COUNT, one, ONE);
- checkExchange(two, TWO, length, LENGTH);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -framework Foundation
-
-#include "test.h"
-#import <Foundation/Foundation.h>
-
-/* foreach tester */
-
-int Errors = 0;
-
-bool testHandwritten(const char *style, const char *test, const char *message, id collection, NSSet *reference) {
- unsigned int counter = 0;
- bool result = true;
- testprintf("testing: %s %s %s\n", style, test, message);
-/*
- for (id elem in collection)
- if ([reference member:elem]) ++counter;
- */
- NSFastEnumerationState state;
- id __unsafe_unretained buffer[4];
- state.state = 0;
- NSUInteger limit = [collection countByEnumeratingWithState:&state objects:buffer count:4];
- if (limit != 0) {
- unsigned long mutationsPtr = *state.mutationsPtr;
- do {
- unsigned long innerCounter = 0;
- do {
- if (mutationsPtr != *state.mutationsPtr) objc_enumerationMutation(collection);
- id elem = state.itemsPtr[innerCounter++];
-
- if ([reference member:elem]) ++counter;
-
- } while (innerCounter < limit);
- } while ((limit = [collection countByEnumeratingWithState:&state objects:buffer count:4]));
- }
-
-
-
- if (counter == [reference count]) {
- testprintf("success: %s %s %s\n", style, test, message);
- }
- else {
- result = false;
- printf("** failed: %s %s %s (%d vs %d)\n", style, test, message, counter, (int)[reference count]);
- ++Errors;
- }
- return result;
-}
-
-bool testCompiler(const char *style, const char *test, const char *message, id collection, NSSet *reference) {
- unsigned int counter = 0;
- bool result = true;
- testprintf("testing: %s %s %s\n", style, test, message);
- for (id elem in collection)
- if ([reference member:elem]) ++counter;
- if (counter == [reference count]) {
- testprintf("success: %s %s %s\n", style, test, message);
- }
- else {
- result = false;
- printf("** failed: %s %s %s (%d vs %d)\n", style, test, message, counter, (int)[reference count]);
- ++Errors;
- }
- return result;
-}
-
-void testContinue(NSArray *array) {
- bool broken = false;
- testprintf("testing: continue statements\n");
- for (id __unused elem in array) {
- if ([array count])
- continue;
- broken = true;
- }
- if (broken) {
- printf("** continue statement did not work\n");
- ++Errors;
- }
-}
-
-
-// array is filled with NSNumbers, in order, from 0 - N
-bool testBreak(unsigned int where, NSArray *array) {
- PUSH_POOL {
- unsigned int counter = 0;
- id enumerator = [array objectEnumerator];
- for (id __unused elem in enumerator) {
- if (++counter == where)
- break;
- }
- if (counter != where) {
- ++Errors;
- printf("*** break at %d didn't work (actual was %d)\n", where, counter);
- return false;
- }
- for (id __unused elem in enumerator)
- ++counter;
- if (counter != [array count]) {
- ++Errors;
- printf("*** break at %d didn't finish (actual was %d)\n", where, counter);
- return false;
- }
- } POP_POOL;
- return true;
-}
-
-bool testBreaks(NSArray *array) {
- bool result = true;
- testprintf("testing breaks\n");
- unsigned int counter = 0;
- for (counter = 1; counter < [array count]; ++counter) {
- result = testBreak(counter, array) && result;
- }
- return result;
-}
-
-bool testCompleteness(const char *test, const char *message, id collection, NSSet *reference) {
- bool result = true;
- result = result && testHandwritten("handwritten", test, message, collection, reference);
- result = result && testCompiler("compiler", test, message, collection, reference);
- return result;
-}
-
-bool testEnumerator(const char *test, const char *message, id collection, NSSet *reference) {
- bool result = true;
- result = result && testHandwritten("handwritten", test, message, [collection objectEnumerator], reference);
- result = result && testCompiler("compiler", test, message, [collection objectEnumerator], reference);
- return result;
-}
-
-NSMutableSet *ReferenceSet = nil;
-NSMutableArray *ReferenceArray = nil;
-
-void makeReferences(int n) {
- if (!ReferenceSet) {
- int i;
- ReferenceSet = [[NSMutableSet alloc] init];
- ReferenceArray = [[NSMutableArray alloc] init];
- for (i = 0; i < n; ++i) {
- NSNumber *number = [[NSNumber alloc] initWithInt:i];
- [ReferenceSet addObject:number];
- [ReferenceArray addObject:number];
- RELEASE_VAR(number);
- }
- }
-}
-
-void testCollections(const char *test, NSArray *array, NSSet *set) {
- PUSH_POOL {
- id collection;
- collection = [NSMutableArray arrayWithArray:array];
- testCompleteness(test, "mutable array", collection, set);
- testEnumerator(test, "mutable array enumerator", collection, set);
- collection = [NSArray arrayWithArray:array];
- testCompleteness(test, "immutable array", collection, set);
- testEnumerator(test, "immutable array enumerator", collection, set);
- collection = set;
- testCompleteness(test, "immutable set", collection, set);
- testEnumerator(test, "immutable set enumerator", collection, set);
- collection = [NSMutableSet setWithArray:array];
- testCompleteness(test, "mutable set", collection, set);
- testEnumerator(test, "mutable set enumerator", collection, set);
- } POP_POOL;
-}
-
-void testInnerDecl(const char *test, const char *message, id collection) {
- unsigned int counter = 0;
- for (id __unused x in collection)
- ++counter;
- if (counter != [collection count]) {
- printf("** failed: %s %s\n", test, message);
- ++Errors;
- }
-}
-
-
-void testOuterDecl(const char *test, const char *message, id collection) {
- unsigned int counter = 0;
- id x;
- for (x in collection)
- ++counter;
- if (counter != [collection count]) {
- printf("** failed: %s %s\n", test, message);
- ++Errors;
- }
-}
-void testInnerExpression(const char *test, const char *message, id collection) {
- unsigned int counter = 0;
- for (id __unused x in [collection self])
- ++counter;
- if (counter != [collection count]) {
- printf("** failed: %s %s\n", test, message);
- ++Errors;
- }
-}
-void testOuterExpression(const char *test, const char *message, id collection) {
- unsigned int counter = 0;
- id x;
- for (x in [collection self])
- ++counter;
- if (counter != [collection count]) {
- printf("** failed: %s %s\n", test, message);
- ++Errors;
- }
-}
-
-void testExpressions(const char *message, id collection) {
- testInnerDecl("inner", message, collection);
- testOuterDecl("outer", message, collection);
- testInnerExpression("outer expression", message, collection);
- testOuterExpression("outer expression", message, collection);
-}
-
-
-int main() {
- PUSH_POOL {
- testCollections("nil", nil, nil);
- testCollections("empty", [NSArray array], [NSSet set]);
- makeReferences(100);
- testCollections("100 item", ReferenceArray, ReferenceSet);
- testExpressions("array", ReferenceArray);
- testBreaks(ReferenceArray);
- testContinue(ReferenceArray);
- if (Errors == 0) succeed(__FILE__);
- else fail("foreach %d errors detected\n", Errors);
- } POP_POOL;
- exit(Errors);
-}
+++ /dev/null
-// TEST_CONFIG MEM=mrc,gc
-// TEST_CFLAGS -Wno-deprecated-declarations
-
-#include "test.h"
-
-#if __cplusplus && !__clang__
-
-int main()
-{
- // llvm-g++ is confused by @selector(foo::) and will never be fixed
- succeed(__FILE__);
-}
-
-#else
-
-#include <objc/runtime.h>
-#include <objc/message.h>
-
-id ID_RESULT = (id)0x12345678;
-long long LL_RESULT = __LONG_LONG_MAX__ - 2LL*__INT_MAX__;
-double FP_RESULT = __DBL_MIN__ + __DBL_EPSILON__;
-long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
-// STRET_RESULT in test.h
-
-
-static int state = 0;
-static id receiver;
-
-OBJC_ROOT_CLASS
-@interface Super { id isa; } @end
-
-@interface Super (Forwarded)
-+(id)idret:
- (long)i1 :(long)i2 :(long)i3 :(long)i4 :(long)i5 :(long)i6 :(long)i7 :(long)i8 :(long)i9 :(long)i10 :(long)i11 :(long)i12 :(long)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
-+(id)idre2:
- (long)i1 :(long)i2 :(long)i3 :(long)i4 :(long)i5 :(long)i6 :(long)i7 :(long)i8 :(long)i9 :(long)i10 :(long)i11 :(long)i12 :(long)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
-+(id)idre3:
- (long)i1 :(long)i2 :(long)i3 :(long)i4 :(long)i5 :(long)i6 :(long)i7 :(long)i8 :(long)i9 :(long)i10 :(long)i11 :(long)i12 :(long)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
-+(long long)llret:
- (long)i1 :(long)i2 :(long)i3 :(long)i4 :(long)i5 :(long)i6 :(long)i7 :(long)i8 :(long)i9 :(long)i10 :(long)i11 :(long)i12 :(long)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
-+(long long)llre2:
- (long)i1 :(long)i2 :(long)i3 :(long)i4 :(long)i5 :(long)i6 :(long)i7 :(long)i8 :(long)i9 :(long)i10 :(long)i11 :(long)i12 :(long)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
-+(long long)llre3:
- (long)i1 :(long)i2 :(long)i3 :(long)i4 :(long)i5 :(long)i6 :(long)i7 :(long)i8 :(long)i9 :(long)i10 :(long)i11 :(long)i12 :(long)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
-+(struct stret)stret:
- (long)i1 :(long)i2 :(long)i3 :(long)i4 :(long)i5 :(long)i6 :(long)i7 :(long)i8 :(long)i9 :(long)i10 :(long)i11 :(long)i12 :(long)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
-+(struct stret)stre2:
- (long)i1 :(long)i2 :(long)i3 :(long)i4 :(long)i5 :(long)i6 :(long)i7 :(long)i8 :(long)i9 :(long)i10 :(long)i11 :(long)i12 :(long)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
-+(struct stret)stre3:
- (long)i1 :(long)i2 :(long)i3 :(long)i4 :(long)i5 :(long)i6 :(long)i7 :(long)i8 :(long)i9 :(long)i10 :(long)i11 :(long)i12 :(long)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
-+(double)fpret:
- (long)i1 :(long)i2 :(long)i3 :(long)i4 :(long)i5 :(long)i6 :(long)i7 :(long)i8 :(long)i9 :(long)i10 :(long)i11 :(long)i12 :(long)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
-+(double)fpre2:
- (long)i1 :(long)i2 :(long)i3 :(long)i4 :(long)i5 :(long)i6 :(long)i7 :(long)i8 :(long)i9 :(long)i10 :(long)i11 :(long)i12 :(long)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
-+(double)fpre3:
- (long)i1 :(long)i2 :(long)i3 :(long)i4 :(long)i5 :(long)i6 :(long)i7 :(long)i8 :(long)i9 :(long)i10 :(long)i11 :(long)i12 :(long)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
-@end
-
-
-long long forward_handler(id self, SEL _cmd, long i1, long i2, long i3, long i4, long i5, long i6, long i7, long i8, long i9, long i10, long i11, long i12, long i13, double f1, double f2, double f3, double f4, double f5, double f6, double f7, double f8, double f9, double f10, double f11, double f12, double f13, double f14, double f15)
-{
-#if __arm64__
- void *struct_addr;
- __asm__ volatile("mov %0, x8" : "=r" (struct_addr) : : "x8");
-#endif
-
- testassert(self == receiver);
-
- testassert(i1 == 1);
- testassert(i2 == 2);
- testassert(i3 == 3);
- testassert(i4 == 4);
- testassert(i5 == 5);
- testassert(i6 == 6);
- testassert(i7 == 7);
- testassert(i8 == 8);
- testassert(i9 == 9);
- testassert(i10 == 10);
- testassert(i11 == 11);
- testassert(i12 == 12);
- testassert(i13 == 13);
-
- testassert(f1 == 1.0);
- testassert(f2 == 2.0);
- testassert(f3 == 3.0);
- testassert(f4 == 4.0);
- testassert(f5 == 5.0);
- testassert(f6 == 6.0);
- testassert(f7 == 7.0);
- testassert(f8 == 8.0);
- testassert(f9 == 9.0);
- testassert(f10 == 10.0);
- testassert(f11 == 11.0);
- testassert(f12 == 12.0);
- testassert(f13 == 13.0);
- testassert(f14 == 14.0);
- testassert(f15 == 15.0);
-
- if (_cmd == @selector(idret::::::::::::::::::::::::::::) ||
- _cmd == @selector(idre2::::::::::::::::::::::::::::) ||
- _cmd == @selector(idre3::::::::::::::::::::::::::::))
- {
- union {
- id idval;
- long long llval;
- } result;
- testassert(state == 11);
- state = 12;
- result.idval = ID_RESULT;
- return result.llval;
- }
- else if (_cmd == @selector(llret::::::::::::::::::::::::::::) ||
- _cmd == @selector(llre2::::::::::::::::::::::::::::) ||
- _cmd == @selector(llre3::::::::::::::::::::::::::::))
- {
- testassert(state == 13);
- state = 14;
- return LL_RESULT;
- }
- else if (_cmd == @selector(fpret::::::::::::::::::::::::::::) ||
- _cmd == @selector(fpre2::::::::::::::::::::::::::::) ||
- _cmd == @selector(fpre3::::::::::::::::::::::::::::))
- {
- testassert(state == 15);
- state = 16;
-#if defined(__i386__)
- __asm__ volatile("fldl %0" : : "m" (FP_RESULT));
-#elif defined(__x86_64__)
- __asm__ volatile("movsd %0, %%xmm0" : : "m" (FP_RESULT));
-#elif defined(__arm__)
- union {
- double fpval;
- long long llval;
- } result;
- result.fpval = FP_RESULT;
- return result.llval;
-#elif defined(__arm64__)
- __asm__ volatile("ldr d0, %0" : : "m" (FP_RESULT));
-#else
-# error unknown architecture
-#endif
- return 0;
- }
- else if (_cmd == @selector(stret::::::::::::::::::::::::::::) ||
- _cmd == @selector(stre2::::::::::::::::::::::::::::) ||
- _cmd == @selector(stre3::::::::::::::::::::::::::::))
- {
-#if __i386__ || __x86_64__ || __arm__
- fail("stret message sent to non-stret forward_handler");
-#elif __arm64__
- testassert(state == 17);
- state = 18;
- memcpy(struct_addr, &STRET_RESULT, sizeof(STRET_RESULT));
- return 0;
-#else
-# error unknown architecture
-#endif
- }
- else {
- fail("unknown selector %s in forward_handler", sel_getName(_cmd));
- }
-}
-
-
-struct stret forward_stret_handler(id self, SEL _cmd, long i1, long i2, long i3, long i4, long i5, long i6, long i7, long i8, long i9, long i10, long i11, long i12, long i13, double f1, double f2, double f3, double f4, double f5, double f6, double f7, double f8, double f9, double f10, double f11, double f12, double f13, double f14, double f15)
-{
- testassert(self == receiver);
-
- testassert(i1 == 1);
- testassert(i2 == 2);
- testassert(i3 == 3);
- testassert(i4 == 4);
- testassert(i5 == 5);
- testassert(i6 == 6);
- testassert(i7 == 7);
- testassert(i8 == 8);
- testassert(i9 == 9);
- testassert(i10 == 10);
- testassert(i11 == 11);
- testassert(i12 == 12);
- testassert(i13 == 13);
-
- testassert(f1 == 1.0);
- testassert(f2 == 2.0);
- testassert(f3 == 3.0);
- testassert(f4 == 4.0);
- testassert(f5 == 5.0);
- testassert(f6 == 6.0);
- testassert(f7 == 7.0);
- testassert(f8 == 8.0);
- testassert(f9 == 9.0);
- testassert(f10 == 10.0);
- testassert(f11 == 11.0);
- testassert(f12 == 12.0);
- testassert(f13 == 13.0);
- testassert(f14 == 14.0);
- testassert(f15 == 15.0);
-
- if (_cmd == @selector(idret::::::::::::::::::::::::::::) ||
- _cmd == @selector(idre2::::::::::::::::::::::::::::) ||
- _cmd == @selector(idre3::::::::::::::::::::::::::::) ||
- _cmd == @selector(llret::::::::::::::::::::::::::::) ||
- _cmd == @selector(llre2::::::::::::::::::::::::::::) ||
- _cmd == @selector(llre3::::::::::::::::::::::::::::) ||
- _cmd == @selector(fpret::::::::::::::::::::::::::::) ||
- _cmd == @selector(fpre2::::::::::::::::::::::::::::) ||
- _cmd == @selector(fpre3::::::::::::::::::::::::::::))
- {
- fail("non-stret selector %s sent to forward_stret_handler", sel_getName(_cmd));
- }
- else if (_cmd == @selector(stret::::::::::::::::::::::::::::) ||
- _cmd == @selector(stre2::::::::::::::::::::::::::::) ||
- _cmd == @selector(stre3::::::::::::::::::::::::::::))
- {
- testassert(state == 17);
- state = 18;
- return STRET_RESULT;
- }
- else {
- fail("unknown selector %s in forward_stret_handler", sel_getName(_cmd));
- }
-
-}
-
-
-@implementation Super
-+(void)initialize { }
-+(id)class { return self; }
-
-#if __OBJC2__
-// forward:: not supported
-#else
--(long long) forward:(SEL)sel :(marg_list)args
-{
- char *p;
- uintptr_t *gp;
- double *fp;
- struct stret *struct_addr;
-
-#if defined(__i386__)
- struct_addr = ((struct stret **)args)[-1];
-#elif defined(__x86_64__)
- struct_addr = *(struct stret **)((char *)args + 8*16+4*8);
-#elif defined(__arm__)
- struct_addr = *(struct stret **)((char *)args + 0);
-#else
-# error unknown architecture
-#endif
-
- testassert(self == receiver);
- testassert(_cmd == sel_registerName("forward::"));
-
- p = (char *)args;
-#if defined(__x86_64__)
- p += 8*16 + 4*8; // skip over xmm and linkage
- if (sel == @selector(stret::::::::::::::::::::::::::::) ||
- sel == @selector(stre2::::::::::::::::::::::::::::) ||
- sel == @selector(stre3::::::::::::::::::::::::::::))
- {
- p += sizeof(void *); // struct return
- }
-#elif defined(__i386__)
- // nothing to do
-#elif defined(__arm__)
- if (sel == @selector(stret::::::::::::::::::::::::::::) ||
- sel == @selector(stre2::::::::::::::::::::::::::::) ||
- sel == @selector(stre3::::::::::::::::::::::::::::))
- {
- p += sizeof(void *); // struct return;
- }
-#else
-# error unknown architecture
-#endif
- gp = (uintptr_t *)p;
- testassert(*gp++ == (uintptr_t)self);
- testassert(*gp++ == (uintptr_t)(void *)sel);
- testassert(*gp++ == 1);
- testassert(*gp++ == 2);
- testassert(*gp++ == 3);
- testassert(*gp++ == 4);
- testassert(*gp++ == 5);
- testassert(*gp++ == 6);
- testassert(*gp++ == 7);
- testassert(*gp++ == 8);
- testassert(*gp++ == 9);
- testassert(*gp++ == 10);
- testassert(*gp++ == 11);
- testassert(*gp++ == 12);
- testassert(*gp++ == 13);
-
-#if defined(__i386__) || defined(__arm__)
-
- fp = (double *)gp;
- testassert(*fp++ == 1.0);
- testassert(*fp++ == 2.0);
- testassert(*fp++ == 3.0);
- testassert(*fp++ == 4.0);
- testassert(*fp++ == 5.0);
- testassert(*fp++ == 6.0);
- testassert(*fp++ == 7.0);
- testassert(*fp++ == 8.0);
- testassert(*fp++ == 9.0);
- testassert(*fp++ == 10.0);
- testassert(*fp++ == 11.0);
- testassert(*fp++ == 12.0);
- testassert(*fp++ == 13.0);
- testassert(*fp++ == 14.0);
- testassert(*fp++ == 15.0);
-
-#elif defined(__x86_64__)
-
- fp = (double *)args; // xmm, double-wide
- testassert(*fp++ == 1.0); fp++;
- testassert(*fp++ == 2.0); fp++;
- testassert(*fp++ == 3.0); fp++;
- testassert(*fp++ == 4.0); fp++;
- testassert(*fp++ == 5.0); fp++;
- testassert(*fp++ == 6.0); fp++;
- testassert(*fp++ == 7.0); fp++;
- testassert(*fp++ == 8.0); fp++;
- fp = (double *)gp;
- testassert(*fp++ == 9.0);
- testassert(*fp++ == 10.0);
- testassert(*fp++ == 11.0);
- testassert(*fp++ == 12.0);
- testassert(*fp++ == 13.0);
- testassert(*fp++ == 14.0);
- testassert(*fp++ == 15.0);
-
-#else
-# error unknown architecture
-#endif
-
- if (sel == @selector(idret::::::::::::::::::::::::::::) ||
- sel == @selector(idre2::::::::::::::::::::::::::::) ||
- sel == @selector(idre3::::::::::::::::::::::::::::))
- {
- union {
- id idval;
- long long llval;
- } result;
- testassert(state == 1);
- state = 2;
- result.idval = ID_RESULT;
- return result.llval;
- } else if (sel == @selector(llret::::::::::::::::::::::::::::) ||
- sel == @selector(llre2::::::::::::::::::::::::::::) ||
- sel == @selector(llre3::::::::::::::::::::::::::::))
- {
- testassert(state == 3);
- state = 4;
- return LL_RESULT;
- } else if (sel == @selector(fpret::::::::::::::::::::::::::::) ||
- sel == @selector(fpre2::::::::::::::::::::::::::::) ||
- sel == @selector(fpre3::::::::::::::::::::::::::::))
- {
- testassert(state == 5);
- state = 6;
-#if defined(__i386__)
- __asm__ volatile("fldl %0" : : "m" (FP_RESULT));
-#elif defined(__x86_64__)
- __asm__ volatile("movsd %0, %%xmm0" : : "m" (FP_RESULT));
-#elif defined(__arm__)
- union {
- double fpval;
- long long llval;
- } result;
- result.fpval = FP_RESULT;
- return result.llval;
-#else
-# error unknown architecture
-#endif
- return 0;
- } else if (sel == @selector(stret::::::::::::::::::::::::::::) ||
- sel == @selector(stre2::::::::::::::::::::::::::::) ||
- sel == @selector(stre3::::::::::::::::::::::::::::))
- {
- testassert(state == 7);
- state = 8;
- *struct_addr = STRET_RESULT;
- return 0;
- } else {
- fail("unknown selector %s in forward::", sel_getName(sel));
- }
- return 0;
-}
-
-#endif
-
-@end
-
-typedef id (*id_fn_t)(id self, SEL _cmd, long i1, long i2, long i3, long i4, long i5, long i6, long i7, long i8, long i9, long i10, long i11, long i12, long i13, double f1, double f2, double f3, double f4, double f5, double f6, double f7, double f8, double f9, double f10, double f11, double f12, double f13, double f14, double f15);
-
-typedef long long (*ll_fn_t)(id self, SEL _cmd, long i1, long i2, long i3, long i4, long i5, long i6, long i7, long i8, long i9, long i10, long i11, long i12, long i13, double f1, double f2, double f3, double f4, double f5, double f6, double f7, double f8, double f9, double f10, double f11, double f12, double f13, double f14, double f15);
-
-typedef double (*fp_fn_t)(id self, SEL _cmd, long i1, long i2, long i3, long i4, long i5, long i6, long i7, long i8, long i9, long i10, long i11, long i12, long i13, double f1, double f2, double f3, double f4, double f5, double f6, double f7, double f8, double f9, double f10, double f11, double f12, double f13, double f14, double f15);
-
-typedef struct stret (*st_fn_t)(id self, SEL _cmd, long i1, long i2, long i3, long i4, long i5, long i6, long i7, long i8, long i9, long i10, long i11, long i12, long i13, double f1, double f2, double f3, double f4, double f5, double f6, double f7, double f8, double f9, double f10, double f11, double f12, double f13, double f14, double f15);
-
-#if __x86_64__
-typedef struct stret * (*fake_st_fn_t)(struct stret *, id self, SEL _cmd, long i1, long i2, long i3, long i4, long i5, long i6, long i7, long i8, long i9, long i10, long i11, long i12, long i13, double f1, double f2, double f3, double f4, double f5, double f6, double f7, double f8, double f9, double f10, double f11, double f12, double f13, double f14, double f15);
-#endif
-
-__BEGIN_DECLS
-extern void *getSP(void);
-__END_DECLS
-
-#if defined(__x86_64__)
- asm(".text \n _getSP: movq %rsp, %rax \n retq \n");
-#elif defined(__i386__)
- asm(".text \n _getSP: movl %esp, %eax \n ret \n");
-#elif defined(__arm__)
- asm(".text \n .thumb \n .thumb_func _getSP \n "
- "_getSP: mov r0, sp \n bx lr \n");
-#elif defined(__arm64__)
- asm(".text \n _getSP: mov x0, sp \n ret \n");
-#else
-# error unknown architecture
-#endif
-
-int main()
-{
- id idval;
- long long llval;
- struct stret stval;
-#if __x86_64__
- struct stret *stptr;
-#endif
- double fpval;
- void *sp1 = (void*)1;
- void *sp2 = (void*)2;
-
- st_fn_t stret_fwd;
-#if __arm64__
- stret_fwd = (st_fn_t)_objc_msgForward;
-#else
- stret_fwd = (st_fn_t)_objc_msgForward_stret;
-#endif
-
- receiver = [Super class];
-
-#if __OBJC2__
- // forward:: not supported
-#else
- // Test default forward handler
-
- state = 1;
- sp1 = getSP();
- idval = [Super idret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 2);
- testassert(idval == ID_RESULT);
-
- state = 3;
- sp1 = getSP();
- llval = [Super llret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 4);
- testassert(llval == LL_RESULT);
-
- state = 5;
- sp1 = getSP();
- fpval = [Super fpret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 6);
- testassert(fpval == FP_RESULT);
-
- state = 7;
- sp1 = getSP();
- stval = [Super stret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 8);
- testassert(stret_equal(stval, STRET_RESULT));
-
-#if __x86_64__
- // check stret return register
- state = 7;
- sp1 = getSP();
- stptr = ((fake_st_fn_t)objc_msgSend_stret)(&stval, [Super class], @selector(stret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 8);
- testassert(stret_equal(stval, STRET_RESULT));
- testassert(stptr == &stval);
-#endif
-
-
- // Test default forward handler, cached
-
- state = 1;
- sp1 = getSP();
- idval = [Super idret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 2);
- testassert(idval == ID_RESULT);
-
- state = 3;
- sp1 = getSP();
- llval = [Super llret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 4);
- testassert(llval == LL_RESULT);
-
- state = 5;
- sp1 = getSP();
- fpval = [Super fpret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 6);
- testassert(fpval == FP_RESULT);
-
- state = 7;
- sp1 = getSP();
- stval = [Super stret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 8);
- testassert(stret_equal(stval, STRET_RESULT));
-
-#if __x86_64__
- // check stret return register
- state = 7;
- sp1 = getSP();
- stptr = ((fake_st_fn_t)objc_msgSend_stret)(&stval, [Super class], @selector(stret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 8);
- testassert(stret_equal(stval, STRET_RESULT));
- testassert(stptr == &stval);
-#endif
-
-
- // Test default forward handler, uncached but fixed-up
-
- _objc_flush_caches(nil);
-
- state = 1;
- sp1 = getSP();
- idval = [Super idret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 2);
- testassert(idval == ID_RESULT);
-
- state = 3;
- sp1 = getSP();
- llval = [Super llret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 4);
- testassert(llval == LL_RESULT);
-
- state = 5;
- sp1 = getSP();
- fpval = [Super fpret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 6);
- testassert(fpval == FP_RESULT);
-
- state = 7;
- sp1 = getSP();
- stval = [Super stret:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 8);
- testassert(stret_equal(stval, STRET_RESULT));
-
-#if __x86_64__
- // check stret return register
- state = 7;
- sp1 = getSP();
- stptr = ((fake_st_fn_t)objc_msgSend_stret)(&stval, [Super class], @selector(stret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 8);
- testassert(stret_equal(stval, STRET_RESULT));
- testassert(stptr == &stval);
-#endif
-
-
- // Test manual forwarding
-
- state = 1;
- sp1 = getSP();
- idval = ((id_fn_t)_objc_msgForward)(receiver, @selector(idre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 2);
- testassert(idval == ID_RESULT);
-
- state = 3;
- sp1 = getSP();
- llval = ((ll_fn_t)_objc_msgForward)(receiver, @selector(llre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 4);
- testassert(llval == LL_RESULT);
-
- state = 5;
- sp1 = getSP();
- fpval = ((fp_fn_t)_objc_msgForward)(receiver, @selector(fpre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 6);
- testassert(fpval == FP_RESULT);
-
- state = 7;
- sp1 = getSP();
- stval = stret_fwd(receiver, @selector(stre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 8);
- testassert(stret_equal(stval, STRET_RESULT));
-
-#if __x86_64__
- // check stret return register
- state = 7;
- sp1 = getSP();
- stptr = ((fake_st_fn_t)_objc_msgForward_stret)(&stval, receiver, @selector(stre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 8);
- testassert(stret_equal(stval, STRET_RESULT));
- testassert(stptr == &stval);
-#endif
-
-
- // Test manual forwarding, cached
-
- state = 1;
- sp1 = getSP();
- idval = ((id_fn_t)_objc_msgForward)(receiver, @selector(idre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 2);
- testassert(idval == ID_RESULT);
-
- state = 3;
- sp1 = getSP();
- llval = ((ll_fn_t)_objc_msgForward)(receiver, @selector(llre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 4);
- testassert(llval == LL_RESULT);
-
- state = 5;
- sp1 = getSP();
- fpval = ((fp_fn_t)_objc_msgForward)(receiver, @selector(fpre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 6);
- testassert(fpval == FP_RESULT);
-
- state = 7;
- sp1 = getSP();
- stval = stret_fwd(receiver, @selector(stre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 8);
- testassert(stret_equal(stval, STRET_RESULT));
-
-#if __x86_64__
- // check stret return register
- state = 7;
- sp1 = getSP();
- stptr = ((fake_st_fn_t)_objc_msgForward_stret)(&stval, receiver, @selector(stre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 8);
- testassert(stret_equal(stval, STRET_RESULT));
- testassert(stptr == &stval);
-#endif
-
-
- // Test manual forwarding, uncached but fixed-up
-
- _objc_flush_caches(nil);
-
- state = 1;
- sp1 = getSP();
- idval = ((id_fn_t)_objc_msgForward)(receiver, @selector(idre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 2);
- testassert(idval == ID_RESULT);
-
- state = 3;
- sp1 = getSP();
- llval = ((ll_fn_t)_objc_msgForward)(receiver, @selector(llre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 4);
- testassert(llval == LL_RESULT);
-
- state = 5;
- sp1 = getSP();
- fpval = ((fp_fn_t)_objc_msgForward)(receiver, @selector(fpre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 6);
- testassert(fpval == FP_RESULT);
-
- state = 7;
- sp1 = getSP();
- stval = stret_fwd(receiver, @selector(stre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 8);
- testassert(stret_equal(stval, STRET_RESULT));
-
-#if __x86_64__
- // check stret return register
- state = 7;
- sp1 = getSP();
- stptr = ((fake_st_fn_t)_objc_msgForward_stret)(&stval, receiver, @selector(stre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 8);
- testassert(stret_equal(stval, STRET_RESULT));
- testassert(stptr == &stval);
-#endif
-
-// !__OBJC2__
-#endif
-
-
- // Test user-defined forward handler
-
- objc_setForwardHandler((void*)&forward_handler, (void*)&forward_stret_handler);
-
- state = 11;
- sp1 = getSP();
- idval = [Super idre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 12);
- testassert(idval == ID_RESULT);
-
- state = 13;
- sp1 = getSP();
- llval = [Super llre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 14);
- testassert(llval == LL_RESULT);
-
- state = 15;
- sp1 = getSP();
- fpval = [Super fpre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 16);
- testassert(fpval == FP_RESULT);
-
- state = 17;
- sp1 = getSP();
- stval = [Super stre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 18);
- testassert(stret_equal(stval, STRET_RESULT));
-
-#if __x86_64__
- // check stret return register
- state = 17;
- sp1 = getSP();
- stptr = ((fake_st_fn_t)objc_msgSend_stret)(&stval, [Super class], @selector(stre3::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 18);
- testassert(stret_equal(stval, STRET_RESULT));
- testassert(stptr == &stval);
-#endif
-
-
- // Test user-defined forward handler, cached
-
- state = 11;
- sp1 = getSP();
- idval = [Super idre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 12);
- testassert(idval == ID_RESULT);
-
- state = 13;
- sp1 = getSP();
- llval = [Super llre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 14);
- testassert(llval == LL_RESULT);
-
- state = 15;
- sp1 = getSP();
- fpval = [Super fpre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 16);
- testassert(fpval == FP_RESULT);
-
- state = 17;
- sp1 = getSP();
- stval = [Super stre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 18);
- testassert(stret_equal(stval, STRET_RESULT));
-
-#if __x86_64__
- // check stret return register
- state = 17;
- sp1 = getSP();
- stptr = ((fake_st_fn_t)objc_msgSend_stret)(&stval, [Super class], @selector(stre3::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 18);
- testassert(stret_equal(stval, STRET_RESULT));
- testassert(stptr == &stval);
-#endif
-
-
- // Test user-defined forward handler, uncached but fixed-up
-
- _objc_flush_caches(nil);
-
- state = 11;
- sp1 = getSP();
- idval = [Super idre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 12);
- testassert(idval == ID_RESULT);
-
- state = 13;
- sp1 = getSP();
- llval = [Super llre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 14);
- testassert(llval == LL_RESULT);
-
- state = 15;
- sp1 = getSP();
- fpval = [Super fpre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 16);
- testassert(fpval == FP_RESULT);
-
- state = 17;
- sp1 = getSP();
- stval = [Super stre3:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 18);
- testassert(stret_equal(stval, STRET_RESULT));
-
-#if __x86_64__
- // check stret return register
- state = 17;
- sp1 = getSP();
- stptr = ((fake_st_fn_t)objc_msgSend_stret)(&stval, [Super class], @selector(stre3::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 18);
- testassert(stret_equal(stval, STRET_RESULT));
- testassert(stptr == &stval);
-#endif
-
-
-
- // Test user-defined forward handler, manual forwarding
-
- state = 11;
- sp1 = getSP();
- idval = ((id_fn_t)_objc_msgForward)(receiver, @selector(idre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 12);
- testassert(idval == ID_RESULT);
-
- state = 13;
- sp1 = getSP();
- llval = ((ll_fn_t)_objc_msgForward)(receiver, @selector(llre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 14);
- testassert(llval == LL_RESULT);
-
- state = 15;
- sp1 = getSP();
- fpval = ((fp_fn_t)_objc_msgForward)(receiver, @selector(fpre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 16);
- testassert(fpval == FP_RESULT);
-
- state = 17;
- sp1 = getSP();
- stval = stret_fwd(receiver, @selector(stre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 18);
- testassert(stret_equal(stval, STRET_RESULT));
-
-
- // Test user-defined forward handler, manual forwarding, cached
-
- state = 11;
- sp1 = getSP();
- idval = ((id_fn_t)_objc_msgForward)(receiver, @selector(idre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 12);
- testassert(idval == ID_RESULT);
-
- state = 13;
- sp1 = getSP();
- llval = ((ll_fn_t)_objc_msgForward)(receiver, @selector(llre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 14);
- testassert(llval == LL_RESULT);
-
- state = 15;
- sp1 = getSP();
- fpval = ((fp_fn_t)_objc_msgForward)(receiver, @selector(fpre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 16);
- testassert(fpval == FP_RESULT);
-
- state = 17;
- sp1 = getSP();
- stval = stret_fwd(receiver, @selector(stre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 18);
- testassert(stret_equal(stval, STRET_RESULT));
-
-
- // Test user-defined forward handler, manual forwarding, uncached but fixed-up
-
- _objc_flush_caches(nil);
-
- state = 11;
- sp1 = getSP();
- idval = ((id_fn_t)_objc_msgForward)(receiver, @selector(idre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 12);
- testassert(idval == ID_RESULT);
-
- state = 13;
- sp1 = getSP();
- llval = ((ll_fn_t)_objc_msgForward)(receiver, @selector(llre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 14);
- testassert(llval == LL_RESULT);
-
- state = 15;
- sp1 = getSP();
- fpval = ((fp_fn_t)_objc_msgForward)(receiver, @selector(fpre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 16);
- testassert(fpval == FP_RESULT);
-
- state = 17;
- sp1 = getSP();
- stval = stret_fwd(receiver, @selector(stre2::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- sp2 = getSP();
- testassert(sp1 == sp2);
- testassert(state == 18);
- testassert(stret_equal(stval, STRET_RESULT));
-
-
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-/*
-no arc, rdar://11368528 confused by Foundation
-TEST_CONFIG MEM=mrc,gc
-TEST_CRASHES
-TEST_RUN_OUTPUT
-objc\[\d+\]: \+\[NSObject fakeorama\]: unrecognized selector sent to instance 0x[0-9a-fA-F]+ \(no message forward handler is installed\)
-CRASHED: SIG(ILL|TRAP)
-OR
-not OBJC2
-objc\[\d+\]: NSObject: Does not recognize selector forward:: \(while forwarding fakeorama\)
-CRASHED: SIG(ILL|TRAP)
-END
-*/
-
-#include "test.h"
-
-#include <objc/NSObject.h>
-
-@interface NSObject (Fake)
--(void)fakeorama;
-@end
-
-int main()
-{
-#if !__OBJC2__
- fprintf(stderr, "not OBJC2\n");
-#endif
- [NSObject fakeorama];
- fail("should have crashed");
-}
-
+++ /dev/null
-/*
-no arc, rdar://11368528 confused by Foundation
-TEST_CONFIG MEM=mrc,gc
-TEST_CRASHES
-TEST_RUN_OUTPUT
-objc\[\d+\]: \+\[NSObject fakeorama\]: unrecognized selector sent to instance 0x[0-9a-fA-F]+ \(no message forward handler is installed\)
-CRASHED: SIG(ILL|TRAP)
-OR
-not OBJC2
-objc\[\d+\]: NSObject: Does not recognize selector forward:: \(while forwarding fakeorama\)
-CRASHED: SIG(ILL|TRAP)
-END
-*/
-
-#include "test.h"
-
-#include <objc/NSObject.h>
-
-@interface NSObject (Fake)
--(struct stret)fakeorama;
-@end
-
-int main()
-{
-#if !__OBJC2__
- fprintf(stderr, "not OBJC2\n");
-#endif
- [NSObject fakeorama];
- fail("should have crashed");
-}
-
+++ /dev/null
-#include "test.h"
-
-@interface Sub1 : TestRoot
-+(int)method;
-+(Class)classref;
-@end
-
-@interface Sub2 : TestRoot
-+(int)method;
-+(Class)classref;
-@end
-
-@interface SubSub1 : Sub1 @end
-
-@interface SubSub2 : Sub2 @end
+++ /dev/null
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/future0.m -o future0.dylib -dynamiclib
- $C{COMPILE} $DIR/future2.m -x none future0.dylib -o future2.dylib -dynamiclib
- $C{COMPILE} $DIR/future.m -x none future0.dylib -o future.out
-END
-*/
-
-#include "test.h"
-
-#if __has_feature(objc_arc)
-
-int main()
-{
- testwarn("rdar://10041403 future class API is not ARC-compatible");
- succeed(__FILE__);
-}
-
-
-#else
-
-#include <objc/runtime.h>
-#include <malloc/malloc.h>
-#include <string.h>
-#include <dlfcn.h>
-#include "future.h"
-
-@implementation Sub2
-+(int)method {
- return 2;
-}
-+(Class)classref {
- return [Sub2 class];
-}
-@end
-
-@implementation SubSub2
-+(int)method {
- return 1 + [super method];
-}
-@end
-
-int main()
-{
- Class oldTestRoot;
- Class oldSub1;
- Class newSub1;
-
- // objc_getFutureClass with existing class
- oldTestRoot = objc_getFutureClass("TestRoot");
- testassert(oldTestRoot == [TestRoot class]);
- testassert(! _class_isFutureClass(oldTestRoot));
-
- // objc_getFutureClass with missing class
- oldSub1 = objc_getFutureClass("Sub1");
- testassert(oldSub1);
- testassert(malloc_size(objc_unretainedPointer(oldSub1)) > 0);
- testassert(objc_getClass("Sub1") == Nil);
- testassert(_class_isFutureClass(oldSub1));
- testassert(0 == strcmp(class_getName(oldSub1), "Sub1"));
- testassert(object_getClass(oldSub1) == Nil); // CF expects this
-
- // objc_getFutureClass a second time
- testassert(oldSub1 == objc_getFutureClass("Sub1"));
-
- // Load class Sub1
- dlopen("future2.dylib", 0);
-
- // Verify use of future class
- newSub1 = objc_getClass("Sub1");
- testassert(oldSub1 == newSub1);
- testassert(newSub1 == [newSub1 classref]);
- testassert(newSub1 == class_getSuperclass(objc_getClass("SubSub1")));
- testassert(! _class_isFutureClass(newSub1));
-
- testassert(1 == [oldSub1 method]);
- testassert(1 == [newSub1 method]);
-
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-#include "future.h"
-#include "testroot.i"
+++ /dev/null
-#include "future.h"
-
-
-@implementation Sub1
-+(Class)classref {
- return [Sub1 class];
-}
-+(int)method {
- return 1;
-}
-@end
-
-@implementation SubSub1
-+(int)method {
- return 1 + [super method];
-}
-@end
+++ /dev/null
-#include "test.h"
-
-OBJC_ROOT_CLASS
-@interface Main @end
-@implementation Main @end
-
-int main(int argc __attribute__((unused)), char **argv)
-{
- succeed(basename(argv[0]));
-}
+++ /dev/null
-int GC(void) { return 42; }
+++ /dev/null
-#import <objc/objc-api.h>
-
-OBJC_ROOT_CLASS
-@interface GC @end
-@implementation GC @end
-
-// silence "no debug symbols in executable" warning
-void foo(void) { }
+++ /dev/null
-// gc-off app loading gc-off dylib: should work
-
-/*
-TEST_CONFIG MEM=mrc,arc OS=macosx
-
-TEST_BUILD
- $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libnogc.dylib
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
-
- $C{COMPILE} $DIR/gc-main.m -x none libnogc.dylib -o gcenforcer-nogc-1.out
-END
-*/
+++ /dev/null
-// gc-on app loading gc-off dylib: should crash
-
-/*
-TEST_CONFIG MEM=gc OS=macosx
-TEST_CRASHES
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: '.*libnogc.dylib' was not compiled with -fobjc-gc or -fobjc-gc-only, but the application requires GC
-objc\[\d+\]: \*\*\* GC capability of application and some libraries did not match
-CRASHED: SIGILL
-END
-
-TEST_BUILD
- $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libnogc.dylib
- $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
- $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
- $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
-
- $C{COMPILE} $DIR/gc-main.m -x none libnogc.dylib -o gcenforcer-nogc-2.out
-END
-*/
+++ /dev/null
-/*
-TEST_CONFIG OS=macosx
-
-TEST_BUILD
- $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libnogc.dylib
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
-
- $C{COMPILE} $DIR/gc-main.m -x none libnoobjc.dylib -o gcenforcer-noobjc.out
-END
-*/
+++ /dev/null
-// gc-off app loading gc-required dylib: should crash
-// linker sees librequiresgc.fake.dylib, runtime uses librequiresgc.dylib
-
-/*
-TEST_CONFIG MEM=mrc,arc OS=macosx
-TEST_CRASHES
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: '.*librequiresgc.dylib' was compiled with -fobjc-gc-only, but the application does not support GC
-objc\[\d+\]: \*\*\* GC capability of application and some libraries did not match
-CRASHED: SIGILL
-END
-
-TEST_BUILD
- $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libnogc.dylib
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
-
- $C{COMPILE} $DIR/gc-main.m -x none librequiresgc.fake.dylib -o gcenforcer-requiresgc-1.out
-END
-*/
+++ /dev/null
-// gc-off app loading gc-required dylib: should crash
-// linker sees librequiresgc.fake.dylib, runtime uses librequiresgc.dylib
-
-/*
-TEST_CONFIG MEM=gc OS=macosx
-
-TEST_BUILD
- $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libnogc.dylib
- $C{COMPILE} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
- $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
- $C{COMPILE} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
-
- $C{COMPILE} $DIR/gc-main.m -x none librequiresgc.fake.dylib -o gcenforcer-requiresgc-2.out
-END
-*/
+++ /dev/null
-/*
-TEST_CONFIG OS=macosx
-
-TEST_BUILD
- $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libnogc.dylib
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
-
- $C{COMPILE} $DIR/gc-main.m -x none libsupportsgc.dylib -o gcenforcer-supportsgc.out
-END
-*/
+++ /dev/null
-/*
-TEST_CONFIG OS=macosx
-
-TEST_BUILD
- $C{COMPILE_C} $DIR/gc.c -dynamiclib -o libnoobjc.dylib
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libnogc.dylib
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o libsupportsgc.dylib -fobjc-gc
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.dylib -fobjc-gc-only
- $C{COMPILE_NOMEM} $DIR/gc.m -dynamiclib -o librequiresgc.fake.dylib -fobjc-gc -install_name librequiresgc.dylib
-
- $C{COMPILE} $DIR/gcenforcer.m -o gcenforcer.out
-END
-*/
-
-#include "test.h"
-#include <objc/objc-auto.h>
-#include <dlfcn.h>
-
-int main()
-{
- int i;
- for (i = 0; i < 1000; i++) {
- testassert(dlopen_preflight("libsupportsgc.dylib"));
- testassert(dlopen_preflight("libnoobjc.dylib"));
-
- if (objc_collectingEnabled()) {
- testassert(dlopen_preflight("librequiresgc.dylib"));
- testassert(! dlopen_preflight("libnogc.dylib"));
- } else {
- testassert(! dlopen_preflight("librequiresgc.dylib"));
- testassert(dlopen_preflight("libnogc.dylib"));
- }
- }
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -Wno-deprecated-declarations
-
-#include "test.h"
-
-#if TARGET_OS_IPHONE
-
-int main()
-{
- succeed(__FILE__);
-}
-
-#else
-
-#include "testroot.i"
-#include <objc/objc-gdb.h>
-#include <objc/runtime.h>
-
-int main()
-{
- // Class hashes
-#if __OBJC2__
-
- Class result;
-
- // Class should not be realized yet
- // fixme not true during class hash rearrangement
- // result = NXMapGet(gdb_objc_realized_classes, "TestRoot");
- // testassert(!result);
-
- [TestRoot class];
- // Now class should be realized
-
- result = (Class)objc_unretainedObject(NXMapGet(gdb_objc_realized_classes, "TestRoot"));
- testassert(result);
- testassert(result == [TestRoot class]);
-
- result = (Class)objc_unretainedObject(NXMapGet(gdb_objc_realized_classes, "DoesNotExist"));
- testassert(!result);
-
-#else
-
- struct objc_class query;
- Class result;
-
- query.name = "TestRoot";
- result = (Class)NXHashGet(_objc_debug_class_hash, &query);
- testassert(result);
- testassert((id)result == [TestRoot class]);
-
- query.name = "DoesNotExist";
- result = (Class)NXHashGet(_objc_debug_class_hash, &query);
- testassert(!result);
-
-#endif
-
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include "testroot.i"
-#include <objc/runtime.h>
-#include <objc/message.h>
-
-static int state = 0;
-
-@interface Super : TestRoot @end
-@implementation Super
-+(void)classMethod { state = 1; }
--(void)instanceMethod { state = 4; }
-+(void)classMethodSuperOnly { state = 3; }
--(void)instanceMethodSuperOnly { state = 6; }
-@end
-
-@interface Sub : Super @end
-@implementation Sub
-+(void)classMethod { state = 2; }
--(void)instanceMethod { state = 5; }
-@end
-
-typedef void (*imp_t)(id, SEL);
-
-int main()
-{
- Class Super_cls, Sub_cls;
- Class buf[10];
- Method m;
- SEL sel;
- IMP imp;
-
- // don't use [Super class] to check laziness handing
- Super_cls = objc_getClass("Super");
- Sub_cls = objc_getClass("Sub");
-
- sel = sel_registerName("classMethod");
- m = class_getClassMethod(Super_cls, sel);
- testassert(m);
- testassert(sel == method_getName(m));
- imp = method_getImplementation(m);
- testassert(imp == class_getMethodImplementation(object_getClass(Super_cls), sel));
- testassert(imp == object_getMethodImplementation(Super_cls, sel));
- state = 0;
- (*(imp_t)imp)(Super_cls, sel);
- testassert(state == 1);
-
- sel = sel_registerName("classMethod");
- m = class_getClassMethod(Sub_cls, sel);
- testassert(m);
- testassert(sel == method_getName(m));
- imp = method_getImplementation(m);
- testassert(imp == class_getMethodImplementation(object_getClass(Sub_cls), sel));
- testassert(imp == object_getMethodImplementation(Sub_cls, sel));
- state = 0;
- (*(imp_t)imp)(Sub_cls, sel);
- testassert(state == 2);
-
- sel = sel_registerName("classMethodSuperOnly");
- m = class_getClassMethod(Sub_cls, sel);
- testassert(m);
- testassert(sel == method_getName(m));
- imp = method_getImplementation(m);
- testassert(imp == class_getMethodImplementation(object_getClass(Sub_cls), sel));
- testassert(imp == object_getMethodImplementation(Sub_cls, sel));
- state = 0;
- (*(imp_t)imp)(Sub_cls, sel);
- testassert(state == 3);
-
- sel = sel_registerName("instanceMethod");
- m = class_getInstanceMethod(Super_cls, sel);
- testassert(m);
- testassert(sel == method_getName(m));
- imp = method_getImplementation(m);
- testassert(imp == class_getMethodImplementation(Super_cls, sel));
- buf[0] = Super_cls;
- testassert(imp == object_getMethodImplementation(objc_unretainedObject(buf), sel));
- state = 0;
- (*(imp_t)imp)(objc_unretainedObject(buf), sel);
- testassert(state == 4);
-
- sel = sel_registerName("instanceMethod");
- m = class_getInstanceMethod(Sub_cls, sel);
- testassert(m);
- testassert(sel == method_getName(m));
- imp = method_getImplementation(m);
- testassert(imp == class_getMethodImplementation(Sub_cls, sel));
- buf[0] = Sub_cls;
- testassert(imp == object_getMethodImplementation(objc_unretainedObject(buf), sel));
- state = 0;
- (*(imp_t)imp)(objc_unretainedObject(buf), sel);
- testassert(state == 5);
-
- sel = sel_registerName("instanceMethodSuperOnly");
- m = class_getInstanceMethod(Sub_cls, sel);
- testassert(m);
- testassert(sel == method_getName(m));
- imp = method_getImplementation(m);
- testassert(imp == class_getMethodImplementation(Sub_cls, sel));
- buf[0] = Sub_cls;
- testassert(imp == object_getMethodImplementation(objc_unretainedObject(buf), sel));
- state = 0;
- (*(imp_t)imp)(objc_unretainedObject(buf), sel);
- testassert(state == 6);
-
- // check class_getClassMethod(cls) == class_getInstanceMethod(cls->isa)
- sel = sel_registerName("classMethod");
- testassert(class_getClassMethod(Sub_cls, sel) == class_getInstanceMethod(object_getClass(Sub_cls), sel));
-
- sel = sel_registerName("nonexistent");
- testassert(! class_getInstanceMethod(Sub_cls, sel));
- testassert(! class_getClassMethod(Sub_cls, sel));
- testassert(class_getMethodImplementation(Sub_cls, sel) == (IMP)&_objc_msgForward);
- buf[0] = Sub_cls;
- testassert(object_getMethodImplementation(objc_unretainedObject(buf), sel) == (IMP)&_objc_msgForward);
-#if !__arm64__
- testassert(class_getMethodImplementation_stret(Sub_cls, sel) == (IMP)&_objc_msgForward_stret);
- testassert(object_getMethodImplementation_stret(objc_unretainedObject(buf), sel) == (IMP)&_objc_msgForward_stret);
-#endif
-
- testassert(! class_getInstanceMethod(NULL, NULL));
- testassert(! class_getInstanceMethod(NULL, sel));
- testassert(! class_getInstanceMethod(Sub_cls, NULL));
- testassert(! class_getClassMethod(NULL, NULL));
- testassert(! class_getClassMethod(NULL, sel));
- testassert(! class_getClassMethod(Sub_cls, NULL));
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG MEM=mrc,gc
-// TEST_CFLAGS -Wno-deprecated-declarations
-
-#include "test.h"
-#include <objc/runtime.h>
-#include <objc/message.h>
-#include <objc/objc-auto.h>
-
-static int state = 0;
-
-OBJC_ROOT_CLASS
-@interface Super { id isa; } @end
-@implementation Super
-+(id)class { return self; }
-+(void)initialize { }
-
-+(id)ordinary { state = 1; return self; }
-+(id)ordinary2 { testassert(0); }
-+(id)retain { state = 2; return self; }
-+(void)release { state = 3; }
-+(id)autorelease { state = 4; return self; }
-+(void)dealloc { state = 5; }
-+(uintptr_t)retainCount { state = 6; return 6; }
-@end
-
-@interface Sub : Super @end
-@implementation Sub @end
-
-@interface Sub2 : Super @end
-@implementation Sub2 @end
-
-OBJC_ROOT_CLASS
-@interface Empty { id isa; } @end
-@implementation Empty
-+(id)class { return self; }
-+(void)initialize { }
-@end
-
-void *forward_handler(id obj, SEL _cmd) {
- testassert(obj == [Empty class]);
- testassert(_cmd == @selector(ordinary));
- state = 1;
- return nil;
-}
-
-@interface Empty (Unimplemented)
-+(id)ordinary;
-+(id)retain;
-+(void)release;
-+(id)autorelease;
-+(void)dealloc;
-+(uintptr_t)retainCount;
-@end
-
-
-#define getImp(sel) \
- do { \
- sel##Method = class_getClassMethod(cls, @selector(sel)); \
- testassert(sel##Method); \
- testassert(@selector(sel) == method_getName(sel##Method)); \
- sel = method_getImplementation(sel##Method); \
- } while (0)
-
-
-static IMP ordinary, ordinary2, retain, release, autorelease, dealloc, retainCount;
-static Method ordinaryMethod, ordinary2Method, retainMethod, releaseMethod, autoreleaseMethod, deallocMethod, retainCountMethod;
-
-void cycle(Class cls)
-{
- id idVal;
- uintptr_t intVal;
-
-#if defined(__i386__)
- if (objc_collectingEnabled()) {
- // i386 GC: all ignored selectors are identical
- testassert(@selector(retain) == @selector(release) &&
- @selector(retain) == @selector(autorelease) &&
- @selector(retain) == @selector(dealloc) &&
- @selector(retain) == @selector(retainCount) );
- }
- else
-#endif
- {
- // x86_64 GC or no GC: all ignored selectors are distinct
- testassert(@selector(retain) != @selector(release) &&
- @selector(retain) != @selector(autorelease) &&
- @selector(retain) != @selector(dealloc) &&
- @selector(retain) != @selector(retainCount) );
- }
-
- // no ignored selector matches a real selector
- testassert(@selector(ordinary) != @selector(retain) &&
- @selector(ordinary) != @selector(release) &&
- @selector(ordinary) != @selector(autorelease) &&
- @selector(ordinary) != @selector(dealloc) &&
- @selector(ordinary) != @selector(retainCount) );
-
- getImp(ordinary);
- getImp(ordinary2);
- getImp(retain);
- getImp(release);
- getImp(autorelease);
- getImp(dealloc);
- getImp(retainCount);
-
- if (objc_collectingEnabled()) {
- // GC: all ignored selector IMPs are identical
- testassert(retain == release &&
- retain == autorelease &&
- retain == dealloc &&
- retain == retainCount );
- }
- else {
- // no GC: all ignored selector IMPs are distinct
- testassert(retain != release &&
- retain != autorelease &&
- retain != dealloc &&
- retain != retainCount );
- }
-
- // no ignored selector IMP matches a real selector IMP
- testassert(ordinary != retain &&
- ordinary != release &&
- ordinary != autorelease &&
- ordinary != dealloc &&
- ordinary != retainCount );
-
- // Test calls via method_invoke
-
- idVal = ((id(*)(id, Method))method_invoke)(cls, ordinaryMethod);
- testassert(state == 1);
- testassert(idVal == cls);
-
- state = 0;
- idVal = ((id(*)(id, Method))method_invoke)(cls, retainMethod);
- testassert(state == (objc_collectingEnabled() ? 0 : 2));
- testassert(idVal == cls);
-
- (void) ((void(*)(id, Method))method_invoke)(cls, releaseMethod);
- testassert(state == (objc_collectingEnabled() ? 0 : 3));
-
- idVal = ((id(*)(id, Method))method_invoke)(cls, autoreleaseMethod);
- testassert(state == (objc_collectingEnabled() ? 0 : 4));
- testassert(idVal == cls);
-
- (void) ((void(*)(id, Method))method_invoke)(cls, deallocMethod);
- testassert(state == (objc_collectingEnabled() ? 0 : 5));
-
- intVal = ((uintptr_t(*)(id, Method))method_invoke)(cls, retainCountMethod);
- testassert(state == (objc_collectingEnabled() ? 0 : 6));
- testassert(intVal == (objc_collectingEnabled() ? (uintptr_t)cls : 6));
-
-
- // Test calls via compiled objc_msgSend
-
- state = 0;
- idVal = [cls ordinary];
- testassert(state == 1);
- testassert(idVal == cls);
-
- state = 0;
- idVal = [cls retain];
- testassert(state == (objc_collectingEnabled() ? 0 : 2));
- testassert(idVal == cls);
-
- (void) [cls release];
- testassert(state == (objc_collectingEnabled() ? 0 : 3));
-
- idVal = [cls autorelease];
- testassert(state == (objc_collectingEnabled() ? 0 : 4));
- testassert(idVal == cls);
-
- (void) [cls dealloc];
- testassert(state == (objc_collectingEnabled() ? 0 : 5));
-
- intVal = [cls retainCount];
- testassert(state == (objc_collectingEnabled() ? 0 : 6));
- testassert(intVal == (objc_collectingEnabled() ? (uintptr_t)cls : 6));
-
- // Test calls via handwritten objc_msgSend
-
- state = 0;
- idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(ordinary));
- testassert(state == 1);
- testassert(idVal == cls);
-
- state = 0;
- idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(retain));
- testassert(state == (objc_collectingEnabled() ? 0 : 2));
- testassert(idVal == cls);
-
- (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(release));
- testassert(state == (objc_collectingEnabled() ? 0 : 3));
-
- idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(autorelease));
- testassert(state == (objc_collectingEnabled() ? 0 : 4));
- testassert(idVal == cls);
-
- (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(dealloc));
- testassert(state == (objc_collectingEnabled() ? 0 : 5));
-
- intVal = ((uintptr_t(*)(id,SEL))objc_msgSend)(cls, @selector(retainCount));
- testassert(state == (objc_collectingEnabled() ? 0 : 6));
- testassert(intVal == (objc_collectingEnabled() ? (uintptr_t)cls : 6));
-}
-
-int main()
-{
- Class cls;
-
- objc_setForwardHandler((void*)&forward_handler, nil);
-
- // Test selector API
-
- testassert(sel_registerName("retain") == @selector(retain));
- testassert(sel_getUid("retain") == @selector(retain));
-#if defined(__i386__)
- if (objc_collectingEnabled()) {
- // only i386's GC currently remaps these
- testassert(0 == strcmp(sel_getName(@selector(retain)), "<ignored selector>"));
- } else
-#endif
- {
- testassert(0 == strcmp(sel_getName(@selector(retain)), "retain"));
- }
-#if !__OBJC2__
- testassert(sel_isMapped(@selector(retain)));
-#endif
-
- cls = [Sub class];
- testassert(cls);
- cycle(cls);
-
- cls = [Super class];
- testassert(cls);
- cycle(cls);
-
- if (objc_collectingEnabled()) {
- // rdar://6200570 Method manipulation shouldn't affect ignored methods.
-
- cls = [Super class];
- testassert(cls);
- cycle(cls);
-
- method_setImplementation(retainMethod, (IMP)1);
- method_setImplementation(releaseMethod, (IMP)1);
- method_setImplementation(autoreleaseMethod, (IMP)1);
- method_setImplementation(deallocMethod, (IMP)1);
- method_setImplementation(retainCountMethod, (IMP)1);
- cycle(cls);
-
- testassert(ordinary2 != retainCount);
- method_exchangeImplementations(retainMethod, autoreleaseMethod);
- method_exchangeImplementations(deallocMethod, releaseMethod);
- method_exchangeImplementations(retainCountMethod, ordinary2Method);
- cycle(cls);
- // ordinary2 exchanged with ignored method is now ignored too
- testassert(ordinary2 == retainCount);
-
- // replace == replace existing
- class_replaceMethod(cls, @selector(retain), (IMP)1, "");
- class_replaceMethod(cls, @selector(release), (IMP)1, "");
- class_replaceMethod(cls, @selector(autorelease), (IMP)1, "");
- class_replaceMethod(cls, @selector(dealloc), (IMP)1, "");
- class_replaceMethod(cls, @selector(retainCount), (IMP)1, "");
- cycle(cls);
-
- cls = [Sub class];
- testassert(cls);
- cycle(cls);
-
- // replace == add override
- class_replaceMethod(cls, @selector(retain), (IMP)1, "");
- class_replaceMethod(cls, @selector(release), (IMP)1, "");
- class_replaceMethod(cls, @selector(autorelease), (IMP)1, "");
- class_replaceMethod(cls, @selector(dealloc), (IMP)1, "");
- class_replaceMethod(cls, @selector(retainCount), (IMP)1, "");
- cycle(cls);
-
- cls = [Sub2 class];
- testassert(cls);
- cycle(cls);
-
- class_addMethod(cls, @selector(retain), (IMP)1, "");
- class_addMethod(cls, @selector(release), (IMP)1, "");
- class_addMethod(cls, @selector(autorelease), (IMP)1, "");
- class_addMethod(cls, @selector(dealloc), (IMP)1, "");
- class_addMethod(cls, @selector(retainCount), (IMP)1, "");
- cycle(cls);
- }
-
- // Test calls via objc_msgSend - ignored selectors are ignored
- // under GC even if the class provides no implementation for them
- if (objc_collectingEnabled()) {
- Class cls;
- id idVal;
- uintptr_t intVal;
-
- cls = [Empty class];
- state = 0;
-
- idVal = [Empty retain];
- testassert(state == 0);
- testassert(idVal == cls);
-
- (void) [Empty release];
- testassert(state == 0);
-
- idVal = [Empty autorelease];
- testassert(state == 0);
- testassert(idVal == cls);
-
- (void) [Empty dealloc];
- testassert(state == 0);
-
- intVal = [Empty retainCount];
- testassert(state == 0);
- testassert(intVal == (uintptr_t)cls);
-
- idVal = [Empty ordinary];
- testassert(state == 1);
- testassert(idVal == nil);
-
- state = 0;
-
- idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(retain));
- testassert(state == 0);
- testassert(idVal == cls);
-
- (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(release));
- testassert(state == 0);
-
- idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(autorelease));
- testassert(state == 0);
- testassert(idVal == cls);
-
- (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(dealloc));
- testassert(state == 0);
-
- intVal = ((uintptr_t(*)(id,SEL))objc_msgSend)(cls, @selector(retainCount));
- testassert(state == 0);
- testassert(intVal == (uintptr_t)cls);
-
- idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(ordinary));
- testassert(state == 1);
- testassert(idVal == nil);
- }
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG MEM=gc
-// TEST_CFLAGS -framework Foundation
-
-// This test must use CF and test ignoredSelector must not use CF.
-
-#include "test.h"
-#include <objc/NSObject.h>
-
-int main()
-{
- if (objc_collectingEnabled()) {
- // ARC RR functions don't retain and don't hit the side table.
- __block int count;
- testblock_t testblock = ^{
- for (int i = 0; i < count; i++) {
- id obj = [NSObject new];
- objc_retain(obj);
- objc_retain(obj);
- objc_release(obj);
- }
- };
- count = 100;
- testonthread(testblock);
- testonthread(testblock);
- leak_mark();
- count = 10000000;
- testonthread(testblock);
-#if __OBJC_GC__
- testwarn("rdar://19042235 possible leaks suppressed under GC");
- leak_check(2000);
-#else
- leak_check(0);
-#endif
- }
-
- succeed(__FILE__);
-}
+++ /dev/null
-extern int state;
-extern int cstate;
-
-OBJC_ROOT_CLASS
-@interface Super { id isa; }
-+(void) method;
-+(void) method0;
-@end
-
-@interface Super (cat1)
-+(void) method1;
-@end
-
-@interface Super (cat2)
-+(void) method2;
-@end
-
-@interface Super (cat3)
-+(void) method3;
-@end
+++ /dev/null
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/imageorder1.m -o imageorder1.dylib -dynamiclib
- $C{COMPILE} $DIR/imageorder2.m -x none imageorder1.dylib -o imageorder2.dylib -dynamiclib
- $C{COMPILE} $DIR/imageorder3.m -x none imageorder2.dylib imageorder1.dylib -o imageorder3.dylib -dynamiclib
- $C{COMPILE} $DIR/imageorder.m -x none imageorder3.dylib imageorder2.dylib imageorder1.dylib -o imageorder.out
-END
-*/
-
-#include "test.h"
-#include "imageorder.h"
-#include <objc/runtime.h>
-#include <dlfcn.h>
-
-int main()
-{
- // +load methods and C static initializers
- testassert(state == 3);
- testassert(cstate == 3);
-
- Class cls = objc_getClass("Super");
- testassert(cls);
-
- // make sure all categories arrived
- state = -1;
- [Super method0];
- testassert(state == 0);
- [Super method1];
- testassert(state == 1);
- [Super method2];
- testassert(state == 2);
- [Super method3];
- testassert(state == 3);
-
- // make sure imageorder3.dylib is the last category to attach
- state = 0;
- [Super method];
- testassert(state == 3);
-
- succeed(__FILE__);
-}
+++ /dev/null
-#include "test.h"
-#include "imageorder.h"
-
-int state = -1;
-int cstate = 0;
-
-static void c1(void) __attribute__((constructor));
-static void c1(void)
-{
- testassert(state == 1); // +load before C/C++
- testassert(cstate == 0);
- cstate = 1;
-}
-
-
-#if __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
-#endif
-
-@implementation Super (cat1)
-+(void) method {
- fail("+[Super(cat1) method] not replaced!");
-}
-+(void) method1 {
- state = 1;
-}
-+(void) load {
- testassert(state == 0);
- state = 1;
-}
-@end
-
-#if __clang__
-#pragma clang diagnostic pop
-#endif
-
-
-@implementation Super
-+(void) initialize { }
-+(void) method {
- fail("+[Super method] not replaced!");
-}
-+(void) method0 {
- state = 0;
-}
-+(void) load {
- testassert(state == -1);
- state = 0;
-}
-@end
-
+++ /dev/null
-#include "test.h"
-#include "imageorder.h"
-
-static void c2(void) __attribute__((constructor));
-static void c2(void)
-{
- testassert(state == 2); // +load before C/C++
- testassert(cstate == 1);
- cstate = 2;
-}
-
-
-#if __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
-#endif
-
-@implementation Super (cat2)
-+(void) method {
- fail("+[Super(cat2) method] not replaced!");
-}
-+(void) method2 {
- state = 2;
-}
-+(void) load {
- testassert(state == 1);
- state = 2;
-}
-@end
-
-#if __clang__
-#pragma clang diagnostic pop
-#endif
+++ /dev/null
-#include "test.h"
-#include "imageorder.h"
-
-static void c3(void) __attribute__((constructor));
-static void c3(void)
-{
- testassert(state == 3); // +load before C/C++
- testassert(cstate == 2);
- cstate = 3;
-}
-
-
-#if __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
-#endif
-
-@implementation Super (cat3)
-+(void) method {
- state = 3;
-}
-+(void) method3 {
- state = 3;
-}
-+(void) load {
- testassert(state == 2);
- state = 3;
-}
-@end
-
-#if __clang__
-#pragma clang diagnostic pop
-#endif
+++ /dev/null
-// TEST_CONFIG
-
-// Verify that all headers can be included in any language.
-
-#include <objc/objc.h>
-
-#include <objc/List.h>
-#include <objc/NSObjCRuntime.h>
-#include <objc/NSObject.h>
-#include <objc/Object.h>
-#include <objc/Protocol.h>
-#include <objc/message.h>
-#include <objc/objc-api.h>
-#include <objc/objc-auto.h>
-#include <objc/objc-class.h>
-#include <objc/objc-exception.h>
-#include <objc/objc-load.h>
-#include <objc/objc-runtime.h>
-#include <objc/objc-sync.h>
-#include <objc/runtime.h>
-
-#include <objc/objc-abi.h>
-#include <objc/objc-auto-dump.h>
-#include <objc/objc-gdb.h>
-#include <objc/objc-internal.h>
-
-#if !TARGET_OS_IPHONE
-#include <objc/hashtable.h>
-#include <objc/hashtable2.h>
-#include <objc/maptable.h>
-#endif
-
-#include "test.h"
-
-int main()
-{
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-
-// initialize.m
-// Test basic +initialize behavior
-// * +initialize before class method
-// * superclass +initialize before subclass +initialize
-// * subclass inheritance of superclass implementation
-// * messaging during +initialize
-// * +initialize provoked by class_getMethodImplementation
-// * +initialize not provoked by objc_getClass
-#include "test.h"
-#include "testroot.i"
-
-int state = 0;
-
-@interface Super0 : TestRoot @end
-@implementation Super0
-+(void)initialize {
- fail("objc_getClass() must not trigger +initialize");
-}
-@end
-
-@interface Super : TestRoot @end
-@implementation Super
-+(void)initialize {
- testprintf("in [Super initialize]\n");
- testassert(state == 0);
- state = 1;
-}
-+(void)method {
- fail("[Super method] shouldn't be called");
-}
-@end
-
-@interface Sub : Super @end
-@implementation Sub
-+(void)initialize {
- testprintf("in [Sub initialize]\n");
- testassert(state == 1);
- state = 2;
-}
-+(void)method {
- testprintf("in [Sub method]\n");
- testassert(state == 2);
- state = 3;
-}
-@end
-
-
-@interface Super2 : TestRoot @end
-@interface Sub2 : Super2 @end
-
-@implementation Super2
-+(void)initialize {
- if (self == objc_getClass("Sub2")) {
- testprintf("in [Super2 initialize] of Sub2\n");
- testassert(state == 1);
- state = 2;
- } else if (self == objc_getClass("Super2")) {
- testprintf("in [Super2 initialize] of Super2\n");
- testassert(state == 0);
- state = 1;
- } else {
- fail("in [Super2 initialize] of unknown class");
- }
-}
-+(void)method {
- testprintf("in [Super2 method]\n");
- testassert(state == 2);
- state = 3;
-}
-@end
-
-@implementation Sub2
-// nothing here
-@end
-
-
-@interface Super3 : TestRoot @end
-@interface Sub3 : Super3 @end
-
-@implementation Super3
-+(void)initialize {
- if (self == [Sub3 class]) { // this message triggers [Sub3 initialize]
- testprintf("in [Super3 initialize] of Sub3\n");
- testassert(state == 0);
- state = 1;
- } else if (self == [Super3 class]) {
- testprintf("in [Super3 initialize] of Super3\n");
- testassert(state == 1);
- state = 2;
- } else {
- fail("in [Super3 initialize] of unknown class");
- }
-}
-+(void)method {
- testprintf("in [Super3 method]\n");
- testassert(state == 2);
- state = 3;
-}
-@end
-
-@implementation Sub3
-// nothing here
-@end
-
-
-@interface Super4 : TestRoot @end
-@implementation Super4
--(void)instanceMethod {
- testassert(state == 1);
- state = 2;
-}
-+(void)initialize {
- testprintf("in [Super4 initialize]\n");
- testassert(state == 0);
- state = 1;
- id x = [[self alloc] init];
- [x instanceMethod];
- RELEASE_VALUE(x);
-}
-@end
-
-
-@interface Super5 : TestRoot @end
-@implementation Super5
--(void)instanceMethod {
-}
-+(void)classMethod {
- testassert(state == 1);
- state = 2;
-}
-+(void)initialize {
- testprintf("in [Super5 initialize]\n");
- testassert(state == 0);
- state = 1;
- class_getMethodImplementation(self, @selector(instanceMethod));
- // this is the "memoized" case for getNonMetaClass
- class_getMethodImplementation(object_getClass(self), @selector(classMethod));
- [self classMethod];
-}
-@end
-
-
-@interface Super6 : TestRoot @end
-@interface Sub6 : Super6 @end
-@implementation Super6
-+(void)initialize {
- static bool once;
- bool wasOnce;
- testprintf("in [Super6 initialize] (#%d)\n", 1+(int)once);
- if (!once) {
- once = true;
- wasOnce = true;
- testassert(state == 0);
- state = 1;
- } else {
- wasOnce = false;
- testassert(state == 2);
- state = 3;
- }
- [Sub6 class];
- if (wasOnce) {
- testassert(state == 5);
- state = 6;
- } else {
- testassert(state == 3);
- state = 4;
- }
-}
-@end
-@implementation Sub6
-+(void)initialize {
- testprintf("in [Sub6 initialize]\n");
- testassert(state == 1);
- state = 2;
- [super initialize];
- testassert(state == 4);
- state = 5;
-}
-@end
-
-
-@interface Super7 : TestRoot @end
-@interface Sub7 : Super7 @end
-@implementation Super7
-+(void)initialize {
- static bool once;
- bool wasOnce;
- testprintf("in [Super7 initialize] (#%d)\n", 1+(int)once);
- if (!once) {
- once = true;
- wasOnce = true;
- testassert(state == 0);
- state = 1;
- } else {
- wasOnce = false;
- testassert(state == 2);
- state = 3;
- }
- [Sub7 class];
- if (wasOnce) {
- testassert(state == 5);
- state = 6;
- } else {
- testassert(state == 3);
- state = 4;
- }
-}
-@end
-@implementation Sub7
-+(void)initialize {
- testprintf("in [Sub7 initialize]\n");
- testassert(state == 1);
- state = 2;
- [super initialize];
- testassert(state == 4);
- state = 5;
-}
-@end
-
-
-int main()
-{
- Class cls;
-
- // objc_getClass() must not +initialize anything
- state = 0;
- objc_getClass("Super0");
- testassert(state == 0);
-
- // initialize superclass, then subclass
- state = 0;
- [Sub method];
- testassert(state == 3);
-
- // check subclass's inheritance of superclass initialize
- state = 0;
- [Sub2 method];
- testassert(state == 3);
-
- // check subclass method called from superclass initialize
- state = 0;
- [Sub3 method];
- testassert(state == 3);
-
- // check class_getMethodImplementation (instance method)
- state = 0;
- cls = objc_getClass("Super4");
- testassert(state == 0);
- class_getMethodImplementation(cls, @selector(classMethod));
- testassert(state == 2);
-
- // check class_getMethodImplementation (class method)
- // this is the "slow" case for getNonMetaClass
- state = 0;
- cls = objc_getClass("Super5");
- testassert(state == 0);
- class_getMethodImplementation(object_getClass(cls), @selector(instanceMethod));
- testassert(state == 2);
-
- // check +initialize cycles
- // this is the "cls is a subclass" case for getNonMetaClass
- state = 0;
- [Super6 class];
- testassert(state == 6);
-
- // check +initialize cycles
- // this is the "cls is a subclass" case for getNonMetaClass
- state = 0;
- [Sub7 class];
- testassert(state == 6);
-
- succeed(__FILE__);
-
- return 0;
-}
+++ /dev/null
-// TEST_CONFIG MEM=arc
-// TEST_CFLAGS -framework Foundation
-
-// Problem: If weak reference operations provoke +initialize, the runtime
-// can deadlock (recursive weak lock, or lock inversion between weak lock
-// and +initialize lock).
-// Solution: object_setClass() and objc_storeWeak() perform +initialize
-// if needed so that no weakly-referenced object can ever have an
-// un-+initialized isa.
-
-#include <Foundation/Foundation.h>
-#include <objc/objc-internal.h>
-#include "test.h"
-
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-#pragma clang diagnostic ignored "-Warc-unsafe-retained-assign"
-
-// This is StripedMap's pointer hash
-uintptr_t hash(id obj) {
- uintptr_t addr = (uintptr_t)obj;
- return ((addr >> 4) ^ (addr >> 9)) % 64;
-}
-
-bool sameAlignment(id o1, id o2)
-{
- return hash(o1) == hash(o2);
-}
-
-// Return a new string object that uses the same striped weak locks as `obj`.
-NSMutableString *newAlignedString(id obj)
-{
- NSMutableArray *strings = [NSMutableArray new];
- NSMutableString *result;
- do {
- result = [NSMutableString new];
- [strings addObject:result];
- } while (!sameAlignment(obj, result));
- return result;
-}
-
-
-__weak NSObject *weak1;
-__weak NSMutableString *weak2;
-NSMutableString *strong2;
-
-@interface A : NSObject @end
-@implementation A
-+(void)initialize {
- weak2 = strong2; // weak store #2
- strong2 = nil;
-}
-@end
-
-void testA()
-{
- // Weak store #1 provokes +initialize which performs weak store #2.
- // Solution: weak store #1 runs +initialize if needed
- // without holding locks.
- @autoreleasepool {
- A *obj = [A new];
- strong2 = newAlignedString(obj);
- [obj addObserver:obj forKeyPath:@"foo" options:0 context:0];
- weak1 = obj; // weak store #1
- [obj removeObserver:obj forKeyPath:@"foo"];
- obj = nil;
- }
-}
-
-
-__weak NSObject *weak3;
-__weak NSMutableString *weak4;
-NSMutableString *strong4;
-
-@interface B : NSObject @end
-@implementation B
-+(void)initialize {
- weak4 = strong4; // weak store #4
- strong4 = nil;
-}
-@end
-
-
-void testB()
-{
- // Weak load #3 provokes +initialize which performs weak store #4.
- // Solution: object_setClass() runs +initialize if needed
- // without holding locks.
- @autoreleasepool {
- B *obj = [B new];
- strong4 = newAlignedString(obj);
- weak3 = obj;
- [obj addObserver:obj forKeyPath:@"foo" options:0 context:0];
- [weak3 self]; // weak load #3
- [obj removeObserver:obj forKeyPath:@"foo"];
- obj = nil;
- }
-}
-
-
-__weak id weak5;
-
-@interface C : NSObject @end
-@implementation C
-+(void)initialize {
- weak5 = [self new];
-}
-@end
-
-void testC()
-{
- // +initialize performs a weak store of itself.
- // Make sure the retry in objc_storeWeak() doesn't spin.
- @autoreleasepool {
- [C self];
- }
-}
-
-
-int main()
-{
- alarm(10); // replace hangs with crashes
-
- testA();
- testB();
- testC();
-
- succeed(__FILE__);
-}
-
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include "testroot.i"
-#include <objc/runtime.h>
-
-
-@interface Sub1 : TestRoot {
- // id isa; // 0..4
- BOOL b; // 4..5
-}
-@end
-
-@implementation Sub1 @end
-
-@interface Sub2 : Sub1 {
- // id isa // 0..4 0..8
- // BOOL b // 4..5 8..9
- BOOL b2; // 5..6 9..10
- id o; // 8..12 16..24
-}
-@end
-@implementation Sub2 @end
-
-@interface Sub3 : Sub1 {
- // id isa; // 0..4 0..8
- // BOOL b; // 4..5 8..9
- id o; // 8..12 16..24
- BOOL b2; // 12..13 24..25
-}
-@end
-@implementation Sub3 @end
-
-int main()
-{
- testassert(sizeof(id) == class_getInstanceSize([TestRoot class]));
- testassert(2*sizeof(id) == class_getInstanceSize([Sub1 class]));
- testassert(3*sizeof(id) == class_getInstanceSize([Sub2 class]));
- testassert(4*sizeof(id) == class_getInstanceSize([Sub3 class]));
-
-#if !__has_feature(objc_arc)
- id o;
-
- o = [TestRoot new];
- testassert(object_getIndexedIvars(o) == (char *)o + class_getInstanceSize(object_getClass(o)));
- RELEASE_VAR(o);
- o = [Sub1 new];
- testassert(object_getIndexedIvars(o) == (char *)o + class_getInstanceSize(object_getClass(o)));
- RELEASE_VAR(o);
- o = [Sub2 new];
- testassert(object_getIndexedIvars(o) == (char *)o + class_getInstanceSize(object_getClass(o)));
- RELEASE_VAR(o);
- o = [Sub3 new];
- testassert(object_getIndexedIvars(o) == (char *)o + class_getInstanceSize(object_getClass(o)));
- RELEASE_VAR(o);
-#endif
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include "testroot.i"
-#include <objc/objc-runtime.h>
-
-int main()
-{
- testassert(!class_isMetaClass([TestRoot class]));
- testassert(class_isMetaClass(object_getClass([TestRoot class])));
- testassert(!class_isMetaClass(nil));
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include "testroot.i"
-#include <stdint.h>
-#include <string.h>
-#include <objc/objc-runtime.h>
-
-@interface Super : TestRoot {
- @public
- char superIvar;
-}
-@end
-
-@interface Sub : Super {
- @public
- id subIvar;
-}
-@end
-
-@implementation Super @end
-@implementation Sub @end
-
-
-int main()
-{
- /*
- Runtime layout of Sub:
- [0] isa
- [1] superIvar
- [2] subIvar
- */
-
- Ivar ivar;
- Sub *sub = [Sub new];
- sub->subIvar = [Sub class];
- testassert(((Class *)objc_unretainedPointer(sub))[2] == [Sub class]);
-
- ivar = class_getInstanceVariable([Sub class], "subIvar");
- testassert(ivar);
- testassert(2*sizeof(intptr_t) == (size_t)ivar_getOffset(ivar));
- testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
- testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "@"));
-
- ivar = class_getInstanceVariable([Super class], "superIvar");
- testassert(ivar);
- testassert(sizeof(intptr_t) == (size_t)ivar_getOffset(ivar));
- testassert(0 == strcmp(ivar_getName(ivar), "superIvar"));
- testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "c"));
- testassert(ivar == class_getInstanceVariable([Sub class], "superIvar"));
-
- ivar = class_getInstanceVariable([Super class], "subIvar");
- testassert(!ivar);
-
- ivar = class_getInstanceVariable(object_getClass([Sub class]), "subIvar");
- testassert(!ivar);
-
- ivar = class_getInstanceVariable([Sub class], "subIvar");
- object_setIvar(sub, ivar, sub);
- testassert(sub->subIvar == sub);
- testassert(sub == object_getIvar(sub, ivar));
-
- testassert(NULL == class_getInstanceVariable(NULL, "foo"));
- testassert(NULL == class_getInstanceVariable([Sub class], NULL));
- testassert(NULL == class_getInstanceVariable(NULL, NULL));
-
- testassert(NULL == object_getIvar(sub, NULL));
- testassert(NULL == object_getIvar(NULL, ivar));
- testassert(NULL == object_getIvar(NULL, NULL));
-
- object_setIvar(sub, NULL, NULL);
- object_setIvar(NULL, ivar, NULL);
- object_setIvar(NULL, NULL, NULL);
-
-#if !__has_feature(objc_arc)
-
- uintptr_t value;
-
- sub->subIvar = (id)10;
- value = 0;
- object_getInstanceVariable(sub, "subIvar", (void **)&value);
- testassert(value == 10);
-
- object_setInstanceVariable(sub, "subIvar", (id)11);
- testassert(sub->subIvar == (id)11);
-
- ivar = class_getInstanceVariable([Sub class], "subIvar");
- testassert(ivar == object_getInstanceVariable(sub, "subIvar", NULL));
-
- testassert(NULL == object_getInstanceVariable(sub, NULL, NULL));
- testassert(NULL == object_getInstanceVariable(NULL, "foo", NULL));
- testassert(NULL == object_getInstanceVariable(NULL, NULL, NULL));
- value = 10;
- testassert(NULL == object_getInstanceVariable(sub, NULL, (void **)&value));
- testassert(value == 0);
- value = 10;
- testassert(NULL == object_getInstanceVariable(NULL, "foo", (void **)&value));
- testassert(value == 0);
- value = 10;
- testassert(NULL == object_getInstanceVariable(NULL, NULL, (void **)&value));
- testassert(value == 0);
-
- testassert(NULL == object_setInstanceVariable(sub, NULL, NULL));
- testassert(NULL == object_setInstanceVariable(NULL, "foo", NULL));
- testassert(NULL == object_setInstanceVariable(NULL, NULL, NULL));
-#endif
-
- succeed(__FILE__);
- return 0;
-}
+++ /dev/null
-@interface Super : TestRoot {
- @public
-#if OLD
- // nothing
-#else
- char superIvar;
-#endif
-}
-@end
-
-
-@interface ShrinkingSuper : TestRoot {
- @public
-#if OLD
- id superIvar[5];
- __weak id superIvar2[5];
-#else
- // nothing
-#endif
-}
-@end;
-
-
-@interface MoreStrongSuper : TestRoot {
- @public
-#if OLD
- void *superIvar;
-#else
- id superIvar;
-#endif
-}
-@end;
-
-
-@interface MoreWeakSuper : TestRoot {
- @public
-#if OLD
- id superIvar;
-#else
- __weak id superIvar;
-#endif
-}
-@end;
-
-@interface MoreWeak2Super : TestRoot {
- @public
-#if OLD
- void *superIvar;
-#else
- __weak id superIvar;
-#endif
-}
-@end;
-
-@interface LessStrongSuper : TestRoot {
- @public
-#if OLD
- id superIvar;
-#else
- void *superIvar;
-#endif
-}
-@end;
-
-@interface LessWeakSuper : TestRoot {
- @public
-#if OLD
- __weak id superIvar;
-#else
- id superIvar;
-#endif
-}
-@end;
-
-@interface LessWeak2Super : TestRoot {
- @public
-#if OLD
- __weak id superIvar;
-#else
- void *superIvar;
-#endif
-}
-@end;
-
-@interface NoGCChangeSuper : TestRoot {
- @public
- intptr_t d;
- char superc1;
-#if OLD
- // nothing
-#else
- char superc2;
-#endif
-}
-@end
-
-@interface RunsOf15 : TestRoot {
- @public
- id scan1;
- intptr_t skip15[15];
- id scan15[15];
- intptr_t skip15_2[15];
- id scan15_2[15];
-#if OLD
- // nothing
-#else
- intptr_t skip1;
-#endif
-}
-@end
+++ /dev/null
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/ivarSlide1.m $DIR/ivarSlide.m -o ivarSlide.out
-END
-*/
-
-#include "test.h"
-#include <string.h>
-#include <stdint.h>
-#include <objc/objc-runtime.h>
-#include <objc/objc-auto.h>
-
-// ARC doesn't like __strong void* or __weak void*
-#if __OBJC_GC__
-# define gc_weak __weak
-# define gc_strong __strong
-#else
-# define gc_weak
-# define gc_strong
-#endif
-
-#define OLD 1
-#include "ivarSlide.h"
-
-#define ustrcmp(a, b) strcmp((char *)a, (char *)b)
-
-#ifdef __cplusplus
-class CXX {
- public:
- static uintptr_t count;
- uintptr_t magic;
- CXX() : magic(1) { }
- ~CXX() { count += magic; }
-};
-uintptr_t CXX::count;
-#endif
-
-@interface Bitfields : Super {
- uint8_t uint8_ivar;
- uint8_t uint8_bitfield1 :7;
- uint8_t uint8_bitfield2 :1;
-
- id id_ivar;
-
- uintptr_t uintptr_ivar;
- uintptr_t /*uintptr_bitfield1*/ :31; // anonymous (rdar://5723893)
- uintptr_t uintptr_bitfield2 :1;
-
- id id_ivar2;
-}
-@end
-
-@implementation Bitfields @end
-
-
-@interface Sub : Super {
- @public
- uintptr_t subIvar;
- gc_strong void* subIvar2;
- gc_weak void* subIvar3;
-#ifdef __cplusplus
- CXX cxx;
-#else
- // same layout as cxx
- uintptr_t cxx_magic;
-#endif
-}
-@end
-
-@implementation Sub @end
-
-
-@interface Sub2 : ShrinkingSuper {
- @public
- gc_weak void* subIvar;
- gc_strong void* subIvar2;
-}
-@end
-
-@implementation Sub2 @end
-
-@interface MoreStrongSub : MoreStrongSuper { id subIvar; } @end
-@interface LessStrongSub : LessStrongSuper { id subIvar; } @end
-@interface MoreWeakSub : MoreWeakSuper { id subIvar; } @end
-@interface MoreWeak2Sub : MoreWeak2Super { id subIvar; } @end
-@interface LessWeakSub : LessWeakSuper { id subIvar; } @end
-@interface LessWeak2Sub : LessWeak2Super { id subIvar; } @end
-
-@implementation MoreStrongSub @end
-@implementation LessStrongSub @end
-@implementation MoreWeakSub @end
-@implementation MoreWeak2Sub @end
-@implementation LessWeakSub @end
-@implementation LessWeak2Sub @end
-
-@interface NoGCChangeSub : NoGCChangeSuper {
- @public
- char subc3;
-}
-@end
-@implementation NoGCChangeSub @end
-
-@interface RunsOf15Sub : RunsOf15 {
- @public
- char sub;
-}
-@end
-@implementation RunsOf15Sub @end
-
-
-int main(int argc __attribute__((unused)), char **argv)
-{
-#if __OBJC2__
-
-#if __has_feature(objc_arc)
- testwarn("fixme check ARC layouts too");
-#endif
-
- /*
- Bitfield ivars.
- rdar://5723893 anonymous bitfield ivars crash when slid
- rdar://5724385 bitfield ivar alignment incorrect
-
- Compile-time layout of Bitfields:
- [0 scan] isa
- [1 skip] uint8_ivar, uint8_bitfield
- [2 scan] id_ivar
- [3 skip] uintptr_ivar
- [4 skip] uintptr_bitfield
- [5 scan] id_ivar2
-
- Runtime layout of Bitfields:
- [0 scan] isa
- [1 skip] superIvar
- [2 skip] uint8_ivar, uint8_bitfield
- [3 scan] id_ivar
- [4 skip] uintptr_ivar
- [5 skip] uintptr_bitfield
- [6 scan] id_ivar2
- */
-
- [Bitfields class];
-
- testassert(class_getInstanceSize([Bitfields class]) == 7*sizeof(void*));
-
- if (objc_collectingEnabled()) {
- const uint8_t *bitfieldlayout;
- bitfieldlayout = class_getIvarLayout([Bitfields class]);
- testassert(0 == ustrcmp(bitfieldlayout, "\x01\x21\x21"));
-
- bitfieldlayout = class_getWeakIvarLayout([Bitfields class]);
- testassert(bitfieldlayout == NULL);
- }
-
- /*
- Compile-time layout of Sub:
- [0 scan] isa
- [1 skip] subIvar
- [2 scan] subIvar2
- [3 weak] subIvar3
- [6 skip] cxx
-
- Runtime layout of Sub:
- [0 scan] isa
- [1 skip] superIvar
- [2 skip] subIvar
- [3 scan] subIvar2
- [4 weak] subIvar3
- [6 skip] cxx
-
- Also, superIvar is only one byte, so subIvar's alignment must
- be handled correctly.
-
- fixme test more layouts
- */
-
- Ivar ivar;
- static Sub * volatile sub;
- sub = [Sub new];
- sub->subIvar = 10;
- testassert(((uintptr_t *)objc_unretainedPointer(sub))[2] == 10);
-
-#ifdef __cplusplus
- testassert(((uintptr_t *)objc_unretainedPointer(sub))[5] == 1);
- testassert(sub->cxx.magic == 1);
- sub->cxx.magic++;
- testassert(((uintptr_t *)objc_unretainedPointer(sub))[5] == 2);
- testassert(sub->cxx.magic == 2);
-# if __has_feature(objc_arc)
- sub = nil;
-# else
- if (! objc_collectingEnabled()) {
- [sub dealloc];
- } else {
- // hack - can't get collector to reliably delete the object
- object_dispose(sub);
- }
-# endif
- testassert(CXX::count == 2);
-#endif
-
- testassert(class_getInstanceSize([Sub class]) == 6*sizeof(void*));
-
- ivar = class_getInstanceVariable([Sub class], "subIvar");
- testassert(ivar);
- testassert(2*sizeof(void*) == (size_t)ivar_getOffset(ivar));
- testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
- // rdar://7466570 clang miscompiles assert(#if __LP64__ ... #endif)
-#if __LP64__
- testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "Q"));
-#elif __clang__
- testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "L"));
-#else
- testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "I"));
-#endif
-
-#ifdef __cplusplus
- ivar = class_getInstanceVariable([Sub class], "cxx");
- testassert(ivar);
-#endif
-
- ivar = class_getInstanceVariable([Super class], "superIvar");
- testassert(ivar);
- testassert(sizeof(void*) == (size_t)ivar_getOffset(ivar));
- testassert(0 == strcmp(ivar_getName(ivar), "superIvar"));
- testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "c"));
-
- ivar = class_getInstanceVariable([Super class], "subIvar");
- testassert(!ivar);
-
- if (objc_collectingEnabled()) {
- const uint8_t *superlayout;
- const uint8_t *sublayout;
- superlayout = class_getIvarLayout([Super class]);
- sublayout = class_getIvarLayout([Sub class]);
- testassert(0 == ustrcmp(superlayout, "\x01\x10"));
- testassert(0 == ustrcmp(sublayout, "\x01\x21\x20"));
-
- superlayout = class_getWeakIvarLayout([Super class]);
- sublayout = class_getWeakIvarLayout([Sub class]);
- testassert(superlayout == NULL);
- testassert(0 == ustrcmp(sublayout, "\x41\x10"));
- }
-
- /*
- Shrinking superclass.
- Subclass ivars do not compact, but the GC layout needs to
- update, including the gap that the superclass no longer spans.
-
- Compile-time layout of Sub2:
- [0 scan] isa
- [1-5 scan] superIvar
- [6-10 weak] superIvar2
- [11 weak] subIvar
- [12 scan] subIvar2
-
- Runtime layout of Sub2:
- [0 scan] isa
- [1-10 skip] was superIvar
- [11 weak] subIvar
- [12 scan] subIvar2
- */
-
- Sub2 *sub2 = [Sub2 new];
- sub2->subIvar = (void *)10;
- testassert(((uintptr_t *)objc_unretainedPointer(sub2))[11] == 10);
-
- testassert(class_getInstanceSize([Sub2 class]) == 13*sizeof(void*));
-
- ivar = class_getInstanceVariable([Sub2 class], "subIvar");
- testassert(ivar);
- testassert(11*sizeof(void*) == (size_t)ivar_getOffset(ivar));
- testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
-
- ivar = class_getInstanceVariable([ShrinkingSuper class], "superIvar");
- testassert(!ivar);
-
- if (objc_collectingEnabled()) {
- const uint8_t *superlayout;
- const uint8_t *sublayout;
- superlayout = class_getIvarLayout([ShrinkingSuper class]);
- sublayout = class_getIvarLayout([Sub2 class]);
- // only `isa` is left; superIvar[] and superIvar2[] are gone
- testassert(superlayout == NULL || 0 == ustrcmp(superlayout, "\x01"));
- testassert(0 == ustrcmp(sublayout, "\x01\xb1"));
-
- superlayout = class_getWeakIvarLayout([ShrinkingSuper class]);
- sublayout = class_getWeakIvarLayout([Sub2 class]);
- testassert(superlayout == NULL);
- testassert(0 == ustrcmp(sublayout, "\xb1\x10"));
- }
-
- /*
- Ivars slide but GC layouts stay the same
- Here, the last word of the superclass is misaligned, but
- its GC layout includes a bit for that whole word.
- Additionally, all of the subclass ivars fit into that word too,
- both before and after sliding.
- The runtime will try to slide the GC layout and must not be
- confused (rdar://6851700). Note that the second skip-word may or may
- not actually be included, because it crosses the end of the object.
-
-
- Compile-time layout of NoGCChangeSub:
- [0 scan] isa
- [1 skip] d
- [2 skip] superc1, subc3
-
- Runtime layout of NoGCChangeSub:
- [0 scan] isa
- [1 skip] d
- [2 skip] superc1, superc2, subc3
- */
- if (objc_collectingEnabled()) {
- Ivar ivar1 = class_getInstanceVariable([NoGCChangeSub class], "superc1");
- testassert(ivar1);
- Ivar ivar2 = class_getInstanceVariable([NoGCChangeSub class], "superc2");
- testassert(ivar2);
- Ivar ivar3 = class_getInstanceVariable([NoGCChangeSub class], "subc3");
- testassert(ivar3);
- testassert(ivar_getOffset(ivar1) != ivar_getOffset(ivar2) &&
- ivar_getOffset(ivar1) != ivar_getOffset(ivar3) &&
- ivar_getOffset(ivar2) != ivar_getOffset(ivar3));
- }
-
- /* Ivar layout includes runs of 15 words.
- rdar://6859875 this would generate a truncated GC layout.
- */
- if (objc_collectingEnabled()) {
- const uint8_t *layout =
- class_getIvarLayout(objc_getClass("RunsOf15Sub"));
- testassert(layout);
- int totalSkip = 0;
- int totalScan = 0;
- // should find 30+ each of skip and scan
- uint8_t c;
- while ((c = *layout++)) {
- totalSkip += c>>4;
- totalScan += c&0xf;
- }
- testassert(totalSkip >= 30);
- testassert(totalScan >= 30);
- }
-
-// __OBJC2__
-#endif
-
-
- /*
- Non-strong -> strong
- Classes do not change size, but GC layouts must be updated.
- Both new and old ABI detect this case (rdar://5774578)
-
- Compile-time layout of MoreStrongSub:
- [0 scan] isa
- [1 skip] superIvar
- [2 scan] subIvar
-
- Runtime layout of MoreStrongSub:
- [0 scan] isa
- [1 scan] superIvar
- [2 scan] subIvar
- */
- testassert(class_getInstanceSize([MoreStrongSub class]) == 3*sizeof(void*));
- if (objc_collectingEnabled()) {
- const uint8_t *layout;
- layout = class_getIvarLayout([MoreStrongSub class]);
- testassert(layout == NULL);
-
- layout = class_getWeakIvarLayout([MoreStrongSub class]);
- testassert(layout == NULL);
- }
-
-
- /*
- Strong -> weak
- Classes do not change size, but GC layouts must be updated.
- Old ABI intentionally does not detect this case (rdar://5774578)
-
- Compile-time layout of MoreWeakSub:
- [0 scan] isa
- [1 scan] superIvar
- [2 scan] subIvar
-
- Runtime layout of MoreWeakSub:
- [0 scan] isa
- [1 weak] superIvar
- [2 scan] subIvar
- */
- testassert(class_getInstanceSize([MoreWeakSub class]) == 3*sizeof(void*));
- if (objc_collectingEnabled()) {
- const uint8_t *layout;
- layout = class_getIvarLayout([MoreWeakSub class]);
-#if __OBJC2__
- // fixed version: scan / weak / scan
- testassert(0 == ustrcmp(layout, "\x01\x11"));
-#else
- // unfixed version: scan / scan / scan
- testassert(layout == NULL || 0 == ustrcmp(layout, "\x03"));
-#endif
-
- layout = class_getWeakIvarLayout([MoreWeakSub class]);
-#if __OBJC2__
- testassert(0 == ustrcmp(layout, "\x11\x10"));
-#else
- testassert(layout == NULL);
-#endif
- }
-
-
- /*
- Non-strong -> weak
- Classes do not change size, but GC layouts must be updated.
- Old ABI intentionally does not detect this case (rdar://5774578)
-
- Compile-time layout of MoreWeak2Sub:
- [0 scan] isa
- [1 skip] superIvar
- [2 scan] subIvar
-
- Runtime layout of MoreWeak2Sub:
- [0 scan] isa
- [1 weak] superIvar
- [2 scan] subIvar
- */
- testassert(class_getInstanceSize([MoreWeak2Sub class]) == 3*sizeof(void*));
- if (objc_collectingEnabled()) {
- const uint8_t *layout;
- layout = class_getIvarLayout([MoreWeak2Sub class]);
- testassert(0 == ustrcmp(layout, "\x01\x11") ||
- 0 == ustrcmp(layout, "\x01\x10\x01"));
-
- layout = class_getWeakIvarLayout([MoreWeak2Sub class]);
-#if __OBJC2__
- testassert(0 == ustrcmp(layout, "\x11\x10"));
-#else
- testassert(layout == NULL);
-#endif
- }
-
-
- /*
- Strong -> non-strong
- Classes do not change size, but GC layouts must be updated.
- Old ABI intentionally does not detect this case (rdar://5774578)
-
- Compile-time layout of LessStrongSub:
- [0 scan] isa
- [1 scan] superIvar
- [2 scan] subIvar
-
- Runtime layout of LessStrongSub:
- [0 scan] isa
- [1 skip] superIvar
- [2 scan] subIvar
- */
- testassert(class_getInstanceSize([LessStrongSub class]) == 3*sizeof(void*));
- if (objc_collectingEnabled()) {
- const uint8_t *layout;
- layout = class_getIvarLayout([LessStrongSub class]);
-#if __OBJC2__
- // fixed version: scan / skip / scan
- testassert(0 == ustrcmp(layout, "\x01\x11"));
-#else
- // unfixed version: scan / scan / scan
- testassert(layout == NULL || 0 == ustrcmp(layout, "\x03"));
-#endif
-
- layout = class_getWeakIvarLayout([LessStrongSub class]);
- testassert(layout == NULL);
- }
-
-
- /*
- Weak -> strong
- Classes do not change size, but GC layouts must be updated.
- Both new and old ABI detect this case (rdar://5774578 rdar://6924114)
-
- Compile-time layout of LessWeakSub:
- [0 scan] isa
- [1 weak] superIvar
- [2 scan] subIvar
-
- Runtime layout of LessWeakSub:
- [0 scan] isa
- [1 scan] superIvar
- [2 scan] subIvar
- */
- testassert(class_getInstanceSize([LessWeakSub class]) == 3*sizeof(void*));
- if (objc_collectingEnabled()) {
- const uint8_t *layout;
- layout = class_getIvarLayout([LessWeakSub class]);
- testassert(layout == NULL);
-
- layout = class_getWeakIvarLayout([LessWeakSub class]);
- testassert(layout == NULL);
- }
-
-
- /*
- Weak -> non-strong
- Classes do not change size, but GC layouts must be updated.
- Old ABI intentionally does not detect this case (rdar://5774578)
-
- Compile-time layout of LessWeak2Sub:
- [0 scan] isa
- [1 weak] superIvar
- [2 scan] subIvar
-
- Runtime layout of LessWeak2Sub:
- [0 scan] isa
- [1 skip] superIvar
- [2 scan] subIvar
- */
- testassert(class_getInstanceSize([LessWeak2Sub class]) == 3*sizeof(void*));
- if (objc_collectingEnabled()) {
- const uint8_t *layout;
- layout = class_getIvarLayout([LessWeak2Sub class]);
- testassert(0 == ustrcmp(layout, "\x01\x11") ||
- 0 == ustrcmp(layout, "\x01\x10\x01"));
-
- layout = class_getWeakIvarLayout([LessWeak2Sub class]);
-#if __OBJC2__
- testassert(layout == NULL);
-#else
- testassert(0 == ustrcmp(layout, "\x11\x10"));
-#endif
- }
-
-
- succeed(basename(argv[0]));
- return 0;
-}
+++ /dev/null
-#include "test.h"
-#include <stdint.h>
-#include <objc/runtime.h>
-
-#define OLD 0
-#include "ivarSlide.h"
-
-#include "testroot.i"
-
-@implementation Super @end
-
-@implementation ShrinkingSuper @end
-
-@implementation MoreStrongSuper @end
-@implementation LessStrongSuper @end
-@implementation MoreWeakSuper @end
-@implementation MoreWeak2Super @end
-@implementation LessWeakSuper @end
-@implementation LessWeak2Super @end
-@implementation NoGCChangeSuper @end
-@implementation RunsOf15 @end
+++ /dev/null
-// TEST_CONFIG MEM=gc OS=macosx
-
-#include "test.h"
-#include <string.h>
-#include <objc/objc-runtime.h>
-
-@class NSObject;
-
-void printlayout(const char *name, const uint8_t *layout)
-{
- testprintf("%s: ", name);
-
- if (!layout) {
- testprintf("NULL\n");
- return;
- }
-
- const uint8_t *c;
- for (c = layout; *c; c++) {
- testprintf("%02x ", *c);
- }
-
- testprintf("00\n");
-}
-
-OBJC_ROOT_CLASS
-@interface Super { id isa; } @end
-@implementation Super @end
-
-
-// strong: 0c 00 (0a00 without structs)
-// weak: NULL
-@interface AllScanned : Super {
- id id1;
- NSObject *o1;
- __strong void *v1;
- __strong intptr_t *i1;
- __strong long *l1;
- /* fixme
- struct {
- id id1;
- id id2;
- } str;
- */
- id arr1[4];
-}
-@end
-@implementation AllScanned @end
-
-// strong: 00
-// weak: 1b 00 (18 00 without structs)
-@interface AllWeak : Super {
- __weak id id1;
- __weak NSObject *o1;
- __weak void *v1;
- __weak intptr_t *i1;
- __weak long *l1;
- /* fixme
- struct {
- __weak id id1;
- __weak id id2;
- } str;
- */
- __weak id arr1[4];
-}
-@end
-@implementation AllWeak @end
-
-// strong: ""
-// weak: NULL
-OBJC_ROOT_CLASS
-@interface NoScanned { long i; } @end
-@implementation NoScanned @end
-
-int main()
-{
- const uint8_t *layout;
-
- layout = class_getIvarLayout(objc_getClass("AllScanned"));
- printlayout("AllScanned", layout);
- layout = class_getWeakIvarLayout(objc_getClass("AllScanned"));
- printlayout("AllScanned weak", layout);
- // testassert(0 == strcmp(layout, "\x0a"));
-
- layout = class_getIvarLayout(objc_getClass("AllWeak"));
- printlayout("AllWeak", layout);
- layout = class_getWeakIvarLayout(objc_getClass("AllWeak"));
- printlayout("AllWeak weak", layout);
- // testassert(0 == strcmp(layout, ""));
-
- layout = class_getIvarLayout(objc_getClass("NoScanned"));
- printlayout("NoScanned", layout);
- // testassert(0 == strcmp(layout, ""));
-
- succeed(__FILE__);
- return 0;
-}
+++ /dev/null
-// TEST_CONFIG MEM=arc,mrc CC=clang LANGUAGE=objc,objc++
-// TEST_CFLAGS -framework Foundation
-
-#import <Foundation/Foundation.h>
-#import <Foundation/NSDictionary.h>
-#import <objc/runtime.h>
-#import <objc/objc-abi.h>
-#import <math.h>
-#include "test.h"
-
-int main() {
- PUSH_POOL {
-
-#if __has_feature(objc_bool) // placeholder until we get a more precise macro.
- NSArray *array = @[ @1, @2, @YES, @NO, @"Hello", @"World" ];
- testassert([array count] == 6);
- NSDictionary *dict = @{ @"Name" : @"John Q. Public", @"Age" : @42 };
- testassert([dict count] == 2);
- NSDictionary *numbers = @{ @"Ï€" : @M_PI, @"e" : @M_E };
- testassert([[numbers objectForKey:@"Ï€"] doubleValue] == M_PI);
- testassert([[numbers objectForKey:@"e"] doubleValue] == M_E);
-
- BOOL yesBool = YES;
- BOOL noBool = NO;
- array = @[
- @(true),
- @(YES),
- [NSNumber numberWithBool:YES],
- @YES,
- @(yesBool),
- @((BOOL)YES),
-
- @(false),
- @(NO),
- [NSNumber numberWithBool:NO],
- @NO,
- @(noBool),
- @((BOOL)NO),
- ];
- NSData * jsonData = [NSJSONSerialization dataWithJSONObject:array options:0 error:nil];
- NSString * string = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
-#if __cplusplus
- testassert([string isEqualToString:@"[true,true,true,true,true,true,false,false,false,false,false,false]"]);
-#else
- // C99 @(true) and @(false) evaluate to @(1) and @(0).
- testassert([string isEqualToString:@"[1,true,true,true,true,true,0,false,false,false,false,false]"]);
-#endif
-
-#endif
-
- } POP_POOL;
-
- succeed(__FILE__);
-
- return 0;
-}
+++ /dev/null
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/load-noobjc.m -o load-noobjc.out
- $C{COMPILE} $DIR/load-noobjc2.m -o libload-noobjc2.dylib -bundle -bundle_loader load-noobjc.out
- $C{COMPILE} $DIR/load-noobjc3.m -o libload-noobjc3.dylib -bundle -bundle_loader load-noobjc.out
-END
-*/
-
-#include "test.h"
-
-#if !__OBJC2__
-// old runtime can't fix this deadlock
-
-int main()
-{
- succeed(__FILE__);
-}
-
-#else
-
-#include <dlfcn.h>
-
-int state = 0;
-semaphore_t go;
-
-void *thread(void *arg __unused)
-{
- objc_registerThreadWithCollector();
- dlopen("libload-noobjc2.dylib", RTLD_LAZY);
- fail("dlopen should not have returned");
-}
-
-int main()
-{
- semaphore_create(mach_task_self(), &go, SYNC_POLICY_FIFO, 0);
-
- pthread_t th;
- pthread_create(&th, nil, &thread, nil);
-
- // Wait for thread to stop in libload-noobjc2's +load method.
- semaphore_wait(go);
-
- // run nooobjc3's constructor function.
- // There's no objc code here so it shouldn't require the +load lock.
- void *dlh = dlopen("libload-noobjc3.dylib", RTLD_LAZY);
- testassert(dlh);
- testassert(state == 1);
-
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-#include "test.h"
-#if __OBJC2__
-
-extern semaphore_t go;
-
-OBJC_ROOT_CLASS
-@interface noobjc @end
-@implementation noobjc
-+(void)load
-{
- semaphore_signal(go);
- while (1) sleep(1);
-}
-@end
-
-#endif
+++ /dev/null
-#include "test.h"
-
-#if __OBJC2__
-
-extern int state;
-
-__attribute__((constructor))
-static void ctor(void)
-{
- state = 1;
-}
-
-#endif
+++ /dev/null
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/load-order3.m -o load-order3.dylib -dynamiclib
- $C{COMPILE} $DIR/load-order2.m -o load-order2.dylib -x none load-order3.dylib -dynamiclib
- $C{COMPILE} $DIR/load-order1.m -o load-order1.dylib -x none load-order3.dylib load-order2.dylib -dynamiclib
- $C{COMPILE} $DIR/load-order.m -o load-order.out -x none load-order3.dylib load-order2.dylib load-order1.dylib
-END
-*/
-
-#include "test.h"
-
-extern int state1, state2, state3;
-
-int main()
-{
- testassert(state1 == 1 && state2 == 2 && state3 == 3);
- succeed(__FILE__);
-}
+++ /dev/null
-#include "test.h"
-
-extern int state2, state3;
-
-int state1 = 0;
-
-OBJC_ROOT_CLASS
-@interface One @end
-@implementation One
-+(void)load
-{
- testassert(state2 == 2 && state3 == 3);
- state1 = 1;
-}
-@end
+++ /dev/null
-#include "test.h"
-
-extern int state3;
-
-int state2 = 0;
-
-OBJC_ROOT_CLASS
-@interface Two @end
-@implementation Two
-+(void)load
-{
- testassert(state3 == 3);
- state2 = 2;
-}
-@end
+++ /dev/null
-#include "test.h"
-
-int state3 = 0;
-
-OBJC_ROOT_CLASS
-@interface Three @end
-@implementation Three
-+(void)load
-{
- state3 = 3;
-}
-@end
+++ /dev/null
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/load-parallel00.m -o load-parallel00.dylib -dynamiclib
- $C{COMPILE} $DIR/load-parallel.m -x none load-parallel00.dylib -o load-parallel.out -DCOUNT=10
-
- $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel0.dylib -dynamiclib -DN=0
- $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel1.dylib -dynamiclib -DN=1
- $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel2.dylib -dynamiclib -DN=2
- $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel3.dylib -dynamiclib -DN=3
- $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel4.dylib -dynamiclib -DN=4
- $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel5.dylib -dynamiclib -DN=5
- $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel6.dylib -dynamiclib -DN=6
- $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel7.dylib -dynamiclib -DN=7
- $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel8.dylib -dynamiclib -DN=8
- $C{COMPILE} $DIR/load-parallel0.m -x none load-parallel00.dylib -o load-parallel9.dylib -dynamiclib -DN=9
-END
-*/
-
-#include "test.h"
-
-#include <dlfcn.h>
-#include <pthread.h>
-
-#ifndef COUNT
-#error -DCOUNT=c missing
-#endif
-
-extern int state;
-
-void *thread(void *arg)
-{
- uintptr_t num = (uintptr_t)arg;
- char *buf;
-
- objc_registerThreadWithCollector();
-
- asprintf(&buf, "load-parallel%lu.dylib", (unsigned long)num);
- testprintf("%s\n", buf);
- void *dlh = dlopen(buf, RTLD_LAZY);
- if (!dlh) {
- fail("dlopen failed: %s", dlerror());
- }
- free(buf);
-
- return NULL;
-}
-
-int main()
-{
- pthread_t t[COUNT];
- uintptr_t i;
-
- for (i = 0; i < COUNT; i++) {
- pthread_create(&t[i], NULL, thread, (void *)i);
- }
-
- for (i = 0; i < COUNT; i++) {
- pthread_join(t[i], NULL);
- }
-
- testprintf("loaded %d/%d\n", state, COUNT*26);
- testassert(state == COUNT*26);
-
- succeed(__FILE__);
-}
+++ /dev/null
-#ifndef N
-#error -DN=n missing
-#endif
-
-#import <objc/objc-api.h>
-#include <stdio.h>
-#include <sched.h>
-#include <unistd.h>
-#include <libkern/OSAtomic.h>
-extern int state;
-
-#define CLASS0(n,nn) \
- OBJC_ROOT_CLASS \
- @interface C_##n##_##nn @end \
- @implementation C_##n##_##nn \
- +(void)load { OSAtomicIncrement32(&state); usleep(10); } \
- @end
-
-#define CLASS(n,nn) CLASS0(n,nn)
-
-CLASS(a,N)
-CLASS(b,N)
-CLASS(c,N)
-CLASS(d,N)
-CLASS(e,N)
-CLASS(f,N)
-CLASS(g,N)
-CLASS(h,N)
-CLASS(i,N)
-CLASS(j,N)
-CLASS(k,N)
-CLASS(l,N)
-CLASS(m,N)
-CLASS(n,N)
-CLASS(o,N)
-CLASS(p,N)
-CLASS(q,N)
-CLASS(r,N)
-CLASS(s,N)
-CLASS(t,N)
-CLASS(u,N)
-CLASS(v,N)
-CLASS(w,N)
-CLASS(x,N)
-CLASS(y,N)
-CLASS(z,N)
+++ /dev/null
-int state = 0;
+++ /dev/null
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/load-reentrant.m -o load-reentrant.out
- $C{COMPILE} $DIR/load-reentrant2.m -o libload-reentrant2.dylib -bundle -bundle_loader load-reentrant.out
-END
-*/
-
-#include "test.h"
-#include <dlfcn.h>
-
-int state1 = 0;
-int *state2_p;
-
-OBJC_ROOT_CLASS
-@interface One @end
-@implementation One
-+(void)load
-{
- state1 = 111;
-
- // Re-entrant +load doesn't get to complete until we do
- void *dlh = dlopen("libload-reentrant2.dylib", RTLD_LAZY);
- testassert(dlh);
- state2_p = (int *)dlsym(dlh, "state2");
- testassert(state2_p);
- testassert(*state2_p == 0);
-
- state1 = 1;
-}
-@end
-
-int main()
-{
- testassert(state1 == 1 && state2_p && *state2_p == 2);
- succeed(__FILE__);
-}
+++ /dev/null
-#include "test.h"
-
-int state2 = 0;
-extern int state1;
-
-static void ctor(void) __attribute__((constructor));
-static void ctor(void)
-{
- // should be called during One's dlopen(), before Two's +load
- testassert(state1 == 111);
- testassert(state2 == 0);
-}
-
-OBJC_ROOT_CLASS
-@interface Two @end
-@implementation Two
-+(void) load
-{
- // Does not run until One's +load completes
- testassert(state1 == 1);
- state2 = 2;
-}
-@end
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include "testroot.i"
-
-int state = 0;
-int catstate = 0;
-int deallocstate = 0;
-
-@interface Deallocator : TestRoot @end
-@implementation Deallocator
--(id)init {
- self = [super init];
- if (objc_collectingEnabled()) {
- deallocstate = 1;
- }
- return self;
-}
--(void)dealloc {
- deallocstate = 1;
- SUPER_DEALLOC();
-}
-@end
-
-
-@interface Super : TestRoot @end
-@implementation Super
-+(void)initialize {
- if (self == [Super class]) {
- testprintf("in +[Super initialize]\n");
- testassert(state == 2);
- state = 3;
- } else {
- testprintf("in +[Super initialize] on behalf of Sub\n");
- testassert(state == 3);
- state = 4;
- }
-}
--(void)load { fail("-[Super load] called!"); }
-+(void)load {
- testprintf("in +[Super load]\n");
- testassert(state == 0);
- state = 1;
-}
-@end
-
-@interface Sub : Super { } @end
-@implementation Sub
-+(void)load {
- testprintf("in +[Sub load]\n");
- testassert(state == 1);
- state = 2;
-}
--(void)load { fail("-[Sub load] called!"); }
-@end
-
-@interface SubNoLoad : Super { } @end
-@implementation SubNoLoad @end
-
-@interface Super (Category) @end
-@implementation Super (Category)
--(void)load { fail("-[Super(Category) load called!"); }
-+(void)load {
- testprintf("in +[Super(Category) load]\n");
- testassert(state >= 1);
- catstate++;
-}
-@end
-
-
-@interface Sub (Category) @end
-@implementation Sub (Category)
--(void)load { fail("-[Sub(Category) load called!"); }
-+(void)load {
- testprintf("in +[Sub(Category) load]\n");
- testassert(state >= 2);
- catstate++;
-
- // test autorelease pool
- __autoreleasing id x;
- x = AUTORELEASE([Deallocator new]);
-}
-@end
-
-
-@interface SubNoLoad (Category) @end
-@implementation SubNoLoad (Category)
--(void)load { fail("-[SubNoLoad(Category) load called!"); }
-+(void)load {
- testprintf("in +[SubNoLoad(Category) load]\n");
- testassert(state >= 1);
- catstate++;
-}
-@end
-
-int main()
-{
- testassert(state == 2);
- testassert(catstate == 3);
- testassert(deallocstate == 1);
- [Sub class];
- testassert(state == 4);
- testassert(catstate == 3);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -Wno-deprecated-declarations
-
-#include "test.h"
-#include "testroot.i"
-#include <string.h>
-#include <objc/objc-runtime.h>
-
-@interface Super : TestRoot @end
-@implementation Super
-+(id)method:(int)__unused arg :(void(^)(void)) __unused arg2 {
- return 0;
-}
-@end
-
-
-int main()
-{
- char buf[128];
- char *arg;
- struct objc_method_description *desc;
- Method m = class_getClassMethod([Super class], sel_registerName("method::"));
- testassert(m);
-
- testassert(method_getNumberOfArguments(m) == 4);
-#if !__OBJC2__
- testassert(method_getSizeOfArguments(m) == 16);
-#endif
-
- arg = method_copyArgumentType(m, 0);
- testassert(arg);
- testassert(0 == strcmp(arg, "@"));
- memset(buf, 1, 128);
- method_getArgumentType(m, 0, buf, 1+strlen(arg));
- testassert(0 == strcmp(arg, buf));
- testassert(buf[1+strlen(arg)] == 1);
- memset(buf, 1, 128);
- method_getArgumentType(m, 0, buf, 2);
- testassert(0 == strncmp(arg, buf, 2));
- testassert(buf[2] == 1);
- free(arg);
-
- arg = method_copyArgumentType(m, 1);
- testassert(arg);
- testassert(0 == strcmp(arg, ":"));
- memset(buf, 1, 128);
- method_getArgumentType(m, 1, buf, 1+strlen(arg));
- testassert(0 == strcmp(arg, buf));
- testassert(buf[1+strlen(arg)] == 1);
- memset(buf, 1, 128);
- method_getArgumentType(m, 1, buf, 2);
- testassert(0 == strncmp(arg, buf, 2));
- testassert(buf[2] == 1);
- free(arg);
-
- arg = method_copyArgumentType(m, 2);
- testassert(arg);
- testassert(0 == strcmp(arg, "i"));
- memset(buf, 1, 128);
- method_getArgumentType(m, 2, buf, 1+strlen(arg));
- testassert(0 == strcmp(arg, buf));
- testassert(buf[1+strlen(arg)] == 1);
- memset(buf, 1, 128);
- method_getArgumentType(m, 2, buf, 2);
- testassert(0 == strncmp(arg, buf, 2));
- testassert(buf[2] == 1);
- free(arg);
-
- arg = method_copyArgumentType(m, 3);
- testassert(arg);
- testassert(0 == strcmp(arg, "@?"));
- memset(buf, 1, 128);
- method_getArgumentType(m, 3, buf, 1+strlen(arg));
- testassert(0 == strcmp(arg, buf));
- testassert(buf[1+strlen(arg)] == 1);
- memset(buf, 1, 128);
- method_getArgumentType(m, 3, buf, 2);
- testassert(0 == strncmp(arg, buf, 2));
- testassert(buf[2] == 1);
- memset(buf, 1, 128);
- method_getArgumentType(m, 3, buf, 3);
- testassert(0 == strncmp(arg, buf, 3));
- testassert(buf[3] == 1);
- free(arg);
-
- arg = method_copyArgumentType(m, 4);
- testassert(!arg);
-
- arg = method_copyArgumentType(m, -1);
- testassert(!arg);
-
- memset(buf, 1, 128);
- method_getArgumentType(m, 4, buf, 127);
- testassert(buf[0] == 0);
- testassert(buf[1] == 0);
- testassert(buf[127] == 1);
-
- memset(buf, 1, 128);
- method_getArgumentType(m, -1, buf, 127);
- testassert(buf[0] == 0);
- testassert(buf[1] == 0);
- testassert(buf[127] == 1);
-
- arg = method_copyReturnType(m);
- testassert(arg);
- testassert(0 == strcmp(arg, "@"));
- memset(buf, 1, 128);
- method_getReturnType(m, buf, 1+strlen(arg));
- testassert(0 == strcmp(arg, buf));
- testassert(buf[1+strlen(arg)] == 1);
- memset(buf, 1, 128);
- method_getReturnType(m, buf, 2);
- testassert(0 == strncmp(arg, buf, 2));
- testassert(buf[2] == 1);
- free(arg);
-
- desc = method_getDescription(m);
- testassert(desc);
- testassert(desc->name == sel_registerName("method::"));
-#if __LP64__
- testassert(0 == strcmp(desc->types, "@28@0:8i16@?20"));
-#else
- testassert(0 == strcmp(desc->types, "@16@0:4i8@?12"));
-#endif
-
- testassert(0 == method_getNumberOfArguments(NULL));
-#if !__OBJC2__
- testassert(0 == method_getSizeOfArguments(NULL));
-#endif
- testassert(NULL == method_copyArgumentType(NULL, 10));
- testassert(NULL == method_copyReturnType(NULL));
- testassert(NULL == method_getDescription(NULL));
-
- memset(buf, 1, 128);
- method_getArgumentType(NULL, 1, buf, 127);
- testassert(buf[0] == 0);
- testassert(buf[1] == 0);
- testassert(buf[127] == 1);
-
- memset(buf, 1, 128);
- method_getArgumentType(NULL, 1, buf, 0);
- testassert(buf[0] == 1);
- testassert(buf[1] == 1);
-
- method_getArgumentType(m, 1, NULL, 128);
- method_getArgumentType(m, 1, NULL, 0);
- method_getArgumentType(NULL, 1, NULL, 128);
- method_getArgumentType(NULL, 1, NULL, 0);
-
- memset(buf, 1, 128);
- method_getReturnType(NULL, buf, 127);
- testassert(buf[0] == 0);
- testassert(buf[1] == 0);
- testassert(buf[127] == 1);
-
- memset(buf, 1, 128);
- method_getReturnType(NULL, buf, 0);
- testassert(buf[0] == 1);
- testassert(buf[1] == 1);
-
- method_getReturnType(m, NULL, 128);
- method_getReturnType(m, NULL, 0);
- method_getReturnType(NULL, NULL, 128);
- method_getReturnType(NULL, NULL, 0);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-// rdar://8052003 rdar://8077031
-
-#include "test.h"
-
-#include <malloc/malloc.h>
-#include <objc/runtime.h>
-
-// add SELCOUNT methods to each of CLASSCOUNT classes
-#define CLASSCOUNT 100
-#define SELCOUNT 200
-
-int main()
-{
- int i, j;
- malloc_statistics_t start, end;
-
- Class root;
- root = objc_allocateClassPair(NULL, "Root", 0);
- objc_registerClassPair(root);
-
- Class classes[CLASSCOUNT];
- for (i = 0; i < CLASSCOUNT; i++) {
- char *classname;
- asprintf(&classname, "GrP_class_%d", i);
- classes[i] = objc_allocateClassPair(root, classname, 0);
- objc_registerClassPair(classes[i]);
- free(classname);
- }
-
- SEL selectors[SELCOUNT];
- for (i = 0; i < SELCOUNT; i++) {
- char *selname;
- asprintf(&selname, "GrP_sel_%d", i);
- selectors[i] = sel_registerName(selname);
- free(selname);
- }
-
- malloc_zone_statistics(NULL, &start);
-
- for (i = 0; i < CLASSCOUNT; i++) {
- for (j = 0; j < SELCOUNT; j++) {
- class_addMethod(classes[i], selectors[j], (IMP)main, "");
- }
- }
-
- malloc_zone_statistics(NULL, &end);
-
- // expected: 3-word method struct plus two other words
- ssize_t expected = (sizeof(void*) * (3+2)) * SELCOUNT * CLASSCOUNT;
- ssize_t actual = end.size_in_use - start.size_in_use;
- testassert(actual < expected * 3); // allow generous fudge factor
-
- succeed(__FILE__);
-}
-
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include <Foundation/NSObject.h>
-#include <objc/runtime.h>
-
-#undef SUPPORT_NONPOINTER_ISA // remove test.h's definition
-#include "../runtime/objc-config.h"
-
-int main() {
- unsigned i;
- Class c = [NSObject class];
- unsigned numMethods;
- Method *methods = class_copyMethodList(c, &numMethods);
-
- for (i=0; i<numMethods; ++i) {
- // <rdar://problem/6190950> method_getName crash on NSObject method when GC is enabled
- SEL aMethod;
- aMethod = method_getName(methods[i]);
-#if defined(kIgnore)
- if (aMethod == (SEL)kIgnore)
- fail(__FILE__);
-#endif
- }
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -Wno-unused-parameter -Wundeclared-selector
-
-#include "test.h"
-#include "testroot.i"
-
-#if __cplusplus && !__clang__
-
-int main()
-{
- // llvm-g++ is confused by @selector(foo::) and will never be fixed
- succeed(__FILE__);
-}
-
-#else
-
-#include <objc/objc.h>
-#include <objc/runtime.h>
-#include <objc/objc-internal.h>
-#include <objc/objc-abi.h>
-#include <simd/simd.h>
-
-// rdar://21694990 simd.h should have a vector_equal(a, b) function
-static bool vector_equal(vector_ulong2 lhs, vector_ulong2 rhs) {
- return vector_all(lhs == rhs);
-}
-
-#if __arm64__
- // no stret dispatchers
-# define SUPPORT_STRET 0
-# define objc_msgSend_stret objc_msgSend
-# define objc_msgSendSuper2_stret objc_msgSendSuper2
-# define objc_msgSend_stret_debug objc_msgSend_debug
-# define objc_msgSendSuper2_stret_debug objc_msgSendSuper2_debug
-# define method_invoke_stret method_invoke
-#else
-# define SUPPORT_STRET 1
-#endif
-
-
-#if defined(__arm__)
-// rdar://8331406
-# define ALIGN_()
-#else
-# define ALIGN_() asm(".align 4");
-#endif
-
-@interface Super : TestRoot @end
-
-@interface Sub : Super @end
-
-static int state = 0;
-
-static id SELF;
-
-// for typeof() shorthand only
-id (*idmsg0)(id, SEL) __attribute__((unused));
-long long (*llmsg0)(id, SEL) __attribute__((unused));
-// struct stret (*stretmsg0)(id, SEL) __attribute__((unused));
-double (*fpmsg0)(id, SEL) __attribute__((unused));
-long double (*lfpmsg0)(id, SEL) __attribute__((unused));
-vector_ulong2 (*vecmsg0)(id, SEL) __attribute__((unused));
-
-#define VEC1 ((vector_ulong2){1, 1})
-#define VEC2 ((vector_ulong2){2, 2})
-#define VEC3 ((vector_ulong2){3, 3})
-#define VEC4 ((vector_ulong2){4, 4})
-#define VEC5 ((vector_ulong2){5, 5})
-#define VEC6 ((vector_ulong2){6, 6})
-#define VEC7 ((vector_ulong2){7, 7})
-#define VEC8 ((vector_ulong2){8, 8})
-
-#define CHECK_ARGS(sel) \
-do { \
- testassert(self == SELF); \
- testassert(_cmd == sel_registerName(#sel "::::::::::::::::::::::::::::::::::::"));\
- testassert(vector_all(v1 == 1)); \
- testassert(vector_all(v2 == 2)); \
- testassert(vector_all(v3 == 3)); \
- testassert(vector_all(v4 == 4)); \
- testassert(vector_all(v5 == 5)); \
- testassert(vector_all(v6 == 6)); \
- testassert(vector_all(v7 == 7)); \
- testassert(vector_all(v8 == 8)); \
- testassert(i1 == 1); \
- testassert(i2 == 2); \
- testassert(i3 == 3); \
- testassert(i4 == 4); \
- testassert(i5 == 5); \
- testassert(i6 == 6); \
- testassert(i7 == 7); \
- testassert(i8 == 8); \
- testassert(i9 == 9); \
- testassert(i10 == 10); \
- testassert(i11 == 11); \
- testassert(i12 == 12); \
- testassert(i13 == 13); \
- testassert(f1 == 1.0); \
- testassert(f2 == 2.0); \
- testassert(f3 == 3.0); \
- testassert(f4 == 4.0); \
- testassert(f5 == 5.0); \
- testassert(f6 == 6.0); \
- testassert(f7 == 7.0); \
- testassert(f8 == 8.0); \
- testassert(f9 == 9.0); \
- testassert(f10 == 10.0); \
- testassert(f11 == 11.0); \
- testassert(f12 == 12.0); \
- testassert(f13 == 13.0); \
- testassert(f14 == 14.0); \
- testassert(f15 == 15.0); \
-} while (0)
-
-#define CHECK_ARGS_NOARG(sel) \
-do { \
- testassert(self == SELF); \
- testassert(_cmd == sel_registerName(#sel "_noarg"));\
-} while (0)
-
-id NIL_RECEIVER;
-id ID_RESULT;
-long long LL_RESULT = __LONG_LONG_MAX__ - 2LL*__INT_MAX__;
-double FP_RESULT = __DBL_MIN__ + __DBL_EPSILON__;
-long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__;
-vector_ulong2 VEC_RESULT = { 0x1234567890abcdefULL, 0xfedcba0987654321ULL };
-// STRET_RESULT in test.h
-
-static struct stret zero;
-
-struct stret_i1 {
- uintptr_t i1;
-};
-struct stret_i2 {
- uintptr_t i1;
- uintptr_t i2;
-};
-struct stret_i3 {
- uintptr_t i1;
- uintptr_t i2;
- uintptr_t i3;
-};
-struct stret_i4 {
- uintptr_t i1;
- uintptr_t i2;
- uintptr_t i3;
-};
-struct stret_i5 {
- uintptr_t i1;
- uintptr_t i2;
- uintptr_t i3;
- uintptr_t i4;
- uintptr_t i5;
-};
-struct stret_i6 {
- uintptr_t i1;
- uintptr_t i2;
- uintptr_t i3;
- uintptr_t i4;
- uintptr_t i5;
- uintptr_t i6;
-};
-struct stret_i7 {
- uintptr_t i1;
- uintptr_t i2;
- uintptr_t i3;
- uintptr_t i4;
- uintptr_t i5;
- uintptr_t i6;
- uintptr_t i7;
-};
-struct stret_i8 {
- uintptr_t i1;
- uintptr_t i2;
- uintptr_t i3;
- uintptr_t i4;
- uintptr_t i5;
- uintptr_t i8;
- uintptr_t i9;
-};
-struct stret_i9 {
- uintptr_t i1;
- uintptr_t i2;
- uintptr_t i3;
- uintptr_t i4;
- uintptr_t i5;
- uintptr_t i6;
- uintptr_t i7;
- uintptr_t i8;
- uintptr_t i9;
-};
-
-struct stret_d1 {
- double d1;
-};
-struct stret_d2 {
- double d1;
- double d2;
-};
-struct stret_d3 {
- double d1;
- double d2;
- double d3;
-};
-struct stret_d4 {
- double d1;
- double d2;
- double d3;
-};
-struct stret_d5 {
- double d1;
- double d2;
- double d3;
- double d4;
- double d5;
-};
-struct stret_d6 {
- double d1;
- double d2;
- double d3;
- double d4;
- double d5;
- double d6;
-};
-struct stret_d7 {
- double d1;
- double d2;
- double d3;
- double d4;
- double d5;
- double d6;
- double d7;
-};
-struct stret_d8 {
- double d1;
- double d2;
- double d3;
- double d4;
- double d5;
- double d8;
- double d9;
-};
-struct stret_d9 {
- double d1;
- double d2;
- double d3;
- double d4;
- double d5;
- double d6;
- double d7;
- double d8;
- double d9;
-};
-
-
-@interface Super (Prototypes)
-
-// Method prototypes to pacify -Wundeclared-selector.
-
--(id)idret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
--(long long)llret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
--(struct stret)stret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
--(double)fpret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
--(long double)lfpret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
--(vector_ulong2)vecret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15;
-
-@end
-
-
-// Zero all volatile registers.
-#if __cplusplus
-extern "C"
-#endif
-void stomp(void);
-
-#if __x86_64__
-asm("\n .text"
- "\n .globl _stomp"
- "\n _stomp:"
- "\n mov $0, %rax"
- "\n mov $0, %rcx"
- "\n mov $0, %rdx"
- "\n mov $0, %rsi"
- "\n mov $0, %rdi"
- "\n mov $0, %r8"
- "\n mov $0, %r9"
- "\n mov $0, %r10"
- "\n mov $0, %r11"
- "\n xorps %xmm0, %xmm0"
- "\n xorps %xmm1, %xmm1"
- "\n xorps %xmm2, %xmm2"
- "\n xorps %xmm3, %xmm3"
- "\n xorps %xmm4, %xmm4"
- "\n xorps %xmm5, %xmm5"
- "\n xorps %xmm6, %xmm6"
- "\n xorps %xmm7, %xmm7"
- "\n xorps %xmm8, %xmm8"
- "\n xorps %xmm9, %xmm9"
- "\n xorps %xmm10, %xmm10"
- "\n xorps %xmm11, %xmm11"
- "\n xorps %xmm12, %xmm12"
- "\n xorps %xmm13, %xmm13"
- "\n xorps %xmm14, %xmm14"
- "\n xorps %xmm15, %xmm15"
- "\n ret");
-
-#elif __i386__
-asm("\n .text"
- "\n .globl _stomp"
- "\n _stomp:"
- "\n mov $0, %eax"
- "\n mov $0, %ecx"
- "\n mov $0, %edx"
- "\n xorps %xmm0, %xmm0"
- "\n xorps %xmm1, %xmm1"
- "\n xorps %xmm2, %xmm2"
- "\n xorps %xmm3, %xmm3"
- "\n xorps %xmm4, %xmm4"
- "\n xorps %xmm5, %xmm5"
- "\n xorps %xmm6, %xmm6"
- "\n xorps %xmm7, %xmm7"
- "\n ret");
-
-#elif __arm64__
-asm("\n .text"
- "\n .globl _stomp"
- "\n _stomp:"
- "\n mov x0, #0"
- "\n mov x1, #0"
- "\n mov x2, #0"
- "\n mov x3, #0"
- "\n mov x4, #0"
- "\n mov x5, #0"
- "\n mov x6, #0"
- "\n mov x7, #0"
- "\n mov x8, #0"
- "\n mov x9, #0"
- "\n mov x10, #0"
- "\n mov x11, #0"
- "\n mov x12, #0"
- "\n mov x13, #0"
- "\n mov x14, #0"
- "\n mov x15, #0"
- "\n mov x16, #0"
- "\n mov x17, #0"
- "\n movi d0, #0"
- "\n movi d1, #0"
- "\n movi d2, #0"
- "\n movi d3, #0"
- "\n movi d4, #0"
- "\n movi d5, #0"
- "\n movi d6, #0"
- "\n movi d7, #0"
- "\n ret"
- );
-
-#elif __arm__
-asm("\n .text"
- "\n .globl _stomp"
- "\n .thumb_func _stomp"
- "\n _stomp:"
- "\n mov r0, #0"
- "\n mov r1, #0"
- "\n mov r2, #0"
- "\n mov r3, #0"
- "\n mov r9, #0"
- "\n mov r12, #0"
- "\n vmov.i32 q0, #0"
- "\n vmov.i32 q1, #0"
- "\n vmov.i32 q2, #0"
- "\n vmov.i32 q3, #0"
- "\n vmov.i32 q4, #0"
- "\n vmov.i32 q5, #0"
- "\n vmov.i32 q6, #0"
- "\n vmov.i32 q7, #0"
- "\n vmov.i32 q8, #0"
- "\n vmov.i32 q9, #0"
- "\n vmov.i32 q10, #0"
- "\n vmov.i32 q11, #0"
- "\n vmov.i32 q12, #0"
- "\n vmov.i32 q13, #0"
- "\n vmov.i32 q14, #0"
- "\n vmov.i32 q15, #0"
- "\n bx lr"
- );
-
-#else
-# error unknown architecture
-#endif
-
-
-@implementation Super
--(struct stret)stret { return STRET_RESULT; }
-
-// The IMPL_ methods are not called directly. Instead the non IMPL_ name is
-// called. The resolver function installs the real method. This allows
-// the resolver function to stomp on registers to help test register
-// preservation in the uncached path.
-
-+(BOOL) resolveInstanceMethod:(SEL)sel
-{
- const char *name = sel_getName(sel);
- if (! strstr(name, "::::::::")) return false;
-
- testprintf("resolving %s\n", name);
-
- stomp();
- char *realName;
- asprintf(&realName, "IMPL_%s", name);
- SEL realSel = sel_registerName(realName);
- free(realName);
-
- IMP imp = class_getMethodImplementation(self, realSel);
- if (imp == &_objc_msgForward) return false;
- return class_addMethod(self, sel, imp, "");
-}
-
--(id)IMPL_idret:
-(vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- CHECK_ARGS(idret);
- state = 1;
- return ID_RESULT;
-}
-
--(long long)IMPL_llret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- CHECK_ARGS(llret);
- state = 2;
- return LL_RESULT;
-}
-
--(struct stret)IMPL_stret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- CHECK_ARGS(stret);
- state = 3;
- return STRET_RESULT;
-}
-
--(double)IMPL_fpret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- CHECK_ARGS(fpret);
- state = 4;
- return FP_RESULT;
-}
-
--(long double)IMPL_lfpret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- CHECK_ARGS(lfpret);
- state = 5;
- return LFP_RESULT;
-}
-
--(vector_ulong2)IMPL_vecret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- CHECK_ARGS(vecret);
- state = 6;
- return VEC_RESULT;
-}
-
-
--(id)idret_noarg
-{
- CHECK_ARGS_NOARG(idret);
- state = 11;
- return ID_RESULT;
-}
-
--(long long)llret_noarg
-{
- CHECK_ARGS_NOARG(llret);
- state = 12;
- return LL_RESULT;
-}
-
--(struct stret)stret_noarg
-{
- CHECK_ARGS_NOARG(stret);
- state = 13;
- return STRET_RESULT;
-}
-
--(double)fpret_noarg
-{
- CHECK_ARGS_NOARG(fpret);
- state = 14;
- return FP_RESULT;
-}
-
--(long double)lfpret_noarg
-{
- CHECK_ARGS_NOARG(lfpret);
- state = 15;
- return LFP_RESULT;
-}
-
--(vector_ulong2)vecret_noarg
-{
- CHECK_ARGS_NOARG(vecret);
- state = 16;
- return VEC_RESULT;
-}
-
-
--(void)voidret_nop
-{
- return;
-}
-
--(void)voidret_nop2
-{
- return;
-}
-
--(id)idret_nop
-{
- return ID_RESULT;
-}
-
--(long long)llret_nop
-{
- return LL_RESULT;
-}
-
--(struct stret)stret_nop
-{
- return STRET_RESULT;
-}
-
--(double)fpret_nop
-{
- return FP_RESULT;
-}
-
--(long double)lfpret_nop
-{
- return LFP_RESULT;
-}
-
--(vector_ulong2)vecret_nop
-{
- return VEC_RESULT;
-}
-
-#define STRET_IMP(n) \
-+(struct stret_##n)stret_##n##_zero \
-{ \
- struct stret_##n ret; \
- bzero(&ret, sizeof(ret)); \
- return ret; \
-} \
-+(struct stret_##n)stret_##n##_nonzero \
-{ \
- struct stret_##n ret; \
- memset(&ret, 0xff, sizeof(ret)); \
- return ret; \
-}
-
-STRET_IMP(i1)
-STRET_IMP(i2)
-STRET_IMP(i3)
-STRET_IMP(i4)
-STRET_IMP(i5)
-STRET_IMP(i6)
-STRET_IMP(i7)
-STRET_IMP(i8)
-STRET_IMP(i9)
-
-STRET_IMP(d1)
-STRET_IMP(d2)
-STRET_IMP(d3)
-STRET_IMP(d4)
-STRET_IMP(d5)
-STRET_IMP(d6)
-STRET_IMP(d7)
-STRET_IMP(d8)
-STRET_IMP(d9)
-
-
-+(id)idret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- fail("+idret called instead of -idret");
- CHECK_ARGS(idret);
-}
-
-+(long long)llret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- fail("+llret called instead of -llret");
- CHECK_ARGS(llret);
-}
-
-+(struct stret)stret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- fail("+stret called instead of -stret");
- CHECK_ARGS(stret);
-}
-
-+(double)fpret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- fail("+fpret called instead of -fpret");
- CHECK_ARGS(fpret);
-}
-
-+(long double)lfpret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- fail("+lfpret called instead of -lfpret");
- CHECK_ARGS(lfpret);
-}
-
-+(id)idret_noarg
-{
- fail("+idret_noarg called instead of -idret_noarg");
- CHECK_ARGS_NOARG(idret);
-}
-
-+(long long)llret_noarg
-{
- fail("+llret_noarg called instead of -llret_noarg");
- CHECK_ARGS_NOARG(llret);
-}
-
-+(struct stret)stret_noarg
-{
- fail("+stret_noarg called instead of -stret_noarg");
- CHECK_ARGS_NOARG(stret);
-}
-
-+(double)fpret_noarg
-{
- fail("+fpret_noarg called instead of -fpret_noarg");
- CHECK_ARGS_NOARG(fpret);
-}
-
-+(long double)lfpret_noarg
-{
- fail("+lfpret_noarg called instead of -lfpret_noarg");
- CHECK_ARGS_NOARG(lfpret);
-}
-
-+(vector_ulong2)vecret_noarg
-{
- fail("+vecret_noarg called instead of -vecret_noarg");
- CHECK_ARGS_NOARG(vecret);
-}
-
-@end
-
-
-@implementation Sub
-
--(id)IMPL_idret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- id result;
- CHECK_ARGS(idret);
- state = 100;
- result = [super idret:v1:v2:v3:v4:v5:v6:v7:v8:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
- testassert(state == 1);
- testassert(result == ID_RESULT);
- state = 101;
- return result;
-}
-
--(long long)IMPL_llret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- long long result;
- CHECK_ARGS(llret);
- state = 100;
- result = [super llret:v1:v2:v3:v4:v5:v6:v7:v8:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
- testassert(state == 2);
- testassert(result == LL_RESULT);
- state = 102;
- return result;
-}
-
--(struct stret)IMPL_stret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- struct stret result;
- CHECK_ARGS(stret);
- state = 100;
- result = [super stret:v1:v2:v3:v4:v5:v6:v7:v8:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
- testassert(state == 3);
- testassert(stret_equal(result, STRET_RESULT));
- state = 103;
- return result;
-}
-
--(double)IMPL_fpret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- double result;
- CHECK_ARGS(fpret);
- state = 100;
- result = [super fpret:v1:v2:v3:v4:v5:v6:v7:v8:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
- testassert(state == 4);
- testassert(result == FP_RESULT);
- state = 104;
- return result;
-}
-
--(long double)IMPL_lfpret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- long double result;
- CHECK_ARGS(lfpret);
- state = 100;
- result = [super lfpret:v1:v2:v3:v4:v5:v6:v7:v8:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
- testassert(state == 5);
- testassert(result == LFP_RESULT);
- state = 105;
- return result;
-}
-
--(vector_ulong2)IMPL_vecret:
- (vector_ulong2)v1 :(vector_ulong2)v2 :(vector_ulong2)v3 :(vector_ulong2)v4 :(vector_ulong2)v5 :(vector_ulong2)v6 :(vector_ulong2)v7 :(vector_ulong2)v8 :(int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15
-{
- vector_ulong2 result;
- CHECK_ARGS(vecret);
- state = 100;
- result = [super vecret:v1:v2:v3:v4:v5:v6:v7:v8:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15];
- testassert(state == 6);
- testassert(vector_equal(result, VEC_RESULT));
- state = 106;
- return result;
-}
-
-
--(id)idret_noarg
-{
- id result;
- CHECK_ARGS_NOARG(idret);
- state = 100;
- result = [super idret_noarg];
- testassert(state == 11);
- testassert(result == ID_RESULT);
- state = 111;
- return result;
-}
-
--(long long)llret_noarg
-{
- long long result;
- CHECK_ARGS_NOARG(llret);
- state = 100;
- result = [super llret_noarg];
- testassert(state == 12);
- testassert(result == LL_RESULT);
- state = 112;
- return result;
-}
-
--(struct stret)stret_noarg
-{
- struct stret result;
- CHECK_ARGS_NOARG(stret);
- state = 100;
- result = [super stret_noarg];
- testassert(state == 13);
- testassert(stret_equal(result, STRET_RESULT));
- state = 113;
- return result;
-}
-
--(double)fpret_noarg
-{
- double result;
- CHECK_ARGS_NOARG(fpret);
- state = 100;
- result = [super fpret_noarg];
- testassert(state == 14);
- testassert(result == FP_RESULT);
- state = 114;
- return result;
-}
-
--(long double)lfpret_noarg
-{
- long double result;
- CHECK_ARGS_NOARG(lfpret);
- state = 100;
- result = [super lfpret_noarg];
- testassert(state == 15);
- testassert(result == LFP_RESULT);
- state = 115;
- return result;
-}
-
--(vector_ulong2)vecret_noarg
-{
- vector_ulong2 result;
- CHECK_ARGS_NOARG(vecret);
- state = 100;
- result = [super vecret_noarg];
- testassert(state == 16);
- testassert(vector_equal(result, VEC_RESULT));
- state = 116;
- return result;
-}
-
-@end
-
-
-#if OBJC_HAVE_TAGGED_POINTERS
-
-@interface TaggedSub : Sub @end
-
-@implementation TaggedSub : Sub
-
-+(void)initialize
-{
- _objc_registerTaggedPointerClass(OBJC_TAG_7, self);
-}
-
-@end
-
-#endif
-
-
-// DWARF checking machinery
-
-#if TARGET_OS_WIN32
-// unimplemented on this platform
-#elif !__OBJC2__
-// 32-bit Mac doesn't use DWARF unwind
-#elif TARGET_OS_IPHONE && __arm__
-// 32-bit iOS device doesn't use DWARF unwind
-#elif __has_feature(objc_arc)
-// ARC's extra RR calls hit the traps at the wrong times
-#else
-
-#define TEST_DWARF 1
-
-// Classes with no implementations and no cache contents from elsewhere.
-@interface SuperDW : TestRoot @end
-@implementation SuperDW @end
-
-@interface Sub0DW : SuperDW @end
-@implementation Sub0DW @end
-
-@interface SubDW : Sub0DW @end
-@implementation SubDW @end
-
-#include <dlfcn.h>
-#include <signal.h>
-#include <sys/mman.h>
-#include <libunwind.h>
-
-#define UNW_STEP_SUCCESS 1
-#define UNW_STEP_END 0
-
-bool caught = false;
-uintptr_t clobbered;
-
-__BEGIN_DECLS
-extern void callit(void *obj, void *sel, void *fn);
-extern struct stret callit_stret(void *obj, void *sel, void *fn);
-__END_DECLS
-
-#if __x86_64__
-
-#define OTOOL "/usr/bin/xcrun otool -arch x86_64 "
-
-typedef uint8_t insn_t;
-#define BREAK_INSN ((insn_t)0xcc) // int3
-
-uintptr_t r12 = 0;
-uintptr_t r13 = 0;
-uintptr_t r14 = 0;
-uintptr_t r15 = 0;
-uintptr_t rbx = 0;
-uintptr_t rbp = 0;
-uintptr_t rsp = 0;
-uintptr_t rip = 0;
-
-void handle_exception(x86_thread_state64_t *state)
-{
- unw_cursor_t curs;
- unw_word_t reg;
- int err;
- int step;
-
- err = unw_init_local(&curs, (unw_context_t *)state);
- testassert(!err);
-
- step = unw_step(&curs);
- testassert(step == UNW_STEP_SUCCESS);
-
- err = unw_get_reg(&curs, UNW_X86_64_R12, ®);
- testassert(!err);
- testassert(reg == r12);
-
- err = unw_get_reg(&curs, UNW_X86_64_R13, ®);
- testassert(!err);
- testassert(reg == r13);
-
- err = unw_get_reg(&curs, UNW_X86_64_R14, ®);
- testassert(!err);
- testassert(reg == r14);
-
- err = unw_get_reg(&curs, UNW_X86_64_R15, ®);
- testassert(!err);
- testassert(reg == r15);
-
- err = unw_get_reg(&curs, UNW_X86_64_RBX, ®);
- testassert(!err);
- testassert(reg == rbx);
-
- err = unw_get_reg(&curs, UNW_X86_64_RBP, ®);
- testassert(!err);
- testassert(reg == rbp);
-
- err = unw_get_reg(&curs, UNW_X86_64_RSP, ®);
- testassert(!err);
- testassert(reg == rsp);
-
- err = unw_get_reg(&curs, UNW_REG_IP, ®);
- testassert(!err);
- testassert(reg == rip);
-
-
- // set thread state to unwound state
- state->__r12 = r12;
- state->__r13 = r13;
- state->__r14 = r14;
- state->__r15 = r15;
- state->__rbx = rbx;
- state->__rbp = rbp;
- state->__rsp = rsp;
- state->__rip = rip;
-
- caught = true;
-}
-
-
-void sigtrap(int sig, siginfo_t *info, void *cc)
-{
- ucontext_t *uc = (ucontext_t *)cc;
- mcontext_t mc = (mcontext_t)uc->uc_mcontext;
-
- testprintf(" handled\n");
-
- testassert(sig == SIGTRAP);
- testassert((uintptr_t)info->si_addr-1 == clobbered);
-
- handle_exception(&mc->__ss);
- // handle_exception changed register state for continuation
-}
-
-__asm__(
-"\n .text"
-"\n .globl _callit"
-"\n _callit:"
-// save sp and return address to variables
-"\n movq (%rsp), %r10"
-"\n movq %r10, _rip(%rip)"
-"\n movq %rsp, _rsp(%rip)"
-"\n addq $8, _rsp(%rip)" // rewind to pre-call value
-// save other non-volatile registers to variables
-"\n movq %rbx, _rbx(%rip)"
-"\n movq %rbp, _rbp(%rip)"
-"\n movq %r12, _r12(%rip)"
-"\n movq %r13, _r13(%rip)"
-"\n movq %r14, _r14(%rip)"
-"\n movq %r15, _r15(%rip)"
-"\n jmpq *%rdx"
- );
-
-__asm__(
-"\n .text"
-"\n .globl _callit_stret"
-"\n _callit_stret:"
-// save sp and return address to variables
-"\n movq (%rsp), %r10"
-"\n movq %r10, _rip(%rip)"
-"\n movq %rsp, _rsp(%rip)"
-"\n addq $8, _rsp(%rip)" // rewind to pre-call value
-// save other non-volatile registers to variables
-"\n movq %rbx, _rbx(%rip)"
-"\n movq %rbp, _rbp(%rip)"
-"\n movq %r12, _r12(%rip)"
-"\n movq %r13, _r13(%rip)"
-"\n movq %r14, _r14(%rip)"
-"\n movq %r15, _r15(%rip)"
-"\n jmpq *%rcx"
- );
-
-
-// x86_64
-
-#elif __i386__
-
-#define OTOOL "/usr/bin/xcrun otool -arch i386 "
-
-typedef uint8_t insn_t;
-#define BREAK_INSN ((insn_t)0xcc) // int3
-
-uintptr_t eip = 0;
-uintptr_t esp = 0;
-uintptr_t ebx = 0;
-uintptr_t ebp = 0;
-uintptr_t edi = 0;
-uintptr_t esi = 0;
-uintptr_t espfix = 0;
-
-void handle_exception(i386_thread_state_t *state)
-{
- unw_cursor_t curs;
- unw_word_t reg;
- int err;
- int step;
-
- err = unw_init_local(&curs, (unw_context_t *)state);
- testassert(!err);
-
- step = unw_step(&curs);
- testassert(step == UNW_STEP_SUCCESS);
-
- err = unw_get_reg(&curs, UNW_REG_IP, ®);
- testassert(!err);
- testassert(reg == eip);
-
- err = unw_get_reg(&curs, UNW_X86_ESP, ®);
- testassert(!err);
- testassert(reg == esp);
-
- err = unw_get_reg(&curs, UNW_X86_EBX, ®);
- testassert(!err);
- testassert(reg == ebx);
-
- err = unw_get_reg(&curs, UNW_X86_EBP, ®);
- testassert(!err);
- testassert(reg == ebp);
-
- err = unw_get_reg(&curs, UNW_X86_EDI, ®);
- testassert(!err);
- testassert(reg == edi);
-
- err = unw_get_reg(&curs, UNW_X86_ESI, ®);
- testassert(!err);
- testassert(reg == esi);
-
-
- // set thread state to unwound state
- state->__eip = eip;
- state->__esp = esp + espfix;
- state->__ebx = ebx;
- state->__ebp = ebp;
- state->__edi = edi;
- state->__esi = esi;
-
- caught = true;
-}
-
-
-void sigtrap(int sig, siginfo_t *info, void *cc)
-{
- ucontext_t *uc = (ucontext_t *)cc;
- mcontext_t mc = (mcontext_t)uc->uc_mcontext;
-
- testprintf(" handled\n");
-
- testassert(sig == SIGTRAP);
- testassert((uintptr_t)info->si_addr-1 == clobbered);
-
- handle_exception(&mc->__ss);
- // handle_exception changed register state for continuation
-}
-
-__asm__(
-"\n .text"
-"\n .globl _callit"
-"\n _callit:"
-// save sp and return address to variables
-"\n call 1f"
-"\n 1: popl %edx"
-"\n movl (%esp), %eax"
-"\n movl %eax, _eip-1b(%edx)"
-"\n movl %esp, _esp-1b(%edx)"
-"\n addl $4, _esp-1b(%edx)" // rewind to pre-call value
-"\n movl $0, _espfix-1b(%edx)"
-// save other non-volatile registers to variables
-"\n movl %ebx, _ebx-1b(%edx)"
-"\n movl %ebp, _ebp-1b(%edx)"
-"\n movl %edi, _edi-1b(%edx)"
-"\n movl %esi, _esi-1b(%edx)"
-"\n jmpl *12(%esp)"
- );
-
-__asm__(
-"\n .text"
-"\n .globl _callit_stret"
-"\n _callit_stret:"
-// save sp and return address to variables
-"\n call 1f"
-"\n 1: popl %edx"
-"\n movl (%esp), %eax"
-"\n movl %eax, _eip-1b(%edx)"
-"\n movl %esp, _esp-1b(%edx)"
-"\n addl $4, _esp-1b(%edx)" // rewind to pre-call value
-"\n movl $4, _espfix-1b(%edx)"
-// save other non-volatile registers to variables
-"\n movl %ebx, _ebx-1b(%edx)"
-"\n movl %ebp, _ebp-1b(%edx)"
-"\n movl %edi, _edi-1b(%edx)"
-"\n movl %esi, _esi-1b(%edx)"
-"\n jmpl *16(%esp)"
- );
-
-
-// i386
-#elif __arm64__
-
-#include <sys/ucontext.h>
-
-// runs on iOS device, no xcrun command present
-#define OTOOL "/usr/bin/otool -arch arm64 "
-
-typedef uint32_t insn_t;
-#define BREAK_INSN ((insn_t)0xd4200020) // brk #1
-
-uintptr_t x19 = 0;
-uintptr_t x20 = 0;
-uintptr_t x21 = 0;
-uintptr_t x22 = 0;
-uintptr_t x23 = 0;
-uintptr_t x24 = 0;
-uintptr_t x25 = 0;
-uintptr_t x26 = 0;
-uintptr_t x27 = 0;
-uintptr_t x28 = 0;
-uintptr_t fp = 0;
-uintptr_t sp = 0;
-uintptr_t pc = 0;
-
-void handle_exception(arm_thread_state64_t *state)
-{
- unw_cursor_t curs;
- unw_word_t reg;
- int err;
- int step;
-
- err = unw_init_local(&curs, (unw_context_t *)state);
- testassert(!err);
-
- step = unw_step(&curs);
- testassert(step == UNW_STEP_SUCCESS);
-
- err = unw_get_reg(&curs, UNW_ARM64_X19, ®);
- testassert(!err);
- testassert(reg == x19);
-
- err = unw_get_reg(&curs, UNW_ARM64_X20, ®);
- testassert(!err);
- testassert(reg == x20);
-
- err = unw_get_reg(&curs, UNW_ARM64_X21, ®);
- testassert(!err);
- testassert(reg == x21);
-
- err = unw_get_reg(&curs, UNW_ARM64_X22, ®);
- testassert(!err);
- testassert(reg == x22);
-
- err = unw_get_reg(&curs, UNW_ARM64_X23, ®);
- testassert(!err);
- testassert(reg == x23);
-
- err = unw_get_reg(&curs, UNW_ARM64_X24, ®);
- testassert(!err);
- testassert(reg == x24);
-
- err = unw_get_reg(&curs, UNW_ARM64_X25, ®);
- testassert(!err);
- testassert(reg == x25);
-
- err = unw_get_reg(&curs, UNW_ARM64_X26, ®);
- testassert(!err);
- testassert(reg == x26);
-
- err = unw_get_reg(&curs, UNW_ARM64_X27, ®);
- testassert(!err);
- testassert(reg == x27);
-
- err = unw_get_reg(&curs, UNW_ARM64_X28, ®);
- testassert(!err);
- testassert(reg == x28);
-
- err = unw_get_reg(&curs, UNW_ARM64_FP, ®);
- testassert(!err);
- testassert(reg == fp);
-
- err = unw_get_reg(&curs, UNW_ARM64_SP, ®);
- testassert(!err);
- testassert(reg == sp);
-
- err = unw_get_reg(&curs, UNW_REG_IP, ®);
- testassert(!err);
- testassert(reg == pc);
-
- // libunwind restores PC into LR and doesn't track LR
- // err = unw_get_reg(&curs, UNW_ARM64_LR, ®);
- // testassert(!err);
- // testassert(reg == lr);
-
- // set thread state to unwound state
- state->__x[19] = x19;
- state->__x[20] = x20;
- state->__x[20] = x21;
- state->__x[22] = x22;
- state->__x[23] = x23;
- state->__x[24] = x24;
- state->__x[25] = x25;
- state->__x[26] = x26;
- state->__x[27] = x27;
- state->__x[28] = x28;
- state->__fp = fp;
- state->__lr = pc; // libunwind restores PC into LR
- state->__sp = sp;
- state->__pc = pc;
-
- caught = true;
-}
-
-
-void sigtrap(int sig, siginfo_t *info, void *cc)
-{
- ucontext_t *uc = (ucontext_t *)cc;
- struct __darwin_mcontext64 *mc = (struct __darwin_mcontext64 *)uc->uc_mcontext;
-
- testprintf(" handled\n");
-
- testassert(sig == SIGTRAP);
- testassert((uintptr_t)info->si_addr == clobbered);
-
- handle_exception(&mc->__ss);
- // handle_exception changed register state for continuation
-}
-
-
-__asm__(
-"\n .text"
-"\n .globl _callit"
-"\n _callit:"
-// save sp and return address to variables
-"\n mov x16, sp"
-"\n adrp x17, _sp@PAGE"
-"\n str x16, [x17, _sp@PAGEOFF]"
-"\n adrp x17, _pc@PAGE"
-"\n str lr, [x17, _pc@PAGEOFF]"
-// save other non-volatile registers to variables
-"\n adrp x17, _x19@PAGE"
-"\n str x19, [x17, _x19@PAGEOFF]"
-"\n adrp x17, _x19@PAGE"
-"\n str x20, [x17, _x20@PAGEOFF]"
-"\n adrp x17, _x19@PAGE"
-"\n str x21, [x17, _x21@PAGEOFF]"
-"\n adrp x17, _x19@PAGE"
-"\n str x22, [x17, _x22@PAGEOFF]"
-"\n adrp x17, _x19@PAGE"
-"\n str x23, [x17, _x23@PAGEOFF]"
-"\n adrp x17, _x19@PAGE"
-"\n str x24, [x17, _x24@PAGEOFF]"
-"\n adrp x17, _x19@PAGE"
-"\n str x25, [x17, _x25@PAGEOFF]"
-"\n adrp x17, _x19@PAGE"
-"\n str x26, [x17, _x26@PAGEOFF]"
-"\n adrp x17, _x19@PAGE"
-"\n str x27, [x17, _x27@PAGEOFF]"
-"\n adrp x17, _x19@PAGE"
-"\n str x28, [x17, _x28@PAGEOFF]"
-"\n adrp x17, _x19@PAGE"
-"\n str fp, [x17, _fp@PAGEOFF]"
-"\n br x2"
- );
-
-
-// arm64
-#else
-
-#error unknown architecture
-
-#endif
-
-
-insn_t set(uintptr_t dst, insn_t newvalue)
-{
- uintptr_t start = dst & ~(PAGE_MAX_SIZE-1);
- mprotect((void*)start, PAGE_MAX_SIZE, PROT_READ|PROT_WRITE);
- insn_t oldvalue = *(insn_t *)dst;
- *(insn_t *)dst = newvalue;
- mprotect((void*)start, PAGE_MAX_SIZE, PROT_READ|PROT_EXEC);
- return oldvalue;
-}
-
-insn_t clobber(void *fn, uintptr_t offset)
-{
- clobbered = (uintptr_t)fn + offset;
- return set((uintptr_t)fn + offset, BREAK_INSN);
-}
-
-void unclobber(void *fn, uintptr_t offset, insn_t oldvalue)
-{
- set((uintptr_t)fn + offset, oldvalue);
-}
-
-
-uintptr_t *getOffsets(void *symbol, const char *symname, uintptr_t *outBase)
-{
- uintptr_t *result = (uintptr_t *)malloc(1000 * sizeof(uintptr_t));
- uintptr_t *end = result + 1000;
- uintptr_t *p = result;
-
- // find library
- Dl_info dl;
- dladdr(symbol, &dl);
-
- // call `otool` on library
- unsetenv("DYLD_LIBRARY_PATH");
- unsetenv("DYLD_ROOT_PATH");
- unsetenv("DYLD_INSERT_LIBRARIES");
- unsetenv("DYLD_SHARED_REGION");
- unsetenv("DYLD_SHARED_CACHE_DIR");
- unsetenv("DYLD_SHARED_CACHE_DONT_VALIDATE");
- char *cmd;
- asprintf(&cmd, OTOOL "-tv -p _%s %s",
- symname, dl.dli_fname);
- testprintf("%s\n", cmd);
- FILE *disa = popen(cmd, "r");
- free(cmd);
- testassert(disa);
-
- // read past "_symname:" line
- char *line;
- size_t len;
- while ((line = fgetln(disa, &len))) {
- if (0 == strncmp(1+line, symname, MIN(len-1, strlen(symname)))) break;
- }
-
- // read instructions and save offsets
- char op[128];
- long base = 0;
- long addr;
- while (2 == fscanf(disa, "%lx%s%*[^\n]\n", &addr, op)) {
- if (base == 0) base = addr;
- if (0 != strncmp(op, "nop", 3)) {
- testassert(p < end);
- *p++ = addr - base;
- } else {
- // assume nops are unreached (e.g. alignment padding)
- }
- }
- pclose(disa);
-
-#if __arm64__
- // Also add breakpoints in _objc_msgSend_uncached_impcache
- // (which is the slow path and has a frame to unwind)
- if (0 != strcmp(symname, "_objc_msgSend_uncached_impcache")) {
- uintptr_t base2;
- uintptr_t *more_offsets = getOffsets(symbol, "_objc_msgSend_uncached_impcache", &base2);
- uintptr_t *q = more_offsets;
- // Skip prologue because it's imprecisely modeled in compact unwind
- testassert(*q != ~0UL);
- q++;
- testassert(*q != ~0UL);
- q++;
- while (*q != ~0UL) *p++ = *q++ + base2 - base;
- // Skip return because it's imprecisely modeled in compact unwind
- p--;
- free(more_offsets);
- }
-#endif
-
- testassert(p > result);
- testassert(p < end);
- *p = ~0UL;
-#if __x86_64__
- // hack: skip last instruction because libunwind blows up if it's
- // one byte long and followed by the next function with no NOPs first
- p[-1] = ~0UL;
-#endif
- if (outBase) *outBase = base;
- return result;
-}
-
-void CALLIT(void *o, void *sel_arg, SEL s, void *f, bool stret) __attribute__((noinline));
-void CALLIT(void *o, void *sel_arg, SEL s, void *f, bool stret)
-{
- uintptr_t message_ref[2];
- if (sel_arg != s) {
- // fixup dispatch
- // copy to a local buffer to keep sel_arg un-fixed-up
- memcpy(message_ref, sel_arg, sizeof(message_ref));
- sel_arg = message_ref;
- }
- if (!stret) callit(o, sel_arg, f);
-#if SUPPORT_STRET
- else callit_stret(o, sel_arg, f);
-#else
- else fail("stret?");
-#endif
-}
-
-void test_dw_forward(void)
-{
- return;
-}
-
-struct stret test_dw_forward_stret(void)
-{
- return zero;
-}
-
-// sub = ordinary receiver object
-// tagged = tagged receiver object
-// SEL = selector to send
-// sub_arg = arg to pass in receiver register (may be objc_super struct)
-// tagged_arg = arg to pass in receiver register (may be objc_super struct)
-// sel_arg = arg to pass in sel register (may be message_ref)
-// uncaughtAllowed is the number of acceptable unreachable instructions
-// (for example, the ones that handle the corrupt-cache-error case)
-void test_dw(const char *name, id sub, id tagged, bool stret,
- int uncaughtAllowed)
-{
-
- testprintf("DWARF FOR %s%s\n", name, stret ? " (stret)" : "");
-
- // We need 2 SELs of each alignment so we can generate hash collisions.
- // sel_registerName() never returns those alignments because they
- // differ from malloc's alignment. So we create lots of compiled-in
- // SELs here and hope something fits.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wundeclared-selector"
- SEL sel = @selector(a);
- SEL lotsOfSels[] = {
- @selector(a1), @selector(a2), @selector(a3), @selector(a4),
- @selector(a5), @selector(a6), @selector(a7), @selector(a8),
- @selector(aa), @selector(ab), @selector(ac), @selector(ad),
- @selector(ae), @selector(af), @selector(ag), @selector(ah),
- @selector(A1), @selector(A2), @selector(A3), @selector(A4),
- @selector(A5), @selector(A6), @selector(A7), @selector(A8),
- @selector(AA), @selector(Ab), @selector(Ac), @selector(Ad),
- @selector(Ae), @selector(Af), @selector(Ag), @selector(Ah),
- @selector(bb1), @selector(bb2), @selector(bb3), @selector(bb4),
- @selector(bb5), @selector(bb6), @selector(bb7), @selector(bb8),
- @selector(bba), @selector(bbb), @selector(bbc), @selector(bbd),
- @selector(bbe), @selector(bbf), @selector(bbg), @selector(bbh),
- @selector(BB1), @selector(BB2), @selector(BB3), @selector(BB4),
- @selector(BB5), @selector(BB6), @selector(BB7), @selector(BB8),
- @selector(BBa), @selector(BBb), @selector(BBc), @selector(BBd),
- @selector(BBe), @selector(BBf), @selector(BBg), @selector(BBh),
- @selector(ccc1), @selector(ccc2), @selector(ccc3), @selector(ccc4),
- @selector(ccc5), @selector(ccc6), @selector(ccc7), @selector(ccc8),
- @selector(ccca), @selector(cccb), @selector(cccc), @selector(cccd),
- @selector(ccce), @selector(cccf), @selector(cccg), @selector(ccch),
- @selector(CCC1), @selector(CCC2), @selector(CCC3), @selector(CCC4),
- @selector(CCC5), @selector(CCC6), @selector(CCC7), @selector(CCC8),
- @selector(CCCa), @selector(CCCb), @selector(CCCc), @selector(CCCd),
- @selector(CCCe), @selector(CCCf), @selector(CCCg), @selector(CCCh),
- };
-#pragma clang diagnostic pop
-
- {
- IMP imp = stret ? (IMP)test_dw_forward_stret : (IMP)test_dw_forward;
- Class cls = object_getClass(sub);
- Class tagcls = object_getClass(tagged);
- class_replaceMethod(cls, sel, imp, "");
- class_replaceMethod(tagcls, sel, imp, "");
- for (size_t i = 0; i < sizeof(lotsOfSels)/sizeof(lotsOfSels[0]); i++) {
- class_replaceMethod(cls, lotsOfSels[i], imp, "");
- class_replaceMethod(tagcls, lotsOfSels[i], imp, "");
- }
- }
-
- #define ALIGNCOUNT 16
- SEL sels[ALIGNCOUNT][2] = {{0}};
- for (int align = 0; align < ALIGNCOUNT; align++) {
- for (size_t i = 0; i < sizeof(lotsOfSels)/sizeof(lotsOfSels[0]); i++) {
- if ((uintptr_t)(void*)lotsOfSels[i] % ALIGNCOUNT == align) {
- if (sels[align][0]) {
- sels[align][1] = lotsOfSels[i];
- } else {
- sels[align][0] = lotsOfSels[i];
- }
- }
- }
- if (!sels[align][0]) fail("no SEL with alignment %d", align);
- if (!sels[align][1]) fail("only one SEL with alignment %d", align);
- }
-
- void *fn = dlsym(RTLD_DEFAULT, name);
- testassert(fn);
-
- // argument substitutions
-
- void *sub_arg = (void*)objc_unretainedPointer(sub);
- void *tagged_arg = (void*)objc_unretainedPointer(tagged);
- void *sel_arg = (void*)sel;
-
- struct objc_super sup_st = { sub, object_getClass(sub) };
- struct objc_super tagged_sup_st = { tagged, object_getClass(tagged) };
- struct { void *imp; SEL sel; } message_ref = { fn, sel };
-
- Class cache_cls = object_getClass(sub);
-
- if (strstr(name, "Super")) {
- // super version - replace receiver with objc_super
- // clear caches of superclass
- cache_cls = class_getSuperclass(cache_cls);
- sub_arg = &sup_st;
- tagged_arg = &tagged_sup_st;
- }
-
- if (strstr(name, "_fixup")) {
- // fixup version - replace sel with message_ref
- sel_arg = &message_ref;
- }
-
-
- uintptr_t *insnOffsets = getOffsets(fn, name, nil);
- uintptr_t offset;
- int uncaughtCount = 0;
- for (int oo = 0; insnOffsets[oo] != ~0UL; oo++) {
- offset = insnOffsets[oo];
- testprintf("OFFSET %lu\n", offset);
-
- insn_t saved_insn = clobber(fn, offset);
- caught = false;
-
- // nil
- if ((void*)objc_unretainedPointer(sub) == sub_arg) {
- SELF = nil;
- testprintf(" nil\n");
- CALLIT(nil, sel_arg, sel, fn, stret);
- CALLIT(nil, sel_arg, sel, fn, stret);
- }
-
- // uncached
- SELF = sub;
- testprintf(" uncached\n");
- _objc_flush_caches(cache_cls);
- CALLIT(sub_arg, sel_arg, sel, fn, stret);
- _objc_flush_caches(cache_cls);
- CALLIT(sub_arg, sel_arg, sel, fn, stret);
-
- // cached
- SELF = sub;
- testprintf(" cached\n");
- CALLIT(sub_arg, sel_arg, sel, fn, stret);
- CALLIT(sub_arg, sel_arg, sel, fn, stret);
-
- // uncached,tagged
- SELF = tagged;
- testprintf(" uncached,tagged\n");
- _objc_flush_caches(cache_cls);
- CALLIT(tagged_arg, sel_arg, sel, fn, stret);
- _objc_flush_caches(cache_cls);
- CALLIT(tagged_arg, sel_arg, sel, fn, stret);
-
- // cached,tagged
- SELF = tagged;
- testprintf(" cached,tagged\n");
- CALLIT(tagged_arg, sel_arg, sel, fn, stret);
- CALLIT(tagged_arg, sel_arg, sel, fn, stret);
-
- // multiple SEL alignments, collisions, wraps
- SELF = sub;
- for (int a = 0; a < ALIGNCOUNT; a++) {
- testprintf(" cached, SEL alignment %d\n", a);
-
- // Count both up and down to be independent of
- // implementation's cache scan direction
-
- _objc_flush_caches(cache_cls);
- for (int x2 = 0; x2 < 8; x2++) {
- for (int s = 0; s < 4; s++) {
- int align = (a+s) % ALIGNCOUNT;
- CALLIT(sub_arg, sels[align][0], sels[align][0], fn, stret);
- CALLIT(sub_arg, sels[align][1], sels[align][1], fn, stret);
- }
- }
-
- _objc_flush_caches(cache_cls);
- for (int x2 = 0; x2 < 8; x2++) {
- for (int s = 0; s < 4; s++) {
- int align = abs(a-s) % ALIGNCOUNT;
- CALLIT(sub_arg, sels[align][0], sels[align][0], fn, stret);
- CALLIT(sub_arg, sels[align][1], sels[align][1], fn, stret);
- }
- }
- }
-
- unclobber(fn, offset, saved_insn);
-
- // remember offsets that were caught by none of the above
- if (caught) {
- insnOffsets[oo] = 0;
- } else {
- uncaughtCount++;
- testprintf("offset %s+%lu not caught (%d/%d)\n",
- name, offset, uncaughtCount, uncaughtAllowed);
- }
- }
-
- // Complain if too many offsets went uncaught.
- // Acceptably-uncaught offsets include the corrupt-cache-error handler.
- if (uncaughtCount != uncaughtAllowed) {
- for (int oo = 0; insnOffsets[oo] != ~0UL; oo++) {
- if (insnOffsets[oo]) {
- fprintf(stderr, "BAD: offset %s+%lu not caught\n",
- name, insnOffsets[oo]);
- }
- }
- fail("wrong instructions not reached for %s (missed %d, expected %d)",
- name, uncaughtCount, uncaughtAllowed);
- }
-
- free(insnOffsets);
-}
-
-
-// TEST_DWARF
-#endif
-
-
-void test_basic(id receiver)
-{
- id idval;
- long long llval;
- struct stret stretval;
- double fpval;
- long double lfpval;
- vector_ulong2 vecval;
-
- // message uncached
- // message uncached long long
- // message uncached stret
- // message uncached fpret
- // message uncached fpret long double
- // message uncached noarg (as above)
- // message cached
- // message cached long long
- // message cached stret
- // message cached fpret
- // message cached fpret long double
- // message cached noarg (as above)
- // fixme verify that uncached lookup didn't happen the 2nd time?
- SELF = receiver;
- for (int i = 0; i < 5; i++) {
- testprintf("idret\n");
- state = 0;
- idval = nil;
- idval = [receiver idret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 101);
- testassert(idval == ID_RESULT);
-
- testprintf("llret\n");
- llval = 0;
- llval = [receiver llret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 102);
- testassert(llval == LL_RESULT);
-
- testprintf("stret\n");
- stretval = zero;
- stretval = [receiver stret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 103);
- testassert(stret_equal(stretval, STRET_RESULT));
-
- testprintf("fpret\n");
- fpval = 0;
- fpval = [receiver fpret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 104);
- testassert(fpval == FP_RESULT);
-
- testprintf("lfpret\n");
- lfpval = 0;
- lfpval = [receiver lfpret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 105);
- testassert(lfpval == LFP_RESULT);
-
- testprintf("vecret\n");
- vecval = 0;
- vecval = [receiver vecret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 106);
- testassert(vector_equal(vecval, VEC_RESULT));
-
-#if __OBJC2__
- // explicitly call noarg messenger, even if compiler doesn't emit it
- state = 0;
- testprintf("idret noarg\n");
- idval = nil;
- idval = ((typeof(idmsg0))objc_msgSend_noarg)(receiver, @selector(idret_noarg));
- testassert(state == 111);
- testassert(idval == ID_RESULT);
-
- testprintf("llret noarg\n");
- llval = 0;
- llval = ((typeof(llmsg0))objc_msgSend_noarg)(receiver, @selector(llret_noarg));
- testassert(state == 112);
- testassert(llval == LL_RESULT);
- /*
- no objc_msgSend_stret_noarg
- stretval = zero;
- stretval = ((typeof(stretmsg0))objc_msgSend_stret_noarg)(receiver, @selector(stret_noarg));
- stretval = [receiver stret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 113);
- testassert(stret_equal(stretval, STRET_RESULT));
- */
-# if !__i386__
- testprintf("fpret noarg\n");
- fpval = 0;
- fpval = ((typeof(fpmsg0))objc_msgSend_noarg)(receiver, @selector(fpret_noarg));
- testassert(state == 114);
- testassert(fpval == FP_RESULT);
-
- testprintf("vecret noarg\n");
- vecval = 0;
- vecval = ((typeof(vecmsg0))objc_msgSend_noarg)(receiver, @selector(vecret_noarg));
- testassert(state == 116);
- testassert(vector_equal(vecval, VEC_RESULT));
-# endif
-# if !__i386__ && !__x86_64__
- testprintf("lfpret noarg\n");
- lfpval = 0;
- lfpval = ((typeof(lfpmsg0))objc_msgSend_noarg)(receiver, @selector(lfpret_noarg));
- testassert(state == 115);
- testassert(lfpval == LFP_RESULT);
-# endif
-#endif
- }
-
- testprintf("basic done\n");
-}
-
-int main()
-{
- PUSH_POOL {
- int i;
-
- id idval;
- long long llval;
- struct stret stretval;
- double fpval;
- long double lfpval;
- vector_ulong2 vecval;
-
-#if __x86_64__
- struct stret *stretptr;
-#endif
-
- uint64_t startTime;
- uint64_t totalTime;
- uint64_t targetTime;
-
- Method idmethod;
- Method llmethod;
- Method stretmethod;
- Method fpmethod;
- Method lfpmethod;
- Method vecmethod;
-
- id (*idfn)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
- long long (*llfn)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
- struct stret (*stretfn)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
- double (*fpfn)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
- long double (*lfpfn)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
- vector_ulong2 (*vecfn)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double);
-
- id (*idmsg)(id, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
- id (*idmsgsuper)(struct objc_super *, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
- long long (*llmsg)(id, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
- struct stret (*stretmsg)(id, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
- struct stret (*stretmsgsuper)(struct objc_super *, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
- double (*fpmsg)(id, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
- long double (*lfpmsg)(id, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
- vector_ulong2 (*vecmsg)(id, SEL, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused));
-
- // get +initialize out of the way
- [Sub class];
-#if OBJC_HAVE_TAGGED_POINTERS
- [TaggedSub class];
-#endif
-
- ID_RESULT = [Super new];
-
- Sub *sub = [Sub new];
- Super *sup = [Super new];
-#if OBJC_HAVE_TAGGED_POINTERS
- TaggedSub *tagged = objc_unretainedObject(_objc_makeTaggedPointer(OBJC_TAG_7, 999));
-#endif
-
- // Basic cached and uncached dispatch.
- // Do this first before anything below caches stuff.
- testprintf("basic\n");
- test_basic(sub);
-#if OBJC_HAVE_TAGGED_POINTERS
- testprintf("basic tagged\n");
- test_basic(tagged);
-#endif
-
- idmethod = class_getInstanceMethod([Super class], @selector(idret::::::::::::::::::::::::::::::::::::));
- testassert(idmethod);
- llmethod = class_getInstanceMethod([Super class], @selector(llret::::::::::::::::::::::::::::::::::::));
- testassert(llmethod);
- stretmethod = class_getInstanceMethod([Super class], @selector(stret::::::::::::::::::::::::::::::::::::));
- testassert(stretmethod);
- fpmethod = class_getInstanceMethod([Super class], @selector(fpret::::::::::::::::::::::::::::::::::::));
- testassert(fpmethod);
- lfpmethod = class_getInstanceMethod([Super class], @selector(lfpret::::::::::::::::::::::::::::::::::::));
- testassert(lfpmethod);
- vecmethod = class_getInstanceMethod([Super class], @selector(vecret::::::::::::::::::::::::::::::::::::));
- testassert(vecmethod);
-
- idfn = (id (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
- llfn = (long long (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
- stretfn = (struct stret (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke_stret;
- fpfn = (double (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
- lfpfn = (long double (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
- vecfn = (vector_ulong2 (*)(id, Method, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, vector_ulong2, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke;
-
- // cached message performance
- // catches failure to cache or (abi=2) failure to fixup (#5584187)
- // fixme unless they all fail
- // `.align 4` matches loop alignment to make -O0 work
- // fill cache first
- testprintf("time checks\n");
-
- SELF = sub;
- [sub voidret_nop];
- [sub voidret_nop2];
- [sub llret_nop];
- [sub stret_nop];
- [sub fpret_nop];
- [sub lfpret_nop];
- [sub vecret_nop];
- [sub voidret_nop];
- [sub voidret_nop2];
- [sub llret_nop];
- [sub stret_nop];
- [sub fpret_nop];
- [sub lfpret_nop];
- [sub vecret_nop];
- [sub voidret_nop];
- [sub voidret_nop2];
- [sub llret_nop];
- [sub stret_nop];
- [sub fpret_nop];
- [sub lfpret_nop];
- [sub vecret_nop];
-
- // Some of these times have high variance on some compilers.
- // The errors we're trying to catch should be catastrophically slow,
- // so the margins here are generous to avoid false failures.
-
- // Use voidret because id return is too slow for perf test with ARC.
-
- // Pick smallest of voidret_nop and voidret_nop2 time
- // in the hopes that one of them didn't collide in the method cache.
-
-#define COUNT 1000000
-
- startTime = mach_absolute_time();
- ALIGN_();
- for (i = 0; i < COUNT; i++) {
- [sub voidret_nop];
- }
- totalTime = mach_absolute_time() - startTime;
- testprintf("time: voidret %llu\n", totalTime);
- targetTime = totalTime;
-
- startTime = mach_absolute_time();
- ALIGN_();
- for (i = 0; i < COUNT; i++) {
- [sub voidret_nop2];
- }
- totalTime = mach_absolute_time() - startTime;
- testprintf("time: voidret2 %llu\n", totalTime);
- if (totalTime < targetTime) targetTime = totalTime;
-
- startTime = mach_absolute_time();
- ALIGN_();
- for (i = 0; i < COUNT; i++) {
- [sub llret_nop];
- }
- totalTime = mach_absolute_time() - startTime;
- timecheck("llret ", totalTime, targetTime * 0.7, targetTime * 2.0);
-
- startTime = mach_absolute_time();
- ALIGN_();
- for (i = 0; i < COUNT; i++) {
- [sub stret_nop];
- }
- totalTime = mach_absolute_time() - startTime;
- timecheck("stret ", totalTime, targetTime * 0.7, targetTime * 5.0);
-
- startTime = mach_absolute_time();
- ALIGN_();
- for (i = 0; i < COUNT; i++) {
- [sub fpret_nop];
- }
- totalTime = mach_absolute_time() - startTime;
- timecheck("fpret ", totalTime, targetTime * 0.7, targetTime * 4.0);
-
- startTime = mach_absolute_time();
- ALIGN_();
- for (i = 0; i < COUNT; i++) {
- [sub lfpret_nop];
- }
- totalTime = mach_absolute_time() - startTime;
- timecheck("lfpret", totalTime, targetTime * 0.7, targetTime * 4.0);
-
- startTime = mach_absolute_time();
- ALIGN_();
- for (i = 0; i < COUNT; i++) {
- [sub vecret_nop];
- }
- totalTime = mach_absolute_time() - startTime;
- timecheck("vecret", totalTime, targetTime * 0.7, targetTime * 4.0);
-
-#if __arm64__
- // Removing this testwarn(), or changing voidret_nop to nop;ret,
- // changes the voidret_nop and stret_nop times above by a factor of 2.
- testwarn("rdar://13896922 nop;ret is faster than ret?");
-#endif
-
-#undef COUNT
-
- // method_invoke
- // method_invoke long long
- // method_invoke_stret stret
- // method_invoke_stret fpret
- // method_invoke fpret long double
- testprintf("method_invoke\n");
-
- SELF = sup;
-
- state = 0;
- idval = nil;
- idval = (*idfn)(sup, idmethod, VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- testassert(state == 1);
- testassert(idval == ID_RESULT);
-
- llval = 0;
- llval = (*llfn)(sup, llmethod, VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- testassert(state == 2);
- testassert(llval == LL_RESULT);
-
- stretval = zero;
- stretval = (*stretfn)(sup, stretmethod, VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- testassert(state == 3);
- testassert(stret_equal(stretval, STRET_RESULT));
-
- fpval = 0;
- fpval = (*fpfn)(sup, fpmethod, VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- testassert(state == 4);
- testassert(fpval == FP_RESULT);
-
- lfpval = 0;
- lfpval = (*lfpfn)(sup, lfpmethod, VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- testassert(state == 5);
- testassert(lfpval == LFP_RESULT);
-
- vecval = 0;
- vecval = (*vecfn)(sup, vecmethod, VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- testassert(state == 6);
- testassert(vector_equal(vecval, VEC_RESULT));
-
-
- // message to nil
- // message to nil long long
- // message to nil stret
- // message to nil fpret
- // message to nil fpret long double
- // Use NIL_RECEIVER to avoid compiler optimizations.
- testprintf("message to nil\n");
-
- state = 0;
- idval = ID_RESULT;
- idval = [(id)NIL_RECEIVER idret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 0);
- testassert(idval == nil);
-
- state = 0;
- llval = LL_RESULT;
- llval = [(id)NIL_RECEIVER llret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 0);
- testassert(llval == 0LL);
-
- state = 0;
- stretval = zero;
- stretval = [(id)NIL_RECEIVER stret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 0);
-#if __clang__
- testassert(0 == memcmp(&stretval, &zero, sizeof(stretval)));
-#else
- // no stret result guarantee
-#endif
-
-#if __x86_64__
- // check stret return register
- state = 0;
- stretval = zero;
- stretptr = ((struct stret *(*)(struct stret *, id, SEL))objc_msgSend_stret)
- (&stretval, nil, @selector(stret_nop));
- testassert(stretptr == &stretval);
- testassert(state == 0);
- // no stret result guarantee for hand-written calls, even with clang
-#endif
-
-#if __i386__
- // check struct-return address stack pop
- for (int i = 0; i < 10000000; i++) {
- state = 0;
- ((struct stret (*)(id, SEL))objc_msgSend_stret)
- (nil, @selector(stret_nop));
- }
-#endif
-
- state = 0;
- fpval = FP_RESULT;
- fpval = [(id)NIL_RECEIVER fpret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 0);
- testassert(fpval == 0.0);
-
- state = 0;
- lfpval = LFP_RESULT;
- lfpval = [(id)NIL_RECEIVER lfpret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 0);
- testassert(lfpval == 0.0);
-
- state = 0;
- vecval = VEC_RESULT;
- vecval = [(id)NIL_RECEIVER vecret :VEC1:VEC2:VEC3:VEC4:VEC5:VEC6:VEC7:VEC8:1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0];
- testassert(state == 0);
- testassert(vector_all(vecval == 0));
-
- // message to nil, different struct types
- // This verifies that ordinary objc_msgSend() erases enough registers
- // for structs that return in registers.
-#define TEST_NIL_STRUCT(i,n) \
- do { \
- struct stret_##i##n z; \
- bzero(&z, sizeof(z)); \
- [Super stret_i##n##_nonzero]; \
- [Super stret_d##n##_nonzero]; \
- struct stret_##i##n val = [(id)NIL_RECEIVER stret_##i##n##_zero]; \
- testassert(0 == memcmp(&z, &val, sizeof(val))); \
- } while (0)
-
- TEST_NIL_STRUCT(i,1);
- TEST_NIL_STRUCT(i,2);
- TEST_NIL_STRUCT(i,3);
- TEST_NIL_STRUCT(i,4);
- TEST_NIL_STRUCT(i,5);
- TEST_NIL_STRUCT(i,6);
- TEST_NIL_STRUCT(i,7);
- TEST_NIL_STRUCT(i,8);
- TEST_NIL_STRUCT(i,9);
-
-#if __i386__
- testwarn("rdar://16267205 i386 struct{float} and struct{double}");
-#else
- TEST_NIL_STRUCT(d,1);
-#endif
- TEST_NIL_STRUCT(d,2);
- TEST_NIL_STRUCT(d,3);
- TEST_NIL_STRUCT(d,4);
- TEST_NIL_STRUCT(d,5);
- TEST_NIL_STRUCT(d,6);
- TEST_NIL_STRUCT(d,7);
- TEST_NIL_STRUCT(d,8);
- TEST_NIL_STRUCT(d,9);
-
-
-#if __OBJC2__
- // message to nil noarg
- // explicitly call noarg messenger, even if compiler doesn't emit it
- state = 0;
- idval = ID_RESULT;
- idval = ((typeof(idmsg0))objc_msgSend_noarg)(nil, @selector(idret_noarg));
- testassert(state == 0);
- testassert(idval == nil);
-
- state = 0;
- llval = LL_RESULT;
- llval = ((typeof(llmsg0))objc_msgSend_noarg)(nil, @selector(llret_noarg));
- testassert(state == 0);
- testassert(llval == 0LL);
-
- // no stret_noarg messenger
-
-# if !__i386__
- state = 0;
- fpval = FP_RESULT;
- fpval = ((typeof(fpmsg0))objc_msgSend_noarg)(nil, @selector(fpret_noarg));
- testassert(state == 0);
- testassert(fpval == 0.0);
-
- state = 0;
- vecval = VEC_RESULT;
- vecval = ((typeof(vecmsg0))objc_msgSend_noarg)(nil, @selector(vecret_noarg));
- testassert(state == 0);
- testassert(vector_all(vecval == 0));
-# endif
-# if !__i386__ && !__x86_64__
- state = 0;
- lfpval = LFP_RESULT;
- lfpval = ((typeof(lfpmsg0))objc_msgSend_noarg)(nil, @selector(lfpret_noarg));
- testassert(state == 0);
- testassert(lfpval == 0.0);
-# endif
-#endif
-
-
-#if __OBJC2__
- // rdar://8271364 objc_msgSendSuper2 must not change objc_super
- testprintf("super struct\n");
- struct objc_super sup_st = {
- sub,
- object_getClass(sub),
- };
-
- SELF = sub;
-
- state = 100;
- idval = nil;
- idval = ((id(*)(struct objc_super *, SEL, vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2, int,int,int,int,int,int,int,int,int,int,int,int,int, double,double,double,double,double,double,double,double,double,double,double,double,double,double,double))objc_msgSendSuper2) (&sup_st, @selector(idret::::::::::::::::::::::::::::::::::::), VEC1,VEC2,VEC3,VEC4,VEC5,VEC6,VEC7,VEC8, 1,2,3,4,5,6,7,8,9,10,11,12,13, 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0);
- testassert(state == 1);
- testassert(idval == ID_RESULT);
- testassert(sup_st.receiver == sub);
- testassert(sup_st.super_class == object_getClass(sub));
-
- state = 100;
- stretval = zero;
- stretval = ((struct stret(*)(struct objc_super *, SEL, vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2,vector_ulong2, int,int,int,int,int,int,int,int,int,int,int,int,int, double,double,double,double,double,double,double,double,double,double,double,double,double,double,double))objc_msgSendSuper2_stret) (&sup_st, @selector(stret::::::::::::::::::::::::::::::::::::), VEC1,VEC2,VEC3,VEC4,VEC5,VEC6,VEC7,VEC8, 1,2,3,4,5,6,7,8,9,10,11,12,13, 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0);
- testassert(state == 3);
- testassert(stret_equal(stretval, STRET_RESULT));
- testassert(sup_st.receiver == sub);
- testassert(sup_st.super_class == object_getClass(sub));
-#endif
-
-#if __OBJC2__ && !__arm64__
- // Debug messengers.
- testprintf("debug messengers\n");
-
- state = 0;
- idmsg = (typeof(idmsg))objc_msgSend_debug;
- idval = nil;
- idval = (*idmsg)(sub, @selector(idret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- testassert(state == 101);
- testassert(idval == ID_RESULT);
-
- state = 0;
- llmsg = (typeof(llmsg))objc_msgSend_debug;
- llval = 0;
- llval = (*llmsg)(sub, @selector(llret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- testassert(state == 102);
- testassert(llval == LL_RESULT);
-
- state = 0;
- stretmsg = (typeof(stretmsg))objc_msgSend_stret_debug;
- stretval = zero;
- stretval = (*stretmsg)(sub, @selector(stret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- testassert(state == 103);
- testassert(stret_equal(stretval, STRET_RESULT));
-
- state = 100;
- sup_st.receiver = sub;
- sup_st.super_class = object_getClass(sub);
- idmsgsuper = (typeof(idmsgsuper))objc_msgSendSuper2_debug;
- idval = nil;
- idval = (*idmsgsuper)(&sup_st, @selector(idret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- testassert(state == 1);
- testassert(idval == ID_RESULT);
-
- state = 100;
- sup_st.receiver = sub;
- sup_st.super_class = object_getClass(sub);
- stretmsgsuper = (typeof(stretmsgsuper))objc_msgSendSuper2_stret_debug;
- stretval = zero;
- stretval = (*stretmsgsuper)(&sup_st, @selector(stret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- testassert(state == 3);
- testassert(stret_equal(stretval, STRET_RESULT));
-
-#if __i386__
- state = 0;
- fpmsg = (typeof(fpmsg))objc_msgSend_fpret_debug;
- fpval = 0;
- fpval = (*fpmsg)(sub, @selector(fpret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- testassert(state == 104);
- testassert(fpval == FP_RESULT);
-#endif
-#if __x86_64__
- state = 0;
- lfpmsg = (typeof(lfpmsg))objc_msgSend_fpret_debug;
- lfpval = 0;
- lfpval = (*lfpmsg)(sub, @selector(lfpret::::::::::::::::::::::::::::::::::::), VEC1, VEC2, VEC3, VEC4, VEC5, VEC6, VEC7, VEC8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0);
- testassert(state == 105);
- testassert(lfpval == LFP_RESULT);
-
- // fixme fp2ret
-#endif
-
-// debug messengers
-#endif
-
-
-#if !TEST_DWARF
- testwarn("no unwind tables in this configuration");
-#else
- // DWARF unwind tables
- testprintf("unwind tables\n");
-
- // install exception handler
- struct sigaction act;
- act.sa_sigaction = sigtrap;
- act.sa_mask = 0;
- act.sa_flags = SA_SIGINFO;
- sigaction(SIGTRAP, &act, NULL);
-
- SubDW *dw = [[SubDW alloc] init];
-
- objc_setForwardHandler((void*)test_dw_forward, (void*)test_dw_forward_stret);
-
-# if __x86_64__
- test_dw("objc_msgSend", dw, tagged, false, 0);
- test_dw("objc_msgSend_stret", dw, tagged, true, 0);
- test_dw("objc_msgSend_fpret", dw, tagged, false, 0);
- test_dw("objc_msgSend_fp2ret", dw, tagged, false, 0);
- test_dw("objc_msgSendSuper", dw, tagged, false, 0);
- test_dw("objc_msgSendSuper2", dw, tagged, false, 0);
- test_dw("objc_msgSendSuper_stret", dw, tagged, true, 0);
- test_dw("objc_msgSendSuper2_stret", dw, tagged, true, 0);
-# elif __i386__
- test_dw("objc_msgSend", dw, dw, false, 0);
- test_dw("objc_msgSend_stret", dw, dw, true, 0);
- test_dw("objc_msgSend_fpret", dw, dw, false, 0);
- test_dw("objc_msgSendSuper", dw, dw, false, 0);
- test_dw("objc_msgSendSuper2", dw, dw, false, 0);
- test_dw("objc_msgSendSuper_stret", dw, dw, true, 0);
- test_dw("objc_msgSendSuper2_stret", dw, dw, true, 0);
-# elif __arm64__
- test_dw("objc_msgSend", dw, tagged, false, 1);
- test_dw("objc_msgSendSuper", dw, tagged, false, 1);
- test_dw("objc_msgSendSuper2", dw, tagged, false, 1);
-# else
-# error unknown architecture
-# endif
-
- // DWARF unwind tables
-#endif
-
- } POP_POOL;
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-
-#import <objc/runtime.h>
-
-int main() {
- // ensure various bits of API don't crash when tossed nil parameters
- class_conformsToProtocol(nil, nil);
- method_setImplementation(nil, NULL);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -framework Foundation
-// TEST_CONFIG MEM=mrc
-
-#include "test.h"
-
-#if !__OBJC2__
-
-int main()
-{
- succeed(__FILE__);
-}
-
-#else
-
-#include <dlfcn.h>
-
-#include <objc/objc-gdb.h>
-#include <Foundation/Foundation.h>
-
-#define ISA(x) (*((uintptr_t *)(x)))
-#define INDEXED(x) (ISA(x) & 1)
-
-#if SUPPORT_NONPOINTER_ISA
-# if __x86_64__
-# define RC_ONE (1ULL<<56)
-# elif __arm64__
-# define RC_ONE (1ULL<<45)
-# else
-# error unknown architecture
-# endif
-#endif
-
-
-void check_unindexed(id obj, Class cls)
-{
- testassert(object_getClass(obj) == cls);
- testassert(!INDEXED(obj));
-
- uintptr_t isa = ISA(obj);
- testassert((Class)isa == cls);
- testassert((Class)(isa & objc_debug_isa_class_mask) == cls);
- testassert((Class)(isa & ~objc_debug_isa_class_mask) == 0);
-
- CFRetain(obj);
- testassert(ISA(obj) == isa);
- testassert([obj retainCount] == 2);
- [obj retain];
- testassert(ISA(obj) == isa);
- testassert([obj retainCount] == 3);
- CFRelease(obj);
- testassert(ISA(obj) == isa);
- testassert([obj retainCount] == 2);
- [obj release];
- testassert(ISA(obj) == isa);
- testassert([obj retainCount] == 1);
-}
-
-
-#if ! SUPPORT_NONPOINTER_ISA
-
-int main()
-{
- testprintf("Isa with index\n");
- id index_o = [NSObject new];
- check_unindexed(index_o, [NSObject class]);
-
- // These variables DO exist even without non-pointer isa support
- testassert(dlsym(RTLD_DEFAULT, "objc_debug_isa_class_mask"));
- testassert(dlsym(RTLD_DEFAULT, "objc_debug_isa_magic_mask"));
- testassert(dlsym(RTLD_DEFAULT, "objc_debug_isa_magic_value"));
-
- succeed(__FILE__);
-}
-
-#else
-// SUPPORT_NONPOINTER_ISA
-
-void check_indexed(id obj, Class cls)
-{
- testassert(object_getClass(obj) == cls);
- testassert(INDEXED(obj));
-
- uintptr_t isa = ISA(obj);
- testassert((Class)(isa & objc_debug_isa_class_mask) == cls);
- testassert((Class)(isa & ~objc_debug_isa_class_mask) != 0);
- testassert((isa & objc_debug_isa_magic_mask) == objc_debug_isa_magic_value);
-
- CFRetain(obj);
- testassert(ISA(obj) == isa + RC_ONE);
- testassert([obj retainCount] == 2);
- [obj retain];
- testassert(ISA(obj) == isa + RC_ONE*2);
- testassert([obj retainCount] == 3);
- CFRelease(obj);
- testassert(ISA(obj) == isa + RC_ONE);
- testassert([obj retainCount] == 2);
- [obj release];
- testassert(ISA(obj) == isa);
- testassert([obj retainCount] == 1);
-}
-
-
-@interface OS_object <NSObject>
-+(id)new;
-@end
-
-@interface Fake_OS_object : NSObject {
- int refcnt;
- int xref_cnt;
-}
-@end
-
-@implementation Fake_OS_object
-+(void)initialize {
- static bool initialized;
- if (!initialized) {
- initialized = true;
- testprintf("Indexed during +initialize\n");
- testassert(INDEXED(self));
- id o = [Fake_OS_object new];
- check_indexed(o, self);
- [o release];
- }
-}
-@end
-
-@interface Sub_OS_object : OS_object @end
-
-@implementation Sub_OS_object
-@end
-
-
-
-int main()
-{
- uintptr_t isa;
-
- testprintf("Isa with index\n");
- id index_o = [Fake_OS_object new];
- check_indexed(index_o, [Fake_OS_object class]);
-
- testprintf("Weakly referenced\n");
- isa = ISA(index_o);
- id weak;
- objc_storeWeak(&weak, index_o);
- testassert(__builtin_popcountl(isa ^ ISA(index_o)) == 1);
-
- testprintf("Has associated references\n");
- id assoc = @"thing";
- isa = ISA(index_o);
- objc_setAssociatedObject(index_o, assoc, assoc, OBJC_ASSOCIATION_ASSIGN);
- testassert(__builtin_popcountl(isa ^ ISA(index_o)) == 1);
-
-
- testprintf("Isa without index\n");
- id unindex_o = [OS_object new];
- check_unindexed(unindex_o, [OS_object class]);
-
-
- id buf[4];
- id bufo = (id)buf;
-
- testprintf("Change isa 0 -> unindexed\n");
- bzero(buf, sizeof(buf));
- object_setClass(bufo, [OS_object class]);
- check_unindexed(bufo, [OS_object class]);
-
- testprintf("Change isa 0 -> indexed\n");
- bzero(buf, sizeof(buf));
- object_setClass(bufo, [NSObject class]);
- check_indexed(bufo, [NSObject class]);
-
- testprintf("Change isa indexed -> indexed\n");
- testassert(INDEXED(bufo));
- _objc_rootRetain(bufo);
- testassert(_objc_rootRetainCount(bufo) == 2);
- object_setClass(bufo, [Fake_OS_object class]);
- testassert(_objc_rootRetainCount(bufo) == 2);
- _objc_rootRelease(bufo);
- testassert(_objc_rootRetainCount(bufo) == 1);
- check_indexed(bufo, [Fake_OS_object class]);
-
- testprintf("Change isa indexed -> unindexed\n");
- // Retain count must be preserved.
- // Use root* to avoid OS_object's overrides.
- testassert(INDEXED(bufo));
- _objc_rootRetain(bufo);
- testassert(_objc_rootRetainCount(bufo) == 2);
- object_setClass(bufo, [OS_object class]);
- testassert(_objc_rootRetainCount(bufo) == 2);
- _objc_rootRelease(bufo);
- testassert(_objc_rootRetainCount(bufo) == 1);
- check_unindexed(bufo, [OS_object class]);
-
- testprintf("Change isa unindexed -> indexed (doesn't happen)\n");
- testassert(!INDEXED(bufo));
- _objc_rootRetain(bufo);
- testassert(_objc_rootRetainCount(bufo) == 2);
- object_setClass(bufo, [Fake_OS_object class]);
- testassert(_objc_rootRetainCount(bufo) == 2);
- _objc_rootRelease(bufo);
- testassert(_objc_rootRetainCount(bufo) == 1);
- check_unindexed(bufo, [Fake_OS_object class]);
-
- testprintf("Change isa unindexed -> unindexed\n");
- testassert(!INDEXED(bufo));
- _objc_rootRetain(bufo);
- testassert(_objc_rootRetainCount(bufo) == 2);
- object_setClass(bufo, [Sub_OS_object class]);
- testassert(_objc_rootRetainCount(bufo) == 2);
- _objc_rootRelease(bufo);
- testassert(_objc_rootRetainCount(bufo) == 1);
- check_unindexed(bufo, [Sub_OS_object class]);
-
-
- succeed(__FILE__);
-}
-
-// SUPPORT_NONPOINTER_ISA
-#endif
-
-// __OBJC2__
-#endif
+++ /dev/null
-// TEST_CONFIG MEM=mrc
-
-#include "test.h"
-#include "testroot.i"
-
-@implementation TestRoot (Loader)
-+(void)load
-{
- [[TestRoot new] autorelease];
- testassert(TestRootAutorelease == 1);
- testassert(TestRootDealloc == 0);
-}
-@end
-
-int main()
-{
- // +load's autoreleased object should have deallocated
- testassert(TestRootDealloc == 1);
-
- [[TestRoot new] autorelease];
- testassert(TestRootAutorelease == 2);
-
- objc_autoreleasePoolPop(objc_autoreleasePoolPush());
-
- [[TestRoot new] autorelease];
- testassert(TestRootAutorelease == 3);
-
- testonthread(^{
- [[TestRoot new] autorelease];
- testassert(TestRootAutorelease == 4);
- testassert(TestRootDealloc == 1);
- });
-
- // thread's autoreleased object should have deallocated
- testassert(TestRootDealloc == 2);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-// test cdtors, with NSObject instead of TestRoot as the root class
-
-#define USE_FOUNDATION 1
-#include "cdtors.mm"
-
+++ /dev/null
-/*
-need exception-safe ARC for exception deallocation tests
-TEST_CFLAGS -fobjc-arc-exceptions -framework Foundation
-
-llvm-gcc unavoidably warns about our deliberately out-of-order handlers
-
-TEST_BUILD_OUTPUT
-In file included from .*
-.*exc.m: In function .*
-.*exc.m:\d+: warning: exception of type .* will be caught
-.*exc.m:\d+: warning: by earlier handler for .*
-.*exc.m:\d+: warning: exception of type .* will be caught
-.*exc.m:\d+: warning: by earlier handler for .*
-.*exc.m:\d+: warning: exception of type .* will be caught
-.*exc.m:\d+: warning: by earlier handler for .*
-OR
-END
-*/
-
-#define USE_FOUNDATION 1
-#include "exc.m"
+++ /dev/null
-// TEST_CONFIG MEM=mrc,gc
-
-#include "test.h"
-
-#import <Foundation/NSObject.h>
-
-@interface Sub : NSObject @end
-@implementation Sub
-+(id)allocWithZone:(NSZone *)zone {
- testprintf("in +[Sub alloc]\n");
- return [super allocWithZone:zone];
- }
--(void)dealloc {
- testprintf("in -[Sub dealloc]\n");
- [super dealloc];
-}
-@end
-
-
-// These declarations and definitions can be used
-// to check the compile-time type of an object.
-@interface NSObject (Checker)
-// fixme this isn't actually enforced
-+(void)NSObjectInstance __attribute__((unavailable));
-@end
-@implementation NSObject (Checker)
--(void)NSObjectInstance { }
-+(void)NSObjectClass { }
-@end
-@interface Sub (Checker)
--(void)NSObjectInstance __attribute__((unavailable));
-+(void)NSObjectClass __attribute__((unavailable));
-@end
-@implementation Sub (Checker)
--(void)SubInstance { }
-+(void)SubClass { }
-@end
-
-int main()
-{
- PUSH_POOL {
- [[Sub new] autorelease];
- } POP_POOL;
-
- // Verify that dot syntax on class objects works with some instance methods
- // (void)NSObject.self; fixme
- (void)NSObject.class;
- (void)NSObject.superclass;
- (void)NSObject.hash;
- (void)NSObject.description;
- (void)NSObject.debugDescription;
-
- // Verify that some methods return the correct type.
- Class cls;
- NSObject *nsobject = nil;
- Sub *subobject = nil;
-
- cls = [NSObject self];
- cls = [Sub self];
- nsobject = [nsobject self];
- subobject = [subobject self];
- [[NSObject self] NSObjectClass];
- [[nsobject self] NSObjectInstance];
- [[Sub self] SubClass];
- [[subobject self] SubInstance];
-
- // fixme
- // cls = NSObject.self;
- // cls = Sub.self;
- // [NSObject.self NSObjectClass];
- // [nsobject.self NSObjectInstance];
- // [Sub.self SubClass];
- // [subobject.self SubInstance];
-
- cls = [NSObject class];
- cls = [nsobject class];
- cls = [Sub class];
- cls = [subobject class];
- [[NSObject class] NSObjectClass];
- [[nsobject class] NSObjectClass];
- [[Sub class] SubClass];
- [[subobject class] SubClass];
-
- cls = NSObject.class;
- cls = nsobject.class;
- cls = Sub.class;
- cls = subobject.class;
- [NSObject.class NSObjectClass];
- [nsobject.class NSObjectClass];
- [Sub.class SubClass];
- [subobject.class SubClass];
-
-
- cls = [NSObject superclass];
- cls = [nsobject superclass];
- cls = [Sub superclass];
- cls = [subobject superclass];
- [[NSObject superclass] NSObjectClass];
- [[nsobject superclass] NSObjectClass];
- [[Sub superclass] NSObjectClass];
- [[subobject superclass] NSObjectClass];
-
- cls = NSObject.superclass;
- cls = nsobject.superclass;
- cls = Sub.superclass;
- cls = subobject.superclass;
- [NSObject.superclass NSObjectClass];
- [nsobject.superclass NSObjectClass];
- [Sub.superclass NSObjectClass];
- [subobject.superclass NSObjectClass];
-
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-
-#if __OBJC2__
-
-#include <objc/Protocol.h>
-
-int main()
-{
- // Class Protocol is always a subclass of NSObject
-
- testassert(objc_getClass("NSObject"));
-
- Class cls = objc_getClass("Protocol");
- testassert(class_getInstanceMethod(cls, sel_registerName("isProxy")));
- testassert(class_getSuperclass(cls) == objc_getClass("NSObject"));
-
- succeed(__FILE__);
-}
-
-#else
-
-#include <dlfcn.h>
-#include <objc/Protocol.h>
-
-int main()
-{
- // Class Protocol is never a subclass of NSObject
- // CoreFoundation adds NSObject methods to Protocol when it loads
-
- testassert(objc_getClass("NSObject"));
-
- Class cls = objc_getClass("Protocol");
- testassert(!class_getInstanceMethod(cls, sel_registerName("isProxy")));
- testassert(class_getSuperclass(cls) != objc_getClass("NSObject"));
-
- void *dl = dlopen("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", RTLD_LAZY);
- testassert(dl);
-
- testassert(class_getInstanceMethod(cls, sel_registerName("isProxy")));
- testassert(class_getSuperclass(cls) != objc_getClass("NSObject"));
-
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-// TEST_CONFIG MEM=mrc,gc
-
-#include "test.h"
-#include <objc/NSObject.h>
-
-@interface Test : NSObject {
-@public
- char bytes[32-sizeof(void*)];
-}
-@end
-@implementation Test
-@end
-
-
-int main()
-{
- Test *o0 = [Test new];
- [o0 retain];
- Test *o1 = class_createInstance([Test class], 32);
- [o1 retain];
- id o2 = object_copy(o0, 0);
- id o3 = object_copy(o1, 0);
- id o4 = object_copy(o1, 32);
- testassert(malloc_size(o0) == 32);
- testassert(malloc_size(o1) == 64);
- testassert(malloc_size(o2) == 32);
- testassert(malloc_size(o3) == 32);
- testassert(malloc_size(o4) == 64);
- if (!objc_collecting_enabled()) {
- testassert([o0 retainCount] == 2);
- testassert([o1 retainCount] == 2);
- testassert([o2 retainCount] == 1);
- testassert([o3 retainCount] == 1);
- testassert([o4 retainCount] == 1);
- }
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include "testroot.i"
-#include <stdint.h>
-#include <string.h>
-#include <objc/objc-runtime.h>
-
-@interface Super : TestRoot {
- @public
- char superIvar;
-}
-
-@property(readonly) char superProp;
-@end
-
-@implementation Super
-@synthesize superProp = superIvar;
-@end
-
-
-@interface Sub : Super {
- @public
- uintptr_t subIvar;
-}
-@property(readonly) uintptr_t subProp;
-@end
-
-@implementation Sub
-@synthesize subProp = subIvar;
-@end
-
-
-int main()
-{
- /*
- Runtime layout of Sub:
- [0] isa
- [1] superIvar
- [2] subIvar
- */
-
- objc_property_t prop;
-
- prop = class_getProperty([Sub class], "subProp");
- testassert(prop);
-
- prop = class_getProperty([Super class], "superProp");
- testassert(prop);
- testassert(prop == class_getProperty([Sub class], "superProp"));
-
- prop = class_getProperty([Super class], "subProp");
- testassert(!prop);
-
- prop = class_getProperty(object_getClass([Sub class]), "subProp");
- testassert(!prop);
-
-
- testassert(NULL == class_getProperty(NULL, "foo"));
- testassert(NULL == class_getProperty([Sub class], NULL));
- testassert(NULL == class_getProperty(NULL, NULL));
-
- succeed(__FILE__);
- return 0;
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include "testroot.i"
-#include <stdint.h>
-#include <string.h>
-#include <objc/runtime.h>
-
-struct objc_property {
- const char *name;
- const char *attr;
-};
-
-#define checkattrlist(attrs, attrcount, target) \
- do { \
- if (target > 0) { \
- testassert(attrs); \
- testassert(attrcount == target); \
- testassert(malloc_size(attrs) >= \
- (1+target) * sizeof(objc_property_attribute_t)); \
- testassert(attrs[target].name == NULL); \
- testassert(attrs[target].value == NULL); \
- } else { \
- testassert(!attrs); \
- testassert(attrcount == 0); \
- } \
- } while (0)
-
-#define checkattr(attrs, i, n, v) \
- do { \
- char *attrsstart = (char *)attrs; \
- char *attrsend = (char *)attrs + malloc_size(attrs); \
- testassert((char*)(attrs+i+1) <= attrsend); \
- testassert(attrs[i].name >= attrsstart); \
- testassert(attrs[i].value >= attrsstart); \
- testassert(attrs[i].name + strlen(attrs[i].name) + 1 <= attrsend); \
- testassert(attrs[i].value + strlen(attrs[i].value) + 1 <= attrsend); \
- if (n) testassert(0 == strcmp(attrs[i].name, n)); \
- else testassert(attrs[i].name == NULL); \
- if (v) testassert(0 == strcmp(attrs[i].value, v)); \
- else testassert(attrs[i].value == NULL); \
- } while (0)
-
-int main()
-{
- char *value;
- objc_property_attribute_t *attrs;
- unsigned int attrcount;
-
- // STRING TO ATTRIBUTE LIST (property_copyAttributeList)
-
- struct objc_property prop;
- prop.name = "test";
-
- // null property
- attrcount = 42;
- attrs = property_copyAttributeList(NULL, &attrcount);
- testassert(!attrs);
- testassert(attrcount == 0);
- attrs = property_copyAttributeList(NULL, NULL);
- testassert(!attrs);
-
- // null attributes
- attrcount = 42;
- prop.attr = NULL;
- attrs = property_copyAttributeList(&prop, &attrcount);
- checkattrlist(attrs, attrcount, 0);
- attrs = property_copyAttributeList(&prop, NULL);
- testassert(!attrs);
-
- // empty attributes
- attrcount = 42;
- prop.attr = "";
- attrs = property_copyAttributeList(&prop, &attrcount);
- checkattrlist(attrs, attrcount, 0);
- attrs = property_copyAttributeList(&prop, NULL);
- testassert(!attrs);
-
- // commas only
- attrcount = 42;
- prop.attr = ",,,";
- attrs = property_copyAttributeList(&prop, &attrcount);
- checkattrlist(attrs, attrcount, 0);
- attrs = property_copyAttributeList(&prop, NULL);
- testassert(!attrs);
-
- // long and short names, with and without values
- attrcount = 42;
- prop.attr = "?XX,',\"?!?!\"YY,\"''''\"";
- attrs = property_copyAttributeList(&prop, &attrcount);
- checkattrlist(attrs, attrcount, 4);
- checkattr(attrs, 0, "?", "XX");
- checkattr(attrs, 1, "'", "");
- checkattr(attrs, 2, "?!?!", "YY");
- checkattr(attrs, 3, "''''", "");
- free(attrs);
-
- // all recognized attributes simultaneously, values with quotes
- attrcount = 42;
- prop.attr = "T11,V2222,S333333\",G\"44444444,W,P,D,R,N,C,&";
- attrs = property_copyAttributeList(&prop, &attrcount);
- checkattrlist(attrs, attrcount, 11);
- checkattr(attrs, 0, "T", "11");
- checkattr(attrs, 1, "V", "2222");
- checkattr(attrs, 2, "S", "333333\"");
- checkattr(attrs, 3, "G", "\"44444444");
- checkattr(attrs, 4, "W", "");
- checkattr(attrs, 5, "P", "");
- checkattr(attrs, 6, "D", "");
- checkattr(attrs, 7, "R", "");
- checkattr(attrs, 8, "N", "");
- checkattr(attrs, 9, "C", "");
- checkattr(attrs,10, "&", "");
- free(attrs);
-
- // kitchen sink
- attrcount = 42;
- prop.attr = "W,T11,P,?XX,D,V2222,R,',N,S333333\",C,\"?!?!\"YY,&,G\"44444444,\"''''\"";
- attrs = property_copyAttributeList(&prop, &attrcount);
- checkattrlist(attrs, attrcount, 15);
- checkattr(attrs, 0, "W", "");
- checkattr(attrs, 1, "T", "11");
- checkattr(attrs, 2, "P", "");
- checkattr(attrs, 3, "?", "XX");
- checkattr(attrs, 4, "D", "");
- checkattr(attrs, 5, "V", "2222");
- checkattr(attrs, 6, "R", "");
- checkattr(attrs, 7, "'", "");
- checkattr(attrs, 8, "N", "");
- checkattr(attrs, 9, "S", "333333\"");
- checkattr(attrs,10, "C", "");
- checkattr(attrs,11, "?!?!", "YY");
- checkattr(attrs,12, "&", "");
- checkattr(attrs,13, "G", "\"44444444");
- checkattr(attrs,14, "''''", "");
- free(attrs);
-
- // SEARCH ATTRIBUTE LIST (property_copyAttributeValue)
-
- // null property, null name, empty name
- value = property_copyAttributeValue(NULL, NULL);
- testassert(!value);
- value = property_copyAttributeValue(NULL, "foo");
- testassert(!value);
- value = property_copyAttributeValue(NULL, "");
- testassert(!value);
- value = property_copyAttributeValue(&prop, NULL);
- testassert(!value);
- value = property_copyAttributeValue(&prop, "");
- testassert(!value);
-
- // null attributes, empty attributes
- prop.attr = NULL;
- value = property_copyAttributeValue(&prop, "foo");
- testassert(!value);
- prop.attr = "";
- value = property_copyAttributeValue(&prop, "foo");
- testassert(!value);
-
- // long and short names, with and without values
- prop.attr = "?XX,',\"?!?!\"YY,\"''''\"";
- value = property_copyAttributeValue(&prop, "missing");
- testassert(!value);
- value = property_copyAttributeValue(&prop, "X");
- testassert(!value);
- value = property_copyAttributeValue(&prop, "\"");
- testassert(!value);
- value = property_copyAttributeValue(&prop, "'''");
- testassert(!value);
- value = property_copyAttributeValue(&prop, "'''''");
- testassert(!value);
-
- value = property_copyAttributeValue(&prop, "?");
- testassert(0 == strcmp(value, "XX"));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
- value = property_copyAttributeValue(&prop, "'");
- testassert(0 == strcmp(value, ""));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
- value = property_copyAttributeValue(&prop, "?!?!");
- testassert(0 == strcmp(value, "YY"));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
- value = property_copyAttributeValue(&prop, "''''");
- testassert(0 == strcmp(value, ""));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
-
- // all recognized attributes simultaneously, values with quotes
- prop.attr = "T11,V2222,S333333\",G\"44444444,W,P,D,R,N,C,&";
- value = property_copyAttributeValue(&prop, "T");
- testassert(0 == strcmp(value, "11"));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
- value = property_copyAttributeValue(&prop, "V");
- testassert(0 == strcmp(value, "2222"));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
- value = property_copyAttributeValue(&prop, "S");
- testassert(0 == strcmp(value, "333333\""));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
- value = property_copyAttributeValue(&prop, "G");
- testassert(0 == strcmp(value, "\"44444444"));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
- value = property_copyAttributeValue(&prop, "W");
- testassert(0 == strcmp(value, ""));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
- value = property_copyAttributeValue(&prop, "P");
- testassert(0 == strcmp(value, ""));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
- value = property_copyAttributeValue(&prop, "D");
- testassert(0 == strcmp(value, ""));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
- value = property_copyAttributeValue(&prop, "R");
- testassert(0 == strcmp(value, ""));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
- value = property_copyAttributeValue(&prop, "N");
- testassert(0 == strcmp(value, ""));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
- value = property_copyAttributeValue(&prop, "C");
- testassert(0 == strcmp(value, ""));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
- value = property_copyAttributeValue(&prop, "&");
- testassert(0 == strcmp(value, ""));
- testassert(malloc_size(value) >= 1 + strlen(value));
- free(value);
-
- // ATTRIBUTE LIST TO STRING (class_addProperty)
-
- BOOL ok;
- objc_property_t prop2;
-
- // null name
- ok = class_addProperty([TestRoot class], NULL, (objc_property_attribute_t *)1, 1);
- testassert(!ok);
-
- // null description
- ok = class_addProperty([TestRoot class], "test-null-desc", NULL, 0);
- testassert(ok);
- prop2 = class_getProperty([TestRoot class], "test-null-desc");
- testassert(prop2);
- testassert(0 == strcmp(property_getAttributes(prop2), ""));
-
- // empty description
- ok = class_addProperty([TestRoot class], "test-empty-desc", (objc_property_attribute_t*)1, 0);
- testassert(ok);
- prop2 = class_getProperty([TestRoot class], "test-empty-desc");
- testassert(prop2);
- testassert(0 == strcmp(property_getAttributes(prop2), ""));
-
- // long and short names, with and without values
- objc_property_attribute_t attrs2[] = {
- { "!", NULL },
- { "?", "XX" },
- { "'", "" },
- { "?!?!", "YY" },
- { "''''", "" }
- };
- ok = class_addProperty([TestRoot class], "test-unrecognized", attrs2, 5);
- testassert(ok);
- prop2 = class_getProperty([TestRoot class], "test-unrecognized");
- testassert(prop2);
- testassert(0 == strcmp(property_getAttributes(prop2), "?XX,',\"?!?!\"YY,\"''''\""));
-
- // all recognized attributes simultaneously, values with quotes
- objc_property_attribute_t attrs3[] = {
- { "&", "" },
- { "C", "" },
- { "N", "" },
- { "R", "" },
- { "D", "" },
- { "P", "" },
- { "W", "" },
- { "G", "\"44444444" },
- { "S", "333333\"" },
- { "V", "2222" },
- { "T", "11" },
- };
- ok = class_addProperty([TestRoot class], "test-recognized", attrs3, 11);
- testassert(ok);
- prop2 = class_getProperty([TestRoot class], "test-recognized");
- testassert(prop2);
- testassert(0 == strcmp(property_getAttributes(prop2),
- "&,C,N,R,D,P,W,G\"44444444,S333333\",V2222,T11"));
-
- // kitchen sink
- objc_property_attribute_t attrs4[] = {
- { "&", "" },
- { "C", "" },
- { "N", "" },
- { "R", "" },
- { "D", "" },
- { "P", "" },
- { "W", "" },
- { "!", NULL },
- { "G", "\"44444444" },
- { "S", "333333\"" },
- { "V", "2222" },
- { "T", "11" },
- { "?", "XX" },
- { "'", "" },
- { "?!?!", "YY" },
- { "''''", "" }
- };
- ok = class_addProperty([TestRoot class], "test-sink", attrs4, 16);
- testassert(ok);
- prop2 = class_getProperty([TestRoot class], "test-sink");
- testassert(prop2);
- testassert(0 == strcmp(property_getAttributes(prop2),
- "&,C,N,R,D,P,W,G\"44444444,S333333\",V2222,T11,"
- "?XX,',\"?!?!\"YY,\"''''\""));
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -framework Foundation -Wno-deprecated-declarations
-// need Foundation to get NSObject compatibility additions for class Protocol
-// because ARC calls [protocol retain]
-
-#include "test.h"
-#include "testroot.i"
-#include <string.h>
-#include <objc/runtime.h>
-#include <objc/objc-internal.h>
-
-#if !__OBJC2__
-#include <objc/Protocol.h>
-#endif
-
-@protocol Proto1
-+(id)proto1ClassMethod;
--(id)proto1InstanceMethod;
-@end
-
-@protocol Proto2
-+(id)proto2ClassMethod;
--(id)proto2InstanceMethod;
-@end
-
-@protocol Proto3 <Proto2>
-+(id)proto3ClassMethod;
--(id)proto3InstanceMethod;
-@end
-
-@protocol Proto4
-@property int i;
-@end
-
-// Force some of Proto5's selectors out of address order rdar://10582325
-SEL fn(int x) { if (x) return @selector(m12:); else return @selector(m22:); }
-
-// This declaration order deliberately looks weird because it determines the
-// selector address order on some architectures rdar://10582325
-@protocol Proto5
--(id)m11:(id<Proto1>)a;
--(void)m12:(id<Proto1>)a;
--(int)m13:(id<Proto1>)a;
-+(void)m22:(TestRoot<Proto1>*)a;
-+(int)m23:(TestRoot<Proto1>*)a;
-+(TestRoot*)m21:(TestRoot<Proto1>*)a;
-@optional
--(id(^)(id))m31:(id<Proto1>(^)(id<Proto1>))a;
--(void)m32:(id<Proto1>(^)(id<Proto1>))a;
--(int)m33:(id<Proto1>(^)(id<Proto1>))a;
-+(void)m42:(TestRoot<Proto1>*(^)(TestRoot<Proto1>*))a;
-+(int)m43:(TestRoot<Proto1>*(^)(TestRoot<Proto1>*))a;
-+(TestRoot*(^)(TestRoot*))m41:(TestRoot<Proto1>*(^)(TestRoot<Proto1>*))a;
-@end
-
-@protocol Proto6 <Proto5>
-@optional
-+(TestRoot*(^)(TestRoot*))n41:(TestRoot<Proto1>*(^)(TestRoot<Proto1>*))a;
-@end
-
-@protocol ProtoEmpty
-@end
-
-#if __OBJC2__
-#define TEST_SWIFT 1
-#define SwiftV1MangledName "_TtP6Module15SwiftV1Protocol_"
-#endif
-
-#if TEST_SWIFT
-__attribute__((objc_runtime_name(SwiftV1MangledName)))
-@protocol SwiftV1Protocol
-@end
-#endif
-
-@interface Super : TestRoot <Proto1> @end
-@implementation Super
-+(id)proto1ClassMethod { return self; }
--(id)proto1InstanceMethod { return self; }
-@end
-
-@interface SubNoProtocols : Super @end
-@implementation SubNoProtocols @end
-
-@interface SuperNoProtocols : TestRoot @end
-@implementation SuperNoProtocols
-@end
-
-@interface SubProp : Super <Proto4> { int i; } @end
-@implementation SubProp
-@synthesize i;
-@end
-
-
-int main()
-{
- Class cls;
- Protocol * __unsafe_unretained *list;
- Protocol *protocol, *empty;
-#if !__OBJC2__
- struct objc_method_description *desc;
-#endif
- struct objc_method_description desc2;
- objc_property_t *proplist;
- unsigned int count;
-
- protocol = @protocol(Proto3);
- empty = @protocol(ProtoEmpty);
- testassert(protocol);
- testassert(empty);
-
-#if !__OBJC2__
- testassert([protocol isKindOf:[Protocol class]]);
- testassert([empty isKindOf:[Protocol class]]);
- testassert(0 == strcmp([protocol name], "Proto3"));
- testassert(0 == strcmp([empty name], "ProtoEmpty"));
-#endif
- testassert(0 == strcmp(protocol_getName(protocol), "Proto3"));
- testassert(0 == strcmp(protocol_getName(empty), "ProtoEmpty"));
-
- testassert(class_conformsToProtocol([Super class], @protocol(Proto1)));
- testassert(!class_conformsToProtocol([SubProp class], @protocol(Proto1)));
- testassert(class_conformsToProtocol([SubProp class], @protocol(Proto4)));
- testassert(!class_conformsToProtocol([SubProp class], @protocol(Proto3)));
- testassert(!class_conformsToProtocol([Super class], @protocol(Proto3)));
-
- testassert(!protocol_conformsToProtocol(@protocol(Proto1), @protocol(Proto2)));
- testassert(protocol_conformsToProtocol(@protocol(Proto3), @protocol(Proto2)));
- testassert(!protocol_conformsToProtocol(@protocol(Proto2), @protocol(Proto3)));
-
-#if !__OBJC2__
- testassert([@protocol(Proto1) isEqual:@protocol(Proto1)]);
- testassert(! [@protocol(Proto1) isEqual:@protocol(Proto2)]);
-#endif
- testassert(protocol_isEqual(@protocol(Proto1), @protocol(Proto1)));
- testassert(! protocol_isEqual(@protocol(Proto1), @protocol(Proto2)));
-
-#if !__OBJC2__
- desc = [protocol descriptionForInstanceMethod:@selector(proto3InstanceMethod)];
- testassert(desc);
- testassert(desc->name == @selector(proto3InstanceMethod));
- desc = [protocol descriptionForClassMethod:@selector(proto3ClassMethod)];
- testassert(desc);
- testassert(desc->name == @selector(proto3ClassMethod));
- desc = [protocol descriptionForClassMethod:@selector(proto2ClassMethod)];
- testassert(desc);
- testassert(desc->name == @selector(proto2ClassMethod));
-
- desc = [protocol descriptionForInstanceMethod:@selector(proto3ClassMethod)];
- testassert(!desc);
- desc = [protocol descriptionForClassMethod:@selector(proto3InstanceMethod)];
- testassert(!desc);
- desc = [empty descriptionForInstanceMethod:@selector(proto3ClassMethod)];
- testassert(!desc);
- desc = [empty descriptionForClassMethod:@selector(proto3InstanceMethod)];
- testassert(!desc);
-#endif
- desc2 = protocol_getMethodDescription(protocol, @selector(proto3InstanceMethod), YES, YES);
- testassert(desc2.name && desc2.types);
- testassert(desc2.name == @selector(proto3InstanceMethod));
- desc2 = protocol_getMethodDescription(protocol, @selector(proto3ClassMethod), YES, NO);
- testassert(desc2.name && desc2.types);
- testassert(desc2.name == @selector(proto3ClassMethod));
- desc2 = protocol_getMethodDescription(protocol, @selector(proto2ClassMethod), YES, NO);
- testassert(desc2.name && desc2.types);
- testassert(desc2.name == @selector(proto2ClassMethod));
-
- desc2 = protocol_getMethodDescription(protocol, @selector(proto3ClassMethod), YES, YES);
- testassert(!desc2.name && !desc2.types);
- desc2 = protocol_getMethodDescription(protocol, @selector(proto3InstanceMethod), YES, NO);
- testassert(!desc2.name && !desc2.types);
- desc2 = protocol_getMethodDescription(empty, @selector(proto3ClassMethod), YES, YES);
- testassert(!desc2.name && !desc2.types);
- desc2 = protocol_getMethodDescription(empty, @selector(proto3InstanceMethod), YES, NO);
- testassert(!desc2.name && !desc2.types);
-
- count = 100;
- list = protocol_copyProtocolList(@protocol(Proto2), &count);
- testassert(!list);
- testassert(count == 0);
- count = 100;
- list = protocol_copyProtocolList(@protocol(Proto3), &count);
- testassert(list);
- testassert(count == 1);
- testassert(protocol_isEqual(list[0], @protocol(Proto2)));
- testassert(!list[1]);
- free(list);
-
- count = 100;
- cls = objc_getClass("Super");
- testassert(cls);
- list = class_copyProtocolList(cls, &count);
- testassert(list);
- testassert(list[count] == NULL);
- testassert(count == 1);
- testassert(0 == strcmp(protocol_getName(list[0]), "Proto1"));
- free(list);
-
- count = 100;
- cls = objc_getClass("SuperNoProtocols");
- testassert(cls);
- list = class_copyProtocolList(cls, &count);
- testassert(!list);
- testassert(count == 0);
-
- count = 100;
- cls = objc_getClass("SubNoProtocols");
- testassert(cls);
- list = class_copyProtocolList(cls, &count);
- testassert(!list);
- testassert(count == 0);
-
-
- cls = objc_getClass("SuperNoProtocols");
- testassert(cls);
- list = class_copyProtocolList(cls, NULL);
- testassert(!list);
-
- cls = objc_getClass("Super");
- testassert(cls);
- list = class_copyProtocolList(cls, NULL);
- testassert(list);
- free(list);
-
- count = 100;
- list = class_copyProtocolList(NULL, &count);
- testassert(!list);
- testassert(count == 0);
-
-
- // Check property added by protocol
- cls = objc_getClass("SubProp");
- testassert(cls);
-
- count = 100;
- list = class_copyProtocolList(cls, &count);
- testassert(list);
- testassert(count == 1);
- testassert(0 == strcmp(protocol_getName(list[0]), "Proto4"));
- testassert(list[1] == NULL);
- free(list);
-
- count = 100;
- proplist = class_copyPropertyList(cls, &count);
- testassert(proplist);
- testassert(count == 1);
- testassert(0 == strcmp(property_getName(proplist[0]), "i"));
- testassert(proplist[1] == NULL);
- free(proplist);
-
- // Check extended type encodings
- testassert(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(DoesNotExist), true, true) == NULL);
- testassert(_protocol_getMethodTypeEncoding(NULL, @selector(m11), true, true) == NULL);
- testassert(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m11), true, false) == NULL);
- testassert(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m11), false, false) == NULL);
- testassert(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m11), false, true) == NULL);
- testassert(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m21), true, true) == NULL);
-#if __LP64__
- const char *types11 = "@24@0:8@\"<Proto1>\"16";
- const char *types12 = "v24@0:8@\"<Proto1>\"16";
- const char *types13 = "i24@0:8@\"<Proto1>\"16";
- const char *types21 = "@\"TestRoot\"24@0:8@\"TestRoot<Proto1>\"16";
- const char *types22 = "v24@0:8@\"TestRoot<Proto1>\"16";
- const char *types23 = "i24@0:8@\"TestRoot<Proto1>\"16";
- const char *types31 = "@?<@@?@>24@0:8@?<@\"<Proto1>\"@?@\"<Proto1>\">16";
- const char *types32 = "v24@0:8@?<@\"<Proto1>\"@?@\"<Proto1>\">16";
- const char *types33 = "i24@0:8@?<@\"<Proto1>\"@?@\"<Proto1>\">16";
- const char *types41 = "@?<@\"TestRoot\"@?@\"TestRoot\">24@0:8@?<@\"TestRoot<Proto1>\"@?@\"TestRoot<Proto1>\">16";
- const char *types42 = "v24@0:8@?<@\"TestRoot<Proto1>\"@?@\"TestRoot<Proto1>\">16";
- const char *types43 = "i24@0:8@?<@\"TestRoot<Proto1>\"@?@\"TestRoot<Proto1>\">16";
-#else
- const char *types11 = "@12@0:4@\"<Proto1>\"8";
- const char *types12 = "v12@0:4@\"<Proto1>\"8";
- const char *types13 = "i12@0:4@\"<Proto1>\"8";
- const char *types21 = "@\"TestRoot\"12@0:4@\"TestRoot<Proto1>\"8";
- const char *types22 = "v12@0:4@\"TestRoot<Proto1>\"8";
- const char *types23 = "i12@0:4@\"TestRoot<Proto1>\"8";
- const char *types31 = "@?<@@?@>12@0:4@?<@\"<Proto1>\"@?@\"<Proto1>\">8";
- const char *types32 = "v12@0:4@?<@\"<Proto1>\"@?@\"<Proto1>\">8";
- const char *types33 = "i12@0:4@?<@\"<Proto1>\"@?@\"<Proto1>\">8";
- const char *types41 = "@?<@\"TestRoot\"@?@\"TestRoot\">12@0:4@?<@\"TestRoot<Proto1>\"@?@\"TestRoot<Proto1>\">8";
- const char *types42 = "v12@0:4@?<@\"TestRoot<Proto1>\"@?@\"TestRoot<Proto1>\">8";
- const char *types43 = "i12@0:4@?<@\"TestRoot<Proto1>\"@?@\"TestRoot<Proto1>\">8";
-#endif
-
- // Make sure some of Proto5's selectors are out of order rdar://10582325
- // These comparisons deliberately look weird because they determine the
- // selector order on some architectures.
- testassert(sel_registerName("m11:") > sel_registerName("m12:") ||
- sel_registerName("m21:") > sel_registerName("m22:") ||
- sel_registerName("m32:") < sel_registerName("m31:") ||
- sel_registerName("m42:") < sel_registerName("m41:") );
-
- if (!_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m11:), true, true)) {
- fail("rdar://10492418 extended type encodings not present (is compiler old?)");
- } else {
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m11:), true, true), types11));
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m12:), true, true), types12));
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m13:), true, true), types13));
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m21:), true, false), types21));
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m22:), true, false), types22));
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m23:), true, false), types23));
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m31:), false, true), types31));
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m32:), false, true), types32));
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m33:), false, true), types33));
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m41:), false, false), types41));
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m42:), false, false), types42));
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto5), @selector(m43:), false, false), types43));
-
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto6), @selector(n41:), false, false), types41));
- testassert(0 == strcmp(_protocol_getMethodTypeEncoding(@protocol(Proto6), @selector(m41:), false, false), types41));
- }
-
-#if TEST_SWIFT
- testassert(@protocol(SwiftV1Protocol) == objc_getProtocol("Module.SwiftV1Protocol"));
- testassert(@protocol(SwiftV1Protocol) == objc_getProtocol(SwiftV1MangledName));
- testassert(0 == strcmp(protocol_getName(@protocol(SwiftV1Protocol)), "Module.SwiftV1Protocol"));
- testassert(!objc_getProtocol("SwiftV1Protocol"));
-#endif
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -framework Foundation
-// need Foundation to get NSObject compatibility additions for class Protocol
-// because ARC calls [protocol retain]
-
-
-#include "test.h"
-#include <malloc/malloc.h>
-#include <objc/objc-runtime.h>
-
-@protocol SuperMethods
-+(void)SuperMethodClass;
-+(void)SuperMethodClass2;
--(void)SuperMethodInstance;
--(void)SuperMethodInstance2;
-@end
-
-@protocol SubMethods
-+(void)SubMethodClass;
-+(void)SubMethodClass2;
--(void)SubMethodInstance;
--(void)SubMethodInstance2;
-@end
-
-@protocol SuperOptionalMethods
-@optional
-+(void)SuperOptMethodClass;
-+(void)SuperOptMethodClass2;
--(void)SuperOptMethodInstance;
--(void)SuperOptMethodInstance2;
-@end
-
-@protocol SubOptionalMethods <SuperOptionalMethods>
-@optional
-+(void)SubOptMethodClass;
-+(void)SubOptMethodClass2;
--(void)SubOptMethodInstance;
--(void)SubOptMethodInstance2;
-@end
-
-@protocol NoMethods @end
-
-static int isNamed(struct objc_method_description m, const char *name)
-{
- return (m.name == sel_registerName(name));
-}
-
-int main()
-{
- struct objc_method_description *methods;
- unsigned int count;
- Protocol *proto;
-
- proto = @protocol(SubMethods);
- testassert(proto);
-
- // Check required methods
- count = 999;
- methods = protocol_copyMethodDescriptionList(proto, YES, YES, &count);
- testassert(methods);
- testassert(count == 2);
- testassert((isNamed(methods[0], "SubMethodInstance") &&
- isNamed(methods[1], "SubMethodInstance2"))
- ||
- (isNamed(methods[1], "SubMethodInstance") &&
- isNamed(methods[0], "SubMethodInstance2")));
- free(methods);
-
- count = 999;
- methods = protocol_copyMethodDescriptionList(proto, YES, NO, &count);
- testassert(methods);
- testassert(count == 2);
- testassert((isNamed(methods[0], "SubMethodClass") &&
- isNamed(methods[1], "SubMethodClass2"))
- ||
- (isNamed(methods[1], "SubMethodClass") &&
- isNamed(methods[0], "SubMethodClass2")));
- free(methods);
-
- // Check lack of optional methods
- count = 999;
- methods = protocol_copyMethodDescriptionList(proto, NO, YES, &count);
- testassert(!methods);
- testassert(count == 0);
- count = 999;
- methods = protocol_copyMethodDescriptionList(proto, NO, NO, &count);
- testassert(!methods);
- testassert(count == 0);
-
-
- proto = @protocol(SubOptionalMethods);
- testassert(proto);
-
- // Check optional methods
- count = 999;
- methods = protocol_copyMethodDescriptionList(proto, NO, YES, &count);
- testassert(methods);
- testassert(count == 2);
- testassert((isNamed(methods[0], "SubOptMethodInstance") &&
- isNamed(methods[1], "SubOptMethodInstance2"))
- ||
- (isNamed(methods[1], "SubOptMethodInstance") &&
- isNamed(methods[0], "SubOptMethodInstance2")));
- free(methods);
-
- count = 999;
- methods = protocol_copyMethodDescriptionList(proto, NO, NO, &count);
- testassert(methods);
- testassert(count == 2);
- testassert((isNamed(methods[0], "SubOptMethodClass") &&
- isNamed(methods[1], "SubOptMethodClass2"))
- ||
- (isNamed(methods[1], "SubOptMethodClass") &&
- isNamed(methods[0], "SubOptMethodClass2")));
- free(methods);
-
- // Check lack of required methods
- count = 999;
- methods = protocol_copyMethodDescriptionList(proto, YES, YES, &count);
- testassert(!methods);
- testassert(count == 0);
- count = 999;
- methods = protocol_copyMethodDescriptionList(proto, YES, NO, &count);
- testassert(!methods);
- testassert(count == 0);
-
-
- // Check NULL protocol parameter
- count = 999;
- methods = protocol_copyMethodDescriptionList(NULL, YES, YES, &count);
- testassert(!methods);
- testassert(count == 0);
- count = 999;
- methods = protocol_copyMethodDescriptionList(NULL, YES, NO, &count);
- testassert(!methods);
- testassert(count == 0);
- count = 999;
- methods = protocol_copyMethodDescriptionList(NULL, NO, YES, &count);
- testassert(!methods);
- testassert(count == 0);
- count = 999;
- methods = protocol_copyMethodDescriptionList(NULL, NO, NO, &count);
- testassert(!methods);
- testassert(count == 0);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -framework Foundation
-// need Foundation to get NSObject compatibility additions for class Protocol
-// because ARC calls [protocol retain]
-
-#include "test.h"
-#include <string.h>
-#include <malloc/malloc.h>
-#include <objc/runtime.h>
-
-@protocol SuperProps
-@property int prop1;
-@property int prop2;
-@end
-
-@protocol SubProps <SuperProps>
-@property int prop3;
-@property int prop4;
-@end
-
-
-@protocol FourProps
-@property int prop1;
-@property int prop2;
-@property int prop3;
-@property int prop4;
-@end
-
-@protocol NoProps @end
-
-static int isNamed(objc_property_t p, const char *name)
-{
- return (0 == strcmp(name, property_getName(p)));
-}
-
-int main()
-{
- objc_property_t *props;
- unsigned int count;
- Protocol *proto;
-
- proto = @protocol(SubProps);
- testassert(proto);
-
- count = 100;
- props = protocol_copyPropertyList(proto, &count);
- testassert(props);
- testassert(count == 2);
- testassert((isNamed(props[0], "prop4") && isNamed(props[1], "prop3")) ||
- (isNamed(props[0], "prop3") && isNamed(props[1], "prop4")));
- // props[] should be null-terminated
- testassert(props[2] == NULL);
- free(props);
-
- proto = @protocol(SuperProps);
- testassert(proto);
-
- count = 100;
- props = protocol_copyPropertyList(proto, &count);
- testassert(props);
- testassert(count == 2);
- testassert((isNamed(props[0], "prop1") && isNamed(props[1], "prop2")) ||
- (isNamed(props[0], "prop2") && isNamed(props[1], "prop1")));
- // props[] should be null-terminated
- testassert(props[2] == NULL);
- free(props);
-
- // Check null-termination - this property list block would be 16 bytes
- // if it weren't for the terminator
- proto = @protocol(FourProps);
- testassert(proto);
-
- count = 100;
- props = protocol_copyPropertyList(proto, &count);
- testassert(props);
- testassert(count == 4);
- testassert(malloc_size(props) >= 5 * sizeof(objc_property_t));
- testassert(props[3] != NULL);
- testassert(props[4] == NULL);
- free(props);
-
- // Check NULL count parameter
- props = protocol_copyPropertyList(proto, NULL);
- testassert(props);
- testassert(props[4] == NULL);
- testassert(props[3] != NULL);
- free(props);
-
- // Check NULL protocol parameter
- count = 100;
- props = protocol_copyPropertyList(NULL, &count);
- testassert(!props);
- testassert(count == 0);
-
- // Check NULL protocol and count
- props = protocol_copyPropertyList(NULL, NULL);
- testassert(!props);
-
- // Check protocol with no properties
- proto = @protocol(NoProps);
- testassert(proto);
-
- count = 100;
- props = protocol_copyPropertyList(proto, &count);
- testassert(!props);
- testassert(count == 0);
-
- succeed(__FILE__);
- return 0;
-}
+++ /dev/null
-// TEST_CFLAGS -Wno-deprecated-declarations
-
-#include "test.h"
-
-#if __OBJC2__
-
-int main()
-{
- succeed(__FILE__);
-}
-
-#else
-
-// rdar://4951638
-
-#include <string.h>
-#include <objc/Protocol.h>
-
-char Protocol_name[] __attribute__((section("__OBJC,__class_names"))) = "Protocol";
-
-struct st {
- void *isa;
- const char *protocol_name;
- void *protocol_list;
- void *instance_methods;
- void *class_methods;
-};
-
-struct st Foo_protocol __attribute__((section("__OBJC,__protocol"))) = { Protocol_name, "Foo", 0, 0, 0 };
-
-int main()
-{
- Protocol *foo = objc_getProtocol("Foo");
-
- testassert(foo == (Protocol *)&Foo_protocol);
- testassert(0 == strcmp("Foo", [foo name]));
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-/*
-TEST_CFLAGS -Xlinker -sectcreate -Xlinker __DATA -Xlinker __objc_rawisa -Xlinker /dev/null
-TEST_ENV OBJC_PRINT_RAW_ISA=YES
-
-TEST_RUN_OUTPUT
-objc\[\d+\]: RAW ISA: disabling non-pointer isa because the app has a __DATA,__objc_rawisa section
-(.* RAW ISA: .*\n)*
-OK: rawisa.m
-OR
-(.* RAW ISA: .*\n)*
-no __DATA,__rawisa support
-OK: rawisa.m
-END
-*/
-
-#include "test.h"
-
-int main()
-{
- fprintf(stderr, "\n");
-#if ! (SUPPORT_NONPOINTER_ISA && TARGET_OS_MAC && !TARGET_OS_IPHONE)
- // only 64-bit Mac supports this
- fprintf(stderr, "no __DATA,__rawisa support\n");
-#endif
- succeed(__FILE__);
-}
-
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-
-#if !__OBJC2__
-
-int main()
-{
- succeed(__FILE__);
-}
-
-#else
-
-#include <objc/objc-internal.h>
-
-// Reuse evil-class-def.m as a non-evil class definition.
-
-#define EVIL_SUPER 0
-#define EVIL_SUPER_META 0
-#define EVIL_SUB 0
-#define EVIL_SUB_META 0
-
-#define OMIT_SUPER 1
-#define OMIT_NL_SUPER 1
-#define OMIT_SUB 1
-#define OMIT_NL_SUB 1
-
-#include "evil-class-def.m"
-
-int main()
-{
- // This definition is ABI and is never allowed to change.
- testassert(OBJC_MAX_CLASS_SIZE == 32*sizeof(void*));
-
- struct objc_image_info ii = { 0, 0 };
-
- // Read a root class.
- testassert(!objc_getClass("Super"));
-
- extern intptr_t OBJC_CLASS_$_Super[OBJC_MAX_CLASS_SIZE/sizeof(void*)];
- Class Super = objc_readClassPair((__bridge Class)(void*)&OBJC_CLASS_$_Super, &ii);
- testassert(Super);
-
- testassert(objc_getClass("Super") == Super);
- testassert(0 == strcmp(class_getName(Super), "Super"));
- testassert(class_getSuperclass(Super) == nil);
- testassert(class_getClassMethod(Super, @selector(load)));
- testassert(class_getInstanceMethod(Super, @selector(load)));
- testassert(class_getInstanceVariable(Super, "super_ivar"));
- testassert(class_getInstanceSize(Super) == sizeof(void*));
- [Super load];
-
- // Read a non-root class.
- testassert(!objc_getClass("Sub"));
-
- extern intptr_t OBJC_CLASS_$_Sub[OBJC_MAX_CLASS_SIZE/sizeof(void*)];
- intptr_t Sub2_buf[OBJC_MAX_CLASS_SIZE/sizeof(void*)];
- memcpy(Sub2_buf, &OBJC_CLASS_$_Sub, sizeof(Sub2_buf));
- Class Sub = objc_readClassPair((__bridge Class)(void*)&OBJC_CLASS_$_Sub, &ii);
- testassert(Sub);
-
- testassert(0 == strcmp(class_getName(Sub), "Sub"));
- testassert(objc_getClass("Sub") == Sub);
- testassert(class_getSuperclass(Sub) == Super);
- testassert(class_getClassMethod(Sub, @selector(load)));
- testassert(class_getInstanceMethod(Sub, @selector(load)));
- testassert(class_getInstanceVariable(Sub, "sub_ivar"));
- testassert(class_getInstanceSize(Sub) == 2*sizeof(void*));
- [Sub load];
-
- // Reading a class whose name already exists fails.
- testassert(! objc_readClassPair((__bridge Class)(void*)Sub2_buf, &ii));
-
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-/* resolve.m
- * Test +resolveClassMethod: and +resolveInstanceMethod:
- */
-
-// TEST_CFLAGS -Wno-deprecated-declarations
-
-#include "test.h"
-#include "testroot.i"
-#include <objc/objc.h>
-#include <objc/objc-runtime.h>
-#include <unistd.h>
-
-#if __has_feature(objc_arc)
-
-int main()
-{
- testwarn("rdar://11368528 confused by Foundation");
- succeed(__FILE__);
-}
-
-#else
-
-static int state = 0;
-
-@interface Super : TestRoot @end
-@interface Sub : Super @end
-
-
-@implementation Super
-+(void)initialize {
- if (self == [Super class]) {
- testassert(state == 1);
- state = 2;
- }
-}
-@end
-
-static id forward_handler(id self, SEL sel)
-{
- if (class_isMetaClass(object_getClass(self))) {
- // self is a class object
- if (sel == @selector(missingClassMethod)) {
- testassert(state == 21 || state == 25 || state == 80);
- if (state == 21) state = 22;
- if (state == 25) state = 26;
- if (state == 80) state = 81;;
- return nil;
- } else if (sel == @selector(lyingClassMethod)) {
- testassert(state == 31 || state == 35);
- if (state == 31) state = 32;
- if (state == 35) state = 36;
- return nil;
- }
- fail("+forward:: shouldn't be called with sel %s", sel_getName(sel));
- return nil;
- }
- else {
- // self is not a class object
- if (sel == @selector(missingInstanceMethod)) {
- testassert(state == 61 || state == 65);
- if (state == 61) state = 62;
- if (state == 65) state = 66;
- return nil;
- } else if (sel == @selector(lyingInstanceMethod)) {
- testassert(state == 71 || state == 75);
- if (state == 71) state = 72;
- if (state == 75) state = 76;
- return nil;
- }
- fail("-forward:: shouldn't be called with sel %s", sel_getName(sel));
- return nil;
- }
-}
-
-
-static id classMethod_c(id __unused self, SEL __unused sel)
-{
- testassert(state == 4 || state == 10);
- if (state == 4) state = 5;
- if (state == 10) state = 11;
- return [Super class];
-}
-
-static id instanceMethod_c(id __unused self, SEL __unused sel)
-{
- testassert(state == 41 || state == 50);
- if (state == 41) state = 42;
- if (state == 50) state = 51;
- return [Sub class];
-}
-
-
-@implementation Sub
-
-+(void)method2 { }
-+(void)method3 { }
-+(void)method4 { }
-+(void)method5 { }
-
-+(void)initialize {
- if (self == [Sub class]) {
- testassert(state == 2);
- state = 3;
- }
-}
-
-+(BOOL)resolveClassMethod:(SEL)sel
-{
- if (sel == @selector(classMethod)) {
- testassert(state == 3);
- state = 4;
- class_addMethod(object_getClass(self), sel, (IMP)&classMethod_c, "");
- return YES;
- } else if (sel == @selector(missingClassMethod)) {
- testassert(state == 20);
- state = 21;
- return NO;
- } else if (sel == @selector(lyingClassMethod)) {
- testassert(state == 30);
- state = 31;
- return YES; // lie
- } else {
- fail("+resolveClassMethod: called incorrectly (sel %s)",
- sel_getName(sel));
- return NO;
- }
-}
-
-+(BOOL)resolveInstanceMethod:(SEL)sel
-{
- if (sel == @selector(instanceMethod)) {
- testassert(state == 40);
- state = 41;
- class_addMethod(self, sel, (IMP)instanceMethod_c, "");
- return YES;
- } else if (sel == @selector(missingInstanceMethod)) {
- testassert(state == 60);
- state = 61;
- return NO;
- } else if (sel == @selector(lyingInstanceMethod)) {
- testassert(state == 70);
- state = 71;
- return YES; // lie
- } else {
- fail("+resolveInstanceMethod: called incorrectly (sel %s)",
- sel_getName(sel));
- return NO;
- }
-}
-
-@end
-
-@interface Super (MissingMethods)
-+(id)missingClassMethod;
-@end
-
-@interface Sub (ResolvedMethods)
-+(id)classMethod;
--(id)instanceMethod;
-+(id)missingClassMethod;
--(id)missingInstanceMethod;
-+(id)lyingClassMethod;
--(id)lyingInstanceMethod;
-@end
-
-
-int main()
-{
- Sub *s;
- id ret;
-
- objc_setForwardHandler((void*)&forward_handler, NULL);
-
- // Be ready for ARC to retain the class object and call +initialize early
- state = 1;
-
- Class dup = objc_duplicateClass(objc_getClass("Sub"), "Sub_copy", 0);
-
- // Resolve a class method
- // +initialize should fire first (if it hasn't already)
- ret = [Sub classMethod];
- testassert(state == 5);
- testassert(ret == [Super class]);
-
- // Call it again, cached
- // Resolver shouldn't be called again.
- state = 10;
- ret = [Sub classMethod];
- testassert(state == 11);
- testassert(ret == [Super class]);
-
- _objc_flush_caches(object_getClass([Sub class]));
-
- // Call a method that won't get resolved
- state = 20;
- ret = [Sub missingClassMethod];
- testassert(state == 22);
- testassert(ret == nil);
-
- // Call it again, cached
- // Resolver shouldn't be called again.
- state = 25;
- ret = [Sub missingClassMethod];
- testassert(state == 26);
- testassert(ret == nil);
-
- _objc_flush_caches(object_getClass([Sub class]));
-
- // Call a method that won't get resolved but the resolver lies about it
- state = 30;
- ret = [Sub lyingClassMethod];
- testassert(state == 32);
- testassert(ret == nil);
-
- // Call it again, cached
- // Resolver shouldn't be called again.
- state = 35;
- ret = [Sub lyingClassMethod];
- testassert(state == 36);
- testassert(ret == nil);
-
- _objc_flush_caches(object_getClass([Sub class]));
-
-
- // Resolve an instance method
- s = [Sub new];
- state = 40;
- ret = [s instanceMethod];
- testassert(state == 42);
- testassert(ret == [Sub class]);
-
- // Call it again, cached
- // Resolver shouldn't be called again.
- state = 50;
- ret = [s instanceMethod];
- testassert(state == 51);
- testassert(ret == [Sub class]);
-
- _objc_flush_caches([Sub class]);
-
- // Call a method that won't get resolved
- state = 60;
- ret = [s missingInstanceMethod];
- testassert(state == 62);
- testassert(ret == nil);
-
- // Call it again, cached
- // Resolver shouldn't be called again.
- state = 65;
- ret = [s missingInstanceMethod];
- testassert(state == 66);
- testassert(ret == nil);
-
- _objc_flush_caches([Sub class]);
-
- // Call a method that won't get resolved but the resolver lies about it
- state = 70;
- ret = [s lyingInstanceMethod];
- testassert(state == 72);
- testassert(ret == nil);
-
- // Call it again, cached
- // Resolver shouldn't be called again.
- state = 75;
- ret = [s lyingInstanceMethod];
- testassert(state == 76);
- testassert(ret == nil);
-
- _objc_flush_caches([Sub class]);
-
- // Call a missing method on a class that doesn't support resolving
- state = 80;
- ret = [Super missingClassMethod];
- testassert(state == 81);
- testassert(ret == nil);
- RELEASE_VAR(s);
-
- // Resolve an instance method on a class duplicated before resolving
- s = [dup new];
- state = 40;
- ret = [s instanceMethod];
- testassert(state == 42);
- testassert(ret == [Sub class]);
-
- // Call it again, cached
- // Resolver shouldn't be called again.
- state = 50;
- ret = [s instanceMethod];
- testassert(state == 51);
- testassert(ret == [Sub class]);
- RELEASE_VAR(s);
-
- succeed(__FILE__);
- return 0;
-}
-
-#endif
-
+++ /dev/null
-// TEST_CONFIG CC=clang MEM=mrc
-// TEST_CFLAGS -Os
-
-#include "test.h"
-#include "testroot.i"
-
-#if __i386__
-
-int main()
-{
- // no optimization on i386 (neither Mac nor Simulator)
- succeed(__FILE__);
-}
-
-#else
-
-#include <objc/objc-internal.h>
-#include <objc/objc-abi.h>
-#include <Foundation/Foundation.h>
-
-@interface TestObject : TestRoot @end
-@implementation TestObject @end
-
-
-#ifdef __arm__
-# define MAGIC asm volatile("mov r7, r7")
-# define NOT_MAGIC asm volatile("mov r6, r6")
-#elif __arm64__
-# define MAGIC asm volatile("mov x29, x29")
-# define NOT_MAGIC asm volatile("mov x28, x28")
-#elif __x86_64__
-# define MAGIC asm volatile("")
-# define NOT_MAGIC asm volatile("nop")
-#else
-# error unknown architecture
-#endif
-
-
-int
-main()
-{
- TestObject *tmp, *obj;
-
-#ifdef __x86_64__
- // need to get DYLD to resolve the stubs on x86
- PUSH_POOL {
- TestObject *warm_up = [[TestObject alloc] init];
- testassert(warm_up);
- warm_up = objc_retainAutoreleasedReturnValue(warm_up);
- warm_up = objc_unsafeClaimAutoreleasedReturnValue(warm_up);
- [warm_up release];
- warm_up = nil;
- } POP_POOL;
-#endif
-
- testprintf(" Successful +1 -> +1 handshake\n");
-
- PUSH_POOL {
- obj = [[TestObject alloc] init];
- testassert(obj);
-
- TestRootRetain = 0;
- TestRootRelease = 0;
- TestRootAutorelease = 0;
- TestRootDealloc = 0;
-
- tmp = objc_autoreleaseReturnValue(obj);
- MAGIC;
- tmp = objc_retainAutoreleasedReturnValue(tmp);
-
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 0);
- testassert(TestRootRelease == 0);
- testassert(TestRootAutorelease == 0);
-
- [tmp release];
- testassert(TestRootDealloc == 1);
- testassert(TestRootRetain == 0);
- testassert(TestRootRelease == 1);
- testassert(TestRootAutorelease == 0);
-
- } POP_POOL;
-
- testprintf("Unsuccessful +1 -> +1 handshake\n");
-
- PUSH_POOL {
- obj = [[TestObject alloc] init];
- testassert(obj);
-
- TestRootRetain = 0;
- TestRootRelease = 0;
- TestRootAutorelease = 0;
- TestRootDealloc = 0;
-
- tmp = objc_autoreleaseReturnValue(obj);
- NOT_MAGIC;
- tmp = objc_retainAutoreleasedReturnValue(tmp);
-
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 0);
- testassert(TestRootAutorelease == 1);
-
- [tmp release];
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 1);
- testassert(TestRootAutorelease == 1);
-
- } POP_POOL;
- testassert(TestRootDealloc == 1);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 2);
- testassert(TestRootAutorelease == 1);
-
-
- testprintf(" Successful +0 -> +1 handshake\n");
-
- PUSH_POOL {
- obj = [[TestObject alloc] init];
- testassert(obj);
-
- TestRootRetain = 0;
- TestRootRelease = 0;
- TestRootAutorelease = 0;
- TestRootDealloc = 0;
-
- tmp = objc_retainAutoreleaseReturnValue(obj);
- MAGIC;
- tmp = objc_retainAutoreleasedReturnValue(tmp);
-
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 0);
- testassert(TestRootAutorelease == 0);
-
- [tmp release];
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 1);
- testassert(TestRootAutorelease == 0);
-
- [tmp release];
- testassert(TestRootDealloc == 1);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 2);
- testassert(TestRootAutorelease == 0);
-
- } POP_POOL;
-
- testprintf("Unsuccessful +0 -> +1 handshake\n");
-
- PUSH_POOL {
- obj = [[TestObject alloc] init];
- testassert(obj);
-
- TestRootRetain = 0;
- TestRootRelease = 0;
- TestRootAutorelease = 0;
- TestRootDealloc = 0;
-
- tmp = objc_retainAutoreleaseReturnValue(obj);
- NOT_MAGIC;
- tmp = objc_retainAutoreleasedReturnValue(tmp);
-
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 2);
- testassert(TestRootRelease == 0);
- testassert(TestRootAutorelease == 1);
-
- [tmp release];
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 2);
- testassert(TestRootRelease == 1);
- testassert(TestRootAutorelease == 1);
-
- [tmp release];
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 2);
- testassert(TestRootRelease == 2);
- testassert(TestRootAutorelease == 1);
-
- } POP_POOL;
- testassert(TestRootDealloc == 1);
- testassert(TestRootRetain == 2);
- testassert(TestRootRelease == 3);
- testassert(TestRootAutorelease == 1);
-
-
- testprintf(" Successful +1 -> +0 handshake\n");
-
- PUSH_POOL {
- obj = [[[TestObject alloc] init] retain];
- testassert(obj);
-
- TestRootRetain = 0;
- TestRootRelease = 0;
- TestRootAutorelease = 0;
- TestRootDealloc = 0;
-
- tmp = objc_autoreleaseReturnValue(obj);
- MAGIC;
- tmp = objc_unsafeClaimAutoreleasedReturnValue(tmp);
-
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 0);
- testassert(TestRootRelease == 1);
- testassert(TestRootAutorelease == 0);
-
- [tmp release];
- testassert(TestRootDealloc == 1);
- testassert(TestRootRetain == 0);
- testassert(TestRootRelease == 2);
- testassert(TestRootAutorelease == 0);
-
- } POP_POOL;
-
- testprintf("Unsuccessful +1 -> +0 handshake\n");
-
- PUSH_POOL {
- obj = [[[TestObject alloc] init] retain];
- testassert(obj);
-
- TestRootRetain = 0;
- TestRootRelease = 0;
- TestRootAutorelease = 0;
- TestRootDealloc = 0;
-
- tmp = objc_autoreleaseReturnValue(obj);
- NOT_MAGIC;
- tmp = objc_unsafeClaimAutoreleasedReturnValue(tmp);
-
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 0);
- testassert(TestRootRelease == 0);
- testassert(TestRootAutorelease == 1);
-
- [tmp release];
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 0);
- testassert(TestRootRelease == 1);
- testassert(TestRootAutorelease == 1);
-
- } POP_POOL;
- testassert(TestRootDealloc == 1);
- testassert(TestRootRetain == 0);
- testassert(TestRootRelease == 2);
- testassert(TestRootAutorelease == 1);
-
-
- testprintf(" Successful +0 -> +0 handshake\n");
-
- PUSH_POOL {
- obj = [[TestObject alloc] init];
- testassert(obj);
-
- TestRootRetain = 0;
- TestRootRelease = 0;
- TestRootAutorelease = 0;
- TestRootDealloc = 0;
-
- tmp = objc_retainAutoreleaseReturnValue(obj);
- MAGIC;
- tmp = objc_unsafeClaimAutoreleasedReturnValue(tmp);
-
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 0);
- testassert(TestRootRelease == 0);
- testassert(TestRootAutorelease == 0);
-
- [tmp release];
- testassert(TestRootDealloc == 1);
- testassert(TestRootRetain == 0);
- testassert(TestRootRelease == 1);
- testassert(TestRootAutorelease == 0);
-
- } POP_POOL;
-
- testprintf("Unsuccessful +0 -> +0 handshake\n");
-
- PUSH_POOL {
- obj = [[TestObject alloc] init];
- testassert(obj);
-
- TestRootRetain = 0;
- TestRootRelease = 0;
- TestRootAutorelease = 0;
- TestRootDealloc = 0;
-
- tmp = objc_retainAutoreleaseReturnValue(obj);
- NOT_MAGIC;
- tmp = objc_unsafeClaimAutoreleasedReturnValue(tmp);
-
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 0);
- testassert(TestRootAutorelease == 1);
-
- [tmp release];
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 1);
- testassert(TestRootAutorelease == 1);
-
- } POP_POOL;
- testassert(TestRootDealloc == 1);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 2);
- testassert(TestRootAutorelease == 1);
-
- succeed(__FILE__);
-
- return 0;
-}
-
-
-#endif
+++ /dev/null
-// TEST_CFLAGS -Os -framework Foundation
-// TEST_DISABLED pending clang support for rdar://20530049
-
-#include "test.h"
-#include "testroot.i"
-
-#if __i386__
-
-int main()
-{
- // no optimization on i386 (neither Mac nor Simulator)
- succeed(__FILE__);
-}
-
-#else
-
-#include <objc/objc-internal.h>
-#include <objc/objc-abi.h>
-#include <Foundation/Foundation.h>
-
-@interface TestObject : TestRoot @end
-@implementation TestObject @end
-
-
-#ifdef __arm__
-# define MAGIC asm volatile("mov r7, r7")
-# define NOT_MAGIC asm volatile("mov r6, r6")
-#elif __arm64__
-# define MAGIC asm volatile("mov x29, x29")
-# define NOT_MAGIC asm volatile("mov x28, x28")
-#elif __x86_64__
-# define MAGIC asm volatile("")
-# define NOT_MAGIC asm volatile("nop")
-#else
-# error unknown architecture
-#endif
-
-
-@interface Tester : NSObject @end
-@implementation Tester {
-@public
- id ivar;
-}
-
--(id) return0 {
- return ivar;
-}
--(id) return1 {
- id x = ivar;
- [x self];
- return x;
-}
-
-@end
-
-OBJC_EXPORT
-id
-objc_retainAutoreleasedReturnValue(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0);
-
-// Accept a value returned through a +0 autoreleasing convention for use at +0.
-OBJC_EXPORT
-id
-objc_unsafeClaimAutoreleasedReturnValue(id obj)
- __OSX_AVAILABLE_STARTING(__MAC_10_11, __IPHONE_9_0);
-
-
-int
-main()
-{
- TestObject *obj;
- Tester *tt = [Tester new];
-
-#ifdef __x86_64__
- // need to get DYLD to resolve the stubs on x86
- PUSH_POOL {
- TestObject *warm_up = [[TestObject alloc] init];
- testassert(warm_up);
- warm_up = objc_retainAutoreleasedReturnValue(warm_up);
- warm_up = objc_unsafeClaimAutoreleasedReturnValue(warm_up);
- warm_up = nil;
- } POP_POOL;
-#endif
-
- testprintf(" Successful +1 -> +1 handshake\n");
-
- PUSH_POOL {
- obj = [[TestObject alloc] init];
- testassert(obj);
- tt->ivar = obj;
- obj = nil;
-
- TestRootRetain = 0;
- TestRootRelease = 0;
- TestRootAutorelease = 0;
- TestRootDealloc = 0;
-
- TestObject *tmp = [tt return1];
-
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 0);
- testassert(TestRootAutorelease == 0);
-
- tt->ivar = nil;
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 1);
- testassert(TestRootAutorelease == 0);
-
- tmp = nil;
- testassert(TestRootDealloc == 1);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 2);
- testassert(TestRootAutorelease == 0);
-
- } POP_POOL;
-
- testprintf(" Successful +0 -> +0 handshake\n");
-
- PUSH_POOL {
- obj = [[TestObject alloc] init];
- testassert(obj);
- tt->ivar = obj;
- obj = nil;
-
- TestRootRetain = 0;
- TestRootRelease = 0;
- TestRootAutorelease = 0;
- TestRootDealloc = 0;
-
- __unsafe_unretained TestObject *tmp = [tt return0];
-
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 0);
- testassert(TestRootRelease == 0);
- testassert(TestRootAutorelease == 0);
-
- tmp = nil;
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 0);
- testassert(TestRootRelease == 0);
- testassert(TestRootAutorelease == 0);
-
- tt->ivar = nil;
- testassert(TestRootDealloc == 1);
- testassert(TestRootRetain == 0);
- testassert(TestRootRelease == 1);
- testassert(TestRootAutorelease == 0);
-
- } POP_POOL;
-
-
- testprintf(" Successful +1 -> +0 handshake\n");
-
- PUSH_POOL {
- obj = [[TestObject alloc] init];
- testassert(obj);
- tt->ivar = obj;
- obj = nil;
-
- TestRootRetain = 0;
- TestRootRelease = 0;
- TestRootAutorelease = 0;
- TestRootDealloc = 0;
-
- __unsafe_unretained TestObject *tmp = [tt return1];
-
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 1);
- testassert(TestRootAutorelease == 0);
-
- tmp = nil;
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 1);
- testassert(TestRootAutorelease == 0);
-
- tt->ivar = nil;
- testassert(TestRootDealloc == 1);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 2);
- testassert(TestRootAutorelease == 0);
-
- } POP_POOL;
-
-
- testprintf(" Successful +0 -> +1 handshake\n");
-
- PUSH_POOL {
- obj = [[TestObject alloc] init];
- testassert(obj);
- tt->ivar = obj;
- obj = nil;
-
- TestRootRetain = 0;
- TestRootRelease = 0;
- TestRootAutorelease = 0;
- TestRootDealloc = 0;
-
- TestObject *tmp = [tt return0];
-
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 0);
- testassert(TestRootAutorelease == 0);
-
- tmp = nil;
- testassert(TestRootDealloc == 0);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 1);
- testassert(TestRootAutorelease == 0);
-
- tt->ivar = nil;
- testassert(TestRootDealloc == 1);
- testassert(TestRootRetain == 1);
- testassert(TestRootRelease == 2);
- testassert(TestRootAutorelease == 0);
-
- } POP_POOL;
-
-
-
- succeed(__FILE__);
-
- return 0;
-}
-
-
-#endif
+++ /dev/null
-// Test OBJC_DEBUG_POOL_ALLOCATION (which is also enabled by MallocStackLogging)
-
-// TEST_ENV OBJC_DEBUG_POOL_ALLOCATION=YES
-// TEST_CFLAGS -framework Foundation
-// TEST_CONFIG MEM=mrc
-
-#include "test.h"
-
-#define FOUNDATION 0
-#define NAME "rr-autorelease-stacklogging"
-
-#include "rr-autorelease2.m"
+++ /dev/null
-// TEST_CFLAGS -framework Foundation
-// TEST_CONFIG MEM=mrc
-
-#include "test.h"
-
-#define FOUNDATION 0
-#define NAME "rr-autorelease"
-
-#include "rr-autorelease2.m"
+++ /dev/null
-// Define FOUNDATION=1 for NSObject and NSAutoreleasePool
-// Define FOUNDATION=0 for _objc_root* and _objc_autoreleasePool*
-
-#include "test.h"
-
-#if FOUNDATION
-# define RR_PUSH() [[NSAutoreleasePool alloc] init]
-# define RR_POP(p) [(id)p release]
-# define RR_RETAIN(o) [o retain]
-# define RR_RELEASE(o) [o release]
-# define RR_AUTORELEASE(o) [o autorelease]
-# define RR_RETAINCOUNT(o) [o retainCount]
-#else
-# define RR_PUSH() _objc_autoreleasePoolPush()
-# define RR_POP(p) _objc_autoreleasePoolPop(p)
-# define RR_RETAIN(o) _objc_rootRetain((id)o)
-# define RR_RELEASE(o) _objc_rootRelease((id)o)
-# define RR_AUTORELEASE(o) _objc_rootAutorelease((id)o)
-# define RR_RETAINCOUNT(o) _objc_rootRetainCount((id)o)
-#endif
-
-#include <objc/objc-internal.h>
-#include <Foundation/Foundation.h>
-
-static int state;
-static pthread_attr_t smallstack;
-
-#define NESTED_COUNT 8
-
-@interface Deallocator : NSObject @end
-@implementation Deallocator
--(void) dealloc
-{
- // testprintf("-[Deallocator %p dealloc]\n", self);
- state++;
- [super dealloc];
-}
-@end
-
-@interface AutoreleaseDuringDealloc : NSObject @end
-@implementation AutoreleaseDuringDealloc
--(void) dealloc
-{
- state++;
- RR_AUTORELEASE([[Deallocator alloc] init]);
- [super dealloc];
-}
-@end
-
-@interface AutoreleasePoolDuringDealloc : NSObject @end
-@implementation AutoreleasePoolDuringDealloc
--(void) dealloc
-{
- // caller's pool
- for (int i = 0; i < NESTED_COUNT; i++) {
- RR_AUTORELEASE([[Deallocator alloc] init]);
- }
-
- // local pool, popped
- void *pool = RR_PUSH();
- for (int i = 0; i < NESTED_COUNT; i++) {
- RR_AUTORELEASE([[Deallocator alloc] init]);
- }
- RR_POP(pool);
-
- // caller's pool again
- for (int i = 0; i < NESTED_COUNT; i++) {
- RR_AUTORELEASE([[Deallocator alloc] init]);
- }
-
-#if FOUNDATION
- {
- static bool warned;
- if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
- warned = true;
- }
- state += NESTED_COUNT;
-#else
- // local pool, not popped
- RR_PUSH();
- for (int i = 0; i < NESTED_COUNT; i++) {
- RR_AUTORELEASE([[Deallocator alloc] init]);
- }
-#endif
-
- [super dealloc];
-}
-@end
-
-void *autorelease_lots_fn(void *singlePool)
-{
- // Enough to blow out the stack if AutoreleasePoolPage is recursive.
- const int COUNT = 1024*1024;
- state = 0;
-
- int p = 0;
- void **pools = (void**)malloc((COUNT+1) * sizeof(void*));
- pools[p++] = RR_PUSH();
-
- id obj = RR_AUTORELEASE([[Deallocator alloc] init]);
-
- // last pool has only 1 autorelease in it
- pools[p++] = RR_PUSH();
-
- for (int i = 0; i < COUNT; i++) {
- if (rand() % 1000 == 0 && !singlePool) {
- pools[p++] = RR_PUSH();
- } else {
- RR_AUTORELEASE(RR_RETAIN(obj));
- }
- }
-
- testassert(state == 0);
- while (--p) {
- RR_POP(pools[p]);
- }
- testassert(state == 0);
- testassert(RR_RETAINCOUNT(obj) == 1);
- RR_POP(pools[0]);
- testassert(state == 1);
- free(pools);
-
- return NULL;
-}
-
-void *nsthread_fn(void *arg __unused)
-{
- [NSThread currentThread];
- void *pool = RR_PUSH();
- RR_AUTORELEASE([[Deallocator alloc] init]);
- RR_POP(pool);
- return NULL;
-}
-
-void cycle(void)
-{
- // Normal autorelease.
- testprintf("-- Normal autorelease.\n");
- {
- void *pool = RR_PUSH();
- state = 0;
- RR_AUTORELEASE([[Deallocator alloc] init]);
- testassert(state == 0);
- RR_POP(pool);
- testassert(state == 1);
- }
-
- // Autorelease during dealloc during autoreleasepool-pop.
- // That autorelease is handled by the popping pool, not the one above it.
- testprintf("-- Autorelease during dealloc during autoreleasepool-pop.\n");
- {
- void *pool = RR_PUSH();
- state = 0;
- RR_AUTORELEASE([[AutoreleaseDuringDealloc alloc] init]);
- testassert(state == 0);
- RR_POP(pool);
- testassert(state == 2);
- }
-
- // Autorelease pool during dealloc during autoreleasepool-pop.
- testprintf("-- Autorelease pool during dealloc during autoreleasepool-pop.\n");
- {
- void *pool = RR_PUSH();
- state = 0;
- RR_AUTORELEASE([[AutoreleasePoolDuringDealloc alloc] init]);
- testassert(state == 0);
- RR_POP(pool);
- testassert(state == 4 * NESTED_COUNT);
- }
-
- // Top-level thread pool popped normally.
- testprintf("-- Thread-level pool popped normally.\n");
- {
- state = 0;
- testonthread(^{
- void *pool = RR_PUSH();
- RR_AUTORELEASE([[Deallocator alloc] init]);
- RR_POP(pool);
- });
- testassert(state == 1);
- }
-
-
- // Autorelease with no pool.
- testprintf("-- Autorelease with no pool.\n");
- {
- state = 0;
- testonthread(^{
- RR_AUTORELEASE([[Deallocator alloc] init]);
- });
- testassert(state == 1);
- }
-
- // Autorelease with no pool after popping the top-level pool.
- testprintf("-- Autorelease with no pool after popping the last pool.\n");
- {
- state = 0;
- testonthread(^{
- void *pool = RR_PUSH();
- RR_AUTORELEASE([[Deallocator alloc] init]);
- RR_POP(pool);
- RR_AUTORELEASE([[Deallocator alloc] init]);
- });
- testassert(state == 2);
- }
-
- // Top-level thread pool not popped.
- // The runtime should clean it up.
-#if FOUNDATION
- {
- static bool warned;
- if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
- warned = true;
- }
-#else
- testprintf("-- Thread-level pool not popped.\n");
- {
- state = 0;
- testonthread(^{
- RR_PUSH();
- RR_AUTORELEASE([[Deallocator alloc] init]);
- // pool not popped
- });
- testassert(state == 1);
- }
-#endif
-
- // Intermediate pool not popped.
- // Popping the containing pool should clean up the skipped pool first.
-#if FOUNDATION
- {
- static bool warned;
- if (!warned) testwarn("rdar://7138159 NSAutoreleasePool leaks");
- warned = true;
- }
-#else
- testprintf("-- Intermediate pool not popped.\n");
- {
- void *pool = RR_PUSH();
- void *pool2 = RR_PUSH();
- RR_AUTORELEASE([[Deallocator alloc] init]);
- state = 0;
- (void)pool2; // pool2 not popped
- RR_POP(pool);
- testassert(state == 1);
- }
-#endif
-}
-
-
-static void
-slow_cycle(void)
-{
- // Large autorelease stack.
- // Do this only once because it's slow.
- testprintf("-- Large autorelease stack.\n");
- {
- // limit stack size: autorelease pop should not be recursive
- pthread_t th;
- pthread_create(&th, &smallstack, &autorelease_lots_fn, NULL);
- pthread_join(th, NULL);
- }
-
- // Single large autorelease pool.
- // Do this only once because it's slow.
- testprintf("-- Large autorelease pool.\n");
- {
- // limit stack size: autorelease pop should not be recursive
- pthread_t th;
- pthread_create(&th, &smallstack, &autorelease_lots_fn, (void*)1);
- pthread_join(th, NULL);
- }
-}
-
-
-int main()
-{
- pthread_attr_init(&smallstack);
- pthread_attr_setstacksize(&smallstack, 16384);
-
- // inflate the refcount side table so it doesn't show up in leak checks
- {
- int count = 10000;
- id *objs = (id *)malloc(count*sizeof(id));
- for (int i = 0; i < count; i++) {
- objs[i] = RR_RETAIN([NSObject new]);
- }
- for (int i = 0; i < count; i++) {
- RR_RELEASE(objs[i]);
- RR_RELEASE(objs[i]);
- }
- free(objs);
- }
-
-#if FOUNDATION
- // inflate NSAutoreleasePool's instance cache
- {
- int count = 32;
- id *objs = (id *)malloc(count * sizeof(id));
- for (int i = 0; i < count; i++) {
- objs[i] = [[NSAutoreleasePool alloc] init];
- }
- for (int i = 0; i < count; i++) {
- [objs[count-i-1] release];
- }
-
- free(objs);
- }
-#endif
-
- // preheat
- {
- for (int i = 0; i < 100; i++) {
- cycle();
- }
-
- slow_cycle();
- }
-
- // check for leaks using top-level pools
- {
- leak_mark();
-
- for (int i = 0; i < 1000; i++) {
- cycle();
- }
-
- leak_check(0);
-
- slow_cycle();
-
- leak_check(0);
- }
-
- // check for leaks using pools not at top level
- void *pool = RR_PUSH();
- {
- leak_mark();
-
- for (int i = 0; i < 1000; i++) {
- cycle();
- }
-
- leak_check(0);
-
- slow_cycle();
-
- leak_check(0);
- }
- RR_POP(pool);
-
- // NSThread.
- // Can't leak check this because it's too noisy.
- testprintf("-- NSThread.\n");
- {
- pthread_t th;
- pthread_create(&th, &smallstack, &nsthread_fn, 0);
- pthread_join(th, NULL);
- }
-
- // NO LEAK CHECK HERE
-
- succeed(NAME);
-}
+++ /dev/null
-// TEST_CFLAGS -framework Foundation
-// TEST_CONFIG MEM=mrc
-
-#define FOUNDATION 1
-#define NAME "rr-nsautorelease"
-
-#include "rr-autorelease2.m"
+++ /dev/null
-// TEST_CFLAGS -framework Foundation
-// TEST_CONFIG MEM=mrc ARCH=x86_64
-
-// Stress-test nonpointer isa's side table retain count transfers.
-
-// x86_64 only. arm64's side table limit is high enough that bugs
-// are harder to reproduce.
-
-#include "test.h"
-#import <Foundation/Foundation.h>
-
-#define OBJECTS 1
-#define LOOPS 256
-#define THREADS 16
-#if __x86_64__
-# define RC_HALF (1ULL<<7)
-#else
-# error sorry
-#endif
-#define RC_DELTA RC_HALF
-
-static bool Deallocated = false;
-@interface Deallocator : NSObject @end
-@implementation Deallocator
--(void)dealloc {
- Deallocated = true;
- [super dealloc];
-}
-@end
-
-// This is global to avoid extra retains by the dispatch block objects.
-static Deallocator *obj;
-
-int main() {
- dispatch_queue_t queue =
- dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-
- for (size_t i = 0; i < OBJECTS; i++) {
- obj = [Deallocator new];
-
- dispatch_apply(THREADS, queue, ^(size_t i __unused) {
- for (size_t a = 0; a < LOOPS; a++) {
- for (size_t b = 0; b < RC_DELTA; b++) {
- [obj retain];
- }
- for (size_t b = 0; b < RC_DELTA; b++) {
- [obj release];
- }
- }
- });
-
- testassert(!Deallocated);
- [obj release];
- testassert(Deallocated);
- Deallocated = false;
- }
-
- succeed(__FILE__);
-}
+++ /dev/null
-/*
-TEST_RUN_OUTPUT
-objc\[\d+\]: class `SwiftV1Class\' not linked into application
-objc\[\d+\]: class `DoesNotExist\' not linked into application
-OK: runtime.m
-OR
-confused by Foundation
-OK: runtime.m
-END
-*/
-
-
-#include "test.h"
-#include "testroot.i"
-#include <string.h>
-#include <dlfcn.h>
-#include <mach-o/ldsyms.h>
-#include <objc/objc-runtime.h>
-
-#if __has_feature(objc_arc)
-
-int main()
-{
- testwarn("rdar://11368528 confused by Foundation");
- fprintf(stderr, "confused by Foundation\n");
- succeed(__FILE__);
-}
-
-#else
-
-@interface Sub : TestRoot @end
-@implementation Sub @end
-
-#if __OBJC2__
-# define TEST_SWIFT 1
-#else
-# define TEST_SWIFT 0
-#endif
-
-#define SwiftV1MangledName "_TtC6Module12SwiftV1Class"
-#define SwiftV1MangledName2 "_TtC2Sw13SwiftV1Class2"
-#define SwiftV1MangledName3 "_TtCSs13SwiftV1Class3"
-#define SwiftV1MangledName4 "_TtC6Swiftt13SwiftV1Class4"
-
-#if TEST_SWIFT
-__attribute__((objc_runtime_name(SwiftV1MangledName)))
-@interface SwiftV1Class : TestRoot @end
-@implementation SwiftV1Class @end
-
-__attribute__((objc_runtime_name(SwiftV1MangledName2)))
-@interface SwiftV1Class2 : TestRoot @end
-@implementation SwiftV1Class2 @end
-
-__attribute__((objc_runtime_name(SwiftV1MangledName3)))
-@interface SwiftV1Class3 : TestRoot @end
-@implementation SwiftV1Class3 @end
-
-__attribute__((objc_runtime_name(SwiftV1MangledName4)))
-@interface SwiftV1Class4 : TestRoot @end
-@implementation SwiftV1Class4 @end
-#endif
-
-
-int main()
-{
- Class list[100];
- Class *list2;
- unsigned int count, count0, count2;
- unsigned int i;
- int foundTestRoot;
- int foundSub;
- int foundSwiftV1;
- int foundSwiftV1class2;
- int foundSwiftV1class3;
- int foundSwiftV1class4;
- const char **names;
- Dl_info info;
-
- [TestRoot class];
-
- // This shouldn't touch any classes.
- dladdr(&_mh_execute_header, &info);
- names = objc_copyClassNamesForImage(info.dli_fname, &count);
- testassert(names);
-#if TEST_SWIFT
- testassert(count == 6);
-#else
- testassert(count == 2);
-#endif
- testassert(names[count] == NULL);
- foundTestRoot = 0;
- foundSub = 0;
- foundSwiftV1 = 0;
- foundSwiftV1class2 = 0;
- foundSwiftV1class3 = 0;
- foundSwiftV1class4 = 0;
- for (i = 0; i < count; i++) {
- if (0 == strcmp(names[i], "TestRoot")) foundTestRoot++;
- if (0 == strcmp(names[i], "Sub")) foundSub++;
- if (0 == strcmp(names[i], "Module.SwiftV1Class")) foundSwiftV1++;
- if (0 == strcmp(names[i], "Sw.SwiftV1Class2")) foundSwiftV1class2++;
- if (0 == strcmp(names[i], "Swift.SwiftV1Class3")) foundSwiftV1class3++;
- if (0 == strcmp(names[i], "Swiftt.SwiftV1Class4")) foundSwiftV1class4++;
- }
- testassert(foundTestRoot == 1);
- testassert(foundSub == 1);
-#if TEST_SWIFT
- testassert(foundSwiftV1 == 1);
- testassert(foundSwiftV1class2 == 1);
- testassert(foundSwiftV1class3 == 1);
- testassert(foundSwiftV1class4 == 1);
-#endif
-
-
- // class Sub hasn't been touched - make sure it's in the class list too
- count0 = objc_getClassList(NULL, 0);
- testassert(count0 >= 2 && count0 < 100);
-
- list[count0-1] = NULL;
- count = objc_getClassList(list, count0-1);
- testassert(list[count0-1] == NULL);
- testassert(count == count0);
-
- count = objc_getClassList(list, count0);
- testassert(count == count0);
-
- for (i = 0; i < count; i++) {
- testprintf("%s\n", class_getName(list[i]));
- }
-
- foundTestRoot = 0;
- foundSub = 0;
- foundSwiftV1 = 0;
- foundSwiftV1class2 = 0;
- foundSwiftV1class3 = 0;
- foundSwiftV1class4 = 0;
- for (i = 0; i < count; i++) {
- if (0 == strcmp(class_getName(list[i]), "TestRoot")) foundTestRoot++;
- if (0 == strcmp(class_getName(list[i]), "Sub")) foundSub++;
- if (0 == strcmp(class_getName(list[i]), "Module.SwiftV1Class")) foundSwiftV1++;
- if (0 == strcmp(class_getName(list[i]), "Sw.SwiftV1Class2")) foundSwiftV1class2++;
- if (0 == strcmp(class_getName(list[i]), "Swift.SwiftV1Class3")) foundSwiftV1class3++;
- if (0 == strcmp(class_getName(list[i]), "Swiftt.SwiftV1Class4")) foundSwiftV1class4++;
- // list should be non-meta classes only
- testassert(!class_isMetaClass(list[i]));
- }
- testassert(foundTestRoot == 1);
- testassert(foundSub == 1);
-#if TEST_SWIFT
- testassert(foundSwiftV1 == 1);
- testassert(foundSwiftV1class2 == 1);
- testassert(foundSwiftV1class3 == 1);
- testassert(foundSwiftV1class4 == 1);
-#endif
-
- // fixme check class handler
- testassert(objc_getClass("TestRoot") == [TestRoot class]);
-#if TEST_SWIFT
- testassert(objc_getClass("Module.SwiftV1Class") == [SwiftV1Class class]);
- testassert(objc_getClass(SwiftV1MangledName) == [SwiftV1Class class]);
- testassert(objc_getClass("Sw.SwiftV1Class2") == [SwiftV1Class2 class]);
- testassert(objc_getClass(SwiftV1MangledName2) == [SwiftV1Class2 class]);
- testassert(objc_getClass("Swift.SwiftV1Class3") == [SwiftV1Class3 class]);
- testassert(objc_getClass(SwiftV1MangledName3) == [SwiftV1Class3 class]);
- testassert(objc_getClass("Swiftt.SwiftV1Class4") == [SwiftV1Class4 class]);
- testassert(objc_getClass(SwiftV1MangledName4) == [SwiftV1Class4 class]);
-#endif
- testassert(objc_getClass("SwiftV1Class") == nil);
- testassert(objc_getClass("DoesNotExist") == nil);
- testassert(objc_getClass(NULL) == nil);
-
- testassert(objc_getMetaClass("TestRoot") == object_getClass([TestRoot class]));
-#if TEST_SWIFT
- testassert(objc_getMetaClass("Module.SwiftV1Class") == object_getClass([SwiftV1Class class]));
- testassert(objc_getMetaClass(SwiftV1MangledName) == object_getClass([SwiftV1Class class]));
-#endif
- testassert(objc_getMetaClass("SwiftV1Class") == nil);
- testassert(objc_getMetaClass("DoesNotExist") == nil);
- testassert(objc_getMetaClass(NULL) == nil);
-
- // fixme check class no handler
- testassert(objc_lookUpClass("TestRoot") == [TestRoot class]);
-#if TEST_SWIFT
- testassert(objc_lookUpClass("Module.SwiftV1Class") == [SwiftV1Class class]);
- testassert(objc_lookUpClass(SwiftV1MangledName) == [SwiftV1Class class]);
-#endif
- testassert(objc_lookUpClass("SwiftV1Class") == nil);
- testassert(objc_lookUpClass("DoesNotExist") == nil);
- testassert(objc_lookUpClass(NULL) == nil);
-
- testassert(! object_isClass(nil));
- testassert(! object_isClass([TestRoot new]));
- testassert(object_isClass([TestRoot class]));
- testassert(object_isClass(object_getClass([TestRoot class])));
- testassert(object_isClass([Sub class]));
- testassert(object_isClass(object_getClass([Sub class])));
-#if TEST_SWIFT
- testassert(object_isClass([SwiftV1Class class]));
- testassert(object_isClass(object_getClass([SwiftV1Class class])));
-#endif
-
- list2 = objc_copyClassList(&count2);
- testassert(count2 == count);
- testassert(list2);
- testassert(malloc_size(list2) >= (1+count2) * sizeof(Class));
- for (i = 0; i < count; i++) {
- testassert(list[i] == list2[i]);
- }
- testassert(list2[count] == NULL);
- free(list2);
- free(objc_copyClassList(NULL));
-
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include <string.h>
-#include <objc/objc-runtime.h>
-#include <objc/objc-auto.h>
-
-int main()
-{
- // Make sure @selector values are correctly fixed up
- testassert(@selector(foo) == sel_registerName("foo"));
-
- // sel_getName recognizes the zero SEL
- testassert(0 == strcmp("<null selector>", sel_getName(0)));
-
- // GC-ignored selectors.
-#if __has_feature(objc_arc)
-
- // ARC dislikes `@selector(retain)`
-
-#else
-
-# if defined(__i386__)
- // sel_getName recognizes GC-ignored SELs
- if (objc_collectingEnabled()) {
- testassert(0 == strcmp("<ignored selector>",
- sel_getName(@selector(retain))));
- } else {
- testassert(0 == strcmp("retain",
- sel_getName(@selector(retain))));
- }
-
- // _objc_search_builtins() shouldn't crash on GC-ignored SELs
- union {
- SEL sel;
- const char *ptr;
- } u;
- u.sel = @selector(retain);
- testassert(@selector(retain) == sel_registerName(u.ptr));
-# endif
-
-#endif
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -Wno-deprecated-declarations
-
-#include "test.h"
-#include "testroot.i"
-#include <objc/runtime.h>
-
-@interface Super1 : TestRoot @end
-@implementation Super1
-+(int)classMethod { return 1; }
--(int)instanceMethod { return 10000; }
-@end
-
-@interface Super2 : TestRoot @end
-@implementation Super2
-+(int)classMethod { return 2; }
--(int)instanceMethod { return 20000; }
-@end
-
-@interface Sub : Super1 @end
-@implementation Sub
-+(int)classMethod { return [super classMethod] + 100; }
--(int)instanceMethod {
- return [super instanceMethod] + 1000000;
-}
-@end
-
-int main()
-{
- Class cls;
- Sub *obj = [Sub new];
-
- testassert(101 == [[Sub class] classMethod]);
- testassert(1010000 == [obj instanceMethod]);
-
- cls = class_setSuperclass([Sub class], [Super2 class]);
-
- testassert(cls == [Super1 class]);
- testassert(object_getClass(cls) == object_getClass([Super1 class]));
-
- testassert(102 == [[Sub class] classMethod]);
- testassert(1020000 == [obj instanceMethod]);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG MEM=arc,mrc CC=clang LANGUAGE=objc,objc++
-// TEST_CFLAGS -framework Foundation
-
-#if !__OBJC2__
-
-#include "test.h"
-
-int main()
-{
- succeed(__FILE__);
-}
-
-#else
-
-#import <Foundation/Foundation.h>
-#import <Foundation/NSDictionary.h>
-#import <objc/runtime.h>
-#import <objc/objc-abi.h>
-#include "test.h"
-
-@interface TestIndexed : NSObject <NSFastEnumeration> {
- NSMutableArray *indexedValues;
-}
-@property(readonly) NSUInteger count;
-- (id)objectAtIndexedSubscript:(NSUInteger)index;
-- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index;
-@end
-
-@implementation TestIndexed
-
-- (id)init {
- if ((self = [super init])) {
- indexedValues = [NSMutableArray new];
- }
- return self;
-}
-
-#if !__has_feature(objc_arc)
-- (void)dealloc {
- [indexedValues release];
- [super dealloc];
-}
-#endif
-
-- (NSUInteger)count { return [indexedValues count]; }
-- (id)objectAtIndexedSubscript:(NSUInteger)index { return [indexedValues objectAtIndex:index]; }
-- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index {
- if (index == NSNotFound)
- [indexedValues addObject:object];
- else
- [indexedValues replaceObjectAtIndex:index withObject:object];
-}
-
-- (NSString *)description {
- return [NSString stringWithFormat:@"indexedValues = %@", indexedValues];
-}
-
-- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len {
- return [indexedValues countByEnumeratingWithState:state objects:buffer count:len];
-}
-
-
-@end
-
-@interface TestKeyed : NSObject <NSFastEnumeration> {
- NSMutableDictionary *keyedValues;
-}
-@property(readonly) NSUInteger count;
-- (id)objectForKeyedSubscript:(id)key;
-- (void)setObject:(id)object forKeyedSubscript:(id)key;
-@end
-
-@implementation TestKeyed
-
-- (id)init {
- if ((self = [super init])) {
- keyedValues = [NSMutableDictionary new];
- }
- return self;
-}
-
-#if !__has_feature(objc_arc)
-- (void)dealloc {
- [keyedValues release];
- [super dealloc];
-}
-#endif
-
-- (NSUInteger)count { return [keyedValues count]; }
-- (id)objectForKeyedSubscript:(id)key { return [keyedValues objectForKey:key]; }
-- (void)setObject:(id)object forKeyedSubscript:(id)key {
- [keyedValues setObject:object forKey:key];
-}
-
-- (NSString *)description {
- return [NSString stringWithFormat:@"keyedValues = %@", keyedValues];
-}
-
-- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len {
- return [keyedValues countByEnumeratingWithState:state objects:buffer count:len];
-}
-
-@end
-
-int main() {
- PUSH_POOL {
-
-#if __has_feature(objc_bool) // placeholder until we get a more precise macro.
- TestIndexed *testIndexed = [TestIndexed new];
- id objects[] = { @1, @2, @3, @4, @5 };
- size_t i, count = sizeof(objects) / sizeof(id);
- for (i = 0; i < count; ++i) {
- testIndexed[NSNotFound] = objects[i];
- }
- for (i = 0; i < count; ++i) {
- id object = testIndexed[i];
- testassert(object == objects[i]);
- }
- if (getenv("VERBOSE")) {
- i = 0;
- for (id object in testIndexed) {
- NSString *message = [NSString stringWithFormat:@"testIndexed[%zu] = %@\n", i++, object];
- testprintf([message UTF8String]);
- }
- }
-
- TestKeyed *testKeyed = [TestKeyed new];
- id keys[] = { @"One", @"Two", @"Three", @"Four", @"Five" };
- for (i = 0; i < count; ++i) {
- id key = keys[i];
- testKeyed[key] = objects[i];
- }
- for (i = 0; i < count; ++i) {
- id key = keys[i];
- id object = testKeyed[key];
- testassert(object == objects[i]);
- }
- if (getenv("VERBOSE")) {
- for (id key in testKeyed) {
- NSString *message = [NSString stringWithFormat:@"testKeyed[@\"%@\"] = %@\n", key, testKeyed[key]];
- testprintf([message UTF8String]);
- }
- }
-#endif
-
- } POP_POOL;
-
- succeed(__FILE__);
-
- return 0;
-}
-
-// __OBJC2__
-#endif
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include "testroot.i"
-#include <objc/objc-runtime.h>
-
-@interface Sub : TestRoot @end
-@implementation Sub @end
-
-int main()
-{
- // [super ...] messages are tested in msgSend.m
-
- testassert(class_getSuperclass([Sub class]) == [TestRoot class]);
- testassert(class_getSuperclass(object_getClass([Sub class])) == object_getClass([TestRoot class]));
- testassert(class_getSuperclass([TestRoot class]) == Nil);
- testassert(class_getSuperclass(object_getClass([TestRoot class])) == [TestRoot class]);
- testassert(class_getSuperclass(Nil) == Nil);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-
-#include <stdlib.h>
-#include <pthread.h>
-#include <objc/runtime.h>
-#include <objc/objc-sync.h>
-#include <Foundation/NSObject.h>
-#include <System/pthread_machdep.h>
-
-// synchronized stress test
-// Single locked counter incremented by many threads.
-
-#if defined(__arm__)
-#define THREADS 16
-#define COUNT 1024*24
-#else
-// 64 / 1024*24 test takes about 20s on 4x2.6GHz Mac Pro
-#define THREADS 64
-#define COUNT 1024*24
-#endif
-
-static id lock;
-static int count;
-
-static void *threadfn(void *arg)
-{
- int n, d;
- int depth = 1 + (int)(intptr_t)arg % 4;
-
- objc_registerThreadWithCollector();
-
- for (n = 0; n < COUNT; n++) {
- // Lock
- for (d = 0; d < depth; d++) {
- int err = objc_sync_enter(lock);
- testassert(err == OBJC_SYNC_SUCCESS);
- }
-
- // Increment
- count++;
-
- // Unlock
- for (d = 0; d < depth; d++) {
- int err = objc_sync_exit(lock);
- testassert(err == OBJC_SYNC_SUCCESS);
- }
- }
-
- // Verify lack of objc pthread data (should have used sync fast cache)
-#ifdef __PTK_FRAMEWORK_OBJC_KEY0
- testassert(! pthread_getspecific(__PTK_FRAMEWORK_OBJC_KEY0));
-#endif
-
- return NULL;
-}
-
-int main()
-{
- pthread_t threads[THREADS];
- int t;
- int err;
-
- lock = [[NSObject alloc] init];
-
- // Verify objc pthread data on this thread (from +initialize)
- // Worker threads shouldn't have any because of sync fast cache.
-#ifdef __PTK_FRAMEWORK_OBJC_KEY0
- testassert(pthread_getspecific(__PTK_FRAMEWORK_OBJC_KEY0));
-#endif
-
- // Start the threads
- for (t = 0; t < THREADS; t++) {
- pthread_create(&threads[t], NULL, &threadfn, (void*)(intptr_t)t);
- }
-
- // Wait for threads to finish
- for (t = 0; t < THREADS; t++) {
- pthread_join(threads[t], NULL);
- }
-
- // Verify lock: should be available
- // Verify count: should be THREADS*COUNT
- err = objc_sync_enter(lock);
- testassert(err == OBJC_SYNC_SUCCESS);
- testassert(count == THREADS*COUNT);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-
-#include <stdlib.h>
-#include <pthread.h>
-#include <objc/runtime.h>
-#include <objc/objc-sync.h>
-#include <Foundation/NSObject.h>
-
-// synchronized stress test
-// 2-D grid of counters and locks.
-// Each thread increments all counters some number of times.
-// To increment:
-// * thread picks a target [row][col]
-// * thread locks all locks [row][0] to [row][col], possibly recursively
-// * thread increments counter [row][col]
-// * thread unlocks all of the locks
-
-#if defined(__arm__)
-// 16 / 4 / 3 / 1024*8 test takes about 30s on 2nd gen iPod touch
-#define THREADS 16
-#define ROWS 4
-#define COLS 3
-#define COUNT 1024*8
-#else
-// 64 / 4 / 3 / 1024*8 test takes about 20s on 4x2.6GHz Mac Pro
-#define THREADS 64
-#define ROWS 4
-#define COLS 3
-#define COUNT 1024*8
-#endif
-
-static id locks[ROWS][COLS];
-static int counts[ROWS][COLS];
-
-
-static void *threadfn(void *arg)
-{
- int n, d;
- int depth = 1 + (int)(intptr_t)arg % 4;
-
- objc_registerThreadWithCollector();
-
- for (n = 0; n < COUNT; n++) {
- int rrr = rand() % ROWS;
- int ccc = rand() % COLS;
- int rr, cc;
- for (rr = 0; rr < ROWS; rr++) {
- int r = (rrr+rr) % ROWS;
- for (cc = 0; cc < COLS; cc++) {
- int c = (ccc+cc) % COLS;
- int l;
-
- // Lock [r][0..c]
- // ... in that order to prevent deadlock
- for (l = 0; l <= c; l++) {
- for (d = 0; d < depth; d++) {
- int err = objc_sync_enter(locks[r][l]);
- testassert(err == OBJC_SYNC_SUCCESS);
- }
- }
-
- // Increment count [r][c]
- counts[r][c]++;
-
- // Unlock [r][0..c]
- // ... in that order to increase contention
- for (l = 0; l <= c; l++) {
- for (d = 0; d < depth; d++) {
- int err = objc_sync_exit(locks[r][l]);
- testassert(err == OBJC_SYNC_SUCCESS);
- }
- }
- }
- }
- }
-
- return NULL;
-}
-
-int main()
-{
- pthread_t threads[THREADS];
- int r, c, t;
-
- for (r = 0; r < ROWS; r++) {
- for (c = 0; c < COLS; c++) {
- locks[r][c] = [[NSObject alloc] init];
- }
- }
-
- // Start the threads
- for (t = 0; t < THREADS; t++) {
- pthread_create(&threads[t], NULL, &threadfn, (void*)(intptr_t)t);
- }
-
- // Wait for threads to finish
- for (t = 0; t < THREADS; t++) {
- pthread_join(threads[t], NULL);
- }
-
- // Verify locks: all should be available
- // Verify counts: all should be THREADS*COUNT
- for (r = 0; r < ROWS; r++) {
- for (c = 0; c < COLS; c++) {
- int err = objc_sync_enter(locks[r][c]);
- testassert(err == OBJC_SYNC_SUCCESS);
- testassert(counts[r][c] == THREADS*COUNT);
- }
- }
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-
-#include <Foundation/NSObject.h>
-#include <mach/mach.h>
-#include <pthread.h>
-#include <sys/time.h>
-#include <objc/runtime.h>
-#include <objc/objc-sync.h>
-
-// Basic @synchronized tests.
-
-
-#define WAIT_SEC 3
-
-static id obj;
-static semaphore_t go;
-static semaphore_t stop;
-
-void *thread(void *arg __unused)
-{
- int err;
-
- objc_registerThreadWithCollector();
-
- // non-blocking sync_enter
- err = objc_sync_enter(obj);
- testassert(err == OBJC_SYNC_SUCCESS);
-
- semaphore_signal(go);
- // main thread: sync_exit of object locked on some other thread
- semaphore_wait(stop);
-
- err = objc_sync_exit(obj);
- testassert(err == OBJC_SYNC_SUCCESS);
- err = objc_sync_enter(obj);
- testassert(err == OBJC_SYNC_SUCCESS);
-
- semaphore_signal(go);
- // main thread: blocking sync_enter
- testassert(WAIT_SEC/3*3 == WAIT_SEC);
- sleep(WAIT_SEC/3);
- // recursive enter while someone waits
- err = objc_sync_enter(obj);
- testassert(err == OBJC_SYNC_SUCCESS);
- sleep(WAIT_SEC/3);
- // recursive exit while someone waits
- err = objc_sync_exit(obj);
- testassert(err == OBJC_SYNC_SUCCESS);
- sleep(WAIT_SEC/3);
- // sync_exit while someone waits
- err = objc_sync_exit(obj);
- testassert(err == OBJC_SYNC_SUCCESS);
-
- return NULL;
-}
-
-int main()
-{
- pthread_t th;
- int err;
- struct timeval start, end;
-
- obj = [[NSObject alloc] init];
-
- // sync_exit of never-locked object
- err = objc_sync_exit(obj);
- testassert(err == OBJC_SYNC_NOT_OWNING_THREAD_ERROR);
-
- semaphore_create(mach_task_self(), &go, 0, 0);
- semaphore_create(mach_task_self(), &stop, 0, 0);
- pthread_create(&th, NULL, &thread, NULL);
- semaphore_wait(go);
-
- // sync_exit of object locked on some other thread
- err = objc_sync_exit(obj);
- testassert(err == OBJC_SYNC_NOT_OWNING_THREAD_ERROR);
-
- semaphore_signal(stop);
- semaphore_wait(go);
-
- // blocking sync_enter
- gettimeofday(&start, NULL);
- err = objc_sync_enter(obj);
- gettimeofday(&end, NULL);
- testassert(err == OBJC_SYNC_SUCCESS);
- // should have waited more than WAIT_SEC but less than WAIT_SEC+1
- // fixme hack: sleep(1) is ending 500 usec too early on x86_64 buildbot
- // (rdar://6456975)
- testassert(end.tv_sec*1000000LL+end.tv_usec >=
- start.tv_sec*1000000LL+start.tv_usec + WAIT_SEC*1000000LL
- - 3*500 /*hack*/);
- testassert(end.tv_sec*1000000LL+end.tv_usec <
- start.tv_sec*1000000LL+start.tv_usec + (1+WAIT_SEC)*1000000LL);
-
- err = objc_sync_exit(obj);
- testassert(err == OBJC_SYNC_SUCCESS);
-
- err = objc_sync_exit(obj);
- testassert(err == OBJC_SYNC_NOT_OWNING_THREAD_ERROR);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS -framework Foundation
-
-#include "test.h"
-#include <objc/runtime.h>
-#import <Foundation/Foundation.h>
-
-#if OBJC_HAVE_TAGGED_POINTERS
-
-void testTaggedNumber()
-{
- NSNumber *taggedNS = [NSNumber numberWithInt: 1234];
- CFNumberRef taggedCF = (CFNumberRef)objc_unretainedPointer(taggedNS);
- int result;
-
- testassert( CFGetTypeID(taggedCF) == CFNumberGetTypeID() );
- testassert(_objc_getClassForTag(OBJC_TAG_NSNumber) == [taggedNS class]);
-
- CFNumberGetValue(taggedCF, kCFNumberIntType, &result);
- testassert(result == 1234);
-
- testassert(_objc_isTaggedPointer(taggedCF));
- testassert(_objc_getTaggedPointerTag(taggedCF) == OBJC_TAG_NSNumber);
- testassert(_objc_makeTaggedPointer(_objc_getTaggedPointerTag(taggedCF), _objc_getTaggedPointerValue(taggedCF)) == taggedCF);
-
- // do some generic object-y things to the taggedPointer instance
- CFRetain(taggedCF);
- CFRelease(taggedCF);
-
- NSMutableDictionary *dict = [NSMutableDictionary dictionary];
- [dict setObject: taggedNS forKey: @"fred"];
- testassert(taggedNS == [dict objectForKey: @"fred"]);
- [dict setObject: @"bob" forKey: taggedNS];
- testassert([@"bob" isEqualToString: [dict objectForKey: taggedNS]]);
-
- NSNumber *iM88 = [NSNumber numberWithInt:-88];
- NSNumber *i12346 = [NSNumber numberWithInt: 12346];
- NSNumber *i12347 = [NSNumber numberWithInt: 12347];
-
- NSArray *anArray = [NSArray arrayWithObjects: iM88, i12346, i12347, nil];
- testassert([anArray count] == 3);
- testassert([anArray indexOfObject: i12346] == 1);
-
- NSSet *aSet = [NSSet setWithObjects: iM88, i12346, i12347, nil];
- testassert([aSet count] == 3);
- testassert([aSet containsObject: i12346]);
-
- [taggedNS performSelector: @selector(intValue)];
- testassert(![taggedNS isProxy]);
- testassert([taggedNS isKindOfClass: [NSNumber class]]);
- testassert([taggedNS respondsToSelector: @selector(intValue)]);
-
- (void)[taggedNS description];
-}
-
-int main()
-{
- PUSH_POOL {
- testTaggedNumber(); // should be tested by CF... our tests are wrong, wrong, wrong.
- } POP_POOL;
-
- succeed(__FILE__);
-}
-
-// OBJC_HAVE_TAGGED_POINTERS
-#else
-// not OBJC_HAVE_TAGGED_POINTERS
-
-// Tagged pointers not supported. Crash if an NSNumber actually
-// is a tagged pointer (which means this test is out of date).
-
-int main()
-{
- PUSH_POOL {
- testassert(*(void **)objc_unretainedPointer([NSNumber numberWithInt:1234]));
- } POP_POOL;
-
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include <objc/runtime.h>
-#include <objc/objc-internal.h>
-#include <objc/objc-gdb.h>
-#include <dlfcn.h>
-#import <Foundation/NSObject.h>
-
-#if OBJC_HAVE_TAGGED_POINTERS
-
-#if !__OBJC2__ || (!__x86_64__ && !__arm64__)
-#error wrong architecture for tagged pointers
-#endif
-
-static BOOL didIt;
-
-@interface WeakContainer : NSObject
-{
- @public
- __weak id weaks[10000];
-}
-@end
-@implementation WeakContainer
--(void) dealloc {
- for (unsigned int i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) {
- testassert(weaks[i] == nil);
- }
- SUPER_DEALLOC();
-}
--(void) finalize {
- for (unsigned int i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) {
- testassert(weaks[i] == nil);
- }
- [super finalize];
-}
-@end
-
-OBJC_ROOT_CLASS
-@interface TaggedBaseClass
-@end
-
-@implementation TaggedBaseClass
--(id) self { return self; }
-
-+ (void) initialize {
-}
-
-- (void) instanceMethod {
- didIt = YES;
-}
-
-- (uintptr_t) taggedValue {
- return _objc_getTaggedPointerValue(objc_unretainedPointer(self));
-}
-
-- (struct stret) stret: (struct stret) aStruct {
- return aStruct;
-}
-
-- (long double) fpret: (long double) aValue {
- return aValue;
-}
-
-
--(void) dealloc {
- fail("TaggedBaseClass dealloc called!");
-}
-
-static void *
-retain_fn(void *self, SEL _cmd __unused) {
- void * (*fn)(void *) = (typeof(fn))_objc_rootRetain;
- return fn(self);
-}
-
-static void
-release_fn(void *self, SEL _cmd __unused) {
- void (*fn)(void *) = (typeof(fn))_objc_rootRelease;
- fn(self);
-}
-
-static void *
-autorelease_fn(void *self, SEL _cmd __unused) {
- void * (*fn)(void *) = (typeof(fn))_objc_rootAutorelease;
- return fn(self);
-}
-
-static unsigned long
-retaincount_fn(void *self, SEL _cmd __unused) {
- unsigned long (*fn)(void *) = (typeof(fn))_objc_rootRetainCount;
- return fn(self);
-}
-
-+(void) load {
- class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
- class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
- class_addMethod(self, sel_registerName("autorelease"), (IMP)autorelease_fn, "");
- class_addMethod(self, sel_registerName("retainCount"), (IMP)retaincount_fn, "");
-}
-
-@end
-
-@interface TaggedSubclass: TaggedBaseClass
-@end
-
-@implementation TaggedSubclass
-
-- (void) instanceMethod {
- return [super instanceMethod];
-}
-
-- (uintptr_t) taggedValue {
- return [super taggedValue];
-}
-
-- (struct stret) stret: (struct stret) aStruct {
- return [super stret: aStruct];
-}
-
-- (long double) fpret: (long double) aValue {
- return [super fpret: aValue];
-}
-@end
-
-@interface TaggedNSObjectSubclass : NSObject
-@end
-
-@implementation TaggedNSObjectSubclass
-
-- (void) instanceMethod {
- didIt = YES;
-}
-
-- (uintptr_t) taggedValue {
- return _objc_getTaggedPointerValue(objc_unretainedPointer(self));
-}
-
-- (struct stret) stret: (struct stret) aStruct {
- return aStruct;
-}
-
-- (long double) fpret: (long double) aValue {
- return aValue;
-}
-@end
-
-void testTaggedPointerValue(Class cls, objc_tag_index_t tag, uintptr_t value)
-{
- void *taggedAddress = _objc_makeTaggedPointer(tag, value);
- testprintf("obj %p, tag %p, value %p\n",
- taggedAddress, (void*)tag, (void*)value);
-
- // _objc_makeTaggedPointer must quietly mask out of range values for now
- value = (value << 4) >> 4;
-
- testassert(_objc_isTaggedPointer(taggedAddress));
- testassert(_objc_getTaggedPointerTag(taggedAddress) == tag);
- testassert(_objc_getTaggedPointerValue(taggedAddress) == value);
-
- testassert((uintptr_t)taggedAddress & objc_debug_taggedpointer_mask);
- uintptr_t slot = ((uintptr_t)taggedAddress >> objc_debug_taggedpointer_slot_shift) & objc_debug_taggedpointer_slot_mask;
- testassert(objc_debug_taggedpointer_classes[slot] == cls);
- testassert((((uintptr_t)taggedAddress << objc_debug_taggedpointer_payload_lshift) >> objc_debug_taggedpointer_payload_rshift) == value);
-
- id taggedPointer = objc_unretainedObject(taggedAddress);
- testassert(!object_isClass(taggedPointer));
- testassert(object_getClass(taggedPointer) == cls);
- testassert([taggedPointer taggedValue] == value);
-
- didIt = NO;
- [taggedPointer instanceMethod];
- testassert(didIt);
-
- struct stret orig = STRET_RESULT;
- testassert(stret_equal(orig, [taggedPointer stret: orig]));
-
- long double dblvalue = 3.14156789;
- testassert(dblvalue == [taggedPointer fpret: dblvalue]);
-
- objc_setAssociatedObject(taggedPointer, (__bridge void *)taggedPointer, taggedPointer, OBJC_ASSOCIATION_RETAIN);
- testassert(objc_getAssociatedObject(taggedPointer, (__bridge void *)taggedPointer) == taggedPointer);
- objc_setAssociatedObject(taggedPointer, (__bridge void *)taggedPointer, nil, OBJC_ASSOCIATION_RETAIN);
- testassert(objc_getAssociatedObject(taggedPointer, (__bridge void *)taggedPointer) == nil);
-}
-
-void testGenericTaggedPointer(objc_tag_index_t tag, Class cls)
-{
- testassert(cls);
- testprintf("%s\n", class_getName(cls));
-
- testTaggedPointerValue(cls, tag, 0);
- testTaggedPointerValue(cls, tag, 1UL << 0);
- testTaggedPointerValue(cls, tag, 1UL << 1);
- testTaggedPointerValue(cls, tag, 1UL << 58);
- testTaggedPointerValue(cls, tag, 1UL << 59);
- testTaggedPointerValue(cls, tag, ~0UL >> 4);
- testTaggedPointerValue(cls, tag, ~0UL);
-
- // Tagged pointers should bypass refcount tables and autorelease pools
- // and weak reference tables
- WeakContainer *w = [WeakContainer new];
-#if !__has_feature(objc_arc)
- // prime method caches before leak checking
- id taggedPointer = (id)_objc_makeTaggedPointer(tag, 1234);
- [taggedPointer retain];
- [taggedPointer release];
- [taggedPointer autorelease];
-#endif
- leak_mark();
- for (uintptr_t i = 0; i < sizeof(w->weaks)/sizeof(w->weaks[0]); i++) {
- id o = objc_unretainedObject(_objc_makeTaggedPointer(tag, i));
- testassert(object_getClass(o) == cls);
-
- id result = WEAK_STORE(w->weaks[i], o);
- testassert(result == o);
- testassert(w->weaks[i] == o);
-
- result = WEAK_LOAD(w->weaks[i]);
- testassert(result == o);
-
- if (!objc_collectingEnabled()) {
- uintptr_t rc = _objc_rootRetainCount(o);
- testassert(rc != 0);
- _objc_rootRelease(o); testassert(_objc_rootRetainCount(o) == rc);
- _objc_rootRelease(o); testassert(_objc_rootRetainCount(o) == rc);
- _objc_rootRetain(o); testassert(_objc_rootRetainCount(o) == rc);
- _objc_rootRetain(o); testassert(_objc_rootRetainCount(o) == rc);
- _objc_rootRetain(o); testassert(_objc_rootRetainCount(o) == rc);
-#if !__has_feature(objc_arc)
- [o release]; testassert(_objc_rootRetainCount(o) == rc);
- [o release]; testassert(_objc_rootRetainCount(o) == rc);
- [o retain]; testassert(_objc_rootRetainCount(o) == rc);
- [o retain]; testassert(_objc_rootRetainCount(o) == rc);
- [o retain]; testassert(_objc_rootRetainCount(o) == rc);
- objc_release(o); testassert(_objc_rootRetainCount(o) == rc);
- objc_release(o); testassert(_objc_rootRetainCount(o) == rc);
- objc_retain(o); testassert(_objc_rootRetainCount(o) == rc);
- objc_retain(o); testassert(_objc_rootRetainCount(o) == rc);
- objc_retain(o); testassert(_objc_rootRetainCount(o) == rc);
-#endif
- PUSH_POOL {
- testassert(_objc_rootRetainCount(o) == rc);
- _objc_rootAutorelease(o);
- testassert(_objc_rootRetainCount(o) == rc);
-#if !__has_feature(objc_arc)
- [o autorelease];
- testassert(_objc_rootRetainCount(o) == rc);
- objc_autorelease(o);
- testassert(_objc_rootRetainCount(o) == rc);
- objc_retainAutorelease(o);
- testassert(_objc_rootRetainCount(o) == rc);
- objc_autoreleaseReturnValue(o);
- testassert(_objc_rootRetainCount(o) == rc);
- objc_retainAutoreleaseReturnValue(o);
- testassert(_objc_rootRetainCount(o) == rc);
- objc_retainAutoreleasedReturnValue(o);
- testassert(_objc_rootRetainCount(o) == rc);
-#endif
- } POP_POOL;
- testassert(_objc_rootRetainCount(o) == rc);
- }
- }
- leak_check(0);
- for (uintptr_t i = 0; i < 10000; i++) {
- testassert(w->weaks[i] != NULL);
- WEAK_STORE(w->weaks[i], NULL);
- testassert(w->weaks[i] == NULL);
- testassert(WEAK_LOAD(w->weaks[i]) == NULL);
- }
- RELEASE_VAR(w);
-}
-
-int main()
-{
- if (objc_collecting_enabled()) {
- // GC's block objects crash without this
- dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", RTLD_LAZY);
- }
-
- testassert(objc_debug_taggedpointer_mask != 0);
- testassert(_objc_taggedPointersEnabled());
-
- PUSH_POOL {
- // Avoid CF's tagged pointer tags because of rdar://11368528
-
- _objc_registerTaggedPointerClass(OBJC_TAG_1,
- objc_getClass("TaggedBaseClass"));
- testGenericTaggedPointer(OBJC_TAG_1,
- objc_getClass("TaggedBaseClass"));
-
- _objc_registerTaggedPointerClass(OBJC_TAG_7,
- objc_getClass("TaggedSubclass"));
- testGenericTaggedPointer(OBJC_TAG_7,
- objc_getClass("TaggedSubclass"));
-
- _objc_registerTaggedPointerClass(OBJC_TAG_NSManagedObjectID,
- objc_getClass("TaggedNSObjectSubclass"));
- testGenericTaggedPointer(OBJC_TAG_NSManagedObjectID,
- objc_getClass("TaggedNSObjectSubclass"));
- } POP_POOL;
-
- succeed(__FILE__);
-}
-
-// OBJC_HAVE_TAGGED_POINTERS
-#else
-// not OBJC_HAVE_TAGGED_POINTERS
-
-// Tagged pointers not supported.
-
-int main()
-{
-#if __OBJC2__
- testassert(objc_debug_taggedpointer_mask == 0);
-#else
- testassert(!dlsym(RTLD_DEFAULT, "objc_debug_taggedpointer_mask"));
-#endif
-
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-// TEST_ENV OBJC_DISABLE_TAGGED_POINTERS=YES
-// TEST_CRASHES
-/*
-TEST_RUN_OUTPUT
-objc\[\d+\]: tagged pointers are disabled
-CRASHED: SIG(ILL|TRAP)
-OR
-OK: taggedPointersDisabled.m
-END
-*/
-
-#include "test.h"
-#include <objc/objc-internal.h>
-
-#if !OBJC_HAVE_TAGGED_POINTERS
-
-int main()
-{
- succeed(__FILE__);
-}
-
-#else
-
-int main()
-{
- testassert(!_objc_taggedPointersEnabled());
- _objc_registerTaggedPointerClass((objc_tag_index_t)0, nil);
- fail("should have crashed in _objc_registerTaggedPointerClass()");
-}
-
-#endif
+++ /dev/null
-// TEST_CONFIG OS=iphoneos ARCH=arm64
-
-#include "test.h"
-
-#ifndef __arm64__
-#error wrong architecture for TBI hardware feature
-#endif
-
-volatile int x = 123456;
-
-int main(void) {
- testassert(*(int *)((unsigned long)&x | 0xFF00000000000000ul) == 123456);
- succeed(__FILE__);
-}
+++ /dev/null
-// test.h
-// Common definitions for trivial test harness
-
-
-#ifndef TEST_H
-#define TEST_H
-
-#include <stdio.h>
-#include <dlfcn.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <libgen.h>
-#include <unistd.h>
-#include <pthread.h>
-#include <sys/param.h>
-#include <malloc/malloc.h>
-#include <mach/mach.h>
-#include <mach/vm_param.h>
-#include <mach/mach_time.h>
-#include <objc/objc.h>
-#include <objc/runtime.h>
-#include <objc/message.h>
-#include <objc/objc-abi.h>
-#include <objc/objc-auto.h>
-#include <objc/objc-internal.h>
-#include <TargetConditionals.h>
-
-#if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
-static OBJC_INLINE malloc_zone_t *objc_collectableZone(void) { return nil; }
-#endif
-
-
-// Configuration macros
-
-#if !__LP64__ || TARGET_OS_WIN32 || __OBJC_GC__ || TARGET_IPHONE_SIMULATOR
-# define SUPPORT_NONPOINTER_ISA 0
-#elif __x86_64__
-# define SUPPORT_NONPOINTER_ISA 1
-#elif __arm64__
-# define SUPPORT_NONPOINTER_ISA 1
-#else
-# error unknown architecture
-#endif
-
-
-// Test output
-
-static inline void succeed(const char *name) __attribute__((noreturn));
-static inline void succeed(const char *name)
-{
- if (name) {
- char path[MAXPATHLEN+1];
- strcpy(path, name);
- fprintf(stderr, "OK: %s\n", basename(path));
- } else {
- fprintf(stderr, "OK\n");
- }
- exit(0);
-}
-
-static inline void fail(const char *msg, ...) __attribute__((noreturn));
-static inline void fail(const char *msg, ...)
-{
- if (msg) {
- char *msg2;
- asprintf(&msg2, "BAD: %s\n", msg);
- va_list v;
- va_start(v, msg);
- vfprintf(stderr, msg2, v);
- va_end(v);
- free(msg2);
- } else {
- fprintf(stderr, "BAD\n");
- }
- exit(1);
-}
-
-#define testassert(cond) \
- ((void) (((cond) != 0) ? (void)0 : __testassert(#cond, __FILE__, __LINE__)))
-#define __testassert(cond, file, line) \
- (fail("failed assertion '%s' at %s:%u", cond, __FILE__, __LINE__))
-
-/* time-sensitive assertion, disabled under valgrind */
-#define timecheck(name, time, fast, slow) \
- if (getenv("VALGRIND") && 0 != strcmp(getenv("VALGRIND"), "NO")) { \
- /* valgrind; do nothing */ \
- } else if (time > slow) { \
- fprintf(stderr, "SLOW: %s %llu, expected %llu..%llu\n", \
- name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
- } else if (time < fast) { \
- fprintf(stderr, "FAST: %s %llu, expected %llu..%llu\n", \
- name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
- } else { \
- testprintf("time: %s %llu, expected %llu..%llu\n", \
- name, (uint64_t)(time), (uint64_t)(fast), (uint64_t)(slow)); \
- }
-
-
-static inline void testprintf(const char *msg, ...)
-{
- static int verbose = -1;
- if (verbose < 0) verbose = atoi(getenv("VERBOSE") ?: "0");
-
- // VERBOSE=1 prints test harness info only
- if (msg && verbose >= 2) {
- char *msg2;
- asprintf(&msg2, "VERBOSE: %s", msg);
- va_list v;
- va_start(v, msg);
- vfprintf(stderr, msg2, v);
- va_end(v);
- free(msg2);
- }
-}
-
-// complain to output, but don't fail the test
-// Use when warning that some test is being temporarily skipped
-// because of something like a compiler bug.
-static inline void testwarn(const char *msg, ...)
-{
- if (msg) {
- char *msg2;
- asprintf(&msg2, "WARN: %s\n", msg);
- va_list v;
- va_start(v, msg);
- vfprintf(stderr, msg2, v);
- va_end(v);
- free(msg2);
- }
-}
-
-static inline void testnoop() { }
-
-// Run GC. This is a macro to reach as high in the stack as possible.
-#ifndef OBJC_NO_GC
-
-# if __OBJC2__
-# define testexc()
-# else
-# include <objc/objc-exception.h>
-# define testexc() \
- do { \
- objc_exception_functions_t table = {0,0,0,0,0,0}; \
- objc_exception_get_functions(&table); \
- if (!table.throw_exc) { \
- table.throw_exc = (typeof(table.throw_exc))abort; \
- table.try_enter = (typeof(table.try_enter))testnoop; \
- table.try_exit = (typeof(table.try_exit))testnoop; \
- table.extract = (typeof(table.extract))abort; \
- table.match = (typeof(table.match))abort; \
- objc_exception_set_functions(&table); \
- } \
- } while (0)
-# endif
-
-# define testcollect() \
- do { \
- if (objc_collectingEnabled()) { \
- testexc(); \
- objc_clear_stack(0); \
- objc_collect(OBJC_COLLECT_IF_NEEDED|OBJC_WAIT_UNTIL_DONE); \
- objc_collect(OBJC_EXHAUSTIVE_COLLECTION|OBJC_WAIT_UNTIL_DONE);\
- objc_collect(OBJC_EXHAUSTIVE_COLLECTION|OBJC_WAIT_UNTIL_DONE);\
- } \
- _objc_flush_caches(NULL); \
- } while (0)
-
-#else
-
-# define testcollect() \
- do { \
- _objc_flush_caches(NULL); \
- } while (0)
-
-#endif
-
-
-// Synchronously run test code on another thread.
-// This can help force GC to kill objects promptly, which some tests depend on.
-
-// The block object is unsafe_unretained because we must not allow
-// ARC to retain them in non-Foundation tests
-typedef void(^testblock_t)(void);
-static __unsafe_unretained testblock_t testcodehack;
-static inline void *_testthread(void *arg __unused)
-{
- objc_registerThreadWithCollector();
- testcodehack();
- return NULL;
-}
-static inline void testonthread(__unsafe_unretained testblock_t code)
-{
- // GC crashes without Foundation because the block object classes
- // are insufficiently initialized.
- if (objc_collectingEnabled()) {
- static bool foundationified = false;
- if (!foundationified) {
- dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", RTLD_LAZY);
- foundationified = true;
- }
- }
-
- pthread_t th;
- testcodehack = code; // force GC not-thread-local, avoid ARC void* casts
- pthread_create(&th, NULL, _testthread, NULL);
- pthread_join(th, NULL);
-}
-
-/* Make sure libobjc does not call global operator new.
- Any test that DOES need to call global operator new must
- `#define TEST_CALLS_OPERATOR_NEW` before including test.h.
- */
-#if __cplusplus && !defined(TEST_CALLS_OPERATOR_NEW)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Winline-new-delete"
-#import <new>
-inline void* operator new(std::size_t) throw (std::bad_alloc) { fail("called global operator new"); }
-inline void* operator new[](std::size_t) throw (std::bad_alloc) { fail("called global operator new[]"); }
-inline void* operator new(std::size_t, const std::nothrow_t&) throw() { fail("called global operator new(nothrow)"); }
-inline void* operator new[](std::size_t, const std::nothrow_t&) throw() { fail("called global operator new[](nothrow)"); }
-inline void operator delete(void*) throw() { fail("called global operator delete"); }
-inline void operator delete[](void*) throw() { fail("called global operator delete[]"); }
-inline void operator delete(void*, const std::nothrow_t&) throw() { fail("called global operator delete(nothrow)"); }
-inline void operator delete[](void*, const std::nothrow_t&) throw() { fail("called global operator delete[](nothrow)"); }
-#pragma clang diagnostic pop
-#endif
-
-
-/* Leak checking
- Fails if total malloc memory in use at leak_check(n)
- is more than n bytes above that at leak_mark().
-*/
-
-static inline void leak_recorder(task_t task __unused, void *ctx, unsigned type __unused, vm_range_t *ranges, unsigned count)
-{
- size_t *inuse = (size_t *)ctx;
- while (count--) {
- *inuse += ranges[count].size;
- }
-}
-
-static inline size_t leak_inuse(void)
-{
- size_t total = 0;
- vm_address_t *zones;
- unsigned count;
- malloc_get_all_zones(mach_task_self(), NULL, &zones, &count);
- for (unsigned i = 0; i < count; i++) {
- size_t inuse = 0;
- malloc_zone_t *zone = (malloc_zone_t *)zones[i];
- if (!zone->introspect || !zone->introspect->enumerator) continue;
-
- // skip DispatchContinuations because it sometimes claims to be
- // using lots of memory that then goes away later
- if (0 == strcmp(zone->zone_name, "DispatchContinuations")) continue;
-
- zone->introspect->enumerator(mach_task_self(), &inuse, MALLOC_PTR_IN_USE_RANGE_TYPE, (vm_address_t)zone, NULL, leak_recorder);
- // fprintf(stderr, "%zu in use for zone %s\n", inuse, zone->zone_name);
- total += inuse;
- }
-
- return total;
-}
-
-
-static inline void leak_dump_heap(const char *msg)
-{
- fprintf(stderr, "%s\n", msg);
-
- // Make `heap` write to stderr
- int outfd = dup(STDOUT_FILENO);
- dup2(STDERR_FILENO, STDOUT_FILENO);
- pid_t pid = getpid();
- char cmd[256];
- // environment variables reset for iOS simulator use
- sprintf(cmd, "DYLD_LIBRARY_PATH= DYLD_ROOT_PATH= /usr/bin/heap -addresses all %d", (int)pid);
-
- system(cmd);
-
- dup2(outfd, STDOUT_FILENO);
- close(outfd);
-}
-
-static size_t _leak_start;
-static inline void leak_mark(void)
-{
- testcollect();
- if (getenv("LEAK_HEAP")) {
- leak_dump_heap("HEAP AT leak_mark");
- }
- _leak_start = leak_inuse();
-}
-
-#define leak_check(n) \
- do { \
- const char *_check = getenv("LEAK_CHECK"); \
- size_t inuse; \
- if (_check && 0 == strcmp(_check, "NO")) break; \
- testcollect(); \
- if (getenv("LEAK_HEAP")) { \
- leak_dump_heap("HEAP AT leak_check"); \
- } \
- inuse = leak_inuse(); \
- if (inuse > _leak_start + n) { \
- if (getenv("HANG_ON_LEAK")) { \
- printf("leaks %d\n", getpid()); \
- while (1) sleep(1); \
- } \
- fprintf(stderr, "BAD: %zu bytes leaked at %s:%u\n", \
- inuse - _leak_start, __FILE__, __LINE__); \
- } \
- } while (0)
-
-static inline bool is_guardmalloc(void)
-{
- const char *env = getenv("GUARDMALLOC");
- return (env && 0 == strcmp(env, "YES"));
-}
-
-
-/* Memory management compatibility macros */
-
-static id self_fn(id x) __attribute__((used));
-static id self_fn(id x) { return x; }
-
-#if __has_feature(objc_arc)
- // ARC
-# define RELEASE_VAR(x) x = nil
-# define WEAK_STORE(dst, val) (dst = (val))
-# define WEAK_LOAD(src) (src)
-# define SUPER_DEALLOC()
-# define RETAIN(x) (self_fn(x))
-# define RELEASE_VALUE(x) ((void)self_fn(x))
-# define AUTORELEASE(x) (self_fn(x))
-
-#elif defined(__OBJC_GC__)
- // GC
-# define RELEASE_VAR(x) x = nil
-# define WEAK_STORE(dst, val) (dst = (val))
-# define WEAK_LOAD(src) (src)
-# define SUPER_DEALLOC() [super dealloc]
-# define RETAIN(x) [x self]
-# define RELEASE_VALUE(x) (void)[x self]
-# define AUTORELEASE(x) [x self]
-
-#else
- // MRC
-# define RELEASE_VAR(x) do { [x release]; x = nil; } while (0)
-# define WEAK_STORE(dst, val) objc_storeWeak((id *)&dst, val)
-# define WEAK_LOAD(src) objc_loadWeak((id *)&src)
-# define SUPER_DEALLOC() [super dealloc]
-# define RETAIN(x) [x retain]
-# define RELEASE_VALUE(x) [x release]
-# define AUTORELEASE(x) [x autorelease]
-#endif
-
-/* gcc compatibility macros */
-/* <rdar://problem/9412038> @autoreleasepool should generate objc_autoreleasePoolPush/Pop on 10.7/5.0 */
-//#if !defined(__clang__)
-# define PUSH_POOL { void *pool = objc_autoreleasePoolPush();
-# define POP_POOL objc_autoreleasePoolPop(pool); }
-//#else
-//# define PUSH_POOL @autoreleasepool
-//# define POP_POOL
-//#endif
-
-#if __OBJC__
-
-/* General purpose root class */
-
-OBJC_ROOT_CLASS
-@interface TestRoot {
- @public
- Class isa;
-}
-
-+(void) load;
-+(void) initialize;
-
--(id) self;
--(Class) class;
--(Class) superclass;
-
-+(id) new;
-+(id) alloc;
-+(id) allocWithZone:(void*)zone;
--(id) copy;
--(id) mutableCopy;
--(id) init;
--(void) dealloc;
--(void) finalize;
-@end
-@interface TestRoot (RR)
--(id) retain;
--(oneway void) release;
--(id) autorelease;
--(unsigned long) retainCount;
--(id) copyWithZone:(void *)zone;
--(id) mutableCopyWithZone:(void*)zone;
-@end
-
-// incremented for each call of TestRoot's methods
-extern int TestRootLoad;
-extern int TestRootInitialize;
-extern int TestRootAlloc;
-extern int TestRootAllocWithZone;
-extern int TestRootCopy;
-extern int TestRootCopyWithZone;
-extern int TestRootMutableCopy;
-extern int TestRootMutableCopyWithZone;
-extern int TestRootInit;
-extern int TestRootDealloc;
-extern int TestRootFinalize;
-extern int TestRootRetain;
-extern int TestRootRelease;
-extern int TestRootAutorelease;
-extern int TestRootRetainCount;
-extern int TestRootTryRetain;
-extern int TestRootIsDeallocating;
-extern int TestRootPlusRetain;
-extern int TestRootPlusRelease;
-extern int TestRootPlusAutorelease;
-extern int TestRootPlusRetainCount;
-
-#endif
-
-
-// Struct that does not return in registers on any architecture
-
-struct stret {
- int a;
- int b;
- int c;
- int d;
- int e;
- int f;
- int g;
- int h;
- int i;
- int j;
-};
-
-static inline BOOL stret_equal(struct stret a, struct stret b)
-{
- return (a.a == b.a &&
- a.b == b.b &&
- a.c == b.c &&
- a.d == b.d &&
- a.e == b.e &&
- a.f == b.f &&
- a.g == b.g &&
- a.h == b.h &&
- a.i == b.i &&
- a.j == b.j);
-}
-
-static struct stret STRET_RESULT __attribute__((used)) = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-
-
-#if TARGET_IPHONE_SIMULATOR
-// Force cwd to executable's directory during launch.
-// sim used to do this but simctl does not.
-#include <crt_externs.h>
- __attribute__((constructor))
-static void hack_cwd(void)
-{
- if (!getenv("HACKED_CWD")) {
- chdir(dirname((*_NSGetArgv())[0]));
- setenv("HACKED_CWD", "1", 1);
- }
-}
-#endif
-
-#endif
+++ /dev/null
-#!/usr/bin/perl
-
-# test.pl
-# Run unit tests.
-
-use strict;
-use File::Basename;
-
-chdir dirname $0;
-chomp (my $DIR = `pwd`);
-
-my $TESTLIBNAME = "libobjc.A.dylib";
-my $TESTLIBPATH = "/usr/lib/$TESTLIBNAME";
-
-my $BUILDDIR = "/tmp/test-$TESTLIBNAME-build";
-
-# xterm colors
-my $red = "\e[41;37m";
-my $yellow = "\e[43;30m";
-my $nocolor = "\e[0m";
-
-# clean, help
-if (scalar(@ARGV) == 1) {
- my $arg = $ARGV[0];
- if ($arg eq "clean") {
- my $cmd = "rm -rf $BUILDDIR *~";
- print "$cmd\n";
- `$cmd`;
- exit 0;
- }
- elsif ($arg eq "-h" || $arg eq "-H" || $arg eq "-help" || $arg eq "help") {
- print(<<END);
-usage: $0 [options] [testname ...]
- $0 clean
- $0 help
-
-testname:
- `testname` runs a specific test. If no testnames are given, runs all tests.
-
-options:
- ARCH=<arch>
- OS=<sdk name>[sdk version][-<deployment target>[-<run target>]]
- ROOT=/path/to/project.roots/
-
- CC=<compiler name>
-
- LANGUAGE=c,c++,objective-c,objective-c++,swift
- MEM=mrc,arc,gc
- STDLIB=libc++,libstdc++
- GUARDMALLOC=0|1|before|after
-
- BUILD=0|1
- RUN=0|1
- VERBOSE=0|1|2
-
-examples:
-
- test installed library, x86_64, no gc
- $0
-
- test buildit-built root, i386 and x86_64, MRC and ARC and GC, clang compiler
- $0 ARCH=i386,x86_64 ROOT=/tmp/libclosure.roots MEM=mrc,arc,gc CC=clang
-
- test buildit-built root with iOS simulator, deploy to iOS 7, run on iOS 8
- $0 ARCH=i386 ROOT=/tmp/libclosure.roots OS=iphonesimulator-7.0-8.0
-
- test buildit-built root on attached iOS device
- $0 ARCH=armv7 ROOT=/tmp/libclosure.roots OS=iphoneos
-END
- exit 0;
- }
-}
-
-#########################################################################
-## Tests
-
-my %ALL_TESTS;
-
-#########################################################################
-## Variables for use in complex build and run rules
-
-# variable # example value
-
-# things you can multiplex on the command line
-# ARCH=i386,x86_64,armv6,armv7
-# OS=macosx,iphoneos,iphonesimulator (plus sdk/deployment/run versions)
-# LANGUAGE=c,c++,objective-c,objective-c++,swift
-# CC=clang,gcc-4.2,llvm-gcc-4.2
-# MEM=mrc,arc,gc
-# STDLIB=libc++,libstdc++
-# GUARDMALLOC=0,1,before,after
-
-# things you can set once on the command line
-# ROOT=/path/to/project.roots
-# BUILD=0|1
-# RUN=0|1
-# VERBOSE=0|1|2
-
-
-
-my $BUILD;
-my $RUN;
-my $VERBOSE;
-
-my $crashcatch = <<'END';
-// interpose-able code to catch crashes, print, and exit cleanly
-#include <signal.h>
-#include <string.h>
-#include <unistd.h>
-
-// from dyld-interposing.h
-#define DYLD_INTERPOSE(_replacement,_replacee) __attribute__((used)) static struct{ const void* replacement; const void* replacee; } _interpose_##_replacee __attribute__ ((section ("__DATA,__interpose"))) = { (const void*)(unsigned long)&_replacement, (const void*)(unsigned long)&_replacee };
-
-static void catchcrash(int sig)
-{
- const char *msg;
- switch (sig) {
- case SIGILL: msg = "CRASHED: SIGILL\\n"; break;
- case SIGBUS: msg = "CRASHED: SIGBUS\\n"; break;
- case SIGSYS: msg = "CRASHED: SIGSYS\\n"; break;
- case SIGSEGV: msg = "CRASHED: SIGSEGV\\n"; break;
- case SIGTRAP: msg = "CRASHED: SIGTRAP\\n"; break;
- case SIGABRT: msg = "CRASHED: SIGABRT\\n"; break;
- default: msg = "SIG\?\?\?\?\\n"; break;
- }
- write(STDERR_FILENO, msg, strlen(msg));
- _exit(0);
-}
-
-static void setupcrash(void) __attribute__((constructor));
-static void setupcrash(void)
-{
- signal(SIGILL, &catchcrash);
- signal(SIGBUS, &catchcrash);
- signal(SIGSYS, &catchcrash);
- signal(SIGSEGV, &catchcrash);
- signal(SIGTRAP, &catchcrash);
- signal(SIGABRT, &catchcrash);
-}
-
-
-static int hacked = 0;
-ssize_t hacked_write(int fildes, const void *buf, size_t nbyte)
-{
- if (!hacked) {
- setupcrash();
- hacked = 1;
- }
- return write(fildes, buf, nbyte);
-}
-
-DYLD_INTERPOSE(hacked_write, write);
-
-END
-
-
-#########################################################################
-## Harness
-
-
-# map language to buildable extensions for that language
-my %extensions_for_language = (
- "c" => ["c"],
- "objective-c" => ["c", "m"],
- "c++" => ["c", "cc", "cp", "cpp", "cxx", "c++"],
- "objective-c++" => ["c", "m", "cc", "cp", "cpp", "cxx", "c++", "mm"],
- "swift" => ["swift"],
-
- "any" => ["c", "m", "cc", "cp", "cpp", "cxx", "c++", "mm", "swift"],
- );
-
-# map extension to languages
-my %languages_for_extension = (
- "c" => ["c", "objective-c", "c++", "objective-c++"],
- "m" => ["objective-c", "objective-c++"],
- "mm" => ["objective-c++"],
- "cc" => ["c++", "objective-c++"],
- "cp" => ["c++", "objective-c++"],
- "cpp" => ["c++", "objective-c++"],
- "cxx" => ["c++", "objective-c++"],
- "c++" => ["c++", "objective-c++"],
- "swift" => ["swift"],
- );
-
-# Run some newline-separated commands like `make` would, stopping if any fail
-# run("cmd1 \n cmd2 \n cmd3")
-sub make {
- my $output = "";
- my @cmds = split("\n", $_[0]);
- die if scalar(@cmds) == 0;
- $? = 0;
- foreach my $cmd (@cmds) {
- chomp $cmd;
- next if $cmd =~ /^\s*$/;
- $cmd .= " 2>&1";
- print "$cmd\n" if $VERBOSE;
- $output .= `$cmd`;
- last if $?;
- }
- print "$output\n" if $VERBOSE;
- return $output;
-}
-
-sub chdir_verbose {
- my $dir = shift;
- print "cd $dir\n" if $VERBOSE;
- chdir $dir || die;
-}
-
-
-# Return test names from the command line.
-# Returns all tests if no tests were named.
-sub gettests {
- my @tests;
-
- foreach my $arg (@ARGV) {
- push @tests, $arg if ($arg !~ /=/ && $arg !~ /^-/);
- }
-
- opendir(my $dir, $DIR) || die;
- while (my $file = readdir($dir)) {
- my ($name, $ext) = ($file =~ /^([^.]+)\.([^.]+)$/);
- next if ! $languages_for_extension{$ext};
-
- open(my $in, "< $file") || die "$file";
- my $contents = join "", <$in>;
- if (defined $ALL_TESTS{$name}) {
- print "${yellow}SKIP: multiple tests named '$name'; skipping file '$file'.${nocolor}\n";
- } else {
- $ALL_TESTS{$name} = $ext if ($contents =~ m#^[/*\s]*TEST_#m);
- }
- close($in);
- }
- closedir($dir);
-
- if (scalar(@tests) == 0) {
- @tests = keys %ALL_TESTS;
- }
-
- @tests = sort @tests;
-
- return @tests;
-}
-
-
-# Turn a C compiler name into a C++ compiler name.
-sub cplusplus {
- my ($c) = @_;
- if ($c =~ /cc/) {
- $c =~ s/cc/\+\+/;
- return $c;
- }
- return $c . "++"; # e.g. clang => clang++
-}
-
-# Turn a C compiler name into a Swift compiler name
-sub swift {
- my ($c) = @_;
- $c =~ s#[^/]*$#swift#;
- return $c;
-}
-
-# Returns an array of all sdks from `xcodebuild -showsdks`
-my @sdks_memo;
-sub getsdks {
- if (!@sdks_memo) {
- @sdks_memo = (`xcodebuild -showsdks` =~ /-sdk (.+)$/mg);
- }
- return @sdks_memo;
-}
-
-my %sdk_path_memo = {};
-sub getsdkpath {
- my ($sdk) = @_;
- if (!defined $sdk_path_memo{$sdk}) {
- ($sdk_path_memo{$sdk}) = (`xcodebuild -version -sdk '$sdk' Path` =~ /^\s*(.+?)\s*$/);
- }
- return $sdk_path_memo{$sdk};
-}
-
-# Extract a version number from a string.
-# Ignore trailing "internal".
-sub versionsuffix {
- my ($str) = @_;
- my ($vers) = ($str =~ /([0-9]+\.[0-9]+)(?:\.?internal)?$/);
- return $vers;
-}
-sub majorversionsuffix {
- my ($str) = @_;
- my ($vers) = ($str =~ /([0-9]+)\.[0-9]+(?:\.?internal)?$/);
- return $vers;
-}
-sub minorversionsuffix {
- my ($str) = @_;
- my ($vers) = ($str =~ /[0-9]+\.([0-9]+)(?:\.?internal)?$/);
- return $vers;
-}
-
-# Compares two SDK names and returns the newer one.
-# Assumes the two SDKs are the same OS.
-sub newersdk {
- my ($lhs, $rhs) = @_;
-
- # Major version wins.
- my $lhsMajor = majorversionsuffix($lhs);
- my $rhsMajor = majorversionsuffix($rhs);
- if ($lhsMajor > $rhsMajor) { return $lhs; }
- if ($lhsMajor < $rhsMajor) { return $rhs; }
-
- # Minor version wins.
- my $lhsMinor = minorversionsuffix($lhs);
- my $rhsMinor = minorversionsuffix($rhs);
- if ($lhsMinor > $rhsMinor) { return $lhs; }
- if ($lhsMinor < $rhsMinor) { return $rhs; }
-
- # Lexically-last wins (i.e. internal is better than not internal)
- if ($lhs gt $rhs) { return $lhs; }
- return $rhs;
-}
-
-# Returns whether the given sdk supports -lauto
-sub supportslibauto {
- my ($sdk) = @_;
- return 1 if $sdk =~ /^macosx/;
- return 0;
-}
-
-# print text with a colored prefix on each line
-sub colorprint {
- my $color = shift;
- while (my @lines = split("\n", shift)) {
- for my $line (@lines) {
- chomp $line;
- print "$color $nocolor$line\n";
- }
- }
-}
-
-sub rewind {
- seek($_[0], 0, 0);
-}
-
-# parse name=value,value pairs
-sub readconditions {
- my ($conditionstring) = @_;
-
- my %results;
- my @conditions = ($conditionstring =~ /\w+=(?:[^\s,]+,?)+/g);
- for my $condition (@conditions) {
- my ($name, $values) = ($condition =~ /(\w+)=(.+)/);
- $results{$name} = [split ',', $values];
- }
-
- return %results;
-}
-
-sub check_output {
- my %C = %{shift()};
- my $name = shift;
- my @output = @_;
-
- my %T = %{$C{"TEST_$name"}};
-
- # Quietly strip MallocScribble before saving the "original" output
- # because it is distracting.
- filter_malloc(\@output);
-
- my @original_output = @output;
-
- # Run result-checking passes, reducing @output each time
- my $xit = 1;
- my $bad = "";
- my $warn = "";
- my $runerror = $T{TEST_RUN_OUTPUT};
- filter_hax(\@output);
- filter_verbose(\@output);
- filter_simulator(\@output);
- $warn = filter_warn(\@output);
- $bad |= filter_guardmalloc(\@output) if ($C{GUARDMALLOC});
- $bad |= filter_valgrind(\@output) if ($C{VALGRIND});
- $bad = filter_expected(\@output, \%C, $name) if ($bad eq "");
- $bad = filter_bad(\@output) if ($bad eq "");
-
- # OK line should be the only one left
- $bad = "(output not 'OK: $name')" if ($bad eq "" && (scalar(@output) != 1 || $output[0] !~ /^OK: $name/));
-
- if ($bad ne "") {
- print "${red}FAIL: /// test '$name' \\\\\\$nocolor\n";
- colorprint($red, @original_output);
- print "${red}FAIL: \\\\\\ test '$name' ///$nocolor\n";
- print "${red}FAIL: $name: $bad$nocolor\n";
- $xit = 0;
- }
- elsif ($warn ne "") {
- print "${yellow}PASS: /// test '$name' \\\\\\$nocolor\n";
- colorprint($yellow, @original_output);
- print "${yellow}PASS: \\\\\\ test '$name' ///$nocolor\n";
- print "PASS: $name (with warnings)\n";
- }
- else {
- print "PASS: $name\n";
- }
- return $xit;
-}
-
-sub filter_expected
-{
- my $outputref = shift;
- my %C = %{shift()};
- my $name = shift;
-
- my %T = %{$C{"TEST_$name"}};
- my $runerror = $T{TEST_RUN_OUTPUT} || return "";
-
- my $bad = "";
-
- my $output = join("\n", @$outputref) . "\n";
- if ($output !~ /$runerror/) {
- $bad = "(run output does not match TEST_RUN_OUTPUT)";
- @$outputref = ("FAIL: $name");
- } else {
- @$outputref = ("OK: $name"); # pacify later filter
- }
-
- return $bad;
-}
-
-sub filter_bad
-{
- my $outputref = shift;
- my $bad = "";
-
- my @new_output;
- for my $line (@$outputref) {
- if ($line =~ /^BAD: (.*)/) {
- $bad = "(failed)";
- } else {
- push @new_output, $line;
- }
- }
-
- @$outputref = @new_output;
- return $bad;
-}
-
-sub filter_warn
-{
- my $outputref = shift;
- my $warn = "";
-
- my @new_output;
- for my $line (@$outputref) {
- if ($line !~ /^WARN: (.*)/) {
- push @new_output, $line;
- } else {
- $warn = "(warned)";
- }
- }
-
- @$outputref = @new_output;
- return $warn;
-}
-
-sub filter_verbose
-{
- my $outputref = shift;
-
- my @new_output;
- for my $line (@$outputref) {
- if ($line !~ /^VERBOSE: (.*)/) {
- push @new_output, $line;
- }
- }
-
- @$outputref = @new_output;
-}
-
-sub filter_simulator
-{
- my $outputref = shift;
-
- my @new_output;
- for my $line (@$outputref) {
- if ($line !~ /No simulator devices appear to be running/) {
- push @new_output, $line;
- }
- }
-
- @$outputref = @new_output;
-}
-
-sub filter_simulator
-{
- my $outputref = shift;
-
- my @new_output;
- for my $line (@$outputref) {
- if ($line !~ /No simulator devices appear to be running/) {
- push @new_output, $line;
- }
- }
-
- @$outputref = @new_output;
-}
-
-sub filter_hax
-{
- my $outputref = shift;
-
- my @new_output;
- for my $line (@$outputref) {
- if ($line !~ /Class OS_tcp_/) {
- push @new_output, $line;
- }
- }
-
- @$outputref = @new_output;
-}
-
-sub filter_valgrind
-{
- my $outputref = shift;
- my $errors = 0;
- my $leaks = 0;
-
- my @new_output;
- for my $line (@$outputref) {
- if ($line =~ /^Approx: do_origins_Dirty\([RW]\): missed \d bytes$/) {
- # --track-origins warning (harmless)
- next;
- }
- if ($line =~ /^UNKNOWN __disable_threadsignal is unsupported. This warning will not be repeated.$/) {
- # signals unsupported (harmless)
- next;
- }
- if ($line =~ /^UNKNOWN __pthread_sigmask is unsupported. This warning will not be repeated.$/) {
- # signals unsupported (harmless)
- next;
- }
- if ($line !~ /^^\.*==\d+==/) {
- # not valgrind output
- push @new_output, $line;
- next;
- }
-
- my ($errcount) = ($line =~ /==\d+== ERROR SUMMARY: (\d+) errors/);
- if (defined $errcount && $errcount > 0) {
- $errors = 1;
- }
-
- (my $leakcount) = ($line =~ /==\d+==\s+(?:definitely|possibly) lost:\s+([0-9,]+)/);
- if (defined $leakcount && $leakcount > 0) {
- $leaks = 1;
- }
- }
-
- @$outputref = @new_output;
-
- my $bad = "";
- $bad .= "(valgrind errors)" if ($errors);
- $bad .= "(valgrind leaks)" if ($leaks);
- return $bad;
-}
-
-
-
-sub filter_malloc
-{
- my $outputref = shift;
- my $errors = 0;
-
- my @new_output;
- my $count = 0;
- for my $line (@$outputref) {
- # Ignore MallocScribble prologue.
- # Ignore MallocStackLogging prologue.
- if ($line =~ /malloc: enabling scribbling to detect mods to free/ ||
- $line =~ /Deleted objects will be dirtied by the collector/ ||
- $line =~ /malloc: stack logs being written into/ ||
- $line =~ /malloc: stack logs deleted from/ ||
- $line =~ /malloc: process \d+ no longer exists/ ||
- $line =~ /malloc: recording malloc and VM allocation stacks/)
- {
- next;
- }
-
- # not malloc output
- push @new_output, $line;
-
- }
-
- @$outputref = @new_output;
-}
-
-sub filter_guardmalloc
-{
- my $outputref = shift;
- my $errors = 0;
-
- my @new_output;
- my $count = 0;
- for my $line (@$outputref) {
- if ($line !~ /^GuardMalloc\[[^\]]+\]: /) {
- # not guardmalloc output
- push @new_output, $line;
- next;
- }
-
- # Ignore 4 lines of guardmalloc prologue.
- # Anything further is a guardmalloc error.
- if (++$count > 4) {
- $errors = 1;
- }
- }
-
- @$outputref = @new_output;
-
- my $bad = "";
- $bad .= "(guardmalloc errors)" if ($errors);
- return $bad;
-}
-
-# TEST_SOMETHING
-# text
-# text
-# END
-sub extract_multiline {
- my ($flag, $contents, $name) = @_;
- if ($contents =~ /$flag\n/) {
- my ($output) = ($contents =~ /$flag\n(.*?\n)END[ *\/]*\n/s);
- die "$name used $flag without END\n" if !defined($output);
- return $output;
- }
- return undef;
-}
-
-
-# TEST_SOMETHING
-# text
-# OR
-# text
-# END
-sub extract_multiple_multiline {
- my ($flag, $contents, $name) = @_;
- if ($contents =~ /$flag\n/) {
- my ($output) = ($contents =~ /$flag\n(.*?\n)END[ *\/]*\n/s);
- die "$name used $flag without END\n" if !defined($output);
-
- $output =~ s/\nOR\n/\n|/sg;
- $output = "^(" . $output . ")\$";
- return $output;
- }
- return undef;
-}
-
-
-sub gather_simple {
- my $CREF = shift;
- my %C = %{$CREF};
- my $name = shift;
- chdir_verbose $DIR;
-
- my $ext = $ALL_TESTS{$name};
- my $file = "$name.$ext";
- return 0 if !$file;
-
- # search file for 'TEST_CONFIG' or '#include "test.h"'
- # also collect other values:
- # TEST_DISABLED disable test with an optional message
- # TEST_CRASHES test is expected to crash
- # TEST_CONFIG test conditions
- # TEST_ENV environment prefix
- # TEST_CFLAGS compile flags
- # TEST_BUILD build instructions
- # TEST_BUILD_OUTPUT expected build stdout/stderr
- # TEST_RUN_OUTPUT expected run stdout/stderr
- open(my $in, "< $file") || die;
- my $contents = join "", <$in>;
-
- my $test_h = ($contents =~ /^\s*#\s*(include|import)\s*"test\.h"/m);
- my ($disabled) = ($contents =~ /\b(TEST_DISABLED\b.*)$/m);
- my $crashes = ($contents =~ /\bTEST_CRASHES\b/m);
- my ($conditionstring) = ($contents =~ /\bTEST_CONFIG\b(.*)$/m);
- my ($envstring) = ($contents =~ /\bTEST_ENV\b(.*)$/m);
- my ($cflags) = ($contents =~ /\bTEST_CFLAGS\b(.*)$/m);
- my ($buildcmd) = extract_multiline("TEST_BUILD", $contents, $name);
- my ($builderror) = extract_multiple_multiline("TEST_BUILD_OUTPUT", $contents, $name);
- my ($runerror) = extract_multiple_multiline("TEST_RUN_OUTPUT", $contents, $name);
-
- return 0 if !$test_h && !$disabled && !$crashes && !defined($conditionstring) && !defined($envstring) && !defined($cflags) && !defined($buildcmd) && !defined($builderror) && !defined($runerror);
-
- if ($disabled) {
- print "${yellow}SKIP: $name (disabled by $disabled)$nocolor\n";
- return 0;
- }
-
- # check test conditions
-
- my $run = 1;
- my %conditions = readconditions($conditionstring);
- if (! $conditions{LANGUAGE}) {
- # implicit language restriction from file extension
- $conditions{LANGUAGE} = $languages_for_extension{$ext};
- }
- for my $condkey (keys %conditions) {
- my @condvalues = @{$conditions{$condkey}};
-
- # special case: RUN=0 does not affect build
- if ($condkey eq "RUN" && @condvalues == 1 && $condvalues[0] == 0) {
- $run = 0;
- next;
- }
-
- my $testvalue = $C{$condkey};
- next if !defined($testvalue);
- # testvalue is the configuration being run now
- # condvalues are the allowed values for this test
-
- my $ok = 0;
- for my $condvalue (@condvalues) {
-
- # special case: objc and objc++
- if ($condkey eq "LANGUAGE") {
- $condvalue = "objective-c" if $condvalue eq "objc";
- $condvalue = "objective-c++" if $condvalue eq "objc++";
- }
-
- $ok = 1 if ($testvalue eq $condvalue);
-
- # special case: CC and CXX allow substring matches
- if ($condkey eq "CC" || $condkey eq "CXX") {
- $ok = 1 if ($testvalue =~ /$condvalue/);
- }
-
- last if $ok;
- }
-
- if (!$ok) {
- my $plural = (@condvalues > 1) ? "one of: " : "";
- print "SKIP: $name ($condkey=$testvalue, but test requires $plural", join(' ', @condvalues), ")\n";
- return 0;
- }
- }
-
- # save some results for build and run phases
- $$CREF{"TEST_$name"} = {
- TEST_BUILD => $buildcmd,
- TEST_BUILD_OUTPUT => $builderror,
- TEST_CRASHES => $crashes,
- TEST_RUN_OUTPUT => $runerror,
- TEST_CFLAGS => $cflags,
- TEST_ENV => $envstring,
- TEST_RUN => $run,
- };
-
- return 1;
-}
-
-# Builds a simple test
-sub build_simple {
- my %C = %{shift()};
- my $name = shift;
- my %T = %{$C{"TEST_$name"}};
- chdir_verbose "$C{DIR}/$name.build";
-
- my $ext = $ALL_TESTS{$name};
- my $file = "$DIR/$name.$ext";
-
- if ($T{TEST_CRASHES}) {
- `echo '$crashcatch' > crashcatch.c`;
- make("$C{COMPILE_C} -dynamiclib -o libcrashcatch.dylib -x c crashcatch.c");
- die "$?" if $?;
- }
-
- my $cmd = $T{TEST_BUILD} ? eval "return \"$T{TEST_BUILD}\"" : "$C{COMPILE} $T{TEST_CFLAGS} $file -o $name.out";
-
- my $output = make($cmd);
-
- # rdar://10163155
- $output =~ s/ld: warning: could not create compact unwind for [^\n]+: does not use standard frame\n//g;
-
- my $ok;
- if (my $builderror = $T{TEST_BUILD_OUTPUT}) {
- # check for expected output and ignore $?
- if ($output =~ /$builderror/) {
- $ok = 1;
- } else {
- print "${red}FAIL: /// test '$name' \\\\\\$nocolor\n";
- colorprint $red, $output;
- print "${red}FAIL: \\\\\\ test '$name' ///$nocolor\n";
- print "${red}FAIL: $name (build output does not match TEST_BUILD_OUTPUT)$nocolor\n";
- $ok = 0;
- }
- } elsif ($?) {
- print "${red}FAIL: /// test '$name' \\\\\\$nocolor\n";
- colorprint $red, $output;
- print "${red}FAIL: \\\\\\ test '$name' ///$nocolor\n";
- print "${red}FAIL: $name (build failed)$nocolor\n";
- $ok = 0;
- } elsif ($output ne "") {
- print "${red}FAIL: /// test '$name' \\\\\\$nocolor\n";
- colorprint $red, $output;
- print "${red}FAIL: \\\\\\ test '$name' ///$nocolor\n";
- print "${red}FAIL: $name (unexpected build output)$nocolor\n";
- $ok = 0;
- } else {
- $ok = 1;
- }
-
-
- if ($ok) {
- foreach my $file (glob("*.out *.dylib *.bundle")) {
- make("dsymutil $file");
- }
- }
-
- return $ok;
-}
-
-# Run a simple test (testname.out, with error checking of stdout and stderr)
-sub run_simple {
- my %C = %{shift()};
- my $name = shift;
- my %T = %{$C{"TEST_$name"}};
-
- if (! $T{TEST_RUN}) {
- print "PASS: $name (build only)\n";
- return 1;
- }
-
- my $testdir = "$C{DIR}/$name.build";
- chdir_verbose $testdir;
-
- my $env = "$C{ENV} $T{TEST_ENV}";
-
- my $output;
-
- if ($C{ARCH} =~ /^arm/ && `unamep -p` !~ /^arm/) {
- # run on iOS or watchos device
-
- my $remotedir = "/var/root/objctest/" . basename($C{DIR}) . "/$name.build";
-
- # Add test dir and libobjc's dir to DYLD_LIBRARY_PATH.
- # Insert libcrashcatch.dylib if necessary.
- $env .= " DYLD_LIBRARY_PATH=$remotedir";
- $env .= ":/var/root/objctest/" if ($C{TESTLIB} ne $TESTLIBPATH);
- if ($T{TEST_CRASHES}) {
- $env .= " DYLD_INSERT_LIBRARIES=$remotedir/libcrashcatch.dylib";
- }
-
- my $cmd = "ssh iphone 'cd $remotedir && env $env ./$name.out'";
- $output = make("$cmd");
- }
- elsif ($C{OS} =~ /simulator/) {
- # run locally in an iOS simulator
- # fixme appletvsimulator and watchsimulator
- # fixme SDK
- my $sim = "xcrun -sdk iphonesimulator simctl spawn 'iPhone 6'";
-
- # Add test dir and libobjc's dir to DYLD_LIBRARY_PATH.
- # Insert libcrashcatch.dylib if necessary.
- $env .= " DYLD_LIBRARY_PATH=$testdir";
- $env .= ":" . dirname($C{TESTLIB}) if ($C{TESTLIB} ne $TESTLIBPATH);
- if ($T{TEST_CRASHES}) {
- $env .= " DYLD_INSERT_LIBRARIES=$testdir/libcrashcatch.dylib";
- }
-
- my $simenv = "";
- foreach my $keyvalue (split(' ', $env)) {
- $simenv .= "SIMCTL_CHILD_$keyvalue ";
- }
- # Use the full path here so hack_cwd in test.h works.
- $output = make("env $simenv $sim $testdir/$name.out");
- }
- else {
- # run locally
-
- # Add test dir and libobjc's dir to DYLD_LIBRARY_PATH.
- # Insert libcrashcatch.dylib if necessary.
- $env .= " DYLD_LIBRARY_PATH=$testdir";
- $env .= ":" . dirname($C{TESTLIB}) if ($C{TESTLIB} ne $TESTLIBPATH);
- if ($T{TEST_CRASHES}) {
- $env .= " DYLD_INSERT_LIBRARIES=$testdir/libcrashcatch.dylib";
- }
-
- $output = make("sh -c '$env ./$name.out'");
- }
-
- return check_output(\%C, $name, split("\n", $output));
-}
-
-
-my %compiler_memo;
-sub find_compiler {
- my ($cc, $toolchain, $sdk_path) = @_;
-
- # memoize
- my $key = $cc . ':' . $toolchain;
- my $result = $compiler_memo{$key};
- return $result if defined $result;
-
- $result = make("xcrun -toolchain $toolchain -find $cc 2>/dev/null");
-
- chomp $result;
- $compiler_memo{$key} = $result;
- return $result;
-}
-
-sub make_one_config {
- my $configref = shift;
- my $root = shift;
- my %C = %{$configref};
-
- # Aliases
- $C{LANGUAGE} = "objective-c" if $C{LANGUAGE} eq "objc";
- $C{LANGUAGE} = "objective-c++" if $C{LANGUAGE} eq "objc++";
-
- # Interpret OS version string from command line.
- my ($sdk_arg, $deployment_arg, $run_arg, undef) = split('-', $C{OSVERSION});
- delete $C{OSVERSION};
- my ($os_arg) = ($sdk_arg =~ /^([^\.0-9]+)/);
- $deployment_arg = "default" if !defined($deployment_arg);
- $run_arg = "default" if !defined($run_arg);
-
-
- die "unknown OS '$os_arg' (expected iphoneos or iphonesimulator or watchos or watchsimulator or macosx)\n" if ($os_arg ne "iphoneos" && $os_arg ne "iphonesimulator" && $os_arg ne "watchos" && $os_arg ne "watchsimulator" && $os_arg ne "macosx");
-
- $C{OS} = $os_arg;
-
- if ($os_arg eq "iphoneos" || $os_arg eq "iphonesimulator") {
- $C{TOOLCHAIN} = "ios";
- } elsif ($os_arg eq "watchos" || $os_arg eq "watchsimulator") {
- $C{TOOLCHAIN} = "watchos";
- } elsif ($os_arg eq "macosx") {
- $C{TOOLCHAIN} = "osx";
- } else {
- print "${yellow}WARN: don't know toolchain for OS $C{OS}${nocolor}\n";
- $C{TOOLCHAIN} = "default";
- }
-
- # Look up SDK
- # Try exact match first.
- # Then try lexically-last prefix match (so "macosx" => "macosx10.7internal")
- my @sdks = getsdks();
- if ($VERBOSE) {
- print "note: Installed SDKs: @sdks\n";
- }
- my $exactsdk = undef;
- my $prefixsdk = undef;
- foreach my $sdk (@sdks) {
- $exactsdk = $sdk if ($sdk eq $sdk_arg);
- $prefixsdk = newersdk($sdk, $prefixsdk) if ($sdk =~ /^$sdk_arg/);
- }
-
- my $sdk;
- if ($exactsdk) {
- $sdk = $exactsdk;
- } elsif ($prefixsdk) {
- $sdk = $prefixsdk;
- } else {
- die "unknown SDK '$sdk_arg'\nInstalled SDKs: @sdks\n";
- }
-
- # Set deployment target and run target.
- # fixme can't enforce version when run_arg eq "default"
- # because we don't know it yet
- $deployment_arg = versionsuffix($sdk) if $deployment_arg eq "default";
- if ($run_arg ne "default") {
- die "Deployment target '$deployment_arg' is newer than run target '$run_arg'\n" if $deployment_arg > $run_arg;
- }
- $C{DEPLOYMENT_TARGET} = $deployment_arg;
- $C{RUN_TARGET} = $run_arg;
-
- # set the config name now, after massaging the language and OS versions,
- # but before adding other settings
- my $configname = config_name(%C);
- die if ($configname =~ /'/);
- die if ($configname =~ / /);
- ($C{NAME} = $configname) =~ s/~/ /g;
- (my $configdir = $configname) =~ s#/##g;
- $C{DIR} = "$BUILDDIR/$configdir";
-
- $C{SDK_PATH} = getsdkpath($sdk);
-
- # Look up test library (possible in root or SDK_PATH)
-
- my $rootarg = $root;
- my $symroot;
- my @sympaths = ( (glob "$root/*~sym")[0],
- (glob "$root/BuildRecords/*_install/Symbols")[0],
- "$root/Symbols" );
- my @dstpaths = ( (glob "$root/*~dst")[0],
- (glob "$root/BuildRecords/*_install/Root")[0],
- "$root/Root" );
- for(my $i = 0; $i < scalar(@sympaths); $i++) {
- if (-e $sympaths[$i] && -e $dstpaths[$i]) {
- $symroot = $sympaths[$i];
- $root = $dstpaths[$i];
- last;
- }
- }
-
- if ($root ne "" && -e "$root$C{SDK_PATH}$TESTLIBPATH") {
- $C{TESTLIB} = "$root$C{SDK_PATH}$TESTLIBPATH";
- } elsif (-e "$root$TESTLIBPATH") {
- $C{TESTLIB} = "$root$TESTLIBPATH";
- } elsif (-e "$root/$TESTLIBNAME") {
- $C{TESTLIB} = "$root/$TESTLIBNAME";
- } else {
- die "No $TESTLIBNAME in root '$rootarg' for sdk '$C{SDK_PATH}'\n"
- # . join("\n", @dstpaths) . "\n"
- ;
- }
-
- if (-e "$symroot/$TESTLIBNAME.dSYM") {
- $C{TESTDSYM} = "$symroot/$TESTLIBNAME.dSYM";
- }
-
- if ($VERBOSE) {
- my @uuids = `/usr/bin/dwarfdump -u '$C{TESTLIB}'`;
- while (my $uuid = shift @uuids) {
- print "note: $uuid";
- }
- }
-
- # Look up compilers
- my $cc = $C{CC};
- my $cxx = cplusplus($C{CC});
- my $swift = swift($C{CC});
- if (! $BUILD) {
- $C{CC} = $cc;
- $C{CXX} = $cxx;
- $C{SWIFT} = $swift
- } else {
- $C{CC} = find_compiler($cc, $C{TOOLCHAIN}, $C{SDK_PATH});
- $C{CXX} = find_compiler($cxx, $C{TOOLCHAIN}, $C{SDK_PATH});
- $C{SWIFT} = find_compiler($swift, $C{TOOLCHAIN}, $C{SDK_PATH});
-
- die "No compiler '$cc' ('$C{CC}') in toolchain '$C{TOOLCHAIN}'\n" if !-e $C{CC};
- die "No compiler '$cxx' ('$C{CXX}') in toolchain '$C{TOOLCHAIN}'\n" if !-e $C{CXX};
- die "No compiler '$swift' ('$C{SWIFT}') in toolchain '$C{TOOLCHAIN}'\n" if !-e $C{SWIFT};
- }
-
- # Populate cflags
-
- # save-temps so dsymutil works so debug info works
- my $cflags = "-I$DIR -W -Wall -Wno-deprecated-declarations -Wshorten-64-to-32 -g -save-temps -Os -arch $C{ARCH} ";
- my $objcflags = "";
- my $swiftflags = "-g ";
-
- $cflags .= " -isysroot '$C{SDK_PATH}'";
- $cflags .= " '-Wl,-syslibroot,$C{SDK_PATH}'";
- $swiftflags .= " -sdk '$C{SDK_PATH}'";
-
- # Set deployment target cflags
- my $target = undef;
- die "No deployment target" if $C{DEPLOYMENT_TARGET} eq "";
- if ($C{OS} eq "iphoneos") {
- $cflags .= " -mios-version-min=$C{DEPLOYMENT_TARGET}";
- $target = "$C{ARCH}-apple-ios$C{DEPLOYMENT_TARGET}";
- }
- elsif ($C{OS} eq "iphonesimulator") {
- $cflags .= " -mios-simulator-version-min=$C{DEPLOYMENT_TARGET}";
- $target = "$C{ARCH}-apple-ios$C{DEPLOYMENT_TARGET}";
- }
- elsif ($C{OS} eq "watchos") {
- $cflags .= " -mwatchos-version-min=$C{DEPLOYMENT_TARGET}";
- $target = "$C{ARCH}-apple-watchos$C{DEPLOYMENT_TARGET}";
- }
- elsif ($C{OS} eq "watchsimulator") {
- $cflags .= " -mwatch-simulator-version-min=$C{DEPLOYMENT_TARGET}";
- $target = "$C{ARCH}-apple-watchos$C{DEPLOYMENT_TARGET}";
- }
- else {
- $cflags .= " -mmacosx-version-min=$C{DEPLOYMENT_TARGET}";
- $target = "$C{ARCH}-apple-macosx$C{DEPLOYMENT_TARGET}";
- }
- $swiftflags .= " -target $target";
-
- # fixme still necessary?
- if ($C{OS} eq "iphonesimulator" && $C{ARCH} eq "i386") {
- $objcflags .= " -fobjc-abi-version=2 -fobjc-legacy-dispatch";
- }
-
- if ($root ne "") {
- my $library_path = dirname($C{TESTLIB});
- $cflags .= " -L$library_path";
- $cflags .= " -I '$root/usr/include'";
- $cflags .= " -I '$root/usr/local/include'";
-
- if ($C{SDK_PATH} ne "/") {
- $cflags .= " -I '$root$C{SDK_PATH}/usr/include'";
- $cflags .= " -I '$root$C{SDK_PATH}/usr/local/include'";
- }
- }
-
- if ($C{CC} =~ /clang/) {
- $cflags .= " -Qunused-arguments -fno-caret-diagnostics";
- $cflags .= " -stdlib=$C{STDLIB}"; # fixme -fno-objc-link-runtime"
- $cflags .= " -Wl,-segalign,0x4000 ";
- }
-
-
- # Populate objcflags
-
- $objcflags .= " -lobjc";
- if ($C{MEM} eq "gc") {
- $objcflags .= " -fobjc-gc";
- }
- elsif ($C{MEM} eq "arc") {
- $objcflags .= " -fobjc-arc";
- }
- elsif ($C{MEM} eq "mrc") {
- # nothing
- }
- else {
- die "unrecognized MEM '$C{MEM}'\n";
- }
-
- if (supportslibauto($C{OS})) {
- # do this even for non-GC tests
- $objcflags .= " -lauto";
- }
-
- # Populate ENV_PREFIX
- $C{ENV} = "LANG=C MallocScribble=1";
- $C{ENV} .= " VERBOSE=$VERBOSE" if $VERBOSE;
- if ($root ne "") {
- die "no spaces allowed in root" if dirname($C{TESTLIB}) =~ /\s+/;
- }
- if ($C{GUARDMALLOC}) {
- $ENV{GUARDMALLOC} = "1"; # checked by tests and errcheck.pl
- $C{ENV} .= " DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib";
- if ($C{GUARDMALLOC} eq "before") {
- $C{ENV} .= " MALLOC_PROTECT_BEFORE=1";
- } elsif ($C{GUARDMALLOC} eq "after") {
- # protect after is the default
- } else {
- die "Unknown guard malloc mode '$C{GUARDMALLOC}'\n";
- }
- }
-
- # Populate compiler commands
- $C{COMPILE_C} = "env LANG=C '$C{CC}' $cflags -x c -std=gnu99";
- $C{COMPILE_CXX} = "env LANG=C '$C{CXX}' $cflags -x c++";
- $C{COMPILE_M} = "env LANG=C '$C{CC}' $cflags $objcflags -x objective-c -std=gnu99";
- $C{COMPILE_MM} = "env LANG=C '$C{CXX}' $cflags $objcflags -x objective-c++";
- $C{COMPILE_SWIFT} = "env LANG=C '$C{SWIFT}' $swiftflags";
-
- $C{COMPILE} = $C{COMPILE_C} if $C{LANGUAGE} eq "c";
- $C{COMPILE} = $C{COMPILE_CXX} if $C{LANGUAGE} eq "c++";
- $C{COMPILE} = $C{COMPILE_M} if $C{LANGUAGE} eq "objective-c";
- $C{COMPILE} = $C{COMPILE_MM} if $C{LANGUAGE} eq "objective-c++";
- $C{COMPILE} = $C{COMPILE_SWIFT} if $C{LANGUAGE} eq "swift";
- die "unknown language '$C{LANGUAGE}'\n" if !defined $C{COMPILE};
-
- ($C{COMPILE_NOMEM} = $C{COMPILE}) =~ s/ -fobjc-(?:gc|arc)\S*//g;
- ($C{COMPILE_NOLINK} = $C{COMPILE}) =~ s/ '?-(?:Wl,|l)\S*//g;
- ($C{COMPILE_NOLINK_NOMEM} = $C{COMPILE_NOMEM}) =~ s/ '?-(?:Wl,|l)\S*//g;
-
-
- # Reject some self-inconsistent configurations
- if ($C{MEM} !~ /^(mrc|arc|gc)$/) {
- die "unknown MEM=$C{MEM} (expected one of mrc arc gc)\n";
- }
-
- if ($C{MEM} eq "gc" && $C{OS} !~ /^macosx/) {
- print "note: skipping configuration $C{NAME}\n";
- print "note: because OS=$C{OS} does not support MEM=$C{MEM}\n";
- return 0;
- }
- if ($C{MEM} eq "gc" && $C{ARCH} eq "x86_64h") {
- print "note: skipping configuration $C{NAME}\n";
- print "note: because ARCH=$C{ARCH} does not support MEM=$C{MEM}\n";
- return 0;
- }
- if ($C{MEM} eq "arc" && $C{OS} =~ /^macosx/ && $C{ARCH} eq "i386") {
- print "note: skipping configuration $C{NAME}\n";
- print "note: because 32-bit Mac does not support MEM=$C{MEM}\n";
- return 0;
- }
- if ($C{MEM} eq "arc" && $C{CC} !~ /clang/) {
- print "note: skipping configuration $C{NAME}\n";
- print "note: because CC=$C{CC} does not support MEM=$C{MEM}\n";
- return 0;
- }
-
- if ($C{STDLIB} ne "libstdc++" && $C{CC} !~ /clang/) {
- print "note: skipping configuration $C{NAME}\n";
- print "note: because CC=$C{CC} does not support STDLIB=$C{STDLIB}\n";
- return 0;
- }
-
- # fixme
- if ($C{LANGUAGE} eq "swift" && $C{ARCH} =~ /^arm/) {
- print "note: skipping configuration $C{NAME}\n";
- print "note: because ARCH=$C{ARCH} does not support LANGUAGE=SWIFT\n";
- return 0;
- }
-
- # fixme unimplemented run targets
- if ($C{RUN_TARGET} ne "default" && $C{OS} !~ /simulator/) {
- print "${yellow}WARN: skipping configuration $C{NAME}${nocolor}\n";
- print "${yellow}WARN: because OS=$C{OS} does not yet implement RUN_TARGET=$C{RUN_TARGET}${nocolor}\n";
- }
-
- %$configref = %C;
-}
-
-sub make_configs {
- my ($root, %args) = @_;
-
- my @results = ({}); # start with one empty config
-
- for my $key (keys %args) {
- my @newresults;
- my @values = @{$args{$key}};
- for my $configref (@results) {
- my %config = %{$configref};
- for my $value (@values) {
- my %newconfig = %config;
- $newconfig{$key} = $value;
- push @newresults, \%newconfig;
- }
- }
- @results = @newresults;
- }
-
- my @newresults;
- for my $configref(@results) {
- if (make_one_config($configref, $root)) {
- push @newresults, $configref;
- }
- }
-
- return @newresults;
-}
-
-sub config_name {
- my %config = @_;
- my $name = "";
- for my $key (sort keys %config) {
- $name .= '~' if $name ne "";
- $name .= "$key=$config{$key}";
- }
- return $name;
-}
-
-sub run_one_config {
- my %C = %{shift()};
- my @tests = @_;
-
- # Build and run
- my $testcount = 0;
- my $failcount = 0;
-
- my @gathertests;
- foreach my $test (@tests) {
- if ($VERBOSE) {
- print "\nGATHER $test\n";
- }
-
- if ($ALL_TESTS{$test}) {
- gather_simple(\%C, $test) || next; # not pass, not fail
- push @gathertests, $test;
- } else {
- die "No test named '$test'\n";
- }
- }
-
- my @builttests;
- if (!$BUILD) {
- @builttests = @gathertests;
- $testcount = scalar(@gathertests);
- } else {
- my $configdir = $C{DIR};
- print $configdir, "\n" if $VERBOSE;
- mkdir $configdir || die;
-
- foreach my $test (@gathertests) {
- if ($VERBOSE) {
- print "\nBUILD $test\n";
- }
- mkdir "$configdir/$test.build" || die;
-
- if ($ALL_TESTS{$test}) {
- $testcount++;
- if (!build_simple(\%C, $test)) {
- $failcount++;
- } else {
- push @builttests, $test;
- }
- } else {
- die "No test named '$test'\n";
- }
- }
- }
-
- if (!$RUN || !scalar(@builttests)) {
- # nothing to do
- }
- else {
- if ($C{ARCH} =~ /^arm/ && `unamep -p` !~ /^arm/) {
- # upload all tests to iOS device
- make("RSYNC_PASSWORD=alpine rsync -av $C{DIR} rsync://root\@localhost:10873/root/var/root/objctest/");
- die "Couldn't rsync tests to device\n" if ($?);
-
- # upload library to iOS device
- if ($C{TESTLIB} ne $TESTLIBPATH) {
- make("RSYNC_PASSWORD=alpine rsync -av $C{TESTLIB} rsync://root\@localhost:10873/root/var/root/objctest/");
- die "Couldn't rsync $C{TESTLIB} to device\n" if ($?);
- make("RSYNC_PASSWORD=alpine rsync -av $C{TESTDSYM} rsync://root\@localhost:10873/root/var/root/objctest/");
- }
- }
-
- foreach my $test (@builttests) {
- print "\nRUN $test\n" if ($VERBOSE);
-
- if ($ALL_TESTS{$test})
- {
- if (!run_simple(\%C, $test)) {
- $failcount++;
- }
- } else {
- die "No test named '$test'\n";
- }
- }
- }
-
- return ($testcount, $failcount);
-}
-
-
-
-# Return value if set by "$argname=value" on the command line
-# Return $default if not set.
-sub getargs {
- my ($argname, $default) = @_;
-
- foreach my $arg (@ARGV) {
- my ($value) = ($arg =~ /^$argname=(.+)$/);
- return [split ',', $value] if defined $value;
- }
-
- return [split ',', $default];
-}
-
-# Return 1 or 0 if set by "$argname=1" or "$argname=0" on the
-# command line. Return $default if not set.
-sub getbools {
- my ($argname, $default) = @_;
-
- my @values = @{getargs($argname, $default)};
- return [( map { ($_ eq "0") ? 0 : 1 } @values )];
-}
-
-# Return an integer if set by "$argname=value" on the
-# command line. Return $default if not set.
-sub getints {
- my ($argname, $default) = @_;
-
- my @values = @{getargs($argname, $default)};
- return [( map { int($_) } @values )];
-}
-
-sub getarg {
- my ($argname, $default) = @_;
- my @values = @{getargs($argname, $default)};
- die "Only one value allowed for $argname\n" if @values > 1;
- return $values[0];
-}
-
-sub getbool {
- my ($argname, $default) = @_;
- my @values = @{getbools($argname, $default)};
- die "Only one value allowed for $argname\n" if @values > 1;
- return $values[0];
-}
-
-sub getint {
- my ($argname, $default) = @_;
- my @values = @{getints($argname, $default)};
- die "Only one value allowed for $argname\n" if @values > 1;
- return $values[0];
-}
-
-
-# main
-my %args;
-
-
-my $default_arch = (`/usr/sbin/sysctl hw.optional.x86_64` eq "hw.optional.x86_64: 1\n") ? "x86_64" : "i386";
-$args{ARCH} = getargs("ARCH", 0);
-$args{ARCH} = getargs("ARCHS", $default_arch) if !@{$args{ARCH}}[0];
-
-$args{OSVERSION} = getargs("OS", "macosx-default-default");
-
-$args{MEM} = getargs("MEM", "mrc");
-$args{LANGUAGE} = [ map { lc($_) } @{getargs("LANGUAGE", "objective-c,swift")} ];
-$args{STDLIB} = getargs("STDLIB", "libc++");
-
-$args{CC} = getargs("CC", "clang");
-
-{
- my $guardmalloc = getargs("GUARDMALLOC", 0);
- # GUARDMALLOC=1 is the same as GUARDMALLOC=before,after
- my @guardmalloc2 = ();
- for my $arg (@$guardmalloc) {
- if ($arg == 1) { push @guardmalloc2, "before";
- push @guardmalloc2, "after"; }
- else { push @guardmalloc2, $arg }
- }
- $args{GUARDMALLOC} = \@guardmalloc2;
-}
-
-$BUILD = getbool("BUILD", 1);
-$RUN = getbool("RUN", 1);
-$VERBOSE = getint("VERBOSE", 0);
-
-my $root = getarg("ROOT", "");
-$root =~ s#/*$##;
-
-my @tests = gettests();
-
-print "note: -----\n";
-print "note: testing root '$root'\n";
-
-my @configs = make_configs($root, %args);
-
-print "note: -----\n";
-print "note: testing ", scalar(@configs), " configurations:\n";
-for my $configref (@configs) {
- my $configname = $$configref{NAME};
- print "note: configuration $configname\n";
-}
-
-if ($BUILD) {
- `rm -rf '$BUILDDIR'`;
- mkdir "$BUILDDIR" || die;
-}
-
-my $failed = 0;
-
-my $testconfigs = @configs;
-my $failconfigs = 0;
-my $testcount = 0;
-my $failcount = 0;
-for my $configref (@configs) {
- my $configname = $$configref{NAME};
- print "note: -----\n";
- print "note: \nnote: $configname\nnote: \n";
-
- (my $t, my $f) = eval { run_one_config($configref, @tests); };
- if ($@) {
- chomp $@;
- print "${red}FAIL: $configname${nocolor}\n";
- print "${red}FAIL: $@${nocolor}\n";
- $failconfigs++;
- } else {
- my $color = ($f ? $red : "");
- print "note:\n";
- print "${color}note: $configname$nocolor\n";
- print "${color}note: $t tests, $f failures$nocolor\n";
- $testcount += $t;
- $failcount += $f;
- $failconfigs++ if ($f);
- }
-}
-
-print "note: -----\n";
-my $color = ($failconfigs ? $red : "");
-print "${color}note: $testconfigs configurations, $failconfigs with failures$nocolor\n";
-print "${color}note: $testcount tests, $failcount failures$nocolor\n";
-
-$failed = ($failconfigs ? 1 : 0);
-
-exit ($failed ? 1 : 0);
+++ /dev/null
-// testroot.i
-// Implementation of class TestRoot
-// Include this file into your main test file to use it.
-
-#include "test.h"
-#include <dlfcn.h>
-#include <objc/objc-internal.h>
-
-int TestRootLoad = 0;
-int TestRootInitialize = 0;
-int TestRootAlloc = 0;
-int TestRootAllocWithZone = 0;
-int TestRootCopy = 0;
-int TestRootCopyWithZone = 0;
-int TestRootMutableCopy = 0;
-int TestRootMutableCopyWithZone = 0;
-int TestRootInit = 0;
-int TestRootDealloc = 0;
-int TestRootFinalize = 0;
-int TestRootRetain = 0;
-int TestRootRelease = 0;
-int TestRootAutorelease = 0;
-int TestRootRetainCount = 0;
-int TestRootTryRetain = 0;
-int TestRootIsDeallocating = 0;
-int TestRootPlusRetain = 0;
-int TestRootPlusRelease = 0;
-int TestRootPlusAutorelease = 0;
-int TestRootPlusRetainCount = 0;
-
-
-@implementation TestRoot
-
-// These all use void* pending rdar://9310005.
-
-static void *
-retain_fn(void *self, SEL _cmd __unused) {
- OSAtomicIncrement32(&TestRootRetain);
- void * (*fn)(void *) = (typeof(fn))_objc_rootRetain;
- return fn(self);
-}
-
-static void
-release_fn(void *self, SEL _cmd __unused) {
- OSAtomicIncrement32(&TestRootRelease);
- void (*fn)(void *) = (typeof(fn))_objc_rootRelease;
- fn(self);
-}
-
-static void *
-autorelease_fn(void *self, SEL _cmd __unused) {
- OSAtomicIncrement32(&TestRootAutorelease);
- void * (*fn)(void *) = (typeof(fn))_objc_rootAutorelease;
- return fn(self);
-}
-
-static unsigned long
-retaincount_fn(void *self, SEL _cmd __unused) {
- OSAtomicIncrement32(&TestRootRetainCount);
- unsigned long (*fn)(void *) = (typeof(fn))_objc_rootRetainCount;
- return fn(self);
-}
-
-static void *
-copywithzone_fn(void *self, SEL _cmd __unused, void *zone) {
- OSAtomicIncrement32(&TestRootCopyWithZone);
- void * (*fn)(void *, void *) = (typeof(fn))dlsym(RTLD_DEFAULT, "object_copy");
- return fn(self, zone);
-}
-
-static void *
-plusretain_fn(void *self __unused, SEL _cmd __unused) {
- OSAtomicIncrement32(&TestRootPlusRetain);
- return self;
-}
-
-static void
-plusrelease_fn(void *self __unused, SEL _cmd __unused) {
- OSAtomicIncrement32(&TestRootPlusRelease);
-}
-
-static void *
-plusautorelease_fn(void *self, SEL _cmd __unused) {
- OSAtomicIncrement32(&TestRootPlusAutorelease);
- return self;
-}
-
-static unsigned long
-plusretaincount_fn(void *self __unused, SEL _cmd __unused) {
- OSAtomicIncrement32(&TestRootPlusRetainCount);
- return ULONG_MAX;
-}
-
-+(void) load {
- OSAtomicIncrement32(&TestRootLoad);
-
- // install methods that ARR refuses to compile
- class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
- class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
- class_addMethod(self, sel_registerName("autorelease"), (IMP)autorelease_fn, "");
- class_addMethod(self, sel_registerName("retainCount"), (IMP)retaincount_fn, "");
- class_addMethod(self, sel_registerName("copyWithZone:"), (IMP)copywithzone_fn, "");
-
- class_addMethod(object_getClass(self), sel_registerName("retain"), (IMP)plusretain_fn, "");
- class_addMethod(object_getClass(self), sel_registerName("release"), (IMP)plusrelease_fn, "");
- class_addMethod(object_getClass(self), sel_registerName("autorelease"), (IMP)plusautorelease_fn, "");
- class_addMethod(object_getClass(self), sel_registerName("retainCount"), (IMP)plusretaincount_fn, "");
-}
-
-
-+(void) initialize {
- OSAtomicIncrement32(&TestRootInitialize);
-}
-
--(id) self {
- return self;
-}
-
-+(Class) class {
- return self;
-}
-
--(Class) class {
- return object_getClass(self);
-}
-
-+(Class) superclass {
- return class_getSuperclass(self);
-}
-
--(Class) superclass {
- return class_getSuperclass([self class]);
-}
-
-+(id) new {
- return [[self alloc] init];
-}
-
-+(id) alloc {
- OSAtomicIncrement32(&TestRootAlloc);
- void * (*fn)(id __unsafe_unretained) = (typeof(fn))_objc_rootAlloc;
- return objc_retainedObject(fn(self));
-}
-
-+(id) allocWithZone:(void *)zone {
- OSAtomicIncrement32(&TestRootAllocWithZone);
- void * (*fn)(id __unsafe_unretained, void *) = (typeof(fn))_objc_rootAllocWithZone;
- return objc_retainedObject(fn(self, zone));
-}
-
-+(id) copy {
- return self;
-}
-
-+(id) copyWithZone:(void *) __unused zone {
- return self;
-}
-
--(id) copy {
- OSAtomicIncrement32(&TestRootCopy);
- return [self copyWithZone:NULL];
-}
-
-+(id) mutableCopyWithZone:(void *) __unused zone {
- fail("+mutableCopyWithZone: called");
-}
-
--(id) mutableCopy {
- OSAtomicIncrement32(&TestRootMutableCopy);
- return [self mutableCopyWithZone:NULL];
-}
-
--(id) mutableCopyWithZone:(void *) __unused zone {
- OSAtomicIncrement32(&TestRootMutableCopyWithZone);
- void * (*fn)(id __unsafe_unretained) = (typeof(fn))_objc_rootAlloc;
- return objc_retainedObject(fn(object_getClass(self)));
-}
-
--(id) init {
- OSAtomicIncrement32(&TestRootInit);
- return _objc_rootInit(self);
-}
-
-+(void) dealloc {
- fail("+dealloc called");
-}
-
--(void) dealloc {
- OSAtomicIncrement32(&TestRootDealloc);
- _objc_rootDealloc(self);
-}
-
-+(void) finalize {
- fail("+finalize called");
-}
-
--(void) finalize {
- OSAtomicIncrement32(&TestRootFinalize);
- _objc_rootFinalize(self);
-}
-
-+(BOOL) _tryRetain {
- return YES;
-}
-
--(BOOL) _tryRetain {
- OSAtomicIncrement32(&TestRootTryRetain);
- return _objc_rootTryRetain(self);
-}
-
-+(BOOL) _isDeallocating {
- return NO;
-}
-
--(BOOL) _isDeallocating {
- OSAtomicIncrement32(&TestRootIsDeallocating);
- return _objc_rootIsDeallocating(self);
-}
-
--(BOOL) allowsWeakReference {
- return ! [self _isDeallocating];
-}
-
--(BOOL) retainWeakReference {
- return [self _tryRetain];
-}
-
-
-@end
+++ /dev/null
-#include "test.h"
-
-@interface SmallClass : TestRoot @end
-
-@interface BigClass : TestRoot @end
-
+++ /dev/null
-// xpc leaks memory in dlopen(). Disable it.
-// TEST_ENV XPC_SERVICES_UNAVAILABLE=1
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/unload4.m -o unload4.dylib -dynamiclib
- $C{COMPILE_C} $DIR/unload3.c -o unload3.dylib -dynamiclib
- $C{COMPILE} $DIR/unload2.m -o unload2.bundle -bundle
- $C{COMPILE} $DIR/unload.m -o unload.out
-END
- */
-
-#include "test.h"
-#include <objc/runtime.h>
-#include <dlfcn.h>
-#include <unistd.h>
-
-#include "unload.h"
-
-#if __has_feature(objc_arc)
-
-int main()
-{
- testwarn("rdar://11368528 confused by Foundation");
- succeed(__FILE__);
-}
-
-#else
-
-static id forward_handler(void)
-{
- return 0;
-}
-
-static BOOL hasName(const char * const *names, const char *query)
-{
- const char *name;
- while ((name = *names++)) {
- if (strstr(name, query)) return YES;
- }
-
- return NO;
-}
-
-void cycle(void)
-{
- int i;
- char buf[100];
- unsigned int imageCount, imageCount0;
- const char **names;
- const char *name;
-
- names = objc_copyImageNames(&imageCount0);
- testassert(names);
- free(names);
-
- void *bundle = dlopen("unload2.bundle", RTLD_LAZY);
- testassert(bundle);
-
- names = objc_copyImageNames(&imageCount);
- testassert(names);
- testassert(imageCount == imageCount0 + 1);
- testassert(hasName(names, "unload2.bundle"));
- free(names);
-
- Class small = objc_getClass("SmallClass");
- Class big = objc_getClass("BigClass");
- testassert(small);
- testassert(big);
-
- name = class_getImageName(small);
- testassert(name);
- testassert(strstr(name, "unload2.bundle"));
- name = class_getImageName(big);
- testassert(name);
- testassert(strstr(name, "unload2.bundle"));
-
- id o1 = [small new];
- id o2 = [big new];
- testassert(o1);
- testassert(o2);
-
- // give BigClass and BigClass->isa large method caches (4692641)
- // Flush caches part way through to test large empty caches.
- for (i = 0; i < 3000; i++) {
- sprintf(buf, "method_%d", i);
- SEL sel = sel_registerName(buf);
- ((void(*)(id, SEL))objc_msgSend)(o2, sel);
- ((void(*)(id, SEL))objc_msgSend)(object_getClass(o2), sel);
- }
- _objc_flush_caches(object_getClass(o2));
- for (i = 0; i < 17000; i++) {
- sprintf(buf, "method_%d", i);
- SEL sel = sel_registerName(buf);
- ((void(*)(id, SEL))objc_msgSend)(o2, sel);
- ((void(*)(id, SEL))objc_msgSend)(object_getClass(o2), sel);
- }
-
- RELEASE_VAR(o1);
- RELEASE_VAR(o2);
-
- testcollect();
-
- int err = dlclose(bundle);
- testassert(err == 0);
- err = dlclose(bundle);
- testassert(err == -1); // already closed
-
- testassert(objc_getClass("SmallClass") == NULL);
- testassert(objc_getClass("BigClass") == NULL);
-
- names = objc_copyImageNames(&imageCount);
- testassert(names);
- testassert(imageCount == imageCount0);
- testassert(! hasName(names, "unload2.bundle"));
- free(names);
-
- // these selectors came from the bundle
- testassert(0 == strcmp("unload2_instance_method", sel_getName(sel_registerName("unload2_instance_method"))));
- testassert(0 == strcmp("unload2_category_method", sel_getName(sel_registerName("unload2_category_method"))));
-
- // This protocol came from the bundle.
- // It isn't unloaded cleanly (rdar://20664713), but neither
- // may it cause the protocol table to crash after unloading.
- testassert(objc_getProtocol("SmallProtocol"));
-}
-
-
-int main()
-{
- // fixme object_dispose() not aggressive enough?
- if (objc_collectingEnabled()) succeed(__FILE__);
-
- objc_setForwardHandler((void*)&forward_handler, (void*)&forward_handler);
-
-#if defined(__arm__) || defined(__arm64__)
- int count = 10;
-#else
- int count = is_guardmalloc() ? 10 : 100;
-#endif
-
- cycle();
-#if __LP64__
- // fixme heap use goes up 512 bytes after the 2nd cycle only - bad or not?
- cycle();
-#endif
-
- leak_mark();
- while (count--) {
- cycle();
- }
- leak_check(0);
-
- // 5359412 Make sure dylibs with nothing other than image_info can close
- void *dylib = dlopen("unload3.dylib", RTLD_LAZY);
- testassert(dylib);
- int err = dlclose(dylib);
- testassert(err == 0);
- err = dlclose(dylib);
- testassert(err == -1); // already closed
-
- // Make sure dylibs with real objc content cannot close
- dylib = dlopen("unload4.dylib", RTLD_LAZY);
- testassert(dylib);
- err = dlclose(dylib);
- testassert(err == 0);
- err = dlclose(dylib);
- testassert(err == 0); // dlopen from libobjc itself
- err = dlclose(dylib);
- testassert(err == -1); // already closed
-
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-#include "unload.h"
-#include "testroot.i"
-#import <objc/objc-api.h>
-
-@implementation SmallClass : TestRoot
--(void)unload2_instance_method { }
-@end
-
-
-@implementation BigClass : TestRoot
-@end
-
-OBJC_ROOT_CLASS
-@interface UnusedClass { id isa; } @end
-@implementation UnusedClass @end
-
-
-@protocol SmallProtocol
--(void)unload2_category_method;
-@end
-
-@interface SmallClass (Category) <SmallProtocol> @end
-
-@implementation SmallClass (Category)
--(void)unload2_category_method { }
-@end
+++ /dev/null
-// unload3: contains imageinfo but no other objc metadata
-// libobjc must not keep it open
-// DO NOT USE __OBJC2__; this is a C file.
-
-#include <TargetConditionals.h>
-
-#if TARGET_OS_WIN32 || (TARGET_OS_MAC && TARGET_CPU_X86 && !TARGET_IPHONE_SIMULATOR)
-// old ABI
-int fake[2] __attribute__((section("__OBJC,__image_info")))
-#else
-// new ABI
-int fake[2] __attribute__((section("__DATA,__objc_imageinfo")))
-#endif
- = { 0, TARGET_IPHONE_SIMULATOR ? (1<<5) : 0 };
-
-// silence "no debug symbols in executable" warning
-void fn(void) { }
+++ /dev/null
-// unload4: contains some objc metadata other than imageinfo
-// libobjc must keep it open
-
-#if __OBJC2__
-int fake2 __attribute__((section("__DATA,__objc_foo"))) = 0;
-#else
-int fake2 __attribute__((section("__OBJC,__foo"))) = 0;
-#endif
-
-// getsectiondata() falls over if __TEXT has no contents
-const char *unload4 = "unload4";
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include <objc/objc-exception.h>
-#include <Foundation/NSObject.h>
-
-#if !defined(__OBJC2__)
-
-int main()
-{
- succeed(__FILE__);
-}
-
-#else
-
-static int state;
-
-@interface Foo : NSObject @end
-@interface Bar : NSObject @end
-
-@interface Foo (Unimplemented)
-+(void)method;
-@end
-
-@implementation Bar @end
-
-@implementation Foo
-
--(void)check { state++; }
-+(void)check { testassert(!"caught class object, not instance"); }
-
-static id exc;
-
-static void handler(id unused, void *ctx) __attribute__((used));
-static void handler(id unused __unused, void *ctx __unused)
-{
- testassert(state == 3); state++;
-}
-
-+(BOOL) resolveClassMethod:(SEL)__unused name
-{
- testassert(state == 1); state++;
-#if !TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
- objc_addExceptionHandler(&handler, 0);
- testassert(state == 2);
-#else
- state++; // handler would have done this
-#endif
- state++;
- exc = [Foo new];
- @throw exc;
-}
-
-
-@end
-
-int main()
-{
- int i;
-
- // unwind exception and alt handler through objc_msgSend()
-
- PUSH_POOL {
-
- state = 0;
- for (i = 0; i < 100000; i++) {
- @try {
- testassert(state == 0); state++;
- [Foo method];
- testassert(0);
- } @catch (Bar *e) {
- testassert(0);
- } @catch (Foo *e) {
- testassert(e == exc);
- testassert(state == 4); state++;
- testassert(state == 5); [e check]; // state++
- RELEASE_VAR(exc);
- } @catch (id e) {
- testassert(0);
- } @catch (...) {
- testassert(0);
- } @finally {
- testassert(state == 6); state++;
- }
- testassert(state == 7); state = 0;
- }
-
- } POP_POOL;
-
- succeed(__FILE__);
-}
-
-#endif
+++ /dev/null
-#!/usr/bin/perl
-
-# verify-exports.pl
-# Check exports in a library vs. declarations in header files.
-# usage: verify-exports.pl /path/to/dylib /glob/path/to/headers decl-prefix [-arch <arch>] [/path/to/project~dst]
-# example: verify-exports.pl /usr/lib/libobjc.A.dylib '/usr/{local/,}include/objc/*' OBJC_EXPORT -arch x86_64 /tmp/objc-test.roots/objc-test~dst
-
-# requirements:
-# - every export must have an @interface or specially-marked declaration
-# - every @interface or specially-marked declaration must have an availability macro
-# - no C++ exports allowed
-
-use strict;
-use File::Basename;
-use File::Glob ':glob';
-
-my $bad = 0;
-
-$0 = basename($0, ".pl");
-my $usage = "/path/to/dylib /glob/path/to/headers decl-prefix [-arch <arch>] [-sdk sdkname] [/path/to/project~dst]";
-
-my $lib_arg = shift || die "$usage";
-die "$usage" unless ($lib_arg =~ /^\//);
-my $headers_arg = shift || die "$usage";
-my $export_arg = shift || die "$usage";
-
-my $arch = "x86_64";
-if ($ARGV[0] eq "-arch") {
- shift;
- $arch = shift || die "$0: -arch requires an architecture";
-}
-my $sdk = "system";
-if ($ARGV[0] eq "-sdk") {
- shift;
- $sdk = shift || die "$0: -sdk requires an SDK name";
-}
-
-my $root = shift || "";
-
-
-# Collect symbols from dylib.
-my $lib_path = "$root$lib_arg";
-die "$0: file not found: $lib_path\n" unless -e $lib_path;
-
-my %symbols;
-my @symbollines = `nm -arch $arch '$lib_path'`;
-die "$0: nm failed: (arch $arch) $lib_path\n" if ($?);
-for my $line (@symbollines) {
- chomp $line;
- (my $type, my $name) = ($line =~ /^[[:xdigit:]]*\s+(.) (.*)$/);
- if ($type =~ /^[A-TV-Z]$/) {
- $symbols{$name} = 1;
- } else {
- # undefined (U) or non-external - ignore
- }
-}
-
-# Complain about C++ exports
-for my $symbol (keys %symbols) {
- if ($symbol =~ /^__Z/) {
- print "BAD: C++ export '$symbol'\n"; $bad++;
- }
-}
-
-
-# Translate arch to unifdef(1) parameters: archnames, __LP64__, __OBJC2__
-my @archnames = ("x86_64", "i386", "arm", "armv6", "armv7");
-my %archOBJC1 = (i386 => 1);
-my %archLP64 = (x86_64 => 1);
-my @archparams;
-
-my $OBJC1 = ($archOBJC1{$arch} && $sdk !~ /^iphonesimulator/);
-
-if ($OBJC1) {
- push @archparams, "-U__OBJC2__";
-} else {
- push @archparams, "-D__OBJC2__=1";
-}
-
-if ($archLP64{$arch}) { push @archparams, "-D__LP64__=1"; }
-else { push @archparams, "-U__LP64__"; }
-
-for my $archname (@archnames) {
- if ($archname eq $arch) {
- push @archparams, "-D__${archname}__=1";
- push @archparams, "-D__$archname=1";
- } else {
- push @archparams, "-U__${archname}__";
- push @archparams, "-U__$archname";
- }
-}
-
-# TargetConditionals.h
-# fixme iphone and simulator
-push @archparams, "-DTARGET_OS_WIN32=0";
-push @archparams, "-DTARGET_OS_EMBEDDED=0";
-push @archparams, "-DTARGET_OS_IPHONE=0";
-push @archparams, "-DTARGET_OS_MAC=1";
-
-# Gather declarations from header files
-# A C declaration starts with $export_arg and ends with ';'
-# A class declaration is @interface plus the line before it.
-my $unifdef_cmd = "/usr/bin/unifdef " . join(" ", @archparams);
-my @cdecls;
-my @classdecls;
-for my $header_path(bsd_glob("$root$headers_arg",GLOB_BRACE)) {
- my $header;
- # feed through unifdef(1) first to strip decls from other archs
- # fixme strip other SDKs as well
- open($header, "$unifdef_cmd < '$header_path' |");
- my $header_contents = join("", <$header>);
-
- # C decls
- push @cdecls, ($header_contents =~ /^\s*$export_arg\s+([^;]*)/msg);
-
- # ObjC classes, but not categories.
- # fixme ivars
- push @classdecls, ($header_contents =~ /^([^\n]*\n\s*\@interface\s+[^(\n]+\n)/mg);
-}
-
-# Find name and availability of C declarations
-my %declarations;
-for my $cdecl (@cdecls) {
- $cdecl =~ s/\n/ /mg; # strip newlines
-
- # Pull availability macro off the end:
- # __OSX_AVAILABLE_*(*)
- # AVAILABLE_MAC_OS_X_VERSION_*
- # OBJC2_UNAVAILABLE
- # OBJC_HASH_AVAILABILITY
- # OBJC_MAP_AVAILABILITY
- # UNAVAILABLE_ATTRIBUTE
- # (DEPRECATED_ATTRIBUTE is not good enough. Be specific.)
- my $avail = undef;
- my $cdecl2;
- ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(__OSX_AVAILABLE_\w+\([a-zA-Z0-9_, ]+\))\s*$/) if (!defined $avail);
- ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(AVAILABLE_MAC_OS_X_VERSION_\w+)\s*$/) if (!defined $avail);
- ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(OBJC2_UNAVAILABLE)\s*$/) if (!defined $avail);
- ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(OBJC_GC_UNAVAILABLE)\s*$/) if (!defined $avail);
- ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(OBJC_ARC_UNAVAILABLE)\s*$/) if (!defined $avail);
- ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(OBJC_HASH_AVAILABILITY)\s*$/) if (!defined $avail);
- ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(OBJC_MAP_AVAILABILITY)\s*$/) if (!defined $avail);
- ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(UNAVAILABLE_ATTRIBUTE)\s*$/) if (!defined $avail);
- # ($cdecl2, $avail) = ($cdecl =~ /^(.*)\s+(DEPRECATED_\w+)\s*$/) if (!defined $avail);
- $cdecl2 = $cdecl if (!defined $cdecl2);
-
- # Extract declaration name (assumes availability macro is already gone):
- # `(*xxx)` (function pointer)
- # `xxx(` (function)
- # `xxx`$` or `xxx[nnn]$` (variable or array variable)
- my $name = undef;
- ($name) = ($cdecl2 =~ /^[^(]*\(\s*\*\s*(\w+)\s*\)/) if (!defined $name);
- ($name) = ($cdecl2 =~ /(\w+)\s*\(/) if (!defined $name);
- ($name) = ($cdecl2 =~ /(\w+)\s*(?:\[\d*\]\s*)*$/) if (!defined $name);
-
- if (!defined $name) {
- print "BAD: unintellible declaration:\n $cdecl\n"; $bad++;
- } elsif (!defined $avail) {
- print "BAD: no availability on declaration of '$name':\n $cdecl\n"; $bad++;
- }
-
- if ($avail eq "UNAVAILABLE_ATTRIBUTE")
- {
- $declarations{$name} = "unavailable";
- } elsif ($avail eq "OBJC2_UNAVAILABLE" && ! $OBJC1) {
- # fixme OBJC2_UNAVAILABLE may or may not have an exported symbol
- # $declarations{$name} = "unavailable";
- } else {
- $declarations{"_$name"} = "available";
- }
-}
-
-# Find name and availability of Objective-C classes
-for my $classdecl (@classdecls) {
- $classdecl =~ s/\n/ /mg; # strip newlines
-
- # Pull availability macro off the front:
- # __OSX_AVAILABLE_*(*)
- # AVAILABLE_MAC_OS_X_VERSION_*
- # OBJC2_UNAVAILABLE
- # OBJC_HASH_AVAILABILITY
- # OBJC_MAP_AVAILABILITY
- # UNAVAILABLE_ATTRIBUTE
- # (DEPRECATED_ATTRIBUTE is not good enough. Be specific.)
- my $avail = undef;
- my $classdecl2;
- ($avail, $classdecl2) = ($classdecl =~ /^\s*(__OSX_AVAILABLE_\w+\([a-zA-Z0-9_, ]+\))\s*(.*)$/) if (!defined $avail);
- ($avail, $classdecl2) = ($classdecl =~ /^\s*(AVAILABLE_MAC_OS_X_VERSION_\w+)\s*(.*)$/) if (!defined $avail);
- ($avail, $classdecl2) = ($classdecl =~ /^\s*(OBJC2_UNAVAILABLE)\s*(.*)$/) if (!defined $avail);
- ($avail, $classdecl2) = ($classdecl =~ /^\s*(OBJC_HASH_AVAILABILITY)\s*(.*)$/) if (!defined $avail);
- ($avail, $classdecl2) = ($classdecl =~ /^\s*(OBJC_MAP_AVAILABILITY)\s*(.*)$/) if (!defined $avail);
- ($avail, $classdecl2) = ($classdecl =~ /^\s*(UNAVAILABLE_ATTRIBUTE)\s*(.*)$/) if (!defined $avail);
- # ($avail, $classdecl2) = ($classdecl =~ /^\s*(DEPRECATED_\w+)\s*(.*)$/) if (!defined $avail);
- $classdecl2 = $classdecl if (!defined $classdecl2);
-
- # Extract class name.
- my $name = undef;
- ($name) = ($classdecl2 =~ /\@interface\s+(\w+)/);
-
- if (!defined $name) {
- print "BAD: unintellible declaration:\n $classdecl\n"; $bad++;
- } elsif (!defined $avail) {
- print "BAD: no availability on declaration of '$name':\n $classdecl\n"; $bad++;
- }
-
- my $availability;
- if ($avail eq "UNAVAILABLE_ATTRIBUTE") {
- $availability = "unavailable";
- } elsif ($avail eq "OBJC2_UNAVAILABLE" && ! $OBJC1) {
- # fixme OBJC2_UNAVAILABLE may or may not have an exported symbol
- # $declarations{$name} = "unavailable";
- $availability = undef;
- } else {
- $availability = "available";
- }
-
- if (! $OBJC1) {
- $declarations{"_OBJC_CLASS_\$_$name"} = $availability;
- $declarations{"_OBJC_METACLASS_\$_$name"} = $availability;
- # fixme ivars
- $declarations{"_OBJC_IVAR_\$_$name.isa"} = $availability if ($name eq "Object");
- } else {
- $declarations{".objc_class_name_$name"} = $availability;
- }
-}
-
-# All exported symbols must have an export declaration
-my @missing_symbols;
-for my $name (keys %symbols) {
- my $avail = $declarations{$name};
- if ($avail eq "unavailable" || !defined $avail) {
- push @missing_symbols, $name;
- }
-}
-for my $symbol (sort @missing_symbols) {
- print "BAD: symbol $symbol has no export declaration\n"; $bad++;
-}
-
-
-# All export declarations must have an exported symbol
-my @missing_decls;
-for my $name (keys %declarations) {
- my $avail = $declarations{$name};
- my $hasSymbol = exists $symbols{$name};
- if ($avail ne "unavailable" && !$hasSymbol) {
- push @missing_decls, $name;
- }
-}
-for my $decl (sort @missing_decls) {
- print "BAD: declaration $decl has no exported symbol\n"; $bad++;
-}
-
-print "OK: verify-exports\n" unless $bad;
-exit ($bad ? 1 : 0);
+++ /dev/null
-/*
- To test -weak-l or -weak-framework:
- * -DWEAK_IMPORT=
- * -DWEAK_FRAMEWORK=1
- * -UEMPTY when building the weak-not-missing library
- * -DEMPTY= when building the weak-missing library
-
- To test attribute((weak_import)):
- * -DWEAK_IMPORT=__attribute__((weak_import))
- * -UWEAK_FRAMEWORK
- * -UEMPTY when building the weak-not-missing library
- * -DEMPTY= when building the weak-missing library
-
-*/
-
-#include "test.h"
-#include <objc/runtime.h>
-
-extern int state;
-
-WEAK_IMPORT OBJC_ROOT_CLASS
-@interface MissingRoot {
- id isa;
-}
-+(void) initialize;
-+(Class) class;
-+(id) alloc;
--(id) init;
--(void) dealloc;
-+(int) method;
-@end
-
-@interface MissingRoot (RR)
--(id) retain;
--(void) release;
-@end
-
-WEAK_IMPORT
-@interface MissingSuper : MissingRoot {
- @public
- int ivar;
-}
-@end
-
-OBJC_ROOT_CLASS
-@interface NotMissingRoot {
- id isa;
-}
-+(void) initialize;
-+(Class) class;
-+(id) alloc;
--(id) init;
--(void) dealloc;
-+(int) method;
-@end
-
-@interface NotMissingRoot (RR)
--(id) retain;
--(void) release;
-@end
-
-@interface NotMissingSuper : NotMissingRoot {
- @public
- int unused[100];
- int ivar;
-}
-@end
+++ /dev/null
-// See instructions in weak.h
-
-#include "test.h"
-#include "weak.h"
-
-// Subclass of superclass that isn't there
-@interface MyMissingSuper : MissingSuper
-+(int) method;
-@end
-@implementation MyMissingSuper
-+(int) method { return 1+[super method]; }
-+(void) load { state++; }
-@end
-
-// Subclass of subclass of superclass that isn't there
-@interface MyMissingSub : MyMissingSuper
-+(int) method;
-@end
-@implementation MyMissingSub
-+(int) method { return 1+[super method]; }
-+(void) load { state++; }
-@end
-
-// Subclass of real superclass
-@interface MyNotMissingSuper : NotMissingSuper
-+(int) method;
-@end
-@implementation MyNotMissingSuper
-+(int) method { return 1+[super method]; }
-+(void) load { state++; }
-@end
-
-// Subclass of subclass of superclass that isn't there
-@interface MyNotMissingSub : MyNotMissingSuper
-+(int) method;
-@end
-@implementation MyNotMissingSub
-+(int) method { return 1+[super method]; }
-+(void) load { state++; }
-@end
-
-// Categories on all of the above
-@interface MissingRoot (MissingRootExtras)
-+(void)load;
-+(int) cat_method;
-@end
-@implementation MissingRoot (MissingRootExtras)
-+(void)load { state++; }
-+(int) cat_method { return 40; }
-@end
-
-@interface MissingSuper (MissingSuperExtras)
-+(void)load;
-+(int) cat_method;
-@end
-@implementation MissingSuper (MissingSuperExtras)
-+(void)load { state++; }
-+(int) cat_method { return 1+[super cat_method]; }
-@end
-
-@interface MyMissingSuper (MyMissingSuperExtras)
-+(void)load;
-+(int) cat_method;
-@end
-@implementation MyMissingSuper (MyMissingSuperExtras)
-+(void)load { state++; }
-+(int) cat_method { return 1+[super cat_method]; }
-@end
-
-@interface MyMissingSub (MyMissingSubExtras)
-+(void)load;
-+(int) cat_method;
-@end
-@implementation MyMissingSub (MyMissingSubExtras)
-+(void)load { state++; }
-+(int) cat_method { return 1+[super cat_method]; }
-@end
-
-
-@interface NotMissingRoot (NotMissingRootExtras)
-+(void)load;
-+(int) cat_method;
-@end
-@implementation NotMissingRoot (NotMissingRootExtras)
-+(void)load { state++; }
-+(int) cat_method { return 30; }
-@end
-
-@interface NotMissingSuper (NotMissingSuperExtras)
-+(void)load;
-+(int) cat_method;
-@end
-@implementation NotMissingSuper (NotMissingSuperExtras)
-+(void)load { state++; }
-+(int) cat_method { return 1+[super cat_method]; }
-@end
-
-@interface MyNotMissingSuper (MyNotMissingSuperExtras)
-+(void)load;
-+(int) cat_method;
-@end
-@implementation MyNotMissingSuper (MyNotMissingSuperExtras)
-+(void)load { state++; }
-+(int) cat_method { return 1+[super cat_method]; }
-@end
-
-@interface MyNotMissingSub (MyNotMissingSubExtras)
-+(void)load;
-+(int) cat_method;
-@end
-@implementation MyNotMissingSub (MyNotMissingSubExtras)
-+(void)load { state++; }
-+(int) cat_method { return 1+[super cat_method]; }
-@end
-
-
-#if WEAK_FRAMEWORK
-# define TESTIVAR(cond) testassert(cond)
-#else
-# define TESTIVAR(cond) /* rdar */
-#endif
-
-static BOOL classInList(__unsafe_unretained Class classes[], const char *name)
-{
- for (int i = 0; classes[i] != nil; i++) {
- if (0 == strcmp(class_getName(classes[i]), name)) return YES;
- }
- return NO;
-}
-
-static BOOL classInNameList(const char **names, const char *name)
-{
- const char **cp;
- for (cp = names; *cp; cp++) {
- if (0 == strcmp(*cp, name)) return YES;
- }
- return NO;
-}
-
-int main(int argc __unused, char **argv)
-{
- BOOL weakMissing;
- if (strstr(argv[0], "-not-missing.out")) {
- weakMissing = NO;
- } else if (strstr(argv[0], "-missing.out")) {
- weakMissing = YES;
- } else {
- fail("executable name must be weak*-missing.out or weak*-not-missing.out");
- }
-
- // class and category +load methods
- if (weakMissing) testassert(state == 8);
- else testassert(state == 16);
- state = 0;
-
- // classes
- testassert([NotMissingRoot class]);
- testassert([NotMissingSuper class]);
- testassert([MyNotMissingSuper class]);
- testassert([MyNotMissingSub class]);
- if (weakMissing) {
- testassert([MissingRoot class] == nil);
- testassert([MissingSuper class] == nil);
- testassert([MyMissingSuper class] == nil);
- testassert([MyMissingSub class] == nil);
- } else {
- testassert([MissingRoot class]);
- testassert([MissingSuper class]);
- testassert([MyMissingSuper class]);
- testassert([MyMissingSub class]);
- }
-
- // objc_getClass
- testassert(objc_getClass("NotMissingRoot"));
- testassert(objc_getClass("NotMissingSuper"));
- testassert(objc_getClass("MyNotMissingSuper"));
- testassert(objc_getClass("MyNotMissingSub"));
- if (weakMissing) {
- testassert(objc_getClass("MissingRoot") == nil);
- testassert(objc_getClass("MissingSuper") == nil);
- testassert(objc_getClass("MyMissingSuper") == nil);
- testassert(objc_getClass("MyMissingSub") == nil);
- } else {
- testassert(objc_getClass("MissingRoot"));
- testassert(objc_getClass("MissingSuper"));
- testassert(objc_getClass("MyMissingSuper"));
- testassert(objc_getClass("MyMissingSub"));
- }
-
- // class list
- union {
- Class *c;
- void *v;
- } classes;
- classes.c = objc_copyClassList(NULL);
- testassert(classInList(classes.c, "NotMissingRoot"));
- testassert(classInList(classes.c, "NotMissingSuper"));
- testassert(classInList(classes.c, "MyNotMissingSuper"));
- testassert(classInList(classes.c, "MyNotMissingSub"));
- if (weakMissing) {
- testassert(! classInList(classes.c, "MissingRoot"));
- testassert(! classInList(classes.c, "MissingSuper"));
- testassert(! classInList(classes.c, "MyMissingSuper"));
- testassert(! classInList(classes.c, "MyMissingSub"));
- } else {
- testassert(classInList(classes.c, "MissingRoot"));
- testassert(classInList(classes.c, "MissingSuper"));
- testassert(classInList(classes.c, "MyMissingSuper"));
- testassert(classInList(classes.c, "MyMissingSub"));
- }
- free(classes.v);
-
- // class name list
- const char *image = class_getImageName(objc_getClass("NotMissingRoot"));
- testassert(image);
- const char **names = objc_copyClassNamesForImage(image, NULL);
- testassert(names);
- testassert(classInNameList(names, "NotMissingRoot"));
- testassert(classInNameList(names, "NotMissingSuper"));
- if (weakMissing) {
- testassert(! classInNameList(names, "MissingRoot"));
- testassert(! classInNameList(names, "MissingSuper"));
- } else {
- testassert(classInNameList(names, "MissingRoot"));
- testassert(classInNameList(names, "MissingSuper"));
- }
- free(names);
-
- image = class_getImageName(objc_getClass("MyNotMissingSub"));
- testassert(image);
- names = objc_copyClassNamesForImage(image, NULL);
- testassert(names);
- testassert(classInNameList(names, "MyNotMissingSuper"));
- testassert(classInNameList(names, "MyNotMissingSub"));
- if (weakMissing) {
- testassert(! classInNameList(names, "MyMissingSuper"));
- testassert(! classInNameList(names, "MyMissingSub"));
- } else {
- testassert(classInNameList(names, "MyMissingSuper"));
- testassert(classInNameList(names, "MyMissingSub"));
- }
- free(names);
-
- // methods
- testassert(20 == [NotMissingRoot method]);
- testassert(21 == [NotMissingSuper method]);
- testassert(22 == [MyNotMissingSuper method]);
- testassert(23 == [MyNotMissingSub method]);
- if (weakMissing) {
- testassert(0 == [MissingRoot method]);
- testassert(0 == [MissingSuper method]);
- testassert(0 == [MyMissingSuper method]);
- testassert(0 == [MyMissingSub method]);
- } else {
- testassert(10 == [MissingRoot method]);
- testassert(11 == [MissingSuper method]);
- testassert(12 == [MyMissingSuper method]);
- testassert(13 == [MyMissingSub method]);
- }
-
- // category methods
- testassert(30 == [NotMissingRoot cat_method]);
- testassert(31 == [NotMissingSuper cat_method]);
- testassert(32 == [MyNotMissingSuper cat_method]);
- testassert(33 == [MyNotMissingSub cat_method]);
- if (weakMissing) {
- testassert(0 == [MissingRoot cat_method]);
- testassert(0 == [MissingSuper cat_method]);
- testassert(0 == [MyMissingSuper cat_method]);
- testassert(0 == [MyMissingSub cat_method]);
- } else {
- testassert(40 == [MissingRoot cat_method]);
- testassert(41 == [MissingSuper cat_method]);
- testassert(42 == [MyMissingSuper cat_method]);
- testassert(43 == [MyMissingSub cat_method]);
- }
-
- // allocations and ivars
- id obj;
- NotMissingSuper *obj2;
- MissingSuper *obj3;
- testassert((obj = [[NotMissingRoot alloc] init]));
- RELEASE_VAR(obj);
- testassert((obj2 = [[NotMissingSuper alloc] init]));
- TESTIVAR(obj2->ivar == 200);
- RELEASE_VAR(obj2);
- testassert((obj2 = [[MyNotMissingSuper alloc] init]));
- TESTIVAR(obj2->ivar == 200);
- RELEASE_VAR(obj2);
- testassert((obj2 = [[MyNotMissingSub alloc] init]));
- TESTIVAR(obj2->ivar == 200);
- RELEASE_VAR(obj2);
- if (weakMissing) {
- testassert([[MissingRoot alloc] init] == nil);
- testassert([[MissingSuper alloc] init] == nil);
- testassert([[MyMissingSuper alloc] init] == nil);
- testassert([[MyMissingSub alloc] init] == nil);
- } else {
- testassert((obj = [[MissingRoot alloc] init]));
- RELEASE_VAR(obj);
- testassert((obj3 = [[MissingSuper alloc] init]));
- TESTIVAR(obj3->ivar == 100);
- RELEASE_VAR(obj3);
- testassert((obj3 = [[MyMissingSuper alloc] init]));
- TESTIVAR(obj3->ivar == 100);
- RELEASE_VAR(obj3);
- testassert((obj3 = [[MyMissingSub alloc] init]));
- TESTIVAR(obj3->ivar == 100);
- RELEASE_VAR(obj3);
- }
-
- *strrchr(argv[0], '.') = 0;
- succeed(basename(argv[0]));
- return 0;
-}
-
+++ /dev/null
-// See instructions in weak.h
-
-#include "test.h"
-#include "weak.h"
-#include <objc/objc-internal.h>
-
-int state = 0;
-
-static void *noop_fn(void *self, SEL _cmd __unused) {
- return self;
-}
-static void *retain_fn(void *self, SEL _cmd __unused) {
- void * (*fn)(void *) = (typeof(fn))_objc_rootRetain;
- return fn(self);
-}
-static void release_fn(void *self, SEL _cmd __unused) {
- void (*fn)(void *) = (typeof(fn))_objc_rootRelease;
- fn(self);
-}
-static void *autorelease_fn(void *self, SEL _cmd __unused) {
- void * (*fn)(void *) = (typeof(fn))_objc_rootAutorelease;
- return fn(self);
-}
-
-#if !defined(EMPTY)
-
-@implementation MissingRoot
-+(void) initialize { }
-+(Class) class { return self; }
-+(id) alloc { return _objc_rootAlloc(self); }
-+(id) allocWithZone:(void*)zone { return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone); }
--(id) init { return self; }
--(void) dealloc { _objc_rootDealloc(self); }
-+(int) method { return 10; }
-+(void) load {
- class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
- class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
- class_addMethod(self, sel_registerName("autorelease"), (IMP)autorelease_fn, "");
-
- class_addMethod(object_getClass(self), sel_registerName("retain"), (IMP)noop_fn, "");
- class_addMethod(object_getClass(self), sel_registerName("release"), (IMP)noop_fn, "");
- class_addMethod(object_getClass(self), sel_registerName("autorelease"), (IMP)noop_fn, "");
-
- state++;
-}
-@end
-
-@implementation MissingSuper
-+(int) method { return 1+[super method]; }
--(id) init { self = [super init]; ivar = 100; return self; }
-+(void) load { state++; }
-@end
-
-#endif
-
-@implementation NotMissingRoot
-+(void) initialize { }
-+(Class) class { return self; }
-+(id) alloc { return _objc_rootAlloc(self); }
-+(id) allocWithZone:(void*)zone { return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone); }
--(id) init { return self; }
--(void) dealloc { _objc_rootDealloc(self); }
-+(int) method { return 20; }
-+(void) load {
- class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
- class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
- class_addMethod(self, sel_registerName("autorelease"), (IMP)autorelease_fn, "");
-
- class_addMethod(object_getClass(self), sel_registerName("retain"), (IMP)noop_fn, "");
- class_addMethod(object_getClass(self), sel_registerName("release"), (IMP)noop_fn, "");
- class_addMethod(object_getClass(self), sel_registerName("autorelease"), (IMP)noop_fn, "");
-
- state++;
-}
-@end
-
-@implementation NotMissingSuper
-+(int) method { return 1+[super method]; }
--(id) init { self = [super init]; ivar = 200; return self; }
-+(void) load { state++; }
-@end
-
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-
-#if __OBJC_GC__ && __cplusplus && __i386__
-
-int main()
-{
- testwarn("rdar://19042235 test disabled for 32-bit objc++ GC because of unknown bit rot");
- succeed(__FILE__);
-}
-
-#else
-
-#include "testroot.i"
-#include <stdint.h>
-#include <string.h>
-#include <objc/objc-runtime.h>
-
-@interface Weak : TestRoot {
- @public
- __weak id value;
-}
-@end
-@implementation Weak
-@end
-
-Weak *oldObject;
-Weak *newObject;
-
-void *fn(void *arg __unused)
-{
- objc_registerThreadWithCollector();
-
- return NULL;
-}
-
-int main()
-{
- testonthread(^{
- TestRoot *value;
-
- PUSH_POOL {
- value = [TestRoot new];
- testassert(value);
- oldObject = [Weak new];
- testassert(oldObject);
-
- oldObject->value = value;
- testassert(oldObject->value == value);
-
- newObject = [oldObject copy];
- testassert(newObject);
- testassert(newObject->value == oldObject->value);
-
- newObject->value = nil;
- testassert(newObject->value == nil);
- testassert(oldObject->value == value);
- } POP_POOL;
-
- testcollect();
- TestRootDealloc = 0;
- TestRootFinalize = 0;
- RELEASE_VAR(value);
- });
-
- testcollect();
- testassert(TestRootDealloc || TestRootFinalize);
-
-#if defined(__OBJC_GC__) || __has_feature(objc_arc)
- testassert(oldObject->value == nil);
-#else
- testassert(oldObject->value != nil);
-#endif
- testassert(newObject->value == nil);
-
- RELEASE_VAR(newObject);
- RELEASE_VAR(oldObject);
-
- succeed(__FILE__);
- return 0;
-}
-
-#endif
+++ /dev/null
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/weak2.m -DWEAK_FRAMEWORK=1 -DWEAK_IMPORT= -UEMPTY -dynamiclib -o libweakframework.dylib
-
- $C{COMPILE} $DIR/weakframework-missing.m -L. -weak-lweakframework -o weakframework-missing.out
-
- $C{COMPILE} $DIR/weak2.m -DWEAK_FRAMEWORK=1 -DWEAK_IMPORT= -DEMPTY= -dynamiclib -o libweakframework.dylib
-
-END
-*/
-
-#define WEAK_FRAMEWORK 1
-#define WEAK_IMPORT
-#include "weak.m"
+++ /dev/null
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/weak2.m -DWEAK_FRAMEWORK=1 -DWEAK_IMPORT= -UEMPTY -dynamiclib -o libweakframework.dylib
-
- $C{COMPILE} $DIR/weakframework-not-missing.m -L. -weak-lweakframework -o weakframework-not-missing.out
-END
-*/
-
-#define WEAK_FRAMEWORK 1
-#define WEAK_IMPORT
-#include "weak.m"
+++ /dev/null
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/weak2.m -UWEAK_FRAMEWORK -DWEAK_IMPORT=__attribute__\\(\\(weak_import\\)\\) -UEMPTY -dynamiclib -o libweakimport.dylib
-
- $C{COMPILE} $DIR/weakimport-missing.m -L. -weak-lweakimport -o weakimport-missing.out
-
- $C{COMPILE} $DIR/weak2.m -UWEAK_FRAMEWORK -DWEAK_IMPORT=__attribute__\\(\\(weak_import\\)\\) -DEMPTY= -dynamiclib -o libweakimport.dylib
-END
-*/
-
-// #define WEAK_FRAMEWORK
-#define WEAK_IMPORT __attribute__((weak_import))
-#include "weak.m"
+++ /dev/null
-/*
-TEST_BUILD
- $C{COMPILE} $DIR/weak2.m -UWEAK_FRAMEWORK -DWEAK_IMPORT=__attribute__\\(\\(weak_import\\)\\) -UEMPTY -dynamiclib -o libweakimport.dylib
-
- $C{COMPILE} $DIR/weakimport-not-missing.m -L. -weak-lweakimport -o weakimport-not-missing.out
-END
-*/
-
-// #define WEAK_FRAMEWORK
-#define WEAK_IMPORT __attribute__((weak_import))
-#include "weak.m"
+++ /dev/null
-// TEST_CONFIG MEM=mrc
-
-#include "test.h"
-#include <objc/NSObject.h>
-
-static semaphore_t go1;
-static semaphore_t go2;
-static semaphore_t done;
-
-#define VARCOUNT 100000
-static id obj;
-static id vars[VARCOUNT];
-
-
-void *destroyer(void *arg __unused)
-{
- while (1) {
- semaphore_wait(go1);
- for (int i = 0; i < VARCOUNT; i++) {
- objc_destroyWeak(&vars[i]);
- }
- semaphore_signal(done);
- }
-}
-
-
-void *deallocator(void *arg __unused)
-{
- while (1) {
- semaphore_wait(go2);
- [obj release];
- semaphore_signal(done);
- }
-}
-
-
-void cycle(void)
-{
- // rdar://12896779 objc_destroyWeak() versus weak clear in dealloc
-
- // Clean up from previous cycle - objc_destroyWeak() doesn't set var to nil
- for (int i = 0; i < VARCOUNT; i++) {
- vars[i] = nil;
- }
-
- obj = [NSObject new];
- for (int i = 0; i < VARCOUNT; i++) {
- objc_storeWeak(&vars[i], obj);
- }
-
- // let destroyer start before deallocator runs
- semaphore_signal(go1);
- sched_yield();
- semaphore_signal(go2);
-
- semaphore_wait(done);
- semaphore_wait(done);
-}
-
-
-int main()
-{
- semaphore_create(mach_task_self(), &go1, 0, 0);
- semaphore_create(mach_task_self(), &go2, 0, 0);
- semaphore_create(mach_task_self(), &done, 0, 0);
-
- pthread_t th[2];
- pthread_create(&th[1], NULL, deallocator, NULL);
- pthread_create(&th[1], NULL, destroyer, NULL);
-
- for (int i = 0; i < 100; i++) {
- cycle();
- }
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CFLAGS
-
-#include <Foundation/NSObject.h>
-#include <objc/runtime.h>
-#include <objc/objc-internal.h>
-
-#include "test.h"
-
-int main()
-{
- // rdar://8350188 External references (handles)
-
- id object = [NSObject new];
- testassert(object);
-
- // STRONG
- objc_xref_t xref = _object_addExternalReference(object, OBJC_XREF_STRONG);
- testassert(xref);
- testassert(_object_readExternalReference(xref) == object);
- _object_removeExternalReference(xref);
- // TODO: expect a crash if a stale xref is used.
-
- // WEAK
- xref = _object_addExternalReference(object, OBJC_XREF_WEAK);
- testassert(xref);
- testassert(_object_readExternalReference(xref) == object);
- _object_removeExternalReference(xref);
-
- RELEASE_VAR(object);
-
- succeed(__FILE__);
-}
+++ /dev/null
-// TEST_CONFIG
-
-#include "test.h"
-#include <mach/mach.h>
-#include <malloc/malloc.h>
-
-// Look for malloc zone "ObjC" iff OBJC_USE_INTERNAL_ZONE is set.
-// This fails if objc tries to allocate before checking its own
-// environment variables (rdar://6688423)
-
-int main()
-{
- if (is_guardmalloc()) {
- // guard malloc confuses this test
- succeed(__FILE__);
- }
-
- kern_return_t kr;
- vm_address_t *zones;
- unsigned int count, i;
- BOOL has_objc = NO, want_objc = NO;
-
- want_objc = (getenv("OBJC_USE_INTERNAL_ZONE") != NULL) ? YES : NO;
- testprintf("want objc %s\n", want_objc ? "YES" : "NO");
-
- kr = malloc_get_all_zones(mach_task_self(), NULL, &zones, &count);
- testassert(!kr);
- for (i = 0; i < count; i++) {
- const char *name = malloc_get_zone_name((malloc_zone_t *)zones[i]);
- if (name) {
- BOOL is_objc = (0 == strcmp(name, "ObjC_Internal")) ? YES : NO;
- if (is_objc) has_objc = YES;
- testprintf("zone %s\n", name);
- }
- }
-
- testassert(want_objc == has_objc);
-
- succeed(__FILE__);
-}