#include #include #include #include #include "objc/runtime.h" #include "objc/objc-arc.h" #include "class.h" #include "visibility.h" #include "gc_ops.h" #include "legacy.h" ptrdiff_t objc_alignof_type(const char *); ptrdiff_t objc_sizeof_type(const char *); PRIVATE void objc_compute_ivar_offsets(Class class) { if (class->ivars == NULL) { Class super_class = class_getSuperclass(class); if (super_class != Nil) { class->instance_size = super_class->instance_size; } return; } if (class->ivars->size != sizeof(struct objc_ivar)) { fprintf(stderr, "Downgrading ivar struct not yet implemented"); abort(); } 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 * declared for just this class}. The individual instance variable offset * fields will then be the offsets from the start of the class, and so must * have the size of the parent class prepended. */ if (class->instance_size <= 0) { Class super = class_getSuperclass(class); long ivar_start = 0; if (Nil != super) { if (super->instance_size <= 0) { objc_compute_ivar_offsets(super); } ivar_start = super->instance_size; } class->instance_size = ivar_start; /* For each instance variable, we add the offset if required (it will be zero * if this class is compiled with a static ivar layout). We then set the * value of a global variable to the offset value. * * Any class compiled with support for the non-fragile ABI, but not actually * using it, will export the ivar offset field as a symbol. * * Note that using non-fragile ivars breaks @defs(). If you need equivalent * functionality, provide an alternative @interface with all variables * declared @public. */ if (class->ivars) { // If the first instance variable had any alignment padding, then we need // to discard it. We will recompute padding ourself later. long next_ivar = ivar_start; long last_offset = LONG_MIN; long last_size = 0; long last_computed_offset = -1; size_t refcount_size = sizeof(uintptr_t); for (i = 0 ; i < class->ivars->count ; i++) { struct objc_ivar *ivar = ivar_at_index(class->ivars, i); // Clang 7 and 8 have a bug where the size of _Bool is encoded // as 0, not 1. Silently fix this up when we see it. if (ivar->size == 0 && ivar->type[0] == 'B') { ivar->size = 1; } // We are going to be allocating an extra word for the reference count // in front of the object. This doesn't matter for aligment most of // the time, but if we have an instance variable that is a vector type // then we will need to ensure that we are properly aligned again. long ivar_size = ivar->size; // Bitfields have the same offset - the base of the variable // that contains them. If we are in a bitfield, then we need // to make sure that we don't add any displacement from the // previous value. if (*ivar->offset < last_offset + last_size) { *ivar->offset = last_computed_offset + (*ivar->offset - last_offset); ivar_size = 0; continue; } last_offset = *ivar->offset; *ivar->offset = next_ivar; last_computed_offset = *ivar->offset; next_ivar += ivar_size; last_size = ivar->size; size_t align = ivarGetAlign(ivar); if ((*ivar->offset + refcount_size) % align != 0) { long padding = align - ((*ivar->offset + refcount_size) % align); *ivar->offset += padding; class->instance_size += padding; next_ivar += padding; } assert((*ivar->offset + sizeof(uintptr_t)) % ivarGetAlign(ivar) == 0); class->instance_size += ivar_size; } #ifdef OLDABI_COMPAT // If we have a legacy ivar list, update the offset in it too - // code from older compilers may access this directly! struct objc_class_gsv1* legacy = objc_legacy_class_for_class(class); if (legacy) { for (i = 0 ; i < class->ivars->count ; i++) { legacy->ivars->ivar_list[i].offset = *ivar_at_index(class->ivars, i)->offset; } } #endif } } } //////////////////////////////////////////////////////////////////////////////// // Public API functions //////////////////////////////////////////////////////////////////////////////// void object_setIvar(id object, Ivar ivar, id value) { id *addr = (id*)((char*)object + ivar_getOffset(ivar)); switch (ivarGetOwnership(ivar)) { case ownership_strong: objc_storeStrong(addr, value); break; case ownership_weak: objc_storeWeak(addr, value); break; case ownership_unsafe: case ownership_invalid: *addr = value; break; } } Ivar object_setInstanceVariable(id obj, const char *name, void *value) { Ivar ivar = class_getInstanceVariable(object_getClass(obj), name); if (ivar_getTypeEncoding(ivar)[0] == '@') { object_setIvar(obj, ivar, *(id*)value); } else { size_t size = objc_sizeof_type(ivar_getTypeEncoding(ivar)); memcpy((char*)obj + ivar_getOffset(ivar), value, size); } return ivar; } id object_getIvar(id object, Ivar ivar) { id *addr = (id*)((char*)object + ivar_getOffset(ivar)); switch (ivarGetOwnership(ivar)) { case ownership_strong: return objc_retainAutoreleaseReturnValue(*addr); case ownership_weak: return objc_loadWeak(addr); break; case ownership_unsafe: case ownership_invalid: return *addr; return nil; } } Ivar object_getInstanceVariable(id obj, const char *name, void **outValue) { Ivar ivar = class_getInstanceVariable(object_getClass(obj), name); if (NULL != outValue) { *outValue = (((char*)obj) + ivar_getOffset(ivar)); } return ivar; }