From 3dc096eb17c8d21ab89475e9c840f50567c7ff71 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Fri, 25 Dec 2015 17:02:23 +0000 Subject: [PATCH] Add new ABI ivar metadata structures. The new structure has extra fields for alignment and flags (e.g. ownership). Old structures are auto-upgraded at load time, so the rest of the code can assume the presence of the new structure type. --- class.h | 18 +++++++++- ivar.c | 91 ++++++++++++++++++++++---------------------------- ivar.h | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++---- runtime.c | 4 +++ 4 files changed, 152 insertions(+), 60 deletions(-) diff --git a/class.h b/class.h index 8da94eb..222b78f 100644 --- a/class.h +++ b/class.h @@ -121,7 +121,8 @@ struct objc_class /** * The version of the ABI used for this class. Zero indicates the ABI first * implemented by clang 1.0. One indicates the presence of bitmaps - * indicating the offsets of strong, weak, and unretained ivars. + * indicating the offsets of strong, weak, and unretained ivars. Two + * indicates that the new ivar structure is used. */ long abi_version; @@ -265,6 +266,21 @@ static inline BOOL objc_test_class_flag(struct objc_class *aClass, { return (aClass->info & (unsigned long)flag) == (unsigned long)flag; } +/** + * Checks the version of a class. Return values are: + * 0. Legacy GCC ABI compatible class. + * 1. First release of GNUstep ABI. + * 2. Second release of the GNUstep ABI, adds strong / weak ivar bitmaps. + * 3. Third release of the GNUstep ABI. Many cleanups. + */ +static inline int objc_get_class_version(struct objc_class *aClass) +{ + if (!objc_test_class_flag(aClass, objc_class_flag_new_abi)) + { + return 0; + } + return aClass->abi_version + 1; +} /** * Adds a class to the class table. diff --git a/ivar.c b/ivar.c index 8504dae..671eea4 100644 --- a/ivar.c +++ b/ivar.c @@ -10,9 +10,18 @@ ptrdiff_t objc_alignof_type(const char *); ptrdiff_t objc_sizeof_type(const char *); +static struct objc_ivar_list *upgradeIvarList(Class cls, struct objc_ivar_list_legacy *l); PRIVATE void objc_compute_ivar_offsets(Class class) { + struct objc_ivar_list_legacy *legacy = NULL; + // If this is an old ABI class, then replace the ivar list with the new + // version + if (objc_get_class_version(class) < 3) + { + legacy = (struct objc_ivar_list_legacy *)class->ivars; + class->ivars = upgradeIvarList(class, legacy); + } int i = 0; /* If this class was compiled with support for late-bound ivars, the * instance_size field will contain 0 - {the size of the instance variables @@ -56,30 +65,7 @@ PRIVATE void objc_compute_ivar_offsets(Class class) long ivar_size = (i+1 == class->ivars->count) ? (class_size - ivar->offset) : ivar->offset - class->ivars->ivar_list[i+1].offset; -#if 0 - // 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 fudge = (ivar_start +ivar->offset + sizeof(void*)) % 16; - if (fudge != 0) - { - // If this is the first ivar in the class, then - // we can eat some of the padding that the compiler - // added... - if ((i == 0) && (ivar->offset > 0) && ((ivar_start + sizeof(void*) %16) == 0)) - { - ivar->offset = 0; - } - else - { - ivar_start += fudge; - class->instance_size += fudge; - } - } - } -#endif + // FIXME: use alignment ivar->offset += ivar_start; /* If we're using the new ABI then we also set up the faster ivar * offset variables. @@ -88,6 +74,9 @@ PRIVATE void objc_compute_ivar_offsets(Class class) { *(class->ivar_offsets[i]) = ivar->offset; } + // If we have a legacy ivar list, update the offset in it too - + // code from older compilers may access this directly! + legacy->ivar_list[i].offset = ivar->offset; } } } @@ -144,53 +133,52 @@ PRIVATE void objc_compute_ivar_offsets(Class class) } } -typedef enum { - ownership_invalid, - ownership_strong, - ownership_weak, - ownership_unsafe -} ownership; -ownership ownershipForIvar(Class cls, Ivar ivar) +ivar_ownership ownershipForIvar(Class cls, int idx) { - struct objc_ivar_list *list = cls->ivars; - if ((ivar < list->ivar_list) || (ivar >= &list->ivar_list[list->count])) - { - // Try the superclass - if (cls->super_class) - { - return ownershipForIvar(cls->super_class, ivar); - } - return ownership_invalid; - } - if (!objc_test_class_flag(cls, objc_class_flag_new_abi)) + if (objc_get_class_version(cls) < 2) { return ownership_unsafe; } - if (cls->abi_version < 1) - { - return ownership_unsafe; - } - if (objc_bitfield_test(cls->strong_pointers, (ivar - list->ivar_list))) + if (objc_bitfield_test(cls->strong_pointers, idx)) { return ownership_strong; } - if (objc_bitfield_test(cls->weak_pointers, (ivar - list->ivar_list))) + if (objc_bitfield_test(cls->weak_pointers, idx)) { return ownership_weak; } return ownership_unsafe; } +static struct objc_ivar_list *upgradeIvarList(Class cls, struct objc_ivar_list_legacy *l) +{ + if (l == NULL) + { + return NULL; + } + struct objc_ivar_list *n = calloc(1, sizeof(struct objc_ivar_list) + + l->count*sizeof(struct objc_ivar)); + n->count = l->count; + for (int i=0 ; icount ; i++) + { + n->ivar_list[i].name = l->ivar_list[i].name; + n->ivar_list[i].type = l->ivar_list[i].type; + n->ivar_list[i].offset = l->ivar_list[i].offset; + n->ivar_list[i].align = objc_alignof_type(n->ivar_list[i].type); + ivarSetOwnership(&n->ivar_list[i], ownershipForIvar(cls, i)); + } + return n; +} + //////////////////////////////////////////////////////////////////////////////// // Public API functions //////////////////////////////////////////////////////////////////////////////// void object_setIvar(id object, Ivar ivar, id value) { - ownershipForIvar(object_getClass(object), ivar); id *addr = (id*)((char*)object + ivar_getOffset(ivar)); - switch (ownershipForIvar(object_getClass(object), ivar)) + switch (ivarGetOwnership(ivar)) { case ownership_strong: objc_storeStrong(addr, value); @@ -226,9 +214,8 @@ Ivar object_setInstanceVariable(id obj, const char *name, void *value) id object_getIvar(id object, Ivar ivar) { - ownershipForIvar(object_getClass(object), ivar); id *addr = (id*)((char*)object + ivar_getOffset(ivar)); - switch (ownershipForIvar(object_getClass(object), ivar)) + switch (ivarGetOwnership(ivar)) { case ownership_strong: return objc_retainAutoreleaseReturnValue(*addr); diff --git a/ivar.h b/ivar.h index ea5a564..a7b42bb 100644 --- a/ivar.h +++ b/ivar.h @@ -2,11 +2,6 @@ /** * Metadata structure for an instance variable. * - * Note: The modern Apple runtime apparently stores the alignment of the ivar - * here. We don't - we can compute it from the type, but it might be useful. - * - * It would also be good to add GC properties to this structure, and possibly - * an assignment policy (e.g. assign / retain / copy). */ struct objc_ivar { @@ -22,18 +17,91 @@ struct objc_ivar * The offset from the start of the object. When using the non-fragile * ABI, this is initialized by the compiler to the offset from the start of * the ivars declared by this class. It is then set by the runtime to the - * offset from the object pointer. + * offset from the object pointer. */ int offset; + /** + * Alignment of this ivar. + */ + int align; + /** + * Flags for this instance variable. + */ + int flags; }; +/** + * Instance variable ownership. + */ +typedef enum { + /** + * Invalid. Indicates that this is not an instance variable with ownership + * semantics. + */ + ownership_invalid = 0, + /** + * Strong ownership. Assignments to this instance variable should retain + * the assigned value. + */ + ownership_strong = 1, + /** + * Weak ownership. This ivar is a zeroing weak reference to an object. + */ + ownership_weak = 2, + /** + * Object that has `__unsafe_unretained` semantics. + */ + ownership_unsafe = 3 +} ivar_ownership; + +/** + * Mask applied to the flags field to indicate ownership. + */ +static const int ivar_ownership_mask = 3; + +static inline void ivarSetOwnership(Ivar ivar, ivar_ownership o) +{ + ivar->flags = (ivar->flags & ~ivar_ownership_mask) | o; +} + +/** + * Look up the ownership for a given instance variable. + */ +static inline ivar_ownership ivarGetOwnership(Ivar ivar) +{ + return (ivar_ownership)(ivar->flags & ivar_ownership_mask); +} + +/** + * Legacy ivar structure, inherited from the GCC ABI. + */ +struct objc_ivar_legacy +{ + /** + * Name of this instance variable. + */ + const char *name; + /** + * Type encoding for this instance variable. + */ + const char *type; + /** + * The offset from the start of the object. When using the non-fragile + * ABI, this is initialized by the compiler to the offset from the start of + * the ivars declared by this class. It is then set by the runtime to the + * offset from the object pointer. + */ + int offset; +}; + + /** * A list of instance variables declared on this class. Unlike the method * list, this is a single array and size. Categories are not allowed to add * instance variables, because that would require existing objects to be * reallocated, which is only possible with accurate GC (i.e. not in C). */ -struct objc_ivar_list +struct objc_ivar_list { /** * The number of instance variables in this list. @@ -45,3 +113,20 @@ struct objc_ivar_list */ struct objc_ivar ivar_list[]; }; + +/** + * Legacy version of the ivar list + */ +struct objc_ivar_list_legacy +{ + /** + * The number of instance variables in this list. + */ + int count; + /** + * An array of instance variable metadata structures. Note that this array + * has count elements. + */ + struct objc_ivar_legacy ivar_list[]; +}; + diff --git a/runtime.c b/runtime.c index 27a8725..9ba8ce0 100644 --- a/runtime.c +++ b/runtime.c @@ -142,6 +142,7 @@ BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, Ivar ivar = &cls->ivars->ivar_list[cls->ivars->count - 1]; ivar->name = strdup(name); ivar->type = strdup(types); + ivar->align = alignment; // Round up the offset of the ivar so it is correctly aligned. long offset = cls->instance_size; if (alignment != 0) @@ -725,6 +726,9 @@ Class objc_allocateClassPair(Class superclass, const char *name, size_t extraByt objc_class_flag_new_abi; newClass->dtable = uninstalled_dtable; + newClass->abi_version = 2; + metaClass->abi_version = 2; + if (Nil == superclass) { newClass->instance_size = sizeof(struct objc_class*);