/*
  Copyright (C) 2000-2005 SKYRIX Software AG

  This file is part of SOPE.

  SOPE is free software; you can redistribute it and/or modify it under
  the terms of the GNU Lesser General Public License as published by the
  Free Software Foundation; either version 2, or (at your option) any
  later version.

  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
  License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with SOPE; see the file COPYING.  If not, write to the
  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  02111-1307, USA.
*/

#include "NGObjCRuntime.h"
#include "NGMemoryAllocation.h"
#include <objc/objc.h>
#include <objc/objc-api.h>
#include <stdlib.h>
#include "common.h"

#if NeXT_RUNTIME || APPLE_RUNTIME
#  include <objc/objc-class.h>
#  include <objc/objc-runtime.h>
typedef struct objc_method_list *MethodList_t;
typedef struct objc_ivar_list   *IvarList_t;
typedef struct objc_method      *Method_t;
#else
#  include <objc/encoding.h>
#endif

#import <Foundation/NSObject.h>
#import <Foundation/NSException.h>
#import <Foundation/NSString.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSEnumerator.h>

#define GCC_VERSION (__GNUC__ * 10000 \
                     + __GNUC_MINOR__ * 100 \
                     + __GNUC_PATCHLEVEL__)

#if GNUSTEP_BASE_LIBRARY
/* this is a hack for the extensions 0.8.6 library */

void class_add_behavior(Class class, Class behavior) {
  extern void behavior_class_add_class (Class class, Class behavior);
  behavior_class_add_class(class, behavior);
}

#endif

@interface _NGMethodNameEnumerator : NSEnumerator
{
  Class        clazz;
  BOOL         includeSuperclassMethods;
  NSMutableSet *names;

  struct objc_method_list *methods; // current method list
  unsigned i; // current index in current method list
#if APPLE_RUNTIME || NeXT_RUNTIME
  void     *iter; // runtime iterator
#endif
}

- (id)initWithClass:(Class)_clazz includeSuperclassMethods:(BOOL)_flag;
- (id)nextObject;

@end

@implementation NSObject(NGObjCRuntime)

static NSArray *emptyArray = nil;

static unsigned countMethodSpecs(SEL _selector, va_list *va)
     __attribute__((unused));

static unsigned countMethodSpecs(SEL _selector, va_list *va) {
  SEL      selector;
  NSString *signature;
  IMP      imp;
  unsigned count;
  
  selector  = _selector;
  signature = nil;
  imp       = NULL;
  if (selector)  signature = va_arg(*va, id);
  if (signature) imp       = va_arg(*va, IMP);
  
  count = 0;
  while ((selector != NULL) && (signature != nil) && (imp != NULL)) {
    count++;
    
    if ((selector  = va_arg(*va, SEL))            == NULL) break;
    if ((signature = (id)va_arg(*va, NSString *)) == nil)  break;
    if ((imp       = (IMP)va_arg(*va, IMP))       == NULL) break;
  }
  return count;
}

static void
fillMethodListWithSpecs(MethodList_t methods, SEL _selector, va_list *va)
{
  /* takes triple: SEL, signature, IMP */
  SEL      selector;
  NSString *signature;
  IMP      imp;
  unsigned count;
  
  selector  = _selector;
  signature = selector  ? va_arg(*va, NSString *) : nil;
  imp       = signature ? va_arg(*va, IMP) : NULL;
  count     = 0;
  while ((selector != NULL) && (signature != nil) && (imp != NULL)) {
    unsigned   len;
    char       *types;
#if GNU_RUNTIME
    const char *selname;
#endif
    
    /* allocate signature buffer */
    len = [signature cStringLength];
    types = malloc(len + 4);
    [signature getCString:types];
    types[len] = 0;
    
#if APPLE_RUNTIME || NeXT_RUNTIME
    count = methods->method_count;
    methods->method_list[count].method_name  = selector;
    methods->method_list[count].method_types = types;
    methods->method_list[count].method_imp   = imp;
    methods->method_count++;
#else
    /* determine selector name */
    selname  = sel_get_name(selector);

    /* fill structure */
    methods->method_list[count].method_name  = (SEL)selname;
    methods->method_list[count].method_types = types;
    methods->method_list[count].method_imp   = imp;
    count++;
#endif
    
    /* go to next method spec */
    if ((selector  = va_arg(*va, SEL))        == NULL) break;
    if ((signature = va_arg(*va, NSString *)) == nil)  break;
    if ((imp       = va_arg(*va, IMP))        == NULL) break;
  }
#if GNU_RUNTIME
  methods->method_count = count;
  methods->method_next  = NULL;
#endif
}

+ (unsigned)instanceSize {
  return ((Class)self)->instance_size;
}

/* adding methods */

+ (void)addMethodList:(MethodList_t)_methods {
  if (_methods == NULL)            return;
  if (_methods->method_count == 0) return;

#if NeXT_RUNTIME
  class_addMethods(self, _methods);
#else
  {
    extern void class_add_method_list (Class class, MethodList_t list);
    class_add_method_list(self, _methods);
  }
#endif
}

+ (void)addClassMethodList:(MethodList_t)_methods {
  if (_methods == NULL)            return;
  if (_methods->method_count == 0) return;
#if NeXT_RUNTIME
  class_addMethods(((Class)self)->isa, _methods);
#else
  {
    extern void class_add_method_list (Class class, MethodList_t list);
    class_add_method_list(((Class)self)->class_pointer, _methods);
  }
#endif
}

+ (void)addMethods:(SEL)_selector, ... {
  /* takes triples (sel, type, imp) finished by nil */
  MethodList_t methods;
  va_list      va;
  unsigned     count;
  
  va_start(va, _selector);
  count = countMethodSpecs(_selector, &va);
  va_end(va);
  if (count == 0) return;

#if NeXT_RUNTIME || APPLE_RUNTIME
  methods = malloc(sizeof(struct objc_method_list) +
                   ((count + 1) * sizeof(struct objc_method)));
  methods->method_count = 0;

  va_start(va, _selector);
  fillMethodListWithSpecs(methods, _selector, &va);
  va_end(va);
  
  [self addMethodList:methods];
#else
  methods = malloc(sizeof(MethodList) + (count + 2) * sizeof(Method));
  NSAssert(methods, @"could not allocate methodlist");
  
  va_start(va, _selector);
  fillMethodListWithSpecs(methods, _selector, &va);
  va_end(va);
  
  [self addMethodList:methods];
#endif
}

+ (void)addClassMethods:(SEL)_selector, ... {
  /* takes triples finished by nil */
  MethodList_t methods;
  va_list      va;
  unsigned     count;
  
  va_start(va, _selector);
  count = countMethodSpecs(_selector, &va);
  va_end(va);
  if (count == 0) return;
  
#if NeXT_RUNTIME
  methods = malloc(sizeof(struct objc_method_list) +
                   ((count + 1) * sizeof(struct objc_method)));
  methods->method_count = 0;

  va_start(va, _selector);
  fillMethodListWithSpecs(methods, _selector, &va);
  va_end(va);
  
  [self addClassMethodList:methods];
#else
  methods = malloc(sizeof(MethodList) + count * sizeof(Method));
  NSAssert(methods, @"couldn't allocate methodlist");
  
  va_start(va, _selector);
  fillMethodListWithSpecs(methods, _selector, &va);
  va_end(va);
  
  [self addClassMethodList:methods];
#endif
}

+ (NSEnumerator *)methodNameEnumerator {
  return [[[_NGMethodNameEnumerator alloc]
                                    initWithClass:self
                                    includeSuperclassMethods:NO]
                                    autorelease];
}
+ (NSEnumerator *)hierachyMethodNameEnumerator {
  return [[[_NGMethodNameEnumerator alloc]
                                    initWithClass:self
                                    includeSuperclassMethods:YES]
                                    autorelease];
}

/* subclassing */

+ (Class)subclass:(NSString *)_className
  ivarsList:(IvarList_t)_ivars
{
#if NeXT_RUNTIME
  [(NSObject *)self doesNotRecognizeSelector:_cmd];
  return Nil;
#else
  // TODO: do we really need a symtab? PyObjC does not seem to require that
  Module_t module;
  Class    newMetaClass, newClass;
  unsigned nameLen;
  char     *name, *moduleName, *metaName;
  int      instanceSize, i;
  
  /* define names */
  
  nameLen = [_className cStringLength];
  name    = malloc(nameLen + 3);
  [_className getCString:name];
  
  moduleName = name;
  metaName   = name;

  /* calc instance size */

  // printf("calc isize ..\n");

  for (i = 0, instanceSize = ((Class)self)->instance_size;
       i < _ivars->ivar_count; i++) {
    unsigned typeAlign, typeLen;
    
    // printf("ivar %s\n", _ivars->ivar_list[i].ivar_name);
    // printf("  type %s\n", _ivars->ivar_list[i].ivar_type);
    
    typeAlign = objc_alignof_type(_ivars->ivar_list[i].ivar_type);
    typeLen   = objc_sizeof_type(_ivars->ivar_list[i].ivar_type);
    
    /* check if offset is aligned */
    if ((instanceSize % typeAlign) != 0) {
      /* add alignment size */
      instanceSize += (typeAlign - (instanceSize % typeAlign));
    }
    instanceSize += typeLen;
  }
  
  /* allocate structures */
  
  newMetaClass = malloc(sizeof(struct objc_class));
  newClass     = malloc(sizeof(struct objc_class));
  NSCAssert(newMetaClass, @"could not allocate new meta class structure");
  NSCAssert(newClass,     @"could not allocate new class structure");
  
  // printf("setup meta ..\n");
  
  /* init meta class */
  newMetaClass->super_class    = (Class)((Class)self)->class_pointer->name;
  newMetaClass->class_pointer  = newMetaClass->super_class->class_pointer;
  newMetaClass->name           = metaName;
  newMetaClass->version        = 0;
  newMetaClass->info           = _CLS_META;
  newMetaClass->instance_size  = newMetaClass->super_class->instance_size;
  newMetaClass->methods        = NULL;
  newMetaClass->dtable         = NULL;
  newMetaClass->subclass_list  = NULL;
  newMetaClass->sibling_class  = NULL;
  newMetaClass->protocols      = NULL;
  newMetaClass->gc_object_type = NULL;
  
  // printf("setup class ..\n");
  /* init class */
  newClass->super_class    = (Class)((Class)self)->name;
  newClass->class_pointer  = newMetaClass;
  newClass->name           = name;
  newClass->version        = 0;
  newClass->info           = _CLS_CLASS;
  newClass->instance_size  = instanceSize;
  newClass->methods        = NULL;
  newClass->dtable         = NULL;
  newClass->subclass_list  = NULL;
  newClass->sibling_class  = NULL;
  newClass->protocols      = NULL;
  newClass->gc_object_type = NULL;
  newClass->ivars          = _ivars;
  
  /* allocate module */
  
  module = malloc(sizeof(Module));
  NSCAssert(module, @"could not allocate module !");
  memset(module, 0, sizeof(Module));
  module->version = 8;
  module->size    = sizeof(Module);
  module->name    = moduleName;

  /* allocate symtab with one entry */
  module->symtab = malloc(sizeof(Symtab) + (2 * sizeof(void *)));
  module->symtab->sel_ref_cnt = 0;
  module->symtab->refs        = 0; // ptr to array of 'struct objc_selector'
  module->symtab->cls_def_cnt = 1;
  module->symtab->cat_def_cnt = 0;
  module->symtab->defs[0] = newClass;
  module->symtab->defs[1] = NULL;

  /* execute module */
  {
#if GCC_VERSION < 30400
    extern void __objc_exec_class(Module_t module); // is thread-safe
    extern void __objc_resolve_class_links();
#else
    void __objc_exec_class(void* module);
    void __objc_resolve_class_links();
#endif
    
    //printf("execute class\n");
    __objc_exec_class(module);
    //printf("resolve links\n");
    __objc_resolve_class_links();
  }
  
  return NSClassFromString(_className);
#endif
}

+ (Class)subclass:(NSString *)_className
  ivars:(NSString *)_name1,...
{
  va_list    va;
  unsigned   ivarCount, currentSize;
  NSString   *n, *t;
  
  currentSize = ((Class)self)->instance_size;
  
  va_start(va, _name1);
  for (n = _name1, t = va_arg(va, NSString *), ivarCount = 0;
       (n != nil && t != nil);
       n = va_arg(va, NSString *), t = va_arg(va, NSString *))
    ivarCount++;
  va_end(va);
  
#if NeXT_RUNTIME || APPLE_RUNTIME
  {
    /* some tricks for Apple inspired by PyObjC, long live OpenSource ;-) */
    IvarList_t        ivars;
    struct objc_class *clazz;
    struct objc_class *metaClazz;
    struct objc_class *rootClazz;
    
    /* build ivars */
    
    ivars = NULL;
    if (ivarCount > 0) {
      ivars = calloc(sizeof(struct objc_ivar_list) +
		     (ivarCount) * sizeof(struct objc_ivar), sizeof(char));
      
      va_start(va, _name1);
      for (n = _name1, t = va_arg(va, NSString *), ivarCount = 0;
	   (n != nil && t != nil);
	   n = va_arg(va, NSString *), t=va_arg(va, NSString *), ivarCount++) {
	Ivar     var;
	char     *ivarName, *ivarType;
	int      ivarOffset;
	unsigned len, typeAlign, typeLen;
	
	len = [n cStringLength];
	ivarName = malloc(len + 2);
	[n getCString:ivarName];
	ivarName[len] = '\0';
	
	len = [t cStringLength];
	ivarType = malloc(len + 2);
	[t getCString:ivarType];
	ivarType[len] = '\0';
	
	/* calc ivarOffset */
	typeAlign = 0; // TODO: alignment?!
	// TODO: add more types ...
	switch (*ivarType) { 
	  /* the Apple runtime has no func to calc a type size ?! */
	case '@': typeLen = sizeof(id);    break;
	case ':': typeLen = sizeof(SEL);   break;
	case 'c': typeLen = sizeof(signed char);  break;
	case 's': typeLen = sizeof(signed short); break;
	case 'i': typeLen = sizeof(signed int);   break;
	case 'C': typeLen = sizeof(unsigned char);  break;
	case 'S': typeLen = sizeof(unsigned short); break;
	case 'I': typeLen = sizeof(unsigned int);   break;
	default:
	  NSAssert1(NO, @"does not support ivars of type '%s'", ivarType);
	  break;
	}
	ivarOffset = currentSize;

	var = ivars->ivar_list + ivars->ivar_count;
	ivars->ivar_count++;
	
	var->ivar_name   = ivarName;
	var->ivar_offset = ivarOffset;
	var->ivar_type   = ivarType;
	
	/* adjust current size */
	currentSize = ivarOffset + typeLen;
      }
      va_end(va);
    }
    
    // TODO: move the following to a subclass method
    
    /* determine root class */
    
    for (rootClazz = self; rootClazz->super_class != NULL; )
      rootClazz = rootClazz->super_class;

    /* setup meta class */
    
    metaClazz = calloc(1, sizeof(struct objc_class));
    metaClazz->isa           = rootClazz->isa; // root-meta is the metameta
    metaClazz->name          = strdup([_className cString]);
    metaClazz->info          = CLS_META;
    metaClazz->super_class   = ((struct objc_class *)self)->isa;
    metaClazz->instance_size = ((struct objc_class *)self)->isa->instance_size;
    metaClazz->ivars         = NULL;
    metaClazz->protocols     = NULL;
    
    /* setup class */
    
    clazz = calloc(1, sizeof(struct objc_class));
    clazz->isa           = metaClazz; /* hook up meta class */
    clazz->name          = strdup([_className cString]);
    clazz->info          = CLS_CLASS;
    clazz->super_class   = self;
    clazz->instance_size = currentSize;
    clazz->ivars         = ivars;
    clazz->protocols     = NULL;

#if 0
    NSLog(@"instance size: %d, ivar-count: %d",
	  currentSize, ivars->ivar_count);
#endif
    
    /* setup method lists */
    
    metaClazz->methodLists = calloc(1, sizeof(struct objc_method_list *));
    clazz->methodLists     = calloc(1, sizeof(struct objc_method_list *));
    
    /* Note: MacOSX specific, Radar #3317376, hint taken from PyObjC */
    metaClazz->methodLists[0] = (struct objc_method_list *)-1;
    clazz->methodLists[0]     = (struct objc_method_list *)-1;
    
    /* add to runtime (according to PyObjC not reversible?) */
    objc_addClass(clazz);
    return NSClassFromString(_className);
  }
#else
  {
  IvarList_t ivars;
  
  ivars = malloc(sizeof(IvarList) + (sizeof(struct objc_ivar) * ivarCount));
  ivars->ivar_count = ivarCount;
  
  va_start(va, _name1);
  for (n = _name1, t = va_arg(va, NSString *), ivarCount = 0;
       (n != nil && t != nil);
       n = va_arg(va, NSString *), t = va_arg(va, NSString *), ivarCount++) {
    char     *ivarName, *ivarType;
    int      ivarOffset;
    unsigned len, typeAlign, typeLen;
    
    len = [n cStringLength];
    ivarName = malloc(len + 2);
    [n getCString:ivarName];
    ivarName[len] = '\0';
    
    len = [t cStringLength];
    ivarType = malloc(len + 2);
    [t getCString:ivarType];
    ivarType[len] = '\0';
    
    /* calc ivarOffset */
    typeAlign  = objc_alignof_type(ivarType);
    typeLen    = objc_sizeof_type(ivarType);
    ivarOffset = currentSize;
    
    /* check if offset is aligned */
    if ((ivarOffset % typeAlign) != 0) {
      /* align offset */
      len = (typeAlign - (ivarOffset % typeAlign));
      ivarOffset += len;
    }

    /* adjust current size */
    currentSize = ivarOffset + typeLen;
    
    ivars->ivar_list[ivarCount].ivar_name   = ivarName;
    ivars->ivar_list[ivarCount].ivar_type   = ivarType;
    ivars->ivar_list[ivarCount].ivar_offset = ivarOffset;
  }
  va_end(va);
  
  return [self subclass:_className ivarsList:ivars];
  }
#endif
}

/* instance variables */

+ (NSArray *)instanceVariableNames {
  NSArray  *result;
  NSString **names;
  int i;
  
  if (((Class)self)->ivars == NULL || ((Class)self)->ivars->ivar_count == 0) {
    if (emptyArray == nil) emptyArray = [[NSArray alloc] init];
    return emptyArray;
  }

  names = calloc(((Class)self)->ivars->ivar_count + 2, sizeof(NSString *));
  
  for (i = 0; i < ((Class)self)->ivars->ivar_count; i++) {
    register unsigned char *ivarName;
    
    ivarName = (void *)(((Class)self)->ivars->ivar_list[i].ivar_name);
    if (ivarName == NULL) {
      NSLog(@"WARNING(%s): ivar without name! (idx=%d)", 
	    __PRETTY_FUNCTION__, i);
      continue;
    }
    
#if !LIB_FOUNDATION_LIBRARY
    names[i] = [NSString stringWithCString:ivarName];
#else
    names[i] = [NSString stringWithCStringNoCopy:ivarName freeWhenDone:NO];
#endif
  }
  
  result = [NSArray arrayWithObjects:names
		    count:((Class)self)->ivars->ivar_count];
  if (names) free(names);
  return result;
}
+ (NSArray *)allInstanceVariableNames {
  NSMutableArray *varNames;
  Class c;

  varNames = [NSMutableArray arrayWithCapacity:32];
  for (c = self; c != Nil; c = [c superclass])
    [varNames addObjectsFromArray:[c instanceVariableNames]];

  return [[varNames copy] autorelease];
}

+ (BOOL)hasInstanceVariableWithName:(NSString *)_ivarName {
  Class    c;
  unsigned len = [_ivarName cStringLength];
  char     *ivarName;
  
  if (len == 0)
    return NO;
  
  ivarName = malloc(len + 1);
  [_ivarName getCString:ivarName]; ivarName[len] = '\0';
  
  for (c = self; c != Nil; c = [c superclass]) {
    int i;
    
    for (i = 0; i < c->ivars->ivar_count; i++) {
      if (strcmp(ivarName, c->ivars->ivar_list[i].ivar_name) == 0) {
        free(ivarName);
        return YES;
      }
    }
  }
  free(ivarName);
  return NO;
}

+ (NSString *)signatureOfInstanceVariableWithName:(NSString *)_ivarName {
  Class    c;
  unsigned len = [_ivarName cStringLength];
  char     *ivarName;
  
  if (len == 0)
    return nil;

  ivarName = malloc(len + 1);
  [_ivarName getCString:ivarName]; ivarName[len] = '\0';
  
  for (c = self; c != Nil; c = [c superclass]) {
    int i;

    for (i = 0; i < c->ivars->ivar_count; i++) {
      if (strcmp(ivarName, c->ivars->ivar_list[i].ivar_name) == 0) {
        /* found matching ivar name */
        if (ivarName) free(ivarName);
#if !LIB_FOUNDATION_LIBRARY
        return [NSString stringWithCString:
                           (char *)(c->ivars->ivar_list[i].ivar_type)];
#else
        return [NSString stringWithCStringNoCopy:
                           (char *)(c->ivars->ivar_list[i].ivar_type)
                         freeWhenDone:NO];
#endif
      }
    }
  }
  if (ivarName) free(ivarName);
  return nil;
}


+ (unsigned)offsetOfInstanceVariableWithName:(NSString *)_ivarName {
  Class    c;
  unsigned len = [_ivarName cStringLength];
  char     *ivarName;
  
  if (len == 0)
    return NSNotFound;

  ivarName = malloc(len + 3);
  [_ivarName getCString:ivarName]; ivarName[len] = '\0';
  
  for (c = self; c != Nil; c = [c superclass]) {
    int i;

    for (i = 0; i < c->ivars->ivar_count; i++) {
      if (strcmp(ivarName, c->ivars->ivar_list[i].ivar_name) == 0) {
        /* found matching ivar name */
        free(ivarName);
        return c->ivars->ivar_list[i].ivar_offset;
      }
    }
  }
  free(ivarName);
  return NSNotFound;
}

@end /* NSObject(NGObjCRuntime) */

@implementation _NGMethodNameEnumerator

- (id)initWithClass:(Class)_clazz includeSuperclassMethods:(BOOL)_flag {
  if (_clazz == Nil) {
    [self release];
    return nil;
  }

  self->names = [[NSMutableSet alloc] initWithCapacity:200];
  self->clazz                    = _clazz;
  self->includeSuperclassMethods = _flag;

#if NeXT_RUNTIME
  self->iter    = 0;
  self->methods = class_nextMethodList(self->clazz, &(self->iter));
#else
  self->methods = _clazz->methods;
  self->i       = 0;
#endif
  return self;
}

- (void)dealloc {
  [self->names release];
  [super dealloc];
}

- (id)nextObject {
  if (self->clazz == nil)
    return nil;

  if (self->methods == NULL) {
    /* methods of current class are done .. */
    if (!self->includeSuperclassMethods)
      return nil;

    /* loop, maybe there are classes without a method-list ? */
    while (self->methods == NULL) {
      if ((self->clazz = [self->clazz superclass]) == Nil)
        /* no more superclasses */
        return nil;
      
#if NeXT_RUNTIME
      self->iter = 0;
      self->methods = class_nextMethodList(self->clazz, &(self->iter));
#else
      self->methods = self->clazz->methods;
#endif
    }
    self->i = 0;
  }
  
#if DEBUG
  NSAssert(self->methods, @"missing method-list !");
#endif
  
  while (self->i >= (unsigned)self->methods->method_count) {
#if NeXT_RUNTIME || APPLE_RUNTIME
    self->methods = class_nextMethodList(self->clazz, &(self->iter));
#else
    self->methods = self->methods->method_next;
#endif
    if (self->methods == NULL)
      break;
    self->i = 0;
  }
  
  if (self->methods == NULL) {
    /* recurse to next super class */
    return self->includeSuperclassMethods
      ? [self nextObject]
      : nil;
  }

  /* get name .. */
  {
    Method_t m;
    NSString *name;

    m = &(self->methods->method_list[self->i]);
    self->i++;
    
    NSAssert(m, @"missing method structure !");
    name = NSStringFromSelector(m->method_name);
    NSAssert(name, @"couldn't get method name !");
    
    if ([self->names containsObject:name]) {
      /* this name was already delivered from a subclass, take next */
      return [self nextObject];
    }

    [self->names addObject:name];
    
    return name;
  }
}

@end /* _NGMethodNameEnumerator */

#if GNU_RUNTIME

@interface NGObjCClassEnumerator : NSEnumerator
{
  void *state;
}
@end

@implementation NGObjCClassEnumerator

- (id)nextObject {
  return objc_next_class(&(self->state));
}

@end /* NGObjCClassEnumerator */

#endif /* GNU_RUNTIME */
