2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "NGObjCRuntime.h"
23 #include "NGMemoryAllocation.h"
24 #include <objc/objc.h>
25 #include <objc/objc-api.h>
29 #if NeXT_RUNTIME || APPLE_RUNTIME
30 # include <objc/objc-class.h>
31 # include <objc/objc-runtime.h>
32 typedef struct objc_method_list *MethodList_t;
33 typedef struct objc_ivar_list *IvarList_t;
34 typedef struct objc_method *Method_t;
36 # include <objc/encoding.h>
39 #import <Foundation/NSObject.h>
40 #import <Foundation/NSException.h>
41 #import <Foundation/NSString.h>
42 #import <Foundation/NSSet.h>
43 #import <Foundation/NSEnumerator.h>
45 #define GCC_VERSION (__GNUC__ * 10000 \
46 + __GNUC_MINOR__ * 100 \
47 + __GNUC_PATCHLEVEL__)
49 #if GNUSTEP_BASE_LIBRARY
50 /* this is a hack for the extensions 0.8.6 library */
52 void class_add_behavior(Class class, Class behavior) {
53 extern void behavior_class_add_class (Class class, Class behavior);
54 behavior_class_add_class(class, behavior);
59 @interface _NGMethodNameEnumerator : NSEnumerator
62 BOOL includeSuperclassMethods;
65 struct objc_method_list *methods; // current method list
66 unsigned i; // current index in current method list
67 #if APPLE_RUNTIME || NeXT_RUNTIME
68 void *iter; // runtime iterator
72 - (id)initWithClass:(Class)_clazz includeSuperclassMethods:(BOOL)_flag;
77 #if NeXT_RUNTIME || APPLE_RUNTIME
79 static __inline__ unsigned NGGetSizeOfType(char *ivarType) {
80 // TODO: add more types ...
82 /* the Apple runtime has no func to calc a type size ?! */
83 case '@': return sizeof(id);
84 case ':': return sizeof(SEL);
85 case 'c': return sizeof(signed char);
86 case 's': return sizeof(signed short);
87 case 'i': return sizeof(signed int);
88 case 'C': return sizeof(unsigned char);
89 case 'S': return sizeof(unsigned short);
90 case 'I': return sizeof(unsigned int);
91 default: return 0xDEAFBEAF;
97 @implementation NSObject(NGObjCRuntime)
99 static NSArray *emptyArray = nil;
101 static unsigned countMethodSpecs(SEL _selector, va_list *va)
102 __attribute__((unused));
104 static unsigned countMethodSpecs(SEL _selector, va_list *va) {
110 selector = _selector;
113 if (selector) signature = va_arg(*va, id);
114 if (signature) imp = va_arg(*va, IMP);
117 while ((selector != NULL) && (signature != nil) && (imp != NULL)) {
120 if ((selector = va_arg(*va, SEL)) == NULL) break;
121 if ((signature = (id)va_arg(*va, NSString *)) == nil) break;
122 if ((imp = (IMP)va_arg(*va, IMP)) == NULL) break;
128 fillMethodListWithSpecs(MethodList_t methods, SEL _selector, va_list *va)
130 /* takes triple: SEL, signature, IMP */
136 selector = _selector;
137 signature = selector ? va_arg(*va, NSString *) : (NSString *)nil;
138 imp = signature ? va_arg(*va, IMP) : NULL;
140 while ((selector != NULL) && (signature != nil) && (imp != NULL)) {
147 /* allocate signature buffer */
148 len = [signature cStringLength];
149 types = malloc(len + 4);
150 [signature getCString:types];
153 #if APPLE_RUNTIME || NeXT_RUNTIME
154 count = methods->method_count;
155 methods->method_list[count].method_name = selector;
156 methods->method_list[count].method_types = types;
157 methods->method_list[count].method_imp = imp;
158 methods->method_count++;
160 /* determine selector name */
161 selname = sel_get_name(selector);
164 methods->method_list[count].method_name = (SEL)selname;
165 methods->method_list[count].method_types = types;
166 methods->method_list[count].method_imp = imp;
170 /* go to next method spec */
171 if ((selector = va_arg(*va, SEL)) == NULL) break;
172 if ((signature = va_arg(*va, NSString *)) == nil) break;
173 if ((imp = va_arg(*va, IMP)) == NULL) break;
176 methods->method_count = count;
177 methods->method_next = NULL;
181 + (unsigned)instanceSize {
182 return ((Class)self)->instance_size;
187 + (void)addMethodList:(MethodList_t)_methods {
188 if (_methods == NULL) return;
189 if (_methods->method_count == 0) return;
192 class_addMethods(self, _methods);
195 extern void class_add_method_list (Class class, MethodList_t list);
196 class_add_method_list(self, _methods);
201 + (void)addClassMethodList:(MethodList_t)_methods {
202 if (_methods == NULL) return;
203 if (_methods->method_count == 0) return;
205 class_addMethods(((Class)self)->isa, _methods);
208 extern void class_add_method_list (Class class, MethodList_t list);
209 class_add_method_list(((Class)self)->class_pointer, _methods);
214 + (void)addMethods:(SEL)_selector, ... {
215 /* takes triples (sel, type, imp) finished by nil */
216 MethodList_t methods;
220 va_start(va, _selector);
221 count = countMethodSpecs(_selector, &va);
223 if (count == 0) return;
225 #if NeXT_RUNTIME || APPLE_RUNTIME
226 methods = malloc(sizeof(struct objc_method_list) +
227 ((count + 1) * sizeof(struct objc_method)));
228 methods->method_count = 0;
230 va_start(va, _selector);
231 fillMethodListWithSpecs(methods, _selector, &va);
234 [self addMethodList:methods];
236 methods = malloc(sizeof(MethodList) + (count + 2) * sizeof(Method));
237 NSAssert(methods, @"could not allocate methodlist");
239 va_start(va, _selector);
240 fillMethodListWithSpecs(methods, _selector, &va);
243 [self addMethodList:methods];
247 + (void)addClassMethods:(SEL)_selector, ... {
248 /* takes triples finished by nil */
249 MethodList_t methods;
253 va_start(va, _selector);
254 count = countMethodSpecs(_selector, &va);
256 if (count == 0) return;
259 methods = malloc(sizeof(struct objc_method_list) +
260 ((count + 1) * sizeof(struct objc_method)));
261 methods->method_count = 0;
263 va_start(va, _selector);
264 fillMethodListWithSpecs(methods, _selector, &va);
267 [self addClassMethodList:methods];
269 methods = malloc(sizeof(MethodList) + count * sizeof(Method));
270 NSAssert(methods, @"couldn't allocate methodlist");
272 va_start(va, _selector);
273 fillMethodListWithSpecs(methods, _selector, &va);
276 [self addClassMethodList:methods];
280 + (NSEnumerator *)methodNameEnumerator {
281 return [[[_NGMethodNameEnumerator alloc]
283 includeSuperclassMethods:NO]
286 + (NSEnumerator *)hierachyMethodNameEnumerator {
287 return [[[_NGMethodNameEnumerator alloc]
289 includeSuperclassMethods:YES]
295 + (Class)subclass:(NSString *)_className
296 ivarsList:(IvarList_t)_ivars
299 [(NSObject *)self doesNotRecognizeSelector:_cmd];
302 // TODO: do we really need a symtab? PyObjC does not seem to require that
304 Class newMetaClass, newClass;
306 char *name, *moduleName, *metaName;
311 nameLen = [_className cStringLength];
312 name = malloc(nameLen + 3);
313 [_className getCString:name];
318 /* calc instance size */
320 // printf("calc isize ..\n");
322 for (i = 0, instanceSize = ((Class)self)->instance_size;
323 i < _ivars->ivar_count; i++) {
324 unsigned typeAlign, typeLen;
326 // printf("ivar %s\n", _ivars->ivar_list[i].ivar_name);
327 // printf(" type %s\n", _ivars->ivar_list[i].ivar_type);
329 typeAlign = objc_alignof_type(_ivars->ivar_list[i].ivar_type);
330 typeLen = objc_sizeof_type(_ivars->ivar_list[i].ivar_type);
332 /* check if offset is aligned */
333 if ((instanceSize % typeAlign) != 0) {
334 /* add alignment size */
335 instanceSize += (typeAlign - (instanceSize % typeAlign));
337 instanceSize += typeLen;
340 /* allocate structures */
342 newMetaClass = malloc(sizeof(struct objc_class));
343 newClass = malloc(sizeof(struct objc_class));
344 NSCAssert(newMetaClass, @"could not allocate new meta class structure");
345 NSCAssert(newClass, @"could not allocate new class structure");
347 // printf("setup meta ..\n");
349 /* init meta class */
350 newMetaClass->super_class = (Class)((Class)self)->class_pointer->name;
351 newMetaClass->class_pointer = newMetaClass->super_class->class_pointer;
352 newMetaClass->name = metaName;
353 newMetaClass->version = 0;
354 newMetaClass->info = _CLS_META;
355 newMetaClass->instance_size = newMetaClass->super_class->instance_size;
356 newMetaClass->methods = NULL;
357 newMetaClass->dtable = NULL;
358 newMetaClass->subclass_list = NULL;
359 newMetaClass->sibling_class = NULL;
360 newMetaClass->protocols = NULL;
361 newMetaClass->gc_object_type = NULL;
363 // printf("setup class ..\n");
365 newClass->super_class = (Class)((Class)self)->name;
366 newClass->class_pointer = newMetaClass;
367 newClass->name = name;
368 newClass->version = 0;
369 newClass->info = _CLS_CLASS;
370 newClass->instance_size = instanceSize;
371 newClass->methods = NULL;
372 newClass->dtable = NULL;
373 newClass->subclass_list = NULL;
374 newClass->sibling_class = NULL;
375 newClass->protocols = NULL;
376 newClass->gc_object_type = NULL;
377 newClass->ivars = _ivars;
379 /* allocate module */
381 module = malloc(sizeof(Module));
382 NSCAssert(module, @"could not allocate module !");
383 memset(module, 0, sizeof(Module));
385 module->size = sizeof(Module);
386 module->name = moduleName;
388 /* allocate symtab with one entry */
389 module->symtab = malloc(sizeof(Symtab) + (2 * sizeof(void *)));
390 module->symtab->sel_ref_cnt = 0;
391 module->symtab->refs = 0; // ptr to array of 'struct objc_selector'
392 module->symtab->cls_def_cnt = 1;
393 module->symtab->cat_def_cnt = 0;
394 module->symtab->defs[0] = newClass;
395 module->symtab->defs[1] = NULL;
399 #if GCC_VERSION < 30400
400 extern void __objc_exec_class(Module_t module); // is thread-safe
401 extern void __objc_resolve_class_links();
403 void __objc_exec_class(void* module);
404 void __objc_resolve_class_links();
408 This is the main entry function in the GNU runtime. It does a LOT of
409 work, including the setup of global hashes if missing. Some things (like
410 the global hashes) are not relevant for us, since the runtime itself is
412 This function uses the internal lock for runtime modifications.
414 The method does with the runtime lock applied (2.95.3):
416 - registers typed selectors for symtab->refs
417 - walks over all classes
418 - adds the class to the hash
419 - registers the selectors of the class
420 - registers the selectors of the metaclass
421 - install dtable's (just assigns NULL?)
422 - registers instance methods as class methods for root classes
424 - add superclasses to unresolved-classes
425 - walks over all categories
426 - register uninitialized statics
427 - walk over unclaimed categories
428 - walk over unclaimed protocols
429 - send the +load message
431 Actually this function just calls __objc_exec_module(), don't know why
432 we call this one instead.
434 // printf("execute class\n");
435 __objc_exec_class(module);
437 //printf("resolve links\n");
438 __objc_resolve_class_links();
441 return NSClassFromString(_className);
445 + (Class)subclass:(NSString *)_className
446 ivarNames:(NSString **)_ivarNames
447 ivarTypes:(NSString **)_ivarTypes
448 ivarCount:(unsigned)ivarCount
450 unsigned currentSize;
452 currentSize = ((Class)self)->instance_size;
454 #if NeXT_RUNTIME || APPLE_RUNTIME
456 /* some tricks for Apple inspired by PyObjC, long live OpenSource ;-) */
458 struct objc_class *clazz;
459 struct objc_class *metaClazz;
460 struct objc_class *rootClazz;
468 ivars = calloc(sizeof(struct objc_ivar_list) +
469 (ivarCount) * sizeof(struct objc_ivar), sizeof(char));
471 for (i = 0; i < ivarCount; i++) {
474 char *ivarName, *ivarType;
476 unsigned len, typeAlign, typeLen;
481 len = [n cStringLength];
482 ivarName = malloc(len + 2);
483 [n getCString:ivarName];
484 ivarName[len] = '\0';
486 len = [t cStringLength];
487 ivarType = malloc(len + 2);
488 [t getCString:ivarType];
489 ivarType[len] = '\0';
491 /* calc ivarOffset */
492 typeAlign = 0; // TODO: alignment?!
494 typeLen = NGGetSizeOfType(ivarType);
495 NSAssert1(typeLen != 0xDEAFBEAF,
496 @"does not support ivars of type '%s'", ivarType);
497 ivarOffset = currentSize;
499 var = ivars->ivar_list + ivars->ivar_count;
502 var->ivar_name = ivarName;
503 var->ivar_offset = ivarOffset;
504 var->ivar_type = ivarType;
506 /* adjust current size */
507 currentSize = ivarOffset + typeLen;
511 // TODO: move the following to a subclass method
513 /* determine root class */
515 for (rootClazz = self; rootClazz->super_class != NULL; )
516 rootClazz = rootClazz->super_class;
518 /* setup meta class */
520 metaClazz = calloc(1, sizeof(struct objc_class));
521 metaClazz->isa = rootClazz->isa; // root-meta is the metameta
522 metaClazz->name = strdup([_className cString]);
523 metaClazz->info = CLS_META;
524 metaClazz->super_class = ((struct objc_class *)self)->isa;
525 metaClazz->instance_size = ((struct objc_class *)self)->isa->instance_size;
526 metaClazz->ivars = NULL;
527 metaClazz->protocols = NULL;
531 clazz = calloc(1, sizeof(struct objc_class));
532 clazz->isa = metaClazz; /* hook up meta class */
533 clazz->name = strdup([_className cString]);
534 clazz->info = CLS_CLASS;
535 clazz->super_class = self;
536 clazz->instance_size = currentSize;
537 clazz->ivars = ivars;
538 clazz->protocols = NULL;
541 NSLog(@"instance size: %d, ivar-count: %d",
542 currentSize, ivars->ivar_count);
545 /* setup method lists */
547 metaClazz->methodLists = calloc(1, sizeof(struct objc_method_list *));
548 clazz->methodLists = calloc(1, sizeof(struct objc_method_list *));
550 /* Note: MacOSX specific, Radar #3317376, hint taken from PyObjC */
551 metaClazz->methodLists[0] = (struct objc_method_list *)-1;
552 clazz->methodLists[0] = (struct objc_method_list *)-1;
554 /* add to runtime (according to PyObjC not reversible?) */
555 objc_addClass(clazz);
556 return NSClassFromString(_className);
563 ivars = malloc(sizeof(IvarList) + (sizeof(struct objc_ivar) * ivarCount));
564 ivars->ivar_count = ivarCount;
566 for (i = 0; i < ivarCount; i++) {
568 char *ivarName, *ivarType;
570 unsigned len, typeAlign, typeLen;
575 len = [n cStringLength];
576 ivarName = malloc(len + 2);
577 [n getCString:ivarName];
578 ivarName[len] = '\0';
580 len = [t cStringLength];
581 ivarType = malloc(len + 2);
582 [t getCString:ivarType];
583 ivarType[len] = '\0';
585 /* calc ivarOffset */
586 typeAlign = objc_alignof_type(ivarType);
587 typeLen = objc_sizeof_type(ivarType);
588 ivarOffset = currentSize;
590 /* check if offset is aligned */
591 if ((ivarOffset % typeAlign) != 0) {
593 len = (typeAlign - (ivarOffset % typeAlign));
597 /* adjust current size */
598 currentSize = ivarOffset + typeLen;
600 ivars->ivar_list[ivarCount].ivar_name = ivarName;
601 ivars->ivar_list[ivarCount].ivar_type = ivarType;
602 ivars->ivar_list[ivarCount].ivar_offset = ivarOffset;
605 return [self subclass:_className ivarsList:ivars];
610 + (Class)subclass:(NSString *)_className
611 ivars:(NSString *)_name1,...
613 va_list va; /* contains: name1, type1, name2, type2, ... */
616 NSString **ivarNames = NULL;
617 NSString **ivarTypes = NULL;
620 /* determine number of args */
622 va_start(va, _name1);
623 for (n = _name1, t = va_arg(va, NSString *), ivarCount = 0;
624 (n != nil && t != nil);
625 n = va_arg(va, NSString *), t = va_arg(va, NSString *))
632 ivarNames = calloc(ivarCount, sizeof(NSString *));
633 ivarTypes = calloc(ivarCount, sizeof(NSString *));
634 va_start(va, _name1);
635 for (n = _name1, t = va_arg(va, NSString *), ivarCount = 0;
636 (n != nil && t != nil);
637 n = va_arg(va, NSString *), t = va_arg(va, NSString *)) {
638 ivarNames[ivarCount] = n;
639 ivarTypes[ivarCount] = t;
645 /* call primary method */
647 clazz = [self subclass:_className
648 ivarNames:ivarNames ivarTypes:ivarTypes ivarCount:ivarCount];
650 if (ivarNames != NULL) free(ivarNames);
651 if (ivarTypes != NULL) free(ivarTypes);
655 /* instance variables */
657 + (NSArray *)instanceVariableNames {
662 if (((Class)self)->ivars == NULL || ((Class)self)->ivars->ivar_count == 0) {
663 if (emptyArray == nil) emptyArray = [[NSArray alloc] init];
667 names = calloc(((Class)self)->ivars->ivar_count + 2, sizeof(NSString *));
669 for (i = 0; i < ((Class)self)->ivars->ivar_count; i++) {
670 register unsigned char *ivarName;
672 ivarName = (void *)(((Class)self)->ivars->ivar_list[i].ivar_name);
673 if (ivarName == NULL) {
674 NSLog(@"WARNING(%s): ivar without name! (idx=%d)",
675 __PRETTY_FUNCTION__, i);
679 #if !LIB_FOUNDATION_LIBRARY
680 names[i] = [NSString stringWithCString:(char *)ivarName];
682 names[i] = [NSString stringWithCStringNoCopy:(char *)ivarName
687 result = [NSArray arrayWithObjects:names
688 count:((Class)self)->ivars->ivar_count];
689 if (names) free(names);
692 + (NSArray *)allInstanceVariableNames {
693 NSMutableArray *varNames;
696 varNames = [NSMutableArray arrayWithCapacity:32];
697 for (c = self; c != Nil; c = [c superclass])
698 [varNames addObjectsFromArray:[c instanceVariableNames]];
700 return [[varNames copy] autorelease];
703 + (BOOL)hasInstanceVariableWithName:(NSString *)_ivarName {
705 unsigned len = [_ivarName cStringLength];
711 ivarName = malloc(len + 1);
712 [_ivarName getCString:ivarName]; ivarName[len] = '\0';
714 for (c = self; c != Nil; c = [c superclass]) {
717 for (i = 0; i < c->ivars->ivar_count; i++) {
718 if (strcmp(ivarName, c->ivars->ivar_list[i].ivar_name) == 0) {
728 + (NSString *)signatureOfInstanceVariableWithName:(NSString *)_ivarName {
730 unsigned len = [_ivarName cStringLength];
736 ivarName = malloc(len + 1);
737 [_ivarName getCString:ivarName]; ivarName[len] = '\0';
739 for (c = self; c != Nil; c = [c superclass]) {
742 for (i = 0; i < c->ivars->ivar_count; i++) {
743 if (strcmp(ivarName, c->ivars->ivar_list[i].ivar_name) == 0) {
744 /* found matching ivar name */
745 if (ivarName) free(ivarName);
746 #if !LIB_FOUNDATION_LIBRARY
747 return [NSString stringWithCString:
748 (char *)(c->ivars->ivar_list[i].ivar_type)];
750 return [NSString stringWithCStringNoCopy:
751 (char *)(c->ivars->ivar_list[i].ivar_type)
757 if (ivarName) free(ivarName);
762 + (unsigned)offsetOfInstanceVariableWithName:(NSString *)_ivarName {
764 unsigned len = [_ivarName cStringLength];
770 ivarName = malloc(len + 3);
771 [_ivarName getCString:ivarName]; ivarName[len] = '\0';
773 for (c = self; c != Nil; c = [c superclass]) {
776 for (i = 0; i < c->ivars->ivar_count; i++) {
777 if (strcmp(ivarName, c->ivars->ivar_list[i].ivar_name) == 0) {
778 /* found matching ivar name */
780 return c->ivars->ivar_list[i].ivar_offset;
788 @end /* NSObject(NGObjCRuntime) */
790 @implementation _NGMethodNameEnumerator
792 - (id)initWithClass:(Class)_clazz includeSuperclassMethods:(BOOL)_flag {
798 self->names = [[NSMutableSet alloc] initWithCapacity:200];
799 self->clazz = _clazz;
800 self->includeSuperclassMethods = _flag;
804 self->methods = class_nextMethodList(self->clazz, &(self->iter));
806 self->methods = _clazz->methods;
813 [self->names release];
818 if (self->clazz == nil)
821 if (self->methods == NULL) {
822 /* methods of current class are done .. */
823 if (!self->includeSuperclassMethods)
826 /* loop, maybe there are classes without a method-list ? */
827 while (self->methods == NULL) {
828 if ((self->clazz = [self->clazz superclass]) == Nil)
829 /* no more superclasses */
834 self->methods = class_nextMethodList(self->clazz, &(self->iter));
836 self->methods = self->clazz->methods;
843 NSAssert(self->methods, @"missing method-list !");
846 while (self->i >= (unsigned)self->methods->method_count) {
847 #if NeXT_RUNTIME || APPLE_RUNTIME
848 self->methods = class_nextMethodList(self->clazz, &(self->iter));
850 self->methods = self->methods->method_next;
852 if (self->methods == NULL)
857 if (self->methods == NULL) {
858 /* recurse to next super class */
859 return self->includeSuperclassMethods
869 m = &(self->methods->method_list[self->i]);
872 NSAssert(m, @"missing method structure !");
873 name = NSStringFromSelector(m->method_name);
874 NSAssert(name, @"couldn't get method name !");
876 if ([self->names containsObject:name]) {
877 /* this name was already delivered from a subclass, take next */
878 return [self nextObject];
881 [self->names addObject:name];
887 @end /* _NGMethodNameEnumerator */
891 @interface NGObjCClassEnumerator : NSEnumerator
897 @implementation NGObjCClassEnumerator
900 return objc_next_class(&(self->state));
903 @end /* NGObjCClassEnumerator */
905 #endif /* GNU_RUNTIME */