You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
5.5 KiB
C
190 lines
5.5 KiB
C
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#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)
|
|
{
|
|
struct objc_ivar_list_legacy *legacy = NULL;
|
|
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;
|
|
}
|
|
long class_size = 0 - class->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 = -1;
|
|
long last_computed_offset = -1;
|
|
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;
|
|
if (*ivar->offset == last_offset)
|
|
{
|
|
*ivar->offset = last_computed_offset;
|
|
}
|
|
else
|
|
{
|
|
last_offset = *ivar->offset;
|
|
*ivar->offset = next_ivar;
|
|
last_computed_offset = *ivar->offset;
|
|
next_ivar += ivar_size;
|
|
}
|
|
// 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 && (ivarGetAlign(ivar) > __alignof__(void*)))
|
|
{
|
|
long offset = *ivar->offset + sizeof(intptr_t);
|
|
long padding = ivarGetAlign(ivar) - (offset % ivarGetAlign(ivar));
|
|
*ivar->offset += padding;
|
|
class->instance_size += padding;
|
|
next_ivar += padding;
|
|
assert((*ivar->offset + sizeof(intptr_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;
|
|
}
|