/
class_table.c
399 lines (345 loc) · 9.91 KB
/
class_table.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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
#include "magic_objects.h"
#include "objc/runtime.h"
#include "objc/hooks.h"
#include "class.h"
#include "method_list.h"
#include "selector.h"
#include "lock.h"
#include <stdlib.h>
#include <assert.h>
void objc_register_selectors_from_class(Class class);
void *__objc_uninstalled_dtable;
void objc_init_protocols(struct objc_protocol_list *protos);
void objc_compute_ivar_offsets(Class class);
////////////////////////////////////////////////////////////////////////////////
// +load method hash table
////////////////////////////////////////////////////////////////////////////////
static int imp_compare(const void *i1, void *i2)
{
return i1 == i2;
}
static int32_t imp_hash(const void *imp)
{
return (int32_t)(((uintptr_t)imp) >> 4);
}
#define MAP_TABLE_NAME load_messages
#define MAP_TABLE_COMPARE_FUNCTION imp_compare
#define MAP_TABLE_HASH_KEY imp_hash
#define MAP_TABLE_HASH_VALUE imp_hash
#include "hash_table.h"
static load_messages_table *load_table;
SEL loadSel;
void objc_init_load_messages_table(void)
{
load_table = load_messages_create(4096);
loadSel = sel_registerName("load");
}
void objc_send_load_message(Class class)
{
for (struct objc_method_list *l=class->methods ; NULL!=l ; l=l->next)
{
for (int i=0 ; i<l->count ; i++)
{
Method m = &l->methods[i];
if (m->selector->name == loadSel->name)
{
if (load_messages_table_get(load_table, m->imp) == 0)
{
m->imp((id)class, loadSel);
load_messages_insert(load_table, m->imp);
}
}
}
}
}
// Get the functions for string hashing
#include "string_hash.h"
static int class_compare(const char *name, const Class class)
{
return string_compare(name, class->name);
}
static int class_hash(const Class class)
{
return string_hash(class->name);
}
#define MAP_TABLE_NAME class_table_internal
#define MAP_TABLE_COMPARE_FUNCTION class_compare
#define MAP_TABLE_HASH_KEY string_hash
#define MAP_TABLE_HASH_VALUE class_hash
// This defines the maximum number of classes that the runtime supports.
/*
#define MAP_TABLE_STATIC_SIZE 2048
#define MAP_TABLE_STATIC_NAME class_table
*/
#include "hash_table.h"
static class_table_internal_table *class_table;
#define unresolved_class_next subclass_list
#define unresolved_class_prev sibling_class
/**
* Linked list using the subclass_list pointer in unresolved classes.
*/
static Class unresolved_class_list;
////////////////////////////////////////////////////////////////////////////////
// Class table manipulation
////////////////////////////////////////////////////////////////////////////////
void class_table_insert(Class class)
{
if (!objc_test_class_flag(class, objc_class_flag_resolved))
{
if (Nil != unresolved_class_list)
{
unresolved_class_list->unresolved_class_prev = class;
}
class->unresolved_class_next = unresolved_class_list;
unresolved_class_list = class;
}
class_table_internal_insert(class_table, class);
}
Class class_table_get_safe(const char *class_name)
{
return class_table_internal_table_get(class_table, class_name);
}
Class class_table_next(void **e)
{
return class_table_internal_next(class_table,
(struct class_table_internal_table_enumerator**)e);
}
void __objc_init_class_tables(void)
{
class_table = class_table_internal_create(4096);
objc_init_load_messages_table();
}
////////////////////////////////////////////////////////////////////////////////
// Loader functions
////////////////////////////////////////////////////////////////////////////////
BOOL objc_resolve_class(Class cls)
{
// Skip this if the class is already resolved.
if (objc_test_class_flag(cls, objc_class_flag_resolved)) { return YES; }
// We can only resolve the class if its superclass is resolved.
if (cls->super_class)
{
Class super = (Class)objc_getClass((char*)cls->super_class);
if (Nil == super) { return NO; }
if (!objc_test_class_flag(cls, objc_class_flag_resolved))
{
if (!objc_resolve_class(super))
{
return NO;
}
}
}
// Give up if we can't resolve the root class yet...
static Class root_class = Nil;
if (Nil == root_class)
{
root_class = (Class)objc_getClass(ROOT_OBJECT_CLASS_NAME);
if (Nil == root_class) { return NO; }
if (!objc_test_class_flag(root_class, objc_class_flag_resolved))
{
objc_resolve_class(root_class);
}
assert(root_class);
}
// Remove the class from the unresolved class list
if (Nil == cls->unresolved_class_prev)
{
unresolved_class_list = cls->unresolved_class_next;
}
else
{
cls->unresolved_class_prev->unresolved_class_next =
cls->unresolved_class_next;
}
if (Nil != cls->unresolved_class_next)
{
cls->unresolved_class_next->unresolved_class_prev =
cls->unresolved_class_prev;
}
cls->unresolved_class_prev = Nil;
cls->unresolved_class_next = Nil;
// Resolve the superclass pointer
// If this class has no superclass, use [NS]Object
Class super = root_class;
Class superMeta = root_class;
if (NULL != cls->super_class)
{
// Resolve the superclass if it isn't already resolved
super = (Class)objc_getClass((char*)cls->super_class);
if (!objc_test_class_flag(super, objc_class_flag_resolved))
{
objc_resolve_class(super);
}
superMeta = super->isa;
// Set the superclass pointer for the class and the superclass
cls->super_class = super;
cls->isa->super_class = super->isa;
}
// Don't make the root class a subclass of itself
if (cls != super)
{
// Set up the class links
cls->sibling_class = super->subclass_list;
super->subclass_list = cls;
// Set up the metaclass links
cls->isa->sibling_class = superMeta->subclass_list;
superMeta->subclass_list = cls->isa;
}
// Mark this class (and its metaclass) as resolved
objc_set_class_flag(cls, objc_class_flag_resolved);
objc_set_class_flag(cls->isa, objc_class_flag_resolved);
// Fix up the ivar offsets
objc_compute_ivar_offsets(cls);
// Send the +load message, if required
objc_send_load_message(cls->isa);
if (_objc_load_callback)
{
_objc_load_callback(cls, 0);
}
return YES;
}
void objc_resolve_class_links(void)
{
LOCK_UNTIL_RETURN(__objc_runtime_mutex);
Class class = unresolved_class_list;
BOOL resolvedClass;
do
{
resolvedClass = NO;
while ((Nil != class))
{
Class next = class->unresolved_class_next;
objc_resolve_class(class);
if (resolvedClass ||
objc_test_class_flag(class, objc_class_flag_resolved))
{
resolvedClass = YES;
}
class = next;
}
} while (resolvedClass);
}
void __objc_resolve_class_links(void)
{
static BOOL warned = NO;
if (!warned)
{
fprintf(stderr,
"Warning: Calling deprecated private ObjC runtime function %s\n", __func__);
warned = YES;
}
objc_resolve_class_links();
}
// FIXME: Remove this once all uses of it in the runtime have been removed
void __objc_add_class_to_hash(Class class)
{
Class old_class = class_table_get_safe(class->name);
// If this class is already in the class table, we don't need to do anything.
if (Nil != old_class) { return; }
LOCK_UNTIL_RETURN(__objc_runtime_mutex);
// Note: The old runtime set the class number here. Nothing ever used the
// class number though, so we don't bother.
class_table_insert (class);
}
/**
* Loads a class. This function assumes that the runtime mutex is locked.
*/
void objc_load_class(struct objc_class *class)
{
// The compiler initialises the super class pointer to the name of the
// superclass, not the superclass pointer.
// Note: With the new ABI, the class pointer is public. We could,
// therefore, directly reference the superclass from the compiler and make
// the linker resolve it. This should be done in the GCC-incompatible ABI.
const char *superclassName = (char*)class->super_class;
// Work around a bug in some versions of GCC that don't initialize the
// class structure correctly.
class->subclass_list = NULL;
// Insert the class into the class table
class_table_insert (class);
// Register all of the selectors used by this class and its metaclass
objc_register_selectors_from_class(class);
objc_register_selectors_from_class(class->isa);
// Set the uninstalled dtable. The compiler could do this as well.
class->dtable = __objc_uninstalled_dtable;
class->isa->dtable = __objc_uninstalled_dtable;
// If this is a root class, make the class into the metaclass's superclass.
// This means that all instance methods will be available to the class.
if (NULL == superclassName)
{
class->isa->super_class = class;
}
if (class->protocols)
{
objc_init_protocols(class->protocols);
}
}
////////////////////////////////////////////////////////////////////////////////
// Public API
////////////////////////////////////////////////////////////////////////////////
int objc_getClassList(Class *buffer, int bufferLen)
{
if (buffer == NULL)
{
return class_table->table_used;
}
int count = 0;
struct class_table_internal_table_enumerator *e = NULL;
Class next;
while (count < bufferLen &&
(next = class_table_internal_next(class_table, &e)))
{
buffer[count++] = next;
}
return count;
}
Class class_getSuperclass(Class cls)
{
if (!objc_test_class_flag(cls, objc_class_flag_resolved))
{
objc_resolve_class(cls);
}
return cls->super_class;
}
id objc_getClass(const char *name)
{
id class = (id)class_table_get_safe(name);
if (nil != class) { return class; }
if (0 != _objc_lookup_class)
{
class = (id)_objc_lookup_class(name);
}
return class;
}
id objc_lookUpClass(const char *name)
{
return (id)class_table_get_safe(name);
}
id objc_getMetaClass(const char *name)
{
Class cls = (Class)objc_getClass(name);
return cls == Nil ? nil : (id)cls->isa;
}
// Legacy interface compatibility
id objc_get_class(const char *name)
{
return objc_getClass(name);
}
id objc_lookup_class(const char *name)
{
return objc_getClass(name);
}
id objc_get_meta_class(const char *name)
{
return objc_getMetaClass(name);
}
Class objc_next_class(void **enum_state)
{
return class_table_next ( enum_state);
}
Class class_pose_as(Class impostor, Class super_class)
{
fprintf(stderr, "Class posing is no longer supported.\n");
fprintf(stderr, "Please use class_replaceMethod() instead.\n");
abort();
}