-
Notifications
You must be signed in to change notification settings - Fork 119
/
ivar.c
190 lines (181 loc) · 5.59 KB
/
ivar.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#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)
{
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;
}