1/*
2 * Objective-C runtime 2.0 compatibility for MacOS X 10.4 and earlier.
3 *
4 * This code works by poking into the ObjC runtime, which means loads of
5 * warnings on 10.5+ ;-)
6 */
7
8#define PYOBJC_COMPAT_IMPL
9#include "pyobjc.h"
10
11
12
13
14#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) &&!defined(__OBJC2__)
15
16BOOL PyObjC_class_isSubclassOf(Class child, Class parent)
17{
18	if (parent == nil) return YES;
19
20	while (child != nil) {
21		if (child == parent) {
22			return YES;
23		}
24		child = PyObjC_class_getSuperclass(child);
25	}
26	return NO;
27}
28
29#import <mach-o/dyld.h>
30#import <mach-o/getsect.h>
31#import <mach-o/loader.h>
32
33typedef struct _ProtocolTemplate { @defs(Protocol) } ProtocolTemplate;
34
35
36
37struct objc10_object {
38	Class	isa;
39};
40
41struct PyObjC_ivar {
42	char* name;
43	size_t size;
44	uint8_t alignment;
45	char* types;
46};
47
48
49static Protocol** compat_objc_copyProtocolList(unsigned int* outCount)
50{
51	Protocol** protocols = NULL;
52	*outCount = 0;
53
54	uint32_t image_count, image_index;
55	image_count = _dyld_image_count();
56	for (image_index = 0; image_index < image_count; image_index++) {
57		uint32_t size = 0;
58		const struct mach_header *mh = _dyld_get_image_header(image_index);
59		intptr_t slide = _dyld_get_image_vmaddr_slide(image_index);
60		ProtocolTemplate *protos = (ProtocolTemplate*)(
61			((char *)getsectdatafromheader(mh, SEG_OBJC, "__protocol", &size)) +
62			slide);
63		uint32_t nprotos = size / sizeof(ProtocolTemplate);
64		uint32_t i;
65
66		if (nprotos == 0) continue;
67
68		if (protocols == NULL) {
69			protocols = malloc(sizeof(Protocol*) * nprotos);
70			if (protocols == NULL) {
71				return NULL;
72			}
73		} else {
74			Protocol** tmp = realloc(protocols,
75				sizeof(Protocol*) * (*outCount+nprotos));
76			if (tmp == NULL) {
77				free(protocols);
78				return NULL;
79			}
80			protocols = tmp;
81		}
82
83		for (i = 0; i < nprotos; i++) {
84			protocols[(*outCount)++] = (Protocol*)&protos[i];
85		}
86	}
87	return protocols;
88}
89
90static Protocol*  compat_objc_getProtocol(const char* name)
91{
92	uint32_t image_count, image_index;
93	image_count = _dyld_image_count();
94	for (image_index = 0; image_index < image_count; image_index++) {
95		uint32_t size = 0;
96		const struct mach_header *mh = _dyld_get_image_header(image_index);
97		intptr_t slide = _dyld_get_image_vmaddr_slide(image_index);
98		ProtocolTemplate *protos = (ProtocolTemplate*)(
99			((char *)getsectdatafromheader(mh, SEG_OBJC, "__protocol", &size)) +
100			slide);
101		uint32_t nprotos = size / sizeof(ProtocolTemplate);
102		uint32_t i;
103
104		if (nprotos == 0) continue;
105
106		for (i = 0; i < nprotos; i++) {
107			Protocol* p = (Protocol*)&protos[i];
108			if (strcmp([p name], name) == 0) {
109				return p;
110			}
111		}
112	}
113	return nil;
114}
115
116
117static void
118compat_objc_registerClassPair(Class cls)
119{
120	struct objc_method_list* list;
121
122	/* Add the list in the official way, just in case class_addMethods
123	 * does something special.
124	 */
125	list = cls->methodLists[0];
126	cls->methodLists[0] = (struct objc_method_list*)-1;
127	class_addMethods(cls, list);
128
129	list = cls->isa->methodLists[0];
130	cls->isa->methodLists[0] = (struct objc_method_list*)-1;
131	class_addMethods(cls->isa, list);
132
133	objc_addClass(cls);
134}
135
136static Class
137compat_objc_allocateClassPair(Class super_class, const char* name, size_t extra)
138{
139	struct objc_class* result;
140	Class root_class;
141
142	if (objc_getClass(name)) {
143		return Nil;
144	}
145
146	root_class = super_class;
147	while (root_class->super_class) {
148		root_class = root_class->super_class;
149	}
150
151	result = malloc(sizeof(struct objc_class) * 2 + extra);
152	if (result == NULL) {
153		return Nil;
154	}
155	memset(result, 0, sizeof(struct objc_class) * 2 + extra);
156
157	result->super_class = super_class;
158	result->isa = result + 1;
159
160	result[0].methodLists = NULL;
161	result[1].methodLists = NULL;
162
163	result[0].info = CLS_CLASS;
164	result[1].info = CLS_META;
165
166	result[0].name = strdup(name);
167	if (result[0].name == NULL) goto error_cleanup;
168
169	result[1].name = result[0].name;
170	result[0].methodLists = malloc(sizeof(struct objc_method_list*));
171	if (result[0].methodLists == NULL) goto error_cleanup;
172	memset(result[0].methodLists, 0, sizeof(struct objc_method_list*));
173
174	result[1].methodLists = malloc(sizeof(struct objc_method_list*));
175	if (result[1].methodLists == NULL) goto error_cleanup;
176	memset(result[1].methodLists, 0, sizeof(struct objc_method_list*));
177
178	/*
179	 * This is MacOS X specific, and an undocumented feature (long live
180	 * Open Source!).
181	 *
182	 * The code in the objc runtime assumes that the method lists are
183	 * terminated by '-1', and will happily overwite existing data if
184	 * they aren't.
185	 *
186	 * Ronald filed a bugreport for this: Radar #3317376
187	 */
188	result[0].methodLists[0] = (struct objc_method_list*)-1;
189	result[1].methodLists[0] = (struct objc_method_list*)-1;
190
191	result[0].methodLists[0] = malloc(sizeof(struct objc_method_list));
192	if (result[0].methodLists[0] == NULL) goto error_cleanup;
193	result[0].methodLists[0]->method_count = 0;
194	result[0].methodLists[0]->obsolete = NULL;
195
196	result[1].methodLists[0] = malloc(sizeof(struct objc_method_list));
197	if (result[1].methodLists[0] == NULL) goto error_cleanup;
198	result[1].methodLists[0]->method_count = 0;
199	result[1].methodLists[0]->obsolete = NULL;
200
201
202	result[0].super_class = super_class;
203	result[1].super_class = ((struct objc10_object*)super_class)->isa;
204	result[1].isa = ((struct objc10_object*)root_class)->isa;
205
206	result[0].instance_size = super_class->instance_size;
207	result[1].instance_size = result[1].super_class->instance_size;
208
209	/* Initialize to NULL, otherwise poseAs: won't work */
210	result[0].ivars = result[1].ivars = NULL;
211
212	result[0].protocols = result[1].protocols = NULL;
213
214	return result;
215
216
217error_cleanup:
218	if (result) {
219		if (result[0].methodLists) {
220			if (result[0].methodLists[0] != 0 &&
221					result[0].methodLists[0] != 0)  {
222				free(result[0].methodLists[0]);
223			}
224			free(result[0].methodLists);
225		}
226		if (result[1].methodLists) {
227			if (result[1].methodLists[1] != 0 &&
228					result[1].methodLists[0] != 0)  {
229				free(result[1].methodLists[0]);
230			}
231			free(result[1].methodLists);
232		}
233
234		if (result[0].ivars) {
235			free(result[0].ivars);
236		}
237		if (result[1].ivars) {
238			free(result[1].ivars);
239		}
240
241		if (result[0].name) {
242			free((char*)result[0].name);
243		}
244		free(result);
245	}
246	return Nil;
247
248}
249
250static void
251compat_objc_disposeClassPair(Class cls)
252{
253	struct objc_class* val = cls;
254	int i;
255
256	for (i = 0; i < 2; i++) {
257		if (val[i].methodLists) {
258			struct objc_method_list** cur;
259
260			cur = val[i].methodLists;
261			if (*cur != (struct objc_method_list*)-1) {
262				free(*cur);
263				*cur = NULL;
264			}
265			free(val[i].methodLists);
266			val[i].methodLists = NULL;
267		}
268	}
269
270	free((char*)val[0].name);
271	val[0].name = val[1].name = NULL;
272	free(val);
273}
274
275
276static size_t
277compat_methodlist_magic(Class cls)
278{
279	void* iterator = NULL;
280	struct objc_method_list* mlist;
281	size_t res = 0, cnt = 0;
282
283        if (cls == NULL) return -1;
284
285	while ( (mlist = class_nextMethodList(cls, &iterator )) != NULL ) {
286		res += mlist->method_count;
287		cnt++;
288	}
289
290	return (cnt << 16) | (res & 0xFFFF);
291}
292
293#ifndef NO_OBJC2_RUNTIME
294static size_t
295objc20_methodlist_magic(Class cls)
296{
297	Method* methods;
298	unsigned int count;
299	unsigned int i;
300	size_t result;
301
302	methods = class_copyMethodList(cls, &count);
303	result = 0;
304	for (i = 0; i < count; i++) {
305		result = (1000003*result) ^ ((size_t)(
306				method_getImplementation(methods[i])) >> 3);
307	}
308	result = result | (count << 16);
309	free(methods);
310	return result;
311}
312#endif
313
314static Class
315compat_object_setClass(id obj, Class cls)
316{
317	Class prev;
318	if (obj == nil) {
319		return Nil;
320	}
321	prev = ((struct objc10_object*)obj)->isa;
322	((struct objc10_object*)obj)->isa = cls;
323	return prev;
324}
325
326
327
328static Class
329compat_object_getClass(id obj)
330{
331	return ((struct objc10_object*)obj)->isa;
332}
333
334static const char*
335compat_object_getClassName(id obj)
336{
337	return ((struct objc10_object*)obj)->isa->name;
338}
339
340
341
342static Method*
343compat_class_copyMethodList(Class cls, unsigned int* outCount)
344{
345	struct objc_method_list* mlist;
346	void* iterator;
347	unsigned int count;
348	Method* result;
349	Method* tmp;
350
351	iterator = NULL;
352	count = 0;
353
354	mlist = class_nextMethodList(cls, &iterator);
355	result = NULL;
356	while (mlist != NULL) {
357		int i;
358
359		tmp = realloc(result, (count + mlist->method_count) * sizeof(Method));
360		if (tmp == NULL) {
361			free(result);
362			return NULL;
363		} else {
364			result = tmp;
365		}
366
367		for (i = 0; i < mlist->method_count; i++) {
368			result[count] = mlist->method_list + i;
369			if (result[count] == NULL) continue;
370			count++;
371		}
372		mlist = class_nextMethodList(cls, &iterator);
373	}
374
375	if (outCount != NULL) {
376		*outCount = count;
377	}
378	return result;
379}
380
381static Ivar*
382compat_class_copyIvarList(Class cls, unsigned int* outCount)
383{
384	Ivar* list;
385	int   i;
386	struct objc_ivar_list* ivars = cls->ivars;
387
388	if (ivars) {
389		list = malloc(sizeof(Ivar) * ivars->ivar_count);
390		if (list == NULL) {
391			return NULL;
392		}
393
394
395		for (i = 0; i < ivars->ivar_count; i++) {
396			list[i] = ivars->ivar_list + i;
397		}
398		*outCount = ivars->ivar_count;
399		return list;
400
401	} else {
402		list = malloc(1);
403		if (list == NULL) {
404			return NULL;
405		}
406
407		*outCount = 0;
408		return list;
409	}
410
411}
412
413static Protocol**
414compat_class_copyProtocolList(Class cls, unsigned int* outCount)
415{
416	Protocol** list;
417	unsigned int count = 0;
418	struct objc_protocol_list *protocol_list;
419
420	protocol_list = cls->protocols;
421	list = malloc(0);
422
423	while (protocol_list != NULL) {
424		list = realloc(list, (count + protocol_list->count)*sizeof(Protocol*));
425		if (list == NULL) {
426			/* Whoops, memory leak */
427			*outCount = 0;
428			return NULL;
429		}
430		memcpy(list + count, protocol_list->list, protocol_list->count*sizeof(Protocol*));
431		count += protocol_list->count;
432		protocol_list = protocol_list->next;
433	}
434
435	*outCount = count;
436	return list;
437}
438
439
440static const char*
441compat_class_getName(Class cls)
442{
443	return cls->name;
444}
445
446static Class
447compat_class_getSuperclass(Class cls)
448{
449	return cls->super_class;
450}
451
452static BOOL
453compat_class_addMethod(Class cls, SEL name, IMP imp, const char* types)
454{
455	struct objc_method_list* methodsToAdd;
456	struct objc_method* objcMethod;
457
458	methodsToAdd = malloc(sizeof(struct objc_method_list) +
459			2*sizeof(struct objc_method));
460	methodsToAdd->method_count = 1;
461	methodsToAdd->obsolete = NULL;
462
463	objcMethod = methodsToAdd->method_list;
464	objcMethod->method_name = name;
465	objcMethod->method_imp = imp;
466	objcMethod->method_types = (char*)types;
467
468	class_addMethods(cls, methodsToAdd);
469	return YES; /* Have to return something ... */
470}
471
472static BOOL
473compat_preclass_addMethod(Class cls, SEL name, IMP imp, const char* types)
474{
475	/* Resize in-place, this is only valid during class construction. */
476	struct objc_method_list* new_list;
477	struct objc_method* objcMethod;
478
479	new_list = realloc(cls->methodLists[0],
480			sizeof(struct objc_method_list) +
481			(sizeof(struct objc_method
482				)*(cls->methodLists[0]->method_count+1)));
483	if (new_list == NULL) {
484		return NO;
485	}
486	cls->methodLists[0] = new_list;
487	objcMethod = new_list->method_list + (new_list->method_count)++;
488
489	objcMethod->method_name = name;
490	objcMethod->method_imp = imp;
491	objcMethod->method_types = strdup(types);
492	if (objcMethod == NULL) {
493		new_list->method_count--;
494		return NO;
495	}
496
497	return YES;
498}
499
500static BOOL
501compat_preclass_addIvar(
502		Class cls,
503		const char* name,
504		size_t size,
505		uint8_t align, const char* types)
506{
507	/* Update the class structure, only valid during class construction */
508	struct objc_ivar_list* new_ivars;
509	struct objc_ivar* ivar;
510
511	if (cls->ivars) {
512		new_ivars = realloc(cls->ivars,
513			sizeof(struct objc_ivar_list) +
514			((cls->ivars->ivar_count+1) * sizeof(struct objc_ivar)));
515	} else {
516		new_ivars = malloc(
517				sizeof(struct objc_ivar_list) +
518				sizeof(struct objc_ivar));
519		new_ivars->ivar_count = 0;
520	}
521	if (new_ivars == NULL) {
522		return NO;
523	}
524	cls->ivars = new_ivars;
525	ivar = new_ivars->ivar_list + new_ivars->ivar_count;
526	ivar->ivar_name = strdup(name);
527	if (ivar->ivar_name == NULL) {
528		return NO;
529	}
530
531	ivar->ivar_type = strdup(types);
532	if (ivar->ivar_type == NULL) {
533		free(ivar->ivar_name);
534		return NO;
535	}
536
537	ivar->ivar_offset = cls->instance_size;
538	if (ivar->ivar_offset % align) {
539		ivar->ivar_offset += align - (ivar->ivar_offset % align);
540	}
541
542	new_ivars->ivar_count ++;
543	cls->instance_size = ivar->ivar_offset + size;
544	return YES;
545}
546
547static BOOL
548compat_preclass_addProtocol(Class cls, Protocol* protocol)
549{
550	struct objc_protocol_list* protocols;
551
552	if (cls->protocols) {
553		protocols = realloc(cls->protocols,
554			sizeof(struct objc_protocol_list) +
555			  (sizeof(Protocol*)*(cls->protocols->count+1)));
556	} else {
557		protocols = malloc(sizeof(struct objc_protocol_list)
558				+ sizeof(Protocol*));
559		protocols->count = 0;
560		protocols->next = NULL;
561	}
562	if (protocols == NULL) {
563		return NO;
564	}
565	cls->protocols = protocols;
566	protocols->list[protocols->count] = protocol;
567	protocols->count++;
568	return YES;
569}
570
571static SEL
572compat_method_getName(Method m)
573{
574	return m->method_name;
575}
576
577static IMP
578compat_method_getImplementation(Method m)
579{
580	return m->method_imp;
581}
582
583static IMP
584compat_method_setImplementation(Method m, IMP imp)
585{
586	IMP result =  m->method_imp;
587	m->method_imp = imp;
588	return result;
589}
590
591static const char *
592compat_method_getTypeEncoding(Method m)
593{
594	return m->method_types;
595}
596
597static BOOL
598compat_sel_isEqual(SEL lhs, SEL rhs)
599{
600	return lhs == rhs;
601}
602
603static const char*
604compat_ivar_getName(Ivar var)
605{
606	return var->ivar_name;
607}
608
609static const char*
610compat_ivar_getTypeEncoding(Ivar var)
611{
612	return var->ivar_type;
613}
614
615static ptrdiff_t
616compat_ivar_getOffset(Ivar var)
617{
618	return var->ivar_offset;
619}
620
621
622#ifndef NO_OBJC2_RUNTIME
623static BOOL
624objc20_class_addMethodList(Class cls,
625		struct PyObjC_method* list, unsigned int count)
626{
627	unsigned int i;
628	BOOL r;
629	Method m;
630
631	for (i = 0; i < count; i++) {
632		r = class_addMethod(cls,
633			list[i].name, list[i].imp, list[i].type);
634		if (!r) {
635			m = class_getInstanceMethod(cls, list[i].name);
636			if (m != NULL) {
637				method_setImplementation(m, list[i].imp);
638			} else {
639				return NO;
640			}
641		}
642	}
643	return YES;
644}
645#endif
646
647static BOOL
648compat_class_isMetaClass(Class cls)
649{
650	return CLS_GETINFO(cls, CLS_META) == CLS_META;
651}
652
653static BOOL
654compat_class_addMethodList(Class cls,
655		struct PyObjC_method* list, unsigned int count)
656{
657	unsigned int i;
658	struct objc_method_list* method_list;
659
660	method_list = malloc(
661			sizeof(struct objc_method_list) +
662			((count+1) * sizeof(struct objc_method)));
663	if (method_list == NULL) {
664		return NO;
665	}
666	memset(method_list, 0,
667			sizeof(struct objc_method_list) +
668			((count+1) * sizeof(struct objc_method)));
669
670	method_list->method_count = 0;
671	method_list->obsolete = 0;
672
673	for (i = 0; i < count; i++) {
674		method_list->method_list[i].method_name = list[i].name;
675		method_list->method_list[i].method_types = (char*)list[i].type;
676		method_list->method_list[i].method_imp = list[i].imp;
677	}
678	method_list->method_count = count;
679	class_addMethods(cls, method_list);
680	return YES;
681}
682
683static BOOL
684compat_protocol_conformsToProtocol(Protocol *proto, Protocol *other)
685{
686	return [proto conformsTo:other];
687}
688
689static const char *
690compat_protocol_getName(Protocol *p)
691{
692	return [p name];
693}
694
695static struct objc_method_description *
696compat_protocol_copyMethodDescriptionList(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount)
697{
698	if (!isRequiredMethod) {
699		*outCount = 0;
700		return NULL;
701	}
702
703	struct objc_method_description_list* list;
704
705	if (isInstanceMethod) {
706		list = ((ProtocolTemplate*)p)->instance_methods;
707	} else {
708		list = ((ProtocolTemplate*)p)->class_methods;
709	}
710
711	if (list == NULL || list->count == 0) {
712		*outCount = 0;
713		return NULL;
714	}
715
716	*outCount = list->count;
717	struct objc_method_description* result;
718
719	result = malloc(sizeof(struct objc_method_description) * list->count);
720	if (result == NULL) {
721		return NULL;
722	}
723
724	int i;
725	*outCount = 0;
726	for (i = 0; i < list->count; i++) {
727		if (list->list[i].name == NULL) continue;
728		result[*outCount].name = list->list[i].name;
729		result[*outCount].types = list->list[i].types;
730		(*outCount)++;
731	}
732	return result;
733}
734
735static Protocol **
736compat_protocol_copyProtocolList(Protocol *proto, unsigned int *outCount)
737{
738	struct objc_protocol_list* list =
739		((ProtocolTemplate*)proto)->protocol_list;
740
741	*outCount = 0;
742
743	struct objc_protocol_list* cur;
744	for (cur = list; cur != NULL; cur = cur->next) {
745		*outCount += cur->count;
746	}
747
748	Protocol** result = (Protocol**)malloc(sizeof(Protocol*) * *outCount);
749	if (result == NULL) {
750		return NULL;
751	}
752
753	unsigned curIdx = 0;
754	for (cur = list; cur != NULL; cur = cur->next) {
755		int i;
756		for (i = 0; i < cur->count; i++) {
757			if (cur->list[i] != NULL) {
758				result[curIdx++] = cur->list[i];
759			} else {
760				*outCount--;
761			}
762		}
763	}
764	return result;
765}
766
767static struct objc_method_description
768compat_protocol_getMethodDescription(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod)
769{
770static struct objc_method_description empty_description =  { NULL, NULL };
771	if (!isRequiredMethod) {
772		return empty_description;
773	}
774
775	struct objc_method_description* result;
776
777	if (isInstanceMethod) {
778		result = [p descriptionForInstanceMethod: aSel];
779	} else {
780		result = [p descriptionForClassMethod: aSel];
781	}
782
783	if (result == NULL) {
784		return empty_description;
785	} else {
786		return *result;
787	}
788}
789
790static id
791compat_object_getIvar(id obj, Ivar ivar)
792{
793	return *(id*)(((char*)obj) + ivar->ivar_offset);
794}
795static void
796compat_object_setIvar(id obj, Ivar ivar, id value)
797{
798	*(id*)(((char*)obj) + ivar->ivar_offset) = value;
799}
800
801/* Dispatch table */
802BOOL (*PyObjC_protocol_conformsToProtocol)(Protocol *proto, Protocol *other) = NULL;
803const char *(*PyObjC_protocol_getName)(Protocol *p) = NULL;
804struct objc_method_description *(*PyObjC_protocol_copyMethodDescriptionList)(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount) = NULL;
805Protocol **(*PyObjC_protocol_copyProtocolList)(Protocol *proto, unsigned int *outCount) = NULL;
806struct objc_method_description (*PyObjC_protocol_getMethodDescription)(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod) = NULL;
807
808id (*PyObjC_object_getIvar)(id obj, Ivar ivar) = NULL;
809void (*PyObjC_object_setIvar)(id obj, Ivar ivar, id value) = NULL;
810
811
812
813Class (*PyObjC_objc_allocateClassPair)(Class, const char*, size_t) = NULL;
814void (*PyObjC_objc_registerClassPair)(Class) = NULL;
815void (*PyObjC_objc_disposeClassPair)(Class) = NULL;
816Protocol** (*PyObjC_objc_copyProtocolList)(unsigned int*) = NULL;
817Protocol*  (*PyObjC_objc_getProtocol)(const char* name) = NULL;
818
819BOOL (*PyObjC_preclass_addMethod)(Class, SEL, IMP, const char*) = NULL;
820BOOL (*PyObjC_preclass_addIvar)(Class cls,
821	const char *name, size_t size, uint8_t alignment,
822	const char *types) = NULL;
823BOOL (*PyObjC_preclass_addProtocol)(Class cls, Protocol *protocol) = NULL;
824
825
826Class   (*PyObjC_object_getClass)(id obj) = NULL;
827Class (*PyObjC_object_setClass)(id obj, Class cls) = NULL;
828const char* (*PyObjC_object_getClassName)(id obj) = NULL;
829
830Method* (*PyObjC_class_copyMethodList)(Class, unsigned int*) = NULL;
831const char*  (*PyObjC_class_getName)(Class) = NULL;
832Class (*PyObjC_class_getSuperclass)(Class) = NULL;
833BOOL (*PyObjC_class_addMethod)(Class, SEL, IMP, const char*) = NULL;
834BOOL (*PyObjC_class_addMethodList)(Class,
835		struct PyObjC_method*, unsigned int) = NULL;
836Ivar* (*PyObjC_class_copyIvarList)(Class, unsigned int*) = NULL;
837Protocol** (*PyObjC_class_copyProtocolList)(Class, unsigned int*) = NULL;
838BOOL (*PyObjC_class_isMetaClass)(Class) = NULL;
839
840SEL (*PyObjC_method_getName)(Method m) = NULL;
841IMP (*PyObjC_method_getImplementation)(Method m) = NULL;
842IMP (*PyObjC_method_setImplementation)(Method m, IMP imp) = NULL;
843const char *(*PyObjC_method_getTypeEncoding)(Method m) = NULL;
844
845BOOL (*PyObjC_sel_isEqual)(SEL, SEL) = NULL;
846
847size_t (*PyObjC_methodlist_magic)(Class cls);
848
849const char*  (*PyObjC_ivar_getName)(Ivar) = NULL;
850const char*  (*PyObjC_ivar_getTypeEncoding)(Ivar) = NULL;
851ptrdiff_t    (*PyObjC_ivar_getOffset)(Ivar) = NULL;
852
853
854
855
856#else
857
858
859BOOL PyObjC_class_isSubclassOf(Class child, Class parent)
860{
861	if (parent == nil) return YES;
862
863	while (child != nil) {
864		if (child == parent) {
865			return YES;
866		}
867		child = class_getSuperclass(child);
868	}
869	return NO;
870}
871
872BOOL PyObjC_class_addMethodList(Class cls,
873		struct PyObjC_method* list, unsigned int count)
874{
875	unsigned int i;
876	BOOL r;
877	Method m;
878
879	for (i = 0; i < count; i++) {
880		/*
881		 * XXX: First try class_addMethod, if that fails assume this is
882		 * because the method already exists in the class.
883		 * Strictly speaking this isn't correct, but this is the best
884		 * we can do through the 2.0 API (see 4809039 in RADAR)
885		 */
886		r = class_addMethod(cls,
887			list[i].name, list[i].imp, list[i].type);
888		if (!r) {
889			m = class_getInstanceMethod(cls, list[i].name);
890			if (m != NULL) {
891				method_setImplementation(m, list[i].imp);
892			} else {
893				return NO;
894			}
895		}
896	}
897	return YES;
898}
899
900size_t PyObjC_methodlist_magic(Class cls)
901{
902	/* This is likely to be much slower than compat_methodlist_magic,
903	 * but should works on the 64-bit runtime. Hopefully a callback will
904	 * be added to the 2.0 runtime that will take away the need for this
905	 * function...
906	 */
907	Method* methods;
908	unsigned int count;
909
910	methods = class_copyMethodList(cls, &count);
911	free(methods);
912	return (size_t)count;
913}
914
915#endif
916
917#if defined(__x86_64__)
918
919@implementation Protocol (NSOBjectCompat)
920- (id)self
921{
922	return self;
923}
924@end
925
926#if PyObjC_BUILD_RELEASE < 1008
927@implementation Object (NSOBjectCompat)
928- (id)self
929{
930	return self;
931}
932
933-doesNotRecognizeSelector:(SEL)sel
934{
935	printf("--> %s\n", sel_getName(sel));
936	abort();
937}
938@end
939#endif
940
941
942#endif
943
944#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7)
945Protocol* (*PyObjC_objc_allocateProtocol)(const char *) = NULL;
946void (*PyObjC_objc_registerProtocol)(Protocol*) = NULL;
947void (*PyObjC_protocol_addMethodDescription)(Protocol*, SEL, const char*, BOOL, BOOL) = NULL;
948void (*PyObjC_protocol_addProtocol)(Protocol*, Protocol*) = NULL;
949
950#ifndef __LP64__
951struct Protocol_struct {
952	Class _isa;
953	char *protocol_name;
954	struct objc_protocol_list *protocol_list;
955	struct objc_method_description_list *instance_methods, *class_methods;
956	struct objc_method_description_list *optional_instance_methods, *optional_class_methods;
957	void *instance_properties;
958};
959
960static Protocol* compat_objc_allocateProtocol(const char *name)
961{
962	struct Protocol_struct* result;
963
964	result = (struct Protocol_struct*)NSAllocateObject([Protocol class], 0, NULL);
965	if (result == NULL) {
966		return NULL;
967	}
968	result->protocol_name = strdup(name);
969	if (result->protocol_name == NULL) {
970		/* Leaking object */
971		return NULL;
972	}
973	result->protocol_list = NULL;
974	result->instance_methods = NULL;
975	result->class_methods = NULL;
976	result->optional_instance_methods = NULL;
977	result->optional_class_methods = NULL;
978	result->instance_properties = NULL;
979	return (Protocol*)result;
980}
981
982static void compat_objc_registerProtocol(Protocol* proto __attribute__((__unused__)))
983{
984	/* Don't know how to register a new protocol in classic
985	 * runtime. Luckily we don't actually need this.
986	 */
987}
988
989static void compat_protocol_addMethodDescription(Protocol* proto, SEL sel, const char* types, BOOL required, BOOL instance_method)
990{
991	struct Protocol_struct* proto_struct = (struct Protocol_struct*)proto;
992	struct objc_method_description_list** plist;
993
994	if (!instance_method) {
995		if (required) {
996			plist = &(proto_struct->class_methods);
997		} else {
998			plist = &(proto_struct->optional_class_methods);
999		}
1000	} else {
1001		if (required) {
1002			plist = &(proto_struct->instance_methods);
1003		} else {
1004			plist = &(proto_struct->optional_instance_methods);
1005		}
1006	}
1007
1008	if (*plist == NULL) {
1009		*plist = malloc(sizeof(struct objc_method_description_list) + (2*sizeof(struct objc_method_description)));
1010		if (*plist == NULL) {
1011			/* Cannot report errors */
1012			abort();
1013		}
1014		(*plist)->count = 0;
1015	} else {
1016		*plist = realloc(*plist, sizeof(struct objc_method_description_list) + (2+((*plist)->count)*sizeof(struct objc_method_description)));
1017		if (*plist == NULL) {
1018			/* Cannot report errors */
1019			abort();
1020		}
1021	}
1022	(*plist)->list[(*plist)->count].name = sel;
1023	(*plist)->list[(*plist)->count].types = strdup(types);
1024	if ((*plist)->list[(*plist)->count].types == NULL) {
1025		/* Cannot report errors */
1026		abort();
1027	}
1028	(*plist)->count++;
1029
1030	(*plist)->list[(*plist)->count].name = NULL;
1031	(*plist)->list[(*plist)->count].types = NULL;
1032}
1033
1034static void compat_protocol_addProtocol(Protocol* proto, Protocol* newProto)
1035{
1036	struct Protocol_struct* proto_struct = (struct Protocol_struct*)proto;
1037
1038	if (proto_struct->protocol_list == NULL) {
1039		proto_struct->protocol_list = malloc(sizeof(struct objc_protocol_list) + 2*sizeof(Protocol*));
1040		if (proto_struct->protocol_list == NULL) {
1041			/* Cannot report an error! */
1042			abort();
1043		}
1044		proto_struct->protocol_list->next = NULL;
1045		proto_struct->protocol_list->count = 0;
1046	} else {
1047		proto_struct->protocol_list = realloc(proto_struct->protocol_list,
1048				sizeof(struct objc_protocol_list) + (2+proto_struct->protocol_list->count)*sizeof(Protocol*));
1049		if (proto_struct->protocol_list == NULL) {
1050			/* Cannot report an error! */
1051			abort();
1052		}
1053	}
1054	proto_struct->protocol_list->list[proto_struct->protocol_list->count] = newProto;
1055	proto_struct->protocol_list->list[proto_struct->protocol_list->count+1] = NULL;
1056	proto_struct->protocol_list->count++;
1057}
1058
1059#endif
1060#endif
1061
1062void PyObjC_SetupRuntimeCompat(void)
1063{
1064#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)  && !defined(__OBJC2__)
1065
1066#ifdef NO_OBJC2_RUNTIME
1067	/*
1068	 * Don't use ObjC 2.0 runtime (compiling on 10.4 or earlier), always
1069	 * use the compat implementation.
1070	 */
1071	PyObjC_class_addMethodList  = compat_class_addMethodList;
1072	PyObjC_methodlist_magic     = compat_methodlist_magic;
1073	PyObjC_objc_disposeClassPair   = compat_objc_disposeClassPair;
1074	PyObjC_preclass_addMethod   = compat_preclass_addMethod;
1075	PyObjC_preclass_addIvar     = compat_preclass_addIvar;
1076	PyObjC_preclass_addProtocol = compat_preclass_addProtocol;
1077
1078#   define SETUP(funcname) \
1079		PyObjC_##funcname = compat_##funcname
1080
1081#else
1082	if (class_addMethod) {
1083		PyObjC_class_addMethodList = objc20_class_addMethodList;
1084		PyObjC_preclass_addMethod  = class_addMethod;
1085		PyObjC_preclass_addIvar    = class_addIvar;
1086		PyObjC_preclass_addProtocol= class_addProtocol;
1087	} else {
1088		PyObjC_class_addMethodList = compat_class_addMethodList;
1089		PyObjC_preclass_addMethod  = compat_preclass_addMethod;
1090		PyObjC_preclass_addIvar    = compat_preclass_addIvar;
1091		PyObjC_preclass_addProtocol= compat_preclass_addProtocol;
1092	}
1093
1094
1095	if (class_copyMethodList) {
1096		PyObjC_methodlist_magic = objc20_methodlist_magic;
1097	} else {
1098		PyObjC_methodlist_magic = compat_methodlist_magic;
1099	}
1100
1101#   define SETUP(funcname) \
1102	if ((funcname) == NULL) { \
1103		PyObjC_##funcname = compat_##funcname; \
1104	} else { \
1105		PyObjC_##funcname = funcname; \
1106	}
1107#endif
1108	SETUP(protocol_getName);
1109	SETUP(protocol_conformsToProtocol);
1110	SETUP(protocol_copyMethodDescriptionList);
1111	SETUP(protocol_copyProtocolList);
1112	SETUP(protocol_getMethodDescription);
1113
1114	SETUP(objc_allocateClassPair);
1115	SETUP(objc_registerClassPair);
1116	SETUP(objc_disposeClassPair);
1117	SETUP(objc_copyProtocolList);
1118	SETUP(objc_getProtocol);
1119
1120	SETUP(object_getClass);
1121	SETUP(object_setClass);
1122	SETUP(object_getClassName);
1123	SETUP(object_getIvar);
1124	SETUP(object_setIvar);
1125
1126	SETUP(class_getSuperclass);
1127	SETUP(class_addMethod);
1128	SETUP(class_copyIvarList);
1129	SETUP(class_copyProtocolList);
1130	SETUP(class_copyMethodList);
1131	SETUP(class_getName);
1132	SETUP(class_isMetaClass);
1133
1134	SETUP(method_getName);
1135	SETUP(method_getTypeEncoding);
1136	SETUP(method_getImplementation);
1137	SETUP(method_setImplementation);
1138
1139	SETUP(sel_isEqual);
1140
1141	SETUP(ivar_getName);
1142	SETUP(ivar_getTypeEncoding);
1143	SETUP(ivar_getOffset);
1144#endif /* MIN_REQUIRED < 10.5 && !OBJC2 */
1145
1146#ifdef SETUP
1147#undef SETUP
1148#endif
1149
1150
1151#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7)
1152	/* Compat definitions for protocol creation
1153	 *
1154	 * - Runtime APIs were introduced in OSX 10.7
1155	 * - Compat functions can only be implemented for 32-bit runtime
1156	 */
1157
1158#if PyObjC_BUILD_RELEASE < 1007
1159
1160#ifndef __LP64__
1161	PyObjC_objc_allocateProtocol = compat_objc_allocateProtocol;
1162	PyObjC_objc_registerProtocol = compat_objc_registerProtocol;
1163	PyObjC_protocol_addMethodDescription = compat_protocol_addMethodDescription;
1164	PyObjC_protocol_addProtocol = compat_protocol_addProtocol;
1165#endif
1166
1167
1168#elif defined(__LP64__)
1169	PyObjC_objc_allocateProtocol = objc_allocateProtocol;
1170	PyObjC_objc_registerProtocol = objc_registerProtocol;
1171	PyObjC_protocol_addMethodDescription = protocol_addMethodDescription;
1172	PyObjC_protocol_addProtocol = protocol_addProtocol;
1173
1174#else
1175#   define SETUP(funcname) \
1176	if ((funcname) == NULL) { \
1177		PyObjC_##funcname = compat_##funcname; \
1178	} else { \
1179		PyObjC_##funcname = funcname; \
1180	}
1181	SETUP(objc_allocateProtocol);
1182	SETUP(objc_registerProtocol);
1183	SETUP(protocol_addMethodDescription);
1184	SETUP(protocol_addProtocol);
1185#endif
1186
1187#endif
1188
1189#undef SETUP
1190}
1191