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 @implementation NSObject(NGObjCRuntime)
79 static NSArray *emptyArray = nil;
81 static unsigned countMethodSpecs(SEL _selector, va_list *va)
82 __attribute__((unused));
84 static unsigned countMethodSpecs(SEL _selector, va_list *va) {
93 if (selector) signature = va_arg(*va, id);
94 if (signature) imp = va_arg(*va, IMP);
97 while ((selector != NULL) && (signature != nil) && (imp != NULL)) {
100 if ((selector = va_arg(*va, SEL)) == NULL) break;
101 if ((signature = (id)va_arg(*va, NSString *)) == nil) break;
102 if ((imp = (IMP)va_arg(*va, IMP)) == NULL) break;
108 fillMethodListWithSpecs(MethodList_t methods, SEL _selector, va_list *va)
110 /* takes triple: SEL, signature, IMP */
116 selector = _selector;
117 signature = selector ? va_arg(*va, NSString *) : nil;
118 imp = signature ? va_arg(*va, IMP) : NULL;
120 while ((selector != NULL) && (signature != nil) && (imp != NULL)) {
127 /* allocate signature buffer */
128 len = [signature cStringLength];
129 types = malloc(len + 4);
130 [signature getCString:types];
133 #if APPLE_RUNTIME || NeXT_RUNTIME
134 count = methods->method_count;
135 methods->method_list[count].method_name = selector;
136 methods->method_list[count].method_types = types;
137 methods->method_list[count].method_imp = imp;
138 methods->method_count++;
140 /* determine selector name */
141 selname = sel_get_name(selector);
144 methods->method_list[count].method_name = (SEL)selname;
145 methods->method_list[count].method_types = types;
146 methods->method_list[count].method_imp = imp;
150 /* go to next method spec */
151 if ((selector = va_arg(*va, SEL)) == NULL) break;
152 if ((signature = va_arg(*va, NSString *)) == nil) break;
153 if ((imp = va_arg(*va, IMP)) == NULL) break;
156 methods->method_count = count;
157 methods->method_next = NULL;
161 + (unsigned)instanceSize {
162 return ((Class)self)->instance_size;
167 + (void)addMethodList:(MethodList_t)_methods {
168 if (_methods == NULL) return;
169 if (_methods->method_count == 0) return;
172 class_addMethods(self, _methods);
175 extern void class_add_method_list (Class class, MethodList_t list);
176 class_add_method_list(self, _methods);
181 + (void)addClassMethodList:(MethodList_t)_methods {
182 if (_methods == NULL) return;
183 if (_methods->method_count == 0) return;
185 class_addMethods(((Class)self)->isa, _methods);
188 extern void class_add_method_list (Class class, MethodList_t list);
189 class_add_method_list(((Class)self)->class_pointer, _methods);
194 + (void)addMethods:(SEL)_selector, ... {
195 /* takes triples (sel, type, imp) finished by nil */
196 MethodList_t methods;
200 va_start(va, _selector);
201 count = countMethodSpecs(_selector, &va);
203 if (count == 0) return;
205 #if NeXT_RUNTIME || APPLE_RUNTIME
206 methods = malloc(sizeof(struct objc_method_list) +
207 ((count + 1) * sizeof(struct objc_method)));
208 methods->method_count = 0;
210 va_start(va, _selector);
211 fillMethodListWithSpecs(methods, _selector, &va);
214 [self addMethodList:methods];
216 methods = malloc(sizeof(MethodList) + (count + 2) * sizeof(Method));
217 NSAssert(methods, @"could not allocate methodlist");
219 va_start(va, _selector);
220 fillMethodListWithSpecs(methods, _selector, &va);
223 [self addMethodList:methods];
227 + (void)addClassMethods:(SEL)_selector, ... {
228 /* takes triples finished by nil */
229 MethodList_t methods;
233 va_start(va, _selector);
234 count = countMethodSpecs(_selector, &va);
236 if (count == 0) return;
239 methods = malloc(sizeof(struct objc_method_list) +
240 ((count + 1) * sizeof(struct objc_method)));
241 methods->method_count = 0;
243 va_start(va, _selector);
244 fillMethodListWithSpecs(methods, _selector, &va);
247 [self addClassMethodList:methods];
249 methods = malloc(sizeof(MethodList) + count * sizeof(Method));
250 NSAssert(methods, @"couldn't allocate methodlist");
252 va_start(va, _selector);
253 fillMethodListWithSpecs(methods, _selector, &va);
256 [self addClassMethodList:methods];
260 + (NSEnumerator *)methodNameEnumerator {
261 return [[[_NGMethodNameEnumerator alloc]
263 includeSuperclassMethods:NO]
266 + (NSEnumerator *)hierachyMethodNameEnumerator {
267 return [[[_NGMethodNameEnumerator alloc]
269 includeSuperclassMethods:YES]
275 + (Class)subclass:(NSString *)_className
276 ivarsList:(IvarList_t)_ivars
279 [(NSObject *)self doesNotRecognizeSelector:_cmd];
282 // TODO: do we really need a symtab? PyObjC does not seem to require that
284 Class newMetaClass, newClass;
286 char *name, *moduleName, *metaName;
291 nameLen = [_className cStringLength];
292 name = malloc(nameLen + 3);
293 [_className getCString:name];
298 /* calc instance size */
300 // printf("calc isize ..\n");
302 for (i = 0, instanceSize = ((Class)self)->instance_size;
303 i < _ivars->ivar_count; i++) {
304 unsigned typeAlign, typeLen;
306 // printf("ivar %s\n", _ivars->ivar_list[i].ivar_name);
307 // printf(" type %s\n", _ivars->ivar_list[i].ivar_type);
309 typeAlign = objc_alignof_type(_ivars->ivar_list[i].ivar_type);
310 typeLen = objc_sizeof_type(_ivars->ivar_list[i].ivar_type);
312 /* check if offset is aligned */
313 if ((instanceSize % typeAlign) != 0) {
314 /* add alignment size */
315 instanceSize += (typeAlign - (instanceSize % typeAlign));
317 instanceSize += typeLen;
320 /* allocate structures */
322 newMetaClass = malloc(sizeof(struct objc_class));
323 newClass = malloc(sizeof(struct objc_class));
324 NSCAssert(newMetaClass, @"could not allocate new meta class structure");
325 NSCAssert(newClass, @"could not allocate new class structure");
327 // printf("setup meta ..\n");
329 /* init meta class */
330 newMetaClass->super_class = (Class)((Class)self)->class_pointer->name;
331 newMetaClass->class_pointer = newMetaClass->super_class->class_pointer;
332 newMetaClass->name = metaName;
333 newMetaClass->version = 0;
334 newMetaClass->info = _CLS_META;
335 newMetaClass->instance_size = newMetaClass->super_class->instance_size;
336 newMetaClass->methods = NULL;
337 newMetaClass->dtable = NULL;
338 newMetaClass->subclass_list = NULL;
339 newMetaClass->sibling_class = NULL;
340 newMetaClass->protocols = NULL;
341 newMetaClass->gc_object_type = NULL;
343 // printf("setup class ..\n");
345 newClass->super_class = (Class)((Class)self)->name;
346 newClass->class_pointer = newMetaClass;
347 newClass->name = name;
348 newClass->version = 0;
349 newClass->info = _CLS_CLASS;
350 newClass->instance_size = instanceSize;
351 newClass->methods = NULL;
352 newClass->dtable = NULL;
353 newClass->subclass_list = NULL;
354 newClass->sibling_class = NULL;
355 newClass->protocols = NULL;
356 newClass->gc_object_type = NULL;
357 newClass->ivars = _ivars;
359 /* allocate module */
361 module = malloc(sizeof(Module));
362 NSCAssert(module, @"could not allocate module !");
363 memset(module, 0, sizeof(Module));
365 module->size = sizeof(Module);
366 module->name = moduleName;
368 /* allocate symtab with one entry */
369 module->symtab = malloc(sizeof(Symtab) + (2 * sizeof(void *)));
370 module->symtab->sel_ref_cnt = 0;
371 module->symtab->refs = 0; // ptr to array of 'struct objc_selector'
372 module->symtab->cls_def_cnt = 1;
373 module->symtab->cat_def_cnt = 0;
374 module->symtab->defs[0] = newClass;
375 module->symtab->defs[1] = NULL;
379 #if GCC_VERSION < 30400
380 extern void __objc_exec_class(Module_t module); // is thread-safe
381 extern void __objc_resolve_class_links();
383 void __objc_exec_class(void* module);
384 void __objc_resolve_class_links();
387 //printf("execute class\n");
388 __objc_exec_class(module);
389 //printf("resolve links\n");
390 __objc_resolve_class_links();
393 return NSClassFromString(_className);
397 + (Class)subclass:(NSString *)_className
398 ivars:(NSString *)_name1,...
401 unsigned ivarCount, currentSize;
404 currentSize = ((Class)self)->instance_size;
406 va_start(va, _name1);
407 for (n = _name1, t = va_arg(va, NSString *), ivarCount = 0;
408 (n != nil && t != nil);
409 n = va_arg(va, NSString *), t = va_arg(va, NSString *))
413 #if NeXT_RUNTIME || APPLE_RUNTIME
415 /* some tricks for Apple inspired by PyObjC, long live OpenSource ;-) */
417 struct objc_class *clazz;
418 struct objc_class *metaClazz;
419 struct objc_class *rootClazz;
425 ivars = calloc(sizeof(struct objc_ivar_list) +
426 (ivarCount) * sizeof(struct objc_ivar), sizeof(char));
428 va_start(va, _name1);
429 for (n = _name1, t = va_arg(va, NSString *), ivarCount = 0;
430 (n != nil && t != nil);
431 n = va_arg(va, NSString *), t=va_arg(va, NSString *), ivarCount++) {
433 char *ivarName, *ivarType;
435 unsigned len, typeAlign, typeLen;
437 len = [n cStringLength];
438 ivarName = malloc(len + 2);
439 [n getCString:ivarName];
440 ivarName[len] = '\0';
442 len = [t cStringLength];
443 ivarType = malloc(len + 2);
444 [t getCString:ivarType];
445 ivarType[len] = '\0';
447 /* calc ivarOffset */
448 typeAlign = 0; // TODO: alignment?!
449 // TODO: add more types ...
451 /* the Apple runtime has no func to calc a type size ?! */
452 case '@': typeLen = sizeof(id); break;
453 case ':': typeLen = sizeof(SEL); break;
454 case 'c': typeLen = sizeof(signed char); break;
455 case 's': typeLen = sizeof(signed short); break;
456 case 'i': typeLen = sizeof(signed int); break;
457 case 'C': typeLen = sizeof(unsigned char); break;
458 case 'S': typeLen = sizeof(unsigned short); break;
459 case 'I': typeLen = sizeof(unsigned int); break;
461 NSAssert1(NO, @"does not support ivars of type '%s'", ivarType);
464 ivarOffset = currentSize;
466 var = ivars->ivar_list + ivars->ivar_count;
469 var->ivar_name = ivarName;
470 var->ivar_offset = ivarOffset;
471 var->ivar_type = ivarType;
473 /* adjust current size */
474 currentSize = ivarOffset + typeLen;
479 // TODO: move the following to a subclass method
481 /* determine root class */
483 for (rootClazz = self; rootClazz->super_class != NULL; )
484 rootClazz = rootClazz->super_class;
486 /* setup meta class */
488 metaClazz = calloc(1, sizeof(struct objc_class));
489 metaClazz->isa = rootClazz->isa; // root-meta is the metameta
490 metaClazz->name = strdup([_className cString]);
491 metaClazz->info = CLS_META;
492 metaClazz->super_class = ((struct objc_class *)self)->isa;
493 metaClazz->instance_size = ((struct objc_class *)self)->isa->instance_size;
494 metaClazz->ivars = NULL;
495 metaClazz->protocols = NULL;
499 clazz = calloc(1, sizeof(struct objc_class));
500 clazz->isa = metaClazz; /* hook up meta class */
501 clazz->name = strdup([_className cString]);
502 clazz->info = CLS_CLASS;
503 clazz->super_class = self;
504 clazz->instance_size = currentSize;
505 clazz->ivars = ivars;
506 clazz->protocols = NULL;
509 NSLog(@"instance size: %d, ivar-count: %d",
510 currentSize, ivars->ivar_count);
513 /* setup method lists */
515 metaClazz->methodLists = calloc(1, sizeof(struct objc_method_list *));
516 clazz->methodLists = calloc(1, sizeof(struct objc_method_list *));
518 /* Note: MacOSX specific, Radar #3317376, hint taken from PyObjC */
519 metaClazz->methodLists[0] = (struct objc_method_list *)-1;
520 clazz->methodLists[0] = (struct objc_method_list *)-1;
522 /* add to runtime (according to PyObjC not reversible?) */
523 objc_addClass(clazz);
524 return NSClassFromString(_className);
530 ivars = malloc(sizeof(IvarList) + (sizeof(struct objc_ivar) * ivarCount));
531 ivars->ivar_count = ivarCount;
533 va_start(va, _name1);
534 for (n = _name1, t = va_arg(va, NSString *), ivarCount = 0;
535 (n != nil && t != nil);
536 n = va_arg(va, NSString *), t = va_arg(va, NSString *), ivarCount++) {
537 char *ivarName, *ivarType;
539 unsigned len, typeAlign, typeLen;
541 len = [n cStringLength];
542 ivarName = malloc(len + 2);
543 [n getCString:ivarName];
544 ivarName[len] = '\0';
546 len = [t cStringLength];
547 ivarType = malloc(len + 2);
548 [t getCString:ivarType];
549 ivarType[len] = '\0';
551 /* calc ivarOffset */
552 typeAlign = objc_alignof_type(ivarType);
553 typeLen = objc_sizeof_type(ivarType);
554 ivarOffset = currentSize;
556 /* check if offset is aligned */
557 if ((ivarOffset % typeAlign) != 0) {
559 len = (typeAlign - (ivarOffset % typeAlign));
563 /* adjust current size */
564 currentSize = ivarOffset + typeLen;
566 ivars->ivar_list[ivarCount].ivar_name = ivarName;
567 ivars->ivar_list[ivarCount].ivar_type = ivarType;
568 ivars->ivar_list[ivarCount].ivar_offset = ivarOffset;
572 return [self subclass:_className ivarsList:ivars];
577 /* instance variables */
579 + (NSArray *)instanceVariableNames {
584 if (((Class)self)->ivars == NULL || ((Class)self)->ivars->ivar_count == 0) {
585 if (emptyArray == nil) emptyArray = [[NSArray alloc] init];
589 names = calloc(((Class)self)->ivars->ivar_count + 2, sizeof(NSString *));
591 for (i = 0; i < ((Class)self)->ivars->ivar_count; i++) {
592 register unsigned char *ivarName;
594 ivarName = (void *)(((Class)self)->ivars->ivar_list[i].ivar_name);
595 if (ivarName == NULL) {
596 NSLog(@"WARNING(%s): ivar without name! (idx=%d)",
597 __PRETTY_FUNCTION__, i);
601 #if !LIB_FOUNDATION_LIBRARY
602 names[i] = [NSString stringWithCString:ivarName];
604 names[i] = [NSString stringWithCStringNoCopy:ivarName freeWhenDone:NO];
608 result = [NSArray arrayWithObjects:names
609 count:((Class)self)->ivars->ivar_count];
610 if (names) free(names);
613 + (NSArray *)allInstanceVariableNames {
614 NSMutableArray *varNames;
617 varNames = [NSMutableArray arrayWithCapacity:32];
618 for (c = self; c != Nil; c = [c superclass])
619 [varNames addObjectsFromArray:[c instanceVariableNames]];
621 return [[varNames copy] autorelease];
624 + (BOOL)hasInstanceVariableWithName:(NSString *)_ivarName {
626 unsigned len = [_ivarName cStringLength];
632 ivarName = malloc(len + 1);
633 [_ivarName getCString:ivarName]; ivarName[len] = '\0';
635 for (c = self; c != Nil; c = [c superclass]) {
638 for (i = 0; i < c->ivars->ivar_count; i++) {
639 if (strcmp(ivarName, c->ivars->ivar_list[i].ivar_name) == 0) {
649 + (NSString *)signatureOfInstanceVariableWithName:(NSString *)_ivarName {
651 unsigned len = [_ivarName cStringLength];
657 ivarName = malloc(len + 1);
658 [_ivarName getCString:ivarName]; ivarName[len] = '\0';
660 for (c = self; c != Nil; c = [c superclass]) {
663 for (i = 0; i < c->ivars->ivar_count; i++) {
664 if (strcmp(ivarName, c->ivars->ivar_list[i].ivar_name) == 0) {
665 /* found matching ivar name */
666 if (ivarName) free(ivarName);
667 #if !LIB_FOUNDATION_LIBRARY
668 return [NSString stringWithCString:
669 (char *)(c->ivars->ivar_list[i].ivar_type)];
671 return [NSString stringWithCStringNoCopy:
672 (char *)(c->ivars->ivar_list[i].ivar_type)
678 if (ivarName) free(ivarName);
683 + (unsigned)offsetOfInstanceVariableWithName:(NSString *)_ivarName {
685 unsigned len = [_ivarName cStringLength];
691 ivarName = malloc(len + 3);
692 [_ivarName getCString:ivarName]; ivarName[len] = '\0';
694 for (c = self; c != Nil; c = [c superclass]) {
697 for (i = 0; i < c->ivars->ivar_count; i++) {
698 if (strcmp(ivarName, c->ivars->ivar_list[i].ivar_name) == 0) {
699 /* found matching ivar name */
701 return c->ivars->ivar_list[i].ivar_offset;
709 @end /* NSObject(NGObjCRuntime) */
711 @implementation _NGMethodNameEnumerator
713 - (id)initWithClass:(Class)_clazz includeSuperclassMethods:(BOOL)_flag {
719 self->names = [[NSMutableSet alloc] initWithCapacity:200];
720 self->clazz = _clazz;
721 self->includeSuperclassMethods = _flag;
725 self->methods = class_nextMethodList(self->clazz, &(self->iter));
727 self->methods = _clazz->methods;
734 [self->names release];
739 if (self->clazz == nil)
742 if (self->methods == NULL) {
743 /* methods of current class are done .. */
744 if (!self->includeSuperclassMethods)
747 /* loop, maybe there are classes without a method-list ? */
748 while (self->methods == NULL) {
749 if ((self->clazz = [self->clazz superclass]) == Nil)
750 /* no more superclasses */
755 self->methods = class_nextMethodList(self->clazz, &(self->iter));
757 self->methods = self->clazz->methods;
764 NSAssert(self->methods, @"missing method-list !");
767 while (self->i >= (unsigned)self->methods->method_count) {
768 #if NeXT_RUNTIME || APPLE_RUNTIME
769 self->methods = class_nextMethodList(self->clazz, &(self->iter));
771 self->methods = self->methods->method_next;
773 if (self->methods == NULL)
778 if (self->methods == NULL) {
779 /* recurse to next super class */
780 return self->includeSuperclassMethods
790 m = &(self->methods->method_list[self->i]);
793 NSAssert(m, @"missing method structure !");
794 name = NSStringFromSelector(m->method_name);
795 NSAssert(name, @"couldn't get method name !");
797 if ([self->names containsObject:name]) {
798 /* this name was already delivered from a subclass, take next */
799 return [self nextObject];
802 [self->names addObject:name];
808 @end /* _NGMethodNameEnumerator */
812 @interface NGObjCClassEnumerator : NSEnumerator
818 @implementation NGObjCClassEnumerator
821 return objc_next_class(&(self->state));
824 @end /* NGObjCClassEnumerator */
826 #endif /* GNU_RUNTIME */