diff --git a/.travis.yml b/.travis.yml index c6a7857..cccd87d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,6 @@ +os: + - linux + - osx language: cpp compiler: clang script: diff --git a/CMakeLists.txt b/CMakeLists.txt index 245e9ab..58fd529 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,8 +13,6 @@ set(libobjc_VERSION 4.6) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fexceptions") # Build configuration add_definitions( -DGNUSTEP -D__OBJC_RUNTIME_INTERNAL__=1) -# Probably not needed anymore? -add_definitions( -D_XOPEN_SOURCE=700 -D__BSD_VISIBLE=1 -D_BSD_SOURCE=1) set(libobjc_ASM_SRCS block_trampolines.S diff --git a/README.md b/README.md index 1ddb22d..196bf53 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ GNUstep Objective-C Runtime =========================== +[![Build Status](https://travis-ci.org/gnustep/libobjc2.svg?branch=master)](https://travis-ci.org/gnustep/libobjc2) + The GNUstep Objective-C runtime is designed as a drop-in replacement for the GCC runtime. It supports both a legacy and a modern ABI, allowing code compiled with old versions of GCC to be supported without requiring @@ -465,3 +467,4 @@ be used directly. If it does not exist, ARC will implement its own `-_ARCCompatibleAutoreleasePool` then it must call `objc_autoreleasePoolPush()` and `objc_autoreleasePoolPop()` to manage autoreleased object storage and call `objc_autorelease()` in its `-addObject:` method. + diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index 8277063..09d6893 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -5,7 +5,7 @@ # List of single-file tests. set(TESTS - #alignTest.m + alignTest.m AllocatePair.m AssociatedObject.m AssociatedObject2.m @@ -26,6 +26,7 @@ set(TESTS WeakBlock_arc.m WeakReferences_arc.m ivar_arc.m + IVarOverlap.m objc_msgSend.m msgInterpose.m NilException.m @@ -58,10 +59,10 @@ endfunction(addtest_flags) foreach(TEST_SOURCE ${TESTS}) get_filename_component(TEST ${TEST_SOURCE} NAME_WE) - addtest_flags(${TEST} "-O0 -fobjc-runtime=gnustep-2.0" ${TEST_SOURCE}) - addtest_flags("${TEST}_optimised" "-O3 -fobjc-runtime=gnustep-2.0" ${TEST_SOURCE}) - addtest_flags("${TEST}_legacy" "-O0 -fobjc-runtime=gnustep-1.7" ${TEST_SOURCE}) - addtest_flags("${TEST}_legacy_optimised" "-O3 -fobjc-runtime=gnustep-1.7" ${TEST_SOURCE}) + addtest_flags(${TEST} "-O0 -fobjc-runtime=gnustep-2.0 -UNDEBUG" ${TEST_SOURCE}) + addtest_flags("${TEST}_optimised" "-O3 -fobjc-runtime=gnustep-2.0 -UNDEBUG" ${TEST_SOURCE}) + addtest_flags("${TEST}_legacy" "-O0 -fobjc-runtime=gnustep-1.7 -UNDEBUG" ${TEST_SOURCE}) + addtest_flags("${TEST}_legacy_optimised" "-O3 -fobjc-runtime=gnustep-1.7 -UNDEBUG" ${TEST_SOURCE}) endforeach() # Tests that are more than a single file. diff --git a/Test/IVarOverlap.m b/Test/IVarOverlap.m new file mode 100644 index 0000000..bc07de6 --- /dev/null +++ b/Test/IVarOverlap.m @@ -0,0 +1,30 @@ +#import +#import "../objc/objc.h" +#import "../objc/runtime.h" +#import "../objc/Object.h" +#include "Test.h" + +#import +#import + +@interface Dummy : Test +{ + id objOne; + struct stat statBuf; + BOOL flagOne; +} +@end + +@implementation Dummy +- (void)test +{ + assert((char*)&statBuf+sizeof(struct stat) <= (char*)&flagOne); +} +@end + + +int main(int argc, char *argv[]) +{ + [[Dummy new] test]; + return 0; +} diff --git a/Test/PropertyIntrospectionTest2_arc.m b/Test/PropertyIntrospectionTest2_arc.m index 3ec266f..7f38070 100644 --- a/Test/PropertyIntrospectionTest2_arc.m +++ b/Test/PropertyIntrospectionTest2_arc.m @@ -6,6 +6,20 @@ #pragma GCC diagnostic ignored "-Wobjc-property-no-attribute" +// Clang < 3 doesn't exist usefully, so we can skip tests for it. Clang 3.5 +// adds proper metadata for weak properties, earlier ones don't, so don't fail +// the tests because of known compiler bugs. +#ifndef __clang_minor__ +#define WEAK_ATTR ATTR("W", ""), +#define WEAK_STR "W," +#elif (__clang_major__ < 4) && (__clang_minor__ < 5) +#define WEAK_ATTR +#define WEAK_STR +#else +#define WEAK_ATTR ATTR("W", ""), +#define WEAK_STR "W," +#endif + enum FooManChu { FOO, MAN, CHU }; struct YorkshireTeaStruct { int pot; signed char lady; }; typedef struct YorkshireTeaStruct YorkshireTeaStructType; @@ -281,7 +295,7 @@ static BOOL testPropertyForProperty_alt(objc_property_t p, attrsList = property_copyAttributeList(p, NULL); OPT_ASSERT(0 != attrsList); objc_property_attribute_t *ra; - for (attrsCount = 0, ra = attrsList; ra->name != NULL; attrsCount++, ra++) {} + for (attrsCount = 0, ra = attrsList; (ra->name != NULL) && (attrsCount < size); attrsCount++, ra++) {} OPT_ASSERT(attrsCount == size); free(attrsList); for (unsigned int index=0; indexs == nil); - return 0; +} + +int main (void) +{ + /* Test for ivars in the same class */ + testIvarsOn([Foo new]); + /* Test for ivars in the superclass (receiver ivar list empty) */ + testIvarsOn([EmptySubFoo new]); + /* Test for ivars in the superclass (receiver ivar list non-empty) */ + testIvarsOn([NonEmptySubFoo new]); + return 0; } diff --git a/arc.m b/arc.m index b8d177f..b7a3b3b 100644 --- a/arc.m +++ b/arc.m @@ -220,7 +220,7 @@ static inline void initAutorelease(void) { if (Nil == AutoreleasePool) { - AutoreleasePool = objc_getRequiredClass("NSAutoreleasePool"); + AutoreleasePool = objc_getClass("NSAutoreleasePool"); if (Nil == AutoreleasePool) { useARCAutoreleasePool = YES; diff --git a/dtable.c b/dtable.c index cb8783a..db29afa 100644 --- a/dtable.c +++ b/dtable.c @@ -822,6 +822,13 @@ PRIVATE void objc_send_initialize(id object) objc_send_initialize((id)class->super_class); } + // 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(); + // 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. @@ -829,19 +836,17 @@ PRIVATE void objc_send_initialize(id object) { // 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 + // released once the class has been fully initialized. The runtime + // lock needs to be released first to prevent a deadlock between the + // runtime lock and the class-specific lock. + UNLOCK_RUNTIME(); + objc_sync_enter((id)meta); objc_sync_exit((id)meta); assert(dtable_for_class(class) != uninstalled_dtable); 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)) diff --git a/ivar.c b/ivar.c index 801d23a..93de22a 100644 --- a/ivar.c +++ b/ivar.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -69,6 +70,7 @@ PRIVATE void objc_compute_ivar_offsets(Class class) */ if (class->ivars) { + long cumulative_fudge = 0; for (i = 0 ; i < class->ivars->count ; i++) { struct objc_ivar *ivar = &class->ivars->ivar_list[i]; @@ -78,8 +80,26 @@ PRIVATE void objc_compute_ivar_offsets(Class class) // then we will need to ensure that we are properly aligned again. long ivar_size = (i+1 == class->ivars->count) ? (class_size - ivar->offset) - : ivar->offset - class->ivars->ivar_list[i+1].offset; + : class->ivars->ivar_list[i+1].offset - ivar->offset ; + assert(ivar_size > 0); // FIXME: use alignment + ivar->offset += cumulative_fudge; + // We only need to do the realignment for things that are + // bigger than a pointer, and we don't need to do it in GC mode + // where we don't add any extra padding. + if (!isGCEnabled && (ivar_size > sizeof(void*))) + { + long offset = ivar_start + ivar->offset + sizeof(intptr_t); + // For now, assume that nothing needs to be more than 16-byte aligned. + // This is not correct for AVX vectors, but we probably + // can't do anything about that for now (as malloc is only + // giving us 16-byte aligned memory) + long fudge = 16 - (offset % 16); + ivar->offset += fudge; + class->instance_size += fudge; + cumulative_fudge += fudge; + assert((ivar_start + ivar->offset + sizeof(intptr_t)) % 16 == 0); + } ivar->offset += ivar_start; /* If we're using the new ABI then we also set up the faster ivar * offset variables. diff --git a/properties.m b/properties.m index 46026cc..cbb8bdb 100644 --- a/properties.m +++ b/properties.m @@ -1,6 +1,7 @@ #include "objc/runtime.h" #include "objc/objc-arc.h" #include +#include #include #include #include @@ -520,64 +521,62 @@ objc_property_attribute_t *property_copyAttributeList(objc_property_t property, 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 (checkAttribute(property->attributes, OBJC_PR_retain) || - checkAttribute(property->attributes2, OBJC_PR_strong)) + // If the compiler provides a type encoding string, then it's more + // informative than the bitfields and should be treated as canonical. If + // the compiler didn't provide a type encoding string, then this will + // create a best-effort one. + const char *attributes = property_getAttributes(property); + for (int i=strlen(types)+1 ; attributes[i] != 0 ; i++) { - 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"; + assert(count<12); + if (attributes[i] == ',') + { + // Comma is never the last character in the string, so this should + // never push us past the end. + i++; + } attrs[count].value = ""; + switch (attributes[i]) + { + case 'R': + attrs[count].name = "R"; + break; + case 'C': + attrs[count].name = "C"; + break; + case '&': + attrs[count].name = "&"; + break; + case 'D': + attrs[count].name = "D"; + break; + case 'W': + attrs[count].name = "W"; + break; + case 'N': + attrs[count].name = "N"; + break; + case 'G': + attrs[count].name = "G"; + attrs[count].value = property->getter_name; + i += strlen(attrs[count].value); + break; + case 'S': + attrs[count].name = "S"; + attrs[count].value = property->setter_name; + i += strlen(attrs[count].value); + break; + case 'V': + attrs[count].name = "V"; + attrs[count].value = attributes+i+1; + i += strlen(attributes+i)-1; + break; + default: + i++; + continue; + } count++; } - if ((property->attributes & OBJC_PR_getter) == OBJC_PR_getter) - { - attrs[count].name = "G"; - attrs[count].value = property->getter_name; - count++; - } - if ((property->attributes & OBJC_PR_setter) == OBJC_PR_setter) - { - attrs[count].name = "S"; - attrs[count].value = property->setter_name; - 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)); if (NULL != outCount) @@ -702,6 +701,7 @@ char *property_copyAttributeValue(objc_property_t property, const char *attributeName) { if ((NULL == property) || (NULL == attributeName)) { return NULL; } + const char *attributes = property_getAttributes(property); switch (attributeName[0]) { case 'T': @@ -710,9 +710,13 @@ char *property_copyAttributeValue(objc_property_t property, return (NULL == types) ? NULL : strdup(types); } case 'D': + case 'R': + case 'W': + case 'C': + case '&': + case 'N': { - return checkAttribute(property->attributes2, OBJC_PR_dynamic) && - !checkAttribute(property->attributes2, OBJC_PR_synthesized) ? strdup("") : 0; + return strchr(attributes, attributeName[0]) ? strdup("") : 0; } case 'V': { @@ -726,27 +730,6 @@ 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 checkAttribute(property->attributes, OBJC_PR_copy) ? strdup("") : 0; - } - case '&': - { - return checkAttribute(property->attributes, OBJC_PR_retain) || - checkAttribute(property->attributes2, OBJC_PR_strong) ? strdup("") : 0; - } - case 'N': - { - return checkAttribute(property->attributes, OBJC_PR_nonatomic) ? strdup("") : 0; - } } return 0; }