2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo 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 OGo 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 OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include "NGObjCRuntime.h"
24 #include "NGMemoryAllocation.h"
25 #include <objc/objc.h>
26 #include <objc/objc-api.h>
30 #if NeXT_RUNTIME || APPLE_RUNTIME
31 # include <objc/objc-class.h>
32 # include <objc/objc-runtime.h>
33 typedef struct objc_method_list *MethodList_t;
34 typedef struct objc_ivar_list *IvarList_t;
35 typedef struct objc_method *Method_t;
37 # include <objc/encoding.h>
40 #import <Foundation/NSObject.h>
41 #import <Foundation/NSException.h>
42 #import <Foundation/NSString.h>
43 #import <Foundation/NSSet.h>
44 #import <Foundation/NSEnumerator.h>
46 #define GCC_VERSION (__GNUC__ * 10000 \
47 + __GNUC_MINOR__ * 100 \
48 + __GNUC_PATCHLEVEL__)
50 #if GNUSTEP_BASE_LIBRARY
51 /* this is a hack for the extensions 0.8.6 library */
53 void class_add_behavior(Class class, Class behavior) {
54 extern void behavior_class_add_class (Class class, Class behavior);
55 behavior_class_add_class(class, behavior);
60 @interface _NGMethodNameEnumerator : NSEnumerator
63 BOOL includeSuperclassMethods;
66 struct objc_method_list *methods; // current method list
67 unsigned i; // current index in current method list
68 #if APPLE_RUNTIME || NeXT_RUNTIME
69 void *iter; // runtime iterator
73 - (id)initWithClass:(Class)_clazz includeSuperclassMethods:(BOOL)_flag;
78 @implementation NSObject(NGObjCRuntime)
80 static NSArray *emptyArray = nil;
82 static unsigned countMethodSpecs(SEL _selector, va_list *va)
83 __attribute__((unused));
85 static unsigned countMethodSpecs(SEL _selector, va_list *va) {
94 if (selector) signature = va_arg(*va, id);
95 if (signature) imp = va_arg(*va, IMP);
98 while ((selector != NULL) && (signature != nil) && (imp != NULL)) {
101 if ((selector = va_arg(*va, SEL)) == NULL) break;
102 if ((signature = (id)va_arg(*va, NSString *)) == nil) break;
103 if ((imp = (IMP)va_arg(*va, IMP)) == NULL) break;
109 fillMethodListWithSpecs(MethodList_t methods, SEL _selector, va_list *va)
111 /* takes triple: SEL, signature, IMP */
117 selector = _selector;
118 signature = selector ? va_arg(*va, NSString *) : nil;
119 imp = signature ? va_arg(*va, IMP) : NULL;
121 while ((selector != NULL) && (signature != nil) && (imp != NULL)) {
128 /* allocate signature buffer */
129 len = [signature cStringLength];
130 types = malloc(len + 4);
131 [signature getCString:types];
134 #if APPLE_RUNTIME || NeXT_RUNTIME
135 count = methods->method_count;
136 methods->method_list[count].method_name = selector;
137 methods->method_list[count].method_types = types;
138 methods->method_list[count].method_imp = imp;
139 methods->method_count++;
141 /* determine selector name */
142 selname = sel_get_name(selector);
145 methods->method_list[count].method_name = (SEL)selname;
146 methods->method_list[count].method_types = types;
147 methods->method_list[count].method_imp = imp;
151 /* go to next method spec */
152 if ((selector = va_arg(*va, SEL)) == NULL) break;
153 if ((signature = va_arg(*va, NSString *)) == nil) break;
154 if ((imp = va_arg(*va, IMP)) == NULL) break;
157 methods->method_count = count;
158 methods->method_next = NULL;
162 + (unsigned)instanceSize {
163 return ((Class)self)->instance_size;
168 + (void)addMethodList:(MethodList_t)_methods {
169 if (_methods == NULL) return;
170 if (_methods->method_count == 0) return;
173 class_addMethods(self, _methods);
176 extern void class_add_method_list (Class class, MethodList_t list);
177 class_add_method_list(self, _methods);
182 + (void)addClassMethodList:(MethodList_t)_methods {
183 if (_methods == NULL) return;
184 if (_methods->method_count == 0) return;
186 class_addMethods(((Class)self)->isa, _methods);
189 extern void class_add_method_list (Class class, MethodList_t list);
190 class_add_method_list(((Class)self)->class_pointer, _methods);
195 + (void)addMethods:(SEL)_selector, ... {
196 /* takes triples (sel, type, imp) finished by nil */
197 MethodList_t methods;
201 va_start(va, _selector);
202 count = countMethodSpecs(_selector, &va);
204 if (count == 0) return;
206 #if NeXT_RUNTIME || APPLE_RUNTIME
207 methods = malloc(sizeof(struct objc_method_list) +
208 ((count + 1) * sizeof(struct objc_method)));
209 methods->method_count = 0;
211 va_start(va, _selector);
212 fillMethodListWithSpecs(methods, _selector, &va);
215 [self addMethodList:methods];
217 methods = malloc(sizeof(MethodList) + (count + 2) * sizeof(Method));
218 NSAssert(methods, @"could not allocate methodlist");
220 va_start(va, _selector);
221 fillMethodListWithSpecs(methods, _selector, &va);
224 [self addMethodList:methods];
228 + (void)addClassMethods:(SEL)_selector, ... {
229 /* takes triples finished by nil */
230 MethodList_t methods;
234 va_start(va, _selector);
235 count = countMethodSpecs(_selector, &va);
237 if (count == 0) return;
240 methods = malloc(sizeof(struct objc_method_list) +
241 ((count + 1) * sizeof(struct objc_method)));
242 methods->method_count = 0;
244 va_start(va, _selector);
245 fillMethodListWithSpecs(methods, _selector, &va);
248 [self addClassMethodList:methods];
250 methods = malloc(sizeof(MethodList) + count * sizeof(Method));
251 NSAssert(methods, @"couldn't allocate methodlist");
253 va_start(va, _selector);
254 fillMethodListWithSpecs(methods, _selector, &va);
257 [self addClassMethodList:methods];
261 + (NSEnumerator *)methodNameEnumerator {
262 return [[[_NGMethodNameEnumerator alloc]
264 includeSuperclassMethods:NO]
267 + (NSEnumerator *)hierachyMethodNameEnumerator {
268 return [[[_NGMethodNameEnumerator alloc]
270 includeSuperclassMethods:YES]
276 + (Class)subclass:(NSString *)_className
277 ivarsList:(IvarList_t)_ivars
280 [(NSObject *)self doesNotRecognizeSelector:_cmd];
283 // TODO: do we really need a symtab? PyObjC does not seem to require that
285 Class newMetaClass, newClass;
287 char *name, *moduleName, *metaName;
292 nameLen = [_className cStringLength];
293 name = malloc(nameLen + 3);
294 [_className getCString:name];
299 /* calc instance size */
301 // printf("calc isize ..\n");
303 for (i = 0, instanceSize = ((Class)self)->instance_size;
304 i < _ivars->ivar_count; i++) {
305 unsigned typeAlign, typeLen;
307 // printf("ivar %s\n", _ivars->ivar_list[i].ivar_name);
308 // printf(" type %s\n", _ivars->ivar_list[i].ivar_type);
310 typeAlign = objc_alignof_type(_ivars->ivar_list[i].ivar_type);
311 typeLen = objc_sizeof_type(_ivars->ivar_list[i].ivar_type);
313 /* check if offset is aligned */
314 if ((instanceSize % typeAlign) != 0) {
315 /* add alignment size */
316 instanceSize += (typeAlign - (instanceSize % typeAlign));
318 instanceSize += typeLen;
321 /* allocate structures */
323 newMetaClass = malloc(sizeof(struct objc_class));
324 newClass = malloc(sizeof(struct objc_class));
325 NSCAssert(newMetaClass, @"could not allocate new meta class structure");
326 NSCAssert(newClass, @"could not allocate new class structure");
328 // printf("setup meta ..\n");
330 /* init meta class */
331 newMetaClass->super_class = (Class)((Class)self)->class_pointer->name;
332 newMetaClass->class_pointer = newMetaClass->super_class->class_pointer;
333 newMetaClass->name = metaName;
334 newMetaClass->version = 0;
335 newMetaClass->info = _CLS_META;
336 newMetaClass->instance_size = newMetaClass->super_class->instance_size;
337 newMetaClass->methods = NULL;
338 newMetaClass->dtable = NULL;
339 newMetaClass->subclass_list = NULL;
340 newMetaClass->sibling_class = NULL;
341 newMetaClass->protocols = NULL;
342 newMetaClass->gc_object_type = NULL;
344 // printf("setup class ..\n");
346 newClass->super_class = (Class)((Class)self)->name;
347 newClass->class_pointer = newMetaClass;
348 newClass->name = name;
349 newClass->version = 0;
350 newClass->info = _CLS_CLASS;
351 newClass->instance_size = instanceSize;
352 newClass->methods = NULL;
353 newClass->dtable = NULL;
354 newClass->subclass_list = NULL;
355 newClass->sibling_class = NULL;
356 newClass->protocols = NULL;
357 newClass->gc_object_type = NULL;
358 newClass->ivars = _ivars;
360 /* allocate module */
362 module = malloc(sizeof(Module));
363 NSCAssert(module, @"could not allocate module !");
364 memset(module, 0, sizeof(Module));
366 module->size = sizeof(Module);
367 module->name = moduleName;
369 /* allocate symtab with one entry */
370 module->symtab = malloc(sizeof(Symtab) + (2 * sizeof(void *)));
371 module->symtab->sel_ref_cnt = 0;
372 module->symtab->refs = 0; // ptr to array of 'struct objc_selector'
373 module->symtab->cls_def_cnt = 1;
374 module->symtab->cat_def_cnt = 0;
375 module->symtab->defs[0] = newClass;
376 module->symtab->defs[1] = NULL;
380 #if GCC_VERSION < 30400
381 extern void __objc_exec_class(Module_t module); // is thread-safe
382 extern void __objc_resolve_class_links();
384 void __objc_exec_class(void* module);
385 void __objc_resolve_class_links();
388 //printf("execute class\n");
389 __objc_exec_class(module);
390 //printf("resolve links\n");
391 __objc_resolve_class_links();
394 return NSClassFromString(_className);
398 + (Class)subclass:(NSString *)_className
399 ivars:(NSString *)_name1,...
402 unsigned ivarCount, currentSize;
405 currentSize = ((Class)self)->instance_size;
407 va_start(va, _name1);
408 for (n = _name1, t = va_arg(va, NSString *), ivarCount = 0;
409 (n != nil && t != nil);
410 n = va_arg(va, NSString *), t = va_arg(va, NSString *))
414 #if NeXT_RUNTIME || APPLE_RUNTIME
416 /* some tricks for Apple inspired by PyObjC, long live OpenSource ;-) */
418 struct objc_class *clazz;
419 struct objc_class *metaClazz;
420 struct objc_class *rootClazz;
426 ivars = calloc(sizeof(struct objc_ivar_list) +
427 (ivarCount) * sizeof(struct objc_ivar), sizeof(char));
429 va_start(va, _name1);
430 for (n = _name1, t = va_arg(va, NSString *), ivarCount = 0;
431 (n != nil && t != nil);
432 n = va_arg(va, NSString *), t=va_arg(va, NSString *), ivarCount++) {
434 char *ivarName, *ivarType;
436 unsigned len, typeAlign, typeLen;
438 len = [n cStringLength];
439 ivarName = malloc(len + 2);
440 [n getCString:ivarName];
441 ivarName[len] = '\0';
443 len = [t cStringLength];
444 ivarType = malloc(len + 2);
445 [t getCString:ivarType];
446 ivarType[len] = '\0';
448 /* calc ivarOffset */
449 typeAlign = 0; // TODO: alignment?!
450 // TODO: add more types ...
452 /* the Apple runtime has no func to calc a type size ?! */
453 case '@': typeLen = sizeof(id); break;
454 case ':': typeLen = sizeof(SEL); break;
455 case 'c': typeLen = sizeof(signed char); break;
456 case 's': typeLen = sizeof(signed short); break;
457 case 'i': typeLen = sizeof(signed int); break;
458 case 'C': typeLen = sizeof(unsigned char); break;
459 case 'S': typeLen = sizeof(unsigned short); break;
460 case 'I': typeLen = sizeof(unsigned int); break;
462 NSAssert1(NO, @"does not support ivars of type '%s'", ivarType);
465 ivarOffset = currentSize;
467 var = ivars->ivar_list + ivars->ivar_count;
470 var->ivar_name = ivarName;
471 var->ivar_offset = ivarOffset;
472 var->ivar_type = ivarType;
474 /* adjust current size */
475 currentSize = ivarOffset + typeLen;
480 // TODO: move the following to a subclass method
482 /* determine root class */
484 for (rootClazz = self; rootClazz->super_class != NULL; )
485 rootClazz = rootClazz->super_class;
487 /* setup meta class */
489 metaClazz = calloc(1, sizeof(struct objc_class));
490 metaClazz->isa = rootClazz->isa; // root-meta is the metameta
491 metaClazz->name = strdup([_className cString]);
492 metaClazz->info = CLS_META;
493 metaClazz->super_class = ((struct objc_class *)self)->isa;
494 metaClazz->instance_size = ((struct objc_class *)self)->isa->instance_size;
495 metaClazz->ivars = NULL;
496 metaClazz->protocols = NULL;
500 clazz = calloc(1, sizeof(struct objc_class));
501 clazz->isa = metaClazz; /* hook up meta class */
502 clazz->name = strdup([_className cString]);
503 clazz->info = CLS_CLASS;
504 clazz->super_class = self;
505 clazz->instance_size = currentSize;
506 clazz->ivars = ivars;
507 clazz->protocols = NULL;
510 NSLog(@"instance size: %d, ivar-count: %d",
511 currentSize, ivars->ivar_count);
514 /* setup method lists */
516 metaClazz->methodLists = calloc(1, sizeof(struct objc_method_list *));
517 clazz->methodLists = calloc(1, sizeof(struct objc_method_list *));
519 /* Note: MacOSX specific, Radar #3317376, hint taken from PyObjC */
520 metaClazz->methodLists[0] = (struct objc_method_list *)-1;
521 clazz->methodLists[0] = (struct objc_method_list *)-1;
523 /* add to runtime (according to PyObjC not reversible?) */
524 objc_addClass(clazz);
525 return NSClassFromString(_className);
531 ivars = malloc(sizeof(IvarList) + (sizeof(struct objc_ivar) * ivarCount));
532 ivars->ivar_count = ivarCount;
534 va_start(va, _name1);
535 for (n = _name1, t = va_arg(va, NSString *), ivarCount = 0;
536 (n != nil && t != nil);
537 n = va_arg(va, NSString *), t = va_arg(va, NSString *), ivarCount++) {
538 char *ivarName, *ivarType;
540 unsigned len, typeAlign, typeLen;
542 len = [n cStringLength];
543 ivarName = malloc(len + 2);
544 [n getCString:ivarName];
545 ivarName[len] = '\0';
547 len = [t cStringLength];
548 ivarType = malloc(len + 2);
549 [t getCString:ivarType];
550 ivarType[len] = '\0';
552 /* calc ivarOffset */
553 typeAlign = objc_alignof_type(ivarType);
554 typeLen = objc_sizeof_type(ivarType);
555 ivarOffset = currentSize;
557 /* check if offset is aligned */
558 if ((ivarOffset % typeAlign) != 0) {
560 len = (typeAlign - (ivarOffset % typeAlign));
564 /* adjust current size */
565 currentSize = ivarOffset + typeLen;
567 ivars->ivar_list[ivarCount].ivar_name = ivarName;
568 ivars->ivar_list[ivarCount].ivar_type = ivarType;
569 ivars->ivar_list[ivarCount].ivar_offset = ivarOffset;
573 return [self subclass:_className ivarsList:ivars];
578 /* instance variables */
580 + (NSArray *)instanceVariableNames {
585 if (((Class)self)->ivars == NULL || ((Class)self)->ivars->ivar_count == 0) {
586 if (emptyArray == nil) emptyArray = [[NSArray alloc] init];
590 names = calloc(((Class)self)->ivars->ivar_count + 2, sizeof(NSString *));
592 for (i = 0; i < ((Class)self)->ivars->ivar_count; i++) {
593 register unsigned char *ivarName;
595 ivarName = (void *)(((Class)self)->ivars->ivar_list[i].ivar_name);
596 if (ivarName == NULL) {
597 NSLog(@"WARNING(%s): ivar without name! (idx=%d)",
598 __PRETTY_FUNCTION__, i);
602 #if !LIB_FOUNDATION_LIBRARY
603 names[i] = [NSString stringWithCString:ivarName];
605 names[i] = [NSString stringWithCStringNoCopy:ivarName freeWhenDone:NO];
609 result = [NSArray arrayWithObjects:names
610 count:((Class)self)->ivars->ivar_count];
611 if (names) free(names);
614 + (NSArray *)allInstanceVariableNames {
615 NSMutableArray *varNames;
618 varNames = [NSMutableArray arrayWithCapacity:32];
619 for (c = self; c != Nil; c = [c superclass])
620 [varNames addObjectsFromArray:[c instanceVariableNames]];
622 return [[varNames copy] autorelease];
625 + (BOOL)hasInstanceVariableWithName:(NSString *)_ivarName {
627 unsigned len = [_ivarName cStringLength];
633 ivarName = malloc(len + 1);
634 [_ivarName getCString:ivarName]; ivarName[len] = '\0';
636 for (c = self; c != Nil; c = [c superclass]) {
639 for (i = 0; i < c->ivars->ivar_count; i++) {
640 if (strcmp(ivarName, c->ivars->ivar_list[i].ivar_name) == 0) {
650 + (NSString *)signatureOfInstanceVariableWithName:(NSString *)_ivarName {
652 unsigned len = [_ivarName cStringLength];
658 ivarName = malloc(len + 1);
659 [_ivarName getCString:ivarName]; ivarName[len] = '\0';
661 for (c = self; c != Nil; c = [c superclass]) {
664 for (i = 0; i < c->ivars->ivar_count; i++) {
665 if (strcmp(ivarName, c->ivars->ivar_list[i].ivar_name) == 0) {
666 /* found matching ivar name */
667 if (ivarName) free(ivarName);
668 #if !LIB_FOUNDATION_LIBRARY
669 return [NSString stringWithCString:
670 (char *)(c->ivars->ivar_list[i].ivar_type)];
672 return [NSString stringWithCStringNoCopy:
673 (char *)(c->ivars->ivar_list[i].ivar_type)
679 if (ivarName) free(ivarName);
684 + (unsigned)offsetOfInstanceVariableWithName:(NSString *)_ivarName {
686 unsigned len = [_ivarName cStringLength];
692 ivarName = malloc(len + 3);
693 [_ivarName getCString:ivarName]; ivarName[len] = '\0';
695 for (c = self; c != Nil; c = [c superclass]) {
698 for (i = 0; i < c->ivars->ivar_count; i++) {
699 if (strcmp(ivarName, c->ivars->ivar_list[i].ivar_name) == 0) {
700 /* found matching ivar name */
702 return c->ivars->ivar_list[i].ivar_offset;
710 @end /* NSObject(NGObjCRuntime) */
712 @implementation _NGMethodNameEnumerator
714 - (id)initWithClass:(Class)_clazz includeSuperclassMethods:(BOOL)_flag {
720 self->names = [[NSMutableSet alloc] initWithCapacity:200];
721 self->clazz = _clazz;
722 self->includeSuperclassMethods = _flag;
726 self->methods = class_nextMethodList(self->clazz, &(self->iter));
728 self->methods = _clazz->methods;
735 [self->names release];
740 if (self->clazz == nil)
743 if (self->methods == NULL) {
744 /* methods of current class are done .. */
745 if (!self->includeSuperclassMethods)
748 /* loop, maybe there are classes without a method-list ? */
749 while (self->methods == NULL) {
750 if ((self->clazz = [self->clazz superclass]) == Nil)
751 /* no more superclasses */
756 self->methods = class_nextMethodList(self->clazz, &(self->iter));
758 self->methods = self->clazz->methods;
765 NSAssert(self->methods, @"missing method-list !");
768 while (self->i >= (unsigned)self->methods->method_count) {
769 #if NeXT_RUNTIME || APPLE_RUNTIME
770 self->methods = class_nextMethodList(self->clazz, &(self->iter));
772 self->methods = self->methods->method_next;
774 if (self->methods == NULL)
779 if (self->methods == NULL) {
780 /* recurse to next super class */
781 return self->includeSuperclassMethods
791 m = &(self->methods->method_list[self->i]);
794 NSAssert(m, @"missing method structure !");
795 name = NSStringFromSelector(m->method_name);
796 NSAssert(name, @"couldn't get method name !");
798 if ([self->names containsObject:name]) {
799 /* this name was already delivered from a subclass, take next */
800 return [self nextObject];
803 [self->names addObject:name];
809 @end /* _NGMethodNameEnumerator */
813 @interface NGObjCClassEnumerator : NSEnumerator
819 @implementation NGObjCClassEnumerator
822 return objc_next_class(&(self->state));
825 @end /* NGObjCClassEnumerator */
827 #endif /* GNU_RUNTIME */