diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c71e65..753f4e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,13 +8,13 @@ set(CMAKE_C_FLAGS_DEBUG "-g -O0 -fno-inline ${CMAKE_C_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_RELEASE "-O3 ${CMAKE_C_FLAGS_RELEASE}") set(CMAKE_C_FLAGS "-std=gnu99 ${CMAKE_C_FLAGS}") -set(libobjc_VERSION 4.6) +set(libobjc_VERSION 4.7) 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=500 -D__BSD_VISIBLE=1 -D_BSD_SOURCE=1) +add_definitions( -D_XOPEN_SOURCE=700 -D__BSD_VISIBLE=1 -D_BSD_SOURCE=1) set(libobjc_ASM_SRCS block_trampolines.S @@ -138,7 +138,7 @@ set(INCLUDE_DIRECTORY "objc" CACHE STRING if (${CMAKE_C_COMPILER_ID} MATCHES Clang*) set(CMAKE_OBJC_FLAGS "${CMAKE_OBJC_FLAGS} -Wno-deprecated-objc-isa-usage -Wno-objc-root-class") if (${CMAKE_C_COMPILER_VERSION} VERSION_GREATER 3.1) - set(CMAKE_OBJC_FLAGS "${CMAKE_OBJC_FLAGS} -fobjc-runtime=gnustep-1.7") + set(CMAKE_OBJC_FLAGS "${CMAKE_OBJC_FLAGS} -fobjc-runtime=gnustep-1.8") endif () if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i686") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=i586") diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index 2e7052d..9fdeea9 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -3,6 +3,15 @@ # the installed version +# Remove compile flags for all optimized targets because they usually contain +# -DNDEBUG flag which disable assert() macro +set(CMAKE_C_FLAGS_MINSIZEREL "") +set(CMAKE_C_FLAGS_RELEASE "") +set(CMAKE_C_FLAGS_RELWITHDEBINFO "") +set(CMAKE_CXX_FLAGS_MINSIZEREL "") +set(CMAKE_CXX_FLAGS_RELEASE "") +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "") + # List of single-file tests. set(TESTS AllocatePair.m @@ -14,6 +23,7 @@ set(TESTS ManyManySelectors.m NestedExceptions.m PropertyAttributeTest.m + PropertyAttributeTest2.m PropertyIntrospectionTest.m ProtocolCreation.m RuntimeTest.m @@ -27,7 +37,7 @@ function(addtest_flags TEST FLAGS TEST_SOURCE) add_test(${TEST} ${TEST}) set_target_properties(${TEST} PROPERTIES INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}" - COMPILE_FLAGS "-fobjc-runtime=gnustep-1.7 -fblocks ${FLAGS}" + COMPILE_FLAGS "-fobjc-runtime=gnustep-1.8 -fblocks ${FLAGS}" LINKER_LANGUAGE C ) set_property(TEST ${TEST} PROPERTY diff --git a/Test/PropertyAttributeTest2.m b/Test/PropertyAttributeTest2.m new file mode 100644 index 0000000..6995045 --- /dev/null +++ b/Test/PropertyAttributeTest2.m @@ -0,0 +1,698 @@ +#if __APPLE__ +// Compile command: clang -o PropertyAttributeTest2 -framework Foundation PropertyAttributeTest2.m +#include +#include + +__attribute__((objc_root_class)) address@hidden Test { id isa; } address@hidden address@hidden Test ++ (id)class { return self; } ++ (id)new +{ + return class_createInstance(self, 0); +} +- (void)dealloc +{ + object_dispose(self); +} +- (id)retain +{ + return self; +} +- (oneway void)release +{ +} address@hidden +#else +#include "Test.h" +#endif +#include +#include +#include +#include + +enum FooManChu { FOO, MAN, CHU }; +struct YorkshireTeaStruct { int pot; char lady; }; +typedef struct YorkshireTeaStruct YorkshireTeaStructType; +union MoneyUnion { float alone; double down; }; + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#if __has_attribute(objc_root_class) +__attribute__((objc_root_class)) +#endif address@hidden PropertyTest +{ address@hidden + Class isa; + char charDefault; + double doubleDefault; + enum FooManChu enumDefault; + float floatDefault; + int intDefault; + long longDefault; + short shortDefault; + signed signedDefault; + struct YorkshireTeaStruct structDefault; + YorkshireTeaStructType typedefDefault; + union MoneyUnion unionDefault; + unsigned unsignedDefault; + int (*functionPointerDefault)(char *); + int *intPointer; + void *voidPointerDefault; + int intSynthEquals; + int intSetterGetter; + int intReadonly; + int intReadonlyGetter; + int intReadwrite; + int intAssign; + id idDefault; + id idRetain; + id idCopy; + id idWeak; + id idStrong; + int intNonatomic; + id idReadonlyCopyNonatomic; + id idReadonlyRetainNonatomic; + id idReadonlyWeakNonatomic; + id _idOther; +} address@hidden char charDefault; address@hidden double doubleDefault; address@hidden enum FooManChu enumDefault; address@hidden float floatDefault; address@hidden int intDefault; address@hidden long longDefault; address@hidden short shortDefault; address@hidden signed signedDefault; address@hidden struct YorkshireTeaStruct structDefault; address@hidden YorkshireTeaStructType typedefDefault; address@hidden union MoneyUnion unionDefault; address@hidden unsigned unsignedDefault; address@hidden int (*functionPointerDefault)(char *); address@hidden int *intPointer; address@hidden void *voidPointerDefault; address@hidden(getter=intGetFoo, setter=intSetFoo:) int intSetterGetter; address@hidden(readonly) int intReadonly; address@hidden(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter; address@hidden(readwrite) int intReadwrite; address@hidden(assign) int intAssign; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wobjc-property-no-attribute" address@hidden id idDefault; +#pragma GCC diagnostic pop address@hidden(retain) id idRetain; address@hidden(copy) id idCopy; address@hidden(weak) id idWeak; address@hidden(strong) id idStrong; address@hidden(nonatomic) int intNonatomic; address@hidden(nonatomic, readonly, copy) id idReadonlyCopyNonatomic; address@hidden(nonatomic, readonly, retain) id idReadonlyRetainNonatomic; address@hidden(nonatomic, readonly, weak) id idReadonlyWeakNonatomic; address@hidden(retain) id idOther; address@hidden(retain) id idDynamic; address@hidden(retain, nonatomic, getter=dynamicGetterSetter, setter=setDynamicGetterSetter:) id idDynamicGetterSetter; address@hidden + address@hidden PropertyTest address@hidden charDefault; address@hidden doubleDefault; address@hidden enumDefault; address@hidden floatDefault; address@hidden intDefault; address@hidden longDefault; address@hidden shortDefault; address@hidden signedDefault; address@hidden structDefault; address@hidden typedefDefault; address@hidden unionDefault; address@hidden unsignedDefault; address@hidden functionPointerDefault; address@hidden intPointer; address@hidden voidPointerDefault; address@hidden intSetterGetter; address@hidden intReadonly; address@hidden intReadonlyGetter; address@hidden intReadwrite; address@hidden intAssign; address@hidden idDefault; address@hidden idRetain; address@hidden idCopy; address@hidden idWeak; address@hidden idStrong; address@hidden intNonatomic; address@hidden idReadonlyCopyNonatomic; address@hidden idReadonlyRetainNonatomic; address@hidden idReadonlyWeakNonatomic; address@hidden idOther = _idOther; address@hidden idDynamic; address@hidden idDynamicGetterSetter; address@hidden + address@hidden ProtocolTest address@hidden char charDefault; address@hidden double doubleDefault; address@hidden enum FooManChu enumDefault; address@hidden float floatDefault; address@hidden int intDefault; address@hidden long longDefault; address@hidden short shortDefault; address@hidden signed signedDefault; address@hidden struct YorkshireTeaStruct structDefault; address@hidden YorkshireTeaStructType typedefDefault; address@hidden union MoneyUnion unionDefault; address@hidden unsigned unsignedDefault; address@hidden int (*functionPointerDefault)(char *); address@hidden int *intPointer; address@hidden void *voidPointerDefault; address@hidden(getter=intGetFoo, setter=intSetFoo:) int intSetterGetter; address@hidden(readonly) int intReadonly; address@hidden(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter; address@hidden(readwrite) int intReadwrite; address@hidden(assign) int intAssign; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wobjc-property-no-attribute" address@hidden id idDefault; +#pragma GCC diagnostic pop address@hidden(retain) id idRetain; address@hidden(copy) id idCopy; address@hidden(weak) id idWeak; address@hidden(strong) id idStrong; address@hidden(nonatomic) int intNonatomic; address@hidden(nonatomic, readonly, copy) id idReadonlyCopyNonatomic; address@hidden(nonatomic, readonly, retain) id idReadonlyRetainNonatomic; address@hidden(nonatomic, readonly, weak) id idReadonlyWeakNonatomic; address@hidden(retain) id idOther; address@hidden(retain) id idDynamic; address@hidden(retain, nonatomic, getter=dynamicGetterSetter, setter=setDynamicGetterSetter:) id idDynamicGetterSetter; address@hidden + +#if __has_attribute(objc_root_class) +__attribute__((objc_root_class)) +#endif address@hidden PropertyProtocolTest +{ + Class isa; + char charDefault; + double doubleDefault; + enum FooManChu enumDefault; + float floatDefault; + int intDefault; + long longDefault; + short shortDefault; + signed signedDefault; + struct YorkshireTeaStruct structDefault; + YorkshireTeaStructType typedefDefault; + union MoneyUnion unionDefault; + unsigned unsignedDefault; + int (*functionPointerDefault)(char *); + int *intPointer; + void *voidPointerDefault; + int intSynthEquals; + int intSetterGetter; + int intReadonly; + int intReadonlyGetter; + int intReadwrite; + int intAssign; + id idDefault; + id idRetain; + id idCopy; + id idWeak; + id idStrong; + int intNonatomic; + id idReadonlyCopyNonatomic; + id idReadonlyRetainNonatomic; + id idReadonlyWeakNonatomic; + id _idOther; +} address@hidden + address@hidden PropertyProtocolTest address@hidden charDefault; address@hidden doubleDefault; address@hidden enumDefault; address@hidden floatDefault; address@hidden intDefault; address@hidden longDefault; address@hidden shortDefault; address@hidden signedDefault; address@hidden structDefault; address@hidden typedefDefault; address@hidden unionDefault; address@hidden unsignedDefault; address@hidden functionPointerDefault; address@hidden intPointer; address@hidden voidPointerDefault; address@hidden intSetterGetter; address@hidden intReadonly; address@hidden intReadonlyGetter; address@hidden intReadwrite; address@hidden intAssign; address@hidden idDefault; address@hidden idRetain; address@hidden idCopy; address@hidden idWeak; address@hidden idStrong; address@hidden intNonatomic; address@hidden idReadonlyCopyNonatomic; address@hidden idReadonlyRetainNonatomic; address@hidden idReadonlyWeakNonatomic; address@hidden idOther = _idOther; address@hidden idDynamic; address@hidden idDynamicGetterSetter; address@hidden + +#define ATTR(n, v) (objc_property_attribute_t){(n), (v)} +#define ATTRS(...) (objc_property_attribute_t[]){ __VA_ARGS__ }, \ + sizeof((objc_property_attribute_t[]){ __VA_ARGS__ }) / sizeof(objc_property_attribute_t) + +static void testPropertyForProperty(objc_property_t p, + const char *name, + const char *types, + objc_property_attribute_t* list, + unsigned int size) +{ + assert(0 != p); + assert(strcmp(name, property_getName(p)) == 0); + const char *attrs = property_getAttributes(p); + assert(0 != attrs); + printf("attributes for '%s': %s, should be: %s\n", name, attrs, types); + assert(strcmp(types, attrs) == 0); + unsigned int attrsCount = 0; + objc_property_attribute_t *attrsList = property_copyAttributeList(p, &attrsCount); + assert(0 != attrsList); + printf("attributes list size for '%s': %u, should be: %u\n", name, attrsCount, size); + assert(attrsCount == size); + for (unsigned int index=0; indexname != NULL; attrsCount++, ra++) {} + assert(attrsCount == size); + free(attrsList); + for (unsigned int index=0; indexidRetain == testValue); + assert(t->_idOther == nil); + +#if __APPLE__ + // why does this test fail with gnu runtime? + Method idRetainSetter = class_getInstanceMethod(testClass, @selector(setIdRetain:)); + Method idOtherSetter = class_getInstanceMethod(testClass, @selector(setIdOther:)); + method_setImplementation(idRetainSetter, method_getImplementation(idOtherSetter)); + + id testValue2 = [Test new]; + t.idRetain = testValue2; + assert(t->idRetain == testValue); + assert(t->_idOther == testValue2); +#endif // __APPLE__ + return 0; +#endif // __has_feature(objc_property_clean_abi) +} diff --git a/Test/ProtocolCreation.m b/Test/ProtocolCreation.m index 204021d..9b2566e 100644 --- a/Test/ProtocolCreation.m +++ b/Test/ProtocolCreation.m @@ -3,6 +3,11 @@ #include #include +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + + @protocol Test2 @end int main(void) @@ -23,6 +28,10 @@ int main(void) unsigned int count; objc_property_t *props = protocol_copyPropertyList(p1, &count); assert(count == 1); +#if __has_feature(objc_property_clean_abi) + assert(strcmp("T@", property_getAttributes(*props)) == 0); +#else assert(strcmp("T@,Vfoo", property_getAttributes(*props)) == 0); +#endif return 0; } diff --git a/block_to_imp.c b/block_to_imp.c index 5b0262c..89d8098 100644 --- a/block_to_imp.c +++ b/block_to_imp.c @@ -10,21 +10,19 @@ #include "objc/blocks_runtime.h" #include "blocks_runtime.h" #include "lock.h" +#include "loader.h" #include "visibility.h" -/* QNX needs a special header for asprintf() */ -#ifdef __QNXNTO__ -#include -#endif - -#define PAGE_SIZE 4096 +#undef PAGE_SIZE +#define PAGE_SIZE page_size static void *executeBuffer; static void *writeBuffer; static ptrdiff_t offset; static mutex_t trampoline_lock; -static char *tmpPattern; +static const char *tmpPattern; +static size_t tmpPatternLength; struct wx_buffer { @@ -40,10 +38,15 @@ PRIVATE void init_trampolines(void) { tmp = "/tmp/"; } - if (0 > asprintf(&tmpPattern, "%s/objc_trampolinesXXXXXXXXXXX", tmp)) + tmpPatternLength = strlen(tmp)+strlen("/objc_trampolinesXXXXXXXXXXX")+1; + tmpPattern = malloc(tmpPatternLength); + if (NULL == tmpPattern) { abort(); } + char *p = (char *)tmpPattern; + p = stpcpy(p, tmp); + p = stpcpy(p, "/objc_trampolinesXXXXXXXXXXX"); } static struct wx_buffer alloc_buffer(size_t size) @@ -51,8 +54,13 @@ static struct wx_buffer alloc_buffer(size_t size) LOCK_FOR_SCOPE(&trampoline_lock); if ((0 == offset) || (offset + size >= PAGE_SIZE)) { - int fd = mkstemp(tmpPattern); - unlink(tmpPattern); + // Duplicate pattern because mkstemp will modify it + char *pattern = malloc(tmpPatternLength); + assert(NULL != pattern); + memcpy(pattern, tmpPattern, tmpPatternLength); + int fd = mkstemp(pattern); + unlink(pattern); + free(pattern); ftruncate(fd, PAGE_SIZE); void *w = mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED, fd, 0); executeBuffer = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0); diff --git a/dtable.c b/dtable.c index f534b17..f24826d 100644 --- a/dtable.c +++ b/dtable.c @@ -7,7 +7,6 @@ #include "selector.h" #include "class.h" #include "lock.h" -#include "method_list.h" #include "slot_pool.h" #include "dtable.h" #include "visibility.h" diff --git a/loader.c b/loader.c index 6f4d88e..d02f43e 100644 --- a/loader.c +++ b/loader.c @@ -8,12 +8,14 @@ #include #endif #include +#include /** * Runtime lock. This is exposed in */ PRIVATE mutex_t runtime_mutex; LEGACY void *__objc_runtime_mutex = &runtime_mutex; +PRIVATE size_t page_size; void init_alias_table(void); void init_arc(void); @@ -60,6 +62,8 @@ void __objc_exec_class(struct objc_module_abi_8 *module) // call dlopen() or equivalent, and the platform's implementation of // this does not perform any synchronization. INIT_LOCK(runtime_mutex); + // Cache system page size + page_size = sysconf(_SC_PAGESIZE); // Create the various tables that the runtime needs. init_selector_tables(); init_protocol_table(); diff --git a/loader.h b/loader.h index a97063b..1b6cdfb 100644 --- a/loader.h +++ b/loader.h @@ -7,6 +7,11 @@ #include "protocol.h" /** + * Cache system page size. + */ +extern size_t page_size; + +/** * Checks whether it is safe to load a module with the specified version and * module size. This depends on whether another module with an incompatible * ABI has already been loaded. diff --git a/opts/CMakeLists.txt b/opts/CMakeLists.txt index 3cd10ef..aa3f794 100644 --- a/opts/CMakeLists.txt +++ b/opts/CMakeLists.txt @@ -36,6 +36,6 @@ EXEC_PROGRAM(llvm-config 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}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_VERSION} -fno-rtti") include_directories( ${LLVM_INCLUDE_DIRS} "${LLVM_SRC}/include/" "${LLVM_OBJ}/include/") diff --git a/opts/ClassMethodInliner.cpp b/opts/ClassMethodInliner.cpp index 7d061f3..4e211e5 100644 --- a/opts/ClassMethodInliner.cpp +++ b/opts/ClassMethodInliner.cpp @@ -34,7 +34,11 @@ namespace virtual bool runOnModule(Module &M) { unsigned MessageSendMDKind = M.getContext().getMDKindID("GNUObjCMessageSend"); +#if LLVM_MAJOR > 3 || (LLVM_MAJOR == 3 && LLVM_MINOR >= 3) + InlineCostAnalysis CA; +#else InlineCostAnalyzer CA; +#endif SmallPtrSet NeverInline; GNUstep::IMPCacher cacher = GNUstep::IMPCacher(M.getContext(), this); diff --git a/opts/TypeFeedbackDrivenInliner.cpp b/opts/TypeFeedbackDrivenInliner.cpp index 050c8be..10b124c 100644 --- a/opts/TypeFeedbackDrivenInliner.cpp +++ b/opts/TypeFeedbackDrivenInliner.cpp @@ -33,7 +33,11 @@ namespace { //TypeInfoProvider::SharedTypeInfoProvider()->PrintStatistics(); GNUstep::IMPCacher cacher = GNUstep::IMPCacher(M.getContext(), this); +#if LLVM_MAJOR > 3 || (LLVM_MAJOR == 3 && LLVM_MINOR >= 3) + InlineCostAnalysis CA; +#else InlineCostAnalyzer CA; +#endif SmallVector messages; for (Module::iterator F=M.begin(), fend=M.end() ; diff --git a/pool.h b/pool.h index f0bd545..f9b7da0 100644 --- a/pool.h +++ b/pool.h @@ -1,5 +1,6 @@ #include #include "lock.h" +#include "loader.h" #ifndef POOL_TYPE #error POOL_TYPE must be defined @@ -13,7 +14,8 @@ #define PREFIX_SUFFIX(x, y) REALLY_PREFIX_SUFFIX(x, y) #define NAME(x) PREFIX_SUFFIX(POOL_NAME, x) -#define PAGE_SIZE 4096 +#undef PAGE_SIZE +#define PAGE_SIZE page_size // Malloc one page at a time. #define POOL_SIZE ((PAGE_SIZE) / sizeof(POOL_TYPE)) diff --git a/properties.h b/properties.h index 4f13a02..33db750 100644 --- a/properties.h +++ b/properties.h @@ -37,10 +37,117 @@ enum PropertyAttributeKind /** * Property has setter. */ - OBJC_PR_setter = (1<<7) + OBJC_PR_setter = (1<<7), + /** + * Property is marked as atomic. + */ + OBJC_PR_atomic = (1<<8), + /** + * Property has weak semantics. + */ + OBJC_PR_weak = (1<<9), + /** + * Property has strong semantics. + */ + OBJC_PR_strong = (1<<10), + /** + * Property has unsafe unretained semantics. + */ + OBJC_PR_unsafe_unretained = (1<<11), + /** + * Property is not synthesized. + */ + OBJC_PR_dynamic = (1<<12), + /** + * Property was allocated by runtime. + */ + OBJC_PR_runtime_allocated = (1<<31) +}; + +#if __clang__ && __has_feature(objc_property_clean_abi) +/** + * Structure used for property enumeration. + * Try to achieve MacOS X compatibility. + */ +struct objc_property +{ + /** + * Name of this property. + */ + const char *name; + /** + * Type encoding for this property. + */ + const char *type_encoding; + /** + * Name of the getter for this property. NULL if undefined. + */ + const char *getter_name; + /** + * Name of the setter for this property. NULL if undefined. + */ + const char *setter_name; + /** + * Name of the ivar for this property. NULL if undefined. + */ + const char *ivar_name; + /** + * Attributes string for this property. Return value for + * property_getAttributes(). + */ + const char *attrs_string; + /** + * Attributes for this property. Made by ORing together + * PropertyAttributeKinds. + */ + unsigned int attributes; }; /** + * List of property introspection data. + */ +struct objc_property_list +{ + /** + * Number of properties in this array. + */ + int count; + /* + * The next property in a linked list. + */ + struct objc_property_list *next; + /** + * List of properties. + */ + struct objc_property properties[]; +}; + +/** + * Structure used for copying property attributes. + */ +struct objc_property_attribute +{ + /** + * Name of the attribute. + */ + const char *name; + /** + * Value of the attribute. + */ + const char *value; + /** + * Length of value string. + */ + size_t length; +}; + +PRIVATE BOOL initPropertyFromAttributesList(struct objc_property *property, + const char *name, + const objc_property_attribute_t *attributes, + unsigned int attributesCount); + +#else /* __clang__ && __has_feature(objc_property_clean_abi) */ +/** * Structure used for property enumeration. Note that property enumeration is * currently quite broken on OS X, so achieving full compatibility there is * impossible. Instead, we strive to achieve compatibility with the @@ -80,7 +187,7 @@ struct objc_property }; /** - * List of property inrospection data. + * List of property introspection data. */ struct objc_property_list { @@ -112,3 +219,5 @@ PRIVATE struct objc_property propertyFromAttrs(const objc_property_attribute_t * */ PRIVATE const char *constructPropertyAttributes(objc_property_t property, const char *iVarName); + +#endif /* __clang__ && __has_feature(objc_property_clean_abi) */ diff --git a/properties.m b/properties.m index f26a2a4..e94012a 100644 --- a/properties.m +++ b/properties.m @@ -292,6 +292,357 @@ objc_property_t* class_copyPropertyList(Class cls, unsigned int *outCount) return list; } +#if __clang__ && __has_feature(objc_property_clean_abi) +BOOL class_addProperty(Class cls, + const char *name, + const objc_property_attribute_t *attributes, + unsigned int attributesCount) +{ + if ((Nil == cls) || (NULL == name) || (class_getProperty(cls, name) != 0)) { return NO; } + + struct objc_property p; + if (!initPropertyFromAttributesList(&p, name, attributes, attributesCount)) + { + return NO; + } + + struct objc_property_list *l = calloc(1, sizeof(struct objc_property_list) + + sizeof(struct objc_property)); + l->count = 1; + memcpy(&l->properties, &p, sizeof(struct objc_property)); + LOCK_RUNTIME_FOR_SCOPE(); + l->next = cls->properties; + cls->properties = l; + return YES; +} + +void class_replaceProperty(Class cls, + const char *name, + const objc_property_attribute_t *attributes, + unsigned int attributesCount) +{ + if ((Nil == cls) || (NULL == name)) { return; } + objc_property_t old = class_getProperty(cls, name); + if (NULL == old) + { + class_addProperty(cls, name, attributes, attributesCount); + return; + } + struct objc_property p; + if (!initPropertyFromAttributesList(&p, name, attributes, attributesCount)) + { + return; + } + LOCK_RUNTIME_FOR_SCOPE(); + if ((old->attributes & OBJC_PR_runtime_allocated) == OBJC_PR_runtime_allocated) + { + free((void *)old->name); + free((void *)old->attrs_string); + if (old->type_encoding != NULL) + free((void *)old->type_encoding); + if (old->getter_name != NULL) + free((void *)old->getter_name); + if (old->setter_name != NULL) + free((void *)old->setter_name); + if (old->ivar_name != NULL) + free((void *)old->ivar_name); + } + memcpy(old, &p, sizeof(p)); +} + +const char *property_getName(objc_property_t property) +{ + if (NULL == property) { return NULL; } + + return property->name; +} + +const char *property_getAttributes(objc_property_t property) +{ + if (NULL == property) { return NULL; } + + return property->attrs_string; +} + +objc_property_attribute_t *property_copyAttributeList(objc_property_t property, + unsigned int *outCount) +{ + if (NULL == property) { if (outCount) *outCount = 0; return NULL; } + struct objc_property_attribute attrs[10]; + size_t attrcount = 0; + size_t stringslen = 0; + + if (property->type_encoding != NULL) + { + attrs[attrcount].name = "T"; + attrs[attrcount].value = property->type_encoding; + attrs[attrcount].length = strlen(property->type_encoding); + stringslen += attrs[attrcount].length + 3; + attrcount++; + } + if ((property->attributes & OBJC_PR_readonly) == OBJC_PR_readonly) + { + attrs[attrcount].name = "R"; + attrs[attrcount].value = ""; + attrs[attrcount].length = 0; + stringslen += 3; + attrcount++; + } + if ((property->attributes & OBJC_PR_retain) == OBJC_PR_retain + || (property->attributes & OBJC_PR_strong) == OBJC_PR_strong) + { + attrs[attrcount].name = "&"; + attrs[attrcount].value = ""; + attrs[attrcount].length = 0; + stringslen += 3; + attrcount++; + } + if ((property->attributes & OBJC_PR_copy) == OBJC_PR_copy) + { + attrs[attrcount].name = "C"; + attrs[attrcount].value = ""; + attrs[attrcount].length = 0; + stringslen += 3; + attrcount++; + } + if ((property->attributes & OBJC_PR_weak) == OBJC_PR_weak) + { + attrs[attrcount].name = "W"; + attrs[attrcount].value = ""; + attrs[attrcount].length = 0; + stringslen += 3; + attrcount++; + } + if ((property->attributes & OBJC_PR_dynamic) == OBJC_PR_dynamic) + { + attrs[attrcount].name = "D"; + attrs[attrcount].value = ""; + attrs[attrcount].length = 0; + stringslen += 3; + attrcount++; + } + if ((property->attributes & OBJC_PR_nonatomic) == OBJC_PR_nonatomic) + { + attrs[attrcount].name = "N"; + attrs[attrcount].value = ""; + attrs[attrcount].length = 0; + stringslen += 3; + attrcount++; + } + if (property->getter_name != NULL) + { + attrs[attrcount].name = "G"; + attrs[attrcount].value = property->getter_name; + attrs[attrcount].length = strlen(property->getter_name); + stringslen += attrs[attrcount].length + 3; + attrcount++; + } + if (property->setter_name != NULL) + { + attrs[attrcount].name = "S"; + attrs[attrcount].value = property->setter_name; + attrs[attrcount].length = strlen(property->setter_name); + stringslen += attrs[attrcount].length + 3; + attrcount++; + } + if (property->ivar_name != NULL) + { + attrs[attrcount].name = "V"; + attrs[attrcount].value = property->ivar_name; + attrs[attrcount].length = strlen(property->ivar_name); + stringslen += attrs[attrcount].length + 3; + attrcount++; + } + + if (attrcount == 0) { if (outCount) *outCount = 0; return NULL; } + + objc_property_attribute_t *propAttrs = calloc(1, (attrcount+1) * sizeof(objc_property_attribute_t) + stringslen); + objc_property_attribute_t *ra = propAttrs; + char *rs = (char *)(ra+attrcount+1); + for (size_t a = 0; a < attrcount; a++) + { + ra->name = rs; + memcpy(rs, attrs[a].name, 2); + rs += 2; + ra->value = rs; + memcpy(rs, attrs[a].value, attrs[a].length + 1); + rs += attrs[a].length + 1; + ra ++; + } + if (NULL != outCount) + { + *outCount = attrcount; + } + return propAttrs; +} + +char *property_copyAttributeValue(objc_property_t property, + const char *attributeName) +{ + if ((NULL == property) || (NULL == attributeName)) { return NULL; } + switch (attributeName[0]) + { + case 'T': + { + return (NULL == property->type_encoding) ? NULL : strdup(property->type_encoding); + } + case 'V': + { + return (NULL == property->ivar_name) ? NULL : strdup(property->ivar_name); + } + case 'S': + { + return (NULL == property->setter_name) ? NULL : strdup(property->setter_name); + } + case 'G': + { + return (NULL == property->getter_name) ? NULL : strdup(property->getter_name); + } + case 'R': + { + return ((property->attributes & OBJC_PR_readonly) == OBJC_PR_readonly) ? strdup("") : 0; + } + case 'C': + { + return ((property->attributes & OBJC_PR_copy) == OBJC_PR_copy) ? strdup("") : 0; + } + case '&': + { + return ((property->attributes & OBJC_PR_retain) == OBJC_PR_retain + || (property->attributes & OBJC_PR_strong) == OBJC_PR_strong) ? strdup("") : 0; + } + case 'W': + { + return ((property->attributes & OBJC_PR_weak) == OBJC_PR_weak) ? strdup("") : 0; + } + case 'N': + { + return ((property->attributes & OBJC_PR_nonatomic) == OBJC_PR_nonatomic) ? strdup("") : 0; + } + case 'D': + { + return ((property->attributes & OBJC_PR_dynamic) == OBJC_PR_dynamic) ? strdup("") : 0; + } + } + return 0; +} + +PRIVATE BOOL initPropertyFromAttributesList(struct objc_property *p, + const char *name, + const objc_property_attribute_t *attributes, + unsigned int attributesCount) +{ + if (p == NULL || name == NULL) { return NO; } + memset(p, 0, sizeof(struct objc_property)); + p->name = strdup(name); + p->attributes = OBJC_PR_runtime_allocated; + + for (unsigned int i=0 ; itype_encoding = strdup(attributes[i].value); + break; + case 'V': + if (attributes[i].value != NULL) + p->ivar_name = strdup(attributes[i].value); + break; + case 'S': + if (attributes[i].value != NULL) + p->setter_name = strdup(attributes[i].value); + break; + case 'G': + if (attributes[i].value != NULL) + p->getter_name = strdup(attributes[i].value); + break; + case 'R': + p->attributes |= OBJC_PR_readonly; + break; + case 'C': + p->attributes |= OBJC_PR_copy; + break; + case '&': + p->attributes |= OBJC_PR_retain; + break; + case 'W': + p->attributes |= OBJC_PR_weak; + break; + case 'N': + p->attributes |= OBJC_PR_nonatomic; + break; + case 'D': + p->attributes |= OBJC_PR_dynamic; + break; + } + } + + p->attrs_string = (char *)malloc(32 + + ((p->type_encoding) ? strlen(p->type_encoding) : 0) + + ((p->getter_name) ? strlen(p->getter_name) : 0) + + ((p->setter_name) ? strlen(p->setter_name) : 0) + + ((p->ivar_name) ? strlen(p->ivar_name) : 0)); + char *s = (char *)p->attrs_string; + if (p->type_encoding != NULL) + { + *s++ = 'T'; + s = stpcpy(s, p->type_encoding); + *s++ = ','; + } + if ((p->attributes & OBJC_PR_readonly) == OBJC_PR_readonly) + { + s = stpcpy(s, "R,"); + } + if ((p->attributes & OBJC_PR_retain) == OBJC_PR_retain + || (p->attributes & OBJC_PR_strong) == OBJC_PR_strong) + { + s = stpcpy(s, "&,"); + } + if ((p->attributes & OBJC_PR_copy) == OBJC_PR_copy) + { + s = stpcpy(s, "C,"); + } + if ((p->attributes & OBJC_PR_weak) == OBJC_PR_weak) + { + s = stpcpy(s, "W,"); + } + if ((p->attributes & OBJC_PR_dynamic) == OBJC_PR_dynamic) + { + s = stpcpy(s, "D,"); + } + if ((p->attributes & OBJC_PR_nonatomic) == OBJC_PR_nonatomic) + { + s = stpcpy(s, "N,"); + } + if (p->getter_name != NULL) + { + *s++ = 'G'; + s = stpcpy(s, p->getter_name); + *s++ = ','; + } + if (p->setter_name != NULL) + { + *s++ = 'S'; + s = stpcpy(s, p->setter_name); + *s++ = ','; + } + if (p->ivar_name != NULL) + { + *s++ = 'V'; + s = stpcpy(s, p->ivar_name); + *s++ = ','; + } + // remove trailing ',' if any + if (s > p->attrs_string) s[-1] = '\0'; + + return YES; +} + +#else /* __clang__ && __has_feature(objc_property_clean_abi) */ const char *property_getName(objc_property_t property) { if (NULL == property) { return NULL; } @@ -678,3 +1029,5 @@ char *property_copyAttributeValue(objc_property_t property, } return 0; } + +#endif /* __clang__ && __has_feature(objc_property_clean_abi) */ diff --git a/protocol.c b/protocol.c index 987463b..a5be2a4 100644 --- a/protocol.c +++ b/protocol.c @@ -516,6 +516,7 @@ void protocol_addMethodDescription(Protocol *aProtocol, BOOL isInstanceMethod) { if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; } + LOCK_RUNTIME_FOR_SCOPE(); if (nil != aProtocol->isa) { return; } Protocol2 *proto = (Protocol2*)aProtocol; struct objc_method_description_list **listPtr; @@ -560,6 +561,7 @@ void protocol_addMethodDescription(Protocol *aProtocol, void protocol_addProtocol(Protocol *aProtocol, Protocol *addition) { if ((NULL == aProtocol) || (NULL == addition)) { return; } + LOCK_RUNTIME_FOR_SCOPE(); Protocol2 *proto = (Protocol2*)aProtocol; if (NULL == proto->protocol_list) { @@ -578,13 +580,28 @@ void protocol_addProtocol(Protocol *aProtocol, Protocol *addition) void protocol_addProperty(Protocol *aProtocol, const char *name, const objc_property_attribute_t *attributes, - unsigned int attributeCount, + unsigned int attributesCount, BOOL isRequiredProperty, BOOL isInstanceProperty) { - if ((NULL == aProtocol) || (NULL == name)) { return; } + if ((NULL == aProtocol) || (NULL == name) || !isInstanceProperty) { return; } +#if __clang__ && __has_feature(objc_property_clean_abi) + struct objc_property p; + if (!initPropertyFromAttributesList(&p, name, attributes, attributesCount)) + { + return; + } +#else /* __clang__ && __has_feature(objc_property_clean_abi) */ + const char *iVarName = NULL; + struct objc_property p = propertyFromAttrs(attributes, attributesCount, &iVarName); + if (iVarName) + { + constructPropertyAttributes(&p, iVarName); + } + p.name = strdup(name); +#endif /* __clang__ && __has_feature(objc_property_clean_abi) */ + LOCK_RUNTIME_FOR_SCOPE(); if (nil != aProtocol->isa) { return; } - if (!isInstanceProperty) { return; } Protocol2 *proto = (Protocol2*)aProtocol; struct objc_property_list **listPtr; if (isRequiredProperty) @@ -608,13 +625,6 @@ void protocol_addProperty(Protocol *aProtocol, } struct objc_property_list *list = *listPtr; int index = list->count-1; - const char *iVarName = NULL; - struct objc_property p = propertyFromAttrs(attributes, attributeCount, &iVarName); - if (iVarName) - { - constructPropertyAttributes(&p, iVarName); - } - p.name = strdup(name); memcpy(&(list->properties[index]), &p, sizeof(p)); } diff --git a/selector_table.c b/selector_table.c index 13b6ca5..760e4fd 100644 --- a/selector_table.c +++ b/selector_table.c @@ -11,7 +11,6 @@ #include "lock.h" #include "sarray2.h" #include "objc/runtime.h" -#include "method_list.h" #include "class.h" #include "selector.h" #include "visibility.h"