ObjFW  Diff

Differences From Artifact [132bb904b5]:

  • File src/runtime/class.m — part of check-in [3b3729a316] at 2019-06-25 20:09:55 on branch trunk — runtime: Don't use static selectors

    Static selectors are initialized by a global constructor that might run
    too late: If a class has a +[load] that triggers an +[initialize], this
    could result in trying to call +[initialize] with the selector in
    class.m still being uninitialized. When this happens, it results in a
    weird looking crash in an entirely unrelated method. This is because
    when uninitialized, the selector name has not been replaced with a
    selector UID yet, meaning the pointer to the selector name is treated as
    the selector UID. As the dispatch only considers the low 16 bits of the
    selector UID for performance reasons, it will just take the low 16 bits
    of the pointer to the selector name as the UID without any complaints.
    This can then result in calling a random method on that class which then
    crashes. (user: js, size: 21746) [annotate] [blame] [check-ins using]

To Artifact [415251c564]:


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
static Class *loadQueue = NULL;
static size_t loadQueueCount = 0;
static struct objc_dtable *emptyDTable = NULL;
static unsigned lookupsUntilFastPath = 128;
static struct objc_sparsearray *fastPath = NULL;

static void
registerClass(struct objc_abi_class *rawClass)
{
	if (classes == NULL)
		classes = objc_hashtable_new(
		    objc_hash_string, objc_equal_string, 2);

	objc_hashtable_set(classes, rawClass->name, rawClass);

	if (emptyDTable == NULL)
		emptyDTable = objc_dtable_new();

	rawClass->DTable = emptyDTable;
	rawClass->metaclass->DTable = emptyDTable;

	if (strcmp(rawClass->name, "Protocol") != 0)
		classesCount++;
}

bool
class_registerAlias_np(Class class, const char *name)
{
	objc_global_mutex_lock();







|





|




|
|

|







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
static Class *loadQueue = NULL;
static size_t loadQueueCount = 0;
static struct objc_dtable *emptyDTable = NULL;
static unsigned lookupsUntilFastPath = 128;
static struct objc_sparsearray *fastPath = NULL;

static void
registerClass(Class class)
{
	if (classes == NULL)
		classes = objc_hashtable_new(
		    objc_hash_string, objc_equal_string, 2);

	objc_hashtable_set(classes, class->name, class);

	if (emptyDTable == NULL)
		emptyDTable = objc_dtable_new();

	class->DTable = emptyDTable;
	class->isa->DTable = emptyDTable;

	if (strcmp(class->name, "Protocol") != 0)
		classesCount++;
}

bool
class_registerAlias_np(Class class, const char *name)
{
	objc_global_mutex_lock();
67
68
69
70
71
72
73
74
75
76

77
78
79
80
81
82
83
84
85
86
87
88
89

	objc_global_mutex_unlock();

	return YES;
}

static void
registerSelectors(struct objc_abi_class *rawClass)
{
	struct objc_abi_method_list *methodList;


	for (methodList = rawClass->methodList; methodList != NULL;
	    methodList = methodList->next)
		for (unsigned int i = 0; i < methodList->count; i++)
			objc_register_selector((struct objc_abi_selector *)
			    &methodList->methods[i]);
}

Class
objc_classname_to_class(const char *name, bool cache)
{
	Class class;








|

|
>

|
<
|
|
<







67
68
69
70
71
72
73
74
75
76
77
78
79

80
81

82
83
84
85
86
87
88

	objc_global_mutex_unlock();

	return YES;
}

static void
registerSelectors(Class class)
{
	struct objc_method_list *iter;
	unsigned int i;

	for (iter = class->methodList; iter != NULL; iter = iter->next)

		for (i = 0; i < iter->count; i++)
			objc_register_selector(&iter->methods[i].selector);

}

Class
objc_classname_to_class(const char *name, bool cache)
{
	Class class;

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
		OBJC_ERROR("Not enough memory for subclass list of class %s\n",
		    class->superclass->name);

	class->superclass->subclassList[i] = class;
	class->superclass->subclassList[i + 1] = Nil;
}


static void
updateIVarOffsets(Class class)
{
	if (!(class->info & OBJC_CLASS_INFO_NEW_ABI))
		return;

	if (class->instanceSize > 0)
		return;

	class->instanceSize = -class->instanceSize;

	if (class->superclass != Nil) {
		class->instanceSize += class->superclass->instanceSize;

		if (class->iVars != NULL) {
			for (unsigned int i = 0; i < class->iVars->count; i++) {
				class->iVars->iVars[i].offset +=
				    class->superclass->instanceSize;
				*class->iVarOffsets[i] =
				    class->iVars->iVars[i].offset;
			}
		}
	} else
		for (unsigned int i = 0; i < class->iVars->count; i++)
			*class->iVarOffsets[i] = class->iVars->iVars[i].offset;
}

static void
setupClass(Class class)
{
	const char *superclassName;

	if (class->info & OBJC_CLASS_INFO_SETUP)
		return;

	superclassName = ((struct objc_abi_class *)class)->superclass;
	if (superclassName != NULL) {
		Class super = objc_classname_to_class(superclassName, false);


		if (super == Nil)
			return;

		setupClass(super);

		if (!(super->info & OBJC_CLASS_INFO_SETUP))
			return;









		class->superclass = super;

		class->isa->superclass = super->isa;

		addSubclass(class);
		addSubclass(class->isa);
	} else

		class->isa->superclass = class;


	updateIVarOffsets(class);

	class->info |= OBJC_CLASS_INFO_SETUP;
	class->isa->info |= OBJC_CLASS_INFO_SETUP;
}

static void
initializeClass(Class class)







<

|












|
|
|

|
|



|
|










|


>









>
>
>
>
>
>
>
>

>




|
>

|
>
|







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
		OBJC_ERROR("Not enough memory for subclass list of class %s\n",
		    class->superclass->name);

	class->superclass->subclassList[i] = class;
	class->superclass->subclassList[i + 1] = Nil;
}


static void
updateIvarOffsets(Class class)
{
	if (!(class->info & OBJC_CLASS_INFO_NEW_ABI))
		return;

	if (class->instanceSize > 0)
		return;

	class->instanceSize = -class->instanceSize;

	if (class->superclass != Nil) {
		class->instanceSize += class->superclass->instanceSize;

		if (class->ivars != NULL) {
			for (unsigned int i = 0; i < class->ivars->count; i++) {
				class->ivars->ivars[i].offset +=
				    class->superclass->instanceSize;
				*class->ivarOffsets[i] =
				    class->ivars->ivars[i].offset;
			}
		}
	} else
		for (unsigned int i = 0; i < class->ivars->count; i++)
			*class->ivarOffsets[i] = class->ivars->ivars[i].offset;
}

static void
setupClass(Class class)
{
	const char *superclassName;

	if (class->info & OBJC_CLASS_INFO_SETUP)
		return;

	superclassName = (const char *)class->superclass;
	if (superclassName != NULL) {
		Class super = objc_classname_to_class(superclassName, false);
		Class rootClass;

		if (super == Nil)
			return;

		setupClass(super);

		if (!(super->info & OBJC_CLASS_INFO_SETUP))
			return;

		/*
		 * GCC sets class->isa->isa to the name of the root class,
		 * while Clang just sets it to Nil. Therefore always calculate
		 * it.
		 */
		for (Class iter = super; iter != NULL; iter = iter->superclass)
			rootClass = iter;

		class->superclass = super;
		class->isa->isa = rootClass->isa;
		class->isa->superclass = super->isa;

		addSubclass(class);
		addSubclass(class->isa);
	} else {
		class->isa->isa = class->isa;
		class->isa->superclass = class;
	}

	updateIvarOffsets(class);

	class->info |= OBJC_CLASS_INFO_SETUP;
	class->isa->info |= OBJC_CLASS_INFO_SETUP;
}

static void
initializeClass(Class class)
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
			if (loadQueue == NULL)
				OBJC_ERROR("Not enough memory for load queue!");
		}
	}
}

void
objc_register_all_classes(struct objc_abi_symtab *symtab)
{
	for (uint16_t i = 0; i < symtab->classDefsCount; i++) {
		struct objc_abi_class *rawClass =
		    (struct objc_abi_class *)symtab->defs[i];

		registerClass(rawClass);
		registerSelectors(rawClass);
		registerSelectors(rawClass->metaclass);
	}

	for (uint16_t i = 0; i < symtab->classDefsCount; i++) {
		Class class = (Class)symtab->defs[i];

		if (hasLoad(class)) {
			setupClass(class);







|


<
|

|
|
|







424
425
426
427
428
429
430
431
432
433

434
435
436
437
438
439
440
441
442
443
444
445
			if (loadQueue == NULL)
				OBJC_ERROR("Not enough memory for load queue!");
		}
	}
}

void
objc_register_all_classes(struct objc_symtab *symtab)
{
	for (uint16_t i = 0; i < symtab->classDefsCount; i++) {

		Class class = (Class)symtab->defs[i];

		registerClass(class);
		registerSelectors(class);
		registerSelectors(class->isa);
	}

	for (uint16_t i = 0; i < symtab->classDefsCount; i++) {
		Class class = (Class)symtab->defs[i];

		if (hasLoad(class)) {
			setupClass(class);
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
}

void
objc_registerClassPair(Class class)
{
	objc_global_mutex_lock();

	registerClass((struct objc_abi_class *)class);

	if (class->superclass != Nil) {
		addSubclass(class);
		addSubclass(class->isa);
	}

	class->info |= OBJC_CLASS_INFO_SETUP;







|







498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
}

void
objc_registerClassPair(Class class)
{
	objc_global_mutex_lock();

	registerClass(class);

	if (class->superclass != Nil) {
		addSubclass(class);
		addSubclass(class->isa);
	}

	class->info |= OBJC_CLASS_INFO_SETUP;
757
758
759
760
761
762
763
764
765
766
767

768
769
770
771
772
773
774
775
776
777
778


779
780
781
782
783
784
785
786
787
788
789
790
	methodList->methods[0].implementation = implementation;

	class->methodList = methodList;

	objc_update_dtable(class);
}

const char *
class_getMethodTypeEncoding(Class class, SEL selector)
{
	struct objc_method *method;


	if (class == Nil)
		return NULL;

	objc_global_mutex_lock();

	if ((method = getMethod(class, selector)) != NULL) {
		const char *ret = method->selector.typeEncoding;
		objc_global_mutex_unlock();
		return ret;
	}



	objc_global_mutex_unlock();

	if (class->superclass != Nil)
		return class_getMethodTypeEncoding(class->superclass, selector);

	return NULL;
}

bool
class_addMethod(Class class, SEL selector, IMP implementation,
    const char *typeEncoding)







|
|

|
>







<

|

>
>



|
|







766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784

785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
	methodList->methods[0].implementation = implementation;

	class->methodList = methodList;

	objc_update_dtable(class);
}

Method
class_getInstanceMethod(Class class, SEL selector)
{
	Method method;
	Class superclass;

	if (class == Nil)
		return NULL;

	objc_global_mutex_lock();

	if ((method = getMethod(class, selector)) != NULL) {

		objc_global_mutex_unlock();
		return method;
	}

	superclass = class->superclass;

	objc_global_mutex_unlock();

	if (superclass != Nil)
		return class_getInstanceMethod(superclass, selector);

	return NULL;
}

bool
class_addMethod(Class class, SEL selector, IMP implementation,
    const char *typeEncoding)
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
{
	return class_getName(object_getClass(object));
}

static void
unregisterClass(Class class)
{
	struct objc_abi_class *rawClass = (struct objc_abi_class *)class;

	if ((class->info & OBJC_CLASS_INFO_SETUP) && class->superclass != Nil &&
	    class->superclass->subclassList != NULL) {
		size_t i = SIZE_MAX, count = 0;
		Class *tmp;

		for (tmp = class->superclass->subclassList;
		    *tmp != Nil; tmp++) {







<
<







873
874
875
876
877
878
879


880
881
882
883
884
885
886
{
	return class_getName(object_getClass(object));
}

static void
unregisterClass(Class class)
{


	if ((class->info & OBJC_CLASS_INFO_SETUP) && class->superclass != Nil &&
	    class->superclass->subclassList != NULL) {
		size_t i = SIZE_MAX, count = 0;
		Class *tmp;

		for (tmp = class->superclass->subclassList;
		    *tmp != Nil; tmp++) {
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913

	if (class->DTable != NULL && class->DTable != emptyDTable)
		objc_dtable_free(class->DTable);

	class->DTable = NULL;

	if ((class->info & OBJC_CLASS_INFO_SETUP) && class->superclass != Nil)
		rawClass->superclass = class->superclass->name;

	class->info &= ~OBJC_CLASS_INFO_SETUP;
}

void
objc_unregister_class(Class class)
{







|







908
909
910
911
912
913
914
915
916
917
918
919
920
921
922

	if (class->DTable != NULL && class->DTable != emptyDTable)
		objc_dtable_free(class->DTable);

	class->DTable = NULL;

	if ((class->info & OBJC_CLASS_INFO_SETUP) && class->superclass != Nil)
		class->superclass = (Class)class->superclass->name;

	class->info &= ~OBJC_CLASS_INFO_SETUP;
}

void
objc_unregister_class(Class class)
{