From 3428d21d55eee9b31b10ed4e2c999844466eb92d Mon Sep 17 00:00:00 2001 From: Konstantin Osipov <kostja@tarantool.org> Date: Thu, 14 Mar 2013 21:46:16 +0400 Subject: [PATCH] Import the new libobjc runtime from the trunk. Preserve the zero-warnings patch. --- third_party/README | 4 +- third_party/libobjc/ANNOUNCE | 46 ++- third_party/libobjc/ANNOUNCE.1.6.1 | 41 ++ third_party/libobjc/Makefile | 26 +- third_party/libobjc/Test/BlockImpTest.m | 54 --- third_party/libobjc/Test/GNUmakefile | 8 - .../libobjc/Test/PropertyIntrospectionTest.m | 36 -- third_party/libobjc/Test/ProtocolCreation.m | 28 -- third_party/libobjc/Test/RuntimeTest.m | 298 -------------- .../RuntimeTest.xcodeproj/project.pbxproj | 203 ---------- third_party/libobjc/Test/objc_msgSend.m | 143 ------- third_party/libobjc/arc.m | 19 +- third_party/libobjc/class_table.c | 5 +- third_party/libobjc/dtable.c | 52 ++- third_party/libobjc/dwarf_eh.h | 4 +- third_party/libobjc/eh_personality.c | 367 ++++++++++++++++-- third_party/libobjc/encoding2.c | 26 +- third_party/libobjc/hash_table.h | 1 + third_party/libobjc/loader.c | 11 + third_party/libobjc/lock.h | 2 +- third_party/libobjc/objc/Availability.h | 4 + third_party/libobjc/objc/Object.h | 4 + third_party/libobjc/objc/Protocol.h | 4 + third_party/libobjc/objc/blocks_private.h | 4 + third_party/libobjc/objc/blocks_runtime.h | 4 + third_party/libobjc/objc/capabilities.h | 4 + third_party/libobjc/objc/developer.h | 4 + third_party/libobjc/objc/encoding.h | 4 + third_party/libobjc/objc/hooks.h | 4 + third_party/libobjc/objc/message.h | 62 +++ third_party/libobjc/objc/objc-api.h | 4 + third_party/libobjc/objc/objc-arc.h | 4 + third_party/libobjc/objc/objc-auto.h | 4 + third_party/libobjc/objc/runtime-deprecated.h | 4 + third_party/libobjc/objc/runtime.h | 6 + third_party/libobjc/objc/slot.h | 4 + third_party/libobjc/objc_msgSend.x86-32.S | 11 +- third_party/libobjc/objc_msgSend.x86-64.S | 2 +- third_party/libobjc/objcxx_eh.cc | 8 +- third_party/libobjc/objcxx_eh.h | 6 +- third_party/libobjc/opts/CMakeLists.txt | 37 +- third_party/libobjc/opts/ClassIMPCache.cpp | 8 - third_party/libobjc/opts/ClassLookupCache.cpp | 12 +- .../libobjc/opts/ClassMethodInliner.cpp | 9 - third_party/libobjc/opts/IMPCacher.cpp | 8 - third_party/libobjc/opts/IvarPass.cpp | 10 +- third_party/libobjc/opts/LLVMCompat.h | 30 +- third_party/libobjc/opts/LoopIMPCachePass.cpp | 9 +- third_party/libobjc/opts/ObjectiveCOpts.cpp | 7 +- third_party/libobjc/opts/TypeFeedback.cpp | 17 +- .../opts/TypeFeedbackDrivenInliner.cpp | 7 +- third_party/libobjc/opts/TypeInfoProvider.h | 9 +- third_party/libobjc/pool.h | 4 + third_party/libobjc/properties.h | 69 +++- third_party/libobjc/properties.m | 317 ++++++++++++--- third_party/libobjc/protocol.c | 45 +-- third_party/libobjc/runtime.c | 9 +- third_party/libobjc/sarray2.c | 98 ++++- third_party/libobjc/sarray2.h | 7 +- third_party/libobjc/selector_table.c | 60 ++- third_party/libobjc/spinlock.h | 2 +- third_party/libobjc/unwind-arm.h | 2 + 62 files changed, 1217 insertions(+), 1084 deletions(-) create mode 100644 third_party/libobjc/ANNOUNCE.1.6.1 delete mode 100644 third_party/libobjc/Test/BlockImpTest.m delete mode 100644 third_party/libobjc/Test/GNUmakefile delete mode 100644 third_party/libobjc/Test/PropertyIntrospectionTest.m delete mode 100644 third_party/libobjc/Test/ProtocolCreation.m delete mode 100644 third_party/libobjc/Test/RuntimeTest.m delete mode 100644 third_party/libobjc/Test/RuntimeTest.xcodeproj/project.pbxproj delete mode 100644 third_party/libobjc/Test/objc_msgSend.m create mode 100644 third_party/libobjc/objc/message.h diff --git a/third_party/README b/third_party/README index 67c19db348..33ce150b1e 100644 --- a/third_party/README +++ b/third_party/README @@ -30,6 +30,8 @@ rm GNUMakefile How to update it: -- delete GNUMakefile +- delete GNUmakefile +- delete CMakeLists.txt - merge our Makefile with the Makefile in the source tarball +- preserve the zero-warnings patch 43771c84f7f5bf04e426dde30a31303d4699f00d diff --git a/third_party/libobjc/ANNOUNCE b/third_party/libobjc/ANNOUNCE index 75950a35df..c7b31ab7b2 100644 --- a/third_party/libobjc/ANNOUNCE +++ b/third_party/libobjc/ANNOUNCE @@ -1,27 +1,47 @@ -GNUstep Objective-C Runtime 1.6.1 +GNUstep Objective-C Runtime 1.7 =============================== -This is a point release to the seventh official release of the GNUstep +This is a point release to the eighth official release of the GNUstep Objective-C runtime (a.k.a. libobjc2). This runtime was designed to support -the features of Objective-C 2 for use with GNUstep and other Objective-C -programs. Highlights of this release include: +the features of modern dialects of Objective-C for use with GNUstep and other +Objective-C programs. Highlights of this release include: -- Improved support for ARC autorelease pools. +- A new CMake-based build system. This makes all of the configurable options + available via a clean interface. CPack is supported for building RPM and DEB + packages out of the box. -- Some small bug fixes in blocks support. +- A new CTest-based test suite, replacing the old ad-hoc tests. -- Improvements to the Objective-C++ unified exception model support. +- Build a single libobjc with support for Objective-C++ on platforms where a + C++ ABI library (libcxxrt or libsupc++) is installed as a shared library. + +- Added specialised property accessor functions and support for atomic + properties with C++ non-POD types. + +- Significant improvements in property introspection and an exhaustive test + suite. + +- A new exception implementation providing better integration with foreign + exceptions (e.g. C++ exceptions). The new ABI is supported by clang 3.3 when + compiling with -fobjc-runtime=gnustep-1.7 (or higher). The old ABI is still + supported and both can be used within the same program, however code compiled + with the old ABI remains unreliable in the presence of foreign exceptions. + It is strongly recommended that anyone using exceptions with Objective-C++ + switches to the new version. + +- MIPS64 support in the assembly routines. (TODO) + +- Updated optimisation passes to work with LLVM 3.2 and recent LLVM trunk. -- Updated optimisation passes to work with LLVM 3.1 You may obtain the code for this release from subversion at the following subversion branch: -svn://svn.gna.org/svn/gnustep/libs/libobjc2/1.6.1 +svn://svn.gna.org/svn/gnustep/libs/libobjc2/1.7 Alternatively, a tarball is available from: -http://download.gna.org/gnustep/libobjc2-1.6.1.tar.bz2 +http://download.gna.org/gnustep/libobjc2-1.7.tar.bz2 The runtime library is responsible for implementing the core features of the object model, as well as exposing introspection features to the user. The @@ -35,7 +55,5 @@ with the FSF's GCC 4.2.1 Objective-C ABI and also implements a new ABI that is supported by Clang and Étoilé's LanguageKit and is required for some of the newer features. -Although the runtime has been tested by several people, and is being used -extensively by the Étoilé project, it is relatively new code and may still -contain bugs. If you come across any problems, please report them to the -GNUstep Developer mailing list <gnustep-dev@gnu.org>. +If you come across any problems, please report them to the GNUstep Developer +mailing list <gnustep-dev@gnu.org>. diff --git a/third_party/libobjc/ANNOUNCE.1.6.1 b/third_party/libobjc/ANNOUNCE.1.6.1 new file mode 100644 index 0000000000..03f6ba0ca2 --- /dev/null +++ b/third_party/libobjc/ANNOUNCE.1.6.1 @@ -0,0 +1,41 @@ +GNUstep Objective-C Runtime 1.6.1 +================================= + +This is a point release to the seventh official release of the GNUstep +Objective-C runtime (a.k.a. libobjc2). This runtime was designed to support +the features of Objective-C 2 for use with GNUstep and other Objective-C +programs. Highlights of this release include: + +- Improved support for ARC autorelease pools. + +- Some small bug fixes in blocks support. + +- Improvements to the Objective-C++ unified exception model support. + +- Updated optimisation passes to work with LLVM 3.1 + +You may obtain the code for this release from subversion at the following +subversion branch: + +svn://svn.gna.org/svn/gnustep/libs/libobjc2/1.6.1 + +Alternatively, a tarball is available from: + +http://download.gna.org/gnustep/libobjc2-1.6.1.tar.bz2 + +The runtime library is responsible for implementing the core features of the +object model, as well as exposing introspection features to the user. The +GNUstep runtime implements Apple's Objective-C Runtime APIs, and a small number +of GCC APIs for legacy compatibility. + +This library is based on the Étoilé Objective-C Runtime, an earlier research +prototype, and includes support for non-fragile instance variables, +type-dependent dispatch, and object planes. It is fully backwards compatible +with the FSF's GCC 4.2.1 Objective-C ABI and also implements a new ABI that is +supported by Clang and Étoilé's LanguageKit and is required for some of the +newer features. + +Although the runtime has been tested by several people, and is being used +extensively by the Étoilé project, it is relatively new code and may still +contain bugs. If you come across any problems, please report them to the +GNUstep Developer mailing list <gnustep-dev@gnu.org>. diff --git a/third_party/libobjc/Makefile b/third_party/libobjc/Makefile index dbe0adbdc3..45bc5634d7 100644 --- a/third_party/libobjc/Makefile +++ b/third_party/libobjc/Makefile @@ -5,27 +5,33 @@ MAJOR_VERSION = 4 MINOR_VERSION = 6 SUBMINOR_VERSION = 0 -VERSION = $(MAJOR_VERSION).$(MINOR_VERSION).$(SUBMINOR_VERSION) +VERSION ?= $(MAJOR_VERSION).$(MINOR_VERSION).$(SUBMINOR_VERSION) LIBOBJCLIBNAME=objc LIBOBJC=libobjc LIBOBJCXX=libobjcxx -#SILENT=@ +INSTALL ?= install +#SILENT ?= @ -CFLAGS+= -std=gnu99 -fexceptions -CXXFLAGS+= -fexceptions -CPPFLAGS+= -DTYPE_DEPENDENT_DISPATCH -DGNUSTEP -CPPFLAGS+= -D__OBJC_RUNTIME_INTERNAL__=1 -D_XOPEN_SOURCE=500 -D__BSD_VISIBLE=1 -D_BSD_SOURCE=1 +CFLAGS += -std=gnu99 -fexceptions +#CFLAGS += -Wno-deprecated-objc-isa-usage +CXXFLAGS += -fexceptions +CPPFLAGS += -DTYPE_DEPENDENT_DISPATCH -DGNUSTEP +CPPFLAGS += -D__OBJC_RUNTIME_INTERNAL__=1 -D_XOPEN_SOURCE=500 -D__BSD_VISIBLE=1 -D_BSD_SOURCE=1 ASMFLAGS += `if $(CC) -v 2>&1| grep -q 'clang' ; then echo -no-integrated-as ; fi` +THE_LD=`if [ "$(LD)" = "" ]; then echo "ld"; else echo "$(LD)"; fi` + STRIP=`if [ "$(strip)" = "yes" ] ; then echo -s ; fi` # Suppress warnings about incorrect selectors CPPFLAGS += -DNO_SELECTOR_MISMATCH_WARNINGS # Some helpful flags for debugging. CPPFLAGS+= ${EXTRA_CFLAGS} +##CPPFLAGS += -g -O0 -fno-inline +#CPPFLAGS += -O3 PREFIX?= /usr/local LIB_DIR= ${PREFIX}/lib @@ -72,18 +78,18 @@ all: $(LIBOBJC).a $(LIBOBJCXX).so.$(VERSION): $(LIBOBJC).so.$(VERSION) $(OBJCXX_OBJECTS) $(SILENT)echo Linking shared Objective-C++ runtime library... $(SILENT)$(CXX) -shared \ - -Wl,-soname=$(LIBOBJCXX).so.$(MAJOR_VERSION) \ + -Wl,-soname=$(LIBOBJCXX).so.$(MAJOR_VERSION) $(LDFLAGS) \ -o $@ $(OBJCXX_OBJECTS) $(LIBOBJC).so.$(VERSION): $(OBJECTS) $(SILENT)echo Linking shared Objective-C runtime library... $(SILENT)$(CC) -shared -rdynamic \ - -Wl,-soname=$(LIBOBJC).so.$(MAJOR_VERSION) \ + -Wl,-soname=$(LIBOBJC).so.$(MAJOR_VERSION) $(LDFLAGS) \ -o $@ $(OBJECTS) $(LIBOBJC).a: $(OBJECTS) $(SILENT)echo Linking static Objective-C runtime library... - $(SILENT)ld -r ${EXTRA_LDFLAGS} -o $@ $(OBJECTS) + $(SILENT)$(THE_LD) -r -s -o $@ $(OBJECTS) .cc.o: Makefile $(SILENT)echo Compiling `basename $<`... @@ -101,7 +107,7 @@ $(LIBOBJC).a: $(OBJECTS) $(SILENT)echo Assembling `basename $<`... $(SILENT)$(CC) $(CPPFLAGS) $(ASMFLAGS) -c $< -o $@ -install: all +$(INSTALL): all $(SILENT)echo Installing libraries... $(SILENT)install -d $(LIB_DIR) $(SILENT)install -m 444 $(STRIP) $(LIBOBJC).so.$(VERSION) $(LIB_DIR) diff --git a/third_party/libobjc/Test/BlockImpTest.m b/third_party/libobjc/Test/BlockImpTest.m deleted file mode 100644 index f43a040f38..0000000000 --- a/third_party/libobjc/Test/BlockImpTest.m +++ /dev/null @@ -1,54 +0,0 @@ -#include <objc/runtime.h> -#include <objc/blocks_runtime.h> -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> - -struct big -{ - int a, b, c, d, e; -}; - -@interface Foo @end -@implementation Foo @end -@interface Foo (Dynamic) -+(int)count: (int)i; -+(struct big)sret; -@end - -int main(void) -{ - __block int b = 0; - void* blk = ^(id self, int a) { - b += a; - return b; }; - blk = Block_copy(blk); - IMP imp = imp_implementationWithBlock(blk); - char *type = block_copyIMPTypeEncoding_np(blk); - assert(NULL != type); - class_addMethod((objc_getMetaClass("Foo")), @selector(count:), imp, type); - free(type); - assert(2 == [Foo count: 2]); - assert(4 == [Foo count: 2]); - assert(6 == [Foo count: 2]); - assert(imp_getBlock(imp) == (blk)); - imp_removeBlock(blk); - - blk = ^(id self) { - struct big b = {1, 2, 3, 4, 5}; - return b; - }; - imp = imp_implementationWithBlock(blk); - assert(imp && "Can't make sret IMP"); - type = block_copyIMPTypeEncoding_np(blk); - assert(NULL != type); - class_addMethod((objc_getMetaClass("Foo")), @selector(sret), imp, type); - free(type); - struct big s = [Foo sret]; - assert(s.a == 1); - assert(s.b == 2); - assert(s.c == 3); - assert(s.d == 4); - assert(s.e == 5); - return 0; -} diff --git a/third_party/libobjc/Test/GNUmakefile b/third_party/libobjc/Test/GNUmakefile deleted file mode 100644 index 94e24fdc15..0000000000 --- a/third_party/libobjc/Test/GNUmakefile +++ /dev/null @@ -1,8 +0,0 @@ -include $(GNUSTEP_MAKEFILES)/common.make - -TOOL_NAME = RuntimeTest - -RuntimeTest_OBJC_FILES=\ - RuntimeTest.m - -include $(GNUSTEP_MAKEFILES)/tool.make diff --git a/third_party/libobjc/Test/PropertyIntrospectionTest.m b/third_party/libobjc/Test/PropertyIntrospectionTest.m deleted file mode 100644 index bcf82b7446..0000000000 --- a/third_party/libobjc/Test/PropertyIntrospectionTest.m +++ /dev/null @@ -1,36 +0,0 @@ -#import <objc/runtime.h> -#include <stdio.h> -#include <string.h> -#include <assert.h> - -@interface Foo -@property (getter=bar, setter=setBar:, nonatomic, copy) id foo; -@end -@interface Foo(Bar) --(id)bar; --(void)setBar:(id)b; -@end -@implementation Foo -@synthesize foo; -@end - -int main(void) -{ - objc_property_t p = class_getProperty(objc_getClass("Foo"), "foo"); - unsigned int count; - objc_property_attribute_t *l = property_copyAttributeList(p, &count); - for (unsigned int i=0 ; i<count ; i++) - { - switch (l[i].name[0]) - { - case 'T': assert(0==strcmp(l[i].value, "@")); break; - case 'C': assert(0==strcmp(l[i].value, "")); break; - case 'N': assert(0==strcmp(l[i].value, "")); break; - case 'G': assert(0==strcmp(l[i].value, "bar")); break; - case 'S': assert(0==strcmp(l[i].value, "setBar:")); break; - case 'B': assert(0==strcmp(l[i].value, "foo")); break; - } - } - assert(0 == property_copyAttributeList(0, &count)); - return 0; -} diff --git a/third_party/libobjc/Test/ProtocolCreation.m b/third_party/libobjc/Test/ProtocolCreation.m deleted file mode 100644 index 204021d88f..0000000000 --- a/third_party/libobjc/Test/ProtocolCreation.m +++ /dev/null @@ -1,28 +0,0 @@ -#import <objc/runtime.h> -#include <stdio.h> -#include <string.h> -#include <assert.h> - -@protocol Test2 @end - -int main(void) -{ - Protocol *p = objc_allocateProtocol("Test"); - protocol_addMethodDescription(p, @selector(someMethod), "@:", YES, NO); - assert(objc_getProtocol("Test2")); - protocol_addProtocol(p, objc_getProtocol("Test2")); - objc_property_attribute_t attrs[] = { {"T", "@" } }; - protocol_addProperty(p, "foo", attrs, 1, YES, YES); - objc_registerProtocol(p); - Protocol *p1 = objc_getProtocol("Test"); - assert(p == p1); - struct objc_method_description d = protocol_getMethodDescription(p1, @selector(someMethod), YES, NO); - assert(strcmp(sel_getName(d.name), "someMethod") == 0); - assert(strcmp((d.types), "@:") == 0); - assert(protocol_conformsToProtocol(p1, objc_getProtocol("Test2"))); - unsigned int count; - objc_property_t *props = protocol_copyPropertyList(p1, &count); - assert(count == 1); - assert(strcmp("T@,Vfoo", property_getAttributes(*props)) == 0); - return 0; -} diff --git a/third_party/libobjc/Test/RuntimeTest.m b/third_party/libobjc/Test/RuntimeTest.m deleted file mode 100644 index 83eeb05b3b..0000000000 --- a/third_party/libobjc/Test/RuntimeTest.m +++ /dev/null @@ -1,298 +0,0 @@ -#import <Foundation/Foundation.h> -#include <objc/runtime.h> - -static int exitStatus = 0; - -static void _test(BOOL X, char *expr, int line) -{ - if (!X) - { - exitStatus = 1; - fprintf(stderr, "ERROR: Test failed: '%s' on %s:%d\n", expr, __FILE__, line); - } -} -#define test(X) _test(X, #X, __LINE__) - -static int stringsEqual(const char *a, const char *b) -{ - return 0 == strcmp(a,b); -} - - - -@interface Foo : NSObject -{ - id a; -} -- (void) aMethod; -+ (void) aMethod; -- (int) manyTypes; -- (void) synchronizedCode; -+ (void) synchronizedCode; -+ (id) shared; -- (BOOL) basicThrowAndCatchException; -@end - -@interface Bar : Foo -{ - id b; -} -- (void) anotherMethod; -+ (void) anotherMethod; -- (id) manyTypes; -- (id) aBool: (BOOL)d andAnInt: (int) w; -@end - - -@implementation Foo -- (void) aMethod -{ -} -+ (void) aMethod -{ -} -- (int) manyTypes -{ - return YES; -} -- (void) synchronizedCode -{ - @synchronized(self) { [[self class] synchronizedCode]; } -} -+ (void) synchronizedCode -{ - @synchronized(self) { } -} -+ (id) shared -{ - @synchronized(self) { } - return nil; -} -- (void) throwException -{ - @throw [NSException exceptionWithName: @"RuntimeTestException" reason: @"" userInfo: nil]; -} -- (BOOL) basicThrowAndCatchException -{ - @try - { - [self throwException]; - } - @catch (NSException *e) - { - NSLog(@"Caught %@", e); - } - @finally - { - return YES; - } - return NO; -} -@end - -@implementation Bar -- (void) anotherMethod -{ -} -+ (void) anotherMethod -{ -} -- (id) manyTypes -{ - return @"Hello"; -} -- (id) aBool: (BOOL)d andAnInt: (int) w -{ - return @"Hello"; -} -@end - - -void testInvalidArguments() -{ - test(NO == class_conformsToProtocol([NSObject class], NULL)); - test(NO == class_conformsToProtocol(Nil, NULL)); - test(NO == class_conformsToProtocol(Nil, @protocol(NSCoding))); - test(NULL == class_copyIvarList(Nil, NULL)); - test(NULL == class_copyMethodList(Nil, NULL)); - test(NULL == class_copyPropertyList(Nil, NULL)); - test(NULL == class_copyProtocolList(Nil, NULL)); - test(nil == class_createInstance(Nil, 0)); - test(0 == class_getVersion(Nil)); - test(NO == class_isMetaClass(Nil)); - test(Nil == class_getSuperclass(Nil)); - - test(NULL == method_getName(NULL)); - test(NULL == method_copyArgumentType(NULL, 0)); - test(NULL == method_copyReturnType(NULL)); - method_exchangeImplementations(NULL, NULL); - test((IMP)NULL == method_setImplementation(NULL, (IMP)NULL)); - test((IMP)NULL == method_getImplementation(NULL)); - method_getArgumentType(NULL, 0, NULL, 0); - test(0 == method_getNumberOfArguments(NULL)); - test(NULL == method_getTypeEncoding(NULL)); - method_getReturnType(NULL, NULL, 0); - - test(NULL == ivar_getName(NULL)); - test(0 == ivar_getOffset(NULL)); - test(NULL == ivar_getTypeEncoding(NULL)); - - test(nil == objc_getProtocol(NULL)); - - test(stringsEqual("<null selector>", sel_getName((SEL)0))); - test((SEL)0 == sel_getUid(NULL)); - test(0 != sel_getUid("")); // the empty string is permitted as a selector - test(stringsEqual("", sel_getName(sel_getUid("")))); - test(YES == sel_isEqual((SEL)0, (SEL)0)); - - //test(NULL == property_getName(NULL)); - - printf("testInvalidArguments() ran\n"); -} - -void testAMethod(Method m) -{ - test(NULL != m); - test(stringsEqual("aMethod", sel_getName(method_getName(m)))); - - printf("testAMethod() ran\n"); -} - -void testGetMethod() -{ - testAMethod(class_getClassMethod([Bar class], @selector(aMethod))); - testAMethod(class_getClassMethod([Bar class], sel_getUid("aMethod"))); - - printf("testGetMethod() ran\n"); -} - -void testProtocols() -{ - test(protocol_isEqual(@protocol(NSCoding), objc_getProtocol("NSCoding"))); - - printf("testProtocols() ran\n"); -} - -void testMultiTypedSelector() -{ - test(sel_isEqual(@selector(manyTypes),sel_getUid("manyTypes"))); - test(@selector(manyTypes) == sel_getUid("manyTypes")); - - Method intMethod = class_getInstanceMethod([Foo class], @selector(manyTypes)); - Method idMethod = class_getInstanceMethod([Bar class], @selector(manyTypes)); - - test(method_getName(intMethod) == @selector(manyTypes)); - test(method_getName(idMethod) == @selector(manyTypes)); - - test(sel_isEqual(method_getName(intMethod), @selector(manyTypes))); - test(sel_isEqual(method_getName(idMethod), @selector(manyTypes))); - - char ret[10]; - method_getReturnType(intMethod, ret, 10); - test(stringsEqual(ret, "i")); - method_getReturnType(idMethod, ret, 10); - test(stringsEqual(ret, "@")); - - printf("testMultiTypedSelector() ran\n"); -} - -void testClassHierarchy() -{ - Class nsProxy = objc_getClass("NSProxy"); - Class nsObject = objc_getClass("NSObject"); - Class nsProxyMeta = object_getClass(nsProxy); - Class nsObjectMeta = object_getClass(nsObject); - - test(object_getClass(nsProxyMeta) == nsProxyMeta); - test(object_getClass(nsObjectMeta) == nsObjectMeta); - - test(Nil == class_getSuperclass(nsProxy)); - test(Nil == class_getSuperclass(nsObject)); - - test(nsObject == class_getSuperclass(nsObjectMeta)); - test(nsProxy == class_getSuperclass(nsProxyMeta)); - printf("testClassHierarchy() ran\n"); -} - -void testAllocateClass() -{ - Class newClass = objc_allocateClassPair(objc_lookUpClass("NSObject"), "UserAllocated", 0); - test(Nil != newClass); - // class_getSuperclass() will call objc_resolve_class(). - // Although we have not called objc_registerClassPair() yet, this works with - // the Apple runtime and GNUstep Base relies on this behavior in - // GSObjCMakeClass(). - test(objc_lookUpClass("NSObject") == class_getSuperclass(newClass)); - printf("testAllocateClass() ran\n"); -} - -void testSynchronized() -{ - Foo *foo = [Foo new]; - printf("Enter synchronized code\n"); - [foo synchronizedCode]; - [foo release]; - [Foo shared]; - printf("testSynchronized() ran\n"); -} - -void testExceptions() -{ - Foo *foo = [Foo new]; - test([foo basicThrowAndCatchException]); - [foo release]; - printf("testExceptions() ran\n"); - -} - -void testRegisterAlias() -{ - class_registerAlias_np([NSObject class], "AliasObject"); - test([NSObject class] == objc_getClass("AliasObject")); - printf("testRegisterAlias() ran\n"); -} - -@interface SlowInit1 -+ (void)doNothing; -@end -@interface SlowInit2 -+ (void)doNothing; -@end -@implementation SlowInit1 -+ (void)initialize -{ - sleep(1); - [SlowInit2 doNothing]; -} -+ (void)doNothing {} -@end -static int initCount; -@implementation SlowInit1 -+ (void)initialize -{ - sleep(1); - __sync_fetch_and_add(&initCount, 1); -} -+ (void)doNothing {} -@end - - - -int main (int argc, const char * argv[]) -{ - testInvalidArguments(); - testGetMethod(); - testProtocols(); - testMultiTypedSelector(); - testClassHierarchy(); - testAllocateClass(); - printf("Instance of NSObject: %p\n", class_createInstance([NSObject class], 0)); - - NSAutoreleasePool *pool = [NSAutoreleasePool new]; - testSynchronized(); - testExceptions(); - testRegisterAlias(); - [pool release]; - - return exitStatus; -} diff --git a/third_party/libobjc/Test/RuntimeTest.xcodeproj/project.pbxproj b/third_party/libobjc/Test/RuntimeTest.xcodeproj/project.pbxproj deleted file mode 100644 index 29fc106662..0000000000 --- a/third_party/libobjc/Test/RuntimeTest.xcodeproj/project.pbxproj +++ /dev/null @@ -1,203 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 45; - objects = { - -/* Begin PBXBuildFile section */ - 8DD76F9A0486AA7600D96B5E /* RuntimeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* RuntimeTest.m */; settings = {ATTRIBUTES = (); }; }; - 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 8DD76F9E0486AA7600D96B5E /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 1; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 08FB7796FE84155DC02AAC07 /* RuntimeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RuntimeTest.m; sourceTree = "<group>"; }; - 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; }; - 8DD76FA10486AA7600D96B5E /* RuntimeTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = RuntimeTest; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 8DD76F9B0486AA7600D96B5E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 08FB7794FE84155DC02AAC07 /* RuntimeTest */ = { - isa = PBXGroup; - children = ( - 08FB7795FE84155DC02AAC07 /* Source */, - 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, - 1AB674ADFE9D54B511CA2CBB /* Products */, - ); - name = RuntimeTest; - sourceTree = "<group>"; - }; - 08FB7795FE84155DC02AAC07 /* Source */ = { - isa = PBXGroup; - children = ( - 08FB7796FE84155DC02AAC07 /* RuntimeTest.m */, - ); - name = Source; - sourceTree = "<group>"; - }; - 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = { - isa = PBXGroup; - children = ( - 08FB779EFE84155DC02AAC07 /* Foundation.framework */, - ); - name = "External Frameworks and Libraries"; - sourceTree = "<group>"; - }; - 1AB674ADFE9D54B511CA2CBB /* Products */ = { - isa = PBXGroup; - children = ( - 8DD76FA10486AA7600D96B5E /* RuntimeTest */, - ); - name = Products; - sourceTree = "<group>"; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 8DD76F960486AA7600D96B5E /* RuntimeTest */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "RuntimeTest" */; - buildPhases = ( - 8DD76F990486AA7600D96B5E /* Sources */, - 8DD76F9B0486AA7600D96B5E /* Frameworks */, - 8DD76F9E0486AA7600D96B5E /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = RuntimeTest; - productInstallPath = "$(HOME)/bin"; - productName = RuntimeTest; - productReference = 8DD76FA10486AA7600D96B5E /* RuntimeTest */; - productType = "com.apple.product-type.tool"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 08FB7793FE84155DC02AAC07 /* Project object */ = { - isa = PBXProject; - buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "RuntimeTest" */; - compatibilityVersion = "Xcode 3.1"; - hasScannedForEncodings = 1; - mainGroup = 08FB7794FE84155DC02AAC07 /* RuntimeTest */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 8DD76F960486AA7600D96B5E /* RuntimeTest */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 8DD76F990486AA7600D96B5E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 8DD76F9A0486AA7600D96B5E /* RuntimeTest.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 1DEB927508733DD40010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = /usr/local/bin; - PRODUCT_NAME = RuntimeTest; - }; - name = Debug; - }; - 1DEB927608733DD40010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_MODEL_TUNING = G5; - INSTALL_PATH = /usr/local/bin; - PRODUCT_NAME = RuntimeTest; - }; - name = Release; - }; - 1DEB927908733DD40010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - ONLY_ACTIVE_ARCH = YES; - PREBINDING = NO; - SDKROOT = macosx10.6; - }; - name = Debug; - }; - 1DEB927A08733DD40010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - PREBINDING = NO; - SDKROOT = macosx10.6; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "RuntimeTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB927508733DD40010E9CD /* Debug */, - 1DEB927608733DD40010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "RuntimeTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB927908733DD40010E9CD /* Debug */, - 1DEB927A08733DD40010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; -} diff --git a/third_party/libobjc/Test/objc_msgSend.m b/third_party/libobjc/Test/objc_msgSend.m deleted file mode 100644 index ff179cda9c..0000000000 --- a/third_party/libobjc/Test/objc_msgSend.m +++ /dev/null @@ -1,143 +0,0 @@ -#include <time.h> -#include <stdio.h> -#include <objc/runtime.h> -#include <assert.h> -#include <string.h> -#include <class.h> -#include <stdarg.h> - -//#define assert(x) if (!(x)) { printf("Failed %d\n", __LINE__); } - -id objc_msgSend(id, SEL, ...); - -typedef struct { int a,b,c,d,e; } s; -s objc_msgSend_stret(id, SEL, ...); -@interface Fake -- (int)izero; -- (float)fzero; -- (double)dzero; -- (long double)ldzero; -@end - -Class TestCls; -@interface Test { id isa; }@end -@implementation Test -- foo -{ - assert((id)1 == self); - assert(strcmp("foo", sel_getName(_cmd)) == 0); - return (id)0x42; -} -+ foo -{ - assert(TestCls == self); - assert(strcmp("foo", sel_getName(_cmd)) == 0); - return (id)0x42; -} -+ (s)sret -{ - assert(TestCls == self); - assert(strcmp("sret", sel_getName(_cmd)) == 0); - s st = {1,2,3,4,5}; - return st; -} -- (s)sret -{ - assert((id)3 == self); - assert(strcmp("sret", sel_getName(_cmd)) == 0); - s st = {1,2,3,4,5}; - return st; -} -+ (void)printf: (const char*)str, ... -{ - va_list ap; - char *s; - - va_start(ap, str); - - vasprintf(&s, str, ap); - va_end(ap); - //fprintf(stderr, "String: '%s'\n", s); - assert(strcmp(s, "Format string 42 42.000000\n") ==0); -} -+ (void)initialize -{ - [self printf: "Format %s %d %f%c", "string", 42, 42.0, '\n']; - @throw self; -} -+ nothing { return 0; } -@end -int main(void) -{ - TestCls = objc_getClass("Test"); - int exceptionThrown = 0; - @try { - objc_msgSend(TestCls, @selector(foo)); - } @catch (id e) - { - assert((TestCls == e) && "Exceptions propagate out of +initialize"); - exceptionThrown = 1; - } - assert(exceptionThrown && "An exception was thrown"); - assert((id)0x42 == objc_msgSend(TestCls, @selector(foo))); - objc_msgSend(TestCls, @selector(nothing)); - objc_msgSend(TestCls, @selector(missing)); - assert(0 == objc_msgSend(0, @selector(nothing))); - id a = objc_msgSend(objc_getClass("Test"), @selector(foo)); - assert((id)0x42 == a); - a = objc_msgSend(TestCls, @selector(foo)); - assert((id)0x42 == a); - assert(objc_registerSmallObjectClass_np(objc_getClass("Test"), 1)); - a = objc_msgSend((id)01, @selector(foo)); - assert((id)0x42 == a); - s ret = objc_msgSend_stret(TestCls, @selector(sret)); - assert(ret.a == 1); - assert(ret.b == 2); - assert(ret.c == 3); - assert(ret.d == 4); - assert(ret.e == 5); - if (sizeof(id) == 8) - { - assert(objc_registerSmallObjectClass_np(objc_getClass("Test"), 3)); - ret = objc_msgSend_stret((id)3, @selector(sret)); - assert(ret.a == 1); - assert(ret.b == 2); - assert(ret.c == 3); - assert(ret.d == 4); - assert(ret.e == 5); - } - Fake *f = nil; - assert(0 == [f izero]); - assert(0 == [f dzero]); - assert(0 == [f ldzero]); - assert(0 == [f fzero]); -#ifdef BENCHMARK - clock_t c1, c2; - c1 = clock(); - for (int i=0 ; i<100000000 ; i++) - { - [TestCls nothing]; - } - c2 = clock(); - printf("Traditional message send took %f seconds. \n", - ((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC); - c1 = clock(); - for (int i=0 ; i<100000000 ; i++) - { - objc_msgSend(TestCls, @selector(nothing)); - } - c2 = clock(); - printf("objc_msgSend() message send took %f seconds. \n", - ((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC); - IMP nothing = objc_msg_lookup(TestCls, @selector(nothing)); - c1 = clock(); - for (int i=0 ; i<100000000 ; i++) - { - nothing(TestCls, @selector(nothing)); - } - c2 = clock(); - printf("Direct IMP call took %f seconds. \n", - ((double)c2 - (double)c1) / (double)CLOCKS_PER_SEC); -#endif - return 0; -} diff --git a/third_party/libobjc/arc.m b/third_party/libobjc/arc.m index 6bc56b6645..483cd5132c 100644 --- a/third_party/libobjc/arc.m +++ b/third_party/libobjc/arc.m @@ -17,6 +17,7 @@ pthread_key_t ARCThreadKey; #endif extern char _NSConcreteMallocBlock; +extern char _NSConcreteStackBlock; extern char _NSConcreteGlobalBlock; @interface NSAutoreleasePool @@ -133,13 +134,12 @@ static void emptyPool(struct arc_tls *tls, id *stop) static void cleanupPools(struct arc_tls* tls) { - struct arc_autorelease_pool *pool = tls->pool; if (tls->returnRetained) { release(tls->returnRetained); tls->returnRetained = nil; } - while(NULL != pool) + if (NULL != tls->pool) { emptyPool(tls, NULL); assert(NULL == tls->pool); @@ -167,7 +167,8 @@ static inline id retain(id obj) { if (isSmallObject(obj)) { return obj; } Class cls = obj->isa; - if ((Class)&_NSConcreteMallocBlock == cls) + if ((Class)&_NSConcreteMallocBlock == cls || + (Class)&_NSConcreteStackBlock == cls) { return Block_copy(obj); } @@ -184,6 +185,16 @@ static inline void release(id obj) { if (isSmallObject(obj)) { return; } Class cls = obj->isa; + if (cls == (Class)&_NSConcreteMallocBlock) + { + _Block_release(obj); + return; + } + if ((cls == (Class)&_NSConcreteStackBlock) || + (cls == (Class)&_NSConcreteGlobalBlock)) + { + return; + } if (objc_test_class_flag(cls, objc_class_flag_fast_arc)) { intptr_t *refCount = ((intptr_t*)obj) - 1; @@ -519,7 +530,7 @@ id objc_storeWeak(id *addr, id obj) break; } } - oldRef = oldRef->next; + oldRef = (oldRef == NULL) ? NULL : oldRef->next; } } if (nil == obj) diff --git a/third_party/libobjc/class_table.c b/third_party/libobjc/class_table.c index 8feb1f4890..fa9a876c78 100644 --- a/third_party/libobjc/class_table.c +++ b/third_party/libobjc/class_table.c @@ -244,7 +244,10 @@ PRIVATE BOOL objc_resolve_class(Class cls) // Fix up the ivar offsets objc_compute_ivar_offsets(cls); // Send the +load message, if required - objc_send_load_message(cls); + if (!objc_test_class_flag(cls, objc_class_flag_user_created)) + { + objc_send_load_message(cls); + } if (_objc_load_callback) { _objc_load_callback(cls, 0); diff --git a/third_party/libobjc/dtable.c b/third_party/libobjc/dtable.c index a223511887..f534b17c9f 100644 --- a/third_party/libobjc/dtable.c +++ b/third_party/libobjc/dtable.c @@ -18,7 +18,8 @@ PRIVATE dtable_t uninstalled_dtable; PRIVATE InitializingDtable *temporary_dtables; /** Lock used to protect the temporary dtables list. */ PRIVATE mutex_t initialize_lock; -/** The size of the largest dtable, rounded up to the nearest power of two. */ +/** The size of the largest dtable. This is a sparse array shift value, so is + * 2^x in increments of 8. */ static uint32_t dtable_depth = 8; struct objc_slot* objc_get_slot(Class cls, SEL selector); @@ -555,11 +556,13 @@ PRIVATE void objc_resize_dtables(uint32_t newSize) LOCK_RUNTIME_FOR_SCOPE(); - dtable_depth <<= 1; + if (1<<dtable_depth > newSize) { return; } + + dtable_depth += 8; uint32_t oldMask = uninstalled_dtable->mask; - SparseArrayExpandingArray(uninstalled_dtable); + SparseArrayExpandingArray(uninstalled_dtable, dtable_depth); // Resize all existing dtables void *e = NULL; struct objc_class *next; @@ -569,7 +572,8 @@ PRIVATE void objc_resize_dtables(uint32_t newSize) NULL != next->dtable && ((SparseArray*)next->dtable)->mask == oldMask) { - SparseArrayExpandingArray((void*)next->dtable); + SparseArrayExpandingArray((void*)next->dtable, dtable_depth); + SparseArrayExpandingArray((void*)next->isa->dtable, dtable_depth); } } } @@ -666,14 +670,11 @@ PRIVATE void objc_send_initialize(id object) objc_send_initialize((id)class->super_class); } - LOCK(&initialize_lock); - // Superclass +initialize might possibly send a message to this class, in // which case this method would be called again. See NSObject and // NSAutoreleasePool +initialize interaction in GNUstep. if (objc_test_class_flag(class, objc_class_flag_initialized)) { - UNLOCK(&initialize_lock); // We know that initialization has started because the flag is set. // Check that it's finished by grabbing the class lock. This will be // released once the class has been fully initialized @@ -683,7 +684,20 @@ PRIVATE void objc_send_initialize(id object) return; } + // Lock the runtime while we're creating dtables and before we acquire any + // other locks. This prevents a lock-order reversal when + // dtable_for_class is called from something holding the runtime lock while + // we're still holding the initialize lock. We should ensure that we never + // acquire the runtime lock after acquiring the initialize lock. + LOCK_RUNTIME(); LOCK_OBJECT_FOR_SCOPE((id)meta); + LOCK(&initialize_lock); + if (objc_test_class_flag(class, objc_class_flag_initialized)) + { + UNLOCK(&initialize_lock); + return; + } + BOOL skipMeta = objc_test_class_flag(meta, objc_class_flag_initialized); // Set the initialized flag on both this class and its metaclass, to make // sure that +initialize is only ever sent once. @@ -691,7 +705,15 @@ PRIVATE void objc_send_initialize(id object) objc_set_class_flag(meta, objc_class_flag_initialized); dtable_t class_dtable = create_dtable_for_class(class, uninstalled_dtable); - dtable_t dtable = create_dtable_for_class(meta, class_dtable); + dtable_t dtable = skipMeta ? 0 : create_dtable_for_class(meta, class_dtable); + // Now we've finished doing things that may acquire the runtime lock, so we + // can hold onto the initialise lock to make anything doing + // dtable_for_class block until we've finished updating temporary dtable + // lists. + // If another thread holds the runtime lock, it can now proceed until it + // gets into a dtable_for_class call, and then block there waiting for us + // to finish setting up the temporary dtable. + UNLOCK_RUNTIME(); static SEL initializeSel = 0; if (0 == initializeSel) @@ -699,14 +721,17 @@ PRIVATE void objc_send_initialize(id object) initializeSel = sel_registerName("initialize"); } - struct objc_slot *initializeSlot = - objc_dtable_lookup(dtable, initializeSel->index); + struct objc_slot *initializeSlot = skipMeta ? 0 : + objc_dtable_lookup(dtable, initializeSel->index); // If there's no initialize method, then don't bother installing and // removing the initialize dtable, just install both dtables correctly now if (0 == initializeSlot) { - meta->dtable = dtable; + if (!skipMeta) + { + meta->dtable = dtable; + } class->dtable = class_dtable; checkARCAccessors(class); UNLOCK(&initialize_lock); @@ -723,7 +748,12 @@ PRIVATE void objc_send_initialize(id object) __attribute__((cleanup(remove_dtable))) InitializingDtable meta_buffer = { meta, dtable, &buffer }; temporary_dtables = &meta_buffer; + // We now release the initialize lock. We'll reacquire it later when we do + // the cleanup, but at this point we allow other threads to get the + // temporary dtable and call +initialize in other threads. UNLOCK(&initialize_lock); + // We still hold the class lock at this point. dtable_for_class will block + // there after acquiring the temporary dtable. checkARCAccessors(class); diff --git a/third_party/libobjc/dwarf_eh.h b/third_party/libobjc/dwarf_eh.h index 6dbe1e6d83..56cb280152 100644 --- a/third_party/libobjc/dwarf_eh.h +++ b/third_party/libobjc/dwarf_eh.h @@ -284,7 +284,7 @@ __attribute__((unused)) static struct dwarf_eh_action dwarf_eh_find_callsite(struct _Unwind_Context *context, struct dwarf_eh_lsda *lsda) { - struct dwarf_eh_action result = { 0, 0}; + struct dwarf_eh_action result = { 0, 0 }; uint64_t ip = _Unwind_GetIP(context) - _Unwind_GetRegionStart(context); unsigned char *callsite_table = (unsigned char*)lsda->call_site_table; while (callsite_table <= lsda->action_table) @@ -320,4 +320,4 @@ static struct dwarf_eh_action return result; } -#define EXCEPTION_CLASS(a,b,c,d,e,f,g,h) (((uint64_t)a << 56) + ((uint64_t)b << 48) + ((uint64_t)c << 40) + ((uint64_t)d << 32) + ((uint64_t)e << 24) + ((uint64_t)f << 16) + ((uint64_t)g << 8) + ((uint64_t)h)) +#define EXCEPTION_CLASS(a,b,c,d,e,f,g,h) ((((uint64_t)a) << 56) + (((uint64_t)b) << 48) + (((uint64_t)c) << 40) + (((uint64_t)d) << 32) + (((uint64_t)e) << 24) + (((uint64_t)f) << 16) + (((uint64_t)g) << 8) + (((uint64_t)h))) diff --git a/third_party/libobjc/eh_personality.c b/third_party/libobjc/eh_personality.c index 77feb2a271..b12d8b9df5 100644 --- a/third_party/libobjc/eh_personality.c +++ b/third_party/libobjc/eh_personality.c @@ -7,7 +7,23 @@ #include "class.h" #include "objcxx_eh.h" -#define fprintf(...) +#ifndef NO_PTHREADS +#include <pthread.h> +#endif + +#ifndef DEBUG_EXCEPTIONS +#define DEBUG_LOG(...) +#else +#define DEBUG_LOG(str, ...) fprintf(stderr, str, ## __VA_ARGS__) +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif +#if !__has_builtin(__builtin_unreachable) +#define __builtin_unreachable abort +#endif + /** * Class of exceptions to distinguish between this and other exception types. @@ -26,7 +42,15 @@ struct objc_exception int handlerSwitchValue; /** The cached landing pad for the catch handler.*/ void *landingPad; - + /** + * Next pointer for chained exceptions. + */ + struct objc_exception *next; + /** + * The number of nested catches that may hold this exception. This is + * negative while an exception is being rethrown. + */ + int catch_count; /** The language-agnostic part of the exception header. */ struct _Unwind_Exception unwindHeader; /** Thrown object. This is after the unwind header so that the C++ @@ -39,6 +63,12 @@ struct objc_exception struct _Unwind_Exception *cxx_exception; }; +struct objc_exception *objc_exception_from_header(struct _Unwind_Exception *ex) +{ + return (struct objc_exception*)((char*)ex - + offsetof(struct objc_exception, unwindHeader)); +} + typedef enum { handler_none, @@ -132,14 +162,14 @@ void objc_exception_throw(id object) if ((nil != object) && (class_respondsToSelector(classForObject(object), rethrow_sel))) { - fprintf(stderr, "Rethrowing\n"); + DEBUG_LOG("Rethrowing\n"); IMP rethrow = objc_msg_lookup(object, rethrow_sel); rethrow(object, rethrow_sel); // Should not be reached! If it is, then the rethrow method actually // didn't, so we throw it normally. } - fprintf(stderr, "Throwing %p\n", object); + DEBUG_LOG("Throwing %p\n", object); struct objc_exception *ex = calloc(1, sizeof(struct objc_exception)); @@ -154,7 +184,7 @@ void objc_exception_throw(id object) { _objc_unexpected_exception(object); } - fprintf(stderr, "Throw returned %d\n",(int) err); + DEBUG_LOG("Throw returned %d\n",(int) err); abort(); } @@ -175,7 +205,7 @@ static Class get_type_table_entry(struct _Unwind_Context *context, if (0 == class_name) { return Nil; } - fprintf(stderr, "Class name: %s\n", class_name); + DEBUG_LOG("Class name: %s\n", class_name); if (strcmp("@id", class_name) == 0) { return (Class)1; } @@ -211,11 +241,11 @@ static handler_type check_action_record(struct _Unwind_Context *context, dw_eh_ptr_t action_record_offset_base = action_record; int displacement = read_sleb128(&action_record); *selector = filter; - fprintf(stderr, "Filter: %d\n", filter); + DEBUG_LOG("Filter: %d\n", filter); if (filter > 0) { Class type = get_type_table_entry(context, lsda, filter); - fprintf(stderr, "%p type: %d\n", type, !foreignException); + DEBUG_LOG("%p type: %d\n", type, !foreignException); // Catchall if (Nil == type) { @@ -225,7 +255,7 @@ static handler_type check_action_record(struct _Unwind_Context *context, // nothing when a foreign exception is thrown else if ((Class)1 == type) { - fprintf(stderr, "Found id catch\n"); + DEBUG_LOG("Found id catch\n"); if (!foreignException) { return handler_catchall_id; @@ -233,7 +263,7 @@ static handler_type check_action_record(struct _Unwind_Context *context, } else if (!foreignException && isKindOfClass(thrown_class, type)) { - fprintf(stderr, "found handler for %s\n", type->name); + DEBUG_LOG("found handler for %s\n", type->name); return handler_class; } else if (thrown_class == type) @@ -243,14 +273,14 @@ static handler_type check_action_record(struct _Unwind_Context *context, } else if (filter == 0) { - fprintf(stderr, "0 filter\n"); + DEBUG_LOG("0 filter\n"); // Cleanup? I think the GNU ABI doesn't actually use this, but it // would be a good way of indicating a non-id catchall... return handler_cleanup; } else { - fprintf(stderr, "Filter value: %d\n" + DEBUG_LOG("Filter value: %d\n" "Your compiler and I disagree on the correct layout of EH data.\n", filter); abort(); @@ -263,10 +293,20 @@ static handler_type check_action_record(struct _Unwind_Context *context, } /** - * The Objective-C exception personality function. + * The Objective-C exception personality function implementation. This is + * shared by the GCC-compatible and the new implementation. + * + * The key difference is that the new implementation always returns the + * exception object and boxes it. */ -BEGIN_PERSONALITY_FUNCTION(__gnu_objc_personality_v0) - fprintf(stderr, "Personality function called\n"); +static inline _Unwind_Reason_Code internal_objc_personality(int version, + _Unwind_Action actions, + uint64_t exceptionClass, + struct _Unwind_Exception *exceptionObject, + struct _Unwind_Context *context, + BOOL isNew) +{ + DEBUG_LOG("%s personality function called %p\n", isNew ? "New" : "Old", exceptionObject); // This personality function is for version 1 of the ABI. If you use it // with a future version of the ABI, it won't know what to do, so it @@ -276,10 +316,10 @@ BEGIN_PERSONALITY_FUNCTION(__gnu_objc_personality_v0) return _URC_FATAL_PHASE1_ERROR; } struct objc_exception *ex = 0; -#ifndef fprintf +#ifdef DEBUG_EXCEPTIONS char *cls = (char*)&exceptionClass; #endif - fprintf(stderr, "Class: %c%c%c%c%c%c%c%c\n", cls[7], cls[6], cls[5], cls[4], cls[3], cls[2], cls[1], cls[0]); + DEBUG_LOG("Class: %c%c%c%c%c%c%c%c\n", cls[7], cls[6], cls[5], cls[4], cls[3], cls[2], cls[1], cls[0]); // Check if this is a foreign exception. If it is a C++ exception, then we // have to box it. If it's something else, like a LanguageKit exception @@ -293,12 +333,13 @@ BEGIN_PERSONALITY_FUNCTION(__gnu_objc_personality_v0) #ifdef NO_OBJCXX if (exceptionClass == cxx_exception_class) { - id obj = objc_object_for_cxx_exception(exceptionObject); - if (obj != (id)-1) + int objcxx; + id obj = objc_object_for_cxx_exception(exceptionObject, &objcxx); + objcxxException = objcxx; + if (objcxxException) { object = obj; - fprintf(stderr, "ObjC++ object exception %p\n", object); - objcxxException = YES; + DEBUG_LOG("ObjC++ object exception %p\n", object); // This is a foreign exception, buy for the purposes of exception // matching, we pretend that it isn't. foreignException = NO; @@ -316,18 +357,16 @@ BEGIN_PERSONALITY_FUNCTION(__gnu_objc_personality_v0) // language-specific exception stuff. else if (!foreignException) { - ex = (struct objc_exception*) ((char*)exceptionObject - - offsetof(struct objc_exception, unwindHeader)); - + ex = objc_exception_from_header(exceptionObject); thrown_class = classForObject(ex->object); } else if (_objc_class_for_boxing_foreign_exception) { thrown_class = _objc_class_for_boxing_foreign_exception(exceptionClass); - fprintf(stderr, "Foreign class: %p\n", thrown_class); + DEBUG_LOG("Foreign class: %p\n", thrown_class); } unsigned char *lsda_addr = (void*)_Unwind_GetLanguageSpecificData(context); - fprintf(stderr, "LSDA: %p\n", lsda_addr); + DEBUG_LOG("LSDA: %p\n", lsda_addr); // No LSDA implies no landing pads - try the next frame if (0 == lsda_addr) { return _URC_CONTINUE_UNWIND; } @@ -338,12 +377,12 @@ BEGIN_PERSONALITY_FUNCTION(__gnu_objc_personality_v0) if (actions & _UA_SEARCH_PHASE) { - fprintf(stderr, "Search phase...\n"); + DEBUG_LOG("Search phase...\n"); struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr); action = dwarf_eh_find_callsite(context, &lsda); handler_type handler = check_action_record(context, foreignException, &lsda, action.action_record, thrown_class, &selector); - fprintf(stderr, "handler: %d\n", handler); + DEBUG_LOG("handler: %d\n", handler); // If there's no action record, we've only found a cleanup, so keep // searching for something real if (handler == handler_class || @@ -351,17 +390,18 @@ BEGIN_PERSONALITY_FUNCTION(__gnu_objc_personality_v0) (handler == handler_catchall)) { saveLandingPad(context, exceptionObject, ex, selector, action.landing_pad); - fprintf(stderr, "Found handler! %d\n", handler); + DEBUG_LOG("Found handler! %d\n", handler); return _URC_HANDLER_FOUND; } return _URC_CONTINUE_UNWIND; } - fprintf(stderr, "Phase 2: Fight!\n"); + DEBUG_LOG("Phase 2: Fight!\n"); // TODO: If this is a C++ exception, we can cache the lookup and cheat a // bit if (!(actions & _UA_HANDLER_FRAME)) { + DEBUG_LOG("Not the handler frame, looking up the cleanup again\n"); struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr); action = dwarf_eh_find_callsite(context, &lsda); // If there's no cleanup here, continue unwinding. @@ -371,20 +411,19 @@ BEGIN_PERSONALITY_FUNCTION(__gnu_objc_personality_v0) } handler_type handler = check_action_record(context, foreignException, &lsda, action.action_record, thrown_class, &selector); - fprintf(stderr, "handler! %d %d\n", (int)handler, (int)selector); + DEBUG_LOG("handler! %d %d\n", (int)handler, (int)selector); // If this is not a cleanup, ignore it and keep unwinding. //if (check_action_record(context, foreignException, &lsda, //action.action_record, thrown_class, &selector) != handler_cleanup) if (handler != handler_cleanup) { - fprintf(stderr, "Ignoring handler! %d\n",handler); + DEBUG_LOG("Ignoring handler! %d\n",handler); return _URC_CONTINUE_UNWIND; } - fprintf(stderr, "Installing cleanup...\n"); + DEBUG_LOG("Installing cleanup...\n"); // If there is a cleanup, we need to return the exception structure // (not the object) to the calling frame. The exception object object = exceptionObject; - //selector = 0; } else if (foreignException || objcxxException) { @@ -396,42 +435,57 @@ BEGIN_PERSONALITY_FUNCTION(__gnu_objc_personality_v0) // exception, then we need to delete the exception object. if (foreignException) { - fprintf(stderr, "Doing the foreign exception thing...\n"); + DEBUG_LOG("Doing the foreign exception thing...\n"); //[thrown_class exceptionWithForeignException: exceptionObject]; SEL box_sel = sel_registerName("exceptionWithForeignException:"); IMP boxfunction = objc_msg_lookup((id)thrown_class, box_sel); - object = boxfunction((id)thrown_class, box_sel, exceptionObject); - fprintf(stderr, "Boxed as %p\n", object); + if (!isNew) + { + object = boxfunction((id)thrown_class, box_sel, exceptionObject); + DEBUG_LOG("Boxed as %p\n", object); + } } - else // ObjCXX exception + else if (!isNew) // ObjCXX exception { _Unwind_DeleteException(exceptionObject); } + // In the new EH ABI, we call objc_begin_catch() / and + // objc_end_catch(), which will wrap their __cxa* versions. } else { // Restore the saved info if we saved some last time. loadLandingPad(context, exceptionObject, ex, &selector, &action.landing_pad); object = ex->object; - free(ex); + if (!isNew) + { + free(ex); + } } _Unwind_SetIP(context, (unsigned long)action.landing_pad); _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), - (unsigned long)object); + (unsigned long)(isNew ? exceptionObject : object)); _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), selector); + DEBUG_LOG("Installing context, selector %d\n", (int)selector); return _URC_INSTALL_CONTEXT; } +BEGIN_PERSONALITY_FUNCTION(__gnu_objc_personality_v0) + return internal_objc_personality(version, actions, exceptionClass, + exceptionObject, context, NO); +} +BEGIN_PERSONALITY_FUNCTION(__gnustep_objc_personality_v0) + return internal_objc_personality(version, actions, exceptionClass, + exceptionObject, context, YES); +} // FIXME! #ifndef __arm__ BEGIN_PERSONALITY_FUNCTION(__gnustep_objcxx_personality_v0) if (exceptionClass == objc_exception_class) { - struct objc_exception *ex = (struct objc_exception*) - ((char*)exceptionObject - offsetof(struct objc_exception, - unwindHeader)); + struct objc_exception *ex = objc_exception_from_header(exceptionObject); if (0 == ex->cxx_exception) { id *newEx = __cxa_allocate_exception(sizeof(id)); @@ -448,3 +502,230 @@ BEGIN_PERSONALITY_FUNCTION(__gnustep_objcxx_personality_v0) return CALL_PERSONALITY_FUNCTION(__gxx_personality_v0); } #endif + +// Weak references to C++ runtime functions. We don't bother testing that +// these are 0 before calling them, because if they are not resolved then we +// should not be in a code path that involves a C++ exception. +__attribute__((weak)) void *__cxa_begin_catch(void *e); +__attribute__((weak)) void __cxa_end_catch(void); +__attribute__((weak)) void __cxa_rethrow(void); + +enum exception_type +{ + NONE, + CXX, + OBJC, + FOREIGN, + BOXED_FOREIGN +}; +struct thread_data +{ + enum exception_type current_exception_type; + struct objc_exception *caughtExceptions; +}; + +// IF we don't have pthreads, then we fall back to using a per-thread +// structure. This will leak memory if we terminate any threads with +// exceptions in-flight. +#ifdef NO_PTHREADS +static __thread struct thread_data thread_data; +#else +void clean_tls(void *td) +{ + struct thread_data *data = td; +} + +static pthread_key_t key; +void init_key(void) +{ + pthread_key_create(&key, clean_tls); +} +#endif + +struct thread_data *get_thread_data(void) +{ +#ifndef NO_PTHREADS + static pthread_once_t once_control = PTHREAD_ONCE_INIT; + pthread_once(&once_control, init_key); + struct thread_data *td = pthread_getspecific(key); + if (td == NULL) + { + td = calloc(sizeof(struct thread_data), 1); + pthread_setspecific(key, td); + if (pthread_getspecific(key) == NULL) + { + fprintf(stderr, "Unable to allocate thread-local storage for exceptions\n"); + } + } + return td; +#else + return &td; +#endif +} + +struct thread_data *get_thread_data_fast(void) +{ +#ifndef NO_PTHREADS + struct thread_data *td = pthread_getspecific(key); + return td; +#else + return &td; +#endif +} + +id objc_begin_catch(struct _Unwind_Exception *exceptionObject) +{ + struct thread_data *td = get_thread_data(); + DEBUG_LOG("Beginning catch %p\n", exceptionObject); + if (exceptionObject->exception_class == objc_exception_class) + { + td->current_exception_type = OBJC; + struct objc_exception *ex = objc_exception_from_header(exceptionObject); + if (ex->catch_count == 0) + { + // If this is the first catch, add it to the list. + ex->catch_count = 1; + ex->next = td->caughtExceptions; + td->caughtExceptions = ex; + } + else if (ex->catch_count < 0) + { + // If this is being thrown, mark it as caught again and increment + // the refcount + ex->catch_count = -ex->catch_count + 1; + } + else + { + // Otherwise, just increment the catch count + ex->catch_count++; + } + DEBUG_LOG("objc catch\n"); + return ex->object; + } + // If we have a foreign exception while we have stacked exceptions, we have + // a problem. We can't chain them, so we follow the example of C++ and + // just abort. + if (td->caughtExceptions != 0) + { + // FIXME: Actually, we can handle a C++ exception if only ObjC + // exceptions are in-flight + abort(); + } + // If this is a C++ exception, let the C++ runtime handle it. + if (exceptionObject->exception_class == cxx_exception_class) + { + DEBUG_LOG("c++ catch\n"); + td->current_exception_type = CXX; + return __cxa_begin_catch(exceptionObject); + } + DEBUG_LOG("foreign exception catch\n"); + // Box if we have a boxing function. + if (_objc_class_for_boxing_foreign_exception) + { + Class thrown_class = + _objc_class_for_boxing_foreign_exception(exceptionObject->exception_class); + SEL box_sel = sel_registerName("exceptionWithForeignException:"); + IMP boxfunction = objc_msg_lookup((id)thrown_class, box_sel); + if (boxfunction != 0) + { + id boxed = boxfunction((id)thrown_class, box_sel, exceptionObject); + td->caughtExceptions = (struct objc_exception*)boxed; + td->current_exception_type = BOXED_FOREIGN; + return boxed; + } + } + td->current_exception_type = FOREIGN; + td->caughtExceptions = (struct objc_exception*)exceptionObject; + // If this is some other kind of exception, then assume that the value is + // at the end of the exception header. + return (id)((char*)exceptionObject + sizeof(struct _Unwind_Exception)); +} + +void objc_end_catch(void) +{ + struct thread_data *td = get_thread_data_fast(); + // If this is a boxed foreign exception then the boxing class is + // responsible for cleaning it up + if (td->current_exception_type == BOXED_FOREIGN) + { + td->caughtExceptions = 0; + td->current_exception_type = NONE; + return; + } + DEBUG_LOG("Ending catch\n"); + // If this is a C++ exception, then just let the C++ runtime handle it. + if (td->current_exception_type == CXX) + { + __cxa_end_catch(); + td->current_exception_type = OBJC; + return; + } + if (td->current_exception_type == FOREIGN) + { + struct _Unwind_Exception *e = ((struct _Unwind_Exception*)td->caughtExceptions); + e->exception_cleanup(_URC_FOREIGN_EXCEPTION_CAUGHT, e); + td->current_exception_type = NONE; + td->caughtExceptions = 0; + return; + } + // Otherwise we should do the cleanup thing. Nested catches are possible, + // so we only clean up the exception if this is the last reference. + assert(td->caughtExceptions != 0); + struct objc_exception *ex = td->caughtExceptions; + // If this is being rethrown decrement its (negated) catch count, but don't + // delete it even if its catch count would be 0. + if (ex->catch_count < 0) + { + ex->catch_count++; + return; + } + ex->catch_count--; + if (ex->catch_count == 0) + { + td->caughtExceptions = ex->next; + free(ex); + } +} + +void objc_exception_rethrow(struct _Unwind_Exception *e) +{ + struct thread_data *td = get_thread_data_fast(); + // If this is an Objective-C exception, then + if (td->current_exception_type == OBJC) + { + struct objc_exception *ex = objc_exception_from_header(e); + assert(e->exception_class == objc_exception_class); + assert(ex == td->caughtExceptions); + assert(ex->catch_count > 0); + // Negate the catch count, so that we can detect that this is a + // rethrown exception in objc_end_catch + ex->catch_count = -ex->catch_count; + _Unwind_Reason_Code err = _Unwind_Resume_or_Rethrow(e); + free(ex); + if (_URC_END_OF_STACK == err && 0 != _objc_unexpected_exception) + { + _objc_unexpected_exception(ex->object); + } + abort(); + } + else if (td->current_exception_type == CXX) + { + assert(e->exception_class == cxx_exception_class); + __cxa_rethrow(); + } + if (td->current_exception_type == BOXED_FOREIGN) + { + SEL rethrow_sel = sel_registerName("rethrow"); + id object = (id)td->caughtExceptions; + if ((nil != object) && + (class_respondsToSelector(classForObject(object), rethrow_sel))) + { + DEBUG_LOG("Rethrowing boxed exception\n"); + IMP rethrow = objc_msg_lookup(object, rethrow_sel); + rethrow(object, rethrow_sel); + } + } + assert(e == (struct _Unwind_Exception*)td->caughtExceptions); + _Unwind_Resume_or_Rethrow(e); + abort(); +} diff --git a/third_party/libobjc/encoding2.c b/third_party/libobjc/encoding2.c index 5c9b4edcf1..439c5aca60 100644 --- a/third_party/libobjc/encoding2.c +++ b/third_party/libobjc/encoding2.c @@ -4,6 +4,7 @@ #include <ctype.h> #include "objc/runtime.h" +#include "objc/encoding.h" #include "method_list.h" #include "visibility.h" @@ -449,32 +450,23 @@ char* method_copyReturnType(Method method) unsigned objc_get_type_qualifiers (const char *type) { unsigned flags = 0; -#define MAP(chr, bit) case chr: flags |= (1<<bit); break; +#define MAP(chr, bit) case chr: flags |= bit; break; do { switch (*(type++)) { default: return flags; - MAP('r', 1) - MAP('n', 1) - MAP('o', 2) - MAP('N', 3) - MAP('O', 4) - MAP('V', 10) - MAP('R', 8) + MAP('r', _F_CONST) + MAP('n', _F_IN) + MAP('o', _F_OUT) + MAP('N', _F_INOUT) + MAP('O', _F_BYCOPY) + MAP('V', _F_ONEWAY) + MAP('R', _F_BYREF) } } while (1); } -struct objc_struct_layout -{ - const char *original_type; - const char *type; - const char *prev_type; - unsigned int record_size; - unsigned int record_align; -}; - // Note: The implementations of these functions is horrible. void objc_layout_structure (const char *type, struct objc_struct_layout *layout) diff --git a/third_party/libobjc/hash_table.h b/third_party/libobjc/hash_table.h index e064af1782..951bd2d7a1 100644 --- a/third_party/libobjc/hash_table.h +++ b/third_party/libobjc/hash_table.h @@ -199,6 +199,7 @@ static int PREFIX(_table_resize)(PREFIX(_table) *table) __sync_synchronize(); table->old = NULL; # if !defined(ENABLE_GC) && defined(MAP_TABLE_SINGLE_THREAD) + free(copy->table); free(copy); # endif return 1; diff --git a/third_party/libobjc/loader.c b/third_party/libobjc/loader.c index 67a8cf5b95..6f4d88e06d 100644 --- a/third_party/libobjc/loader.c +++ b/third_party/libobjc/loader.c @@ -25,6 +25,13 @@ void init_selector_tables(void); void init_trampolines(void); void objc_send_load_message(Class class); +void log_selector_memory_usage(void); + +static void log_memory_stats(void) +{ + log_selector_memory_usage(); +} + /* Number of threads that are alive. */ int __objc_runtime_threads_alive = 1; /* !T:MUTEX */ @@ -62,6 +69,10 @@ void __objc_exec_class(struct objc_module_abi_8 *module) init_arc(); init_trampolines(); first_run = NO; + if (getenv("LIBOBJC_MEMORY_PROFILE")) + { + atexit(log_memory_stats); + } } // The runtime mutex is held for the entire duration of a load. It does diff --git a/third_party/libobjc/lock.h b/third_party/libobjc/lock.h index 1ec7a68d99..03656818c0 100644 --- a/third_party/libobjc/lock.h +++ b/third_party/libobjc/lock.h @@ -22,7 +22,7 @@ typedef HANDLE mutex_t; typedef pthread_mutex_t mutex_t; // If this pthread implementation has a static initializer for recursive // mutexes, use that, otherwise fall back to the portable version -# define INIT_LOCK(x) init_recursive_mutex(&(x)) +# define INIT_LOCK(x) init_recursive_mutex(&(x)) static inline void init_recursive_mutex(pthread_mutex_t *x) { diff --git a/third_party/libobjc/objc/Availability.h b/third_party/libobjc/objc/Availability.h index 408b9988ca..0bdc7cda55 100644 --- a/third_party/libobjc/objc/Availability.h +++ b/third_party/libobjc/objc/Availability.h @@ -1,3 +1,7 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + #ifdef STRICT_MACOS_X # define OBJC_NONPORTABLE __attribute__((error("Function not supported by the Apple runtime"))) diff --git a/third_party/libobjc/objc/Object.h b/third_party/libobjc/objc/Object.h index e5c9c4669d..b37647d648 100644 --- a/third_party/libobjc/objc/Object.h +++ b/third_party/libobjc/objc/Object.h @@ -1,3 +1,7 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + #include <objc/runtime.h> @interface Object diff --git a/third_party/libobjc/objc/Protocol.h b/third_party/libobjc/objc/Protocol.h index d38c1f51d3..b67085dd56 100644 --- a/third_party/libobjc/objc/Protocol.h +++ b/third_party/libobjc/objc/Protocol.h @@ -1,3 +1,7 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + #import "Object.h" @interface Protocol : Object @end diff --git a/third_party/libobjc/objc/blocks_private.h b/third_party/libobjc/objc/blocks_private.h index 5f1fca1558..60a1d590ac 100644 --- a/third_party/libobjc/objc/blocks_private.h +++ b/third_party/libobjc/objc/blocks_private.h @@ -1,5 +1,9 @@ #ifndef __LIBOBJC_BLOCKS_PRIVATE_H_INCLUDED__ #define __LIBOBJC_BLOCKS_PRIVATE_H_INCLUDED__ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + /* * This header file exposes some implementation details of the blocks runtime diff --git a/third_party/libobjc/objc/blocks_runtime.h b/third_party/libobjc/objc/blocks_runtime.h index 8a64bf2fd5..e5dbdea6f0 100644 --- a/third_party/libobjc/objc/blocks_runtime.h +++ b/third_party/libobjc/objc/blocks_runtime.h @@ -1,3 +1,7 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + /* * Blocks Runtime */ diff --git a/third_party/libobjc/objc/capabilities.h b/third_party/libobjc/objc/capabilities.h index 950749c080..dc4e046471 100644 --- a/third_party/libobjc/objc/capabilities.h +++ b/third_party/libobjc/objc/capabilities.h @@ -1,3 +1,7 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + /** * capabilities.h - This file defines the list of capabilities. Runtime * capabilities can be checked. You may use #ifdef to test at compile time diff --git a/third_party/libobjc/objc/developer.h b/third_party/libobjc/objc/developer.h index 2752cf12d5..321ea8d424 100644 --- a/third_party/libobjc/objc/developer.h +++ b/third_party/libobjc/objc/developer.h @@ -1,3 +1,7 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + enum objc_developer_mode_np { /** User mode - the default. */ diff --git a/third_party/libobjc/objc/encoding.h b/third_party/libobjc/objc/encoding.h index bb58c0b8c4..4c4ff53b15 100644 --- a/third_party/libobjc/objc/encoding.h +++ b/third_party/libobjc/objc/encoding.h @@ -1,3 +1,7 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + #ifndef __LIBOBJC_ENCODING_H_INCLUDED__ #define __LIBOBJC_ENCODING_H_INCLUDED__ diff --git a/third_party/libobjc/objc/hooks.h b/third_party/libobjc/objc/hooks.h index 7fdcf88091..8ec6b70e6e 100644 --- a/third_party/libobjc/objc/hooks.h +++ b/third_party/libobjc/objc/hooks.h @@ -1,3 +1,7 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + /** * This file includes all of the hooks that can be used to alter the behaviour * of the runtime. diff --git a/third_party/libobjc/objc/message.h b/third_party/libobjc/objc/message.h new file mode 100644 index 0000000000..ed946950e4 --- /dev/null +++ b/third_party/libobjc/objc/message.h @@ -0,0 +1,62 @@ +#if defined(__clang__) +#pragma clang system_header +#endif + +#ifndef _OBJC_MESSAGE_H_ +#define _OBJC_MESSAGE_H_ + +#if defined(__x86_64) || defined(__i386) || defined(__arm__) || \ + defined(__mips_n64) || defined(__mips_n32) +/** + * Standard message sending function. This function must be cast to the + * correct types for the function before use. The first argument is the + * receiver and the second the selector. + * + * Note that this function is not available on all architectures. For a more + * portable solution to sending arbitrary messages, consider using + * objc_msg_lookup_sender() and then calling the returned IMP directly. + * + * This version of the function is used for all messages that return either an + * integer, a pointer, or a small structure value that is returned in + * registers. Be aware that calling conventions differ between operating + * systems even within the same architecture, so take great care if using this + * function for small (two integer) structures. + */ +id objc_msgSend(id self, SEL _cmd, ...); +/** + * Standard message sending function. This function must be cast to the + * correct types for the function before use. The first argument is the + * receiver and the second the selector. + * + * Note that this function is not available on all architectures. For a more + * portable solution to sending arbitrary messages, consider using + * objc_msg_lookup_sender() and then calling the returned IMP directly. + * + * This version of the function is used for all messages that return a + * structure that is not returned in registers. Be aware that calling + * conventions differ between operating systems even within the same + * architecture, so take great care if using this function for small (two + * integer) structures. + */ +#ifdef __cplusplus +id objc_msgSend_stret(id self, SEL _cmd, ...); +#else +void objc_msgSend_stret(id self, SEL _cmd, ...); +#endif +/** + * Standard message sending function. This function must be cast to the + * correct types for the function before use. The first argument is the + * receiver and the second the selector. + * + * Note that this function is not available on all architectures. For a more + * portable solution to sending arbitrary messages, consider using + * objc_msg_lookup_sender() and then calling the returned IMP directly. + * + * This version of the function is used for all messages that return floating + * point values. + */ +long double objc_msgSend_fpret(id self, SEL _cmd, ...); + +#endif + +#endif //_OBJC_MESSAGE_H_ diff --git a/third_party/libobjc/objc/objc-api.h b/third_party/libobjc/objc/objc-api.h index 9786447367..9cf4aaf894 100644 --- a/third_party/libobjc/objc/objc-api.h +++ b/third_party/libobjc/objc/objc-api.h @@ -1 +1,5 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + #include <objc/runtime.h> diff --git a/third_party/libobjc/objc/objc-arc.h b/third_party/libobjc/objc/objc-arc.h index c7a420038d..0da4030928 100644 --- a/third_party/libobjc/objc/objc-arc.h +++ b/third_party/libobjc/objc/objc-arc.h @@ -1,3 +1,7 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + #ifndef __OBJC_ARC_INCLUDED__ #define __OBJC_ARC_INCLUDED__ /** diff --git a/third_party/libobjc/objc/objc-auto.h b/third_party/libobjc/objc/objc-auto.h index 2ec4939574..1dfe132f99 100644 --- a/third_party/libobjc/objc/objc-auto.h +++ b/third_party/libobjc/objc/objc-auto.h @@ -1,3 +1,7 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + /** * objc-auto.h - This file provides the interface for Objective-C garbage * collection diff --git a/third_party/libobjc/objc/runtime-deprecated.h b/third_party/libobjc/objc/runtime-deprecated.h index f85d3df27a..2f6c78ad3d 100644 --- a/third_party/libobjc/objc/runtime-deprecated.h +++ b/third_party/libobjc/objc/runtime-deprecated.h @@ -1,3 +1,7 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + #if !defined(__GNUSTEP_LIBOBJC_RUNTIME_DEPRECATED_INCLUDED__) && !defined(GNUSTEP_LIBOBJC_NO_LEGACY) # define __GNUSTEP_LIBOBJC_RUNTIME_DEPRECATED_INCLUDED__ diff --git a/third_party/libobjc/objc/runtime.h b/third_party/libobjc/objc/runtime.h index 86c67ed5a2..3092f72e34 100644 --- a/third_party/libobjc/objc/runtime.h +++ b/third_party/libobjc/objc/runtime.h @@ -1,3 +1,7 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + #ifndef __LIBOBJC_RUNTIME_H_INCLUDED__ #define __LIBOBJC_RUNTIME_H_INCLUDED__ @@ -208,6 +212,8 @@ typedef struct #endif #include "slot.h" +#include "message.h" + /** * Adds an instance variable to the named class. The class must not have been diff --git a/third_party/libobjc/objc/slot.h b/third_party/libobjc/objc/slot.h index 0afd7548ad..cae2ee322d 100644 --- a/third_party/libobjc/objc/slot.h +++ b/third_party/libobjc/objc/slot.h @@ -1,3 +1,7 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif + #ifndef __OBJC_SLOT_H_INCLUDED__ #define __OBJC_SLOT_H_INCLUDED__ /** diff --git a/third_party/libobjc/objc_msgSend.x86-32.S b/third_party/libobjc/objc_msgSend.x86-32.S index f55f8c3c44..452cc7541b 100644 --- a/third_party/libobjc/objc_msgSend.x86-32.S +++ b/third_party/libobjc/objc_msgSend.x86-32.S @@ -76,11 +76,14 @@ jmp *%eax -6: # smallObject: +6: # smallObject: push %ebx # Save old %ebx - call __i686.get_pc_thunk.bx - addl $_GLOBAL_OFFSET_TABLE_, %ebx - mov SmallObjectClasses@GOT(%ebx), %eax + calll 7f +7: + popl %ebx; +8: + addl $_GLOBAL_OFFSET_TABLE_+(8b-7b), %ebx + leal SmallObjectClasses@GOTOFF(%ebx), %eax mov (%eax), %eax popl %ebx jmp 1b diff --git a/third_party/libobjc/objc_msgSend.x86-64.S b/third_party/libobjc/objc_msgSend.x86-64.S index 8ce012baf0..62d78daa59 100644 --- a/third_party/libobjc/objc_msgSend.x86-64.S +++ b/third_party/libobjc/objc_msgSend.x86-64.S @@ -95,7 +95,7 @@ push %rsi # Save self where it can be modified mov %rsp, %rdi push %rdx - mov %rdx, %rsi # move _cmd to where the callee expects it to be + mov %rdx, %rsi # move _cmd to where the callee expects it to be .endif .cfi_adjust_cfa_offset 0xD8 diff --git a/third_party/libobjc/objcxx_eh.cc b/third_party/libobjc/objcxx_eh.cc index 0a29c3ce73..7893e61d90 100644 --- a/third_party/libobjc/objcxx_eh.cc +++ b/third_party/libobjc/objcxx_eh.cc @@ -29,7 +29,6 @@ namespace std bool operator==(const type_info &) const; bool operator!=(const type_info &) const; bool before(const type_info &) const; - const char* name() const; type_info(); private: type_info(const type_info& rhs); @@ -38,6 +37,7 @@ namespace std protected: type_info(const char *name): __type_name(name) { } public: + const char* name() const { return __type_name; } virtual bool __is_pointer_p() const; virtual bool __is_function_p() const; virtual bool __do_catch(const type_info *thrown_type, @@ -214,7 +214,7 @@ struct _Unwind_Exception *objc_init_cxx_exception(void *thrown_exception) return &ex->unwindHeader; } -void* objc_object_for_cxx_exception(void *thrown_exception) +void* objc_object_for_cxx_exception(void *thrown_exception, int *isValid) { __cxa_exception *ex = (__cxa_exception*) ((char*)thrown_exception - offsetof(struct __cxa_exception, unwindHeader)); @@ -222,8 +222,10 @@ void* objc_object_for_cxx_exception(void *thrown_exception) if (!dynamic_cast<const gnustep::libobjc::__objc_id_type_info*>(thrownType) && !dynamic_cast<const gnustep::libobjc::__objc_class_type_info*>(thrownType)) { - return (id)-1; + *isValid = 0; + return 0; } + *isValid = 1; return *(id*)(ex+1); } diff --git a/third_party/libobjc/objcxx_eh.h b/third_party/libobjc/objcxx_eh.h index 9866105504..987479c1de 100644 --- a/third_party/libobjc/objcxx_eh.h +++ b/third_party/libobjc/objcxx_eh.h @@ -28,11 +28,11 @@ __attribute__((weak)) void __cxa_free_exception(void *thrown_exception); /** * Tests whether a C++ exception contains an Objective-C object, and returns if - * if it does. Returns -1 if it doesn't. -1 is used instead of 0, because - * throwing nil is allowed, but throwing non-nil, invalid objects is not. + * if it does. The second argument is a pointer to a boolean value indicating + * whether this is a valid object. */ __attribute__((weak)) -void *objc_object_for_cxx_exception(void *thrown_exception); +void *objc_object_for_cxx_exception(void *thrown_exception, int *isValid); /** * Prints the type info associated with an exception. Used only when diff --git a/third_party/libobjc/opts/CMakeLists.txt b/third_party/libobjc/opts/CMakeLists.txt index e017de8fb0..aa3f7947b5 100644 --- a/third_party/libobjc/opts/CMakeLists.txt +++ b/third_party/libobjc/opts/CMakeLists.txt @@ -1,3 +1,13 @@ +cmake_minimum_required(VERSION 2.8) + +find_package(LLVM) +include(AddLLVM) + +add_definitions(${LLVM_DEFINITIONS}) +include_directories(${LLVM_INCLUDE_DIRS}) +link_directories(${LLVM_LIBRARY_DIRS}) + + add_llvm_loadable_module( libGNUObjCRuntime ClassIMPCache.cpp ClassMethodInliner.cpp @@ -10,13 +20,22 @@ add_llvm_loadable_module( libGNUObjCRuntime TypeFeedback.cpp ) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-variadic-macros -DLLVM_MAJOR=3 -DLLVM_MINOR=0") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-variadic-macros") + +set(CMAKE_CXX "clang++") + +EXEC_PROGRAM(llvm-config + ARGS --src-root + OUTPUT_VARIABLE LLVM_SRC) +EXEC_PROGRAM(llvm-config + ARGS --obj-root + OUTPUT_VARIABLE LLVM_OBJ) +EXEC_PROGRAM(llvm-config + ARGS --version + OUTPUT_VARIABLE LLVM_VER) + +string(REGEX REPLACE "([0-9]*).([0-9]*).*" "-DLLVM_MAJOR=\\1 -DLLVM_MINOR=\\2" LLVM_VERSION "${LLVM_VER}") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_VERSION} -fno-rtti") +include_directories( ${LLVM_INCLUDE_DIRS} "${LLVM_SRC}/include/" "${LLVM_OBJ}/include/") -add_llvm_library_dependencies( libGNUObjCRuntime - LLVMAnalysis - LLVMCore - LLVMSupport - LLVMTarget - LLVMipa - LLVMipo -) diff --git a/third_party/libobjc/opts/ClassIMPCache.cpp b/third_party/libobjc/opts/ClassIMPCache.cpp index e427eeca6c..5f420d88bd 100644 --- a/third_party/libobjc/opts/ClassIMPCache.cpp +++ b/third_party/libobjc/opts/ClassIMPCache.cpp @@ -1,14 +1,6 @@ #include "llvm/Pass.h" -#include "llvm/Function.h" -#include "llvm/Module.h" -#include "llvm/LLVMContext.h" -#include "llvm/Instructions.h" -#include "llvm/Constants.h" -#include "llvm/GlobalVariable.h" #include "llvm/Support/CallSite.h" -#include "llvm/Support/IRBuilder.h" #include "llvm/Analysis/LoopInfo.h" -#include "llvm/DefaultPasses.h" #include "ObjectiveCOpts.h" #include "IMPCacher.h" #include <string> diff --git a/third_party/libobjc/opts/ClassLookupCache.cpp b/third_party/libobjc/opts/ClassLookupCache.cpp index 0f74cf0e46..e41aabe7c4 100644 --- a/third_party/libobjc/opts/ClassLookupCache.cpp +++ b/third_party/libobjc/opts/ClassLookupCache.cpp @@ -1,14 +1,6 @@ -#include "llvm/Pass.h" -#include "llvm/Function.h" -#include "llvm/Module.h" -#include "llvm/Instructions.h" -#include "llvm/Constants.h" -#include "llvm/GlobalVariable.h" -#include "llvm/Support/IRBuilder.h" #include "llvm/ADT/StringMap.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/DefaultPasses.h" #include "ObjectiveCOpts.h" #include <string> @@ -50,7 +42,7 @@ namespace for (BasicBlock::iterator b=i->begin(), last=i->end() ; b != last ; ++b) { if (CallInst *call = dyn_cast<CallInst>(b)) { - if (Function *func = call->getCalledFunction()) { + if (Function *func = dyn_cast<Function>(call->getCalledValue()->stripPointerCasts())) { if (func->getName() == "objc_lookup_class") { ClassLookup lookup; GlobalVariable *classNameVar = dyn_cast<GlobalVariable>( @@ -79,7 +71,6 @@ namespace std::string &cls = i->second; LLVMType *clsTy = lookup->getType(); Value *global = M->getGlobalVariable(("_OBJC_CLASS_" + i->second).c_str(), true); - global = 0; // If we can see the class reference for this, then reference it // directly. If not, then do the lookup and cache it. if (global) { @@ -88,6 +79,7 @@ namespace Value *cls = new BitCastInst(global, clsTy, "class", lookup); lookup->replaceAllUsesWith(cls); lookup->removeFromParent(); + delete lookup; } else { GlobalVariable *cache = statics[cls]; if (!cache) { diff --git a/third_party/libobjc/opts/ClassMethodInliner.cpp b/third_party/libobjc/opts/ClassMethodInliner.cpp index dff8b7154a..7d061f30ef 100644 --- a/third_party/libobjc/opts/ClassMethodInliner.cpp +++ b/third_party/libobjc/opts/ClassMethodInliner.cpp @@ -1,15 +1,6 @@ -#include "llvm/Pass.h" -#include "llvm/Function.h" -#include "llvm/Module.h" -#include "llvm/Instructions.h" #include "llvm/Support/CallSite.h" -#include "llvm/Constants.h" -#include "llvm/LLVMContext.h" -#include "llvm/GlobalVariable.h" -#include "llvm/Support/IRBuilder.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/InlineCost.h" -#include "llvm/DefaultPasses.h" #include "ObjectiveCOpts.h" #include "IMPCacher.h" #include <string> diff --git a/third_party/libobjc/opts/IMPCacher.cpp b/third_party/libobjc/opts/IMPCacher.cpp index b6b5f49e51..5b354c16c8 100644 --- a/third_party/libobjc/opts/IMPCacher.cpp +++ b/third_party/libobjc/opts/IMPCacher.cpp @@ -1,13 +1,5 @@ #include "llvm/Analysis/Verifier.h" #include "IMPCacher.h" -#include "llvm/Pass.h" -#include "llvm/Function.h" -#include "llvm/Module.h" -#include "llvm/Instructions.h" -#include "llvm/Constants.h" -#include "llvm/LLVMContext.h" -#include "llvm/Metadata.h" -#include "llvm/Support/IRBuilder.h" #include "llvm/Support/CallSite.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Cloning.h" diff --git a/third_party/libobjc/opts/IvarPass.cpp b/third_party/libobjc/opts/IvarPass.cpp index 648901bb38..5760963202 100644 --- a/third_party/libobjc/opts/IvarPass.cpp +++ b/third_party/libobjc/opts/IvarPass.cpp @@ -1,12 +1,5 @@ -#include "llvm/Pass.h" -#include "llvm/Function.h" -#include "llvm/Module.h" -#include "llvm/Instructions.h" -#include "llvm/GlobalAlias.h" -#include "llvm/GlobalVariable.h" -#include "llvm/Constants.h" +#include "LLVMCompat.h" #include "llvm/Analysis/Verifier.h" -#include "llvm/DefaultPasses.h" #include "llvm/ADT/DenseSet.h" #include "ObjectiveCOpts.h" #include <string> @@ -156,7 +149,6 @@ namespace { end=replacements.end() ; i != end ; ++i) { if (i->second) i->first->replaceAllUsesWith(i->second); - i->first->removeFromParent(); } verifyFunction(F); return modified; diff --git a/third_party/libobjc/opts/LLVMCompat.h b/third_party/libobjc/opts/LLVMCompat.h index 85f472ceb4..2a37344e06 100644 --- a/third_party/libobjc/opts/LLVMCompat.h +++ b/third_party/libobjc/opts/LLVMCompat.h @@ -7,11 +7,39 @@ #ifndef __LANGUAGEKIT_LLVM_HACKS__ #define __LANGUAGEKIT_LLVM_HACKS__ +#if LLVM_MAJOR < 3 || (LLVM_MAJOR >=3 && LLVM_MINOR <= 2) +#if LLVM_MAJOR < 3 || (LLVM_MAJOR >=3 && LLVM_MINOR <= 1) +#include <llvm/Support/IRBuilder.h> +#else +#include <llvm/IRBuilder.h> +#endif +#include <llvm/Function.h> +#include <llvm/Module.h> +#include <llvm/LLVMContext.h> #include <llvm/Instructions.h> #include <llvm/Metadata.h> #include <llvm/Intrinsics.h> -#include <llvm/Support/IRBuilder.h> +#include <llvm/Constants.h> +#include <llvm/GlobalAlias.h> +#include <llvm/GlobalVariable.h> +#include <llvm/DefaultPasses.h> +#else +#include <llvm/IR/IRBuilder.h> +#include <llvm/IR/Function.h> +#include <llvm/IR/Module.h> +#include <llvm/IR/LLVMContext.h> +#include <llvm/IR/Instructions.h> +#include <llvm/IR/Metadata.h> +#include <llvm/IR/Intrinsics.h> +#include <llvm/IR/Constants.h> +#include <llvm/IR/GlobalVariable.h> +#include <llvm/IR/GlobalAlias.h> +#include <llvm/PassSupport.h> +#endif +#if (LLVM_MAJOR > 3) || ((LLVM_MAJOR == 3) && (LLVM_MINOR >= 3)) +# define InlineCostAnalyzer InlineCostAnalysis +#endif // Only preserve names in a debug build. This simplifies the // IR in a release build, but makes it much harder to debug. diff --git a/third_party/libobjc/opts/LoopIMPCachePass.cpp b/third_party/libobjc/opts/LoopIMPCachePass.cpp index 21fcfe1a3a..317a04cd27 100644 --- a/third_party/libobjc/opts/LoopIMPCachePass.cpp +++ b/third_party/libobjc/opts/LoopIMPCachePass.cpp @@ -1,13 +1,6 @@ -#include "llvm/LLVMContext.h" -#include "llvm/Pass.h" -#include "llvm/Function.h" -#include "llvm/Module.h" -#include "llvm/Instructions.h" -#include "llvm/Constants.h" -#include "llvm/Support/IRBuilder.h" +#include "LLVMCompat.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/Verifier.h" -#include "llvm/DefaultPasses.h" #include "ObjectiveCOpts.h" #include "IMPCacher.h" #include <string> diff --git a/third_party/libobjc/opts/ObjectiveCOpts.cpp b/third_party/libobjc/opts/ObjectiveCOpts.cpp index 9cc64048a9..fa585f7767 100644 --- a/third_party/libobjc/opts/ObjectiveCOpts.cpp +++ b/third_party/libobjc/opts/ObjectiveCOpts.cpp @@ -1,8 +1,7 @@ -#include "llvm/Pass.h" -#include "llvm/Module.h" +#include "LLVMCompat.h" #if LLVM_MAJOR >= 3 -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/PassManager.h" +#include <llvm/Transforms/IPO/PassManagerBuilder.h> +#include <llvm/PassManager.h> #endif #include "ObjectiveCOpts.h" diff --git a/third_party/libobjc/opts/TypeFeedback.cpp b/third_party/libobjc/opts/TypeFeedback.cpp index 8c940ef54f..e2988a00f0 100644 --- a/third_party/libobjc/opts/TypeFeedback.cpp +++ b/third_party/libobjc/opts/TypeFeedback.cpp @@ -1,11 +1,5 @@ -#include "llvm/Constants.h" -#include "llvm/Pass.h" -#include "llvm/Module.h" -#include "llvm/Function.h" -#include "llvm/Instructions.h" -#include "llvm/Support/CallSite.h" -#include "llvm/Support/IRBuilder.h" -#include "llvm/Linker.h" +#include <llvm/Support/CallSite.h> +#include <llvm/Linker.h> #include <vector> using namespace llvm; @@ -131,7 +125,12 @@ namespace { ctors.size()), ctors)); // Create the new global and replace the old one GlobalVariable *NGV = new GlobalVariable(CA->getType(), - GCL->isConstant(), GCL->getLinkage(), CA, "", GCL->isThreadLocal()); + GCL->isConstant(), GCL->getLinkage(), CA, "", +#if LLVM_MAJOR < 3 || (LLVM_MAJOR == 3 && LLVM_MINOR < 2) + GCL->isThreadLocal()); +#else + GCL-> getThreadLocalMode()); +#endif GCL->getParent()->getGlobalList().insert(GCL, NGV); NGV->takeName(GCL); GCL->replaceAllUsesWith(NGV); diff --git a/third_party/libobjc/opts/TypeFeedbackDrivenInliner.cpp b/third_party/libobjc/opts/TypeFeedbackDrivenInliner.cpp index a189f03c2b..050c8be416 100644 --- a/third_party/libobjc/opts/TypeFeedbackDrivenInliner.cpp +++ b/third_party/libobjc/opts/TypeFeedbackDrivenInliner.cpp @@ -1,9 +1,4 @@ -#include "llvm/Constants.h" -#include "llvm/Pass.h" -#include "llvm/Module.h" -#include "llvm/Function.h" -#include "llvm/Instructions.h" -#include "llvm/Support/IRBuilder.h" +#include "LLVMCompat.h" #include "llvm/Analysis/InlineCost.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Linker.h" diff --git a/third_party/libobjc/opts/TypeInfoProvider.h b/third_party/libobjc/opts/TypeInfoProvider.h index cd659bf3c7..be50c362a5 100644 --- a/third_party/libobjc/opts/TypeInfoProvider.h +++ b/third_party/libobjc/opts/TypeInfoProvider.h @@ -1,12 +1,5 @@ +#include "LLVMCompat.h" #include "IMPCacher.h" -#include "llvm/Pass.h" -#include "llvm/Function.h" -#include "llvm/Module.h" -#include "llvm/Instructions.h" -#include "llvm/Constants.h" -#include "llvm/LLVMContext.h" -#include "llvm/Metadata.h" -#include "llvm/Support/IRBuilder.h" #include "llvm/Support/CallSite.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Cloning.h" diff --git a/third_party/libobjc/pool.h b/third_party/libobjc/pool.h index ffa8a6ad7c..f0bd5451e7 100644 --- a/third_party/libobjc/pool.h +++ b/third_party/libobjc/pool.h @@ -29,13 +29,17 @@ static mutex_t NAME(_lock); #define UNLOCK_POOL() #endif +static int pool_size = 0; +static int pool_allocs = 0; static inline POOL_TYPE*NAME(_pool_alloc)(void) { LOCK_POOL(); + pool_allocs++; if (0 > NAME(_pool_next_index)) { NAME(_pool) = malloc(PAGE_SIZE); NAME(_pool_next_index) = POOL_SIZE - 1; + pool_size += PAGE_SIZE; } POOL_TYPE* new = &NAME(_pool)[NAME(_pool_next_index)--]; UNLOCK_POOL(); diff --git a/third_party/libobjc/properties.h b/third_party/libobjc/properties.h index 9500ac56ee..a9ac581384 100644 --- a/third_party/libobjc/properties.h +++ b/third_party/libobjc/properties.h @@ -40,6 +40,50 @@ enum PropertyAttributeKind OBJC_PR_setter = (1<<7) }; +/** + * Flags in the second attributes field in declared properties. + * Note: This field replaces the old 'is synthesized' field and so these values + * are shifted left one from their values in clang. + */ +enum PropertyAttributeKind2 +{ + /** + * No extended attributes. + */ + OBJC_PR_noextattr = 0, + /** + * The property is synthesized. This has no meaning in properties on + * protocols. + */ + OBJC_PR_synthesized = (1<<0), + /** + * The property is dynamic (i.e. the implementation is inherited or + * provided at run time). + */ + OBJC_PR_dynamic = (1<<1), + /** + * This property belongs to a protocol. + */ + OBJC_PR_protocol = OBJC_PR_synthesized | OBJC_PR_dynamic, + /** + * The property is atomic. + */ + OBJC_PR_atomic = (1<<2), + /** + * The property value is a zeroing weak reference. + */ + OBJC_PR_weak = (1<<3), + /** + * The property value is strong (retained). Currently, this is equivalent + * to the strong attribute. + */ + OBJC_PR_strong = (1<<4), + /** + * The property value is just copied. + */ + OBJC_PR_unsafe_unretained = (1<<5), +}; + /** * Structure used for property enumeration. Note that property enumeration is * currently quite broken on OS X, so achieving full compatibility there is @@ -60,7 +104,17 @@ struct objc_property /** * Flag set if the property is synthesized. */ - const char isSynthesized; + char attributes2; + /** + * Padding field. These were implicit in the structure field alignment + * (four more on 64-bit platforms), but we'll make them explicit now for + * future use. + */ + char unused1; + /** + * More padding. + */ + char unused2; /** * Name of the getter for this property. */ @@ -99,7 +153,16 @@ struct objc_property_list }; /** - * Constructs a property description from a list of attributes. + * Constructs a property description from a list of attributes, returning the + * instance variable name via the third parameter. */ PRIVATE struct objc_property propertyFromAttrs(const objc_property_attribute_t *attributes, - unsigned int attributeCount); + unsigned int attributeCount, + const char **iVarName); + +/** + * Constructs and installs a property attribute string from the property + * attributes and, optionally, an ivar string. + */ +PRIVATE const char *constructPropertyAttributes(objc_property_t property, + const char *iVarName); diff --git a/third_party/libobjc/properties.m b/third_party/libobjc/properties.m index fe4344d996..36930a9083 100644 --- a/third_party/libobjc/properties.m +++ b/third_party/libobjc/properties.m @@ -14,6 +14,11 @@ PRIVATE int spinlocks[spinlock_count]; +static inline BOOL checkAttribute(char field, int attr) +{ + return (field & attr) == attr; +} + /** * Public function for getting a property. */ @@ -84,6 +89,82 @@ void objc_setProperty(id obj, SEL _cmd, ptrdiff_t offset, id arg, BOOL isAtomic, objc_release(old); } +void objc_setProperty_atomic(id obj, SEL _cmd, id arg, ptrdiff_t offset) +{ + char *addr = (char*)obj; + addr += offset; + arg = objc_retain(arg); + volatile int *lock = lock_for_pointer(addr); + lock_spinlock(lock); + id old = *(id*)addr; + *(id*)addr = arg; + unlock_spinlock(lock); + objc_release(old); +} + +void objc_setProperty_atomic_copy(id obj, SEL _cmd, id arg, ptrdiff_t offset) +{ + char *addr = (char*)obj; + addr += offset; + + arg = [arg copy]; + volatile int *lock = lock_for_pointer(addr); + lock_spinlock(lock); + id old = *(id*)addr; + *(id*)addr = arg; + unlock_spinlock(lock); + objc_release(old); +} + +void objc_setProperty_nonatomic(id obj, SEL _cmd, id arg, ptrdiff_t offset) +{ + char *addr = (char*)obj; + addr += offset; + arg = objc_retain(arg); + id old = *(id*)addr; + *(id*)addr = arg; + objc_release(old); +} + +void objc_setProperty_nonatomic_copy(id obj, SEL _cmd, id arg, ptrdiff_t offset) +{ + char *addr = (char*)obj; + addr += offset; + id old = *(id*)addr; + *(id*)addr = [arg copy]; + objc_release(old); +} + +void objc_copyCppObjectAtomic(void *dest, const void *src, + void (*copyHelper) (void *dest, const void *source)) +{ + volatile int *lock = lock_for_pointer(src < dest ? src : dest); + volatile int *lock2 = lock_for_pointer(src < dest ? dest : src); + lock_spinlock(lock); + lock_spinlock(lock2); + copyHelper(dest, src); + unlock_spinlock(lock); + unlock_spinlock(lock2); +} + +void objc_getCppObjectAtomic(void *dest, const void *src, + void (*copyHelper) (void *dest, const void *source)) +{ + volatile int *lock = lock_for_pointer(src); + lock_spinlock(lock); + copyHelper(dest, src); + unlock_spinlock(lock); +} + +void objc_setCppObjectAtomic(void *dest, const void *src, + void (*copyHelper) (void *dest, const void *source)) +{ + volatile int *lock = lock_for_pointer(dest); + lock_spinlock(lock); + copyHelper(dest, src); + unlock_spinlock(lock); +} + /** * Structure copy function. This is provided for compatibility with the Apple * APIs (it's an ABI function, so it's semi-public), but it's a bad design so @@ -99,8 +180,8 @@ void objc_copyPropertyStruct(void *dest, { if (atomic) { - volatile int *lock = lock_for_pointer(src); - volatile int *lock2 = lock_for_pointer(src); + volatile int *lock = lock_for_pointer(src < dest ? src : dest); + volatile int *lock2 = lock_for_pointer(src < dest ? dest : src); lock_spinlock(lock); lock_spinlock(lock2); memcpy(dest, src, size); @@ -173,7 +254,7 @@ objc_property_t class_getProperty(Class cls, const char *name) for (int i=0 ; i<properties->count ; i++) { objc_property_t p = &properties->properties[i]; - if (strcmp(p->name, name) == 0) + if (strcmp(property_getName(p), name) == 0) { return p; } @@ -210,17 +291,33 @@ objc_property_t* class_copyPropertyList(Class cls, unsigned int *outCount) { for (int i=0 ; i<properties->count ; i++) { - list[out] = &l->properties[i]; + list[out++] = &l->properties[i]; } } return list; } +static const char* property_getIVar(objc_property_t property) { + const char *iVar = property_getAttributes(property); + if (iVar != 0) + { + while ((*iVar != 0) && (*iVar != 'V')) + { + iVar++; + } + if (*iVar == 'V') + { + return iVar+1; + } + } + return 0; +} const char *property_getName(objc_property_t property) { if (NULL == property) { return NULL; } const char *name = property->name; + if (NULL == name) { return NULL; } if (name[0] == 0) { name += name[1]; @@ -238,6 +335,7 @@ PRIVATE size_t lengthOfTypeEncoding(const char *types); static const char *property_getTypeEncoding(objc_property_t property) { if (NULL == property) { return NULL; } + if (NULL == property->getter_types) { return NULL; } const char *name = property->getter_types; if (name[0] == 0) @@ -256,39 +354,44 @@ static const char *property_getTypeEncoding(objc_property_t property) return &property->getter_types[1]; } -const char *property_getAttributes(objc_property_t property) +PRIVATE const char *constructPropertyAttributes(objc_property_t property, + const char *iVarName) { - if (NULL == property) { return NULL; } - const char *name = (char*)property->name; - if (name[0] == 0) - { - return name + 2; - } - const char *typeEncoding = property_getTypeEncoding(property); - size_t typeSize = strlen(typeEncoding); - size_t nameSize = strlen(property->name); + size_t typeSize = (NULL == typeEncoding) ? 0 : strlen(typeEncoding); + size_t nameSize = (NULL == name) ? 0 : strlen(name); + size_t iVarNameSize = (NULL == iVarName) ? 0 : strlen(iVarName); // Encoding is T{type},V{name}, so 4 bytes for the "T,V" that we always // need. We also need two bytes for the leading null and the length. size_t encodingSize = typeSize + nameSize + 6; - char flags[16]; + char flags[20]; size_t i = 0; // Flags that are a comma then a character - if ((property->attributes & OBJC_PR_readonly) == OBJC_PR_readonly) + if (checkAttribute(property->attributes, OBJC_PR_readonly)) { flags[i++] = ','; flags[i++] = 'R'; } - if ((property->attributes & OBJC_PR_copy) == OBJC_PR_copy) + if (checkAttribute(property->attributes, OBJC_PR_retain)) + { + flags[i++] = ','; + flags[i++] = '&'; + } + if (checkAttribute(property->attributes, OBJC_PR_copy)) { flags[i++] = ','; flags[i++] = 'C'; } - if ((property->attributes & OBJC_PR_retain) == OBJC_PR_retain) + if (checkAttribute(property->attributes2, OBJC_PR_weak)) { flags[i++] = ','; - flags[i++] = '&'; + flags[i++] = 'W'; + } + if (checkAttribute(property->attributes2, OBJC_PR_dynamic)) + { + flags[i++] = ','; + flags[i++] = 'D'; } if ((property->attributes & OBJC_PR_nonatomic) == OBJC_PR_nonatomic) { @@ -309,36 +412,67 @@ const char *property_getAttributes(objc_property_t property) setterLength = strlen(property->setter_name); encodingSize += 2 + setterLength; } + if (NULL != iVarName) + { + encodingSize += 2 + iVarNameSize; + } unsigned char *encoding = malloc(encodingSize); // Set the leading 0 and the offset of the name unsigned char *insert = encoding; + BOOL needsComma = NO; *(insert++) = 0; *(insert++) = 0; // Set the type encoding - *(insert++) = 'T'; - memcpy(insert, typeEncoding, typeSize); - insert += typeSize; + if (NULL != typeEncoding) + { + *(insert++) = 'T'; + memcpy(insert, typeEncoding, typeSize); + insert += typeSize; + needsComma = YES; + } // Set the flags memcpy(insert, flags, i); insert += i; if ((property->attributes & OBJC_PR_getter) == OBJC_PR_getter) { - *(insert++) = ','; + if (needsComma) + { + *(insert++) = ','; + } + i++; + needsComma = YES; *(insert++) = 'G'; memcpy(insert, property->getter_name, getterLength); insert += getterLength; } if ((property->attributes & OBJC_PR_setter) == OBJC_PR_setter) { - *(insert++) = ','; + if (needsComma) + { + *(insert++) = ','; + } + i++; + needsComma = YES; *(insert++) = 'S'; memcpy(insert, property->setter_name, setterLength); insert += setterLength; } - *(insert++) = ','; - *(insert++) = 'V'; + // If the instance variable name is the same as the property name, then we + // use the same string for both, otherwise we write the ivar name in the + // attributes string and then a null and then the name. + if (NULL != iVarName) + { + if (needsComma) + { + *(insert++) = ','; + } + *(insert++) = 'V'; + memcpy(insert, iVarName, iVarNameSize); + insert += iVarNameSize; + } + *(insert++) = '\0'; encoding[1] = (unsigned char)(uintptr_t)(insert - encoding); - memcpy(insert, property->name, nameSize); + memcpy(insert, name, nameSize); insert += nameSize; *insert = '\0'; // If another thread installed the encoding string while we were computing @@ -351,28 +485,66 @@ const char *property_getAttributes(objc_property_t property) return (const char*)(encoding + 2); } + +const char *property_getAttributes(objc_property_t property) +{ + if (NULL == property) { return NULL; } + + const char *name = (char*)property->name; + if (name[0] == 0) + { + return name + 2; + } + return constructPropertyAttributes(property, NULL); +} + + objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount) { if (NULL == property) { return NULL; } - objc_property_attribute_t attrs[10]; + objc_property_attribute_t attrs[12]; int count = 0; - attrs[count].name = "T"; - attrs[count].value = property_getTypeEncoding(property); - count++; - if ((property->attributes & OBJC_PR_copy) == OBJC_PR_copy) + const char *types = property_getTypeEncoding(property); + if (NULL != types) + { + attrs[count].name = "T"; + attrs[count].value = types; + count++; + } + if (checkAttribute(property->attributes, OBJC_PR_readonly)) + { + attrs[count].name = "R"; + attrs[count].value = ""; + count++; + } + if (checkAttribute(property->attributes, OBJC_PR_copy)) { attrs[count].name = "C"; attrs[count].value = ""; count++; } - if ((property->attributes & OBJC_PR_retain) == OBJC_PR_retain) + if (checkAttribute(property->attributes, OBJC_PR_retain) || + checkAttribute(property->attributes2, OBJC_PR_strong)) { attrs[count].name = "&"; attrs[count].value = ""; count++; } + if (checkAttribute(property->attributes2, OBJC_PR_dynamic) && + !checkAttribute(property->attributes2, OBJC_PR_synthesized)) + { + attrs[count].name = "D"; + attrs[count].value = ""; + count++; + } + if (checkAttribute(property->attributes2, OBJC_PR_weak)) + { + attrs[count].name = "W"; + attrs[count].value = ""; + count++; + } if ((property->attributes & OBJC_PR_nonatomic) == OBJC_PR_nonatomic) { attrs[count].name = "N"; @@ -391,9 +563,13 @@ objc_property_attribute_t *property_copyAttributeList(objc_property_t property, attrs[count].value = property->setter_name; count++; } - attrs[count].name = "V"; - attrs[count].value = property_getName(property); - count++; + const char *name = property_getIVar(property); + if (name != NULL) + { + attrs[count].name = "V"; + attrs[count].value = name; + count++; + } objc_property_attribute_t *propAttrs = calloc(sizeof(objc_property_attribute_t), count); memcpy(propAttrs, attrs, count * sizeof(objc_property_attribute_t)); @@ -405,7 +581,8 @@ objc_property_attribute_t *property_copyAttributeList(objc_property_t property, } PRIVATE struct objc_property propertyFromAttrs(const objc_property_attribute_t *attributes, - unsigned int attributeCount) + unsigned int attributeCount, + const char **name) { struct objc_property p = { 0 }; for (unsigned int i=0 ; i<attributeCount ; i++) @@ -434,20 +611,38 @@ PRIVATE struct objc_property propertyFromAttrs(const objc_property_attribute_t * } case 'V': { - p.name = strdup(attributes[i].value); + *name = attributes[i].value; break; } case 'C': { p.attributes |= OBJC_PR_copy; + break; + } + case 'R': + { + p.attributes |= OBJC_PR_readonly; + break; + } + case 'W': + { + p.attributes2 |= OBJC_PR_weak; + break; } case '&': { p.attributes |= OBJC_PR_retain; + break; } case 'N': { p.attributes |= OBJC_PR_nonatomic; + break; + } + case 'D': + { + p.attributes2 |= OBJC_PR_dynamic; + break; } } } @@ -460,13 +655,16 @@ BOOL class_addProperty(Class cls, unsigned int attributeCount) { if ((Nil == cls) || (NULL == name) || (class_getProperty(cls, name) != 0)) { return NO; } - struct objc_property p = propertyFromAttrs(attributes, attributeCount); - // If there is a name mismatch, the attributes are invalid. - if ((p.name != 0) && (strcmp(name, p.name) != 0)) { return NO; } + const char *iVarname = NULL; + struct objc_property p = propertyFromAttrs(attributes, attributeCount, &iVarname); + // If the iVar name is not the same as the name, then we need to construct + // the attributes string now, otherwise we can construct it lazily. + p.name = name; + constructPropertyAttributes(&p, iVarname); struct objc_property_list *l = calloc(1, sizeof(struct objc_property_list) + sizeof(struct objc_property)); - l->count = 0; + l->count = 1; memcpy(&l->properties, &p, sizeof(struct objc_property)); LOCK_RUNTIME_FOR_SCOPE(); l->next = cls->properties; @@ -486,12 +684,12 @@ void class_replaceProperty(Class cls, class_addProperty(cls, name, attributes, attributeCount); return; } - struct objc_property p = propertyFromAttrs(attributes, attributeCount); + const char *iVarname = 0; + struct objc_property p = propertyFromAttrs(attributes, attributeCount, &iVarname); + p.name = name; + LOCK_RUNTIME_FOR_SCOPE(); + constructPropertyAttributes(&p, iVarname); memcpy(old, &p, sizeof(struct objc_property)); - if (NULL == old->name) - { - old->name = name; - } } char *property_copyAttributeValue(objc_property_t property, const char *attributeName) @@ -501,11 +699,17 @@ char *property_copyAttributeValue(objc_property_t property, { case 'T': { - return strdup(property_getTypeEncoding(property)); + const char *types = property_getTypeEncoding(property); + return (NULL == types) ? NULL : strdup(types); + } + case 'D': + { + return checkAttribute(property->attributes2, OBJC_PR_dynamic) && + !checkAttribute(property->attributes2, OBJC_PR_synthesized) ? strdup("") : 0; } case 'V': { - return strdup(property_getName(property)); + return strdup(property_getIVar(property)); } case 'S': { @@ -515,17 +719,26 @@ char *property_copyAttributeValue(objc_property_t property, { return strdup(property->getter_name); } + case 'R': + { + return checkAttribute(property->attributes, OBJC_PR_readonly) ? strdup("") : 0; + } + case 'W': + { + return checkAttribute(property->attributes2, OBJC_PR_weak) ? strdup("") : 0; + } case 'C': { - return ((property->attributes |= OBJC_PR_copy) == OBJC_PR_copy) ? strdup("") : 0; + return checkAttribute(property->attributes, OBJC_PR_copy) ? strdup("") : 0; } case '&': { - return ((property->attributes |= OBJC_PR_retain) == OBJC_PR_retain) ? strdup("") : 0; + return checkAttribute(property->attributes, OBJC_PR_retain) || + checkAttribute(property->attributes2, OBJC_PR_strong) ? strdup("") : 0; } case 'N': { - return ((property->attributes |= OBJC_PR_nonatomic) == OBJC_PR_nonatomic) ? strdup("") : 0; + return checkAttribute(property->attributes, OBJC_PR_nonatomic) ? strdup("") : 0; } } return 0; diff --git a/third_party/libobjc/protocol.c b/third_party/libobjc/protocol.c index e9b80ce93c..63e5c92fea 100644 --- a/third_party/libobjc/protocol.c +++ b/third_party/libobjc/protocol.c @@ -11,7 +11,7 @@ // Get the functions for string hashing #include "string_hash.h" -static int protocol_compare(const char *name, +static int protocol_compare(const char *name, const struct objc_protocol2 *protocol) { return string_compare(name, protocol->name); @@ -31,7 +31,7 @@ static protocol_table *known_protocol_table; void init_protocol_table(void) { protocol_initialize(&known_protocol_table, 128); -} +} static void protocol_table_insert(const struct objc_protocol2 *protocol) { @@ -47,10 +47,10 @@ static id ObjC2ProtocolClass = 0; static int isEmptyProtocol(struct objc_protocol2 *aProto) { - int isEmpty = - ((aProto->instance_methods == NULL) || + int isEmpty = + ((aProto->instance_methods == NULL) || (aProto->instance_methods->count == 0)) && - ((aProto->class_methods == NULL) || + ((aProto->class_methods == NULL) || (aProto->class_methods->count == 0)) && ((aProto->protocol_list == NULL) || (aProto->protocol_list->count == 0)); @@ -67,7 +67,7 @@ static int isEmptyProtocol(struct objc_protocol2 *aProto) // FIXME: Make p1 adopt all of the stuff in p2 static void makeProtocolEqualToProtocol(struct objc_protocol2 *p1, - struct objc_protocol2 *p2) + struct objc_protocol2 *p2) { #define COPY(x) p1->x = p2->x COPY(instance_methods); @@ -90,7 +90,7 @@ static struct objc_protocol2 *unique_protocol(struct objc_protocol2 *aProto) { ObjC2ProtocolClass = objc_getClass("Protocol2"); } - struct objc_protocol2 *oldProtocol = + struct objc_protocol2 *oldProtocol = protocol_for_name(aProto->name); if (NULL == oldProtocol) { @@ -166,7 +166,7 @@ static BOOL init_protocols(struct objc_protocol_list *protocols) // Protocols in the protocol list have their class pointers set to the // version of the protocol class that they expect. - enum protocol_version version = + enum protocol_version version = (enum protocol_version)(uintptr_t)aProto->isa; switch (version) { @@ -309,17 +309,16 @@ get_method_list(Protocol *p, struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *count) { - if (NULL == p) { return NULL; } - struct objc_method_description_list *list = + if ((NULL == p) || (NULL == count)){ return NULL; } + struct objc_method_description_list *list = get_method_list(p, isRequiredMethod, isInstanceMethod); *count = 0; if (NULL == list || list->count == 0) { return NULL; } *count = list->count; - struct objc_method_description *out = - calloc(sizeof(struct objc_method_description_list), list->count); - - for (int i=0 ; i<list->count ; i++) + struct objc_method_description *out = + calloc(sizeof(struct objc_method_description), list->count); + for (int i=0 ; i < (list->count) ; i++) { out[i].name = sel_registerTypedName_np(list->methods[i].name, list->methods[i].types); @@ -403,14 +402,14 @@ objc_property_t protocol_getProperty(Protocol *protocol, return NULL; } Protocol2 *p = (Protocol2*)protocol; - struct objc_property_list *properties = + struct objc_property_list *properties = isRequiredProperty ? p->properties : p->optional_properties; while (NULL != properties) { for (int i=0 ; i<properties->count ; i++) { objc_property_t prop = &properties->properties[i]; - if (strcmp(prop->name, name) == 0) + if (strcmp(property_getName(prop), name) == 0) { return prop; } @@ -421,20 +420,20 @@ objc_property_t protocol_getProperty(Protocol *protocol, } -struct objc_method_description +struct objc_method_description protocol_getMethodDescription(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod) { struct objc_method_description d = {0,0}; - struct objc_method_description_list *list = + struct objc_method_description_list *list = get_method_list(p, isRequiredMethod, isInstanceMethod); if (NULL == list) { return d; } - // TODO: We could make this much more efficient if + // TODO: We could make this much more efficient if for (int i=0 ; i<list->count ; i++) { SEL s = sel_registerTypedName_np(list->methods[i].name, 0); @@ -464,7 +463,7 @@ BOOL protocol_isEqual(Protocol *p, Protocol *other) { return NO; } - if (p == other || + if (p == other || p->name == other->name || 0 == strcmp(p->name, other->name)) { @@ -609,8 +608,10 @@ void protocol_addProperty(Protocol *aProtocol, } struct objc_property_list *list = *listPtr; int index = list->count-1; - struct objc_property p = propertyFromAttrs(attributes, attributeCount); - p.name = strdup(name); + const char *iVarName = NULL; + struct objc_property p = propertyFromAttrs(attributes, attributeCount, &iVarName); + p.name = name; + constructPropertyAttributes(&p, iVarName); memcpy(&(list->properties[index]), &p, sizeof(p)); } diff --git a/third_party/libobjc/runtime.c b/third_party/libobjc/runtime.c index 627be9a237..d0db7138cf 100644 --- a/third_party/libobjc/runtime.c +++ b/third_party/libobjc/runtime.c @@ -52,7 +52,7 @@ static void call_cxx_construct_for_class(Class cls, id obj) static SEL cxx_construct; if (NULL == cxx_construct) { - cxx_construct = sel_registerName(".cxx_contruct"); + cxx_construct = sel_registerName(".cxx_construct"); } struct objc_slot *slot = objc_get_slot(cls, cxx_construct); if (NULL != slot) @@ -570,7 +570,7 @@ IMP method_setImplementation(Method method, IMP imp) { if (NULL == method) { return (IMP)NULL; } IMP old = (IMP)method->imp; - method->imp = old; + method->imp = imp; objc_updateDtableForClassContainingMethod(method); return old; } @@ -698,6 +698,8 @@ Class objc_allocateClassPair(Class superclass, const char *name, size_t extraByt // Initialize the metaclass // Set the meta-metaclass pointer to the name. The runtime will fix this // in objc_resolve_class(). + // If the superclass is not yet resolved, then we need to look it up + // via the class table. metaClass->isa = (Class)superclass->isa->isa->name; metaClass->super_class = superclass->isa; } @@ -778,9 +780,12 @@ const char *object_getClassName(id obj) return class_getName(object_getClass(obj)); } +PRIVATE void objc_resolve_class(Class); + void objc_registerClassPair(Class cls) { LOCK_RUNTIME_FOR_SCOPE(); class_table_insert(cls); + objc_resolve_class(cls); } diff --git a/third_party/libobjc/sarray2.c b/third_party/libobjc/sarray2.c index b9ca967fa6..c3c79d8f4a 100644 --- a/third_party/libobjc/sarray2.c +++ b/third_party/libobjc/sarray2.c @@ -8,6 +8,12 @@ static void *EmptyArrayData[256]; static SparseArray EmptyArray = { 0xff, 0, 0, (void**)&EmptyArrayData}; +static void *EmptyArrayData8[256] = { [0 ... 255] = &EmptyArray }; +static SparseArray EmptyArray8 = { 0xff00, 8, 0, (void**)&EmptyArrayData8}; +static void *EmptyArrayData16[256] = { [0 ... 255] = &EmptyArray8 }; +static SparseArray EmptyArray16 = { 0xff0000, 16, 0, (void**)&EmptyArrayData16}; +static void *EmptyArrayData24[256] = { [0 ... 255] = &EmptyArray16 }; +static SparseArray EmptyArray24 = { 0xff0000, 24, 0, (void**)&EmptyArrayData24}; #define MAX_INDEX(sarray) (sarray->mask >> sarray->shift) #define DATA_SIZE(sarray) ((sarray->mask >> sarray->shift) + 1) @@ -17,17 +23,35 @@ static SparseArray EmptyArray = { 0xff, 0, 0, (void**)&EmptyArrayData}; #define base_shift 8 #define base_mask ((1<<base_shift) - 1) +void *EmptyChildForShift(uint32_t shift) +{ + switch(shift) + { + default: UNREACHABLE("Broken sparse array"); + case 8: + return &EmptyArray; + case 16: + return &EmptyArray8; + case 24: + return &EmptyArray16; + case 32: + return &EmptyArray24; + } +} + static void init_pointers(SparseArray * sarray) { sarray->data = calloc(DATA_SIZE(sarray), sizeof(void*)); if(sarray->shift != 0) { + void *data = EmptyChildForShift(sarray->shift); for(unsigned i=0 ; i<=MAX_INDEX(sarray) ; i++) { - sarray->data[i] = &EmptyArray; + sarray->data[i] = data; } } } + PRIVATE SparseArray * SparseArrayNewWithDepth(uint32_t depth) { SparseArray * sarray = calloc(1, sizeof(SparseArray)); @@ -42,8 +66,13 @@ PRIVATE SparseArray *SparseArrayNew() { return SparseArrayNewWithDepth(32); } -PRIVATE SparseArray *SparseArrayExpandingArray(SparseArray *sarray) +PRIVATE SparseArray *SparseArrayExpandingArray(SparseArray *sarray, uint32_t new_depth) { + if (new_depth == sarray->shift) + { + return sarray; + } + assert(new_depth > sarray->shift); // Expanding a child sarray has undefined results. assert(sarray->refCount == 1); SparseArray *new = calloc(1, sizeof(SparseArray)); @@ -51,12 +80,12 @@ PRIVATE SparseArray *SparseArrayExpandingArray(SparseArray *sarray) new->shift = sarray->shift; new->mask = sarray->mask; void **newData = malloc(DATA_SIZE(sarray) * sizeof(void*)); - for(unsigned i=0 ; i<=MAX_INDEX(sarray) ; i++) + void *data = EmptyChildForShift(new->shift + 8); + for(unsigned i=1 ; i<=MAX_INDEX(sarray) ; i++) { - newData[i] = &EmptyArray; + newData[i] = data; } new->data = sarray->data; - // new is now an exact copy of sarray. newData[0] = new; sarray->data = newData; // Now, any lookup in sarray for any value less than its capacity will have @@ -65,7 +94,7 @@ PRIVATE SparseArray *SparseArrayExpandingArray(SparseArray *sarray) sarray->shift += base_shift; // Finally, set the mask to the correct value. Now all lookups should work. sarray->mask <<= base_shift; - return new; + return sarray; } static void *SparseArrayFind(SparseArray * sarray, uint32_t * index) @@ -86,27 +115,34 @@ static void *SparseArrayFind(SparseArray * sarray, uint32_t * index) } else while (j<max) { + // If the shift is not 0, then we need to recursively look at child + // nodes. uint32_t zeromask = ~(sarray->mask >> base_shift); while (j<max) { //Look in child nodes - if (sarray->data[j] != SARRAY_EMPTY) - { - void * ret = SparseArrayFind(sarray->data[j], index); - if (ret != SARRAY_EMPTY) - { - return ret; - } - // The recursive call will set index to the correct value for - // the next index, but won't update j - } - else + SparseArray *child = sarray->data[j]; + // Skip over known-empty children + if ((&EmptyArray == child) || + (&EmptyArray8 == child) || + (&EmptyArray16 == child) || + (&EmptyArray24 == child)) { //Add 2^n to index so j is still correct (*index) += 1<<sarray->shift; //Zero off the next component of the index so we don't miss any. *index &= zeromask; } + else + { + // The recursive call will set index to the correct value for + // the next index, but won't update j + void * ret = SparseArrayFind(child, index); + if (ret != SARRAY_EMPTY) + { + return ret; + } + } //Go to the next child j++; } @@ -126,7 +162,10 @@ PRIVATE void SparseArrayInsert(SparseArray * sarray, uint32_t index, void *value { uint32_t i = MASK_INDEX(index); SparseArray *child = sarray->data[i]; - if(&EmptyArray == child) + if ((&EmptyArray == child) || + (&EmptyArray8 == child) || + (&EmptyArray16 == child) || + (&EmptyArray24 == child)) { // Insert missing nodes SparseArray * newsarray = calloc(1, sizeof(SparseArray)); @@ -185,6 +224,8 @@ PRIVATE void SparseArrayDestroy(SparseArray * sarray) { // Don't really delete this sarray if its ref count is > 0 if (sarray == &EmptyArray || + sarray == &EmptyArray8 || + sarray == &EmptyArray16 || (__sync_sub_and_fetch(&sarray->refCount, 1) > 0)) { return; @@ -202,3 +243,24 @@ PRIVATE void SparseArrayDestroy(SparseArray * sarray) free(sarray); } +PRIVATE int SparseArraySize(SparseArray *sarray) +{ + int size = 0; + if (sarray->shift == 0) + { + return 256*sizeof(void*) + sizeof(SparseArray); + } + size += 256*sizeof(void*) + sizeof(SparseArray); + for(unsigned i=0 ; i<=MAX_INDEX(sarray) ; i++) + { + SparseArray *child = sarray->data[i]; + if (child == &EmptyArray || + child == &EmptyArray8 || + child == &EmptyArray16) + { + continue; + } + size += SparseArraySize(child); + } + return size; +} diff --git a/third_party/libobjc/sarray2.h b/third_party/libobjc/sarray2.h index ce1d41a850..cefc521574 100644 --- a/third_party/libobjc/sarray2.h +++ b/third_party/libobjc/sarray2.h @@ -112,7 +112,7 @@ SparseArray *SparseArrayNewWithDepth(uint32_t depth); * Returns a new sparse array created by adding this one as the first child * node in an expanded one. */ -SparseArray *SparseArrayExpandingArray(SparseArray *sarray); +SparseArray *SparseArrayExpandingArray(SparseArray *sarray, uint32_t new_depth); /** * Insert a value at the specified index. */ @@ -136,4 +136,9 @@ void * SparseArrayNext(SparseArray * sarray, uint32_t * index); */ SparseArray *SparseArrayCopy(SparseArray * sarray); +/** + * Returns the total memory usage of a sparse array. + */ +int SparseArraySize(SparseArray *sarray); + #endif //_SARRAY_H_INCLUDED_ diff --git a/third_party/libobjc/selector_table.c b/third_party/libobjc/selector_table.c index d75e1c494c..13b6ca5278 100644 --- a/third_party/libobjc/selector_table.c +++ b/third_party/libobjc/selector_table.c @@ -22,7 +22,6 @@ # define TDD(x) #endif -#define fprintf(...) // Define the pool allocator for selectors. This is a simple bump-the-pointer @@ -43,6 +42,12 @@ static uint32_t selector_count = 1; */ PRIVATE SparseArray *selector_list = NULL; +#ifdef DEBUG_SELECTOR_TABLE +#define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__) +#else +#define DEBUG_LOG(...) +#endif + // Get the functions for string hashing #include "string_hash.h" @@ -156,7 +161,7 @@ static int selector_identical(const void *k, const SEL value) { SEL key = (SEL)k; - fprintf(stderr, "Comparing %s %s, %s %s\n", sel_getNameNonUnique(key), sel_getNameNonUnique(value), sel_getType_np(key), sel_getType_np(value)); + DEBUG_LOG("Comparing %s %s, %s %s\n", sel_getNameNonUnique(key), sel_getNameNonUnique(value), sel_getType_np(key), sel_getType_np(value)); return string_compare(sel_getNameNonUnique(key), sel_getNameNonUnique(value)) && selector_types_equal(sel_getType_np(key), sel_getType_np(value)); } @@ -211,6 +216,7 @@ static inline uint32_t hash_selector(const void *s) } #define MAP_TABLE_NAME selector +#define MAP_TABLE_SINGLE_THREAD #define MAP_TABLE_COMPARE_FUNCTION selector_identical #define MAP_TABLE_HASH_KEY hash_selector #define MAP_TABLE_HASH_VALUE hash_selector @@ -225,6 +231,20 @@ static selector_table *sel_table; */ mutex_t selector_table_lock; +static int selector_name_copies; + +PRIVATE void log_selector_memory_usage(void) +{ + fprintf(stderr, "%d bytes in selector name list.\n", SparseArraySize(selector_list)); + fprintf(stderr, "%d bytes in selector names.\n", selector_name_copies); + fprintf(stderr, "%d bytes (%d entries) in selector hash table.\n", (int)(sel_table->table_size * + sizeof(struct selector_table_cell_struct)), sel_table->table_size); + fprintf(stderr, "%d selectors registered.\n", selector_count); + fprintf(stderr, "%d hash table cells per selector (%.2f%% full)\n", sel_table->table_size / selector_count, ((float)selector_count) / sel_table->table_size * 100); +} + + + /** * Resizes the dtables to ensure that they can store as many selectors as @@ -245,11 +265,12 @@ PRIVATE void init_selector_tables() static SEL selector_lookup(const char *name, const char *types) { struct objc_selector sel = {{name}, types}; + LOCK_FOR_SCOPE(&selector_table_lock); return selector_table_get(sel_table, &sel); } static inline void add_selector_to_table(SEL aSel, int32_t uid, uint32_t idx) { - //fprintf(stderr, "Sel %s uid: %d, idx: %d, hash: %d\n", sel_getNameNonUnique(aSel), uid, idx, hash_selector(aSel)); + DEBUG_LOG("Sel %s uid: %d, idx: %d, hash: %d\n", sel_getNameNonUnique(aSel), uid, idx, hash_selector(aSel)); struct sel_type_list *typeList = (struct sel_type_list *)selector_pool_alloc(); typeList->value = aSel->name; @@ -269,7 +290,7 @@ static inline void register_selector_locked(SEL aSel) uintptr_t idx = selector_count++; if (NULL == aSel->types) { - fprintf(stderr, "Registering selector %d %s\n", (int)idx, sel_getNameNonUnique(aSel)); + DEBUG_LOG("Registering selector %d %s\n", (int)idx, sel_getNameNonUnique(aSel)); add_selector_to_table(aSel, idx, idx); objc_resize_dtables(selector_count); return; @@ -281,7 +302,7 @@ static inline void register_selector_locked(SEL aSel) untyped = selector_pool_alloc(); untyped->name = aSel->name; untyped->types = 0; - fprintf(stderr, "Registering selector %d %s\n", (int)idx, sel_getNameNonUnique(aSel)); + DEBUG_LOG("Registering selector %d %s\n", (int)idx, sel_getNameNonUnique(aSel)); add_selector_to_table(untyped, idx, idx); // If we are in type dependent dispatch mode, the uid for the typed // and untyped versions will be different @@ -294,7 +315,7 @@ static inline void register_selector_locked(SEL aSel) } uintptr_t uid = (uintptr_t)untyped->name; TDD(uid = idx); - fprintf(stderr, "Registering typed selector %d %s %s\n", (int)uid, sel_getNameNonUnique(aSel), sel_getType_np(aSel)); + DEBUG_LOG("Registering typed selector %d %s %s\n", (int)uid, sel_getNameNonUnique(aSel), sel_getType_np(aSel)); add_selector_to_table(aSel, uid, idx); // Add this set of types to the list. @@ -338,10 +359,10 @@ static SEL objc_register_selector_copy(SEL aSel, BOOL copyArgs) { // If an identical selector is already registered, return it. SEL copy = selector_lookup(aSel->name, aSel->types); - //fprintf(stderr, "Checking if old selector is registered: %d (%d)\n", NULL != copy ? selector_equal(aSel, copy) : 0, ((NULL != copy) && selector_equal(aSel, copy))); + DEBUG_LOG("Checking if old selector is registered: %d (%d)\n", NULL != copy ? selector_equal(aSel, copy) : 0, ((NULL != copy) && selector_equal(aSel, copy))); if ((NULL != copy) && selector_identical(aSel, copy)) { - //fprintf(stderr, "Not adding new copy\n"); + DEBUG_LOG("Not adding new copy\n"); return copy; } LOCK_FOR_SCOPE(&selector_table_lock); @@ -352,9 +373,26 @@ static SEL objc_register_selector_copy(SEL aSel, BOOL copyArgs) } // Create a copy of this selector. copy = selector_pool_alloc(); - copy->name = copyArgs ? strdup(aSel->name) : aSel->name; - copy->types = (NULL == aSel->types) ? NULL : - (copyArgs ? strdup(aSel->types) : aSel->types); + copy->name = aSel->name; + copy->types = (NULL == aSel->types) ? NULL : aSel->types; + if (copyArgs) + { + SEL untyped = selector_lookup(aSel->name, 0); + if (untyped != NULL) + { + copy->name = sel_getName(untyped); + } + else + { + copy->name = strdup(aSel->name); + selector_name_copies += strlen(copy->name); + } + if (copy->types != NULL) + { + copy->types = strdup(copy->types); + selector_name_copies += strlen(copy->types); + } + } // Try to register the copy as the authoritative version register_selector_locked(copy); return copy; diff --git a/third_party/libobjc/spinlock.h b/third_party/libobjc/spinlock.h index f3dc2cfb29..e65e60815f 100644 --- a/third_party/libobjc/spinlock.h +++ b/third_party/libobjc/spinlock.h @@ -26,7 +26,7 @@ extern int spinlocks[spinlock_count]; * contention between the same property in different objects, so we can't just * use the ivar offset. */ -static inline volatile int *lock_for_pointer(void *ptr) +static inline volatile int *lock_for_pointer(const void *ptr) { intptr_t hash = (intptr_t)ptr; // Most properties will be pointers, so disregard the lowest few bits diff --git a/third_party/libobjc/unwind-arm.h b/third_party/libobjc/unwind-arm.h index 63774611ca..385abab171 100644 --- a/third_party/libobjc/unwind-arm.h +++ b/third_party/libobjc/unwind-arm.h @@ -25,6 +25,8 @@ static const _Unwind_State _US_UNWIND_FRAME_RESUME = 2; # define _US_UNWIND_FRAME_RESUME 2 #endif +typedef int _Unwind_Action; + typedef struct _Unwind_Context _Unwind_Context; typedef uint32_t _Unwind_EHT_Header; -- GitLab