]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
1998-09-21 Ben Elliston <bje@cygnus.com>
authorbje <bje@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 21 Sep 1998 01:22:07 +0000 (01:22 +0000)
committerbje <bje@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 21 Sep 1998 01:22:07 +0000 (01:22 +0000)
        * New directory.  Moved files from ../gcc/objc.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@22514 138bc75d-0d04-0410-961f-82ee72b054a4

44 files changed:
libobjc/ChangeLog [new file with mode: 0644]
libobjc/NXConstStr.h [new file with mode: 0644]
libobjc/NXConstStr.m [new file with mode: 0644]
libobjc/Object.h [new file with mode: 0644]
libobjc/Object.m [new file with mode: 0644]
libobjc/Protocol.h [new file with mode: 0644]
libobjc/Protocol.m [new file with mode: 0644]
libobjc/archive.c [new file with mode: 0644]
libobjc/class.c [new file with mode: 0644]
libobjc/encoding.c [new file with mode: 0644]
libobjc/encoding.h [new file with mode: 0644]
libobjc/gc.c [new file with mode: 0644]
libobjc/hash.c [new file with mode: 0644]
libobjc/hash.h [new file with mode: 0644]
libobjc/init.c [new file with mode: 0644]
libobjc/libobjc.def [new file with mode: 0644]
libobjc/libobjc_entry.c [new file with mode: 0644]
libobjc/linking.m [new file with mode: 0644]
libobjc/makefile.dos [new file with mode: 0644]
libobjc/misc.c [new file with mode: 0644]
libobjc/nil_method.c [new file with mode: 0644]
libobjc/objc-api.h [new file with mode: 0644]
libobjc/objc-list.h [new file with mode: 0644]
libobjc/objc.h [new file with mode: 0644]
libobjc/objects.c [new file with mode: 0644]
libobjc/runtime.h [new file with mode: 0644]
libobjc/sarray.c [new file with mode: 0644]
libobjc/sarray.h [new file with mode: 0644]
libobjc/selector.c [new file with mode: 0644]
libobjc/sendmsg.c [new file with mode: 0644]
libobjc/thr-dce.c [new file with mode: 0644]
libobjc/thr-decosf1.c [new file with mode: 0644]
libobjc/thr-irix.c [new file with mode: 0644]
libobjc/thr-mach.c [new file with mode: 0644]
libobjc/thr-os2.c [new file with mode: 0644]
libobjc/thr-posix.c [new file with mode: 0644]
libobjc/thr-pthreads.c [new file with mode: 0644]
libobjc/thr-single.c [new file with mode: 0644]
libobjc/thr-solaris.c [new file with mode: 0644]
libobjc/thr-vxworks.c [new file with mode: 0644]
libobjc/thr-win32.c [new file with mode: 0644]
libobjc/thr.c [new file with mode: 0644]
libobjc/thr.h [new file with mode: 0644]
libobjc/typedstream.h [new file with mode: 0644]

diff --git a/libobjc/ChangeLog b/libobjc/ChangeLog
new file mode 100644 (file)
index 0000000..28d7554
--- /dev/null
@@ -0,0 +1,4 @@
+1998-09-21  Ben Elliston  <bje@cygnus.com>
+
+       * New directory.  Moved files from ../gcc/objc.
+
diff --git a/libobjc/NXConstStr.h b/libobjc/NXConstStr.h
new file mode 100644 (file)
index 0000000..c979954
--- /dev/null
@@ -0,0 +1,44 @@
+/* Interface for the NXConstantString class for Objective-C.
+   Copyright (C) 1995 Free Software Foundation, Inc.
+   Contributed by Pieter J. Schoenmakers <tiggr@es.ele.tue.nl>
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU CC 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 General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+#ifndef __nxconstantstring_INCLUDE_GNU
+#define __nxconstantstring_INCLUDE_GNU
+
+#include "objc/Object.h"
+
+@interface NXConstantString: Object
+{
+  char *c_string;
+  unsigned int len;
+}
+
+-(const char *) cString;
+-(unsigned int) length;
+
+@end
+
+#endif
diff --git a/libobjc/NXConstStr.m b/libobjc/NXConstStr.m
new file mode 100644 (file)
index 0000000..4d2f3e1
--- /dev/null
@@ -0,0 +1,42 @@
+/* Implementation of the NXConstantString class for Objective-C.
+   Copyright (C) 1995 Free Software Foundation, Inc.
+   Contributed by Pieter J. Schoenmakers <tiggr@es.ele.tue.nl>
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU CC 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 General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+#include "objc/NXConstStr.h"
+
+@implementation NXConstantString
+
+-(const char *) cString
+{
+  return (c_string);
+} /* -cString */
+
+-(unsigned int) length
+{
+  return (len);
+} /* -length */
+
+@end
diff --git a/libobjc/Object.h b/libobjc/Object.h
new file mode 100644 (file)
index 0000000..a762acc
--- /dev/null
@@ -0,0 +1,124 @@
+/* Interface for the Object class for Objective-C.
+   Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU CC 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 General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled
+   with GCC to produce an executable, this does not cause the resulting
+   executable to be covered by the GNU General Public License.  This
+   exception does not however invalidate any other reasons why the
+   executable file might be covered by the GNU General Public License. */
+
+#ifndef __object_INCLUDE_GNU
+#define __object_INCLUDE_GNU
+
+#include <objc/objc.h>
+#include <objc/typedstream.h>
+
+/*
+ * All classes are derived from Object.  As such,
+ * this is the overhead tacked onto those objects.
+ */
+@interface Object
+{
+    Class      isa;    /* A pointer to the instance's class structure */
+}
+
+        /* Initializing classes and instances */
++ initialize;
+- init;
+
+        /* Creating, freeing, and copying instances */
++ new;
++ alloc;
+- free;
+- copy;
+- shallowCopy;
+- deepen;
+- deepCopy;
+
+        /* Identifying classes */
+- (Class)class;
+- (Class)superClass;
+- (MetaClass)metaClass;
+- (const char *)name;
+
+        /* Identifying and comparing objects */
+- self;
+- (unsigned int)hash;
+- (BOOL)isEqual:anObject;
+- (int)compare:anotherObject;
+
+        /* Testing object type */
+- (BOOL)isMetaClass;
+- (BOOL)isClass;
+- (BOOL)isInstance;
+
+        /* Testing inheritance relationships */
+- (BOOL)isKindOf:(Class)aClassObject;
+- (BOOL)isMemberOf:(Class)aClassObject;
+- (BOOL)isKindOfClassNamed:(const char *)aClassName;
+- (BOOL)isMemberOfClassNamed:(const char *)aClassName;
+
+        /* Testing class functionality */
++ (BOOL)instancesRespondTo:(SEL)aSel;
+- (BOOL)respondsTo:(SEL)aSel;
+
+       /* Testing protocol conformance */
+- (BOOL)conformsTo:(Protocol*)aProtocol;
+
+        /* Introspection */
++ (IMP)instanceMethodFor:(SEL)aSel;
+- (IMP)methodFor:(SEL)aSel;
++ (struct objc_method_description *)descriptionForInstanceMethod:(SEL)aSel;
+- (struct objc_method_description *)descriptionForMethod:(SEL)aSel;
+
+        /* Sending messages determined at run time */
+- perform:(SEL)aSel;
+- perform:(SEL)aSel with:anObject;
+- perform:(SEL)aSel with:anObject1 with:anObject2;
+
+        /* Forwarding */
+- (retval_t)forward:(SEL)aSel :(arglist_t)argFrame;
+- (retval_t)performv:(SEL)aSel :(arglist_t)argFrame;
+
+        /* Posing */
++ poseAs:(Class)aClassObject;
+- (Class)transmuteClassTo:(Class)aClassObject;
+
+        /* Enforcing intentions */
+- subclassResponsibility:(SEL)aSel;
+- notImplemented:(SEL)aSel;
+- shouldNotImplement:(SEL)aSel;
+
+        /* Error handling */
+- doesNotRecognize:(SEL)aSel;
+- error:(const char *)aString, ...;
+
+        /* Archiving */
++ (int)version;
++ setVersion:(int)aVersion;
++ (int)streamVersion: (TypedStream*)aStream;
+
+- read: (TypedStream*)aStream;
+- write: (TypedStream*)aStream;
+- awake;
+
+@end
+
+#endif
diff --git a/libobjc/Object.m b/libobjc/Object.m
new file mode 100644 (file)
index 0000000..64b52f4
--- /dev/null
@@ -0,0 +1,387 @@
+/* The implementation of class Object for Objective-C.
+   Copyright (C) 1993, 1994, 1995, 1997 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU CC 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 General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled
+   with GCC to produce an executable, this does not cause the resulting
+   executable to be covered by the GNU General Public License.  This
+   exception does not however invalidate any other reasons why the
+   executable file might be covered by the GNU General Public License. */
+
+#include <stdarg.h>
+#include "objc/Object.h"
+#include "objc/Protocol.h"
+#include "objc/objc-api.h"
+
+extern int errno;
+
+#define MAX_CLASS_NAME_LEN 256
+
+@implementation Object
+
++ initialize
+{
+  return self;
+}
+
+- init
+{
+  return self;
+}
+
++ new
+{
+  return [[self alloc] init];
+}
+
++ alloc
+{
+  return class_create_instance(self);
+}
+
+- free
+{
+  return object_dispose(self);
+}
+
+- copy
+{
+  return [[self shallowCopy] deepen];
+}
+
+- shallowCopy
+{
+  return object_copy(self);
+}
+
+- deepen
+{
+  return self;
+}
+
+- deepCopy
+{
+  return [self copy];
+}
+
+- (Class)class
+{
+  return object_get_class(self);
+}
+
+- (Class)superClass
+{
+  return object_get_super_class(self);
+}
+
+- (MetaClass)metaClass
+{
+  return object_get_meta_class(self);
+}
+
+- (const char *)name
+{
+  return object_get_class_name(self);
+}
+
+- self
+{
+  return self;
+}
+
+- (unsigned int)hash
+{
+  return (size_t)self;
+}
+
+- (BOOL)isEqual:anObject
+{
+  return self==anObject;
+}
+
+- (int)compare:anotherObject;
+{
+  if ([self isEqual:anotherObject])
+    return 0;
+  // Ordering objects by their address is pretty useless, 
+  // so subclasses should override this is some useful way.
+  else if (self > anotherObject)
+    return 1;
+  else 
+    return -1;
+}
+
+- (BOOL)isMetaClass
+{
+  return NO;
+}
+
+- (BOOL)isClass
+{
+  return object_is_class(self);
+}
+
+- (BOOL)isInstance
+{
+  return object_is_instance(self);
+}
+
+- (BOOL)isKindOf:(Class)aClassObject
+{
+  Class class;
+
+  for (class = self->isa; class!=Nil; class = class_get_super_class(class))
+    if (class==aClassObject)
+      return YES;
+  return NO;
+}
+
+- (BOOL)isMemberOf:(Class)aClassObject
+{
+  return self->isa==aClassObject;
+}
+
+- (BOOL)isKindOfClassNamed:(const char *)aClassName
+{
+  Class class;
+
+  if (aClassName!=NULL)
+    for (class = self->isa; class!=Nil; class = class_get_super_class(class))
+      if (!strcmp(class_get_class_name(class), aClassName))
+        return YES;
+  return NO;
+}
+
+- (BOOL)isMemberOfClassNamed:(const char *)aClassName
+{
+  return ((aClassName!=NULL)
+          &&!strcmp(class_get_class_name(self->isa), aClassName));
+}
+
++ (BOOL)instancesRespondTo:(SEL)aSel
+{
+  return class_get_instance_method(self, aSel)!=METHOD_NULL;
+}
+
+- (BOOL)respondsTo:(SEL)aSel
+{
+  return ((object_is_instance(self)
+           ?class_get_instance_method(self->isa, aSel)
+           :class_get_class_method(self->isa, aSel))!=METHOD_NULL);
+}
+
++ (IMP)instanceMethodFor:(SEL)aSel
+{
+  return method_get_imp(class_get_instance_method(self, aSel));
+}
+
+// Indicates if the receiving class or instance conforms to the given protocol
+// not usually overridden by subclasses
+//
+// Modified 9/5/94 to always search the class object's protocol list, rather
+// than the meta class.
+
++ (BOOL) conformsTo: (Protocol*)aProtocol
+{
+  int i;
+  struct objc_protocol_list* proto_list;
+  id parent;
+
+  for (proto_list = ((Class)self)->protocols;
+       proto_list; proto_list = proto_list->next)
+    {
+      for (i=0; i < proto_list->count; i++)
+      {
+        if ([proto_list->list[i] conformsTo: aProtocol])
+          return YES;
+      }
+    }
+
+  if ((parent = [self superClass]))
+    return [parent conformsTo: aProtocol];
+  else
+    return NO;
+}
+
+- (BOOL) conformsTo: (Protocol*)aProtocol
+{
+  return [[self class] conformsTo:aProtocol];
+}
+
+- (IMP)methodFor:(SEL)aSel
+{
+  return (method_get_imp(object_is_instance(self)
+                         ?class_get_instance_method(self->isa, aSel)
+                         :class_get_class_method(self->isa, aSel)));
+}
+
++ (struct objc_method_description *)descriptionForInstanceMethod:(SEL)aSel
+{
+  return ((struct objc_method_description *)
+           class_get_instance_method(self, aSel));
+}
+
+- (struct objc_method_description *)descriptionForMethod:(SEL)aSel
+{
+  return ((struct objc_method_description *)
+           (object_is_instance(self)
+            ?class_get_instance_method(self->isa, aSel)
+            :class_get_class_method(self->isa, aSel)));
+}
+
+- perform:(SEL)aSel
+{
+  IMP msg = objc_msg_lookup(self, aSel);
+  if (!msg)
+    return [self error:"invalid selector passed to %s", sel_get_name(_cmd)];
+  return (*msg)(self, aSel);
+}
+
+- perform:(SEL)aSel with:anObject
+{
+  IMP msg = objc_msg_lookup(self, aSel);
+  if (!msg)
+    return [self error:"invalid selector passed to %s", sel_get_name(_cmd)];
+  return (*msg)(self, aSel, anObject);
+}
+
+- perform:(SEL)aSel with:anObject1 with:anObject2
+{
+  IMP msg = objc_msg_lookup(self, aSel);
+  if (!msg)
+    return [self error:"invalid selector passed to %s", sel_get_name(_cmd)];
+  return (*msg)(self, aSel, anObject1, anObject2);
+}
+
+- (retval_t)forward:(SEL)aSel :(arglist_t)argFrame
+{
+  return (retval_t)[self doesNotRecognize: aSel];
+}
+
+- (retval_t)performv:(SEL)aSel :(arglist_t)argFrame
+{
+  return objc_msg_sendv(self, aSel, argFrame);
+}
+
++ poseAs:(Class)aClassObject
+{
+  return class_pose_as(self, aClassObject);
+}
+
+- (Class)transmuteClassTo:(Class)aClassObject
+{
+  if (object_is_instance(self))
+    if (class_is_class(aClassObject))
+      if (class_get_instance_size(aClassObject)==class_get_instance_size(isa))
+        if ([self isKindOf:aClassObject])
+          {
+            Class old_isa = isa;
+            isa = aClassObject;
+            return old_isa;
+          }
+  return nil;
+}
+
+- subclassResponsibility:(SEL)aSel
+{
+  return [self error:"subclass should override %s", sel_get_name(aSel)];
+}
+
+- notImplemented:(SEL)aSel
+{
+  return [self error:"method %s not implemented", sel_get_name(aSel)];
+}
+
+- shouldNotImplement:(SEL)aSel
+{
+  return [self error:"%s should not implement %s", 
+                    object_get_class_name(self), sel_get_name(aSel)];
+}
+
+- doesNotRecognize:(SEL)aSel
+{
+  return [self error:"%s does not recognize %s",
+                     object_get_class_name(self), sel_get_name(aSel)];
+}
+
+#ifdef __alpha__
+extern size_t strlen(const char*);
+#endif
+
+- error:(const char *)aString, ...
+{
+#define FMT "error: %s (%s)\n%s\n"
+  char fmt[(strlen((char*)FMT)+strlen((char*)object_get_class_name(self))
+            +((aString!=NULL)?strlen((char*)aString):0)+8)];
+  va_list ap;
+
+  sprintf(fmt, FMT, object_get_class_name(self),
+                    object_is_instance(self)?"instance":"class",
+                    (aString!=NULL)?aString:"");
+  va_start(ap, aString);
+  objc_verror(self, OBJC_ERR_UNKNOWN, fmt, ap);
+  va_end(ap);
+  return nil;
+#undef FMT
+}
+
++ (int)version
+{
+  return class_get_version(self);
+}
+
++ setVersion:(int)aVersion
+{
+  class_set_version(self, aVersion);
+  return self;
+}
+
++ (int)streamVersion: (TypedStream*)aStream
+{
+  if (aStream->mode == OBJC_READONLY)
+    return objc_get_stream_class_version (aStream, self);
+  else
+    return class_get_version (self);
+}
+
+// These are used to write or read the instance variables 
+// declared in this particular part of the object.  Subclasses
+// should extend these, by calling [super read/write: aStream]
+// before doing their own archiving.  These methods are private, in
+// the sense that they should only be called from subclasses.
+
+- read: (TypedStream*)aStream
+{
+  // [super read: aStream];  
+  return self;
+}
+
+- write: (TypedStream*)aStream
+{
+  // [super write: aStream];
+  return self;
+}
+
+- awake
+{
+  // [super awake];
+  return self;
+}
+
+@end
diff --git a/libobjc/Protocol.h b/libobjc/Protocol.h
new file mode 100644 (file)
index 0000000..c7464cf
--- /dev/null
@@ -0,0 +1,58 @@
+/* Declare the class Protocol for Objective C programs.
+   Copyright (C) 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+#ifndef __Protocol_INCLUDE_GNU
+#define __Protocol_INCLUDE_GNU
+
+#include "objc/Object.h"
+
+@interface Protocol : Object
+{
+@private
+        char *protocol_name;
+        struct objc_protocol_list *protocol_list;
+        struct objc_method_description_list *instance_methods, *class_methods; 
+}
+
+/* Obtaining attributes intrinsic to the protocol */
+
+- (const char *)name;
+
+/* Testing protocol conformance */
+
+- (BOOL) conformsTo: (Protocol *)aProtocolObject;
+
+/* Looking up information specific to a protocol */
+
+- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel;
+- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel;
+
+@end
+
+
+
+
+#endif __Protocol_INCLUDE_GNU
diff --git a/libobjc/Protocol.m b/libobjc/Protocol.m
new file mode 100644 (file)
index 0000000..43ba44e
--- /dev/null
@@ -0,0 +1,128 @@
+/* This file contains the implementation of class Protocol.
+   Copyright (C) 1993 Free Software Foundation, Inc.
+
+This file is part of GNU CC. 
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+#include "objc/Protocol.h"
+#include "objc/objc-api.h"
+
+/* Method description list */
+struct objc_method_description_list {
+        int count;
+        struct objc_method_description list[1];
+};
+
+
+@implementation Protocol
+{
+@private
+        char *protocol_name;
+        struct objc_protocol_list *protocol_list;
+        struct objc_method_description_list *instance_methods, *class_methods; 
+}
+
+/* Obtaining attributes intrinsic to the protocol */
+
+- (const char *)name
+{
+  return protocol_name;
+}
+
+/* Testing protocol conformance */
+
+- (BOOL) conformsTo: (Protocol *)aProtocolObject
+{
+  int i;
+  struct objc_protocol_list* proto_list;
+
+  if (!strcmp(aProtocolObject->protocol_name, self->protocol_name))
+    return YES;
+
+  for (proto_list = protocol_list; proto_list; proto_list = proto_list->next)
+    {
+      for (i=0; i < proto_list->count; i++)
+       {
+         if ([proto_list->list[i] conformsTo: aProtocolObject])
+           return YES;
+       }
+    }
+
+  return NO;
+}
+
+/* Looking up information specific to a protocol */
+
+- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel
+{
+  int i;
+  struct objc_protocol_list* proto_list;
+  const char* name = sel_get_name (aSel);
+  struct objc_method_description *result;
+
+  for (i = 0; i < instance_methods->count; i++)
+    {
+      if (!strcmp ((char*)instance_methods->list[i].name, name))
+       return &(instance_methods->list[i]);
+    }
+
+  for (proto_list = protocol_list; proto_list; proto_list = proto_list->next)
+    {
+      for (i=0; i < proto_list->count; i++)
+       {
+         if ((result = [proto_list->list[i]
+                        descriptionForInstanceMethod: aSel]))
+           return result;
+       }
+    }
+
+  return NULL;
+}
+
+- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel;
+{
+  int i;
+  struct objc_protocol_list* proto_list;
+  const char* name = sel_get_name (aSel);
+  struct objc_method_description *result;
+
+  for (i = 0; i < class_methods->count; i++)
+    {
+      if (!strcmp ((char*)class_methods->list[i].name, name))
+       return &(class_methods->list[i]);
+    }
+
+  for (proto_list = protocol_list; proto_list; proto_list = proto_list->next)
+    {
+      for (i=0; i < proto_list->count; i++)
+       {
+         if ((result = [proto_list->list[i]
+                        descriptionForClassMethod: aSel]))
+           return result;
+       }
+    }
+
+  return NULL;
+}
+
+@end
diff --git a/libobjc/archive.c b/libobjc/archive.c
new file mode 100644 (file)
index 0000000..c762fe6
--- /dev/null
@@ -0,0 +1,1651 @@
+/* GNU Objective C Runtime archiving
+   Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Kresten Krab Thorup
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include "config.h"
+#include "runtime.h"
+#include "typedstream.h"
+#include "encoding.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+extern int fflush(FILE*);
+
+#define ROUND(V, A) \
+  ({ typeof(V) __v=(V); typeof(A) __a=(A);  \
+     __a*((__v+__a-1)/__a); })
+
+#define PTR2LONG(P) (((char*)(P))-(char*)0)
+#define LONG2PTR(L) (((char*)0)+(L))
+
+/* Declare some functions... */
+
+static int
+objc_read_class (struct objc_typed_stream* stream, Class* class);
+
+int objc_sizeof_type(const char* type);
+
+static int
+objc_write_use_common (struct objc_typed_stream* stream, unsigned long key);
+
+static int
+objc_write_register_common (struct objc_typed_stream* stream,
+                           unsigned long key);
+
+static int 
+objc_write_class (struct objc_typed_stream* stream,
+                        struct objc_class* class);
+
+const char* objc_skip_type (const char* type);
+
+static void __objc_finish_write_root_object(struct objc_typed_stream*);
+static void __objc_finish_read_root_object(struct objc_typed_stream*);
+
+static __inline__ int
+__objc_code_unsigned_char (unsigned char* buf, unsigned char val)
+{
+  if ((val&_B_VALUE) == val)
+    {
+      buf[0] = val|_B_SINT;
+      return 1;
+    }
+  else
+    {
+      buf[0] = _B_NINT|0x01;
+      buf[1] = val;
+      return 2;
+    }
+}
+
+int
+objc_write_unsigned_char (struct objc_typed_stream* stream,
+                         unsigned char value)
+{
+  unsigned char buf[sizeof (unsigned char)+1];
+  int len = __objc_code_unsigned_char (buf, value);
+  return (*stream->write)(stream->physical, buf, len);
+}
+
+static __inline__ int
+__objc_code_char (unsigned char* buf, char val)
+{
+  if (val >= 0)
+    return __objc_code_unsigned_char (buf, val);
+  else
+    {
+      buf[0] = _B_NINT|_B_SIGN|0x01;
+      buf[1] = -val;
+      return 2;
+    }
+}
+
+int
+objc_write_char (struct objc_typed_stream* stream, char value)
+{
+  unsigned char buf[sizeof (char)+1];
+  int len = __objc_code_char (buf, value);
+  return (*stream->write)(stream->physical, buf, len);
+}
+
+static __inline__ int
+__objc_code_unsigned_short (unsigned char* buf, unsigned short val)
+{
+  if ((val&_B_VALUE) == val)
+    {
+      buf[0] = val|_B_SINT;
+      return 1;
+    }
+  else 
+    {
+      int c, b;
+
+      buf[0] = _B_NINT;
+
+      for (c= sizeof(short); c != 0; c -= 1)
+       if (((val>>(8*(c-1)))%0x100) != 0)
+         break;
+
+      buf[0] |= c;
+
+      for (b = 1; c != 0; c--, b++)
+       {
+         buf[b] = (val >> (8*(c-1)))%0x100;
+       }
+
+      return b;
+    }
+}
+
+int
+objc_write_unsigned_short (struct objc_typed_stream* stream, 
+                          unsigned short value)
+{
+  unsigned char buf[sizeof (unsigned short)+1];
+  int len = __objc_code_unsigned_short (buf, value);
+  return (*stream->write)(stream->physical, buf, len);
+}
+      
+static __inline__ int
+__objc_code_short (unsigned char* buf, short val)
+{
+  int sign = (val < 0);
+  int size = __objc_code_unsigned_short (buf, sign ? -val : val);
+  if (sign)
+    buf[0] |= _B_SIGN;
+  return size;
+}
+
+int
+objc_write_short (struct objc_typed_stream* stream, short value)
+{
+  unsigned char buf[sizeof (short)+1];
+  int len = __objc_code_short (buf, value);
+  return (*stream->write)(stream->physical, buf, len);
+}
+      
+
+static __inline__ int
+__objc_code_unsigned_int (unsigned char* buf, unsigned int val)
+{
+  if ((val&_B_VALUE) == val)
+    {
+      buf[0] = val|_B_SINT;
+      return 1;
+    }
+  else 
+    {
+      int c, b;
+
+      buf[0] = _B_NINT;
+
+      for (c= sizeof(int); c != 0; c -= 1)
+       if (((val>>(8*(c-1)))%0x100) != 0)
+         break;
+
+      buf[0] |= c;
+
+      for (b = 1; c != 0; c--, b++)
+       {
+         buf[b] = (val >> (8*(c-1)))%0x100;
+       }
+
+      return b;
+    }
+}
+
+int
+objc_write_unsigned_int (struct objc_typed_stream* stream, unsigned int value)
+{
+  unsigned char buf[sizeof(unsigned int)+1];
+  int len = __objc_code_unsigned_int (buf, value);
+  return (*stream->write)(stream->physical, buf, len);
+}
+
+static __inline__ int
+__objc_code_int (unsigned char* buf, int val)
+{
+  int sign = (val < 0);
+  int size = __objc_code_unsigned_int (buf, sign ? -val : val);
+  if (sign)
+    buf[0] |= _B_SIGN;
+  return size;
+}
+
+int
+objc_write_int (struct objc_typed_stream* stream, int value)
+{
+  unsigned char buf[sizeof(int)+1];
+  int len = __objc_code_int (buf, value);
+  return (*stream->write)(stream->physical, buf, len);
+}
+
+static __inline__ int
+__objc_code_unsigned_long (unsigned char* buf, unsigned long val)
+{
+  if ((val&_B_VALUE) == val)
+    {
+      buf[0] = val|_B_SINT;
+      return 1;
+    }
+  else 
+    {
+      int c, b;
+
+      buf[0] = _B_NINT;
+
+      for (c= sizeof(long); c != 0; c -= 1)
+       if (((val>>(8*(c-1)))%0x100) != 0)
+         break;
+
+      buf[0] |= c;
+
+      for (b = 1; c != 0; c--, b++)
+       {
+         buf[b] = (val >> (8*(c-1)))%0x100;
+       }
+
+      return b;
+    }
+}
+
+int
+objc_write_unsigned_long (struct objc_typed_stream* stream, 
+                         unsigned long value)
+{
+  unsigned char buf[sizeof(unsigned long)+1];
+  int len = __objc_code_unsigned_long (buf, value);
+  return (*stream->write)(stream->physical, buf, len);
+}
+
+static __inline__ int
+__objc_code_long (unsigned char* buf, long val)
+{
+  int sign = (val < 0);
+  int size = __objc_code_unsigned_long (buf, sign ? -val : val);
+  if (sign)
+    buf[0] |= _B_SIGN;
+  return size;
+}
+
+int
+objc_write_long (struct objc_typed_stream* stream, long value)
+{
+  unsigned char buf[sizeof(long)+1];
+  int len = __objc_code_long (buf, value);
+  return (*stream->write)(stream->physical, buf, len);
+}
+
+
+int
+objc_write_string (struct objc_typed_stream* stream,
+                  const unsigned char* string, unsigned int nbytes)
+{
+  unsigned char buf[sizeof(unsigned int)+1];
+  int len = __objc_code_unsigned_int (buf, nbytes);
+  
+  if ((buf[0]&_B_CODE) == _B_SINT)
+    buf[0] = (buf[0]&_B_VALUE)|_B_SSTR;
+
+  else /* _B_NINT */
+    buf[0] = (buf[0]&_B_VALUE)|_B_NSTR;
+
+  if ((*stream->write)(stream->physical, buf, len) != 0)
+    return (*stream->write)(stream->physical, string, nbytes);
+  else
+    return 0;
+}
+
+int
+objc_write_string_atomic (struct objc_typed_stream* stream,
+                         unsigned char* string, unsigned int nbytes)
+{
+  unsigned long key;
+  if ((key = PTR2LONG(hash_value_for_key (stream->stream_table, string))))
+    return objc_write_use_common (stream, key);
+  else
+    {
+      int length;
+      hash_add (&stream->stream_table, LONG2PTR(key=PTR2LONG(string)), string);
+      if ((length = objc_write_register_common (stream, key)))
+       return objc_write_string (stream, string, nbytes);
+      return length;
+    }
+}
+
+static int
+objc_write_register_common (struct objc_typed_stream* stream, 
+                           unsigned long key)
+{
+  unsigned char buf[sizeof (unsigned long)+2];
+  int len = __objc_code_unsigned_long (buf+1, key);
+  if (len == 1)
+    {
+      buf[0] = _B_RCOMM|0x01;
+      buf[1] &= _B_VALUE;
+      return (*stream->write)(stream->physical, buf, len+1);
+    }
+  else
+    {
+      buf[1] = (buf[1]&_B_VALUE)|_B_RCOMM;
+      return (*stream->write)(stream->physical, buf+1, len);
+    }
+}
+
+static int
+objc_write_use_common (struct objc_typed_stream* stream, unsigned long key)
+{
+  unsigned char buf[sizeof (unsigned long)+2];
+  int len = __objc_code_unsigned_long (buf+1, key);
+  if (len == 1)
+    {
+      buf[0] = _B_UCOMM|0x01;
+      buf[1] &= _B_VALUE;
+      return (*stream->write)(stream->physical, buf, 2);
+    }
+  else
+    {
+      buf[1] = (buf[1]&_B_VALUE)|_B_UCOMM;
+      return (*stream->write)(stream->physical, buf+1, len);
+    }
+}
+
+static __inline__ int
+__objc_write_extension (struct objc_typed_stream* stream, unsigned char code)
+{
+  if (code <= _B_VALUE)
+    {
+      unsigned char buf = code|_B_EXT;
+      return (*stream->write)(stream->physical, &buf, 1);
+    }
+  else 
+    {
+      objc_error(nil, OBJC_ERR_BAD_OPCODE,
+                "__objc_write_extension: bad opcode %c\n", code);
+      return -1;
+    }
+}
+
+__inline__ int
+__objc_write_object (struct objc_typed_stream* stream, id object)
+{
+  unsigned char buf = '\0';
+  SEL write_sel = sel_get_any_uid ("write:");
+  if (object)
+    {
+      __objc_write_extension (stream, _BX_OBJECT);
+      objc_write_class (stream, object->class_pointer);
+      (*objc_msg_lookup(object, write_sel))(object, write_sel, stream);
+      return (*stream->write)(stream->physical, &buf, 1);
+    }
+  else
+    return objc_write_use_common(stream, 0);
+}
+
+int 
+objc_write_object_reference (struct objc_typed_stream* stream, id object)
+{
+  unsigned long key;
+  if ((key = PTR2LONG(hash_value_for_key (stream->object_table, object))))
+    return objc_write_use_common (stream, key);
+
+  __objc_write_extension (stream, _BX_OBJREF);
+  return objc_write_unsigned_long (stream, PTR2LONG (object));
+}
+
+int 
+objc_write_root_object (struct objc_typed_stream* stream, id object)
+{
+  int len = 0;
+  if (stream->writing_root_p)
+    objc_error (nil, OBJC_ERR_RECURSE_ROOT, 
+               "objc_write_root_object called recursively");
+  else
+    {
+      stream->writing_root_p = 1;
+      __objc_write_extension (stream, _BX_OBJROOT);
+      if((len = objc_write_object (stream, object)))
+       __objc_finish_write_root_object(stream);
+      stream->writing_root_p = 0;
+    }
+  return len;
+}
+
+int 
+objc_write_object (struct objc_typed_stream* stream, id object)
+{
+  unsigned long key;
+  if ((key = PTR2LONG(hash_value_for_key (stream->object_table, object))))
+    return objc_write_use_common (stream, key);
+
+  else if (object == nil)
+    return objc_write_use_common(stream, 0);
+
+  else
+    {
+      int length;
+      hash_add (&stream->object_table, LONG2PTR(key=PTR2LONG(object)), object);
+      if ((length = objc_write_register_common (stream, key)))
+       return __objc_write_object (stream, object);
+      return length;
+    }
+}
+
+__inline__ int
+__objc_write_class (struct objc_typed_stream* stream, struct objc_class* class)
+{
+  __objc_write_extension (stream, _BX_CLASS);
+  objc_write_string_atomic(stream, (char*)class->name,
+                          strlen((char*)class->name));
+  return objc_write_unsigned_long (stream, class->version);
+}
+
+
+static int 
+objc_write_class (struct objc_typed_stream* stream,
+                        struct objc_class* class)
+{
+  unsigned long key;
+  if ((key = PTR2LONG(hash_value_for_key (stream->stream_table, class))))
+    return objc_write_use_common (stream, key);
+  else
+    {
+      int length;
+      hash_add (&stream->stream_table, LONG2PTR(key=PTR2LONG(class)), class);
+      if ((length = objc_write_register_common (stream, key)))
+       return __objc_write_class (stream, class);
+      return length;
+    }
+}
+
+
+__inline__ int 
+__objc_write_selector (struct objc_typed_stream* stream, SEL selector)
+{
+  const char* sel_name;
+  __objc_write_extension (stream, _BX_SEL);
+  /* to handle NULL selectors */
+  if ((SEL)0 == selector)
+    return objc_write_string (stream, "", 0);
+  sel_name = sel_get_name (selector);
+  return objc_write_string (stream, sel_name, strlen ((char*)sel_name));
+}
+
+int 
+objc_write_selector (struct objc_typed_stream* stream, SEL selector)
+{
+  const char* sel_name;
+  unsigned long key;
+
+  /* to handle NULL selectors */
+  if ((SEL)0 == selector)
+    return __objc_write_selector (stream, selector);
+
+  sel_name = sel_get_name (selector);
+  if ((key = PTR2LONG(hash_value_for_key (stream->stream_table, sel_name))))
+    return objc_write_use_common (stream, key);
+  else
+    {
+      int length;
+      hash_add (&stream->stream_table, 
+               LONG2PTR(key=PTR2LONG(sel_name)), (char*)sel_name);
+      if ((length = objc_write_register_common (stream, key)))
+       return __objc_write_selector (stream, selector);
+      return length;
+    }
+}
+
+
+
+/*
+** Read operations 
+*/
+
+__inline__ int
+objc_read_char (struct objc_typed_stream* stream, char* val)
+{
+  unsigned char buf;
+  int len;
+  len = (*stream->read)(stream->physical, &buf, 1);
+  if (len != 0)
+    {
+      if ((buf & _B_CODE) == _B_SINT)
+       (*val) = (buf & _B_VALUE);
+
+      else if ((buf & _B_NUMBER) == 1)
+       {
+         len = (*stream->read)(stream->physical, val, 1);
+         if (buf&_B_SIGN)
+           (*val) = -1*(*val);
+       }
+
+      else
+       objc_error(nil, OBJC_ERR_BAD_DATA,
+                  "expected 8bit signed int, got %dbit int",
+                  (int)(buf&_B_NUMBER)*8);
+    }
+  return len;
+}
+
+
+__inline__ int
+objc_read_unsigned_char (struct objc_typed_stream* stream, unsigned char* val)
+{
+  unsigned char buf;
+  int len;
+  if ((len = (*stream->read)(stream->physical, &buf, 1)))
+    {
+      if ((buf & _B_CODE) == _B_SINT)
+       (*val) = (buf & _B_VALUE);
+
+      else if ((buf & _B_NUMBER) == 1)
+       len = (*stream->read)(stream->physical, val, 1);
+
+      else
+       objc_error(nil, OBJC_ERR_BAD_DATA,
+                  "expected 8bit unsigned int, got %dbit int",
+                  (int)(buf&_B_NUMBER)*8);
+    }
+  return len;
+}
+
+__inline__ int
+objc_read_short (struct objc_typed_stream* stream, short* value)
+{
+  unsigned char buf[sizeof(short)+1];
+  int len;
+  if ((len = (*stream->read)(stream->physical, buf, 1)))
+    {
+      if ((buf[0] & _B_CODE) == _B_SINT)
+       (*value) = (buf[0] & _B_VALUE);
+
+      else
+       {
+         int pos = 1;
+         int nbytes = buf[0] & _B_NUMBER;
+         if (nbytes > sizeof (short))
+           objc_error(nil, OBJC_ERR_BAD_DATA,
+                      "expected short, got bigger (%dbits)", nbytes*8);
+         len = (*stream->read)(stream->physical, buf+1, nbytes);
+         (*value) = 0;
+         while (pos <= nbytes)
+           (*value) = ((*value)*0x100) + buf[pos++];
+         if (buf[0] & _B_SIGN)
+           (*value) = -(*value);
+       }
+    }
+  return len;
+}
+
+__inline__ int
+objc_read_unsigned_short (struct objc_typed_stream* stream,
+                         unsigned short* value)
+{
+  unsigned char buf[sizeof(unsigned short)+1];
+  int len;
+  if ((len = (*stream->read)(stream->physical, buf, 1)))
+    {
+      if ((buf[0] & _B_CODE) == _B_SINT)
+       (*value) = (buf[0] & _B_VALUE);
+
+      else
+       {
+         int pos = 1;
+         int nbytes = buf[0] & _B_NUMBER;
+         if (nbytes > sizeof (short))
+           objc_error(nil, OBJC_ERR_BAD_DATA,
+                      "expected short, got int or bigger");
+         len = (*stream->read)(stream->physical, buf+1, nbytes);
+         (*value) = 0;
+         while (pos <= nbytes)
+           (*value) = ((*value)*0x100) + buf[pos++];
+       }
+    }
+  return len;
+}
+
+
+__inline__ int
+objc_read_int (struct objc_typed_stream* stream, int* value)
+{
+  unsigned char buf[sizeof(int)+1];
+  int len;
+  if ((len = (*stream->read)(stream->physical, buf, 1)))
+    {
+      if ((buf[0] & _B_CODE) == _B_SINT)
+       (*value) = (buf[0] & _B_VALUE);
+
+      else
+       {
+         int pos = 1;
+         int nbytes = buf[0] & _B_NUMBER;
+         if (nbytes > sizeof (int))
+           objc_error(nil, OBJC_ERR_BAD_DATA, "expected int, got bigger");
+         len = (*stream->read)(stream->physical, buf+1, nbytes);
+         (*value) = 0;
+         while (pos <= nbytes)
+           (*value) = ((*value)*0x100) + buf[pos++];
+         if (buf[0] & _B_SIGN)
+           (*value) = -(*value);
+       }
+    }
+  return len;
+}
+
+__inline__ int
+objc_read_long (struct objc_typed_stream* stream, long* value)
+{
+  unsigned char buf[sizeof(long)+1];
+  int len;
+  if ((len = (*stream->read)(stream->physical, buf, 1)))
+    {
+      if ((buf[0] & _B_CODE) == _B_SINT)
+       (*value) = (buf[0] & _B_VALUE);
+
+      else
+       {
+         int pos = 1;
+         int nbytes = buf[0] & _B_NUMBER;
+         if (nbytes > sizeof (long))
+           objc_error(nil, OBJC_ERR_BAD_DATA, "expected long, got bigger");
+         len = (*stream->read)(stream->physical, buf+1, nbytes);
+         (*value) = 0;
+         while (pos <= nbytes)
+           (*value) = ((*value)*0x100) + buf[pos++];
+         if (buf[0] & _B_SIGN)
+           (*value) = -(*value);
+       }
+    }
+  return len;
+}
+
+__inline__ int
+__objc_read_nbyte_uint (struct objc_typed_stream* stream,
+                      unsigned int nbytes, unsigned int* val)
+{
+  int len, pos = 0;
+  unsigned char buf[sizeof(unsigned int)+1];
+
+  if (nbytes > sizeof (int))
+    objc_error(nil, OBJC_ERR_BAD_DATA, "expected int, got bigger");
+
+  len = (*stream->read)(stream->physical, buf, nbytes);
+  (*val) = 0;
+  while (pos < nbytes)
+    (*val) = ((*val)*0x100) + buf[pos++];
+  return len;
+}
+  
+
+__inline__ int
+objc_read_unsigned_int (struct objc_typed_stream* stream,
+                       unsigned int* value)
+{
+  unsigned char buf[sizeof(unsigned int)+1];
+  int len;
+  if ((len = (*stream->read)(stream->physical, buf, 1)))
+    {
+      if ((buf[0] & _B_CODE) == _B_SINT)
+       (*value) = (buf[0] & _B_VALUE);
+
+      else
+       len = __objc_read_nbyte_uint (stream, (buf[0] & _B_VALUE), value);
+
+    }
+  return len;
+}
+
+int
+__objc_read_nbyte_ulong (struct objc_typed_stream* stream,
+                      unsigned int nbytes, unsigned long* val)
+{
+  int len, pos = 0;
+  unsigned char buf[sizeof(unsigned long)+1];
+
+  if (nbytes > sizeof (long))
+    objc_error(nil, OBJC_ERR_BAD_DATA, "expected long, got bigger");
+
+  len = (*stream->read)(stream->physical, buf, nbytes);
+  (*val) = 0;
+  while (pos < nbytes)
+    (*val) = ((*val)*0x100) + buf[pos++];
+  return len;
+}
+  
+
+__inline__ int
+objc_read_unsigned_long (struct objc_typed_stream* stream,
+                       unsigned long* value)
+{
+  unsigned char buf[sizeof(unsigned long)+1];
+  int len;
+  if ((len = (*stream->read)(stream->physical, buf, 1)))
+    {
+      if ((buf[0] & _B_CODE) == _B_SINT)
+       (*value) = (buf[0] & _B_VALUE);
+
+      else
+       len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), value);
+
+    }
+  return len;
+}
+
+__inline__ int
+objc_read_string (struct objc_typed_stream* stream,
+                 char** string)
+{
+  unsigned char buf[sizeof(unsigned int)+1];
+  int len;
+  if ((len = (*stream->read)(stream->physical, buf, 1)))
+    {
+      unsigned long key = 0;
+
+      if ((buf[0]&_B_CODE) == _B_RCOMM)        /* register following */
+       {
+         len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
+         len = (*stream->read)(stream->physical, buf, 1);
+       }
+
+      switch (buf[0]&_B_CODE) {
+      case _B_SSTR:
+       {
+         int length = buf[0]&_B_VALUE;
+         (*string) = (char*)objc_malloc(length+1);
+         if (key)
+           hash_add (&stream->stream_table, LONG2PTR(key), *string);
+         len = (*stream->read)(stream->physical, *string, length);
+         (*string)[length] = '\0';
+       }
+       break;
+
+      case _B_UCOMM:
+       {
+         char *tmp;
+         len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
+         tmp = hash_value_for_key (stream->stream_table, LONG2PTR (key));
+         *string = objc_malloc (strlen(tmp) + 1);
+         strcpy (*string, tmp);
+       }
+       break;
+
+      case _B_NSTR:
+       {
+         unsigned int nbytes = buf[0]&_B_VALUE;
+         len = __objc_read_nbyte_uint(stream, nbytes, &nbytes);
+         if (len) {
+           (*string) = (char*)objc_malloc(nbytes+1);
+           if (key)
+             hash_add (&stream->stream_table, LONG2PTR(key), *string);
+           len = (*stream->read)(stream->physical, *string, nbytes);
+           (*string)[nbytes] = '\0';
+         }
+       }
+       break;
+       
+      default:
+       objc_error(nil, OBJC_ERR_BAD_DATA,
+                  "expected string, got opcode %c\n", (buf[0]&_B_CODE));
+      }
+    }
+
+  return len;
+}
+
+
+int
+objc_read_object (struct objc_typed_stream* stream, id* object)
+{
+  unsigned char buf[sizeof (unsigned int)];
+  int len;
+  if ((len = (*stream->read)(stream->physical, buf, 1)))
+    {
+      SEL read_sel = sel_get_any_uid ("read:");
+      unsigned long key = 0;
+
+      if ((buf[0]&_B_CODE) == _B_RCOMM)        /* register common */
+       {
+         len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
+         len = (*stream->read)(stream->physical, buf, 1);
+       }
+
+      if (buf[0] == (_B_EXT | _BX_OBJECT))
+       {
+         Class class;
+
+         /* get class */
+         len = objc_read_class (stream, &class);
+
+         /* create instance */
+         (*object) = class_create_instance(class);
+
+         /* register? */
+         if (key)
+           hash_add (&stream->object_table, LONG2PTR(key), *object);
+
+         /* send -read: */
+         if (__objc_responds_to (*object, read_sel))
+           (*get_imp(class, read_sel))(*object, read_sel, stream);
+
+         /* check null-byte */
+         len = (*stream->read)(stream->physical, buf, 1);
+         if (buf[0] != '\0')
+           objc_error(nil, OBJC_ERR_BAD_DATA,
+                      "expected null-byte, got opcode %c", buf[0]);
+       }
+
+      else if ((buf[0]&_B_CODE) == _B_UCOMM)
+       {
+         if (key)
+           objc_error(nil, OBJC_ERR_BAD_KEY, "cannot register use upcode...");
+         len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
+         (*object) = hash_value_for_key (stream->object_table, LONG2PTR(key));
+       }
+
+      else if (buf[0] == (_B_EXT | _BX_OBJREF))        /* a forward reference */
+       {
+         struct objc_list* other;
+         len = objc_read_unsigned_long (stream, &key);
+         other = (struct objc_list*)hash_value_for_key (stream->object_refs, 
+                                                        LONG2PTR(key));
+         hash_add (&stream->object_refs, LONG2PTR(key), 
+                   (void*)list_cons(object, other));
+       }
+
+      else if (buf[0] == (_B_EXT | _BX_OBJROOT)) /* a root object */
+       {
+         if (key)
+           objc_error(nil, OBJC_ERR_BAD_KEY,
+                      "cannot register root object...");
+         len = objc_read_object (stream, object);
+         __objc_finish_read_root_object (stream);
+       }
+
+      else
+       objc_error(nil, OBJC_ERR_BAD_DATA,
+                  "expected object, got opcode %c", buf[0]);
+    }
+  return len;
+}
+
+static int
+objc_read_class (struct objc_typed_stream* stream, Class* class)
+{
+  unsigned char buf[sizeof (unsigned int)];
+  int len;
+  if ((len = (*stream->read)(stream->physical, buf, 1)))
+    {
+      unsigned long key = 0;
+
+      if ((buf[0]&_B_CODE) == _B_RCOMM)        /* register following */
+       {
+         len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
+         len = (*stream->read)(stream->physical, buf, 1);
+       }
+
+      if (buf[0] == (_B_EXT | _BX_CLASS))
+       {
+         char* class_name;
+         unsigned long version;
+
+         /* get class */
+         len = objc_read_string (stream, &class_name);
+         (*class) = objc_get_class(class_name);
+         objc_free(class_name);
+
+         /* register */
+         if (key)
+           hash_add (&stream->stream_table, LONG2PTR(key), *class);
+
+         objc_read_unsigned_long(stream, &version);
+         hash_add (&stream->class_table, (*class)->name, (void*)version);
+       }
+
+      else if ((buf[0]&_B_CODE) == _B_UCOMM)
+       {
+         if (key)
+           objc_error(nil, OBJC_ERR_BAD_KEY, "cannot register use upcode...");
+         len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
+         (*class) = hash_value_for_key (stream->stream_table, LONG2PTR(key));
+         if (!*class)
+           objc_error(nil, OBJC_ERR_BAD_CLASS,
+                      "cannot find class for key %lu", key);
+       }
+
+      else
+       objc_error(nil, OBJC_ERR_BAD_DATA,
+                  "expected class, got opcode %c", buf[0]);
+    }
+  return len;
+}
+
+int
+objc_read_selector (struct objc_typed_stream* stream, SEL* selector)
+{
+  unsigned char buf[sizeof (unsigned int)];
+  int len;
+  if ((len = (*stream->read)(stream->physical, buf, 1)))
+    {
+      unsigned long key = 0;
+
+      if ((buf[0]&_B_CODE) == _B_RCOMM)        /* register following */
+       {
+         len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
+         len = (*stream->read)(stream->physical, buf, 1);
+       }
+
+      if (buf[0] == (_B_EXT|_BX_SEL)) /* selector! */
+       {
+         char* selector_name;
+
+         /* get selector */
+         len = objc_read_string (stream, &selector_name);
+         /* To handle NULL selectors */
+         if (0 == strlen(selector_name))
+           {
+             (*selector) = (SEL)0;
+             return 0;
+           }
+         else 
+           (*selector) = sel_get_any_uid(selector_name);
+         objc_free(selector_name);
+
+         /* register */
+         if (key)
+           hash_add (&stream->stream_table, LONG2PTR(key), (void*)*selector);
+       }
+
+      else if ((buf[0]&_B_CODE) == _B_UCOMM)
+       {
+         if (key)
+           objc_error(nil, OBJC_ERR_BAD_KEY, "cannot register use upcode...");
+         len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key);
+         (*selector) = hash_value_for_key (stream->stream_table, 
+                                           LONG2PTR(key));
+       }
+
+      else
+       objc_error(nil, OBJC_ERR_BAD_DATA,
+                  "expected selector, got opcode %c", buf[0]);
+    }
+  return len;
+}
+
+/*
+** USER LEVEL FUNCTIONS
+*/
+
+/*
+** Write one object, encoded in TYPE and pointed to by DATA to the
+** typed stream STREAM.  
+*/
+
+int
+objc_write_type(TypedStream* stream, const char* type, const void* data)
+{
+  switch(*type) {
+  case _C_ID:
+    return objc_write_object (stream, *(id*)data);
+    break;
+
+  case _C_CLASS:
+    return objc_write_class (stream, *(Class*)data);
+    break;
+
+  case _C_SEL:
+    return objc_write_selector (stream, *(SEL*)data);
+    break;
+
+  case _C_CHR:
+    return objc_write_char(stream, *(char*)data);
+    break;
+    
+  case _C_UCHR:
+    return objc_write_unsigned_char(stream, *(unsigned char*)data);
+    break;
+
+  case _C_SHT:
+    return objc_write_short(stream, *(short*)data);
+    break;
+
+  case _C_USHT:
+    return objc_write_unsigned_short(stream, *(unsigned short*)data);
+    break;
+
+  case _C_INT:
+    return objc_write_int(stream, *(int*)data);
+    break;
+
+  case _C_UINT:
+    return objc_write_unsigned_int(stream, *(unsigned int*)data);
+    break;
+
+  case _C_LNG:
+    return objc_write_long(stream, *(long*)data);
+    break;
+
+  case _C_ULNG:
+    return objc_write_unsigned_long(stream, *(unsigned long*)data);
+    break;
+
+  case _C_CHARPTR:
+    return objc_write_string (stream, *(char**)data, strlen(*(char**)data));
+    break;
+
+  case _C_ATOM:
+    return objc_write_string_atomic (stream, *(char**)data, 
+                                    strlen(*(char**)data));
+    break;
+
+  case _C_ARY_B:
+    {
+      int len = atoi(type+1);
+      while (isdigit(*++type))
+       ;
+      return objc_write_array (stream, type, len, data);
+    }
+    break; 
+
+  case _C_STRUCT_B:
+    {
+      int acc_size = 0;
+      int align;
+      while (*type != _C_STRUCT_E && *type++ != '=')
+       ; /* skip "<name>=" */
+      while (*type != _C_STRUCT_E)
+       {
+         align = objc_alignof_type (type);       /* padd to alignment */
+         acc_size += ROUND (acc_size, align);
+         objc_write_type (stream, type, ((char*)data)+acc_size);
+         acc_size += objc_sizeof_type (type);   /* add component size */
+         type = objc_skip_typespec (type);      /* skip component */
+       }
+      return 1;
+    }
+
+  default:
+    {
+      objc_error(nil, OBJC_ERR_BAD_TYPE,
+                "objc_write_type: cannot parse typespec: %s\n", type);
+      return 0;
+    }
+  }
+}
+
+/*
+** Read one object, encoded in TYPE and pointed to by DATA to the
+** typed stream STREAM.  DATA specifies the address of the types to
+** read.  Expected type is checked against the type actually present
+** on the stream. 
+*/
+
+int
+objc_read_type(TypedStream* stream, const char* type, void* data)
+{
+  char c;
+  switch(c = *type) {
+  case _C_ID:
+    return objc_read_object (stream, (id*)data);
+    break;
+
+  case _C_CLASS:
+    return objc_read_class (stream, (Class*)data);
+    break;
+
+  case _C_SEL:
+    return objc_read_selector (stream, (SEL*)data);
+    break;
+
+  case _C_CHR:
+    return objc_read_char (stream, (char*)data);
+    break;
+    
+  case _C_UCHR:
+    return objc_read_unsigned_char (stream, (unsigned char*)data);
+    break;
+
+  case _C_SHT:
+    return objc_read_short (stream, (short*)data);
+    break;
+
+  case _C_USHT:
+    return objc_read_unsigned_short (stream, (unsigned short*)data);
+    break;
+
+  case _C_INT:
+    return objc_read_int (stream, (int*)data);
+    break;
+
+  case _C_UINT:
+    return objc_read_unsigned_int (stream, (unsigned int*)data);
+    break;
+
+  case _C_LNG:
+    return objc_read_long (stream, (long*)data);
+    break;
+
+  case _C_ULNG:
+    return objc_read_unsigned_long (stream, (unsigned long*)data);
+    break;
+
+  case _C_CHARPTR:
+  case _C_ATOM:
+    return objc_read_string (stream, (char**)data);
+    break;
+
+  case _C_ARY_B:
+    {
+      int len = atoi(type+1);
+      while (isdigit(*++type))
+       ;
+      return objc_read_array (stream, type, len, data);
+    }
+    break; 
+
+  case _C_STRUCT_B:
+    {
+      int acc_size = 0;
+      int align;
+      while (*type != _C_STRUCT_E && *type++ != '=')
+       ; /* skip "<name>=" */
+      while (*type != _C_STRUCT_E)
+       {
+         align = objc_alignof_type (type);       /* padd to alignment */
+         acc_size += ROUND (acc_size, align);
+         objc_read_type (stream, type, ((char*)data)+acc_size);
+         acc_size += objc_sizeof_type (type);   /* add component size */
+         type = objc_skip_typespec (type);      /* skip component */
+       }
+      return 1;
+    }
+
+  default:
+    {
+      objc_error(nil, OBJC_ERR_BAD_TYPE,
+                "objc_read_type: cannot parse typespec: %s\n", type);
+      return 0;
+    }
+  }
+}
+
+/*
+** Write the object specified by the template TYPE to STREAM.  Last
+** arguments specify addresses of values to be written.  It might 
+** seem surprising to specify values by address, but this is extremely
+** convenient for copy-paste with objc_read_types calls.  A more
+** down-to-the-earth cause for this passing of addresses is that values
+** of arbitrary size is not well supported in ANSI C for functions with
+** variable number of arguments.
+*/
+
+int 
+objc_write_types (TypedStream* stream, const char* type, ...)
+{
+  va_list args;
+  const char *c;
+  int res = 0;
+
+  va_start(args, type);
+
+  for (c = type; *c; c = objc_skip_typespec (c))
+    {
+      switch(*c) {
+      case _C_ID:
+       res = objc_write_object (stream, *va_arg (args, id*));
+       break;
+
+      case _C_CLASS:
+       res = objc_write_class (stream, *va_arg(args, Class*));
+       break;
+
+      case _C_SEL:
+       res = objc_write_selector (stream, *va_arg(args, SEL*));
+       break;
+       
+      case _C_CHR:
+       res = objc_write_char (stream, *va_arg (args, char*));
+       break;
+       
+      case _C_UCHR:
+       res = objc_write_unsigned_char (stream,
+                                       *va_arg (args, unsigned char*));
+       break;
+       
+      case _C_SHT:
+       res = objc_write_short (stream, *va_arg(args, short*));
+       break;
+
+      case _C_USHT:
+       res = objc_write_unsigned_short (stream,
+                                        *va_arg(args, unsigned short*));
+       break;
+
+      case _C_INT:
+       res = objc_write_int(stream, *va_arg(args, int*));
+       break;
+       
+      case _C_UINT:
+       res = objc_write_unsigned_int(stream, *va_arg(args, unsigned int*));
+       break;
+
+      case _C_LNG:
+       res = objc_write_long(stream, *va_arg(args, long*));
+       break;
+       
+      case _C_ULNG:
+       res = objc_write_unsigned_long(stream, *va_arg(args, unsigned long*));
+       break;
+
+      case _C_CHARPTR:
+       {
+         char** str = va_arg(args, char**);
+         res = objc_write_string (stream, *str, strlen(*str));
+       }
+       break;
+
+      case _C_ATOM:
+       {
+         char** str = va_arg(args, char**);
+         res = objc_write_string_atomic (stream, *str, strlen(*str));
+       }
+       break;
+
+      case _C_ARY_B:
+       {
+         int len = atoi(c+1);
+         const char* t = c;
+         while (isdigit(*++t))
+           ;
+         res = objc_write_array (stream, t, len, va_arg(args, void*));
+         t = objc_skip_typespec (t);
+         if (*t != _C_ARY_E)
+           objc_error(nil, OBJC_ERR_BAD_TYPE, "expected `]', got: %s", t);
+       }
+       break; 
+       
+      default:
+       objc_error(nil, OBJC_ERR_BAD_TYPE, 
+                  "objc_write_types: cannot parse typespec: %s\n", type);
+      }
+    }
+  va_end(args);
+  return res;
+}
+
+
+/* 
+** Last arguments specify addresses of values to be read.  Expected
+** type is checked against the type actually present on the stream. 
+*/
+
+int 
+objc_read_types(TypedStream* stream, const char* type, ...)
+{
+  va_list args;
+  const char *c;
+  int res = 0;
+
+  va_start(args, type);
+
+  for (c = type; *c; c = objc_skip_typespec(c))
+    {
+      switch(*c) {
+      case _C_ID:
+       res = objc_read_object(stream, va_arg(args, id*));
+       break;
+
+      case _C_CLASS:
+       res = objc_read_class(stream, va_arg(args, Class*));
+       break;
+
+      case _C_SEL:
+       res = objc_read_selector(stream, va_arg(args, SEL*));
+       break;
+       
+      case _C_CHR:
+       res = objc_read_char(stream, va_arg(args, char*));
+       break;
+       
+      case _C_UCHR:
+       res = objc_read_unsigned_char(stream, va_arg(args, unsigned char*));
+       break;
+       
+      case _C_SHT:
+       res = objc_read_short(stream, va_arg(args, short*));
+       break;
+
+      case _C_USHT:
+       res = objc_read_unsigned_short(stream, va_arg(args, unsigned short*));
+       break;
+
+      case _C_INT:
+       res = objc_read_int(stream, va_arg(args, int*));
+       break;
+       
+      case _C_UINT:
+       res = objc_read_unsigned_int(stream, va_arg(args, unsigned int*));
+       break;
+
+      case _C_LNG:
+       res = objc_read_long(stream, va_arg(args, long*));
+       break;
+       
+      case _C_ULNG:
+       res = objc_read_unsigned_long(stream, va_arg(args, unsigned long*));
+       break;
+
+      case _C_CHARPTR:
+      case _C_ATOM:
+       {
+         char** str = va_arg(args, char**);
+         res = objc_read_string (stream, str);
+       }
+       break;
+
+      case _C_ARY_B:
+       {
+         int len = atoi(c+1);
+         const char* t = c;
+         while (isdigit(*++t))
+           ;
+         res = objc_read_array (stream, t, len, va_arg(args, void*));
+         t = objc_skip_typespec (t);
+         if (*t != _C_ARY_E)
+           objc_error(nil, OBJC_ERR_BAD_TYPE, "expected `]', got: %s", t);
+       }
+       break; 
+       
+      default:
+       objc_error(nil, OBJC_ERR_BAD_TYPE, 
+                  "objc_read_types: cannot parse typespec: %s\n", type);
+      }
+    }
+  va_end(args);
+  return res;
+}
+
+/*
+** Write an array of COUNT elements of TYPE from the memory address DATA.
+** This is equivalent of objc_write_type (stream, "[N<type>]", data)
+*/
+
+int
+objc_write_array (TypedStream* stream, const char* type,
+                 int count, const void* data)
+{
+  int off = objc_sizeof_type(type);
+  const char* where = data;
+
+  while (count-- > 0)
+    {
+      objc_write_type(stream, type, where);
+      where += off;
+    }
+  return 1;
+}
+
+/*
+** Read an array of COUNT elements of TYPE into the memory address
+** DATA.  The memory pointed to by data is supposed to be allocated
+** by the callee.  This is equivalent of 
+**   objc_read_type (stream, "[N<type>]", data)
+*/
+
+int
+objc_read_array (TypedStream* stream, const char* type,
+                int count, void* data)
+{
+  int off = objc_sizeof_type(type);
+  char* where = (char*)data;
+
+  while (count-- > 0)
+    {
+      objc_read_type(stream, type, where);
+      where += off;
+    }
+  return 1;
+}
+
+static int 
+__objc_fread(FILE* file, char* data, int len)
+{
+  return fread(data, len, 1, file);
+}
+
+static int 
+__objc_fwrite(FILE* file, char* data, int len)
+{
+  return fwrite(data, len, 1, file);
+}
+
+static int
+__objc_feof(FILE* file)
+{
+  return feof(file);
+}
+
+static int 
+__objc_no_write(FILE* file, char* data, int len)
+{
+  objc_error (nil, OBJC_ERR_NO_WRITE, "TypedStream not open for writing");
+  return 0;
+}
+
+static int 
+__objc_no_read(FILE* file, char* data, int len)
+{
+  objc_error (nil, OBJC_ERR_NO_READ, "TypedStream not open for reading");
+  return 0;
+}
+
+static int
+__objc_read_typed_stream_signature (TypedStream* stream)
+{
+  char buffer[80];
+  int pos = 0;
+  do
+    (*stream->read)(stream->physical, buffer+pos, 1);
+  while (buffer[pos++] != '\0')
+    ;
+  sscanf (buffer, "GNU TypedStream %d", &stream->version);
+  if (stream->version != OBJC_TYPED_STREAM_VERSION)
+    objc_error (nil, OBJC_ERR_STREAM_VERSION,
+               "cannot handle TypedStream version %d", stream->version);
+  return 1;
+}
+
+static int
+__objc_write_typed_stream_signature (TypedStream* stream)
+{
+  char buffer[80];
+  sprintf(buffer, "GNU TypedStream %d", OBJC_TYPED_STREAM_VERSION);
+  stream->version = OBJC_TYPED_STREAM_VERSION;
+  (*stream->write)(stream->physical, buffer, strlen(buffer)+1);
+  return 1;
+}
+
+static void __objc_finish_write_root_object(struct objc_typed_stream* stream)
+{
+  hash_delete (stream->object_table);
+  stream->object_table = hash_new(64,
+                                 (hash_func_type)hash_ptr,
+                                 (compare_func_type)compare_ptrs);
+}
+
+static void __objc_finish_read_root_object(struct objc_typed_stream* stream)
+{
+  node_ptr node;
+  SEL awake_sel = sel_get_any_uid ("awake");
+  cache_ptr free_list = hash_new (64,
+                                 (hash_func_type) hash_ptr,
+                                 (compare_func_type) compare_ptrs);
+
+  /* resolve object forward references */
+  for (node = hash_next (stream->object_refs, NULL); node;
+       node = hash_next (stream->object_refs, node))
+    {
+      struct objc_list* reflist = node->value;
+      const void* key = node->key;
+      id object = hash_value_for_key (stream->object_table, key);
+      while(reflist)
+       {
+         *((id*)reflist->head) = object;
+         if (hash_value_for_key (free_list,reflist) == NULL)
+           hash_add (&free_list,reflist,reflist);
+
+         reflist = reflist->tail;
+       }
+    }
+    
+  /* apply __objc_free to all objects stored in free_list */
+  for (node = hash_next (free_list, NULL); node;
+       node = hash_next (free_list, node))
+    objc_free ((void *) node->key);
+
+  hash_delete (free_list);
+
+  /* empty object reference table */
+  hash_delete (stream->object_refs);
+  stream->object_refs = hash_new(8, (hash_func_type)hash_ptr,
+                                (compare_func_type)compare_ptrs);
+  
+  /* call -awake for all objects read  */
+  if (awake_sel)
+    {
+      for (node = hash_next (stream->object_table, NULL); node;
+          node = hash_next (stream->object_table, node))
+       {
+         id object = node->value;
+         if (__objc_responds_to (object, awake_sel))
+           (*objc_msg_lookup(object, awake_sel))(object, awake_sel);
+       }
+    }
+
+  /* empty object table */
+  hash_delete (stream->object_table);
+  stream->object_table = hash_new(64,
+                                 (hash_func_type)hash_ptr,
+                                 (compare_func_type)compare_ptrs);
+}
+
+/*
+** Open the stream PHYSICAL in MODE
+*/
+
+TypedStream* 
+objc_open_typed_stream (FILE* physical, int mode)
+{
+  TypedStream* s = (TypedStream*)objc_malloc(sizeof(TypedStream));
+
+  s->mode = mode;
+  s->physical = physical;
+  s->stream_table = hash_new(64,
+                            (hash_func_type)hash_ptr,
+                            (compare_func_type)compare_ptrs);
+  s->object_table = hash_new(64,
+                            (hash_func_type)hash_ptr,
+                            (compare_func_type)compare_ptrs);
+  s->eof = (objc_typed_eof_func)__objc_feof;
+  s->flush = (objc_typed_flush_func)fflush;
+  s->writing_root_p = 0;
+  if (mode == OBJC_READONLY)
+    {
+      s->class_table = hash_new(8, (hash_func_type)hash_string,
+                               (compare_func_type)compare_strings);
+      s->object_refs = hash_new(8, (hash_func_type)hash_ptr,
+                               (compare_func_type)compare_ptrs);
+      s->read = (objc_typed_read_func)__objc_fread;
+      s->write = (objc_typed_write_func)__objc_no_write;
+      __objc_read_typed_stream_signature (s);
+    }
+  else if (mode == OBJC_WRITEONLY)
+    {
+      s->class_table = 0;
+      s->object_refs = 0;
+      s->read = (objc_typed_read_func)__objc_no_read;
+      s->write = (objc_typed_write_func)__objc_fwrite;
+      __objc_write_typed_stream_signature (s);
+    }      
+  else
+    {
+      objc_close_typed_stream (s);
+      return NULL;
+    }
+  s->type = OBJC_FILE_STREAM;
+  return s;
+}
+
+/*
+** Open the file named by FILE_NAME in MODE
+*/
+
+TypedStream*
+objc_open_typed_stream_for_file (const char* file_name, int mode)
+{
+  FILE* file = NULL;
+  TypedStream* s;
+
+  if (mode == OBJC_READONLY)
+    file = fopen (file_name, "r");
+  else
+    file = fopen (file_name, "w");
+
+  if (file)
+    {
+      s = objc_open_typed_stream (file, mode);
+      if (s)
+       s->type |= OBJC_MANAGED_STREAM;
+      return s;
+    }
+  else
+    return NULL;
+}
+
+/*
+** Close STREAM freeing the structure it self.  If it was opened with 
+** objc_open_typed_stream_for_file, the file will also be closed.
+*/
+
+void
+objc_close_typed_stream (TypedStream* stream)
+{
+  if (stream->mode == OBJC_READONLY)
+    {
+      __objc_finish_read_root_object (stream); /* Just in case... */
+      hash_delete (stream->class_table);
+      hash_delete (stream->object_refs);
+    }
+
+  hash_delete (stream->stream_table);
+  hash_delete (stream->object_table);
+
+  if (stream->type == (OBJC_MANAGED_STREAM | OBJC_FILE_STREAM))
+    fclose ((FILE*)stream->physical);
+
+  objc_free(stream);
+}
+
+BOOL
+objc_end_of_typed_stream (TypedStream* stream)
+{
+  return (*stream->eof)(stream->physical);
+}
+
+void
+objc_flush_typed_stream (TypedStream* stream)
+{
+  (*stream->flush)(stream->physical);
+}
+
+long
+objc_get_stream_class_version (TypedStream* stream, Class class)
+{
+  if (stream->class_table)
+    return PTR2LONG(hash_value_for_key (stream->class_table, class->name));
+  else
+    return class_get_version (class);
+}
+
diff --git a/libobjc/class.c b/libobjc/class.c
new file mode 100644 (file)
index 0000000..44aa1b9
--- /dev/null
@@ -0,0 +1,358 @@
+/* GNU Objective C Runtime class related functions
+   Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Kresten Krab Thorup and Dennis Glatting.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include "runtime.h"           /* the kitchen sink */
+#include "sarray.h"
+
+/* The table of classname->class.  Used for objc_lookup_class and friends */
+static cache_ptr __objc_class_hash = 0;                 /* !T:MUTEX */
+
+/* This is a hook which is called by objc_get_class and 
+   objc_lookup_class if the runtime is not able to find the class.
+   This may e.g. try to load in the class using dynamic loading */
+Class (*_objc_lookup_class)(const char* name) = 0;      /* !T:SAFE */
+
+
+/* True when class links has been resolved */     
+BOOL __objc_class_links_resolved = NO;                  /* !T:UNUSED */
+
+
+/* Initial number of buckets size of class hash table. */
+#define CLASS_HASH_SIZE 32
+
+void __objc_init_class_tables()
+{
+  /* Allocate the class hash table */
+
+  if(__objc_class_hash)
+    return;
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  __objc_class_hash
+    =  hash_new (CLASS_HASH_SIZE,
+                (hash_func_type) hash_string,
+                (compare_func_type) compare_strings);
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+}  
+
+/* This function adds a class to the class hash table, and assigns the 
+   class a number, unless it's already known */
+void
+__objc_add_class_to_hash(Class class)
+{
+  Class h_class;
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  /* make sure the table is there */
+  assert(__objc_class_hash);
+
+  /* make sure it's not a meta class */  
+  assert(CLS_ISCLASS(class));
+
+  /* Check to see if the class is already in the hash table.  */
+  h_class = hash_value_for_key (__objc_class_hash, class->name);
+  if (!h_class)
+    {
+      /* The class isn't in the hash table.  Add the class and assign a class
+         number.  */
+      static unsigned int class_number = 1;
+
+      CLS_SETNUMBER(class, class_number);
+      CLS_SETNUMBER(class->class_pointer, class_number);
+
+      ++class_number;
+      hash_add (&__objc_class_hash, class->name, class);
+    }
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+}
+
+/* Get the class object for the class named NAME.  If NAME does not
+   identify a known class, the hook _objc_lookup_class is called.  If
+   this fails, nil is returned */
+Class objc_lookup_class (const char* name)
+{
+  Class class;
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  /* Make sure the class hash table exists.  */
+  assert (__objc_class_hash);
+
+  class = hash_value_for_key (__objc_class_hash, name);
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+
+  if (class)
+    return class;
+
+  if (_objc_lookup_class)
+    return (*_objc_lookup_class)(name);
+  else
+    return 0;
+}
+
+/* Get the class object for the class named NAME.  If NAME does not
+   identify a known class, the hook _objc_lookup_class is called.  If
+   this fails,  an error message is issued and the system aborts */
+Class
+objc_get_class (const char *name)
+{
+  Class class;
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  /* Make sure the class hash table exists.  */
+  assert (__objc_class_hash);
+
+  class = hash_value_for_key (__objc_class_hash, name);
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+
+  if (class)
+    return class;
+
+  if (_objc_lookup_class)
+    class = (*_objc_lookup_class)(name);
+
+  if(class)
+    return class;
+  
+  objc_error(nil, OBJC_ERR_BAD_CLASS, 
+            "objc runtime: cannot find class %s\n", name);
+  return 0;
+}
+
+MetaClass
+objc_get_meta_class(const char *name)
+{
+  return objc_get_class(name)->class_pointer;
+}
+
+/* This function provides a way to enumerate all the classes in the
+   executable.  Pass *ENUM_STATE == NULL to start the enumeration.  The
+   function will return 0 when there are no more classes.  
+   For example: 
+       id class; 
+       void *es = NULL;
+       while ((class = objc_next_class(&es)))
+         ... do something with class; 
+*/
+Class
+objc_next_class(void **enum_state)
+{
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  /* make sure the table is there */
+  assert(__objc_class_hash);
+
+  *(node_ptr*)enum_state = 
+    hash_next(__objc_class_hash, *(node_ptr*)enum_state);
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+
+  if (*(node_ptr*)enum_state)
+    return (*(node_ptr*)enum_state)->value;
+  return (Class)0;
+}
+
+/* Resolve super/subclass links for all classes.  The only thing we 
+   can be sure of is that the class_pointer for class objects point 
+   to the right meta class objects */
+void __objc_resolve_class_links()
+{
+  node_ptr node;
+  Class object_class = objc_get_class ("Object");
+
+  assert(object_class);
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  /* Assign subclass links */
+  for (node = hash_next (__objc_class_hash, NULL); node;
+       node = hash_next (__objc_class_hash, node))
+    {
+      Class class1 = node->value;
+
+      /* Make sure we have what we think we have.  */
+      assert (CLS_ISCLASS(class1));
+      assert (CLS_ISMETA(class1->class_pointer));
+
+      /* The class_pointer of all meta classes point to Object's meta class. */
+      class1->class_pointer->class_pointer = object_class->class_pointer;
+
+      if (!(CLS_ISRESOLV(class1)))
+        {
+          CLS_SETRESOLV(class1);
+          CLS_SETRESOLV(class1->class_pointer);
+              
+          if(class1->super_class)
+            {   
+              Class a_super_class 
+                = objc_get_class ((char *) class1->super_class);
+              
+              assert (a_super_class);
+              
+              DEBUG_PRINTF ("making class connections for: %s\n",
+                            class1->name);
+              
+              /* assign subclass links for superclass */
+              class1->sibling_class = a_super_class->subclass_list;
+              a_super_class->subclass_list = class1;
+              
+              /* Assign subclass links for meta class of superclass */
+              if (a_super_class->class_pointer)
+                {
+                  class1->class_pointer->sibling_class
+                    = a_super_class->class_pointer->subclass_list;
+                  a_super_class->class_pointer->subclass_list 
+                    = class1->class_pointer;
+                }
+            }
+          else                  /* a root class, make its meta object */
+                                /* be a subclass of Object */
+            {
+              class1->class_pointer->sibling_class 
+                = object_class->subclass_list;
+              object_class->subclass_list = class1->class_pointer;
+            }
+        }
+    }
+
+  /* Assign superclass links */
+  for (node = hash_next (__objc_class_hash, NULL); node;
+       node = hash_next (__objc_class_hash, node))
+    {
+      Class class1 = node->value;
+      Class sub_class;
+      for (sub_class = class1->subclass_list; sub_class;
+           sub_class = sub_class->sibling_class)
+        {
+          sub_class->super_class = class1;
+          if(CLS_ISCLASS(sub_class))
+            sub_class->class_pointer->super_class = class1->class_pointer;
+        }
+    }
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+}
+
+
+
+#define CLASSOF(c) ((c)->class_pointer)
+
+Class
+class_pose_as (Class impostor, Class super_class)
+{
+  node_ptr node;
+  Class class1;
+
+  if (!CLS_ISRESOLV (impostor))
+    __objc_resolve_class_links ();
+
+  /* preconditions */
+  assert (impostor);
+  assert (super_class);
+  assert (impostor->super_class == super_class);
+  assert (CLS_ISCLASS (impostor));
+  assert (CLS_ISCLASS (super_class));
+  assert (impostor->instance_size == super_class->instance_size);
+
+  {
+    Class *subclass = &(super_class->subclass_list);
+
+    /* move subclasses of super_class to impostor */
+    while (*subclass)
+      {
+       Class nextSub = (*subclass)->sibling_class;
+
+       if (*subclass != impostor)
+         {
+           Class sub = *subclass;
+
+           /* classes */
+           sub->sibling_class = impostor->subclass_list;
+           sub->super_class = impostor;
+           impostor->subclass_list = sub;
+
+           /* It will happen that SUB is not a class object if it is 
+              the top of the meta class hierarchy chain.  (root
+              meta-class objects inherit their class object)  If that is
+              the case... don't mess with the meta-meta class. */ 
+           if (CLS_ISCLASS (sub))
+             {
+               /* meta classes */
+               CLASSOF (sub)->sibling_class = 
+                 CLASSOF (impostor)->subclass_list;
+               CLASSOF (sub)->super_class = CLASSOF (impostor);
+               CLASSOF (impostor)->subclass_list = CLASSOF (sub);
+             }
+         }
+
+       *subclass = nextSub;
+      }
+
+    /* set subclasses of superclass to be impostor only */
+    super_class->subclass_list = impostor;
+    CLASSOF (super_class)->subclass_list = CLASSOF (impostor);
+    
+    /* set impostor to have no sibling classes */
+    impostor->sibling_class = 0;
+    CLASSOF (impostor)->sibling_class = 0;
+  }
+  
+  /* check relationship of impostor and super_class is kept. */
+  assert (impostor->super_class == super_class);
+  assert (CLASSOF (impostor)->super_class == CLASSOF (super_class));
+
+  /* This is how to update the lookup table. Regardless of
+     what the keys of the hashtable is, change all values that are
+     superclass into impostor. */
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  for (node = hash_next (__objc_class_hash, NULL); node;
+       node = hash_next (__objc_class_hash, node))
+    {
+      class1 = (Class)node->value;
+      if (class1 == super_class)
+       {
+         node->value = impostor; /* change hash table value */
+       }
+    }      
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+
+  /* next, we update the dispatch tables... */
+  __objc_update_dispatch_table_for_class (CLASSOF (impostor));
+  __objc_update_dispatch_table_for_class (impostor);
+
+  return impostor;
+}
+  
+
diff --git a/libobjc/encoding.c b/libobjc/encoding.c
new file mode 100644 (file)
index 0000000..ab47e9c
--- /dev/null
@@ -0,0 +1,912 @@
+/* Encoding of types for Objective C.
+   Copyright (C) 1993, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+   Contributed by Kresten Krab Thorup
+   Bitfield support by Ovidiu Predescu
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+#include "tconfig.h"
+#include "objc-api.h"
+#include "encoding.h"
+
+#define MAX(X, Y)                    \
+  ({ typeof(X) __x = (X), __y = (Y); \
+     (__x > __y ? __x : __y); })
+
+#define MIN(X, Y)                    \
+  ({ typeof(X) __x = (X), __y = (Y); \
+     (__x < __y ? __x : __y); })
+
+#define ROUND(V, A) \
+  ({ typeof(V) __v=(V); typeof(A) __a=(A); \
+     __a*((__v+__a-1)/__a); })
+
+
+/* Various hacks for objc_layout_record. These are used by the target
+   macros. */
+
+#define TREE_CODE(TYPE) *TYPE
+#define RECORD_TYPE     _C_STRUCT_B
+#define UNION_TYPE      _C_UNION_B
+#define QUAL_UNION_TYPE _C_UNION_B
+
+#define TYPE_FIELDS(TYPE)     objc_skip_typespec (TYPE)
+
+#define DECL_MODE(TYPE)         *(TYPE)
+
+#define DFmode          _C_DBL
+
+
+
+static inline int
+atoi (const char* str)
+{
+  int res = 0;
+  
+  while (isdigit (*str))
+    res *= 10, res += (*str++ - '0');
+
+  return res;
+}
+
+/*
+  return the size of an object specified by type 
+*/
+
+int
+objc_sizeof_type (const char* type)
+{
+  /* Skip the variable name if any */
+  if (*type == '"')
+    {
+      for (type++; *type++ != '"';)
+       /* do nothing */;
+    }
+
+  switch(*type) {
+  case _C_ID:
+    return sizeof(id);
+    break;
+
+  case _C_CLASS:
+    return sizeof(Class);
+    break;
+
+  case _C_SEL:
+    return sizeof(SEL);
+    break;
+
+  case _C_CHR:
+    return sizeof(char);
+    break;
+    
+  case _C_UCHR:
+    return sizeof(unsigned char);
+    break;
+
+  case _C_SHT:
+    return sizeof(short);
+    break;
+
+  case _C_USHT:
+    return sizeof(unsigned short);
+    break;
+
+  case _C_INT:
+    return sizeof(int);
+    break;
+
+  case _C_UINT:
+    return sizeof(unsigned int);
+    break;
+
+  case _C_LNG:
+    return sizeof(long);
+    break;
+
+  case _C_ULNG:
+    return sizeof(unsigned long);
+    break;
+
+  case _C_LNG_LNG:
+    return sizeof(long long);
+    break;
+
+  case _C_ULNG_LNG:
+    return sizeof(unsigned long long);
+    break;
+
+  case _C_FLT:
+    return sizeof(float);
+    break;
+
+  case _C_DBL:
+    return sizeof(double);
+    break;
+
+  case _C_VOID:
+    return sizeof(void);
+    break;
+  case _C_PTR:
+  case _C_ATOM:
+  case _C_CHARPTR:
+    return sizeof(char*);
+    break;
+
+  case _C_ARY_B:
+    {
+      int len = atoi(type+1);
+      while (isdigit(*++type));
+      return len*objc_aligned_size (type);
+    }
+    break; 
+
+  case _C_BFLD:
+    {
+      /* The new encoding of bitfields is: b 'position' 'type' 'size' */
+      int position, size;
+      int startByte, endByte;
+
+      position = atoi (type + 1);
+      while (isdigit (*++type));
+      size = atoi (type + 1);
+
+      startByte = position / BITS_PER_UNIT;
+      endByte = (position + size) / BITS_PER_UNIT;
+      return endByte - startByte;
+    }
+
+  case _C_STRUCT_B:
+    {
+      struct objc_struct_layout layout;
+      unsigned int size;
+
+      objc_layout_structure (type, &layout);
+      while (objc_layout_structure_next_member (&layout))
+        /* do nothing */ ;
+      objc_layout_finish_structure (&layout, &size, NULL);
+
+      return size;
+    }
+
+  case _C_UNION_B:
+    {
+      int max_size = 0;
+      while (*type != _C_UNION_E && *type++ != '=') /* do nothing */;
+      while (*type != _C_UNION_E)
+       {
+         /* Skip the variable name if any */
+         if (*type == '"')
+           {
+             for (type++; *type++ != '"';)
+               /* do nothing */;
+           }
+         max_size = MAX (max_size, objc_sizeof_type (type));
+         type = objc_skip_typespec (type);
+       }
+      return max_size;
+    }
+    
+  default:
+    {
+      objc_error(nil, OBJC_ERR_BAD_TYPE, "unknown type %s\n", type);
+      return 0;
+    }
+  }
+}
+
+
+/*
+  Return the alignment of an object specified by type 
+*/
+
+int
+objc_alignof_type(const char* type)
+{
+  /* Skip the variable name if any */
+  if (*type == '"')
+    {
+      for (type++; *type++ != '"';)
+       /* do nothing */;
+    }
+  switch(*type) {
+  case _C_ID:
+    return __alignof__(id);
+    break;
+
+  case _C_CLASS:
+    return __alignof__(Class);
+    break;
+    
+  case _C_SEL:
+    return __alignof__(SEL);
+    break;
+
+  case _C_CHR:
+    return __alignof__(char);
+    break;
+    
+  case _C_UCHR:
+    return __alignof__(unsigned char);
+    break;
+
+  case _C_SHT:
+    return __alignof__(short);
+    break;
+
+  case _C_USHT:
+    return __alignof__(unsigned short);
+    break;
+
+  case _C_INT:
+    return __alignof__(int);
+    break;
+
+  case _C_UINT:
+    return __alignof__(unsigned int);
+    break;
+
+  case _C_LNG:
+    return __alignof__(long);
+    break;
+
+  case _C_ULNG:
+    return __alignof__(unsigned long);
+    break;
+
+  case _C_LNG_LNG:
+    return __alignof__(long long);
+    break;
+
+  case _C_ULNG_LNG:
+    return __alignof__(unsigned long long);
+    break;
+
+  case _C_FLT:
+    return __alignof__(float);
+    break;
+
+  case _C_DBL:
+    return __alignof__(double);
+    break;
+
+  case _C_PTR:
+  case _C_ATOM:
+  case _C_CHARPTR:
+    return __alignof__(char*);
+    break;
+
+  case _C_ARY_B:
+    while (isdigit(*++type)) /* do nothing */;
+    return objc_alignof_type (type);
+
+  case _C_STRUCT_B:
+    {
+      struct objc_struct_layout layout;
+      unsigned int align;
+
+      objc_layout_structure (type, &layout);
+      while (objc_layout_structure_next_member (&layout))
+        /* do nothing */;
+      objc_layout_finish_structure (&layout, NULL, &align);
+
+      return align;
+    }
+
+  case _C_UNION_B:
+    {
+      int maxalign = 0;
+      while (*type != _C_UNION_E && *type++ != '=') /* do nothing */;
+      while (*type != _C_UNION_E)
+       {
+         /* Skip the variable name if any */
+         if (*type == '"')
+           {
+             for (type++; *type++ != '"';)
+               /* do nothing */;
+           }
+         maxalign = MAX (maxalign, objc_alignof_type (type));
+         type = objc_skip_typespec (type);
+       }
+      return maxalign;
+    }
+
+  default:
+    {
+      objc_error(nil, OBJC_ERR_BAD_TYPE, "unknown type %s\n", type);
+      return 0;
+    }
+  }
+}
+
+/*
+  The aligned size if the size rounded up to the nearest alignment.
+*/
+
+int
+objc_aligned_size (const char* type)
+{
+  int size, align;
+
+  /* Skip the variable name */
+  if (*type == '"')
+    {
+      for (type++; *type++ != '"';)
+       /* do nothing */;
+    }
+
+  size = objc_sizeof_type (type);
+  align = objc_alignof_type (type);
+
+  return ROUND (size, align);
+}
+
+/*
+  The size rounded up to the nearest integral of the wordsize, taken
+  to be the size of a void*.
+*/
+
+int 
+objc_promoted_size (const char* type)
+{
+  int size, wordsize;
+
+  /* Skip the variable name */
+  if (*type == '"')
+    {
+      for (type++; *type++ != '"';)
+       /* do nothing */;
+    }
+
+  size = objc_sizeof_type (type);
+  wordsize = sizeof (void*);
+
+  return ROUND (size, wordsize);
+}
+
+/*
+  Skip type qualifiers.  These may eventually precede typespecs
+  occurring in method prototype encodings.
+*/
+
+inline const char*
+objc_skip_type_qualifiers (const char* type)
+{
+  while (*type == _C_CONST
+        || *type == _C_IN 
+        || *type == _C_INOUT
+        || *type == _C_OUT 
+        || *type == _C_BYCOPY
+        || *type == _C_ONEWAY
+        || *type == _C_GCINVISIBLE)
+    {
+      type += 1;
+    }
+  return type;
+}
+
+  
+/*
+  Skip one typespec element.  If the typespec is prepended by type
+  qualifiers, these are skipped as well.
+*/
+
+const char* 
+objc_skip_typespec (const char* type)
+{
+  /* Skip the variable name if any */
+  if (*type == '"')
+    {
+      for (type++; *type++ != '"';)
+       /* do nothing */;
+    }
+
+  type = objc_skip_type_qualifiers (type);
+  
+  switch (*type) {
+
+  case _C_ID:
+    /* An id may be annotated by the actual type if it is known
+       with the @"ClassName" syntax */
+
+    if (*++type != '"')
+      return type;
+    else
+      {
+       while (*++type != '"') /* do nothing */;
+       return type + 1;
+      }
+
+    /* The following are one character type codes */
+  case _C_CLASS:
+  case _C_SEL:
+  case _C_CHR:
+  case _C_UCHR:
+  case _C_CHARPTR:
+  case _C_ATOM:
+  case _C_SHT:
+  case _C_USHT:
+  case _C_INT:
+  case _C_UINT:
+  case _C_LNG:
+  case _C_ULNG:
+  case _C_LNG_LNG:
+  case _C_ULNG_LNG:
+  case _C_FLT:
+  case _C_DBL:
+  case _C_VOID:
+  case _C_UNDEF:
+    return ++type;
+    break;
+
+  case _C_ARY_B:
+    /* skip digits, typespec and closing ']' */
+    
+    while(isdigit(*++type));
+    type = objc_skip_typespec(type);
+    if (*type == _C_ARY_E)
+      return ++type;
+    else
+      {
+       objc_error(nil, OBJC_ERR_BAD_TYPE, "bad array type %s\n", type);
+       return 0;
+      }
+
+  case _C_BFLD:
+    /* The new encoding of bitfields is: b 'position' 'type' 'size' */
+    while (isdigit (*++type)); /* skip position */
+    while (isdigit (*++type)); /* skip type and size */
+    return type;
+
+  case _C_STRUCT_B:
+    /* skip name, and elements until closing '}'  */
+    
+    while (*type != _C_STRUCT_E && *type++ != '=');
+    while (*type != _C_STRUCT_E) { type = objc_skip_typespec (type); }
+    return ++type;
+
+  case _C_UNION_B:
+    /* skip name, and elements until closing ')'  */
+    
+    while (*type != _C_UNION_E && *type++ != '=');
+    while (*type != _C_UNION_E) { type = objc_skip_typespec (type); }
+    return ++type;
+
+  case _C_PTR:
+    /* Just skip the following typespec */
+    
+    return objc_skip_typespec (++type);
+    
+  default:
+    {
+      objc_error(nil, OBJC_ERR_BAD_TYPE, "unknown type %s\n", type);
+      return 0;
+    }
+  }
+}
+
+/*
+  Skip an offset as part of a method encoding.  This is prepended by a
+  '+' if the argument is passed in registers.
+*/
+inline const char* 
+objc_skip_offset (const char* type)
+{
+  if (*type == '+') type++;
+  while(isdigit(*++type));
+  return type;
+}
+
+/*
+  Skip an argument specification of a method encoding.
+*/
+const char*
+objc_skip_argspec (const char* type)
+{
+  type = objc_skip_typespec (type);
+  type = objc_skip_offset (type);
+  return type;
+}
+
+/*
+  Return the number of arguments that the method MTH expects.
+  Note that all methods need two implicit arguments `self' and
+  `_cmd'. 
+*/
+int
+method_get_number_of_arguments (struct objc_method* mth)
+{
+  int i = 0;
+  const char* type = mth->method_types;
+  while (*type)
+    {
+      type = objc_skip_argspec (type);
+      i += 1;
+    }
+  return i - 1;
+}
+
+/*
+  Return the size of the argument block needed on the stack to invoke
+  the method MTH.  This may be zero, if all arguments are passed in
+  registers.
+*/
+
+int
+method_get_sizeof_arguments (struct objc_method* mth)
+{
+  const char* type = objc_skip_typespec (mth->method_types);
+  return atoi (type);
+}
+
+/*
+  Return a pointer to the next argument of ARGFRAME.  type points to
+  the last argument.  Typical use of this look like:
+
+  {
+    char *datum, *type; 
+    for (datum = method_get_first_argument (method, argframe, &type);
+         datum; datum = method_get_next_argument (argframe, &type))
+      {
+        unsigned flags = objc_get_type_qualifiers (type);
+        type = objc_skip_type_qualifiers (type);
+       if (*type != _C_PTR)
+          [portal encodeData: datum ofType: type];
+       else
+         {
+           if ((flags & _F_IN) == _F_IN)
+              [portal encodeData: *(char**)datum ofType: ++type];
+         }
+      }
+  }
+*/  
+
+char*
+method_get_next_argument (arglist_t argframe,
+                         const char **type)
+{
+  const char *t = objc_skip_argspec (*type);
+
+  if (*t == '\0')
+    return 0;
+
+  *type = t;
+  t = objc_skip_typespec (t);
+
+  if (*t == '+')
+    return argframe->arg_regs + atoi (++t);
+  else
+    return argframe->arg_ptr + atoi (t);
+}
+
+/*
+  Return a pointer to the value of the first argument of the method 
+  described in M with the given argumentframe ARGFRAME.  The type
+  is returned in TYPE.  type must be passed to successive calls of 
+  method_get_next_argument.
+*/
+char*
+method_get_first_argument (struct objc_method* m,
+                          arglist_t argframe, 
+                          const char** type)
+{
+  *type = m->method_types;
+  return method_get_next_argument (argframe, type);
+}
+
+/*
+   Return a pointer to the ARGth argument of the method
+   M from the frame ARGFRAME.  The type of the argument
+   is returned in the value-result argument TYPE 
+*/
+
+char*
+method_get_nth_argument (struct objc_method* m,
+                        arglist_t argframe, int arg, 
+                        const char **type)
+{
+  const char* t = objc_skip_argspec (m->method_types);
+
+  if (arg > method_get_number_of_arguments (m))
+    return 0;
+
+  while (arg--)
+    t = objc_skip_argspec (t);
+  
+  *type = t;
+  t = objc_skip_typespec (t);
+
+  if (*t == '+')
+    return argframe->arg_regs + atoi (++t);
+  else
+    return argframe->arg_ptr + atoi (t);
+}
+
+unsigned
+objc_get_type_qualifiers (const char* type)
+{
+  unsigned res = 0;
+  BOOL flag = YES;
+
+  while (flag)
+    switch (*type++)
+      {
+      case _C_CONST:   res |= _F_CONST; break;
+      case _C_IN:      res |= _F_IN; break;
+      case _C_INOUT:   res |= _F_INOUT; break;
+      case _C_OUT:     res |= _F_OUT; break;
+      case _C_BYCOPY:  res |= _F_BYCOPY; break;
+      case _C_ONEWAY:  res |= _F_ONEWAY; break;
+      case _C_GCINVISIBLE: res |= _F_GCINVISIBLE; break;
+      default: flag = NO;
+    }
+
+  return res;
+}
+
+
+/* The following three functions can be used to determine how a
+   structure is laid out by the compiler. For example:
+
+  struct objc_struct_layout layout;
+  int i;
+
+  objc_layout_structure (type, &layout);
+  while (objc_layout_structure_next_member (&layout))
+    {
+      int position, align;
+      const char *type;
+
+      objc_layout_structure_get_info (&layout, &position, &align, &type);
+      printf ("element %d has offset %d, alignment %d\n",
+              i++, position, align);
+    }
+
+  These functions are used by objc_sizeof_type and objc_alignof_type
+  functions to compute the size and alignment of structures. The
+  previous method of computing the size and alignment of a structure
+  was not working on some architectures, particulary on AIX, and in
+  the presence of bitfields inside the structure. */
+void
+objc_layout_structure (const char *type,
+                           struct objc_struct_layout *layout)
+{
+  const char *ntype;
+
+  if (*type++ != _C_STRUCT_B)
+    {
+      objc_error(nil, OBJC_ERR_BAD_TYPE,
+                 "record type expected in objc_layout_structure, got %s\n",
+                 type);
+    }
+
+  layout->original_type = type;
+
+  /* Skip "<name>=" if any. Avoid embedded structures and unions. */
+  ntype = type;
+  while (*ntype != _C_STRUCT_E && *ntype != _C_STRUCT_B && *ntype != _C_UNION_B
+         && *ntype++ != '=')
+    /* do nothing */;
+
+  /* If there's a "<name>=", ntype - 1 points to '='; skip the the name */
+  if (*(ntype - 1) == '=')
+    type = ntype;
+
+  layout->type = type;
+  layout->prev_type = NULL;
+  layout->record_size = 0;
+  layout->record_align = BITS_PER_UNIT;
+
+#ifdef STRUCTURE_SIZE_BOUNDARY
+  layout->record_align = MAX (layout->record_align, STRUCTURE_SIZE_BOUNDARY);
+#endif
+}
+
+
+BOOL
+objc_layout_structure_next_member (struct objc_struct_layout *layout)
+{
+  register int known_align = layout->record_size;
+  register int desired_align = 0;
+
+  /* The following are used only if the field is a bitfield */
+  register const char *bfld_type;
+  register int bfld_type_size, bfld_type_align, bfld_field_size;
+
+  /* The current type without the type qualifiers */
+  const char *type;
+
+#if 1
+  if (layout->prev_type == NULL)
+    {
+      layout->prev_type = layout->type;
+      layout->type = objc_skip_typespec (layout->prev_type);
+      return YES;
+    }
+#endif
+
+  /* Add the size of the previous field to the size of the record.  */
+  if (layout->prev_type)
+    {
+      type = objc_skip_type_qualifiers (layout->prev_type);
+
+      if (*type != _C_BFLD)
+        layout->record_size += objc_sizeof_type (type) * BITS_PER_UNIT;
+      else {
+        desired_align = 1;
+        /* Get the bitfield's type */
+        for (bfld_type = type + 1;
+             isdigit(*bfld_type);
+             bfld_type++)
+          /* do nothing */;
+
+        bfld_type_size = objc_sizeof_type (bfld_type) * BITS_PER_UNIT;
+        bfld_type_align = objc_alignof_type (bfld_type) * BITS_PER_UNIT;
+        bfld_field_size = atoi (objc_skip_typespec (bfld_type));
+        layout->record_size += bfld_field_size;
+      }
+    }
+
+  if (*layout->type == _C_STRUCT_E)
+    return NO;
+
+  /* Skip the variable name if any */
+  if (*layout->type == '"')
+    {
+      for (layout->type++; *layout->type++ != '"';)
+        /* do nothing */;
+    }
+
+  type = objc_skip_type_qualifiers (layout->type);
+
+  if (*type != _C_BFLD)
+    desired_align = objc_alignof_type(type) * BITS_PER_UNIT;
+  else
+    {
+      desired_align = 1;
+      /* Skip the bitfield's offset */
+      for (bfld_type = type + 1; isdigit(*bfld_type); bfld_type++)
+        /* do nothing */;
+
+      bfld_type_size = objc_sizeof_type (bfld_type) * BITS_PER_UNIT;
+      bfld_type_align = objc_alignof_type (bfld_type) * BITS_PER_UNIT;
+      bfld_field_size = atoi (objc_skip_typespec (bfld_type));
+    }
+
+#ifdef BIGGEST_FIELD_ALIGNMENT
+  desired_align = MIN (desired_align, BIGGEST_FIELD_ALIGNMENT);
+#endif
+#ifdef ADJUST_FIELD_ALIGN
+  desired_align = ADJUST_FIELD_ALIGN (type, desired_align);
+#endif
+
+  /* Record must have at least as much alignment as any field.
+     Otherwise, the alignment of the field within the record
+     is meaningless.  */
+#ifndef PCC_BITFIELD_TYPE_MATTERS
+  layout->record_align = MAX (layout->record_align, desired_align);
+#else
+  if (*type == _C_BFLD)
+    {
+      /* For these machines, a zero-length field does not
+         affect the alignment of the structure as a whole.
+         It does, however, affect the alignment of the next field
+         within the structure.  */
+      if (bfld_field_size)
+        layout->record_align = MAX (layout->record_align, desired_align);
+      else
+        desired_align = objc_alignof_type (bfld_type) * BITS_PER_UNIT;
+
+      /* A named bit field of declared type `int'
+         forces the entire structure to have `int' alignment.
+         Q1: How is encoded this thing and how to check for it?
+         Q2: How to determine maximum_field_alignment at runtime? */
+
+/*       if (DECL_NAME (field) != 0) */
+      {
+        int type_align = bfld_type_align;
+#if 0
+        if (maximum_field_alignment != 0)
+          type_align = MIN (type_align, maximum_field_alignment);
+        else if (DECL_PACKED (field))
+          type_align = MIN (type_align, BITS_PER_UNIT);
+#endif
+
+        layout->record_align = MAX (layout->record_align, type_align);
+      }
+    }
+  else
+    layout->record_align = MAX (layout->record_align, desired_align);
+#endif
+
+  /* Does this field automatically have alignment it needs
+     by virtue of the fields that precede it and the record's
+     own alignment?  */
+
+  if (*type == _C_BFLD)
+    layout->record_size = atoi (type + 1);
+  else if (layout->record_size % desired_align != 0)
+    {
+      /* No, we need to skip space before this field.
+         Bump the cumulative size to multiple of field alignment.  */
+      layout->record_size = ROUND (layout->record_size, desired_align);
+    }
+  
+  /* Jump to the next field in record. */
+
+  layout->prev_type = layout->type;
+  layout->type = objc_skip_typespec (layout->type);      /* skip component */
+
+  return YES;
+}
+
+
+void objc_layout_finish_structure (struct objc_struct_layout *layout,
+                                   unsigned int *size,
+                                   unsigned int *align)
+{
+  if (layout->type && *layout->type == _C_STRUCT_E)
+    {
+      /* Work out the alignment of the record as one expression and store
+         in the record type.  Round it up to a multiple of the record's
+         alignment. */
+
+#ifdef ROUND_TYPE_ALIGN
+      layout->record_align = ROUND_TYPE_ALIGN (layout->original_type,
+                                               1,
+                                               layout->record_align);
+#else
+      layout->record_align = MAX (1, layout->record_align);
+#endif
+
+#ifdef ROUND_TYPE_SIZE
+      layout->record_size = ROUND_TYPE_SIZE (layout->original_type,
+                                             layout->record_size,
+                                             layout->record_align);
+#else
+      /* Round the size up to be a multiple of the required alignment */
+      layout->record_size = ROUND (layout->record_size, layout->record_align);
+#endif
+
+      layout->type = NULL;
+    }
+  if (size)
+    *size = layout->record_size / BITS_PER_UNIT;
+  if (align)
+    *align = layout->record_align / BITS_PER_UNIT;
+}
+
+
+void objc_layout_structure_get_info (struct objc_struct_layout *layout,
+                                     unsigned int *offset,
+                                     unsigned int *align,
+                                     const char **type)
+{
+  if (offset)
+    *offset = layout->record_size / BITS_PER_UNIT;
+  if (align)
+    *align = layout->record_align / BITS_PER_UNIT;
+  if (type)
+    *type = layout->prev_type;
+}
diff --git a/libobjc/encoding.h b/libobjc/encoding.h
new file mode 100644 (file)
index 0000000..cbbc8de
--- /dev/null
@@ -0,0 +1,97 @@
+/* Encoding of types for Objective C.
+   Copyright (C) 1993, 1997 Free Software Foundation, Inc.
+
+Author: Kresten Krab Thorup
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+#ifndef __encoding_INCLUDE_GNU
+#define __encoding_INCLUDE_GNU
+
+#include <ctype.h>
+#include "objc/objc-api.h"
+
+#define _C_CONST       'r'
+#define _C_IN          'n'
+#define _C_INOUT       'N'
+#define _C_OUT         'o'
+#define _C_BYCOPY      'O'
+#define _C_ONEWAY      'V'
+#define _C_GCINVISIBLE '!'
+
+#define _F_CONST       0x01
+#define _F_IN          0x01
+#define _F_OUT         0x02
+#define _F_INOUT       0x03
+#define _F_BYCOPY      0x04
+#define _F_ONEWAY      0x08
+#define _F_GCINVISIBLE 0x10
+
+int objc_aligned_size (const char* type);
+int objc_sizeof_type (const char* type);
+int objc_alignof_type (const char* type);
+int objc_aligned_size (const char* type);
+int objc_promoted_size (const char* type);
+
+const char* objc_skip_type_qualifiers (const char* type);
+const char* objc_skip_typespec (const char* type);
+const char* objc_skip_offset (const char* type);
+const char* objc_skip_argspec (const char* type);
+int method_get_number_of_arguments (struct objc_method*);
+int method_get_sizeof_arguments (struct objc_method*);
+
+char* method_get_first_argument (struct objc_method*,
+                                arglist_t argframe, 
+                                const char** type);
+char* method_get_next_argument (arglist_t argframe, 
+                               const char **type);
+char* method_get_nth_argument (struct objc_method* m, 
+                              arglist_t argframe,
+                              int arg, 
+                              const char **type);
+
+unsigned objc_get_type_qualifiers (const char* type);
+
+
+struct objc_struct_layout 
+{
+  const char *original_type;
+  const char *type;
+  const char *prev_type;
+  unsigned int record_size;
+  unsigned int record_align;
+};
+
+void objc_layout_structure (const char *type,
+                            struct objc_struct_layout *layout);
+BOOL  objc_layout_structure_next_member (struct objc_struct_layout *layout);
+void objc_layout_finish_structure (struct objc_struct_layout *layout,
+                                   unsigned int *size,
+                                   unsigned int *align);
+void objc_layout_structure_get_info (struct objc_struct_layout *layout,
+                                     unsigned int *offset,
+                                     unsigned int *align,
+                                     const char **type);
+
+#endif /* __encoding_INCLUDE_GNU */
diff --git a/libobjc/gc.c b/libobjc/gc.c
new file mode 100644 (file)
index 0000000..8abf493
--- /dev/null
@@ -0,0 +1,458 @@
+/* Basic data types for Objective C.
+   Copyright (C) 1998 Free Software Foundation, Inc.
+   Contributed by Ovidiu Predescu.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+#include "../tconfig.h"
+#include "objc.h"
+#include "encoding.h"
+
+#include <assert.h>
+#include <string.h>
+
+#if OBJC_WITH_GC
+
+#include <gc.h>
+
+/* gc_typed.h uses the following but doesn't declare them */
+typedef GC_word word;
+typedef GC_signed_word signed_word;
+
+#if BITS_PER_WORD == 32
+# define LOGWL 5
+# define modWORDSZ(n) ((n) & 0x1f)        /* n mod size of word            */
+#endif
+
+#if BITS_PER_WORD == 64
+# define LOGWL 6
+# define modWORDSZ(n) ((n) & 0x3f)        /* n mod size of word            */
+#endif
+
+#define divWORDSZ(n) ((n) >> LOGWL)       /* divide n by size of word      */
+
+#include <gc_typed.h>
+
+/* The following functions set up in `mask` the corresponding pointers.
+   The offset is incremented with the size of the type.  */
+
+#define ROUND(V, A) \
+  ({ typeof(V) __v=(V); typeof(A) __a=(A); \
+     __a*((__v+__a-1)/__a); })
+
+#define SET_BIT_FOR_OFFSET(mask, offset) \
+  GC_set_bit(mask, offset / sizeof (void*))
+
+/* Some prototypes */
+static void
+__objc_gc_setup_struct (GC_bitmap mask, const char *type, int offset);
+static void
+__objc_gc_setup_union (GC_bitmap mask, const char *type, int offset);
+
+
+static void
+__objc_gc_setup_array (GC_bitmap mask, const char *type, int offset)
+{
+  int i, len = atoi(type + 1);
+
+  while (isdigit(*++type))
+    /* do nothing */;          /* skip the size of the array */
+
+  switch (*type) {
+  case _C_ARY_B:
+    for (i = 0; i < len; i++)
+      __objc_gc_setup_array (mask, type, offset);
+    break;
+
+  case _C_STRUCT_B:
+    for (i = 0; i < len; i++)
+      __objc_gc_setup_struct (mask, type, offset);
+    break;
+
+  case _C_UNION_B:
+    for (i = 0; i < len; i++)
+      __objc_gc_setup_union (mask, type, offset);
+    break;
+
+  default:
+    break;
+  }
+}
+
+static void
+__objc_gc_setup_struct (GC_bitmap mask, const char *type, int offset)
+{
+  struct objc_struct_layout layout;
+  unsigned int position;
+  const char *mtype;
+
+  objc_layout_structure (type, &layout);
+
+  while (objc_layout_structure_next_member (&layout))
+    {
+      BOOL gc_invisible = NO;
+
+      objc_layout_structure_get_info (&layout, &position, NULL, &mtype);
+
+      /* Skip the variable name */
+      if (*mtype == '"')
+       {
+         for (mtype++; *mtype++ != '"';)
+           /* do nothing */;
+       }
+
+      if (*mtype == _C_GCINVISIBLE)
+       {
+         gc_invisible = YES;
+         mtype++;
+       }
+
+      /* Add to position the offset of this structure */
+      position += offset;
+
+      switch (*mtype) {
+      case _C_ID:
+      case _C_CLASS:
+      case _C_SEL:
+      case _C_PTR:
+      case _C_CHARPTR:
+      case _C_ATOM:
+       if (!gc_invisible)
+         SET_BIT_FOR_OFFSET(mask, position);
+       break;
+
+      case _C_ARY_B:
+       __objc_gc_setup_array (mask, mtype, position);
+       break;
+
+      case _C_STRUCT_B:
+       __objc_gc_setup_struct (mask, mtype, position);
+       break;
+
+      case _C_UNION_B:
+       __objc_gc_setup_union (mask, mtype, position);
+       break;
+
+      default:
+        break;
+      }
+    }
+}
+
+static void
+__objc_gc_setup_union (GC_bitmap mask, const char *type, int offset)
+{
+  /* Sub-optimal, quick implementation: assume the union is made of
+     pointers, set up the mask accordingly. */
+
+  int i, size, align;
+
+  /* Skip the variable name */
+  if (*type == '"')
+    {
+      for (type++; *type++ != '"';)
+       /* do nothing */;
+    }
+
+  size = objc_sizeof_type (type);
+  align = objc_alignof_type (type);
+
+  offset = ROUND(offset, align);
+  for (i = 0; i < size; i += sizeof (void*))
+    {
+      SET_BIT_FOR_OFFSET(mask, offset);
+      offset += sizeof (void*);
+    }
+}
+
+
+/* Iterates over the types in the structure that represents the class
+   encoding and sets the bits in mask according to each ivar type.  */
+static void
+__objc_gc_type_description_from_type (GC_bitmap mask, const char *type)
+{
+  struct objc_struct_layout layout;
+  unsigned int offset, align;
+  const char *ivar_type;
+
+  objc_layout_structure (type, &layout);
+
+  while (objc_layout_structure_next_member (&layout))
+    {
+      BOOL gc_invisible = NO;
+
+      objc_layout_structure_get_info (&layout, &offset, &align, &ivar_type);
+
+      /* Skip the variable name */
+      if (*ivar_type == '"')
+       {
+         for (ivar_type++; *ivar_type++ != '"';)
+           /* do nothing */;
+       }
+
+      if (*ivar_type == _C_GCINVISIBLE)
+       {
+         gc_invisible = YES;
+         ivar_type++;
+       }
+
+      switch (*ivar_type) {
+      case _C_ID:
+      case _C_CLASS:
+      case _C_SEL:
+      case _C_PTR:
+      case _C_CHARPTR:
+        if (!gc_invisible)
+          SET_BIT_FOR_OFFSET(mask, offset);
+       break;
+
+      case _C_ARY_B:
+       __objc_gc_setup_array (mask, ivar_type, offset);
+       break;
+
+      case _C_STRUCT_B:
+       __objc_gc_setup_struct (mask, ivar_type, offset);
+       break;
+
+      case _C_UNION_B:
+       __objc_gc_setup_union (mask, ivar_type, offset);
+       break;
+
+      default:
+        break;
+      }
+    }
+}
+
+/* Computes in *type the full type encoding of this class including
+   its super classes. '*size' gives the total number of bytes allocated
+   into *type, '*current' the number of bytes used so far by the
+   encoding. */
+static void
+__objc_class_structure_encoding (Class class, char **type, int *size,
+                                 int *current)
+{
+  int i, ivar_count;
+  struct objc_ivar_list* ivars;
+
+  if (!class)
+    {
+      strcat (*type, "{");
+      *current++;
+      return;
+    }
+
+  /* Add the type encodings of the super classes */
+  __objc_class_structure_encoding (class->super_class, type, size, current);
+
+  ivars = class->ivars;
+  if (!ivars)
+    return;
+
+  ivar_count = ivars->ivar_count;
+
+  for (i = 0; i < ivar_count; i++)
+    {
+      struct objc_ivar *ivar = &(ivars->ivar_list[i]);
+      const char *ivar_type = ivar->ivar_type;
+      int len = strlen (ivar_type);
+
+      if (*current + len + 1 >= *size)
+        {
+          /* Increase the size of the encoding string so that it
+             contains this ivar's type. */
+          *size = ROUND(*current + len + 1, 10);
+          *type = objc_realloc (*type, *size);
+        }
+      strcat (*type + *current, ivar_type);
+      *current += len;
+    }
+}
+
+
+/* Allocates the memory that will hold the type description for class
+   and calls the __objc_class_structure_encoding that generates this
+   value. */
+void
+__objc_generate_gc_type_description (Class class)
+{
+  GC_bitmap mask;
+  int bits_no, size;
+  int type_size = 10, current;
+  char *class_structure_type;
+
+  if (!CLS_ISCLASS(class))
+    return;
+
+  /* We have to create a mask in which each bit counts for a pointer member.
+     We take into consideration all the non-pointer instance variables and we
+     round them up to the alignment. */
+
+  /* The number of bits in the mask is the size of an instance in bytes divided
+     by the size of a pointer. */
+  bits_no = (ROUND(class_get_instance_size (class), sizeof(void*))
+             / sizeof (void*));
+  size = ROUND(bits_no, BITS_PER_WORD) / BITS_PER_WORD;
+  mask = objc_atomic_malloc (size * sizeof (int));
+  memset (mask, 0, size * sizeof (int));
+
+  class_structure_type = objc_atomic_malloc (type_size);
+  *class_structure_type = current = 0;
+  __objc_class_structure_encoding (class, &class_structure_type,
+                                   &type_size, &current);
+  if (current + 1 == type_size)
+    class_structure_type = objc_realloc (class_structure_type, ++type_size);
+  strcat (class_structure_type + current, "}");
+//  printf ("type description for '%s' is %s\n", class->name, class_structure_type);
+  
+  __objc_gc_type_description_from_type (mask, class_structure_type);
+  objc_free (class_structure_type);
+
+#define DEBUG 1
+#ifdef DEBUG
+  printf ("  mask for '%s', type '%s' (bits %d, mask size %d) is:",
+         class_structure_type, class->name, bits_no, size);
+  {
+    int i;
+    for (i = 0; i < size; i++)
+      printf (" %lx", mask[i]);
+  }
+  puts ("");
+#endif
+
+  class->gc_object_type = (void*)GC_make_descriptor (mask, bits_no);
+}
+
+
+/* Returns YES if type denotes a pointer type, NO otherwise */
+static inline BOOL
+__objc_ivar_pointer (const char *type)
+{
+  type = objc_skip_type_qualifiers (type);
+
+  return (*type == _C_ID
+          || *type == _C_CLASS
+          || *type == _C_SEL
+          || *type == _C_PTR
+          || *type == _C_CHARPTR
+          || *type == _C_ATOM);
+}
+
+
+/* Mark the instance variable whose name is given by ivarname as a
+   weak pointer (a pointer hidden to the garbage collector) if
+   gc_invisible is true. If gc_invisible is false it unmarks the
+   instance variable and makes it a normal pointer, visible to the
+   garbage collector.
+
+   This operation only makes sense on instance variables that are
+   pointers.  */
+void
+class_ivar_set_gcinvisible (Class class, const char* ivarname,
+                            BOOL gc_invisible)
+{
+  int i, ivar_count;
+  struct objc_ivar_list* ivars;
+
+  if (!class || !ivarname)
+    return;
+
+  ivars = class->ivars;
+  if (!ivars)
+    return;
+
+  ivar_count = ivars->ivar_count;
+
+  for (i = 0; i < ivar_count; i++)
+    {
+      struct objc_ivar *ivar = &(ivars->ivar_list[i]);
+      const char *type;
+
+      if (!ivar->ivar_name || strcmp (ivar->ivar_name, ivarname))
+       continue;
+
+      assert (ivar->ivar_type);
+      type = ivar->ivar_type;
+
+      /* Skip the variable name */
+      if (*type == '"')
+       {
+         for (type++; *type++ != '"';)
+           /* do nothing */;
+       }
+
+      if (*type == _C_GCINVISIBLE)
+       {
+         char *new_type;
+
+         if (gc_invisible || !__objc_ivar_pointer (type))
+           return;     /* The type of the variable already matches the
+                          requested gc_invisible type */
+
+         /* The variable is gc_invisible and we have to reverse it */
+         new_type = objc_atomic_malloc (strlen (ivar->ivar_type));
+         strncpy (new_type, ivar->ivar_type,
+                  (size_t)(type - ivar->ivar_type));
+         strcat (new_type, type + 1);
+         ivar->ivar_type = new_type;
+       }
+      else
+       {
+         char *new_type;
+
+         if (!gc_invisible || !__objc_ivar_pointer (type))
+           return;     /* The type of the variable already matches the
+                          requested gc_invisible type */
+
+         /* The variable is gc visible and we have to make it gc_invisible */
+         new_type = objc_malloc (strlen (ivar->ivar_type) + 2);
+         strncpy (new_type, ivar->ivar_type,
+                  (size_t)(type - ivar->ivar_type));
+         strcat (new_type, "!");
+         strcat (new_type, type);
+         ivar->ivar_type = new_type;
+       }
+
+      __objc_generate_gc_type_description (class);
+      return;
+    }
+
+  /* Search the instance variable in the superclasses */
+  class_ivar_set_gcinvisible (class->super_class, ivarname, gc_invisible);
+}
+
+#else /* !OBJC_WITH_GC */
+
+void
+__objc_generate_gc_type_description (Class class)
+{
+}
+
+void class_ivar_set_gcinvisible (Class class,
+                                const char* ivarname,
+                                BOOL gc_invisible)
+{
+}
+
+#endif /* OBJC_WITH_GC */
diff --git a/libobjc/hash.c b/libobjc/hash.c
new file mode 100644 (file)
index 0000000..7534330
--- /dev/null
@@ -0,0 +1,283 @@
+/* Hash tables for Objective C internal structures
+   Copyright (C) 1993, 1996, 1997 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+#include "assert.h"
+
+#include "objc/hash.h"
+
+#include "runtime.h"           /* for DEBUG_PRINTF */
+
+/* These two macros determine when a hash table is full and
+   by how much it should be expanded respectively.
+
+   These equations are percentages.  */
+#define FULLNESS(cache) \
+   ((((cache)->size * 75) / 100) <= (cache)->used)
+#define EXPANSION(cache) \
+  ((cache)->size * 2)
+
+cache_ptr
+hash_new (unsigned int size, hash_func_type hash_func,
+         compare_func_type compare_func)
+{
+  cache_ptr cache;
+
+  /* Pass me a value greater than 0 and a power of 2.  */
+  assert (size);
+  assert (!(size & (size - 1)));
+
+  /* Allocate the cache structure.  calloc insures
+     its initialization for default values.  */
+  cache = (cache_ptr) objc_calloc (1, sizeof (struct cache));
+  assert (cache);
+
+  /* Allocate the array of buckets for the cache.
+     calloc initializes all of the pointers to NULL.  */
+  cache->node_table
+    = (node_ptr *) objc_calloc (size, sizeof (node_ptr));
+  assert (cache->node_table);
+
+  cache->size  = size;
+
+  /* This should work for all processor architectures? */
+  cache->mask = (size - 1);
+       
+  /* Store the hashing function so that codes can be computed.  */
+  cache->hash_func = hash_func;
+
+  /* Store the function that compares hash keys to
+     determine if they are equal.  */
+  cache->compare_func = compare_func;
+
+  return cache;
+}
+
+
+void
+hash_delete (cache_ptr cache)
+{
+  node_ptr node;
+  node_ptr next_node;
+  unsigned int i;
+
+  /* Purge all key/value pairs from the table.  */
+  /* Step through the nodes one by one and remove every node WITHOUT
+     using hash_next. this makes hash_delete much more efficient. */
+  for (i = 0;i < cache->size;i++) {
+    if ((node = cache->node_table[i])) {
+      /* an entry in the hash table has been found, now step through the
+        nodes next in the list and free them. */
+      while ((next_node = node->next)) {
+       hash_remove (cache,node->key);
+       node = next_node;
+      }
+
+      hash_remove (cache,node->key);
+    }
+  }
+
+  /* Release the array of nodes and the cache itself.  */
+  objc_free(cache->node_table);
+  objc_free(cache);
+}
+
+
+void
+hash_add (cache_ptr *cachep, const void *key, void *value)
+{
+  size_t indx = (*(*cachep)->hash_func)(*cachep, key);
+  node_ptr node = (node_ptr) objc_calloc (1, sizeof (struct cache_node));
+
+
+  assert (node);
+
+  /* Initialize the new node.  */
+  node->key    = key;
+  node->value  = value;
+  node->next  = (*cachep)->node_table[indx];
+
+  /* Debugging.
+     Check the list for another key.  */
+#ifdef DEBUG
+  { node_ptr node1 = (*cachep)->node_table[indx];
+
+    while (node1) {
+
+      assert (node1->key != key);
+      node1 = node1->next;
+    }
+  }
+#endif
+
+  /* Install the node as the first element on the list.  */
+  (*cachep)->node_table[indx] = node;
+
+  /* Bump the number of entries in the cache.  */
+  ++(*cachep)->used;
+
+  /* Check the hash table's fullness.   We're going
+     to expand if it is above the fullness level.  */
+  if (FULLNESS (*cachep)) {
+
+    /* The hash table has reached its fullness level.  Time to
+       expand it.
+
+       I'm using a slow method here but is built on other
+       primitive functions thereby increasing its
+       correctness.  */
+    node_ptr node1 = NULL;
+    cache_ptr new = hash_new (EXPANSION (*cachep),
+                             (*cachep)->hash_func,
+                             (*cachep)->compare_func);
+
+    DEBUG_PRINTF ("Expanding cache %#x from %d to %d\n",
+                 *cachep, (*cachep)->size, new->size);
+
+    /* Copy the nodes from the first hash table to the new one.  */
+    while ((node1 = hash_next (*cachep, node1)))
+      hash_add (&new, node1->key, node1->value);
+
+    /* Trash the old cache.  */
+    hash_delete (*cachep);
+
+    /* Return a pointer to the new hash table.  */
+    *cachep = new;
+  }
+}
+
+
+void
+hash_remove (cache_ptr cache, const void *key)
+{
+  size_t indx = (*cache->hash_func)(cache, key);
+  node_ptr node = cache->node_table[indx];
+
+
+  /* We assume there is an entry in the table.  Error if it is not.  */
+  assert (node);
+
+  /* Special case.  First element is the key/value pair to be removed.  */
+  if ((*cache->compare_func)(node->key, key)) {
+    cache->node_table[indx] = node->next;
+    objc_free(node);
+  } else {
+
+    /* Otherwise, find the hash entry.  */
+    node_ptr prev = node;
+    BOOL removed = NO;
+
+    do {
+
+      if ((*cache->compare_func)(node->key, key)) {
+        prev->next = node->next, removed = YES;
+        objc_free(node);
+      } else
+        prev = node, node = node->next;
+    } while (!removed && node);
+    assert (removed);
+  }
+
+  /* Decrement the number of entries in the hash table.  */
+  --cache->used;
+}
+
+
+node_ptr
+hash_next (cache_ptr cache, node_ptr node)
+{
+  /* If the scan is being started then reset the last node
+     visitied pointer and bucket index.  */
+  if (!node)
+    cache->last_bucket  = 0;
+
+  /* If there is a node visited last then check for another
+     entry in the same bucket;  Otherwise step to the next bucket.  */
+  if (node) {
+    if (node->next)
+      /* There is a node which follows the last node
+        returned.  Step to that node and retun it.  */
+      return node->next;
+    else
+      ++cache->last_bucket;
+  }
+
+  /* If the list isn't exhausted then search the buckets for
+     other nodes.  */
+  if (cache->last_bucket < cache->size) {
+    /*  Scan the remainder of the buckets looking for an entry
+       at the head of the list.  Return the first item found.  */
+    while (cache->last_bucket < cache->size)
+      if (cache->node_table[cache->last_bucket])
+        return cache->node_table[cache->last_bucket];
+      else
+        ++cache->last_bucket;
+
+    /* No further nodes were found in the hash table.  */
+    return NULL;
+  } else
+    return NULL;
+}
+
+
+/* Given KEY, return corresponding value for it in CACHE.
+   Return NULL if the KEY is not recorded.  */
+
+void *
+hash_value_for_key (cache_ptr cache, const void *key)
+{
+  node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)];
+  void *retval = NULL;
+
+  if (node)
+    do {
+      if ((*cache->compare_func)(node->key, key)) {
+        retval = node->value;
+              break;
+      } else
+        node = node->next;
+    } while (!retval && node);
+
+  return retval;
+}
+
+/* Given KEY, return YES if it exists in the CACHE.
+   Return NO if it does not */
+
+BOOL
+hash_is_key_in_hash (cache_ptr cache, const void *key)
+{
+  node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)];
+
+  if (node)
+    do {
+      if ((*cache->compare_func)(node->key, key))
+         return YES;
+      else
+        node = node->next;
+    } while (node);
+
+  return NO;
+}
diff --git a/libobjc/hash.h b/libobjc/hash.h
new file mode 100644 (file)
index 0000000..bddb791
--- /dev/null
@@ -0,0 +1,206 @@
+/* Hash tables for Objective C method dispatch.
+   Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+
+#ifndef __hash_INCLUDE_GNU
+#define __hash_INCLUDE_GNU
+
+#include <stddef.h>
+#include <objc/objc.h>
+
+/*
+ * This data structure is used to hold items
+ *  stored in a hash table.  Each node holds 
+ *  a key/value pair.
+ *
+ * Items in the cache are really of type void *.
+ */
+typedef struct cache_node
+{
+  struct cache_node *next;     /* Pointer to next entry on the list.
+                                  NULL indicates end of list. */
+  const void *key;             /* Key used to locate the value.  Used
+                                  to locate value when more than one
+                                  key computes the same hash
+                                  value. */
+  void *value;                 /* Value stored for the key. */
+} *node_ptr;
+
+
+/*
+ * This data type is the function that computes a hash code given a key.
+ * Therefore, the key can be a pointer to anything and the function specific
+ * to the key type. 
+ *
+ * Unfortunately there is a mutual data structure reference problem with this
+ * typedef.  Therefore, to remove compiler warnings the functions passed to
+ * hash_new will have to be casted to this type. 
+ */
+typedef unsigned int (*hash_func_type)(void *, const void *);
+
+/*
+ * This data type is the function that compares two hash keys and returns an
+ * integer greater than, equal to, or less than 0, according as the first
+ * parameter is lexicographically greater than, equal to, or less than the
+ * second. 
+ */
+
+typedef int (*compare_func_type)(const void *, const void *);
+
+
+/*
+ * This data structure is the cache.
+ *
+ * It must be passed to all of the hashing routines
+ *   (except for new).
+ */
+typedef struct cache
+{
+  /* Variables used to implement the hash itself.  */
+  node_ptr *node_table; /* Pointer to an array of hash nodes.  */
+  /* Variables used to track the size of the hash table so to determine
+    when to resize it.  */
+  unsigned int size; /* Number of buckets allocated for the hash table
+                       (number of array entries allocated for
+                       "node_table").  Must be a power of two.  */
+  unsigned int used; /* Current number of entries in the hash table.  */
+  unsigned int mask; /* Precomputed mask.  */
+
+  /* Variables used to implement indexing through the hash table.  */
+
+  unsigned int last_bucket; /* Tracks which entry in the array where
+                              the last value was returned.  */
+  /* Function used to compute a hash code given a key. 
+     This function is specified when the hash table is created.  */
+  hash_func_type    hash_func;
+  /* Function used to compare two hash keys to see if they are equal.  */
+  compare_func_type compare_func;
+} *cache_ptr;
+
+
+/* Two important hash tables.  */
+extern cache_ptr module_hash_table, class_hash_table;
+
+/* Allocate and initialize a hash table.  */ 
+
+cache_ptr hash_new (unsigned int size,
+                   hash_func_type hash_func,
+                   compare_func_type compare_func);
+                       
+/* Deallocate all of the hash nodes and the cache itself.  */
+
+void hash_delete (cache_ptr cache);
+
+/* Add the key/value pair to the hash table.  If the
+   hash table reaches a level of fullness then it will be resized. 
+                                                   
+   assert if the key is already in the hash.  */
+
+void hash_add (cache_ptr *cachep, const void *key, void *value);
+     
+/* Remove the key/value pair from the hash table.  
+   assert if the key isn't in the table.  */
+
+void hash_remove (cache_ptr cache, const void *key);
+
+/* Used to index through the hash table.  Start with NULL
+   to get the first entry.
+                                                  
+   Successive calls pass the value returned previously.
+   ** Don't modify the hash during this operation *** 
+                                                  
+   Cache nodes are returned such that key or value can
+   be extracted.  */
+
+node_ptr hash_next (cache_ptr cache, node_ptr node);
+
+/* Used to return a value from a hash table using a given key.  */
+
+void *hash_value_for_key (cache_ptr cache, const void *key);
+
+/* Used to determine if the given key exists in the hash table */
+
+BOOL hash_is_key_in_hash (cache_ptr cache, const void *key);
+
+/************************************************
+
+        Useful hashing functions.  
+        
+        Declared inline for your pleasure.
+        
+************************************************/
+
+/* Calculate a hash code by performing some 
+   manipulation of the key pointer.  (Use the lowest bits
+   except for those likely to be 0 due to alignment.)  */
+
+static inline unsigned int
+hash_ptr (cache_ptr cache, const void *key)
+{
+  return ((size_t)key / sizeof (void *)) & cache->mask;
+}
+
+
+/* Calculate a hash code by iterating over a NULL 
+   terminate string.  */
+static inline unsigned int 
+hash_string (cache_ptr cache, const void *key)
+{
+  unsigned int ret = 0;
+  unsigned int ctr = 0;
+        
+        
+  while (*(char*)key) {
+    ret ^= *(char*)key++ << ctr;
+    ctr = (ctr + 1) % sizeof (void *);
+  }
+
+  return ret & cache->mask;
+}
+
+
+/* Compare two pointers for equality.  */
+static inline int 
+compare_ptrs (const void *k1, const void *k2)
+{
+  return !(k1 - k2);
+}
+
+
+/* Compare two strings.  */
+static inline int 
+compare_strings (const void *k1, const void *k2)
+{
+  if (k1 == k2)
+    return 1;
+  else if (k1 == 0 || k2 == 0)
+    return 0;
+  else
+    return !strcmp (k1, k2);
+}
+
+
+#endif /* not __hash_INCLUDE_GNU */
diff --git a/libobjc/init.c b/libobjc/init.c
new file mode 100644 (file)
index 0000000..f1fea81
--- /dev/null
@@ -0,0 +1,834 @@
+/* GNU Objective C Runtime initialization 
+   Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Kresten Krab Thorup
+   +load support contributed by Ovidiu Predescu <ovidiu@net-community.com>
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include "runtime.h"
+
+/* The version number of this runtime.  This must match the number 
+   defined in gcc (objc-act.c) */
+#define OBJC_VERSION 8
+#define PROTOCOL_VERSION 2
+
+/* This list contains all modules currently loaded into the runtime */
+static struct objc_list* __objc_module_list = 0;       /* !T:MUTEX */
+
+/* This list contains all proto_list's not yet assigned class links */
+static struct objc_list* unclaimed_proto_list = 0;     /* !T:MUTEX */
+
+/* List of unresolved static instances.  */
+static struct objc_list *uninitialized_statics = 0;    /* !T:MUTEX */
+
+/* Global runtime "write" mutex. */
+objc_mutex_t __objc_runtime_mutex = 0;
+
+/* Number of threads that are alive. */
+int __objc_runtime_threads_alive = 1;                  /* !T:MUTEX */
+
+/* Check compiler vs runtime version */
+static void init_check_module_version (Module_t);
+
+/* Assign isa links to protos */
+static void __objc_init_protocols (struct objc_protocol_list* protos);
+
+/* Add protocol to class */
+static void __objc_class_add_protocols (Class, struct objc_protocol_list*);
+
+/* This is a hook which is called by __objc_exec_class every time a class
+   or a category is loaded into the runtime.  This may e.g. help a
+   dynamic loader determine the classes that have been loaded when
+   an object file is dynamically linked in */
+void (*_objc_load_callback)(Class class, Category* category); /* !T:SAFE */
+
+/* Is all categories/classes resolved? */
+BOOL __objc_dangling_categories = NO;           /* !T:UNUSED */
+
+extern SEL
+__sel_register_typed_name (const char *name, const char *types, 
+                          struct objc_selector *orig, BOOL is_const);
+
+/* Sends +load to all classes and categories in certain situations. */
+static void objc_send_load (void);
+
+/* Inserts all the classes defined in module in a tree of classes that
+   resembles the class hierarchy. This tree is traversed in preorder and the
+   classes in its nodes receive the +load message if these methods were not
+   executed before. The algorithm ensures that when the +load method of a class
+   is executed all the superclasses have been already received the +load
+   message. */
+static void __objc_create_classes_tree (Module_t module);
+
+static void __objc_call_callback (Module_t module);
+
+/* A special version that works only before the classes are completely
+   installed in the runtime. */
+static BOOL class_is_subclass_of_class (Class class, Class superclass);
+
+typedef struct objc_class_tree {
+  Class class;
+  struct objc_list *subclasses; /* `head' is pointer to an objc_class_tree */
+} objc_class_tree;
+
+/* This is a linked list of objc_class_tree trees. The head of these trees
+   are root classes (their super class is Nil). These different trees
+   represent different class hierarchies. */
+static struct objc_list *__objc_class_tree_list = NULL;
+
+/* Keeps the +load methods who have been already executed. This hash should
+   not be destroyed during the execution of the program. */
+static cache_ptr __objc_load_methods = NULL;
+
+/* Creates a tree of classes whose topmost class is directly inherited from
+   `upper' and the bottom class in this tree is `bottom_class'. The classes
+   in this tree are super classes of `bottom_class'. `subclasses' member
+   of each tree node point to the next subclass tree node. */
+static objc_class_tree *
+create_tree_of_subclasses_inherited_from (Class bottom_class, Class upper)
+{
+  Class superclass = bottom_class->super_class ?
+                       objc_lookup_class ((char*)bottom_class->super_class)
+                     : Nil;
+                                       
+  objc_class_tree *tree, *prev;
+
+  DEBUG_PRINTF ("create_tree_of_subclasses_inherited_from:");
+  DEBUG_PRINTF ("bottom_class = %s, upper = %s\n",
+               (bottom_class ? bottom_class->name : NULL),
+               (upper ? upper->name : NULL));
+
+  tree = prev = objc_calloc (1, sizeof (objc_class_tree));
+  prev->class = bottom_class;
+
+  while (superclass != upper)
+    {
+      tree = objc_calloc (1, sizeof (objc_class_tree));
+      tree->class = superclass;
+      tree->subclasses = list_cons (prev, tree->subclasses);
+      superclass = (superclass->super_class ?
+                       objc_lookup_class ((char*)superclass->super_class)
+                     : Nil);
+      prev = tree;
+    }
+
+  return tree;
+}
+
+/* Insert the `class' into the proper place in the `tree' class hierarchy. This
+   function returns a new tree if the class has been successfully inserted into
+   the tree or NULL if the class is not part of the classes hierarchy described
+   by `tree'. This function is private to objc_tree_insert_class(), you should
+   not call it directly. */
+static objc_class_tree *
+__objc_tree_insert_class (objc_class_tree *tree, Class class)
+{
+  DEBUG_PRINTF ("__objc_tree_insert_class: tree = %x, class = %s\n",
+               tree, class->name);
+
+  if (tree == NULL)
+    return create_tree_of_subclasses_inherited_from (class, NULL);
+  else if (class == tree->class)
+    {
+      /* `class' has been already inserted */
+      DEBUG_PRINTF ("1. class %s was previously inserted\n", class->name);
+      return tree;
+    }
+  else if ((class->super_class ?
+                   objc_lookup_class ((char*)class->super_class)
+                 : Nil)
+           == tree->class)
+    {
+      /* If class is a direct subclass of tree->class then add class to the
+        list of subclasses. First check to see if it wasn't already
+        inserted. */
+      struct objc_list *list = tree->subclasses;
+      objc_class_tree *node;
+
+      while (list)
+       {
+         /* Class has been already inserted; do nothing just return
+            the tree. */
+         if (((objc_class_tree*)list->head)->class == class)
+           {
+             DEBUG_PRINTF ("2. class %s was previously inserted\n",
+                           class->name);
+             return tree;
+           }
+         list = list->tail;
+       }
+
+      /* Create a new node class and insert it into the list of subclasses */
+      node = objc_calloc (1, sizeof (objc_class_tree));
+      node->class = class;
+      tree->subclasses = list_cons (node, tree->subclasses);
+      DEBUG_PRINTF ("3. class %s inserted\n", class->name);
+      return tree;
+    }
+  else
+    {
+      /* The class is not a direct subclass of tree->class. Search for class's
+         superclasses in the list of subclasses. */
+      struct objc_list *subclasses = tree->subclasses;
+
+      /* Precondition: the class must be a subclass of tree->class; otherwise
+         return NULL to indicate our caller that it must take the next tree. */
+      if (!class_is_subclass_of_class (class, tree->class))
+       return NULL;
+
+      for (; subclasses != NULL; subclasses = subclasses->tail)
+       {
+         Class aClass = ((objc_class_tree*)(subclasses->head))->class;
+
+         if (class_is_subclass_of_class (class, aClass))
+           {
+             /* If we found one of class's superclasses we insert the class
+                into its subtree and return the original tree since nothing
+                has been changed. */
+             subclasses->head
+                 = __objc_tree_insert_class (subclasses->head, class);
+             DEBUG_PRINTF ("4. class %s inserted\n", class->name);
+             return tree;
+           }
+       }
+
+      /* We haven't found a subclass of `class' in the `subclasses' list.
+         Create a new tree of classes whose topmost class is a direct subclass
+        of tree->class. */
+      {
+       objc_class_tree *new_tree
+           = create_tree_of_subclasses_inherited_from (class, tree->class);
+       tree->subclasses = list_cons (new_tree, tree->subclasses);
+       DEBUG_PRINTF ("5. class %s inserted\n", class->name);
+       return tree;
+      }
+    }
+}
+
+/* This function inserts `class' in the right tree hierarchy classes. */
+static void
+objc_tree_insert_class (Class class)
+{
+  struct objc_list *list_node;
+  objc_class_tree *tree;
+
+  list_node = __objc_class_tree_list;
+  while (list_node)
+    {
+      tree = __objc_tree_insert_class (list_node->head, class);
+      if (tree)
+       {
+         list_node->head = tree;
+         break;
+       }
+      else
+       list_node = list_node->tail;
+    }
+
+  /* If the list was finished but the class hasn't been inserted, insert it
+     here. */
+  if (!list_node)
+    {
+      __objc_class_tree_list = list_cons (NULL, __objc_class_tree_list);
+      __objc_class_tree_list->head = __objc_tree_insert_class (NULL, class);
+    }
+}
+
+/* Traverse tree in preorder. Used to send +load. */
+static void
+objc_preorder_traverse (objc_class_tree *tree,
+                       int level,
+                       void (*function)(objc_class_tree*, int))
+{
+  struct objc_list *node;
+
+  (*function) (tree, level);
+  for (node = tree->subclasses; node; node = node->tail)
+    objc_preorder_traverse (node->head, level + 1, function);
+}
+
+/* Traverse tree in postorder. Used to destroy a tree. */
+static void
+objc_postorder_traverse (objc_class_tree *tree,
+                       int level,
+                       void (*function)(objc_class_tree*, int))
+{
+  struct objc_list *node;
+
+  for (node = tree->subclasses; node; node = node->tail)
+    objc_postorder_traverse (node->head, level + 1, function);
+  (*function) (tree, level);
+}
+
+/* Used to print a tree class hierarchy. */
+#ifdef DEBUG
+static void
+__objc_tree_print (objc_class_tree *tree, int level)
+{
+  int i;
+
+  for (i = 0; i < level; i++)
+    printf ("  ");
+  printf ("%s\n", tree->class->name);
+}
+#endif
+
+/* Walks on a linked list of methods in the reverse order and executes all
+   the methods corresponding to `op' selector. Walking in the reverse order
+   assures the +load of class is executed first and then +load of categories
+   because of the way in which categories are added to the class methods. */
+static void
+__objc_send_message_in_list (MethodList_t method_list, Class class, SEL op)
+{
+  int i;
+
+  if (!method_list)
+    return;
+
+  /* First execute the `op' message in the following method lists */
+  __objc_send_message_in_list (method_list->method_next, class, op);
+
+  /* Search the method list. */
+  for (i = 0; i < method_list->method_count; i++)
+    {
+      Method_t mth = &method_list->method_list[i];
+
+      if (mth->method_name && sel_eq (mth->method_name, op)
+         && !hash_is_key_in_hash (__objc_load_methods, mth->method_name))
+       {
+         /* The method was found and wasn't previously executed. */
+         (*mth->method_imp) ((id)class, mth->method_name);
+
+         /* Add this method into the +load hash table */
+         hash_add (&__objc_load_methods, mth->method_imp, mth->method_imp);
+
+         DEBUG_PRINTF ("sending +load in class: %s\n", class->name);
+
+         break;
+       }
+    }
+}
+
+static void
+__objc_send_load (objc_class_tree *tree, int level)
+{
+  static SEL load_sel = 0;
+  Class class = tree->class;
+  MethodList_t method_list = class->class_pointer->methods;
+
+  if (!load_sel)
+    load_sel = sel_register_name ("load");
+
+  __objc_send_message_in_list (method_list, class, load_sel);
+}
+
+static void
+__objc_destroy_class_tree_node (objc_class_tree *tree, int level)
+{
+  objc_free (tree);
+}
+
+/* This is used to check if the relationship between two classes before the
+   runtime completely installs the classes. */
+static BOOL
+class_is_subclass_of_class (Class class, Class superclass)
+{
+  for (; class != Nil;)
+    {
+      if (class == superclass)
+       return YES;
+      class = (class->super_class ?
+                 objc_lookup_class ((char*)class->super_class)
+               : Nil);
+    }
+
+  return NO;
+}
+
+/* This list contains all the classes in the runtime system for whom their
+   superclasses are not yet know to the runtime. */
+static struct objc_list* unresolved_classes = 0;
+
+/* Static function used to reference the Object and NXConstantString classes.
+ */
+static void
+__objc_force_linking (void)
+{
+  extern void __objc_linking (void);
+  __objc_linking ();
+
+  /* Call the function to avoid compiler warning */
+  __objc_force_linking ();
+}
+
+/* Run through the statics list, removing modules as soon as all its statics
+   have been initialized.  */
+static void
+objc_init_statics (void)
+{
+  struct objc_list **cell = &uninitialized_statics;
+  struct objc_static_instances **statics_in_module;
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  while (*cell)
+    {
+      int module_initialized = 1;
+
+      for (statics_in_module = (*cell)->head;
+          *statics_in_module; statics_in_module++)
+       {
+         struct objc_static_instances *statics = *statics_in_module;
+         Class class = objc_lookup_class (statics->class_name);
+
+         if (!class)
+           module_initialized = 0;
+         /* Actually, the static's class_pointer will be NULL when we
+             haven't been here before.  However, the comparison is to be
+             reminded of taking into account class posing and to think about
+             possible semantics...  */
+         else if (class != statics->instances[0]->class_pointer)
+           {
+             id *inst;
+
+             for (inst = &statics->instances[0]; *inst; inst++)
+               {
+                 (*inst)->class_pointer = class;
+
+                 /* ??? Make sure the object will not be freed.  With
+                     refcounting, invoke `-retain'.  Without refcounting, do
+                     nothing and hope that `-free' will never be invoked.  */
+
+                 /* ??? Send the object an `-initStatic' or something to
+                     that effect now or later on?  What are the semantics of
+                     statically allocated instances, besides the trivial
+                     NXConstantString, anyway?  */
+               }
+           }
+       }
+      if (module_initialized)
+       {
+         /* Remove this module from the uninitialized list.  */
+         struct objc_list *this = *cell;
+         *cell = this->tail;
+         objc_free(this);
+       }
+      else
+       cell = &(*cell)->tail;
+    }
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+} /* objc_init_statics */
+
+/* This function is called by constructor functions generated for each
+   module compiled.  (_GLOBAL_$I$...) The purpose of this function is to
+   gather the module pointers so that they may be processed by the
+   initialization routines as soon as possible */
+
+void
+__objc_exec_class (Module_t module)
+{
+  /* Have we processed any constructors previously?  This flag is used to
+     indicate that some global data structures need to be built.  */
+  static BOOL previous_constructors = 0;
+
+  static struct objc_list* unclaimed_categories = 0;
+
+  /* The symbol table (defined in objc-api.h) generated by gcc */
+  Symtab_t symtab = module->symtab;
+
+  /* The statics in this module */
+  struct objc_static_instances **statics
+    = symtab->defs[symtab->cls_def_cnt + symtab->cat_def_cnt];
+
+  /* Entry used to traverse hash lists */
+  struct objc_list** cell;
+
+  /* The table of selector references for this module */
+  SEL selectors = symtab->refs; 
+
+  /* dummy counter */
+  int i;
+
+  DEBUG_PRINTF ("received module: %s\n", module->name);
+
+  /* check gcc version */
+  init_check_module_version(module);
+
+  /* On the first call of this routine, initialize some data structures.  */
+  if (!previous_constructors)
+    {
+       /* Initialize thread-safe system */
+      __objc_init_thread_system();
+      __objc_runtime_threads_alive = 1;
+      __objc_runtime_mutex = objc_mutex_allocate();
+
+      __objc_init_selector_tables();
+      __objc_init_class_tables();
+      __objc_init_dispatch_tables();
+      __objc_class_tree_list = list_cons (NULL, __objc_class_tree_list);
+      __objc_load_methods
+         = hash_new (128, (hash_func_type)hash_ptr, compare_ptrs);
+      previous_constructors = 1;
+    }
+
+  /* Save the module pointer for later processing. (not currently used) */
+  objc_mutex_lock(__objc_runtime_mutex);
+  __objc_module_list = list_cons(module, __objc_module_list);
+
+  /* Replace referenced selectors from names to SEL's.  */
+  if (selectors)
+    {
+      for (i = 0; selectors[i].sel_id; ++i)
+       {
+         const char *name, *type;
+         name = (char*)selectors[i].sel_id;
+         type = (char*)selectors[i].sel_types;
+         /* Constructors are constant static data so we can safely store
+            pointers to them in the runtime structures. is_const == YES */
+         __sel_register_typed_name (name, type, 
+                                    (struct objc_selector*)&(selectors[i]),
+                                    YES);
+       }
+    }
+
+  /* Parse the classes in the load module and gather selector information.  */
+  DEBUG_PRINTF ("gathering selectors from module: %s\n", module->name);
+  for (i = 0; i < symtab->cls_def_cnt; ++i)
+    {
+      Class class = (Class) symtab->defs[i];
+      const char* superclass = (char*)class->super_class;
+
+      /* Make sure we have what we think.  */
+      assert (CLS_ISCLASS(class));
+      assert (CLS_ISMETA(class->class_pointer));
+      DEBUG_PRINTF ("phase 1, processing class: %s\n", class->name);
+
+      /* Initialize the subclass list to be NULL.
+        In some cases it isn't and this crashes the program. */
+      class->subclass_list = NULL;
+
+      /* Store the class in the class table and assign class numbers.  */
+      __objc_add_class_to_hash (class);
+
+      /* Register all of the selectors in the class and meta class.  */
+      __objc_register_selectors_from_class (class);
+      __objc_register_selectors_from_class ((Class) class->class_pointer);
+
+      /* Install the fake dispatch tables */
+      __objc_install_premature_dtable(class);
+      __objc_install_premature_dtable(class->class_pointer);
+
+      /* Register the instance methods as class methods, this is
+        only done for root classes. */
+      __objc_register_instance_methods_to_class(class);
+
+      if (class->protocols)
+       __objc_init_protocols (class->protocols);
+
+      /* Check to see if the superclass is known in this point. If it's not
+        add the class to the unresolved_classes list. */
+      if (superclass && !objc_lookup_class (superclass))
+       unresolved_classes = list_cons (class, unresolved_classes);
+   }
+
+  /* Process category information from the module.  */
+  for (i = 0; i < symtab->cat_def_cnt; ++i)
+    {
+      Category_t category = symtab->defs[i + symtab->cls_def_cnt];
+      Class class = objc_lookup_class (category->class_name);
+      
+      /* If the class for the category exists then append its methods.  */
+      if (class)
+       {
+
+         DEBUG_PRINTF ("processing categories from (module,object): %s, %s\n",
+                       module->name,
+                       class->name);
+
+         /* Do instance methods.  */
+         if (category->instance_methods)
+           class_add_method_list (class, category->instance_methods);
+
+         /* Do class methods.  */
+         if (category->class_methods)
+           class_add_method_list ((Class) class->class_pointer, 
+                                  category->class_methods);
+
+         if (category->protocols)
+           {
+             __objc_init_protocols (category->protocols);
+             __objc_class_add_protocols (class, category->protocols);
+           }
+
+          /* Register the instance methods as class methods, this is
+             only done for root classes. */
+          __objc_register_instance_methods_to_class(class);
+       }
+      else
+       {
+         /* The object to which the category methods belong can't be found.
+            Save the information.  */
+         unclaimed_categories = list_cons(category, unclaimed_categories);
+       }
+    }
+
+  if (statics)
+    uninitialized_statics = list_cons (statics, uninitialized_statics);
+  if (uninitialized_statics)
+    objc_init_statics ();
+
+  /* Scan the unclaimed category hash.  Attempt to attach any unclaimed
+     categories to objects.  */
+  for (cell = &unclaimed_categories;
+       *cell;
+       ({ if (*cell) cell = &(*cell)->tail; }))
+    {
+      Category_t category = (*cell)->head;
+      Class class = objc_lookup_class (category->class_name);
+      
+      if (class)
+       {
+         DEBUG_PRINTF ("attaching stored categories to object: %s\n",
+                       class->name);
+         
+         list_remove_head (cell);
+         
+         if (category->instance_methods)
+           class_add_method_list (class, category->instance_methods);
+         
+         if (category->class_methods)
+           class_add_method_list ((Class) class->class_pointer,
+                                  category->class_methods);
+
+         if (category->protocols)
+           {
+             __objc_init_protocols (category->protocols);
+             __objc_class_add_protocols (class, category->protocols);
+           }
+
+          /* Register the instance methods as class methods, this is
+             only done for root classes. */
+          __objc_register_instance_methods_to_class(class);
+       }
+    }
+  
+  if (unclaimed_proto_list && objc_lookup_class ("Protocol"))
+    {
+      list_mapcar (unclaimed_proto_list,(void(*)(void*))__objc_init_protocols);
+      list_free (unclaimed_proto_list);
+      unclaimed_proto_list = 0;
+    }
+
+  objc_send_load ();
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+}
+
+static void objc_send_load (void)
+{
+  if (!__objc_module_list)
+    return;
+  /* Try to find out if all the classes loaded so far also have their
+     superclasses known to the runtime. We suppose that the objects that are
+     allocated in the +load method are in general of a class declared in the
+     same module. */
+  if (unresolved_classes)
+    {
+      Class class = unresolved_classes->head;
+
+      while (objc_lookup_class ((char*)class->super_class))
+       {
+         list_remove_head (&unresolved_classes);
+         if (unresolved_classes)
+           class = unresolved_classes->head;
+         else
+           break;
+       }
+
+      /*
+       * If we still have classes for whom we don't have yet their super
+       * classes known to the runtime we don't send the +load messages.
+       */
+      if (unresolved_classes)
+       return;
+    }
+
+  /* Special check to allow creating and sending messages to constant strings
+     in +load methods. If these classes are not yet known, even if all the
+     other classes are known, delay sending of +load. */
+  if (!objc_lookup_class ("NXConstantString") ||
+      !objc_lookup_class ("Object"))
+    return;
+
+  /* Iterate over all modules in the __objc_module_list and call on them the
+     __objc_create_classes_tree function. This function creates a tree of
+     classes that resembles the class hierarchy. */
+  list_mapcar (__objc_module_list, (void(*)(void*))__objc_create_classes_tree);
+
+  while (__objc_class_tree_list)
+    {
+#ifdef DEBUG
+      objc_preorder_traverse (__objc_class_tree_list->head,
+                             0, __objc_tree_print);
+#endif
+      objc_preorder_traverse (__objc_class_tree_list->head,
+                             0, __objc_send_load);
+      objc_postorder_traverse (__objc_class_tree_list->head,
+                             0, __objc_destroy_class_tree_node);
+      list_remove_head (&__objc_class_tree_list);
+    }
+
+  list_mapcar (__objc_module_list, (void(*)(void*))__objc_call_callback);
+  list_free (__objc_module_list);
+  __objc_module_list = NULL;
+}
+
+static void
+__objc_create_classes_tree (Module_t module)
+{
+  /* The runtime mutex is locked in this point */
+
+  Symtab_t symtab = module->symtab;
+  int i;
+
+  /* Iterate thru classes defined in this module and insert them in the classes
+     tree hierarchy. */
+  for (i = 0; i < symtab->cls_def_cnt; i++)
+    {
+      Class class = (Class) symtab->defs[i];
+
+      objc_tree_insert_class (class);
+    }
+}
+
+static void
+__objc_call_callback (Module_t module)
+{
+  /* The runtime mutex is locked in this point */
+
+  Symtab_t symtab = module->symtab;
+  int i;
+
+  /* Iterate thru classes defined in this module and call the callback for
+     each one. */
+  for (i = 0; i < symtab->cls_def_cnt; i++)
+    {
+      Class class = (Class) symtab->defs[i];
+
+      /* Call the _objc_load_callback for this class. */
+      if (_objc_load_callback)
+       _objc_load_callback(class, 0);
+    }
+
+  /* Call the _objc_load_callback for categories. Don't register the instance
+     methods as class methods for categories to root classes since they were
+     already added in the class. */
+  for (i = 0; i < symtab->cat_def_cnt; i++)
+    {
+      Category_t category = symtab->defs[i + symtab->cls_def_cnt];
+      Class class = objc_lookup_class (category->class_name);
+      
+      if (_objc_load_callback)
+       _objc_load_callback(class, category);
+    }
+}
+
+/* Sanity check the version of gcc used to compile `module'*/
+static void init_check_module_version(Module_t module)
+{
+  if ((module->version != OBJC_VERSION) || (module->size != sizeof (Module)))
+    {
+      int code;
+
+      if(module->version > OBJC_VERSION)
+       code = OBJC_ERR_OBJC_VERSION;
+      else if (module->version < OBJC_VERSION)
+       code = OBJC_ERR_GCC_VERSION;
+      else
+       code = OBJC_ERR_MODULE_SIZE;
+
+      objc_error(nil, code, "Module %s version %d doesn't match runtime %d\n",
+              module->name, (int)module->version, OBJC_VERSION);
+    }
+}
+
+static void
+__objc_init_protocols (struct objc_protocol_list* protos)
+{
+  int i;
+  static Class proto_class = 0;
+
+  if (! protos)
+    return;
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  if (!proto_class)
+    proto_class = objc_lookup_class("Protocol");
+
+  if (!proto_class)
+    {
+      unclaimed_proto_list = list_cons (protos, unclaimed_proto_list);
+      objc_mutex_unlock(__objc_runtime_mutex);
+      return;
+    }
+
+#if 0
+  assert (protos->next == 0);  /* only single ones allowed */
+#endif
+
+  for(i = 0; i < protos->count; i++)
+    {
+      struct objc_protocol* aProto = protos->list[i];
+      if (((size_t)aProto->class_pointer) == PROTOCOL_VERSION)
+       {
+         /* assign class pointer */
+         aProto->class_pointer = proto_class;
+
+         /* init super protocols */
+         __objc_init_protocols (aProto->protocol_list);
+       }
+      else if (protos->list[i]->class_pointer != proto_class)
+       {
+         objc_error(nil, OBJC_ERR_PROTOCOL_VERSION,
+                    "Version %d doesn't match runtime protocol version %d\n",
+                    (int)((char*)protos->list[i]->class_pointer-(char*)0),
+                    PROTOCOL_VERSION);
+       }
+    }
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+}
+
+static void __objc_class_add_protocols (Class class,
+                                       struct objc_protocol_list* protos)
+{
+  /* Well... */
+  if (! protos)
+    return;
+
+  /* Add it... */
+  protos->next = class->protocols;
+  class->protocols = protos;
+}
diff --git a/libobjc/libobjc.def b/libobjc/libobjc.def
new file mode 100644 (file)
index 0000000..a4a6049
--- /dev/null
@@ -0,0 +1,161 @@
+;  GNU Objective C Runtime DLL Export Definitions
+;  Copyright (C) 1997 Free Software Foundation, Inc.
+;  Contributed by Scott Christley <scottc@net-community.com>
+;
+;  This file is part of GNU CC.
+;
+;  GNU CC is free software; you can redistribute it and/or modify it under the
+;  terms of the GNU General Public License as published by the Free Software
+;  Foundation; either version 2, or (at your option) any later version.
+;
+;  GNU CC 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 General Public License for more
+;  details.
+;
+;  You should have received a copy of the GNU General Public License along with
+;  GNU CC; see the file COPYING.  If not, write to the Free Software
+;  Foundation, 59 Temple Place - Suite 330,
+;  Boston, MA 02111-1307, USA.
+
+LIBRARY libobjc
+EXPORTS
+search_for_method_in_list
+objc_get_uninstalled_dtable
+hash_is_key_in_hash
+objc_verror
+_objc_load_callback
+objc_malloc
+objc_atomic_malloc
+objc_valloc
+objc_realloc
+objc_calloc
+objc_free
+__objc_init_thread_system
+objc_mutex_allocate
+objc_mutex_deallocate
+objc_mutex_lock
+objc_mutex_trylock
+objc_mutex_unlock
+objc_thread_detach
+objc_thread_exit
+objc_thread_get_data
+objc_thread_get_priority
+objc_thread_id
+objc_thread_set_data
+objc_thread_set_priority
+objc_thread_yield
+__objc_class_name_Object
+__objc_class_name_Protocol
+__objc_class_name_NXConstantString
+objc_error
+__objc_object_alloc
+__objc_object_copy
+__objc_object_dispose
+class_create_instance
+object_copy
+object_dispose
+__objc_init_selector_tables
+__objc_register_selectors_from_class
+__sel_register_typed_name
+sel_get_any_typed_uid
+sel_get_any_uid
+sel_get_name
+sel_get_type
+sel_get_typed_uid
+sel_get_uid
+sel_is_mapped
+sel_register_name
+sel_register_typed_name
+sel_types_match
+method_get_first_argument
+method_get_next_argument
+method_get_nth_argument
+method_get_number_of_arguments
+method_get_sizeof_arguments
+objc_aligned_size
+objc_alignof_type
+objc_get_type_qualifiers
+objc_promoted_size
+objc_sizeof_type
+objc_skip_argspec
+objc_skip_offset
+objc_skip_type_qualifiers
+objc_skip_typespec
+__objc_read_nbyte_uint
+__objc_read_nbyte_ulong
+__objc_write_class
+__objc_write_object
+__objc_write_selector
+objc_close_typed_stream
+objc_end_of_typed_stream
+objc_flush_typed_stream
+objc_get_stream_class_version
+objc_open_typed_stream
+objc_open_typed_stream_for_file
+objc_read_array
+objc_read_char
+objc_read_int
+objc_read_long
+objc_read_object
+objc_read_selector
+objc_read_short
+objc_read_string
+objc_read_type
+objc_read_types
+objc_read_unsigned_char
+objc_read_unsigned_int
+objc_read_unsigned_long
+objc_read_unsigned_short
+objc_write_array
+objc_write_char
+objc_write_int
+objc_write_long
+objc_write_object
+objc_write_object_reference
+objc_write_root_object
+objc_write_selector
+objc_write_short
+objc_write_string
+objc_write_string_atomic
+objc_write_type
+objc_write_types
+objc_write_unsigned_char
+objc_write_unsigned_int
+objc_write_unsigned_long
+objc_write_unsigned_short
+__objc_exec_class
+__objc_init_dispatch_tables
+__objc_install_premature_dtable
+__objc_print_dtable_stats
+__objc_responds_to
+__objc_update_dispatch_table_for_class
+class_add_method_list
+class_get_class_method
+class_get_instance_method
+get_imp
+nil_method
+objc_msg_lookup
+objc_msg_lookup_super
+objc_msg_sendv
+__objc_add_class_to_hash
+__objc_init_class_tables
+__objc_resolve_class_links
+class_pose_as
+objc_get_class
+objc_get_meta_class
+objc_lookup_class
+objc_next_class
+sarray_at_put
+sarray_at_put_safe
+sarray_free
+sarray_lazy_copy
+sarray_new
+sarray_realloc
+sarray_remove_garbage
+hash_add
+hash_delete
+hash_new
+hash_next
+hash_remove
+hash_value_for_key
diff --git a/libobjc/libobjc_entry.c b/libobjc/libobjc_entry.c
new file mode 100644 (file)
index 0000000..2d584ab
--- /dev/null
@@ -0,0 +1,55 @@
+/* GNU Objective C Runtime DLL Entry
+   Copyright (C) 1997 Free Software Foundation, Inc.
+   Contributed by Scott Christley <scottc@net-community.com>
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU CC 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 General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <windows.h>
+
+/*
+  DLL entry function for Objective-C Runtime library
+  This function gets called everytime a process/thread attaches to DLL
+  */
+WINBOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call,
+        LPVOID lpReserved)
+{
+  switch(ul_reason_for_call)
+    {
+    case DLL_PROCESS_ATTACH:
+      break;
+    case DLL_PROCESS_DETACH:
+      break;
+    case DLL_THREAD_ATTACH:
+      break;
+    case DLL_THREAD_DETACH:
+      break;
+    }
+  return TRUE;
+}
+
+/*
+  This section terminates the list of imports under GCC. If you do not
+  include this then you will have problems when linking with DLLs.
+  */
+asm (".section .idata$3\n" ".long 0,0,0,0,0,0,0,0");
diff --git a/libobjc/linking.m b/libobjc/linking.m
new file mode 100644 (file)
index 0000000..8ecca02
--- /dev/null
@@ -0,0 +1,40 @@
+/* Force linking of classes required by Objective C runtime.
+   Copyright (C) 1997 Free Software Foundation, Inc.
+   Contributed by Ovidiu Predescu (ovidiu@net-community.com).
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <objc/Object.h>
+#include <objc/NXConstStr.h>
+
+/* Generate references to Object and NXConstanstString classes since they are
+   needed by the runtime system to run correctly. */
+
+
+void __objc_linking (void)
+{
+  [Object name];
+  [NXConstantString name];
+}
+
diff --git a/libobjc/makefile.dos b/libobjc/makefile.dos
new file mode 100644 (file)
index 0000000..3e1b187
--- /dev/null
@@ -0,0 +1,56 @@
+#  GNU Objective C Runtime Makefile for compiling with djgpp
+#  Copyright (C) 1993, 1994, 1996 Free Software Foundation, Inc.
+#
+#  This file is part of GNU CC.
+#
+#  GNU CC is free software; you can redistribute it and/or modify it under the
+#  terms of the GNU General Public License as published by the Free Software
+#  Foundation; either version 2, or (at your option) any later version.
+#
+#  GNU CC 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 General Public License for more
+#  details.
+#
+#  You should have received a copy of the GNU General Public License along with
+#  GNU CC; see the file COPYING.  If not, write to the Free Software
+#  Foundation, 59 Temple Place - Suite 330,
+#  Boston, MA 02111-1307, USA. 
+
+# This Makefile is configured for GnuMAKE
+
+GCC_FOR_TARGET=gcc
+
+.SUFFIXES: .o .m
+
+OPTIMIZE = -O2
+
+# Always search these dirs when compiling.
+SUBDIR_INCLUDES = -I. -I.. -I../config
+
+.c.o:
+       $(GCC_FOR_TARGET) $(OPTIMIZE) \
+               -c $(GCC_CFLAGS) $(SUBDIR_INCLUDES) $<
+
+.m.o:
+       $(GCC_FOR_TARGET) $(OPTIMIZE) -fgnu-runtime \
+               -c $(GCC_CFLAGS) $(SUBDIR_INCLUDES) $<
+
+OBJC_O = hash.o sarray.o class.o sendmsg.o init.o archive.o \
+         selector.o objects.o misc.o object.o protocol.o encoding.o thread.o
+
+libobjc.a: $(OBJC_O)
+       -rm -f libobjc.a
+       ar rc libobjc.a $(OBJC_O)
+       ranlib libobjc.a
+
+OBJC_H = hash.h objc-list.h sarray.h objc.h \
+         objc-api.h \
+        object.h protocol.h mutex.h \
+        typedstream.h thread.h
+
+mostlyclean:
+       -rm -f *.o libobjc.a xforward fflags
+clean: mostlyclean
+distclean: mostlyclean
+extraclean: mostlyclean
diff --git a/libobjc/misc.c b/libobjc/misc.c
new file mode 100644 (file)
index 0000000..7339888
--- /dev/null
@@ -0,0 +1,180 @@
+/* GNU Objective C Runtime Miscellaneous 
+   Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Kresten Krab Thorup
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU CC 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 General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#define __USE_FIXED_PROTOTYPES__
+#include <stdlib.h>
+#include "runtime.h"
+
+/*
+** Error handler function
+** NULL so that default is to just print to stderr
+*/
+static objc_error_handler _objc_error_handler = NULL;
+
+/* Trigger an objc error */
+void
+objc_error(id object, int code, const char* fmt, ...)
+{
+  va_list ap;
+
+  va_start(ap, fmt);
+  objc_verror(object, code, fmt, ap);
+  va_end(ap);
+}
+
+/* Trigger an objc error */
+void
+objc_verror(id object, int code, const char* fmt, va_list ap)
+{
+  BOOL result = NO;
+
+  /* Call the error handler if its there
+     Otherwise print to stderr */
+  if (_objc_error_handler)
+    result = (*_objc_error_handler)(object, code, fmt, ap);
+  else
+    vfprintf (stderr, fmt, ap);
+
+  /* Continue if the error handler says its ok
+     Otherwise abort the program */
+  if (result)
+    return;
+  else
+    abort();
+}
+
+/* Set the error handler */
+objc_error_handler
+objc_set_error_handler(objc_error_handler func)
+{
+  objc_error_handler temp = _objc_error_handler;
+  _objc_error_handler = func;
+  return temp;
+}
+
+/*
+** Standard functions for memory allocation and disposal.
+** Users should use these functions in their ObjC programs so
+** that they work properly with garbage collectors as well as
+** can take advantage of the exception/error handling available.
+*/
+
+void *
+objc_malloc(size_t size)
+{
+  void* res = (void*) (*_objc_malloc)(size);
+  if(!res)
+    objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n");
+  return res;
+}
+
+void *
+objc_atomic_malloc(size_t size)
+{
+  void* res = (void*) (*_objc_atomic_malloc)(size);
+  if(!res)
+    objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n");
+  return res;
+}
+
+void *
+objc_valloc(size_t size)
+{
+  void* res = (void*) (*_objc_valloc)(size);
+  if(!res)
+    objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n");
+  return res;
+}
+
+void *
+objc_realloc(void *mem, size_t size)
+{
+  void* res = (void*) (*_objc_realloc)(mem, size);
+  if(!res)
+    objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n");
+  return res;
+}
+
+void *
+objc_calloc(size_t nelem, size_t size)
+{
+  void* res = (void*) (*_objc_calloc)(nelem, size);
+  if(!res)
+    objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n");
+  return res;
+}
+
+void
+objc_free(void *mem)
+{
+  (*_objc_free)(mem);
+}
+
+/*
+** Hook functions for memory allocation and disposal.
+** This makes it easy to substitute garbage collection systems
+** such as Boehm's GC by assigning these function pointers
+** to the GC's allocation routines.  By default these point
+** to the ANSI standard malloc, realloc, free, etc.
+**
+** Users should call the normal objc routines above for
+** memory allocation and disposal within their programs.
+*/
+
+#if OBJC_WITH_GC
+#include <gc.h>
+
+static void *GC_calloc (size_t nelem, size_t size)
+{
+  void* p = GC_malloc (nelem * size);
+  if (!p)
+    objc_error (nil, OBJC_ERR_MEMORY, "Virtual memory exhausted!\n");
+
+  memset (p, 0, nelem * size);
+  return p;
+}
+
+static void noFree (void* p) {}
+
+void *(*_objc_malloc)(size_t) = GC_malloc;
+void *(*_objc_atomic_malloc)(size_t) = GC_malloc_atomic;
+void *(*_objc_valloc)(size_t) = GC_malloc;
+void *(*_objc_realloc)(void *, size_t) = GC_realloc;
+void *(*_objc_calloc)(size_t, size_t) = GC_calloc;
+void (*_objc_free)(void *) = noFree;
+
+#else
+
+void *(*_objc_malloc)(size_t) = malloc;
+void *(*_objc_atomic_malloc)(size_t) = malloc;
+void *(*_objc_valloc)(size_t) = malloc;
+void *(*_objc_realloc)(void *, size_t) = realloc;
+void *(*_objc_calloc)(size_t, size_t) = calloc;
+void (*_objc_free)(void *) = free;
+
+
+#endif
diff --git a/libobjc/nil_method.c b/libobjc/nil_method.c
new file mode 100644 (file)
index 0000000..1b62128
--- /dev/null
@@ -0,0 +1,40 @@
+/* GNU Objective C Runtime nil receiver function
+   Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
+   Contributed by Kresten Krab Thorup
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+/* This is the nil method, the function that is called when the receiver
+   of a method is nil */
+
+#include "runtime.h"
+
+id
+nil_method(id receiver, SEL op, ...)
+{
+  return receiver;
+}
+
+
+
+
diff --git a/libobjc/objc-api.h b/libobjc/objc-api.h
new file mode 100644 (file)
index 0000000..fe34daf
--- /dev/null
@@ -0,0 +1,597 @@
+/* GNU Objective-C Runtime API.
+   Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU CC 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 General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled
+   with GCC to produce an executable, this does not cause the resulting
+   executable to be covered by the GNU General Public License.  This
+   exception does not however invalidate any other reasons why the
+   executable file might be covered by the GNU General Public License. */
+
+#ifndef __objc_api_INCLUDE_GNU
+#define __objc_api_INCLUDE_GNU
+
+#include "objc/objc.h"
+#include "objc/hash.h"
+#include "objc/thr.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+/* For functions which return Method_t */
+#define METHOD_NULL    (Method_t)0
+                                                /* Boolean typedefs */
+/*
+** Method descriptor returned by introspective Object methods.
+** This is really just the first part of the more complete objc_method
+** structure defined below and used internally by the runtime.
+*/
+struct objc_method_description
+{
+    SEL name;                  /* this is a selector, not a string */
+    char *types;               /* type encoding */
+};
+
+/* Filer types used to describe Ivars and Methods.  */
+#define _C_ID       '@'
+#define _C_CLASS    '#'
+#define _C_SEL      ':'
+#define _C_CHR      'c'
+#define _C_UCHR     'C'
+#define _C_SHT      's'
+#define _C_USHT     'S'
+#define _C_INT      'i'
+#define _C_UINT     'I'
+#define _C_LNG      'l'
+#define _C_ULNG     'L'
+#define _C_LNG_LNG  'q'
+#define _C_ULNG_LNG 'Q'
+#define _C_FLT      'f'
+#define _C_DBL      'd'
+#define _C_BFLD     'b'
+#define _C_VOID     'v'
+#define _C_UNDEF    '?'
+#define _C_PTR      '^'
+#define _C_CHARPTR  '*'
+#define _C_ATOM     '%'
+#define _C_ARY_B    '['
+#define _C_ARY_E    ']'
+#define _C_UNION_B  '('
+#define _C_UNION_E  ')'
+#define _C_STRUCT_B '{'
+#define _C_STRUCT_E '}'
+
+
+/*
+** Error handling
+**
+** Call objc_error() or objc_verror() to record an error; this error
+** routine will generally exit the program but not necessarily if the
+** user has installed his own error handler.
+**
+** Call objc_set_error_handler to assign your own function for
+** handling errors.  The function should return YES if it is ok
+** to continue execution, or return NO or just abort if the
+** program should be stopped.  The default error handler is just to
+** print a message on stderr.
+**
+** The error handler function should be of type objc_error_handler
+** The first parameter is an object instance of relevance.
+** The second parameter is an error code.
+** The third parameter is a format string in the printf style.
+** The fourth parameter is a variable list of arguments.
+*/
+extern void objc_error(id object, int code, const char* fmt, ...);
+extern void objc_verror(id object, int code, const char* fmt, va_list ap);
+typedef BOOL (*objc_error_handler)(id, int code, const char *fmt, va_list ap);
+objc_error_handler objc_set_error_handler(objc_error_handler func);
+
+/*
+** Error codes
+** These are used by the runtime library, and your
+** error handling may use them to determine if the error is
+** hard or soft thus whether execution can continue or abort.
+*/
+#define OBJC_ERR_UNKNOWN 0             /* Generic error */
+
+#define OBJC_ERR_OBJC_VERSION 1        /* Incorrect runtime version */
+#define OBJC_ERR_GCC_VERSION 2         /* Incorrect compiler version */
+#define OBJC_ERR_MODULE_SIZE 3         /* Bad module size */
+#define OBJC_ERR_PROTOCOL_VERSION 4    /* Incorrect protocol version */
+
+#define OBJC_ERR_MEMORY 10             /* Out of memory */
+
+#define OBJC_ERR_RECURSE_ROOT 20       /* Attempt to archive the root
+                                         object more than once. */
+#define OBJC_ERR_BAD_DATA 21           /* Didn't read expected data */
+#define OBJC_ERR_BAD_KEY 22            /* Bad key for object */
+#define OBJC_ERR_BAD_CLASS 23          /* Unknown class */
+#define OBJC_ERR_BAD_TYPE 24           /* Bad type specification */
+#define OBJC_ERR_NO_READ 25            /* Cannot read stream */
+#define OBJC_ERR_NO_WRITE 26           /* Cannot write stream */
+#define OBJC_ERR_STREAM_VERSION 27     /* Incorrect stream version */
+#define OBJC_ERR_BAD_OPCODE 28         /* Bad opcode */
+
+#define OBJC_ERR_UNIMPLEMENTED 30      /* Method is not implemented */
+
+#define OBJC_ERR_BAD_STATE 40          /* Bad thread state */
+
+/*
+** Set this variable nonzero to print a line describing each
+** message that is sent.  (this is currently disabled)
+*/
+extern BOOL objc_trace;
+
+
+/* For every class which happens to have statically allocated instances in
+   this module, one OBJC_STATIC_INSTANCES is allocated by the compiler.
+   INSTANCES is NULL terminated and points to all statically allocated
+   instances of this class.  */
+struct objc_static_instances
+{
+  char *class_name;
+  id instances[0];
+};
+
+/*
+** Whereas a Module (defined further down) is the root (typically) of a file,
+** a Symtab is the root of the class and category definitions within the
+** module.  
+** 
+** A Symtab contains a variable length array of pointers to classes and
+** categories  defined in the module. 
+*/
+typedef struct objc_symtab {
+  unsigned long sel_ref_cnt;                     /* Unknown. */
+  SEL        refs;                              /* Unknown. */
+  unsigned short cls_def_cnt;                   /* Number of classes compiled
+                                                  (defined) in the module. */
+  unsigned short cat_def_cnt;                   /* Number of categories 
+                                                  compiled (defined) in the 
+                                                  module. */
+
+  void      *defs[1];                           /* Variable array of pointers.
+                                                  cls_def_cnt of type Class 
+                                                  followed by cat_def_cnt of
+                                                  type Category_t, followed
+                                                 by a NULL terminated array
+                                                 of objc_static_instances. */
+} Symtab,   *Symtab_t;
+
+
+/*
+** The compiler generates one of these structures for each module that
+** composes the executable (eg main.m).  
+** 
+** This data structure is the root of the definition tree for the module.  
+** 
+** A collect program runs between ld stages and creates a ObjC ctor array. 
+** That array holds a pointer to each module structure of the executable. 
+*/
+typedef struct objc_module {
+  unsigned long version;                        /* Compiler revision. */
+  unsigned long size;                           /* sizeof(Module). */
+  const char* name;                             /* Name of the file where the 
+                                                  module was generated.   The 
+                                                  name includes the path. */
+
+  Symtab_t    symtab;                           /* Pointer to the Symtab of
+                                                  the module.  The Symtab
+                                                  holds an array of 
+                                                 pointers to 
+                                                  the classes and categories 
+                                                  defined in the module. */
+} Module, *Module_t;
+
+
+/*
+** The compiler generates one of these structures for a class that has
+** instance variables defined in its specification. 
+*/
+typedef struct objc_ivar* Ivar_t;
+typedef struct objc_ivar_list {
+  int   ivar_count;                             /* Number of structures (Ivar) 
+                                                  contained in the list.  One
+                                                  structure per instance 
+                                                  variable defined in the
+                                                  class. */
+  struct objc_ivar {
+    const char* ivar_name;                      /* Name of the instance
+                                                  variable as entered in the
+                                                  class definition. */
+    const char* ivar_type;                      /* Description of the Ivar's
+                                                  type.  Useful for 
+                                                  debuggers. */
+    int        ivar_offset;                    /* Byte offset from the base 
+                                                  address of the instance 
+                                                  structure to the variable. */
+
+  } ivar_list[1];                               /* Variable length 
+                                                  structure. */
+} IvarList, *IvarList_t;
+
+
+/*
+** The compiler generates one (or more) of these structures for a class that
+** has methods defined in its specification. 
+** 
+** The implementation of a class can be broken into separate pieces in a file
+** and categories can break them across modules. To handle this problem is a
+** singly linked list of methods. 
+*/
+typedef struct objc_method Method;
+typedef Method* Method_t;
+typedef struct objc_method_list {
+  struct objc_method_list*  method_next;      /* This variable is used to link 
+                                                a method list to another.  It 
+                                                is a singly linked list. */
+  int            method_count;               /* Number of methods defined in 
+                                                this structure. */
+  struct objc_method {
+    SEL         method_name;                  /* This variable is the method's 
+                                                name.  It is a char*. 
+                                                  The unique integer passed to 
+                                                objc_msg_send is a char* too.  
+                                                It is compared against 
+                                                method_name using strcmp. */
+    const char* method_types;                 /* Description of the method's
+                                                parameter list.  Useful for
+                                                debuggers. */
+    IMP         method_imp;                   /* Address of the method in the 
+                                                executable. */
+  } method_list[1];                           /* Variable length 
+                                                structure. */
+} MethodList, *MethodList_t;
+
+struct objc_protocol_list {
+  struct objc_protocol_list *next;
+  int count;
+  Protocol *list[1];
+};
+
+/*
+** This is used to assure consistent access to the info field of 
+** classes
+*/
+#ifndef HOST_BITS_PER_LONG
+#define HOST_BITS_PER_LONG  (sizeof(long)*8)
+#endif 
+
+#define __CLS_INFO(cls) ((cls)->info)
+#define __CLS_ISINFO(cls, mask) ((__CLS_INFO(cls)&mask)==mask)
+#define __CLS_SETINFO(cls, mask) (__CLS_INFO(cls) |= mask)
+
+/* The structure is of type MetaClass */
+#define _CLS_META 0x2L
+#define CLS_ISMETA(cls) ((cls)&&__CLS_ISINFO(cls, _CLS_META))
+
+
+/* The structure is of type Class */
+#define _CLS_CLASS 0x1L
+#define CLS_ISCLASS(cls) ((cls)&&__CLS_ISINFO(cls, _CLS_CLASS))
+
+/*
+** The class is initialized within the runtime.  This means that 
+** it has had correct super and sublinks assigned
+*/
+#define _CLS_RESOLV 0x8L
+#define CLS_ISRESOLV(cls) __CLS_ISINFO(cls, _CLS_RESOLV)
+#define CLS_SETRESOLV(cls) __CLS_SETINFO(cls, _CLS_RESOLV)
+
+/*
+** The class has been send a +initialize message or a such is not 
+** defined for this class
+*/
+#define _CLS_INITIALIZED 0x04L
+#define CLS_ISINITIALIZED(cls) __CLS_ISINFO(cls, _CLS_INITIALIZED)
+#define CLS_SETINITIALIZED(cls) __CLS_SETINFO(cls, _CLS_INITIALIZED)
+
+/*
+** The class number of this class.  This must be the same for both the 
+** class and its meta class object
+*/
+#define CLS_GETNUMBER(cls) (__CLS_INFO(cls) >> (HOST_BITS_PER_LONG/2))
+#define CLS_SETNUMBER(cls, num) \
+  ({ (cls)->info <<= (HOST_BITS_PER_LONG/2); \
+     (cls)->info >>= (HOST_BITS_PER_LONG/2); \
+     __CLS_SETINFO(cls, (((unsigned long)num) << (HOST_BITS_PER_LONG/2))); })
+
+/*
+** The compiler generates one of these structures for each category.  A class
+** may have many categories and contain both instance and factory methods.  
+*/
+typedef struct objc_category {
+  const char*   category_name;                /* Name of the category.  Name
+                                                contained in the () of the
+                                                category definition. */
+  const char*   class_name;                   /* Name of the class to which
+                                                the category belongs. */
+  MethodList_t  instance_methods;             /* Linked list of instance
+                                                methods defined in the 
+                                                category. NULL indicates no
+                                                instance methods defined. */
+  MethodList_t  class_methods;                /* Linked list of factory 
+                                                methods defined in the
+                                                category.  NULL indicates no
+                                                class methods defined. */
+  struct objc_protocol_list *protocols;              /* List of Protocols 
+                                                conformed to */
+} Category, *Category_t;
+
+/*
+** Structure used when a message is send to a class's super class.  The
+** compiler generates one of these structures and passes it to
+** objc_msg_super.
+*/
+typedef struct objc_super {
+  id      self;                           /* Id of the object sending
+                                                the message. */
+  Class class;                              /* Object's super class. */
+} Super, *Super_t;
+
+IMP objc_msg_lookup_super(Super_t super, SEL sel);
+
+retval_t objc_msg_sendv(id, SEL, arglist_t);
+
+
+
+/*
+** This is a hook which is called by objc_lookup_class and
+** objc_get_class if the runtime is not able to find the class.
+** This may e.g. try to load in the class using dynamic loading.
+** The function is guaranteed to be passed a non-NULL name string.
+*/
+extern Class (*_objc_lookup_class)(const char *name);
+
+/*
+** This is a hook which is called by __objc_exec_class every time a class
+** or a category is loaded into the runtime.  This may e.g. help a
+** dynamic loader determine the classes that have been loaded when
+** an object file is dynamically linked in.
+*/
+extern void (*_objc_load_callback)(Class class, Category* category);
+
+/*
+** Hook functions for allocating, copying and disposing of instances
+*/
+extern id (*_objc_object_alloc)(Class class);
+extern id (*_objc_object_copy)(id object);
+extern id (*_objc_object_dispose)(id object);
+
+/*
+** Standard functions for memory allocation and disposal.
+** Users should use these functions in their ObjC programs so
+** that they work properly with garbage collectors as well as
+** can take advantage of the exception/error handling available.
+*/
+void *
+objc_malloc(size_t size);
+
+void *
+objc_atomic_malloc(size_t size);
+
+void *
+objc_valloc(size_t size);
+
+void *
+objc_realloc(void *mem, size_t size);
+
+void *
+objc_calloc(size_t nelem, size_t size);
+
+void
+objc_free(void *mem);
+
+/*
+** Hook functions for memory allocation and disposal.
+** This makes it easy to substitute garbage collection systems
+** such as Boehm's GC by assigning these function pointers
+** to the GC's allocation routines.  By default these point
+** to the ANSI standard malloc, realloc, free, etc.
+**
+** Users should call the normal objc routines above for
+** memory allocation and disposal within their programs.
+*/
+extern void *(*_objc_malloc)(size_t);
+extern void *(*_objc_atomic_malloc)(size_t);
+extern void *(*_objc_valloc)(size_t);
+extern void *(*_objc_realloc)(void *, size_t);
+extern void *(*_objc_calloc)(size_t, size_t);
+extern void (*_objc_free)(void *);
+
+Method_t class_get_class_method(MetaClass class, SEL aSel);
+
+Method_t class_get_instance_method(Class class, SEL aSel);
+
+Class class_pose_as(Class impostor, Class superclass);
+
+Class objc_get_class(const char *name);
+
+Class objc_lookup_class(const char *name);
+
+Class objc_next_class(void **enum_state);
+
+const char *sel_get_name(SEL selector);
+
+const char *sel_get_type(SEL selector);
+
+SEL sel_get_uid(const char *name);
+
+SEL sel_get_any_uid(const char *name);
+
+SEL sel_get_any_typed_uid(const char *name);
+
+SEL sel_get_typed_uid(const char *name, const char*);
+
+SEL sel_register_name(const char *name);
+
+SEL sel_register_typed_name(const char *name, const char*type);
+
+
+BOOL sel_is_mapped (SEL aSel);
+
+extern id class_create_instance(Class class);
+
+static inline const char *
+class_get_class_name(Class class)
+{
+  return CLS_ISCLASS(class)?class->name:((class==Nil)?"Nil":0);
+}
+
+static inline long
+class_get_instance_size(Class class)
+{
+  return CLS_ISCLASS(class)?class->instance_size:0;
+}
+
+static inline MetaClass
+class_get_meta_class(Class class)
+{
+  return CLS_ISCLASS(class)?class->class_pointer:Nil;
+}
+
+static inline Class
+class_get_super_class(Class class)
+{
+  return CLS_ISCLASS(class)?class->super_class:Nil;
+}
+
+static inline int
+class_get_version(Class class)
+{
+  return CLS_ISCLASS(class)?class->version:-1;
+}
+
+static inline BOOL
+class_is_class(Class class)
+{
+  return CLS_ISCLASS(class);
+}
+
+static inline BOOL
+class_is_meta_class(Class class)
+{
+  return CLS_ISMETA(class);
+}
+
+
+static inline void
+class_set_version(Class class, long version)
+{
+  if (CLS_ISCLASS(class))
+    class->version = version;
+}
+
+static inline void *
+class_get_gc_object_type (Class class)
+{
+  return CLS_ISCLASS(class) ? class->gc_object_type : NULL;
+}
+
+/* Mark the instance variable as innaccessible to the garbage collector */
+extern void class_ivar_set_gcinvisible (Class class,
+                                       const char* ivarname,
+                                       BOOL gcInvisible);
+
+static inline IMP
+method_get_imp(Method_t method)
+{
+  return (method!=METHOD_NULL)?method->method_imp:(IMP)0;
+}
+
+IMP get_imp (Class class, SEL sel);
+
+/* Redefine on NeXTSTEP so as not to conflict with system function */
+#ifdef __NeXT__
+#define object_copy    gnu_object_copy
+#define object_dispose gnu_object_dispose
+#endif
+
+id object_copy(id object);
+
+id object_dispose(id object);
+
+static inline Class
+object_get_class(id object)
+{
+  return ((object!=nil)
+         ? (CLS_ISCLASS(object->class_pointer)
+            ? object->class_pointer
+            : (CLS_ISMETA(object->class_pointer)
+               ? (Class)object
+               : Nil))
+         : Nil);
+}
+
+static inline const char *
+object_get_class_name(id object)
+{
+  return ((object!=nil)?(CLS_ISCLASS(object->class_pointer)
+                         ?object->class_pointer->name
+                         :((Class)object)->name)
+                       :"Nil");
+}
+
+static inline MetaClass
+object_get_meta_class(id object)
+{
+  return ((object!=nil)?(CLS_ISCLASS(object->class_pointer)
+                         ?object->class_pointer->class_pointer
+                         :(CLS_ISMETA(object->class_pointer)
+                           ?object->class_pointer
+                           :Nil))
+                       :Nil);
+}
+
+static inline Class
+object_get_super_class
+(id object)
+{
+  return ((object!=nil)?(CLS_ISCLASS(object->class_pointer)
+                         ?object->class_pointer->super_class
+                         :(CLS_ISMETA(object->class_pointer)
+                           ?((Class)object)->super_class
+                           :Nil))
+                       :Nil);
+}
+
+static inline BOOL
+object_is_class(id object)
+{
+  return CLS_ISCLASS((Class)object);
+}
+
+static inline BOOL
+object_is_instance(id object)
+{
+  return (object!=nil)&&CLS_ISCLASS(object->class_pointer);
+}
+
+static inline BOOL
+object_is_meta_class(id object)
+{
+  return CLS_ISMETA((Class)object);
+}
+
+struct sarray* 
+objc_get_uninstalled_dtable(void);
+
+#endif /* not __objc_api_INCLUDE_GNU */
+
+
+
diff --git a/libobjc/objc-list.h b/libobjc/objc-list.h
new file mode 100644 (file)
index 0000000..1976090
--- /dev/null
@@ -0,0 +1,147 @@
+/* Generic single linked list to keep various information 
+   Copyright (C) 1993, 1994, 1996 Free Software Foundation, Inc.
+   Contributed by Kresten Krab Thorup.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#ifndef __GNU_OBJC_LIST_H
+#define __GNU_OBJC_LIST_H
+
+struct objc_list {
+  void *head;
+  struct objc_list *tail;
+};
+
+/* Return a cons cell produced from (head . tail) */
+
+static inline struct objc_list* 
+list_cons(void* head, struct objc_list* tail)
+{
+  struct objc_list* cell;
+
+  cell = (struct objc_list*)objc_malloc(sizeof(struct objc_list));
+  cell->head = head;
+  cell->tail = tail;
+  return cell;
+}
+
+/* Return the length of a list, list_length(NULL) returns zero */
+
+static inline int
+list_length(struct objc_list* list)
+{
+  int i = 0;
+  while(list)
+    {
+      i += 1;
+      list = list->tail;
+    }
+  return i;
+}
+
+/* Return the Nth element of LIST, where N count from zero.  If N 
+   larger than the list length, NULL is returned  */
+
+static inline void*
+list_nth(int index, struct objc_list* list)
+{
+  while(index-- != 0)
+    {
+      if(list->tail)
+       list = list->tail;
+      else
+       return 0;
+    }
+  return list->head;
+}
+
+/* Remove the element at the head by replacing it by its successor */
+
+static inline void
+list_remove_head(struct objc_list** list)
+{
+  if ((*list)->tail)
+    {
+      struct objc_list* tail = (*list)->tail; /* fetch next */
+      *(*list) = *tail;                /* copy next to list head */
+      objc_free(tail);                 /* free next */
+    }
+  else                         /* only one element in list */
+    {
+      objc_free(*list);
+      (*list) = 0;
+    }
+}
+
+
+/* Remove the element with `car' set to ELEMENT */
+
+static inline void
+list_remove_elem(struct objc_list** list, void* elem)
+{
+  while (*list) {
+    if ((*list)->head == elem)
+      list_remove_head(list);
+    list = &((*list)->tail);
+  }
+}
+
+/* Map FUNCTION over all elements in LIST */
+
+static inline void
+list_mapcar(struct objc_list* list, void(*function)(void*))
+{
+  while(list)
+    {
+      (*function)(list->head);
+      list = list->tail;
+    }
+}
+
+/* Return element that has ELEM as car */
+
+static inline struct objc_list**
+list_find(struct objc_list** list, void* elem)
+{
+  while(*list)
+    {
+    if ((*list)->head == elem)
+      return list;
+    list = &((*list)->tail);
+    }
+  return NULL;
+}
+
+/* Free list (backwards recursive) */
+
+static void
+list_free(struct objc_list* list)
+{
+  if(list)
+    {
+      list_free(list->tail);
+      objc_free(list);
+    }
+}
+#endif __GNU_OBJC_LIST_H
diff --git a/libobjc/objc.h b/libobjc/objc.h
new file mode 100644 (file)
index 0000000..79b2519
--- /dev/null
@@ -0,0 +1,158 @@
+/* Basic data types for Objective C.
+   Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+#ifndef __objc_INCLUDE_GNU
+#define __objc_INCLUDE_GNU
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+/*
+** Definition of the boolean type.  
+*/
+#ifdef __vxworks
+typedef int BOOL;
+#else
+typedef unsigned char  BOOL;
+#endif
+#define YES   (BOOL)1
+#define NO    (BOOL)0
+
+/*
+** Definition of a selector.  Selectors themselves are not unique, but
+** the sel_id is a unique identifier.
+*/
+typedef const struct objc_selector 
+{
+  void *sel_id;
+  const char *sel_types;
+} *SEL;
+
+inline static BOOL
+sel_eq (SEL s1, SEL s2)
+{
+  if (s1 == 0 || s2 == 0)
+    return s1 == s2;
+  else
+    return s1->sel_id == s2->sel_id;
+}
+
+
+/*
+** ObjC uses this typedef for untyped instances.
+*/
+typedef struct objc_object {
+  struct objc_class*  class_pointer;
+} *id;
+
+/*
+** Definition of method type.  When retrieving the implementation of a
+** method, this is type of the pointer returned
+*/
+typedef id (*IMP)(id, SEL, ...); 
+
+/*
+** More simple types...
+*/
+#define nil (id)0                               /* id of Nil instance */
+#define Nil (Class)0                            /* id of Nil class */
+typedef char *STR;                              /* String alias */
+
+/*
+** The compiler generates one of these structures for each class.  
+** 
+** This structure is the definition for classes. 
+** 
+** This structure is generated by the compiler in the executable and used by
+** the run-time during normal messaging operations.  Therefore some members
+** change type. The compiler generates "char* const" and places a string in
+** the following member variables:  super_class. 
+*/
+typedef struct objc_class *MetaClass;
+typedef struct objc_class *Class;
+struct objc_class {     
+  MetaClass           class_pointer;          /* Pointer to the class's
+                                                meta class. */
+  struct objc_class*  super_class;            /* Pointer to the super 
+                                                class. NULL for class 
+                                                Object. */
+  const char*         name;                   /* Name of the class. */
+  long                version;                /* Unknown. */
+  unsigned long       info;                   /* Bit mask.  See class masks 
+                                                defined above. */
+  long                instance_size;          /* Size in bytes of the class.  
+                                                The sum of the class 
+                                               definition and all super 
+                                               class definitions. */
+  struct objc_ivar_list* ivars;               /* Pointer to a structure that
+                                                describes the instance 
+                                                variables in the class
+                                                definition.  NULL indicates
+                                                no instance variables.  Does
+                                                not include super class
+                                                variables. */
+  struct objc_method_list*  methods;          /* Linked list of instance
+                                                methods defined for the 
+                                                class. */
+  struct sarray *    dtable;                  /* Pointer to instance 
+                                                method dispatch table. */  
+  struct objc_class* subclass_list;           /* Subclasses */
+  struct objc_class* sibling_class;
+
+  struct objc_protocol_list *protocols;              /* Protocols conformed to */
+  void* gc_object_type;
+};
+
+#ifndef __OBJC__
+typedef struct objc_protocol {
+  struct objc_class* class_pointer;
+  char *protocol_name;
+  struct objc_protocol_list *protocol_list;
+  struct objc_method_description_list *instance_methods, *class_methods; 
+} Protocol; 
+
+#else /* __OBJC__ */
+@class Protocol;
+#endif 
+
+typedef void* retval_t;                /* return value */
+typedef void(*apply_t)(void);  /* function pointer */
+typedef union {
+  char *arg_ptr;
+  char arg_regs[sizeof (char*)];
+} *arglist_t;                  /* argument frame */
+
+
+IMP objc_msg_lookup(id receiver, SEL op);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not __objc_INCLUDE_GNU */
diff --git a/libobjc/objects.c b/libobjc/objects.c
new file mode 100644 (file)
index 0000000..d079c5d
--- /dev/null
@@ -0,0 +1,105 @@
+/* GNU Objective C Runtime class related functions
+   Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
+   Contributed by Kresten Krab Thorup
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include "../tconfig.h"         /* include defs of bzero for target */
+#include "objc.h"
+#include "runtime.h"           /* the kitchen sink */
+
+#if OBJC_WITH_GC
+# include <gc.h>
+#endif
+
+id __objc_object_alloc(Class);
+id __objc_object_dispose(id);
+id __objc_object_copy(id);
+
+id (*_objc_object_alloc)(Class)   = __objc_object_alloc;   /* !T:SINGLE */ 
+id (*_objc_object_dispose)(id)    = __objc_object_dispose; /* !T:SINGLE */
+id (*_objc_object_copy)(id)       = __objc_object_copy;    /* !T:SINGLE */
+
+id
+class_create_instance(Class class)
+{
+  id new = nil;
+
+#if OBJC_WITH_GC
+  if (CLS_ISCLASS(class))
+    new = (id)GC_malloc_explicitly_typed (class->instance_size,
+                                         class->gc_object_type);
+#else
+  if (CLS_ISCLASS(class))
+    new = (*_objc_object_alloc)(class);
+#endif
+
+  if (new!=nil)
+    {
+      memset (new, 0, class->instance_size);
+      new->class_pointer = class;
+    }
+  return new;
+}
+
+id
+object_copy(id object)
+{
+  if ((object!=nil)&&CLS_ISCLASS(object->class_pointer))
+    return (*_objc_object_copy)(object);
+  else
+    return nil;
+}
+
+id
+object_dispose(id object)
+{
+  if ((object!=nil)&&CLS_ISCLASS(object->class_pointer))
+    {
+      if (_objc_object_dispose)
+        (*_objc_object_dispose)(object);
+      else
+        objc_free(object);
+    }
+  return nil;
+}
+
+id __objc_object_alloc(Class class)
+{
+  return (id)objc_malloc(class->instance_size);
+}
+
+id __objc_object_dispose(id object) 
+{
+  objc_free(object);
+  return 0;
+}
+
+id __objc_object_copy(id object)
+{
+  id copy = class_create_instance(object->class_pointer);
+  memcpy(copy, object, object->class_pointer->instance_size);
+  return copy;
+}
+
+
diff --git a/libobjc/runtime.h b/libobjc/runtime.h
new file mode 100644 (file)
index 0000000..b0eae4a
--- /dev/null
@@ -0,0 +1,88 @@
+/* GNU Objective C Runtime internal declarations
+   Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Kresten Krab Thorup
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#ifndef __objc_runtime_INCLUDE_GNU
+#define __objc_runtime_INCLUDE_GNU
+
+#include <stdarg.h>            /* for varargs and va_list's */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <stddef.h>            /* so noone else will get system versions */
+#include "assert.h"
+
+#include "objc/objc.h"         /* core data types */
+#include "objc/objc-api.h"     /* runtime api functions */
+
+#include "objc/thr.h"          /* thread and mutex support */
+
+#include "objc/hash.h"         /* hash structures */
+#include "objc/objc-list.h"    /* linear lists */
+
+extern void __objc_add_class_to_hash(Class);   /* (objc-class.c) */
+extern void __objc_init_selector_tables(void); /* (objc-sel.c) */
+extern void __objc_init_class_tables(void);    /* (objc-class.c) */
+extern void __objc_init_dispatch_tables(void); /* (objc-dispatch.c) */
+extern void __objc_install_premature_dtable(Class); /* (objc-dispatch.c) */
+extern void __objc_resolve_class_links(void);  /* (objc-class.c) */
+extern void __objc_register_selectors_from_class(Class); /* (objc-sel.c) */
+extern void __objc_update_dispatch_table_for_class (Class);/* (objc-msg.c) */
+
+extern int  __objc_init_thread_system(void);    /* thread.c */
+extern int  __objc_fini_thread_system(void);    /* thread.c */
+extern void __objc_print_dtable_stats(void);    /* sendmsg.c */
+
+extern void class_add_method_list(Class, MethodList_t);
+
+/* Registering instance methods as class methods for root classes */
+extern void __objc_register_instance_methods_to_class(Class);
+extern Method_t search_for_method_in_list(MethodList_t list, SEL op);
+
+/* True when class links has been resolved */     
+extern BOOL __objc_class_links_resolved;
+
+/* Number of selectors stored in each of the selector  tables */
+extern int __objc_selector_max_index;
+
+/* Mutex locking __objc_selector_max_index and its arrays. */
+extern objc_mutex_t __objc_runtime_mutex;
+
+/* Number of threads which are alive. */
+extern int __objc_runtime_threads_alive;
+
+#ifdef DEBUG
+#define DEBUG_PRINTF(format, args...) printf (format, ## args)
+#else
+#define DEBUG_PRINTF(format, args...)
+#endif 
+
+BOOL __objc_responds_to (id object, SEL sel); /* for internal use only! */
+SEL  __sel_register_typed_name (const char*, const char*, 
+                               struct objc_selector*, BOOL is_const);
+
+#endif /* not __objc_runtime_INCLUDE_GNU */
+
+
diff --git a/libobjc/sarray.c b/libobjc/sarray.c
new file mode 100644 (file)
index 0000000..7e40fba
--- /dev/null
@@ -0,0 +1,522 @@
+/* Sparse Arrays for Objective C dispatch tables
+   Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+#include "objc/sarray.h"
+#include "objc/runtime.h"
+#include <stdio.h>
+#include "assert.h"
+
+int nbuckets = 0;                                      /* !T:MUTEX */
+int nindices = 0;                                      /* !T:MUTEX */
+int narrays = 0;                                       /* !T:MUTEX */
+int idxsize = 0;                                       /* !T:MUTEX */
+
+static void *  first_free_data = NULL;                 /* !T:MUTEX */
+
+#ifdef OBJC_SPARSE2
+const char* __objc_sparse2_id = "2 level sparse indices";
+#endif
+
+#ifdef OBJC_SPARSE3
+const char* __objc_sparse3_id = "3 level sparse indices";
+#endif
+
+#ifdef __alpha__
+const void *memcpy (void*, const void*, size_t);
+#endif
+
+/* This function removes any structures left over from free operations
+   that were not safe in a multi-threaded environment. */
+void
+sarray_remove_garbage(void)
+{
+  void **vp;
+  void *np;
+  
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  vp = first_free_data;
+  first_free_data = NULL;
+
+  while (vp) {
+    np = *vp;
+    objc_free(vp);
+    vp = np;
+  }
+  
+  objc_mutex_unlock(__objc_runtime_mutex);
+}
+
+/* Free a block of dynamically allocated memory.  If we are in multi-threaded
+   mode, it is ok to free it.  If not, we add it to the garbage heap to be
+   freed later. */
+
+static void
+sarray_free_garbage(void *vp)
+{
+  objc_mutex_lock(__objc_runtime_mutex);
+  
+  if (__objc_runtime_threads_alive == 1) {
+    objc_free(vp);
+    if (first_free_data)
+      sarray_remove_garbage();
+  }
+  else {
+    *(void **)vp = first_free_data;
+    first_free_data = vp;
+  }
+      
+  objc_mutex_unlock(__objc_runtime_mutex);
+}
+
+/* sarray_at_put : copies data in such a way as to be thread reader safe. */
+void
+sarray_at_put(struct sarray* array, sidx index, void* element)
+{
+#ifdef OBJC_SPARSE3
+  struct sindex** the_index;
+  struct sindex*  new_index;
+#endif
+  struct sbucket** the_bucket;
+  struct sbucket*  new_bucket;
+#ifdef OBJC_SPARSE3
+  size_t ioffset;
+#endif
+  size_t boffset;
+  size_t eoffset;
+#ifdef PRECOMPUTE_SELECTORS
+  union sofftype xx; 
+  xx.idx = index;
+#ifdef OBJC_SPARSE3
+  ioffset = xx.off.ioffset;
+#endif
+  boffset = xx.off.boffset;
+  eoffset = xx.off.eoffset;
+#else /* not PRECOMPUTE_SELECTORS */
+#ifdef OBJC_SPARSE3
+  ioffset = index/INDEX_CAPACITY;
+  boffset = (index/BUCKET_SIZE)%INDEX_SIZE;
+  eoffset = index%BUCKET_SIZE;
+#else
+  boffset = index/BUCKET_SIZE;
+  eoffset = index%BUCKET_SIZE;
+#endif
+#endif /* not PRECOMPUTE_SELECTORS */
+
+  assert(soffset_decode(index) < array->capacity); /* Range check */
+
+#ifdef OBJC_SPARSE3
+  the_index = &(array->indices[ioffset]);
+  the_bucket = &((*the_index)->buckets[boffset]);
+#else
+  the_bucket = &(array->buckets[boffset]);
+#endif
+  
+  if ((*the_bucket)->elems[eoffset] == element)
+    return;            /* great! we just avoided a lazy copy */
+
+#ifdef OBJC_SPARSE3
+
+  /* First, perform lazy copy/allocation of index if needed */
+
+  if ((*the_index) == array->empty_index) {
+
+    /* The index was previously empty, allocate a new */
+    new_index = (struct sindex*)objc_malloc(sizeof(struct sindex));
+    memcpy(new_index, array->empty_index, sizeof(struct sindex));
+    new_index->version.version = array->version.version;
+    *the_index = new_index;                     /* Prepared for install. */
+    the_bucket = &((*the_index)->buckets[boffset]);
+    
+    nindices += 1;
+  } else if ((*the_index)->version.version != array->version.version) {
+
+    /* This index must be lazy copied */
+    struct sindex* old_index = *the_index;
+    new_index = (struct sindex*)objc_malloc(sizeof(struct sindex));
+    memcpy( new_index, old_index, sizeof(struct sindex));
+    new_index->version.version = array->version.version;
+    *the_index = new_index;                     /* Prepared for install. */
+    the_bucket = &((*the_index)->buckets[boffset]);
+    
+    nindices += 1;
+  }
+
+#endif /* OBJC_SPARSE3 */
+
+  /* next, perform lazy allocation/copy of the bucket if needed */
+
+  if ((*the_bucket) == array->empty_bucket) {
+
+    /* The bucket was previously empty (or something like that), */
+    /* allocate a new.  This is the effect of `lazy' allocation */  
+    new_bucket = (struct sbucket*)objc_malloc(sizeof(struct sbucket));
+    memcpy((void *) new_bucket, (const void*)array->empty_bucket, 
+          sizeof(struct sbucket));
+    new_bucket->version.version = array->version.version;
+    *the_bucket = new_bucket;                   /* Prepared for install. */
+    
+    nbuckets += 1;
+
+  } else if ((*the_bucket)->version.version != array->version.version) {
+
+    /* Perform lazy copy. */
+    struct sbucket* old_bucket = *the_bucket;
+    new_bucket = (struct sbucket*)objc_malloc(sizeof(struct sbucket));
+    memcpy( new_bucket, old_bucket, sizeof(struct sbucket));
+    new_bucket->version.version = array->version.version;
+    *the_bucket = new_bucket;                   /* Prepared for install. */
+    
+    nbuckets += 1;
+
+  }
+  (*the_bucket)->elems[eoffset] = element;
+}
+
+void
+sarray_at_put_safe(struct sarray* array, sidx index, void* element)
+{
+  if(soffset_decode(index) >= array->capacity)
+    sarray_realloc(array, soffset_decode(index)+1);
+  sarray_at_put(array, index, element);
+}
+
+struct sarray* 
+sarray_new (int size, void* default_element)
+{
+  struct sarray* arr;
+#ifdef OBJC_SPARSE3
+  size_t num_indices = ((size-1)/(INDEX_CAPACITY))+1;
+  struct sindex ** new_indices;
+#else /* OBJC_SPARSE2 */
+  size_t num_indices = ((size-1)/BUCKET_SIZE)+1;
+  struct sbucket ** new_buckets;
+#endif
+  int counter;
+
+  assert(size > 0);
+
+  /* Allocate core array */
+  arr = (struct sarray*) objc_malloc(sizeof(struct sarray));
+  arr->version.version = 0;
+  
+  /* Initialize members */
+#ifdef OBJC_SPARSE3
+  arr->capacity = num_indices*INDEX_CAPACITY;
+  new_indices = (struct sindex**) 
+    objc_malloc(sizeof(struct sindex*)*num_indices);
+
+  arr->empty_index = (struct sindex*) objc_malloc(sizeof(struct sindex));
+  arr->empty_index->version.version = 0;
+  
+  narrays  += 1;
+  idxsize  += num_indices;
+  nindices += 1;
+
+#else /* OBJC_SPARSE2 */
+  arr->capacity = num_indices*BUCKET_SIZE;
+  new_buckets = (struct sbucket**) 
+    objc_malloc(sizeof(struct sbucket*)*num_indices);
+  
+  narrays  += 1;
+  idxsize  += num_indices;
+
+#endif
+
+  arr->empty_bucket = (struct sbucket*) objc_malloc(sizeof(struct sbucket));
+  arr->empty_bucket->version.version = 0;
+  
+  nbuckets += 1;
+
+  arr->ref_count = 1;
+  arr->is_copy_of = (struct sarray*)0;
+  
+  for (counter=0; counter<BUCKET_SIZE; counter++)
+    arr->empty_bucket->elems[counter] = default_element;
+
+#ifdef OBJC_SPARSE3
+  for (counter=0; counter<INDEX_SIZE; counter++)
+    arr->empty_index->buckets[counter] = arr->empty_bucket;
+
+  for (counter=0; counter<num_indices; counter++)
+    new_indices[counter] = arr->empty_index;
+
+#else /* OBJC_SPARSE2 */
+
+  for (counter=0; counter<num_indices; counter++)
+    new_buckets[counter] = arr->empty_bucket;
+
+#endif
+  
+#ifdef OBJC_SPARSE3
+  arr->indices = new_indices;
+#else /* OBJC_SPARSE2 */
+  arr->buckets = new_buckets;
+#endif
+  
+  return arr;
+}
+\f
+
+/* Reallocate the sparse array to hold `newsize' entries
+   Note: We really allocate and then free.  We have to do this to ensure that
+   any concurrent readers notice the update. */
+
+void 
+sarray_realloc(struct sarray* array, int newsize)
+{
+#ifdef OBJC_SPARSE3
+  size_t old_max_index = (array->capacity-1)/INDEX_CAPACITY;
+  size_t new_max_index = ((newsize-1)/INDEX_CAPACITY);
+  size_t rounded_size = (new_max_index+1)*INDEX_CAPACITY;
+
+  struct sindex ** new_indices;
+  struct sindex ** old_indices;
+  
+#else /* OBJC_SPARSE2 */
+  size_t old_max_index = (array->capacity-1)/BUCKET_SIZE;
+  size_t new_max_index = ((newsize-1)/BUCKET_SIZE);
+  size_t rounded_size = (new_max_index+1)*BUCKET_SIZE;
+
+  struct sbucket ** new_buckets;
+  struct sbucket ** old_buckets;
+  
+#endif
+
+  int counter;
+
+  assert(newsize > 0);
+
+  /* The size is the same, just ignore the request */
+  if(rounded_size <= array->capacity)
+    return;
+
+  assert(array->ref_count == 1);       /* stop if lazy copied... */
+
+  /* We are asked to extend the array -- allocate new bucket table, */
+  /* and insert empty_bucket in newly allocated places. */
+  if(rounded_size > array->capacity) 
+    {
+
+#ifdef OBJC_SPARSE3
+      new_max_index += 4;
+      rounded_size = (new_max_index+1)*INDEX_CAPACITY;
+      
+#else /* OBJC_SPARSE2 */
+      new_max_index += 4;
+      rounded_size = (new_max_index+1)*BUCKET_SIZE;
+#endif
+      
+      /* update capacity */
+      array->capacity = rounded_size;
+
+#ifdef OBJC_SPARSE3
+      /* alloc to force re-read by any concurrent readers. */
+      old_indices = array->indices;
+      new_indices = (struct sindex**)
+       objc_malloc((new_max_index+1)*sizeof(struct sindex*));
+#else /* OBJC_SPARSE2 */
+      old_buckets = array->buckets;
+      new_buckets = (struct sbucket**)
+       objc_malloc((new_max_index+1)*sizeof(struct sbucket*));
+#endif
+
+      /* copy buckets below old_max_index (they are still valid) */
+      for(counter = 0; counter <= old_max_index; counter++ ) {
+#ifdef OBJC_SPARSE3
+       new_indices[counter] = old_indices[counter];
+#else /* OBJC_SPARSE2 */
+       new_buckets[counter] = old_buckets[counter];
+#endif
+      }
+
+#ifdef OBJC_SPARSE3
+      /* reset entries above old_max_index to empty_bucket */
+      for(counter = old_max_index+1; counter <= new_max_index; counter++)
+       new_indices[counter] = array->empty_index;
+#else /* OBJC_SPARSE2 */
+      /* reset entries above old_max_index to empty_bucket */
+      for(counter = old_max_index+1; counter <= new_max_index; counter++)
+       new_buckets[counter] = array->empty_bucket;
+#endif
+      
+#ifdef OBJC_SPARSE3
+      /* install the new indices */
+      array->indices = new_indices;
+#else /* OBJC_SPARSE2 */
+      array->buckets = new_buckets;
+#endif
+
+#ifdef OBJC_SPARSE3
+      /* free the old indices */
+      sarray_free_garbage(old_indices);
+#else /* OBJC_SPARSE2 */
+      sarray_free_garbage(old_buckets);
+#endif
+      
+      idxsize += (new_max_index-old_max_index);
+      return;
+    }
+}
+\f
+
+/* Free a sparse array allocated with sarray_new */
+
+void 
+sarray_free(struct sarray* array) {
+
+#ifdef OBJC_SPARSE3
+  size_t old_max_index = (array->capacity-1)/INDEX_CAPACITY;
+  struct sindex ** old_indices;
+#else
+  size_t old_max_index = (array->capacity-1)/BUCKET_SIZE;
+  struct sbucket ** old_buckets;
+#endif
+  int counter = 0;
+
+  assert(array->ref_count != 0);       /* Freed multiple times!!! */
+
+  if(--(array->ref_count) != 0)        /* There exists copies of me */
+    return;
+
+#ifdef OBJC_SPARSE3
+  old_indices = array->indices;
+#else
+  old_buckets = array->buckets;
+#endif
+  
+  if((array->is_copy_of) && ((array->is_copy_of->ref_count - 1) == 0))
+    sarray_free(array->is_copy_of);
+
+  /* Free all entries that do not point to empty_bucket */
+  for(counter = 0; counter <= old_max_index; counter++ ) {
+#ifdef OBJC_SPARSE3
+    struct sindex* idx = old_indices[counter];
+    if((idx != array->empty_index) &&
+       (idx->version.version == array->version.version)) {
+      int c2; 
+      for(c2=0; c2<INDEX_SIZE; c2++) {
+       struct sbucket* bkt = idx->buckets[c2];
+       if((bkt != array->empty_bucket) &&
+          (bkt->version.version == array->version.version))
+         {
+           sarray_free_garbage(bkt);
+           nbuckets -= 1;
+         }
+      }
+      sarray_free_garbage(idx);
+      nindices -= 1;
+    }
+#else /* OBJC_SPARSE2 */
+    struct sbucket* bkt = array->buckets[counter];
+    if ((bkt != array->empty_bucket) &&
+       (bkt->version.version == array->version.version))
+      {
+       sarray_free_garbage(bkt);
+       nbuckets -= 1;
+      }
+#endif
+  }
+       
+#ifdef OBJC_SPARSE3  
+  /* free empty_index */
+  if(array->empty_index->version.version == array->version.version) {
+    sarray_free_garbage(array->empty_index);
+    nindices -= 1;
+  }
+#endif
+
+  /* free empty_bucket */
+  if(array->empty_bucket->version.version == array->version.version) {
+    sarray_free_garbage(array->empty_bucket);
+    nbuckets -= 1;
+  }
+  idxsize -= (old_max_index+1);
+  narrays -= 1;
+
+#ifdef OBJC_SPARSE3
+  /* free bucket table */
+  sarray_free_garbage(array->indices);
+
+#else
+  /* free bucket table */
+  sarray_free_garbage(array->buckets);
+
+#endif
+  
+  /* free array */
+  sarray_free_garbage(array);
+}
+
+/* This is a lazy copy.  Only the core of the structure is actually */
+/* copied.   */
+
+struct sarray* 
+sarray_lazy_copy(struct sarray* oarr)
+{
+  struct sarray* arr;
+
+#ifdef OBJC_SPARSE3
+  size_t num_indices = ((oarr->capacity-1)/INDEX_CAPACITY)+1;
+  struct sindex ** new_indices;
+#else /* OBJC_SPARSE2 */
+  size_t num_indices = ((oarr->capacity-1)/BUCKET_SIZE)+1;
+  struct sbucket ** new_buckets;
+#endif
+
+  /* Allocate core array */
+  arr = (struct sarray*) objc_malloc(sizeof(struct sarray)); /* !!! */
+  arr->version.version = oarr->version.version + 1;
+#ifdef OBJC_SPARSE3
+  arr->empty_index = oarr->empty_index;
+#endif
+  arr->empty_bucket = oarr->empty_bucket;
+  arr->ref_count = 1;
+  oarr->ref_count += 1;
+  arr->is_copy_of = oarr;
+  arr->capacity = oarr->capacity;
+  
+#ifdef OBJC_SPARSE3
+  /* Copy bucket table */
+  new_indices = (struct sindex**) 
+    objc_malloc(sizeof(struct sindex*)*num_indices);
+  memcpy( new_indices,oarr->indices, 
+       sizeof(struct sindex*)*num_indices);
+  arr->indices = new_indices;
+#else 
+  /* Copy bucket table */
+  new_buckets = (struct sbucket**) 
+    objc_malloc(sizeof(struct sbucket*)*num_indices);
+  memcpy( new_buckets,oarr->buckets, 
+       sizeof(struct sbucket*)*num_indices);
+  arr->buckets = new_buckets;
+#endif
+
+  idxsize += num_indices;
+  narrays += 1;
+  
+  return arr;
+}
diff --git a/libobjc/sarray.h b/libobjc/sarray.h
new file mode 100644 (file)
index 0000000..74fa386
--- /dev/null
@@ -0,0 +1,237 @@
+/* Sparse Arrays for Objective C dispatch tables
+   Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
+   Contributed by Kresten Krab Thorup.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+#ifndef __sarray_INCLUDE_GNU
+#define __sarray_INCLUDE_GNU
+
+#define OBJC_SPARSE2           /* 2-level sparse array */
+/* #define OBJC_SPARSE3 */      /* 3-level sparse array */
+
+#ifdef OBJC_SPARSE2
+extern const char* __objc_sparse2_id;
+#endif
+
+#ifdef OBJC_SPARSE3
+extern const char* __objc_sparse3_id;
+#endif
+
+#include <stddef.h>
+
+#include "objc/thr.h"
+
+extern int nbuckets;           /* for stats */
+extern int nindices;
+extern int narrays;
+extern int idxsize;
+
+#include <assert.h>
+
+/* An unsigned integer of same size as a pointer */
+#define SIZET_BITS (sizeof(size_t)*8)
+
+#if defined(__sparc__) || defined(OBJC_SPARSE2)
+#define PRECOMPUTE_SELECTORS
+#endif
+
+#ifdef OBJC_SPARSE3
+
+/* Buckets are 8 words each */
+#define BUCKET_BITS 3
+#define BUCKET_SIZE (1<<BUCKET_BITS)
+#define BUCKET_MASK (BUCKET_SIZE-1)
+
+/* Indices are 16 words each */
+#define INDEX_BITS 4
+#define INDEX_SIZE (1<<INDEX_BITS)
+#define INDEX_MASK (INDEX_SIZE-1)
+
+#define INDEX_CAPACITY (BUCKET_SIZE*INDEX_SIZE)
+
+#else /* OBJC_SPARSE2 */
+
+/* Buckets are 32 words each */
+#define BUCKET_BITS 5
+#define BUCKET_SIZE (1<<BUCKET_BITS)
+#define BUCKET_MASK (BUCKET_SIZE-1)
+
+#endif /* OBJC_SPARSE2 */
+
+typedef size_t sidx;
+
+#ifdef PRECOMPUTE_SELECTORS
+
+struct soffset {
+#ifdef OBJC_SPARSE3
+  unsigned int unused : SIZET_BITS/4;
+  unsigned int eoffset : SIZET_BITS/4;
+  unsigned int boffset : SIZET_BITS/4;
+  unsigned int ioffset : SIZET_BITS/4;
+#else /* OBJC_SPARSE2 */
+#ifdef __sparc__
+  unsigned int boffset : (SIZET_BITS - 2) - BUCKET_BITS;
+  unsigned int eoffset : BUCKET_BITS;
+  unsigned int unused  : 2;
+#else
+  unsigned int boffset : SIZET_BITS/2;
+  unsigned int eoffset : SIZET_BITS/2;
+#endif
+#endif /* OBJC_SPARSE2 */
+};
+
+union sofftype {
+  struct soffset off;
+  sidx idx;
+};
+
+#endif /* not PRECOMPUTE_SELECTORS */
+
+union sversion {
+  int  version;
+  void *next_free;
+};
+
+struct sbucket {
+  void* elems[BUCKET_SIZE];    /* elements stored in array */
+  union sversion       version;                /* used for copy-on-write */
+};
+
+#ifdef OBJC_SPARSE3
+
+struct sindex {
+  struct sbucket* buckets[INDEX_SIZE];
+  union sversion       version;                /* used for copy-on-write */
+};
+
+#endif /* OBJC_SPARSE3 */
+
+struct sarray {
+#ifdef OBJC_SPARSE3
+  struct sindex** indices;
+  struct sindex* empty_index;
+#else /* OBJC_SPARSE2 */
+  struct sbucket** buckets;
+#endif  /* OBJC_SPARSE2 */
+  struct sbucket* empty_bucket;
+  union sversion       version;                /* used for copy-on-write */
+  short ref_count;
+  struct sarray* is_copy_of;
+  size_t capacity;
+};
+
+struct sarray* sarray_new(int, void* default_element);
+void sarray_free(struct sarray*);
+struct sarray* sarray_lazy_copy(struct sarray*);
+void sarray_realloc(struct sarray*, int new_size);
+void sarray_at_put(struct sarray*, sidx index, void* elem);
+void sarray_at_put_safe(struct sarray*, sidx index, void* elem);
+
+struct sarray* sarray_hard_copy(struct sarray*); /* ... like the name? */
+void sarray_remove_garbage(void);
+\f
+
+#ifdef PRECOMPUTE_SELECTORS
+/* Transform soffset values to ints and vica verca */
+static inline unsigned int
+soffset_decode(sidx index)
+{
+  union sofftype x;
+  x.idx = index;
+#ifdef OBJC_SPARSE3
+  return x.off.eoffset
+    + (x.off.boffset*BUCKET_SIZE)
+      + (x.off.ioffset*INDEX_CAPACITY);
+#else /* OBJC_SPARSE2 */
+  return x.off.eoffset + (x.off.boffset*BUCKET_SIZE);
+#endif /* OBJC_SPARSE2 */
+}
+
+static inline sidx
+soffset_encode(size_t offset)
+{
+  union sofftype x;
+  x.off.eoffset = offset%BUCKET_SIZE;
+#ifdef OBJC_SPARSE3
+  x.off.boffset = (offset/BUCKET_SIZE)%INDEX_SIZE;
+  x.off.ioffset = offset/INDEX_CAPACITY;
+#else /* OBJC_SPARSE2 */
+  x.off.boffset = offset/BUCKET_SIZE;
+#endif
+  return (sidx)x.idx;
+}
+
+#else /* not PRECOMPUTE_SELECTORS */
+
+static inline size_t
+soffset_decode(sidx index)
+{
+  return index;
+}
+
+static inline sidx
+soffset_encode(size_t offset)
+{
+  return offset;
+}
+#endif /* not PRECOMPUTE_SELECTORS */
+
+/* Get element from the Sparse array `array' at offset `index' */
+
+static inline void* sarray_get(struct sarray* array, sidx index)
+{
+#ifdef PRECOMPUTE_SELECTORS
+  union sofftype x;
+  x.idx = index;
+#ifdef OBJC_SPARSE3
+  return 
+    array->
+      indices[x.off.ioffset]->
+       buckets[x.off.boffset]->
+         elems[x.off.eoffset];
+#else /* OBJC_SPARSE2 */
+  return array->buckets[x.off.boffset]->elems[x.off.eoffset];
+#endif /* OBJC_SPARSE2 */
+#else /* not PRECOMPUTE_SELECTORS */
+#ifdef OBJC_SPARSE3
+  return array->
+    indices[index/INDEX_CAPACITY]->
+      buckets[(index/BUCKET_SIZE)%INDEX_SIZE]->
+       elems[index%BUCKET_SIZE];
+#else /* OBJC_SPARSE2 */
+  return array->buckets[index/BUCKET_SIZE]->elems[index%BUCKET_SIZE];
+#endif /* not OBJC_SPARSE3 */
+#endif /* not PRECOMPUTE_SELECTORS */
+}
+
+static inline void* sarray_get_safe(struct sarray* array, sidx index)
+{
+  if(soffset_decode(index) < array->capacity)
+    return sarray_get(array, index);
+  else
+    return (array->empty_bucket->elems[0]);
+}
+
+#endif /* __sarray_INCLUDE_GNU */
diff --git a/libobjc/selector.c b/libobjc/selector.c
new file mode 100644 (file)
index 0000000..83c70e4
--- /dev/null
@@ -0,0 +1,458 @@
+/* GNU Objective C Runtime selector related functions
+   Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Kresten Krab Thorup
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include "runtime.h"
+#include "objc/sarray.h"
+#include "encoding.h"
+
+/* Initial selector hash table size. Value doesn't matter much */
+#define SELECTOR_HASH_SIZE 128
+
+/* Tables mapping selector names to uid and opposite */
+static struct sarray* __objc_selector_array = 0; /* uid -> sel  !T:MUTEX */
+static struct sarray* __objc_selector_names = 0; /* uid -> name !T:MUTEX */
+static cache_ptr      __objc_selector_hash  = 0; /* name -> uid !T:MUTEX */
+
+static void register_selectors_from_list(MethodList_t);
+
+/* Number of selectors stored in each of the above tables */
+int __objc_selector_max_index = 0;              /* !T:MUTEX */
+
+void __objc_init_selector_tables()
+{
+  __objc_selector_array = sarray_new (SELECTOR_HASH_SIZE, 0);
+  __objc_selector_names = sarray_new (SELECTOR_HASH_SIZE, 0);
+  __objc_selector_hash
+    = hash_new (SELECTOR_HASH_SIZE,
+               (hash_func_type) hash_string,
+               (compare_func_type) compare_strings);
+}  
+
+/* This routine is given a class and records all of the methods in its class
+   structure in the record table.  */
+void
+__objc_register_selectors_from_class (Class class)
+{
+  MethodList_t method_list;
+
+  method_list = class->methods;
+  while (method_list)
+    {
+      register_selectors_from_list (method_list);
+      method_list = method_list->method_next;
+    }
+}
+
+
+/* This routine is given a list of methods and records each of the methods in
+   the record table.  This is the routine that does the actual recording
+   work.
+
+   This one is only called for Class objects.  For categories,
+   class_add_method_list is called.
+   */
+static void
+register_selectors_from_list (MethodList_t method_list)
+{
+  int i = 0;
+  while (i < method_list->method_count)
+    {
+      Method_t method = &method_list->method_list[i];
+      method->method_name 
+       = sel_register_typed_name ((const char*)method->method_name, 
+                                    method->method_types);
+      i += 1;
+    }
+}
+
+
+/* Register instance methods as class methods for root classes */
+void __objc_register_instance_methods_to_class(Class class)
+{
+  MethodList_t method_list;
+  MethodList_t class_method_list;
+  int max_methods_no = 16;
+  MethodList_t new_list;
+  Method_t curr_method;
+
+  /* Only if a root class. */
+  if(class->super_class)
+    return;
+
+  /* Allocate a method list to hold the new class methods */
+  new_list = objc_calloc(sizeof(struct objc_method_list)
+                           + sizeof(struct objc_method[max_methods_no]), 1);
+  method_list = class->methods;
+  class_method_list = class->class_pointer->methods;
+  curr_method = &new_list->method_list[0];
+
+  /* Iterate through the method lists for the class */
+  while (method_list)
+    {
+      int i;
+
+      /* Iterate through the methods from this method list */
+      for (i = 0; i < method_list->method_count; i++)
+       {
+         Method_t mth = &method_list->method_list[i];
+         if (mth->method_name
+             && !search_for_method_in_list (class_method_list,
+                                             mth->method_name))
+           {
+             /* This instance method isn't a class method. 
+                 Add it into the new_list. */
+             *curr_method = *mth;
+  
+             /* Reallocate the method list if necessary */
+             if(++new_list->method_count == max_methods_no)
+               new_list =
+                 objc_realloc(new_list, sizeof(struct objc_method_list)
+                               + sizeof(struct 
+                                       objc_method[max_methods_no += 16]));
+             curr_method = &new_list->method_list[new_list->method_count];
+           }
+       }
+
+      method_list = method_list->method_next;
+    }
+
+  /* If we created any new class methods
+     then attach the method list to the class */
+  if (new_list->method_count)
+    {
+      new_list =
+       objc_realloc(new_list, sizeof(struct objc_method_list)
+                    + sizeof(struct objc_method[new_list->method_count]));
+      new_list->method_next = class->class_pointer->methods;
+      class->class_pointer->methods = new_list;
+    }
+
+    __objc_update_dispatch_table_for_class (class->class_pointer);
+}
+
+
+/* Returns YES iff t1 and t2 have same method types, but we ignore
+   the argframe layout */
+BOOL
+sel_types_match (const char* t1, const char* t2)
+{
+  if (!t1 || !t2)
+    return NO;
+  while (*t1 && *t2)
+    {
+      if (*t1 == '+') t1++;
+      if (*t2 == '+') t2++;
+      while (isdigit(*t1)) t1++;
+      while (isdigit(*t2)) t2++;
+      /* xxx Remove these next two lines when qualifiers are put in
+        all selectors, not just Protocol selectors. */
+      t1 = objc_skip_type_qualifiers(t1);
+      t2 = objc_skip_type_qualifiers(t2);
+      if (!*t1 && !*t2)
+       return YES;
+      if (*t1 != *t2)
+       return NO;
+      t1++;
+      t2++;
+    }
+  return NO;
+}
+
+/* return selector representing name */
+SEL
+sel_get_typed_uid (const char *name, const char *types)
+{
+  struct objc_list *l;
+  sidx i;
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  i = (sidx) hash_value_for_key (__objc_selector_hash, name);
+  if (i == 0)
+    {
+      objc_mutex_unlock(__objc_runtime_mutex);
+      return 0;
+    }
+
+  for (l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i);
+       l; l = l->tail)
+    {
+      SEL s = (SEL)l->head;
+      if (types == 0 || s->sel_types == 0)
+       {
+         if (s->sel_types == types)
+           {
+             objc_mutex_unlock(__objc_runtime_mutex);
+             return s;
+           }
+       }
+      else if (sel_types_match (s->sel_types, types))
+       {
+         objc_mutex_unlock(__objc_runtime_mutex);
+         return s;
+       }
+    }
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+  return 0;
+}
+
+/* Return selector representing name; prefer a selector with non-NULL type */
+SEL
+sel_get_any_typed_uid (const char *name)
+{
+  struct objc_list *l;
+  sidx i;
+  SEL s = NULL;
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  i = (sidx) hash_value_for_key (__objc_selector_hash, name);
+  if (i == 0)
+    {
+      objc_mutex_unlock(__objc_runtime_mutex);
+      return 0;
+    }
+
+  for (l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i);
+       l; l = l->tail)
+    {
+      s = (SEL) l->head;
+      if (s->sel_types)
+       {
+           objc_mutex_unlock(__objc_runtime_mutex);
+           return s;
+       }
+    }
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+  return s;
+}
+
+/* return selector representing name */
+SEL
+sel_get_any_uid (const char *name)
+{
+  struct objc_list *l;
+  sidx i;
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  i = (sidx) hash_value_for_key (__objc_selector_hash, name);
+  if (soffset_decode (i) == 0)
+    {
+      objc_mutex_unlock(__objc_runtime_mutex);
+      return 0;
+    }
+
+  l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i);
+  objc_mutex_unlock(__objc_runtime_mutex);
+
+  if (l == 0)
+    return 0;
+
+  return (SEL)l->head;
+}
+
+/* return selector representing name */
+SEL
+sel_get_uid (const char *name)
+{
+  return sel_register_typed_name (name, 0);
+}
+
+/* Get name of selector.  If selector is unknown, the empty string "" 
+   is returned */ 
+const char*
+sel_get_name (SEL selector)
+{
+  const char *ret;
+
+  objc_mutex_lock(__objc_runtime_mutex);
+  if ((soffset_decode((sidx)selector->sel_id) > 0)
+      && (soffset_decode((sidx)selector->sel_id) <= __objc_selector_max_index))
+    ret = sarray_get_safe (__objc_selector_names, (sidx) selector->sel_id);
+  else
+    ret = 0;
+  objc_mutex_unlock(__objc_runtime_mutex);
+  return ret;
+}
+
+BOOL
+sel_is_mapped (SEL selector)
+{
+  unsigned int idx = soffset_decode ((sidx)selector->sel_id);
+  return ((idx > 0) && (idx <= __objc_selector_max_index));
+}
+
+
+const char*
+sel_get_type (SEL selector)
+{
+  if (selector)
+    return selector->sel_types;
+  else
+    return 0;
+}
+
+/* The uninstalled dispatch table */
+extern struct sarray* __objc_uninstalled_dtable;
+
+/* Store the passed selector name in the selector record and return its
+   selector value (value returned by sel_get_uid).
+   Assumes that the calling function has locked down __objc_runtime_mutex. */
+/* is_const parameter tells us if the name and types parameters
+   are really constant or not.  If YES then they are constant and
+   we can just store the pointers.  If NO then we need to copy
+   name and types because the pointers may disappear later on. */
+SEL
+__sel_register_typed_name (const char *name, const char *types, 
+                          struct objc_selector *orig, BOOL is_const)
+{
+  struct objc_selector* j;
+  sidx i;
+  struct objc_list *l;
+
+  i = (sidx) hash_value_for_key (__objc_selector_hash, name);
+  if (soffset_decode (i) != 0)
+    {
+      for (l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i);
+          l; l = l->tail)
+       {
+         SEL s = (SEL)l->head;
+         if (types == 0 || s->sel_types == 0)
+           {
+             if (s->sel_types == types)
+               {
+                 if (orig)
+                   {
+                     orig->sel_id = (void*)i;
+                     return orig;
+                   }
+                 else
+                   return s;
+               }
+           }
+         else if (!strcmp (s->sel_types, types))
+           {
+             if (orig)
+               {
+                 orig->sel_id = (void*)i;
+                 return orig;
+               }
+             else
+               return s;
+           }
+       }
+      if (orig)
+       j = orig;
+      else
+       j = objc_malloc (sizeof (struct objc_selector));
+
+      j->sel_id = (void*)i;
+      /* Can we use the pointer or must copy types?  Don't copy if NULL */
+      if ((is_const) || (types == 0))
+       j->sel_types = (const char*)types;
+      else {
+       j->sel_types = (char *) objc_malloc(strlen(types)+1);
+       strcpy((char *)j->sel_types, types);
+      }
+      l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i);
+    }
+  else
+    {
+      __objc_selector_max_index += 1;
+      i = soffset_encode(__objc_selector_max_index);
+      if (orig)
+       j = orig;
+      else
+       j = objc_malloc (sizeof (struct objc_selector));
+       
+      j->sel_id = (void*)i;
+      /* Can we use the pointer or must copy types?  Don't copy if NULL */
+      if ((is_const) || (types == 0))
+       j->sel_types = (const char*)types;
+      else {
+       j->sel_types = (char *) objc_malloc(strlen(types)+1);
+       strcpy((char *)j->sel_types, types);
+      }
+      l = 0;
+    }
+
+  DEBUG_PRINTF ("Record selector %s[%s] as: %ld\n", name, types, 
+               soffset_decode (i));
+  
+  {
+    int is_new = (l == 0);
+    const char *new_name;
+
+    /* Can we use the pointer or must copy name?  Don't copy if NULL */
+    if ((is_const) || (name == 0))
+      new_name = name;
+    else {
+      new_name = (char *) objc_malloc(strlen(name)+1);
+      strcpy((char *)new_name, name);
+    }
+
+    l = list_cons ((void*)j, l);
+    sarray_at_put_safe (__objc_selector_names, i, (void *) new_name);
+    sarray_at_put_safe (__objc_selector_array, i, (void *) l);
+    if (is_new)
+      hash_add (&__objc_selector_hash, (void *) new_name, (void *) i);
+  }
+
+  sarray_realloc(__objc_uninstalled_dtable, __objc_selector_max_index+1);
+
+  return (SEL) j;
+}
+
+SEL
+sel_register_name (const char *name)
+{
+  SEL ret;
+    
+  objc_mutex_lock(__objc_runtime_mutex);
+  /* Assume that name is not constant static memory and needs to be
+     copied before put into a runtime structure.  is_const == NO */
+  ret = __sel_register_typed_name (name, 0, 0, NO);
+  objc_mutex_unlock(__objc_runtime_mutex);
+  
+  return ret;
+}
+
+SEL
+sel_register_typed_name (const char *name, const char *type)
+{
+  SEL ret;
+    
+  objc_mutex_lock(__objc_runtime_mutex);
+  /* Assume that name and type are not constant static memory and need to
+     be copied before put into a runtime structure.  is_const == NO */
+  ret = __sel_register_typed_name (name, type, 0, NO);
+  objc_mutex_unlock(__objc_runtime_mutex);
+  
+  return ret;
+}
+
diff --git a/libobjc/sendmsg.c b/libobjc/sendmsg.c
new file mode 100644 (file)
index 0000000..17f8b97
--- /dev/null
@@ -0,0 +1,648 @@
+/* GNU Objective C Runtime message lookup 
+   Copyright (C) 1993, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+   Contributed by Kresten Krab Thorup
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include "../tconfig.h"
+#include "runtime.h"
+#include "sarray.h"
+#include "encoding.h"
+#include "runtime-info.h"
+
+/* this is how we hack STRUCT_VALUE to be 1 or 0 */
+#define gen_rtx(args...) 1
+#define gen_rtx_MEM(args...) 1
+#define rtx int
+
+#if !defined(STRUCT_VALUE) || STRUCT_VALUE == 0
+#define INVISIBLE_STRUCT_RETURN 1
+#else
+#define INVISIBLE_STRUCT_RETURN 0
+#endif
+
+/* The uninstalled dispatch table */
+struct sarray* __objc_uninstalled_dtable = 0;   /* !T:MUTEX */
+
+/* Send +initialize to class */
+static void __objc_send_initialize(Class);
+
+static void __objc_install_dispatch_table_for_class (Class);
+
+/* Forward declare some functions */
+static void __objc_init_install_dtable(id, SEL);
+
+/* Various forwarding functions that are used based upon the
+   return type for the selector.
+   __objc_block_forward for structures.
+   __objc_double_forward for floats/doubles.
+   __objc_word_forward for pointers or types that fit in registers.
+   */
+static double __objc_double_forward(id, SEL, ...);
+static id __objc_word_forward(id, SEL, ...);
+typedef struct { id many[8]; } __big;
+#if INVISIBLE_STRUCT_RETURN 
+static __big 
+#else
+static id
+#endif
+__objc_block_forward(id, SEL, ...);
+static Method_t search_for_method_in_hierarchy (Class class, SEL sel);
+Method_t search_for_method_in_list(MethodList_t list, SEL op);
+id nil_method(id, SEL, ...);
+
+/* Given a selector, return the proper forwarding implementation. */
+__inline__
+IMP
+__objc_get_forward_imp (SEL sel)
+{
+  const char *t = sel->sel_types;
+
+  if (t && (*t == '[' || *t == '(' || *t == '{')
+#ifdef OBJC_MAX_STRUCT_BY_VALUE
+    && objc_sizeof_type(t) > OBJC_MAX_STRUCT_BY_VALUE
+#endif
+      )
+    return (IMP)__objc_block_forward;
+  else if (t && (*t == 'f' || *t == 'd'))
+    return (IMP)__objc_double_forward;
+  else
+    return (IMP)__objc_word_forward;
+}
+
+/* Given a class and selector, return the selector's implementation.  */
+__inline__
+IMP
+get_imp (Class class, SEL sel)
+{
+  void* res = sarray_get_safe (class->dtable, (size_t) sel->sel_id);
+  if (res == 0)
+    {
+      /* Not a valid method */
+      if(class->dtable == __objc_uninstalled_dtable)
+       {
+         /* The dispatch table needs to be installed. */
+         objc_mutex_lock(__objc_runtime_mutex);
+         __objc_install_dispatch_table_for_class (class);
+         objc_mutex_unlock(__objc_runtime_mutex);
+         /* Call ourselves with the installed dispatch table
+            and get the real method */
+         res = get_imp(class, sel);
+       }
+      else
+       {
+         /* The dispatch table has been installed so the
+            method just doesn't exist for the class.
+            Return the forwarding implementation. */
+         res = __objc_get_forward_imp(sel);
+       }
+    }
+  return res;
+}
+
+/* Query if an object can respond to a selector, returns YES if the
+object implements the selector otherwise NO.  Does not check if the
+method can be forwarded. */
+__inline__
+BOOL
+__objc_responds_to (id object, SEL sel)
+{
+  void* res;
+
+  /* Install dispatch table if need be */
+  if (object->class_pointer->dtable == __objc_uninstalled_dtable)
+    {
+      objc_mutex_lock(__objc_runtime_mutex);
+      __objc_install_dispatch_table_for_class (object->class_pointer);
+      objc_mutex_unlock(__objc_runtime_mutex);
+    }
+
+  /* Get the method from the dispatch table */
+  res = sarray_get_safe (object->class_pointer->dtable, (size_t) sel->sel_id);
+  return (res != 0);
+}
+
+/* This is the lookup function.  All entries in the table are either a 
+   valid method *or* zero.  If zero then either the dispatch table
+   needs to be installed or it doesn't exist and forwarding is attempted. */
+__inline__
+IMP
+objc_msg_lookup(id receiver, SEL op)
+{
+  IMP result;
+  if(receiver)
+    {
+      result = sarray_get_safe (receiver->class_pointer->dtable, 
+                               (sidx)op->sel_id);
+      if (result == 0)
+       {
+         /* Not a valid method */
+         if(receiver->class_pointer->dtable == __objc_uninstalled_dtable)
+           {
+             /* The dispatch table needs to be installed.
+                This happens on the very first method call to the class. */
+             __objc_init_install_dtable(receiver, op);
+
+             /* Get real method for this in newly installed dtable */
+             result = get_imp(receiver->class_pointer, op);
+           }
+         else
+           {
+             /* The dispatch table has been installed so the
+                method just doesn't exist for the class.
+                Attempt to forward the method. */
+             result = __objc_get_forward_imp(op);
+           }
+       }
+      return result;
+    }
+  else
+    return nil_method;
+}
+
+IMP
+objc_msg_lookup_super (Super_t super, SEL sel)
+{
+  if (super->self)
+    return get_imp (super->class, sel);
+  else
+    return nil_method;
+}
+
+int method_get_sizeof_arguments (Method*);
+
+retval_t
+objc_msg_sendv(id object, SEL op, arglist_t arg_frame)
+{
+  Method* m = class_get_instance_method(object->class_pointer, op);
+  const char *type;
+  *((id*)method_get_first_argument (m, arg_frame, &type)) = object;
+  *((SEL*)method_get_next_argument (arg_frame, &type)) = op;
+  return __builtin_apply((apply_t)m->method_imp, 
+                        arg_frame,
+                        method_get_sizeof_arguments (m));
+}
+
+void
+__objc_init_dispatch_tables()
+{
+  __objc_uninstalled_dtable
+    = sarray_new(200, 0);
+}
+
+/* This function is called by objc_msg_lookup when the
+   dispatch table needs to be installed; thus it is called once
+   for each class, namely when the very first message is sent to it. */
+static void
+__objc_init_install_dtable(id receiver, SEL op)
+{
+  /* This may happen, if the programmer has taken the address of a 
+     method before the dtable was initialized... too bad for him! */
+  if(receiver->class_pointer->dtable != __objc_uninstalled_dtable)
+    return;
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  if(CLS_ISCLASS(receiver->class_pointer))
+    {
+      /* receiver is an ordinary object */
+      assert(CLS_ISCLASS(receiver->class_pointer));
+
+      /* install instance methods table */
+      __objc_install_dispatch_table_for_class (receiver->class_pointer);
+
+      /* call +initialize -- this will in turn install the factory 
+        dispatch table if not already done :-) */
+      __objc_send_initialize(receiver->class_pointer);
+    }
+  else
+    {
+      /* receiver is a class object */
+      assert(CLS_ISCLASS((Class)receiver));
+      assert(CLS_ISMETA(receiver->class_pointer));
+
+      /* Install real dtable for factory methods */
+      __objc_install_dispatch_table_for_class (receiver->class_pointer);
+
+      __objc_send_initialize((Class)receiver);
+    }
+  objc_mutex_unlock(__objc_runtime_mutex);
+}
+
+/* Install dummy table for class which causes the first message to
+   that class (or instances hereof) to be initialized properly */
+void
+__objc_install_premature_dtable(Class class)
+{
+  assert(__objc_uninstalled_dtable);
+  class->dtable = __objc_uninstalled_dtable;
+}   
+
+/* Send +initialize to class if not already done */
+static void
+__objc_send_initialize(Class class)
+{
+  /* This *must* be a class object */
+  assert(CLS_ISCLASS(class));
+  assert(!CLS_ISMETA(class));
+
+  if (!CLS_ISINITIALIZED(class))
+    {
+      CLS_SETINITIALIZED(class);
+      CLS_SETINITIALIZED(class->class_pointer);
+
+      /* Create the garbage collector type memory description */
+      __objc_generate_gc_type_description (class);
+
+      if(class->super_class)
+       __objc_send_initialize(class->super_class);
+
+      {
+       SEL          op = sel_register_name ("initialize");
+       IMP          imp = 0;
+        MethodList_t method_list = class->class_pointer->methods;
+
+        while (method_list) {
+         int i;
+          Method_t method;
+
+          for (i = 0; i< method_list->method_count; i++) {
+           method = &(method_list->method_list[i]);
+            if (method->method_name
+                && method->method_name->sel_id == op->sel_id) {
+             imp = method->method_imp;
+              break;
+            }
+          }
+
+          if (imp)
+            break;
+
+          method_list = method_list->method_next;
+
+       }
+       if (imp)
+           (*imp)((id)class, op);
+               
+      }
+    }
+}
+
+/* Walk on the methods list of class and install the methods in the reverse
+   order of the lists. Since methods added by categories are before the methods
+   of class in the methods list, this allows categories to substitute methods
+   declared in class. However if more than one category replaces the same
+   method nothing is guaranteed about what method will be used.
+   Assumes that __objc_runtime_mutex is locked down. */
+static void
+__objc_install_methods_in_dtable (Class class, MethodList_t method_list)
+{
+  int i;
+
+  if (!method_list)
+    return;
+
+  if (method_list->method_next)
+    __objc_install_methods_in_dtable (class, method_list->method_next);
+
+  for (i = 0; i < method_list->method_count; i++)
+    {
+      Method_t method = &(method_list->method_list[i]);
+      sarray_at_put_safe (class->dtable,
+                         (sidx) method->method_name->sel_id,
+                         method->method_imp);
+    }
+}
+
+/* Assumes that __objc_runtime_mutex is locked down. */
+static void
+__objc_install_dispatch_table_for_class (Class class)
+{
+  Class super;
+
+  /* If the class has not yet had its class links resolved, we must 
+     re-compute all class links */
+  if(!CLS_ISRESOLV(class))
+    __objc_resolve_class_links();
+
+  super = class->super_class;
+
+  if (super != 0 && (super->dtable == __objc_uninstalled_dtable))
+    __objc_install_dispatch_table_for_class (super);
+
+  /* Allocate dtable if necessary */
+  if (super == 0)
+    {
+      objc_mutex_lock(__objc_runtime_mutex);
+      class->dtable = sarray_new (__objc_selector_max_index, 0);
+      objc_mutex_unlock(__objc_runtime_mutex);
+    }
+  else
+    class->dtable = sarray_lazy_copy (super->dtable);
+
+  __objc_install_methods_in_dtable (class, class->methods);
+}
+
+void
+__objc_update_dispatch_table_for_class (Class class)
+{
+  Class next;
+  struct sarray *arr;
+
+  /* not yet installed -- skip it */
+  if (class->dtable == __objc_uninstalled_dtable) 
+    return;
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  arr = class->dtable;
+  __objc_install_premature_dtable (class); /* someone might require it... */
+  sarray_free (arr);                      /* release memory */
+
+  /* could have been lazy... */
+  __objc_install_dispatch_table_for_class (class); 
+
+  if (class->subclass_list)    /* Traverse subclasses */
+    for (next = class->subclass_list; next; next = next->sibling_class)
+      __objc_update_dispatch_table_for_class (next);
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+}
+
+
+/* This function adds a method list to a class.  This function is
+   typically called by another function specific to the run-time.  As
+   such this function does not worry about thread safe issues.
+
+   This one is only called for categories. Class objects have their
+   methods installed right away, and their selectors are made into
+   SEL's by the function __objc_register_selectors_from_class. */ 
+void
+class_add_method_list (Class class, MethodList_t list)
+{
+  int i;
+
+  /* Passing of a linked list is not allowed.  Do multiple calls.  */
+  assert (!list->method_next);
+
+  /* Check for duplicates.  */
+  for (i = 0; i < list->method_count; ++i)
+    {
+      Method_t method = &list->method_list[i];
+
+      if (method->method_name)  /* Sometimes these are NULL */
+       {
+         /* This is where selector names are transmogrified to SEL's */
+         method->method_name = 
+           sel_register_typed_name ((const char*)method->method_name,
+                                    method->method_types);
+       }
+    }
+
+  /* Add the methods to the class's method list.  */
+  list->method_next = class->methods;
+  class->methods = list;
+
+  /* Update the dispatch table of class */
+  __objc_update_dispatch_table_for_class (class);
+}
+
+Method_t
+class_get_instance_method(Class class, SEL op)
+{
+  return search_for_method_in_hierarchy(class, op);
+}
+
+Method_t
+class_get_class_method(MetaClass class, SEL op)
+{
+  return search_for_method_in_hierarchy(class, op);
+}
+
+
+/* Search for a method starting from the current class up its hierarchy.
+   Return a pointer to the method's method structure if found.  NULL
+   otherwise. */   
+
+static Method_t
+search_for_method_in_hierarchy (Class cls, SEL sel)
+{
+  Method_t method = NULL;
+  Class class;
+
+  if (! sel_is_mapped (sel))
+    return NULL;
+
+  /* Scan the method list of the class.  If the method isn't found in the
+     list then step to its super class. */
+  for (class = cls; ((! method) && class); class = class->super_class)
+    method = search_for_method_in_list (class->methods, sel);
+
+  return method;
+}
+
+
+
+/* Given a linked list of method and a method's name.  Search for the named
+   method's method structure.  Return a pointer to the method's method
+   structure if found.  NULL otherwise. */  
+Method_t
+search_for_method_in_list (MethodList_t list, SEL op)
+{
+  MethodList_t method_list = list;
+
+  if (! sel_is_mapped (op))
+    return NULL;
+
+  /* If not found then we'll search the list.  */
+  while (method_list)
+    {
+      int i;
+
+      /* Search the method list.  */
+      for (i = 0; i < method_list->method_count; ++i)
+        {
+          Method_t method = &method_list->method_list[i];
+
+          if (method->method_name)
+            if (method->method_name->sel_id == op->sel_id)
+              return method;
+        }
+
+      /* The method wasn't found.  Follow the link to the next list of
+         methods.  */
+      method_list = method_list->method_next;
+    }
+
+  return NULL;
+}
+
+static retval_t __objc_forward (id object, SEL sel, arglist_t args);
+
+/* Forwarding pointers/integers through the normal registers */
+static id
+__objc_word_forward (id rcv, SEL op, ...)
+{
+  void *args, *res;
+
+  args = __builtin_apply_args ();
+  res = __objc_forward (rcv, op, args);
+  if (res)
+    __builtin_return (res);
+  else
+    return res;
+}
+
+/* Specific routine for forwarding floats/double because of
+   architectural differences on some processors.  i386s for
+   example which uses a floating point stack versus general
+   registers for floating point numbers.  This forward routine 
+   makes sure that GCC restores the proper return values */
+static double
+__objc_double_forward (id rcv, SEL op, ...)
+{
+  void *args, *res;
+
+  args = __builtin_apply_args ();
+  res = __objc_forward (rcv, op, args);
+  __builtin_return (res);
+}
+
+#if INVISIBLE_STRUCT_RETURN
+static __big
+#else
+static id
+#endif
+__objc_block_forward (id rcv, SEL op, ...)
+{
+  void *args, *res;
+
+  args = __builtin_apply_args ();
+  res = __objc_forward (rcv, op, args);
+  if (res)
+    __builtin_return (res);
+  else
+#if INVISIBLE_STRUCT_RETURN
+    return (__big) {{0, 0, 0, 0, 0, 0, 0, 0}};
+#else
+    return nil;
+#endif
+}
+
+
+/* This function is installed in the dispatch table for all methods which are
+   not implemented.  Thus, it is called when a selector is not recognized. */
+static retval_t
+__objc_forward (id object, SEL sel, arglist_t args)
+{
+  IMP imp;
+  static SEL frwd_sel = 0;                      /* !T:SAFE2 */
+  SEL err_sel;
+
+  /* first try if the object understands forward:: */
+  if (!frwd_sel)
+    frwd_sel = sel_get_any_uid("forward::");
+
+  if (__objc_responds_to (object, frwd_sel))
+    {
+      imp = get_imp(object->class_pointer, frwd_sel);
+      return (*imp)(object, frwd_sel, sel, args);
+    }
+
+  /* If the object recognizes the doesNotRecognize: method then we're going
+     to send it. */
+  err_sel = sel_get_any_uid ("doesNotRecognize:");
+  if (__objc_responds_to (object, err_sel))
+    {
+      imp = get_imp (object->class_pointer, err_sel);
+      return (*imp) (object, err_sel, sel);
+    }
+  
+  /* The object doesn't recognize the method.  Check for responding to
+     error:.  If it does then sent it. */
+  {
+    size_t strlen (const char*);
+    char msg[256 + strlen ((const char*)sel_get_name (sel))
+             + strlen ((const char*)object->class_pointer->name)];
+
+    sprintf (msg, "(%s) %s does not recognize %s",
+            (CLS_ISMETA(object->class_pointer)
+             ? "class"
+             : "instance" ),
+             object->class_pointer->name, sel_get_name (sel));
+
+    err_sel = sel_get_any_uid ("error:");
+    if (__objc_responds_to (object, err_sel))
+      {
+       imp = get_imp (object->class_pointer, err_sel);
+       return (*imp) (object, sel_get_any_uid ("error:"), msg);
+      }
+
+    /* The object doesn't respond to doesNotRecognize: or error:;  Therefore,
+       a default action is taken. */
+    objc_error (object, OBJC_ERR_UNIMPLEMENTED, "%s\n", msg);
+
+    return 0;
+  }
+}
+
+void
+__objc_print_dtable_stats()
+{
+  int total = 0;
+
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  printf("memory usage: (%s)\n",
+#ifdef OBJC_SPARSE2
+        "2-level sparse arrays"
+#else
+        "3-level sparse arrays"
+#endif
+        );
+
+  printf("arrays: %d = %ld bytes\n", narrays, 
+        (long)narrays*sizeof(struct sarray));
+  total += narrays*sizeof(struct sarray);
+  printf("buckets: %d = %ld bytes\n", nbuckets, 
+        (long)nbuckets*sizeof(struct sbucket));
+  total += nbuckets*sizeof(struct sbucket);
+
+  printf("idxtables: %d = %ld bytes\n", idxsize, (long)idxsize*sizeof(void*));
+  total += idxsize*sizeof(void*);
+  printf("-----------------------------------\n");
+  printf("total: %d bytes\n", total);
+  printf("===================================\n");
+
+  objc_mutex_unlock(__objc_runtime_mutex);
+}
+
+/* Returns the uninstalled dispatch table indicator.
+ If a class' dispatch table points to __objc_uninstalled_dtable
+ then that means it needs its dispatch table to be installed. */
+__inline__
+struct sarray* 
+objc_get_uninstalled_dtable()
+{
+  return __objc_uninstalled_dtable;
+}
diff --git a/libobjc/thr-dce.c b/libobjc/thr-dce.c
new file mode 100644 (file)
index 0000000..0f7063b
--- /dev/null
@@ -0,0 +1,281 @@
+/* GNU Objective C Runtime Thread Interface
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <pthread.h>
+#include <objc/thr.h>
+#include "runtime.h"
+
+/* Key structure for maintaining thread specific storage */
+static pthread_key_t _objc_thread_storage;
+
+/* Backend initialization functions */
+
+/* Initialize the threads subsystem. */
+int
+__objc_init_thread_system(void)
+{
+  /* Initialize the thread storage key */
+  return pthread_keycreate(&_objc_thread_storage, NULL);
+}
+
+/* Close the threads subsystem. */
+int
+__objc_close_thread_system(void)
+{
+  /* Destroy the thread storage key */
+  /* Not implemented yet */
+  /* return pthread_key_delete(&_objc_thread_storage); */
+  return 0;
+}
+
+/* Backend thread functions */
+
+/* Create a new thread of execution. */
+objc_thread_t
+__objc_thread_detach(void (*func)(void *arg), void *arg)
+{
+  objc_thread_t thread_id;
+  pthread_t new_thread_handle;
+
+  if (pthread_create(&new_thread_handle, pthread_attr_default,
+                    (void *)func, arg) == 0)
+    {
+      /* ??? May not work! (64bit) */
+      thread_id = *(objc_thread_t *)&new_thread_handle; 
+      pthread_detach(&new_thread_handle);     /* Fully detach thread.     */
+    }
+  else
+    thread_id = NULL;
+  
+  return thread_id;
+}
+
+/* Set the current thread's priority. */
+int
+__objc_thread_set_priority(int priority)
+{
+  int sys_priority = 0;
+
+  switch (priority)
+    {
+    case OBJC_THREAD_INTERACTIVE_PRIORITY:
+      sys_priority = (PRI_FG_MIN_NP + PRI_FG_MAX_NP) / 2;
+      break;
+    default:
+    case OBJC_THREAD_BACKGROUND_PRIORITY:
+      sys_priority = (PRI_BG_MIN_NP + PRI_BG_MAX_NP) / 2;
+      break;
+    case OBJC_THREAD_LOW_PRIORITY:
+      sys_priority = (PRI_BG_MIN_NP + PRI_BG_MAX_NP) / 2;
+      break;
+    }
+    
+  /* Change the priority. */
+  if (pthread_setprio(pthread_self(), sys_priority) >= 0)
+    return 0;
+  else
+    /* Failed */
+    return -1;
+}
+
+/* Return the current thread's priority. */
+int
+__objc_thread_get_priority(void)
+{
+  int sys_priority;
+    
+  if ((sys_priority = pthread_getprio(pthread_self())) >= 0) {
+    if (sys_priority >= PRI_FG_MIN_NP && sys_priority <= PRI_FG_MAX_NP)
+      return OBJC_THREAD_INTERACTIVE_PRIORITY;
+    if (sys_priority >= PRI_BG_MIN_NP && sys_priority <= PRI_BG_MAX_NP)
+      return OBJC_THREAD_BACKGROUND_PRIORITY;
+    return OBJC_THREAD_LOW_PRIORITY;
+  }
+
+  /* Failed */
+  return -1;
+}
+
+/* Yield our process time to another thread. */
+void
+__objc_thread_yield(void)
+{
+  pthread_yield();
+}
+
+/* Terminate the current thread. */
+int
+__objc_thread_exit(void)
+{
+  /* exit the thread */
+  pthread_exit(&__objc_thread_exit_status);
+
+  /* Failed if we reached here */
+  return -1;
+}
+
+/* Returns an integer value which uniquely describes a thread. */
+objc_thread_t
+__objc_thread_id(void)
+{
+  pthread_t self = pthread_self();
+
+  return (objc_thread_t) pthread_getunique_np (&self);
+}
+
+/* Sets the thread's local storage pointer. */
+int
+__objc_thread_set_data(void *value)
+{
+  return pthread_setspecific(_objc_thread_storage, value);
+}
+
+/* Returns the thread's local storage pointer. */
+void *
+__objc_thread_get_data(void)
+{
+  void *value = NULL;
+
+  if ( !(pthread_getspecific(_objc_thread_storage, &value)) )
+    return value;
+
+  return NULL;
+}
+
+/* Backend mutex functions */
+
+/* Allocate a mutex. */
+int
+__objc_mutex_allocate(objc_mutex_t mutex)
+{
+  if (pthread_mutex_init((pthread_mutex_t *)(&(mutex->backend)), 
+                        pthread_mutexattr_default))
+    return -1;
+  else
+    return 0;
+}
+
+/* Deallocate a mutex. */
+int
+__objc_mutex_deallocate(objc_mutex_t mutex)
+{
+  if (pthread_mutex_destroy((pthread_mutex_t *)(&(mutex->backend))))
+    return -1;
+  else
+    return 0;
+}
+
+/* Grab a lock on a mutex. */
+int
+__objc_mutex_lock(objc_mutex_t mutex)
+{
+  return pthread_mutex_lock((pthread_mutex_t *)(&(mutex->backend)));
+}
+
+/* Try to grab a lock on a mutex. */
+int
+__objc_mutex_trylock(objc_mutex_t mutex)
+{
+  if (pthread_mutex_trylock((pthread_mutex_t *)(&(mutex->backend))) != 1)
+    return -1;
+  else
+    return 0;
+}
+
+/* Unlock the mutex */
+int
+__objc_mutex_unlock(objc_mutex_t mutex)
+{
+  return pthread_mutex_unlock((pthread_mutex_t *)(&(mutex->backend)));
+}
+
+/* Backend condition mutex functions */
+
+/* Allocate a condition. */
+int
+__objc_condition_allocate(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+
+  /*
+  if (pthread_cond_init((pthread_cond_t *)(&(condition->backend)), NULL))
+    return -1;
+  else
+    return 0;
+    */
+}
+
+/* Deallocate a condition. */
+int
+__objc_condition_deallocate(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+
+  /*
+  return pthread_cond_destroy((pthread_cond_t *)(&(condition->backend)));
+  */
+}
+
+/* Wait on the condition */
+int
+__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
+{
+  /* Unimplemented. */
+  return -1;
+
+  /*
+  return pthread_cond_wait((pthread_cond_t *)(&(condition->backend)),
+                          (pthread_mutex_t *)(&(mutex->backend)));
+                          */
+}
+
+/* Wake up all threads waiting on this condition. */
+int
+__objc_condition_broadcast(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+
+  /*
+  return pthread_cond_broadcast((pthread_cond_t *)(&(condition->backend)));
+  */
+}
+
+/* Wake up one thread waiting on this condition. */
+int
+__objc_condition_signal(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+
+  /*
+  return pthread_cond_signal((pthread_cond_t *)(&(condition->backend)));
+  */
+}
+
+/* End of File */
diff --git a/libobjc/thr-decosf1.c b/libobjc/thr-decosf1.c
new file mode 100644 (file)
index 0000000..0f7063b
--- /dev/null
@@ -0,0 +1,281 @@
+/* GNU Objective C Runtime Thread Interface
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <pthread.h>
+#include <objc/thr.h>
+#include "runtime.h"
+
+/* Key structure for maintaining thread specific storage */
+static pthread_key_t _objc_thread_storage;
+
+/* Backend initialization functions */
+
+/* Initialize the threads subsystem. */
+int
+__objc_init_thread_system(void)
+{
+  /* Initialize the thread storage key */
+  return pthread_keycreate(&_objc_thread_storage, NULL);
+}
+
+/* Close the threads subsystem. */
+int
+__objc_close_thread_system(void)
+{
+  /* Destroy the thread storage key */
+  /* Not implemented yet */
+  /* return pthread_key_delete(&_objc_thread_storage); */
+  return 0;
+}
+
+/* Backend thread functions */
+
+/* Create a new thread of execution. */
+objc_thread_t
+__objc_thread_detach(void (*func)(void *arg), void *arg)
+{
+  objc_thread_t thread_id;
+  pthread_t new_thread_handle;
+
+  if (pthread_create(&new_thread_handle, pthread_attr_default,
+                    (void *)func, arg) == 0)
+    {
+      /* ??? May not work! (64bit) */
+      thread_id = *(objc_thread_t *)&new_thread_handle; 
+      pthread_detach(&new_thread_handle);     /* Fully detach thread.     */
+    }
+  else
+    thread_id = NULL;
+  
+  return thread_id;
+}
+
+/* Set the current thread's priority. */
+int
+__objc_thread_set_priority(int priority)
+{
+  int sys_priority = 0;
+
+  switch (priority)
+    {
+    case OBJC_THREAD_INTERACTIVE_PRIORITY:
+      sys_priority = (PRI_FG_MIN_NP + PRI_FG_MAX_NP) / 2;
+      break;
+    default:
+    case OBJC_THREAD_BACKGROUND_PRIORITY:
+      sys_priority = (PRI_BG_MIN_NP + PRI_BG_MAX_NP) / 2;
+      break;
+    case OBJC_THREAD_LOW_PRIORITY:
+      sys_priority = (PRI_BG_MIN_NP + PRI_BG_MAX_NP) / 2;
+      break;
+    }
+    
+  /* Change the priority. */
+  if (pthread_setprio(pthread_self(), sys_priority) >= 0)
+    return 0;
+  else
+    /* Failed */
+    return -1;
+}
+
+/* Return the current thread's priority. */
+int
+__objc_thread_get_priority(void)
+{
+  int sys_priority;
+    
+  if ((sys_priority = pthread_getprio(pthread_self())) >= 0) {
+    if (sys_priority >= PRI_FG_MIN_NP && sys_priority <= PRI_FG_MAX_NP)
+      return OBJC_THREAD_INTERACTIVE_PRIORITY;
+    if (sys_priority >= PRI_BG_MIN_NP && sys_priority <= PRI_BG_MAX_NP)
+      return OBJC_THREAD_BACKGROUND_PRIORITY;
+    return OBJC_THREAD_LOW_PRIORITY;
+  }
+
+  /* Failed */
+  return -1;
+}
+
+/* Yield our process time to another thread. */
+void
+__objc_thread_yield(void)
+{
+  pthread_yield();
+}
+
+/* Terminate the current thread. */
+int
+__objc_thread_exit(void)
+{
+  /* exit the thread */
+  pthread_exit(&__objc_thread_exit_status);
+
+  /* Failed if we reached here */
+  return -1;
+}
+
+/* Returns an integer value which uniquely describes a thread. */
+objc_thread_t
+__objc_thread_id(void)
+{
+  pthread_t self = pthread_self();
+
+  return (objc_thread_t) pthread_getunique_np (&self);
+}
+
+/* Sets the thread's local storage pointer. */
+int
+__objc_thread_set_data(void *value)
+{
+  return pthread_setspecific(_objc_thread_storage, value);
+}
+
+/* Returns the thread's local storage pointer. */
+void *
+__objc_thread_get_data(void)
+{
+  void *value = NULL;
+
+  if ( !(pthread_getspecific(_objc_thread_storage, &value)) )
+    return value;
+
+  return NULL;
+}
+
+/* Backend mutex functions */
+
+/* Allocate a mutex. */
+int
+__objc_mutex_allocate(objc_mutex_t mutex)
+{
+  if (pthread_mutex_init((pthread_mutex_t *)(&(mutex->backend)), 
+                        pthread_mutexattr_default))
+    return -1;
+  else
+    return 0;
+}
+
+/* Deallocate a mutex. */
+int
+__objc_mutex_deallocate(objc_mutex_t mutex)
+{
+  if (pthread_mutex_destroy((pthread_mutex_t *)(&(mutex->backend))))
+    return -1;
+  else
+    return 0;
+}
+
+/* Grab a lock on a mutex. */
+int
+__objc_mutex_lock(objc_mutex_t mutex)
+{
+  return pthread_mutex_lock((pthread_mutex_t *)(&(mutex->backend)));
+}
+
+/* Try to grab a lock on a mutex. */
+int
+__objc_mutex_trylock(objc_mutex_t mutex)
+{
+  if (pthread_mutex_trylock((pthread_mutex_t *)(&(mutex->backend))) != 1)
+    return -1;
+  else
+    return 0;
+}
+
+/* Unlock the mutex */
+int
+__objc_mutex_unlock(objc_mutex_t mutex)
+{
+  return pthread_mutex_unlock((pthread_mutex_t *)(&(mutex->backend)));
+}
+
+/* Backend condition mutex functions */
+
+/* Allocate a condition. */
+int
+__objc_condition_allocate(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+
+  /*
+  if (pthread_cond_init((pthread_cond_t *)(&(condition->backend)), NULL))
+    return -1;
+  else
+    return 0;
+    */
+}
+
+/* Deallocate a condition. */
+int
+__objc_condition_deallocate(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+
+  /*
+  return pthread_cond_destroy((pthread_cond_t *)(&(condition->backend)));
+  */
+}
+
+/* Wait on the condition */
+int
+__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
+{
+  /* Unimplemented. */
+  return -1;
+
+  /*
+  return pthread_cond_wait((pthread_cond_t *)(&(condition->backend)),
+                          (pthread_mutex_t *)(&(mutex->backend)));
+                          */
+}
+
+/* Wake up all threads waiting on this condition. */
+int
+__objc_condition_broadcast(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+
+  /*
+  return pthread_cond_broadcast((pthread_cond_t *)(&(condition->backend)));
+  */
+}
+
+/* Wake up one thread waiting on this condition. */
+int
+__objc_condition_signal(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+
+  /*
+  return pthread_cond_signal((pthread_cond_t *)(&(condition->backend)));
+  */
+}
+
+/* End of File */
diff --git a/libobjc/thr-irix.c b/libobjc/thr-irix.c
new file mode 100644 (file)
index 0000000..528a3e3
--- /dev/null
@@ -0,0 +1,235 @@
+/* GNU Objective C Runtime Thread Interface - SGI IRIX Implementation
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/sysmp.h>
+#include <sys/prctl.h>
+#include <ulocks.h>
+#include <objc/thr.h>
+#include "runtime.h"
+
+/* Key structure for maintaining thread specific storage */
+static void * __objc_shared_arena_handle = NULL;
+
+/* Backend initialization functions */
+
+/* Initialize the threads subsystem. */
+int
+__objc_init_thread_system(void)
+{
+  /* Name of IRIX arena. */
+  char arena_name[64];
+
+  DEBUG_PRINTF("__objc_init_thread_system\n");
+
+  /* Construct a temporary name for arena. */
+  sprintf(arena_name, "/usr/tmp/objc_%05u", (unsigned)getpid());
+
+  /* Up to 256 threads.  Arena only for threads. */
+  usconfig(CONF_INITUSERS, 256);
+  usconfig(CONF_ARENATYPE, US_SHAREDONLY);
+
+  /* Initialize the arena */
+  if (!(__objc_shared_arena_handle = usinit(arena_name)))
+    /* Failed */
+    return -1;
+
+  return 0;
+}
+
+/* Close the threads subsystem. */
+int
+__objc_close_thread_system(void)
+{
+  return 0;
+}
+
+/* Backend thread functions */
+
+/* Create a new thread of execution. */
+objc_thread_t
+__objc_thread_detach(void (*func)(void *arg), void *arg)
+{
+  objc_thread_t thread_id;
+  int sys_id;
+
+  if ((sys_id = sproc((void *)func, PR_SALL, arg)) >= 0)
+    thread_id = (objc_thread_t)sys_id;
+  else
+    thread_id = NULL;
+  
+  return thread_id;
+}
+
+/* Set the current thread's priority. */
+int
+__objc_thread_set_priority(int priority)
+{
+  /* Not implemented yet */
+  return -1;
+}
+
+/* Return the current thread's priority. */
+int
+__objc_thread_get_priority(void)
+{
+  /* Not implemented yet */
+  return OBJC_THREAD_INTERACTIVE_PRIORITY;
+}
+
+/* Yield our process time to another thread. */
+void
+__objc_thread_yield(void)
+{
+  sginap(0);
+}
+
+/* Terminate the current thread. */
+int
+__objc_thread_exit(void)
+{
+  /* IRIX only has exit. */
+  exit(__objc_thread_exit_status);
+
+  /* Failed if we reached here */
+  return -1;
+}
+
+/* Returns an integer value which uniquely describes a thread. */
+objc_thread_t
+__objc_thread_id(void)
+{
+  /* Threads are processes. */
+  return (objc_thread_t)get_pid();
+}
+
+/* Sets the thread's local storage pointer. */
+int
+__objc_thread_set_data(void *value)
+{
+  *((void **)&PRDA->usr_prda) = value;
+  return 0;
+}
+
+/* Returns the thread's local storage pointer. */
+void *
+__objc_thread_get_data(void)
+{
+  return *((void **)&PRDA->usr_prda);
+}
+
+/* Backend mutex functions */
+
+/* Allocate a mutex. */
+int
+__objc_mutex_allocate(objc_mutex_t mutex)
+{
+  if (!( (ulock_t)(mutex->backend) = usnewlock(__objc_shared_arena_handle) ))
+    return -1;
+  else
+    return 0;
+}
+
+/* Deallocate a mutex. */
+int
+__objc_mutex_deallocate(objc_mutex_t mutex)
+{
+  usfreelock((ulock_t)(mutex->backend), __objc_shared_arena_handle);
+  return 0;
+}
+
+/* Grab a lock on a mutex. */
+int
+__objc_mutex_lock(objc_mutex_t mutex)
+{
+  if (ussetlock((ulock_t)(mutex->backend)) == 0)
+    return -1;
+  else
+    return 0;
+}
+
+/* Try to grab a lock on a mutex. */
+int
+__objc_mutex_trylock(objc_mutex_t mutex)
+{
+  if (ustestlock((ulock_t)(mutex->backend)) == 0)
+    return -1;
+  else
+    return 0;
+}
+
+/* Unlock the mutex */
+int
+__objc_mutex_unlock(objc_mutex_t mutex)
+{
+  usunsetlock((ulock_t)(mutex->backend));
+  return 0;
+}
+
+/* Backend condition mutex functions */
+
+/* Allocate a condition. */
+int
+__objc_condition_allocate(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* Deallocate a condition. */
+int
+__objc_condition_deallocate(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* Wait on the condition */
+int
+__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* Wake up all threads waiting on this condition. */
+int
+__objc_condition_broadcast(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* Wake up one thread waiting on this condition. */
+int
+__objc_condition_signal(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* End of File */
diff --git a/libobjc/thr-mach.c b/libobjc/thr-mach.c
new file mode 100644 (file)
index 0000000..44af0c1
--- /dev/null
@@ -0,0 +1,312 @@
+/* GNU Objective C Runtime Thread Implementation
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
+   Modified for Mach threads by Bill Bumgarner <bbum@friday.com>
+   Condition functions added by Mircea Oancea <mircea@first.elcom.pub.ro>
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <mach/mach.h>
+#include <mach/cthreads.h>
+#include <objc/thr.h>
+#include "runtime.h"
+
+/*
+  Obtain the maximum thread priority that can set for t.  Under the
+  mach threading model, it is possible for the developer to adjust the
+  maximum priority downward only-- cannot be raised without superuser
+  privileges.  Once lowered, it cannot be raised.
+  */
+static int __mach_get_max_thread_priority(cthread_t t, int *base)
+{
+  thread_t threadP;
+  kern_return_t error;
+  struct thread_sched_info info;
+  unsigned int info_count=THREAD_SCHED_INFO_COUNT;
+    
+  if (t == NULL)
+    return -1;
+
+  threadP  = cthread_thread(t);        /* get thread underlying */
+
+  error=thread_info(threadP, THREAD_SCHED_INFO, 
+                   (thread_info_t)&info, &info_count);
+
+  if (error != KERN_SUCCESS)
+    return -1;
+
+  if (base != NULL)
+    *base = info.base_priority;
+
+  return info.max_priority;
+}
+       
+/* Backend initialization functions */
+
+/* Initialize the threads subsystem. */
+int
+__objc_init_thread_system(void)
+{
+  return 0;
+}
+
+/* Close the threads subsystem. */
+int
+__objc_close_thread_system(void)
+{
+  return 0;
+}
+
+/* Backend thread functions */
+
+/* Create a new thread of execution. */
+objc_thread_t
+__objc_thread_detach(void (*func)(void *arg), void *arg)
+{
+  objc_thread_t thread_id;
+  cthread_t new_thread_handle;
+
+  /* create thread */
+  new_thread_handle = cthread_fork((cthread_fn_t)func, arg);
+
+  if(new_thread_handle)
+    {
+      /* this is not terribly portable */
+      thread_id = *(objc_thread_t *)&new_thread_handle; 
+      cthread_detach(new_thread_handle);
+    }
+  else
+    thread_id = NULL;
+  
+  return thread_id;
+}
+
+/* Set the current thread's priority. */
+int
+__objc_thread_set_priority(int priority)
+{
+  objc_thread_t *t = objc_thread_id();
+  cthread_t cT = (cthread_t) t; 
+  int maxPriority = __mach_get_max_thread_priority(cT, NULL);
+  int sys_priority = 0;
+
+  if (maxPriority == -1)
+    return -1;
+
+  switch (priority)
+    {
+    case OBJC_THREAD_INTERACTIVE_PRIORITY:
+      sys_priority = maxPriority;
+      break;
+    case OBJC_THREAD_BACKGROUND_PRIORITY:
+      sys_priority = (maxPriority * 2) / 3;
+      break;
+    case OBJC_THREAD_LOW_PRIORITY:
+      sys_priority = maxPriority / 3;
+      break;
+    default:
+      return -1;
+    }
+
+  if (sys_priority == 0)
+    return -1;
+
+  /* Change the priority */
+  if (cthread_priority(cT, sys_priority, 0) == KERN_SUCCESS)
+    return 0;
+  else
+    return -1;
+}
+
+/* Return the current thread's priority. */
+int
+__objc_thread_get_priority(void)
+{
+  objc_thread_t *t = objc_thread_id();
+  cthread_t cT = (cthread_t) t; /* see objc_thread_id() */
+  int basePriority;
+  int maxPriority;
+  int sys_priority = 0;
+
+  int interactiveT, backgroundT, lowT; /* thresholds */
+
+  maxPriority = __mach_get_max_thread_priority(cT, &basePriority);
+
+  if(maxPriority == -1)
+    return -1;
+
+  if (basePriority > ( (maxPriority * 2) / 3))
+    return OBJC_THREAD_INTERACTIVE_PRIORITY;
+
+  if (basePriority > ( maxPriority / 3))
+    return OBJC_THREAD_BACKGROUND_PRIORITY;
+
+  return OBJC_THREAD_LOW_PRIORITY;
+}
+
+/* Yield our process time to another thread. */
+void
+__objc_thread_yield(void)
+{
+  cthread_yield();
+}
+
+/* Terminate the current thread. */
+int
+__objc_thread_exit(void)
+{
+  /* exit the thread */
+  cthread_exit(&__objc_thread_exit_status);
+
+  /* Failed if we reached here */
+  return -1;
+}
+
+/* Returns an integer value which uniquely describes a thread. */
+objc_thread_t
+__objc_thread_id(void)
+{
+  cthread_t self = cthread_self();
+
+  return *(objc_thread_t *)&self;
+}
+
+/* Sets the thread's local storage pointer. */
+int
+__objc_thread_set_data(void *value)
+{
+  cthread_set_data(cthread_self(), (any_t) value);
+  return 0;
+}
+
+/* Returns the thread's local storage pointer. */
+void *
+__objc_thread_get_data(void)
+{
+  return (void *) cthread_data(cthread_self());
+}
+
+/* Backend mutex functions */
+
+/* Allocate a mutex. */
+int
+__objc_mutex_allocate(objc_mutex_t mutex)
+{
+  int err = 0;
+  mutex->backend = objc_malloc(sizeof(struct mutex));
+
+  err = mutex_init((mutex_t)(mutex->backend));
+
+  if (err != 0)
+    {
+      objc_free(mutex->backend);
+      return -1;
+    }
+  else
+    return 0;
+}
+
+/* Deallocate a mutex. */
+int
+__objc_mutex_deallocate(objc_mutex_t mutex)
+{
+  mutex_clear((mutex_t)(mutex->backend));
+
+  objc_free(mutex->backend);
+  mutex->backend = NULL;
+  return 0;
+}
+
+/* Grab a lock on a mutex. */
+int
+__objc_mutex_lock(objc_mutex_t mutex)
+{
+  mutex_lock((mutex_t)(mutex->backend));
+  return 0;
+}
+
+/* Try to grab a lock on a mutex. */
+int
+__objc_mutex_trylock(objc_mutex_t mutex)
+{
+  if (mutex_try_lock((mutex_t)(mutex->backend)) == 0)
+    return -1;
+  else
+    return 0;
+}
+
+/* Unlock the mutex */
+int
+__objc_mutex_unlock(objc_mutex_t mutex)
+{
+  mutex_unlock((mutex_t)(mutex->backend));
+  return 0;
+}
+
+/* Backend condition mutex functions */
+
+/* Allocate a condition. */
+int
+__objc_condition_allocate(objc_condition_t condition)
+{
+  condition->backend = objc_malloc(sizeof(struct condition));
+  condition_init((condition_t)(condition->backend));
+  return 0;
+}
+
+/* Deallocate a condition. */
+int
+__objc_condition_deallocate(objc_condition_t condition)
+{
+  condition_clear((condition_t)(condition->backend));
+  objc_free(condition->backend);
+  condition->backend = NULL;
+  return 0;
+}
+
+/* Wait on the condition */
+int
+__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
+{
+  condition_wait((condition_t)(condition->backend),
+                (mutex_t)(mutex->backend));
+  return 0;
+}
+
+/* Wake up all threads waiting on this condition. */
+int
+__objc_condition_broadcast(objc_condition_t condition)
+{
+  condition_broadcast((condition_t)(condition->backend));
+  return 0;
+}
+
+/* Wake up one thread waiting on this condition. */
+int
+__objc_condition_signal(objc_condition_t condition)
+{
+  condition_signal((condition_t)(condition->backend));
+  return 0;
+}
+
+/* End of File */
diff --git a/libobjc/thr-os2.c b/libobjc/thr-os2.c
new file mode 100644 (file)
index 0000000..a0d7d43
--- /dev/null
@@ -0,0 +1,267 @@
+/* GNU Objective C Runtime Thread Interface - OS/2 emx Implementation
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Thomas Baier (baier@ci.tuwien.ac.at)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <objc/thr.h>
+#include "runtime.h"
+
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSPROCESS
+
+/*
+ * conflicts with objc.h:       SEL, BOOL, id
+ * solution:  prefixing those with _OS2_ before including <os2.h>
+ */
+#define SEL _OS2_SEL
+#define BOOL _OS2_BOOL
+#define id _OS2_id
+#include <os2.h>
+#undef id
+#undef SEL
+#undef BOOL
+
+#include <stdlib.h>
+
+/* Backend initialization functions */
+
+/* Initialize the threads subsystem. */
+int
+__objc_init_thread_system(void)
+{
+  return 0;
+}
+
+/* Close the threads subsystem. */
+int
+__objc_close_thread_system(void)
+{
+  return 0;
+}
+
+/* Backend thread functions */
+
+/* Create a new thread of execution. */
+objc_thread_t
+__objc_thread_detach(void (*func)(void *arg), void *arg)
+{
+  int thread_id = 0;
+
+  if ((thread_id = _beginthread (func,NULL,32768,arg)) < 0)
+    thread_id = 0;
+  
+  return (objc_thread_t)thread_id;
+}
+
+/* Set the current thread's priority. */
+int
+__objc_thread_set_priority(int priority)
+{
+  ULONG sys_class = 0;
+  ULONG sys_priority = 0;
+
+  /* OBJC_THREAD_INTERACTIVE_PRIORITY -> PRTYC_FOREGROUNDSERVER
+   * OBJC_THREAD_BACKGROUND_PRIORITY  -> PRTYC_REGULAR
+   * OBJC_THREAD_LOW_PRIORITY         -> PRTYC_IDLETIME */
+  
+  switch (priority) {
+  case OBJC_THREAD_INTERACTIVE_PRIORITY:
+    sys_class = PRTYC_REGULAR;
+    sys_priority = 10;
+    break;
+  default:
+  case OBJC_THREAD_BACKGROUND_PRIORITY:
+    sys_class = PRTYC_IDLETIME;
+    sys_priority = 25;
+    break;
+  case OBJC_THREAD_LOW_PRIORITY:
+    sys_class = PRTYC_IDLETIME;
+    sys_priority = 0;
+    break;
+  }
+
+  /* Change priority */
+  if (!DosSetPriority (PRTYS_THREAD,sys_class,sys_priority,*_threadid))
+    return 0;
+  else
+    return -1;
+}
+
+/* Return the current thread's priority. */
+int
+__objc_thread_get_priority(void)
+{
+  PTIB ptib;
+  PPIB ppib;
+
+  /* get information about current thread */
+  DosGetInfoBlocks (&ptib,&ppib);
+
+  switch (ptib->tib_ptib2->tib2_ulpri)
+    {
+    case PRTYC_IDLETIME:
+    case PRTYC_REGULAR:
+    case PRTYC_TIMECRITICAL:
+    case PRTYC_FOREGROUNDSERVER:
+    default:
+      return OBJC_THREAD_INTERACTIVE_PRIORITY;
+    }
+
+  return -1;
+}
+
+/* Yield our process time to another thread. */
+void
+__objc_thread_yield(void)
+{
+  DosSleep (0);
+}
+
+/* Terminate the current thread. */
+int
+__objc_thread_exit(void)
+{
+  /* terminate the thread, NEVER use DosExit () */
+  _endthread ();
+
+  /* Failed if we reached here */
+  return -1;
+}
+
+/* Returns an integer value which uniquely describes a thread. */
+objc_thread_t
+__objc_thread_id(void)
+{
+  return (objc_thread_t) *_threadid;
+}
+
+/* Sets the thread's local storage pointer. */
+int
+__objc_thread_set_data(void *value)
+{
+  *_threadstore () = value;
+
+  return 0;
+}
+
+/* Returns the thread's local storage pointer. */
+void *
+__objc_thread_get_data(void)
+{
+  return *_threadstore ();
+}
+
+/* Backend mutex functions */
+
+/* Allocate a mutex. */
+int
+__objc_mutex_allocate(objc_mutex_t mutex)
+{
+  if (DosCreateMutexSem (NULL, (HMTX)(&(mutex->backend)),0L,0) > 0)
+    return -1;
+  else
+    return 0;
+}
+
+/* Deallocate a mutex. */
+int
+__objc_mutex_deallocate(objc_mutex_t mutex)
+{
+  DosCloseMutexSem ((HMTX)(mutex->backend));
+  return 0;
+}
+
+/* Grab a lock on a mutex. */
+int
+__objc_mutex_lock(objc_mutex_t mutex)
+{
+  if (DosRequestMutexSem ((HMTX)(mutex->backend),-1L) != 0)
+    return -1;
+  else
+    return 0;
+}
+
+/* Try to grab a lock on a mutex. */
+int
+__objc_mutex_trylock(objc_mutex_t mutex)
+{
+  if (DosRequestMutexSem ((HMTX)(mutex->backend),0L) != 0)
+    return -1;
+  else
+    return 0;
+}
+
+/* Unlock the mutex */
+int
+__objc_mutex_unlock(objc_mutex_t mutex)
+{
+  if (DosReleaseMutexSem((HMTX)(mutex->backend)) != 0)
+    return -1;
+  else
+    return 0;
+}
+
+/* Backend condition mutex functions */
+
+/* Allocate a condition. */
+int
+__objc_condition_allocate(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* Deallocate a condition. */
+int
+__objc_condition_deallocate(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* Wait on the condition */
+int
+__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* Wake up all threads waiting on this condition. */
+int
+__objc_condition_broadcast(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* Wake up one thread waiting on this condition. */
+int
+__objc_condition_signal(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* End of File */
diff --git a/libobjc/thr-posix.c b/libobjc/thr-posix.c
new file mode 100644 (file)
index 0000000..5b40f71
--- /dev/null
@@ -0,0 +1,229 @@
+/* GNU Objective C Runtime Thread Interface for POSIX compliant threads
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
+   Modified for Linux/Pthreads by Kai-Uwe Sattler (kus@iti.cs.uni-magdeburg.de)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <objc/thr.h>
+#include "runtime.h"
+#include <pthread.h>
+
+/* Key structure for maintaining thread specific storage */
+static pthread_key_t _objc_thread_storage;
+
+/* Backend initialization functions */
+
+/* Initialize the threads subsystem. */
+int
+__objc_init_thread_system(void)
+{
+  /* Initialize the thread storage key */
+  return pthread_key_create(&_objc_thread_storage, NULL);
+}
+
+/* Close the threads subsystem. */
+int
+__objc_close_thread_system(void)
+{
+  return 0;
+}
+
+/* Backend thread functions */
+
+/* Create a new thread of execution. */
+objc_thread_t
+__objc_thread_detach(void (*func)(void *arg), void *arg)
+{
+  objc_thread_t thread_id;
+  pthread_t new_thread_handle;
+
+  if ( !(pthread_create(&new_thread_handle, NULL, (void *)func, arg)) )
+      thread_id = *(objc_thread_t *)&new_thread_handle;
+  else
+    thread_id = NULL;
+  
+  return thread_id;
+}
+
+/* Set the current thread's priority. */
+int
+__objc_thread_set_priority(int priority)
+{
+  /* Not implemented yet */
+  return -1;
+}
+
+/* Return the current thread's priority. */
+int
+__objc_thread_get_priority(void)
+{
+  /* Not implemented yet */
+  return -1;
+}
+
+/* Yield our process time to another thread. */
+void
+__objc_thread_yield(void)
+{
+  sched_yield();
+}
+
+/* Terminate the current thread. */
+int
+__objc_thread_exit(void)
+{
+  /* exit the thread */
+  pthread_exit(&__objc_thread_exit_status);
+
+  /* Failed if we reached here */
+  return -1;
+}
+
+/* Returns an integer value which uniquely describes a thread. */
+objc_thread_t
+__objc_thread_id(void)
+{
+  pthread_t self = pthread_self();
+
+  return *(objc_thread_t *)&self;
+}
+
+/* Sets the thread's local storage pointer. */
+int
+__objc_thread_set_data(void *value)
+{
+  return pthread_setspecific(_objc_thread_storage, value);
+}
+
+/* Returns the thread's local storage pointer. */
+void *
+__objc_thread_get_data(void)
+{
+  return pthread_getspecific(_objc_thread_storage);
+}
+
+/* Backend mutex functions */
+
+/* Allocate a mutex. */
+int
+__objc_mutex_allocate(objc_mutex_t mutex)
+{
+  mutex->backend = objc_malloc(sizeof(pthread_mutex_t));
+
+  if (pthread_mutex_init((pthread_mutex_t *)mutex->backend, NULL))
+    {
+      objc_free(mutex->backend);
+      mutex->backend = NULL;
+      return -1;
+    }
+
+  return 0;
+}
+
+/* Deallocate a mutex. */
+int
+__objc_mutex_deallocate(objc_mutex_t mutex)
+{
+  if (pthread_mutex_destroy((pthread_mutex_t *)mutex->backend))
+    return -1;
+
+  objc_free(mutex->backend);
+  mutex->backend = NULL;
+  return 0;
+}
+
+/* Grab a lock on a mutex. */
+int
+__objc_mutex_lock(objc_mutex_t mutex)
+{
+  return pthread_mutex_lock((pthread_mutex_t *)mutex->backend);
+}
+
+/* Try to grab a lock on a mutex. */
+int
+__objc_mutex_trylock(objc_mutex_t mutex)
+{
+  return pthread_mutex_trylock((pthread_mutex_t *)mutex->backend);
+}
+
+/* Unlock the mutex */
+int
+__objc_mutex_unlock(objc_mutex_t mutex)
+{
+  return pthread_mutex_unlock((pthread_mutex_t *)mutex->backend);
+}
+
+/* Backend condition mutex functions */
+
+/* Allocate a condition. */
+int
+__objc_condition_allocate(objc_condition_t condition)
+{
+  condition->backend = objc_malloc(sizeof(pthread_cond_t));
+
+  if (pthread_cond_init((pthread_cond_t *)condition->backend, NULL))
+    {
+      objc_free(condition->backend);
+      condition->backend = NULL;
+      return -1;
+    }
+
+  return 0;
+}
+
+/* Deallocate a condition. */
+int
+__objc_condition_deallocate(objc_condition_t condition)
+{
+  if (pthread_cond_destroy((pthread_cond_t *)condition->backend))
+    return -1;
+
+  objc_free(condition->backend);
+  condition->backend = NULL;
+  return 0;
+}
+
+/* Wait on the condition */
+int
+__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
+{
+  return pthread_cond_wait((pthread_cond_t *)condition->backend,
+                          (pthread_mutex_t *)mutex->backend);
+}
+
+/* Wake up all threads waiting on this condition. */
+int
+__objc_condition_broadcast(objc_condition_t condition)
+{
+  return pthread_cond_broadcast((pthread_cond_t *)condition->backend);
+}
+
+/* Wake up one thread waiting on this condition. */
+int
+__objc_condition_signal(objc_condition_t condition)
+{
+  return pthread_cond_signal((pthread_cond_t *)condition->backend);
+}
+
+/* End of File */
diff --git a/libobjc/thr-pthreads.c b/libobjc/thr-pthreads.c
new file mode 100644 (file)
index 0000000..2efdd15
--- /dev/null
@@ -0,0 +1,218 @@
+/* GNU Objective C Runtime Thread Implementation for PCThreads under GNU/Linux.
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Scott Christley <scottc@net-community.com>
+   Condition functions added by: Mircea Oancea <mircea@first.elcom.pub.ro>
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <pcthread.h>
+#include <objc/thr.h>
+#include "runtime.h"
+
+/* Key structure for maintaining thread specific storage */
+static pthread_key_t _objc_thread_storage;
+
+/* Backend initialization functions */
+
+/* Initialize the threads subsystem. */
+int
+__objc_init_thread_system(void)
+{
+  /* Initialize the thread storage key */
+  return pthread_key_create(&_objc_thread_storage, NULL);
+}
+
+/* Close the threads subsystem. */
+int
+__objc_close_thread_system(void)
+{
+  /* Destroy the thread storage key */
+  /* Not implemented yet */
+  /* return pthread_key_delete(&_objc_thread_storage); */
+  return 0;
+}
+
+/* Backend thread functions */
+
+/* Create a new thread of execution. */
+objc_thread_t
+__objc_thread_detach(void (*func)(void *arg), void *arg)
+{
+  objc_thread_t thread_id;
+  pthread_t new_thread_handle;
+
+  if ( !(pthread_create(&new_thread_handle, NULL, (void *)func, arg)) )
+      thread_id = *(objc_thread_t *)&new_thread_handle;
+  else
+    thread_id = NULL;
+  
+  return thread_id;
+}
+
+/* Set the current thread's priority. */
+int
+__objc_thread_set_priority(int priority)
+{
+  /* Not implemented yet */
+  return -1;
+}
+
+/* Return the current thread's priority. */
+int
+__objc_thread_get_priority(void)
+{
+  /* Not implemented yet */
+  return OBJC_THREAD_INTERACTIVE_PRIORITY;
+}
+
+/* Yield our process time to another thread. */
+void
+__objc_thread_yield(void)
+{
+  pthread_yield(NULL);
+}
+
+/* Terminate the current thread. */
+int
+__objc_thread_exit(void)
+{
+  /* exit the thread */
+  pthread_exit(&__objc_thread_exit_status);
+
+  /* Failed if we reached here */
+  return -1;
+}
+
+/* Returns an integer value which uniquely describes a thread. */
+objc_thread_t
+__objc_thread_id(void)
+{
+  pthread_t self = pthread_self();
+
+  return *(objc_thread_t *)&self;
+}
+
+/* Sets the thread's local storage pointer. */
+int
+__objc_thread_set_data(void *value)
+{
+  return pthread_setspecific(_objc_thread_storage, value);
+}
+
+/* Returns the thread's local storage pointer. */
+void *
+__objc_thread_get_data(void)
+{
+  void *value = NULL;
+
+  if ( !(pthread_getspecific(_objc_thread_storage, &value)) )
+    return value;
+
+  return NULL;
+}
+
+/* Backend mutex functions */
+
+/* Allocate a mutex. */
+int
+__objc_mutex_allocate(objc_mutex_t mutex)
+{
+  if (pthread_mutex_init((pthread_mutex_t *)(&(mutex->backend)), NULL))
+    return -1;
+  else
+    return 0;
+}
+
+/* Deallocate a mutex. */
+int
+__objc_mutex_deallocate(objc_mutex_t mutex)
+{
+  if (pthread_mutex_destroy((pthread_mutex_t *)(&(mutex->backend))))
+    return -1;
+  else
+    return 0;
+}
+
+/* Grab a lock on a mutex. */
+int
+__objc_mutex_lock(objc_mutex_t mutex)
+{
+  return pthread_mutex_lock((pthread_mutex_t *)(&(mutex->backend)));
+}
+
+/* Try to grab a lock on a mutex. */
+int
+__objc_mutex_trylock(objc_mutex_t mutex)
+{
+  return pthread_mutex_trylock((pthread_mutex_t *)(&(mutex->backend)));
+}
+
+/* Unlock the mutex */
+int
+__objc_mutex_unlock(objc_mutex_t mutex)
+{
+  return pthread_mutex_unlock((pthread_mutex_t *)(&(mutex->backend)));
+}
+
+/* Backend condition mutex functions */
+
+/* Allocate a condition. */
+int
+__objc_condition_allocate(objc_condition_t condition)
+{
+  if (pthread_cond_init((pthread_cond_t *)(&(condition->backend)), NULL))
+    return -1;
+  else
+    return 0;
+}
+
+/* Deallocate a condition. */
+int
+__objc_condition_deallocate(objc_condition_t condition)
+{
+  return pthread_cond_destroy((pthread_cond_t *)(&(condition->backend)));
+}
+
+/* Wait on the condition */
+int
+__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
+{
+  return pthread_cond_wait((pthread_cond_t *)(&(condition->backend)),
+                          (pthread_mutex_t *)(&(mutex->backend)));
+}
+
+/* Wake up all threads waiting on this condition. */
+int
+__objc_condition_broadcast(objc_condition_t condition)
+{
+  return pthread_cond_broadcast((pthread_cond_t *)(&(condition->backend)));
+}
+
+/* Wake up one thread waiting on this condition. */
+int
+__objc_condition_signal(objc_condition_t condition)
+{
+  return pthread_cond_signal((pthread_cond_t *)(&(condition->backend)));
+}
+
+/* End of File */
diff --git a/libobjc/thr-single.c b/libobjc/thr-single.c
new file mode 100644 (file)
index 0000000..b196677
--- /dev/null
@@ -0,0 +1,192 @@
+/* GNU Objective C Runtime Thread Implementation
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <objc/thr.h>
+#include "runtime.h"
+
+/* Thread local storage for a single thread */
+static void *thread_local_storage = NULL;
+
+/* Backend initialization functions */
+
+/* Initialize the threads subsystem. */
+int
+__objc_init_thread_system(void)
+{
+  /* No thread support available */
+  return -1;
+}
+
+/* Close the threads subsystem. */
+int
+__objc_close_thread_system(void)
+{
+  /* No thread support available */
+  return -1;
+}
+
+/* Backend thread functions */
+
+/* Create a new thread of execution. */
+objc_thread_t
+__objc_thread_detach(void (*func)(void *arg), void *arg)
+{
+  /* No thread support available */
+  return NULL;
+}
+
+/* Set the current thread's priority. */
+int
+__objc_thread_set_priority(int priority)
+{
+  /* No thread support available */
+  return -1;
+}
+
+/* Return the current thread's priority. */
+int
+__objc_thread_get_priority(void)
+{
+  return OBJC_THREAD_INTERACTIVE_PRIORITY;
+}
+
+/* Yield our process time to another thread. */
+void
+__objc_thread_yield(void)
+{
+  return;
+}
+
+/* Terminate the current thread. */
+int
+__objc_thread_exit(void)
+{
+  /* No thread support available */
+  /* Should we really exit the program */
+  /* exit(&__objc_thread_exit_status); */
+  return -1;
+}
+
+/* Returns an integer value which uniquely describes a thread. */
+objc_thread_t
+__objc_thread_id(void)
+{
+  /* No thread support, use 1. */
+  return (objc_thread_t)1;
+}
+
+/* Sets the thread's local storage pointer. */
+int
+__objc_thread_set_data(void *value)
+{
+  thread_local_storage = value;
+  return 0;
+}
+
+/* Returns the thread's local storage pointer. */
+void *
+__objc_thread_get_data(void)
+{
+  return thread_local_storage;
+}
+
+/* Backend mutex functions */
+
+/* Allocate a mutex. */
+int
+__objc_mutex_allocate(objc_mutex_t mutex)
+{
+  return 0;
+}
+
+/* Deallocate a mutex. */
+int
+__objc_mutex_deallocate(objc_mutex_t mutex)
+{
+  return 0;
+}
+
+/* Grab a lock on a mutex. */
+int
+__objc_mutex_lock(objc_mutex_t mutex)
+{
+  /* There can only be one thread, so we always get the lock */
+  return 0;
+}
+
+/* Try to grab a lock on a mutex. */
+int
+__objc_mutex_trylock(objc_mutex_t mutex)
+{
+  /* There can only be one thread, so we always get the lock */
+  return 0;
+}
+
+/* Unlock the mutex */
+int
+__objc_mutex_unlock(objc_mutex_t mutex)
+{
+  return 0;
+}
+
+/* Backend condition mutex functions */
+
+/* Allocate a condition. */
+int
+__objc_condition_allocate(objc_condition_t condition)
+{
+  return 0;
+}
+
+/* Deallocate a condition. */
+int
+__objc_condition_deallocate(objc_condition_t condition)
+{
+  return 0;
+}
+
+/* Wait on the condition */
+int
+__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
+{
+  return 0;
+}
+
+/* Wake up all threads waiting on this condition. */
+int
+__objc_condition_broadcast(objc_condition_t condition)
+{
+  return 0;
+}
+
+/* Wake up one thread waiting on this condition. */
+int
+__objc_condition_signal(objc_condition_t condition)
+{
+  return 0;
+}
+
+/* End of File */
diff --git a/libobjc/thr-solaris.c b/libobjc/thr-solaris.c
new file mode 100644 (file)
index 0000000..90351b4
--- /dev/null
@@ -0,0 +1,259 @@
+/* GNU Objective C Runtime Thread Interface
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
+   Conditions added by Mircea Oancea (mircea@first.elcom.pub.ro)
+      
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <objc/thr.h>
+#include "runtime.h"
+
+#include <thread.h>
+#include <synch.h>
+#include <errno.h>
+
+/* Key structure for maintaining thread specific storage */
+static thread_key_t     __objc_thread_data_key;
+
+/* Backend initialization functions */
+
+/* Initialize the threads subsystem. */
+int
+__objc_init_thread_system(void)
+{
+  /* Initialize the thread storage key */
+  if (thr_keycreate(&__objc_thread_data_key, NULL) == 0)
+    return 0;
+  else
+    return -1;
+}
+
+/* Close the threads subsystem. */
+int
+__objc_close_thread_system(void)
+{
+  return 0;
+}
+
+/* Backend thread functions */
+
+/* Create a new thread of execution. */
+objc_thread_t
+__objc_thread_detach(void (*func)(void *arg), void *arg)
+{
+  objc_thread_t thread_id;
+  thread_t new_thread_id = 0;
+
+  if (thr_create(NULL, 0, (void *)func, arg,
+                 THR_DETACHED | THR_NEW_LWP,
+                 &new_thread_id) == 0)
+    thread_id = *(objc_thread_t *)&new_thread_id;
+  else
+    thread_id = NULL;
+  
+  return thread_id;
+}
+
+/* Set the current thread's priority. */
+int
+__objc_thread_set_priority(int priority)
+{
+  int sys_priority = 0;
+
+  switch (priority)
+    {
+    case OBJC_THREAD_INTERACTIVE_PRIORITY:
+      sys_priority = 300;
+      break;
+    default:
+    case OBJC_THREAD_BACKGROUND_PRIORITY:
+      sys_priority = 200;
+      break;
+    case OBJC_THREAD_LOW_PRIORITY:
+      sys_priority = 1000;
+      break;
+    }
+
+  /* Change priority */
+  if (thr_setprio(thr_self(), sys_priority) == 0)
+    return 0;
+  else
+    return -1;
+}
+
+/* Return the current thread's priority. */
+int
+__objc_thread_get_priority(void)
+{
+  int sys_priority;
+                                                   
+  if (thr_getprio(thr_self(), &sys_priority) == 0)
+    {
+      if (sys_priority >= 250)
+       return OBJC_THREAD_INTERACTIVE_PRIORITY;
+      else if (sys_priority >= 150)
+       return OBJC_THREAD_BACKGROUND_PRIORITY;
+      return OBJC_THREAD_LOW_PRIORITY;
+    }
+
+  /* Couldn't get priority. */
+  return -1;
+}
+
+/* Yield our process time to another thread. */
+void
+__objc_thread_yield(void)
+{
+  thr_yield();
+}
+
+/* Terminate the current thread. */
+int
+__objc_thread_exit(void)
+{
+  /* exit the thread */
+  thr_exit(&__objc_thread_exit_status);
+
+  /* Failed if we reached here */
+  return -1;
+}
+
+/* Returns an integer value which uniquely describes a thread. */
+objc_thread_t
+__objc_thread_id(void)
+{
+  return (objc_thread_t)thr_self();
+}
+
+/* Sets the thread's local storage pointer. */
+int
+__objc_thread_set_data(void *value)
+{
+  if (thr_setspecific(__objc_thread_data_key, value) == 0)
+    return 0;
+  else
+    return -1;
+}
+
+/* Returns the thread's local storage pointer. */
+void *
+__objc_thread_get_data(void)
+{
+  void *value = NULL;
+
+  if (thr_getspecific(__objc_thread_data_key, &value) == 0)
+    return value;
+
+  return NULL;
+}
+
+/* Backend mutex functions */
+
+/* Allocate a mutex. */
+int
+__objc_mutex_allocate(objc_mutex_t mutex)
+{
+  if (mutex_init( (mutex_t *)(&(mutex->backend)), USYNC_THREAD, 0))
+    return -1;
+  else
+    return 0;
+}
+
+
+/* Deallocate a mutex. */
+int
+__objc_mutex_deallocate(objc_mutex_t mutex)
+{
+  mutex_destroy((mutex_t *)(&(mutex->backend)));
+  return 0;
+}
+
+/* Grab a lock on a mutex. */
+int
+__objc_mutex_lock(objc_mutex_t mutex)
+{
+  if (mutex_lock((mutex_t *)(&(mutex->backend))) != 0)
+    return -1;
+  else
+    return 0;
+}
+
+/* Try to grab a lock on a mutex. */
+int
+__objc_mutex_trylock(objc_mutex_t mutex)
+{
+  if (mutex_trylock((mutex_t *)(&(mutex->backend))) != 0)
+    return -1;
+  else
+    return 0;
+}
+
+/* Unlock the mutex */
+int
+__objc_mutex_unlock(objc_mutex_t mutex)
+{
+  if (mutex_unlock((mutex_t *)(&(mutex->backend))) != 0)
+    return -1;
+  else
+    return 0;
+}
+
+/* Backend condition mutex functions */
+
+/* Allocate a condition. */
+int
+__objc_condition_allocate(objc_condition_t condition)
+{
+  return cond_init((cond_t *)(&(condition->backend)), USYNC_THREAD, NULL);
+}
+
+/* Deallocate a condition. */
+int
+__objc_condition_deallocate(objc_condition_t condition)
+{
+  return cond_destroy((cond_t *)(&(condition->backend)));
+}
+
+/* Wait on the condition */
+int
+__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
+{
+  return cond_wait((cond_t *)(&(condition->backend)),
+                  (mutex_t *)(&(mutex->backend)));
+}
+
+/* Wake up all threads waiting on this condition. */
+int
+__objc_condition_broadcast(objc_condition_t condition)
+{
+  return cond_broadcast((cond_t *)(&(condition->backend)));
+}
+
+/* Wake up one thread waiting on this condition. */
+int
+__objc_condition_signal(objc_condition_t condition)
+{
+  return cond_signal((cond_t *)(&(condition->backend)));
+}
+
+/* End of File */
diff --git a/libobjc/thr-vxworks.c b/libobjc/thr-vxworks.c
new file mode 100644 (file)
index 0000000..b196677
--- /dev/null
@@ -0,0 +1,192 @@
+/* GNU Objective C Runtime Thread Implementation
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <objc/thr.h>
+#include "runtime.h"
+
+/* Thread local storage for a single thread */
+static void *thread_local_storage = NULL;
+
+/* Backend initialization functions */
+
+/* Initialize the threads subsystem. */
+int
+__objc_init_thread_system(void)
+{
+  /* No thread support available */
+  return -1;
+}
+
+/* Close the threads subsystem. */
+int
+__objc_close_thread_system(void)
+{
+  /* No thread support available */
+  return -1;
+}
+
+/* Backend thread functions */
+
+/* Create a new thread of execution. */
+objc_thread_t
+__objc_thread_detach(void (*func)(void *arg), void *arg)
+{
+  /* No thread support available */
+  return NULL;
+}
+
+/* Set the current thread's priority. */
+int
+__objc_thread_set_priority(int priority)
+{
+  /* No thread support available */
+  return -1;
+}
+
+/* Return the current thread's priority. */
+int
+__objc_thread_get_priority(void)
+{
+  return OBJC_THREAD_INTERACTIVE_PRIORITY;
+}
+
+/* Yield our process time to another thread. */
+void
+__objc_thread_yield(void)
+{
+  return;
+}
+
+/* Terminate the current thread. */
+int
+__objc_thread_exit(void)
+{
+  /* No thread support available */
+  /* Should we really exit the program */
+  /* exit(&__objc_thread_exit_status); */
+  return -1;
+}
+
+/* Returns an integer value which uniquely describes a thread. */
+objc_thread_t
+__objc_thread_id(void)
+{
+  /* No thread support, use 1. */
+  return (objc_thread_t)1;
+}
+
+/* Sets the thread's local storage pointer. */
+int
+__objc_thread_set_data(void *value)
+{
+  thread_local_storage = value;
+  return 0;
+}
+
+/* Returns the thread's local storage pointer. */
+void *
+__objc_thread_get_data(void)
+{
+  return thread_local_storage;
+}
+
+/* Backend mutex functions */
+
+/* Allocate a mutex. */
+int
+__objc_mutex_allocate(objc_mutex_t mutex)
+{
+  return 0;
+}
+
+/* Deallocate a mutex. */
+int
+__objc_mutex_deallocate(objc_mutex_t mutex)
+{
+  return 0;
+}
+
+/* Grab a lock on a mutex. */
+int
+__objc_mutex_lock(objc_mutex_t mutex)
+{
+  /* There can only be one thread, so we always get the lock */
+  return 0;
+}
+
+/* Try to grab a lock on a mutex. */
+int
+__objc_mutex_trylock(objc_mutex_t mutex)
+{
+  /* There can only be one thread, so we always get the lock */
+  return 0;
+}
+
+/* Unlock the mutex */
+int
+__objc_mutex_unlock(objc_mutex_t mutex)
+{
+  return 0;
+}
+
+/* Backend condition mutex functions */
+
+/* Allocate a condition. */
+int
+__objc_condition_allocate(objc_condition_t condition)
+{
+  return 0;
+}
+
+/* Deallocate a condition. */
+int
+__objc_condition_deallocate(objc_condition_t condition)
+{
+  return 0;
+}
+
+/* Wait on the condition */
+int
+__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
+{
+  return 0;
+}
+
+/* Wake up all threads waiting on this condition. */
+int
+__objc_condition_broadcast(objc_condition_t condition)
+{
+  return 0;
+}
+
+/* Wake up one thread waiting on this condition. */
+int
+__objc_condition_signal(objc_condition_t condition)
+{
+  return 0;
+}
+
+/* End of File */
diff --git a/libobjc/thr-win32.c b/libobjc/thr-win32.c
new file mode 100644 (file)
index 0000000..8570ffd
--- /dev/null
@@ -0,0 +1,272 @@
+/* GNU Objective C Runtime Thread Interface - Win32 Implementation
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <objc/thr.h>
+#include "runtime.h"
+
+#ifndef __OBJC__
+#define __OBJC__
+#endif
+#include <windows.h>
+
+/* Key structure for maintaining thread specific storage */
+static DWORD   __objc_data_tls = (DWORD)-1;
+
+/* Backend initialization functions */
+
+/* Initialize the threads subsystem. */
+int
+__objc_init_thread_system(void)
+{
+  /* Initialize the thread storage key */
+  if ((__objc_data_tls = TlsAlloc()) != (DWORD)-1)
+    return 0;
+  else
+    return -1;
+}
+
+/* Close the threads subsystem. */
+int
+__objc_close_thread_system(void)
+{
+  if (__objc_data_tls != (DWORD)-1)
+    TlsFree(__objc_data_tls);
+  return 0;
+}
+
+/* Backend thread functions */
+
+/* Create a new thread of execution. */
+objc_thread_t
+__objc_thread_detach(void (*func)(void *arg), void *arg)
+{
+  DWORD        thread_id = 0;
+  HANDLE win32_handle;
+
+  if (!(win32_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func,
+                                   arg, 0, &thread_id)))
+    thread_id = 0;
+  
+  return (objc_thread_t)thread_id;
+}
+
+/* Set the current thread's priority. */
+int
+__objc_thread_set_priority(int priority)
+{
+  int sys_priority = 0;
+
+  switch (priority)
+    {
+    case OBJC_THREAD_INTERACTIVE_PRIORITY:
+      sys_priority = THREAD_PRIORITY_NORMAL;
+      break;
+    default:
+    case OBJC_THREAD_BACKGROUND_PRIORITY:
+      sys_priority = THREAD_PRIORITY_BELOW_NORMAL;
+      break;
+    case OBJC_THREAD_LOW_PRIORITY:
+      sys_priority = THREAD_PRIORITY_LOWEST;
+      break;
+    }
+
+  /* Change priority */
+  if (SetThreadPriority(GetCurrentThread(), sys_priority))
+    return 0;
+  else
+    return -1;
+}
+
+/* Return the current thread's priority. */
+int
+__objc_thread_get_priority(void)
+{
+  int sys_priority;
+
+  sys_priority = GetThreadPriority(GetCurrentThread());
+  
+  switch (sys_priority)
+    {
+    case THREAD_PRIORITY_HIGHEST:
+    case THREAD_PRIORITY_TIME_CRITICAL:
+    case THREAD_PRIORITY_ABOVE_NORMAL:
+    case THREAD_PRIORITY_NORMAL:
+      return OBJC_THREAD_INTERACTIVE_PRIORITY;
+
+    default:
+    case THREAD_PRIORITY_BELOW_NORMAL:
+      return OBJC_THREAD_BACKGROUND_PRIORITY;
+    
+    case THREAD_PRIORITY_IDLE:
+    case THREAD_PRIORITY_LOWEST:
+      return OBJC_THREAD_LOW_PRIORITY;
+    }
+
+  /* Couldn't get priority. */
+  return -1;
+}
+
+/* Yield our process time to another thread. */
+void
+__objc_thread_yield(void)
+{
+  Sleep(0);
+}
+
+/* Terminate the current thread. */
+int
+__objc_thread_exit(void)
+{
+  /* exit the thread */
+  ExitThread(__objc_thread_exit_status);
+
+  /* Failed if we reached here */
+  return -1;
+}
+
+/* Returns an integer value which uniquely describes a thread. */
+objc_thread_t
+__objc_thread_id(void)
+{
+  return (objc_thread_t)GetCurrentThreadId();
+}
+
+/* Sets the thread's local storage pointer. */
+int
+__objc_thread_set_data(void *value)
+{
+  if (TlsSetValue(__objc_data_tls, value))
+    return 0;
+  else
+    return -1;
+}
+
+/* Returns the thread's local storage pointer. */
+void *
+__objc_thread_get_data(void)
+{
+  return TlsGetValue(__objc_data_tls);          /* Return thread data.      */
+}
+
+/* Backend mutex functions */
+
+/* Allocate a mutex. */
+int
+__objc_mutex_allocate(objc_mutex_t mutex)
+{
+  if ((mutex->backend = (void *)CreateMutex(NULL, 0, NULL)) == NULL)
+    return -1;
+  else
+    return 0;
+}
+
+/* Deallocate a mutex. */
+int
+__objc_mutex_deallocate(objc_mutex_t mutex)
+{
+  CloseHandle((HANDLE)(mutex->backend));
+  return 0;
+}
+
+/* Grab a lock on a mutex. */
+int
+__objc_mutex_lock(objc_mutex_t mutex)
+{
+  int status;
+
+  status = WaitForSingleObject((HANDLE)(mutex->backend), INFINITE);
+  if (status != WAIT_OBJECT_0 && status != WAIT_ABANDONED)
+    return -1;
+  else
+    return 0;
+}
+
+/* Try to grab a lock on a mutex. */
+int
+__objc_mutex_trylock(objc_mutex_t mutex)
+{
+  int status;
+
+  status = WaitForSingleObject((HANDLE)(mutex->backend), 0);
+  if (status != WAIT_OBJECT_0 && status != WAIT_ABANDONED)
+    return -1;
+  else
+    return 0;
+}
+
+/* Unlock the mutex */
+int
+__objc_mutex_unlock(objc_mutex_t mutex)
+{
+  if (ReleaseMutex((HANDLE)(mutex->backend)) == 0)
+    return -1;
+  else
+    return 0;
+}
+
+/* Backend condition mutex functions */
+
+/* Allocate a condition. */
+int
+__objc_condition_allocate(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* Deallocate a condition. */
+int
+__objc_condition_deallocate(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* Wait on the condition */
+int
+__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* Wake up all threads waiting on this condition. */
+int
+__objc_condition_broadcast(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* Wake up one thread waiting on this condition. */
+int
+__objc_condition_signal(objc_condition_t condition)
+{
+  /* Unimplemented. */
+  return -1;
+}
+
+/* End of File */
diff --git a/libobjc/thr.c b/libobjc/thr.c
new file mode 100644 (file)
index 0000000..f1c957a
--- /dev/null
@@ -0,0 +1,534 @@
+/* GNU Objective C Runtime Thread Interface
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled with
+   GCC to produce an executable, this does not cause the resulting executable
+   to be covered by the GNU General Public License. This exception does not
+   however invalidate any other reasons why the executable file might be
+   covered by the GNU General Public License.  */
+
+#include <stdlib.h>
+#include "runtime.h"
+
+/* Global exit status. */
+int __objc_thread_exit_status = 0;
+
+/* Flag which lets us know if we ever became multi threaded */
+int __objc_is_multi_threaded = 0;
+
+/* The hook function called when the runtime becomes multi threaded */
+objc_thread_callback _objc_became_multi_threaded = NULL;
+
+/*
+  Use this to set the hook function that will be called when the 
+  runtime initially becomes multi threaded.
+  The hook function is only called once, meaning only when the 
+  2nd thread is spawned, not for each and every thread.
+
+  It returns the previous hook function or NULL if there is none.
+
+  A program outside of the runtime could set this to some function so
+  it can be informed; for example, the GNUstep Base Library sets it 
+  so it can implement the NSBecomingMultiThreaded notification.
+  */
+objc_thread_callback objc_set_thread_callback(objc_thread_callback func)
+{
+  objc_thread_callback temp = _objc_became_multi_threaded;
+  _objc_became_multi_threaded = func;
+  return temp;
+}
+
+/*
+  Private functions
+
+  These functions are utilized by the frontend, but they are not
+  considered part of the public interface.
+  */
+
+/*
+  First function called in a thread, starts everything else.
+
+  This function is passed to the backend by objc_thread_detach
+  as the starting function for a new thread.
+ */
+struct __objc_thread_start_state
+{
+  SEL selector;
+  id object;
+  id argument;
+};
+
+static volatile void
+__objc_thread_detach_function(struct __objc_thread_start_state *istate)
+{
+  /* Valid state? */
+  if (istate) {
+    id (*imp)(id,SEL,id);
+    SEL selector = istate->selector;
+    id object   = istate->object;
+    id argument = istate->argument;
+
+    /* Don't need anymore so free it */
+    objc_free(istate);
+
+    /* Clear out the thread local storage */
+    objc_thread_set_data(NULL);
+
+    /* Check to see if we just became multi threaded */
+    if (!__objc_is_multi_threaded)
+      {
+       __objc_is_multi_threaded = 1;
+
+       /* Call the hook function */
+       if (_objc_became_multi_threaded != NULL)
+         (*_objc_became_multi_threaded)();
+      }
+
+    /* Call the method */
+    if ((imp = (id(*)(id, SEL, id))objc_msg_lookup(object, selector)))
+       (*imp)(object, selector, argument);
+    else
+      objc_error(object, OBJC_ERR_UNIMPLEMENTED,
+                "objc_thread_detach called with bad selector.\n");
+  }
+  else
+    objc_error(nil, OBJC_ERR_BAD_STATE,
+              "objc_thread_detach called with NULL state.\n");
+
+  /* Exit the thread */
+  objc_thread_exit();
+}
+
+/*
+  Frontend functions
+
+  These functions constitute the public interface to the Objective-C thread
+  and mutex functionality.
+  */
+
+/* Frontend thread functions */
+
+/*
+  Detach a new thread of execution and return its id.  Returns NULL if fails.
+  Thread is started by sending message with selector to object.  Message
+  takes a single argument.
+  */
+objc_thread_t
+objc_thread_detach(SEL selector, id object, id argument)
+{
+  struct __objc_thread_start_state *istate;
+  objc_thread_t        thread_id = NULL;
+
+  /* Allocate the state structure */
+  if (!(istate = (struct __objc_thread_start_state *)
+       objc_malloc(sizeof(*istate))))
+    return NULL;
+
+  /* Initialize the state structure */
+  istate->selector = selector;
+  istate->object = object;
+  istate->argument = argument;
+
+  /* lock access */
+  objc_mutex_lock(__objc_runtime_mutex);
+
+  /* Call the backend to spawn the thread */
+  if ((thread_id = __objc_thread_detach((void *)__objc_thread_detach_function,
+                                       istate)) == NULL)
+    {
+      /* failed! */
+      objc_mutex_unlock(__objc_runtime_mutex);
+      objc_free(istate);
+      return NULL;
+    }
+
+  /* Increment our thread counter */
+  __objc_runtime_threads_alive++;
+  objc_mutex_unlock(__objc_runtime_mutex);
+
+  return thread_id;
+}
+
+/* Set the current thread's priority. */
+int
+objc_thread_set_priority(int priority)
+{
+  /* Call the backend */
+  return __objc_thread_set_priority(priority);
+}
+
+/* Return the current thread's priority. */
+int
+objc_thread_get_priority(void)
+{
+  /* Call the backend */
+  return __objc_thread_get_priority();
+}
+
+/*
+  Yield our process time to another thread.  Any BUSY waiting that is done
+  by a thread should use this function to make sure that other threads can
+  make progress even on a lazy uniprocessor system.
+  */
+void
+objc_thread_yield(void)
+{
+  /* Call the backend */
+  __objc_thread_yield();
+}
+
+/*
+  Terminate the current tread.  Doesn't return.
+  Actually, if it failed returns -1.
+  */
+int
+objc_thread_exit(void)
+{
+  /* Decrement our counter of the number of threads alive */
+  objc_mutex_lock(__objc_runtime_mutex);
+  __objc_runtime_threads_alive--;
+  objc_mutex_unlock(__objc_runtime_mutex);
+
+  /* Call the backend to terminate the thread */
+  return __objc_thread_exit();
+}
+
+/*
+  Returns an integer value which uniquely describes a thread.  Must not be
+  NULL which is reserved as a marker for "no thread".
+  */
+objc_thread_t
+objc_thread_id(void)
+{
+  /* Call the backend */
+  return __objc_thread_id();
+}
+
+/*
+  Sets the thread's local storage pointer. 
+  Returns 0 if successful or -1 if failed.
+  */
+int
+objc_thread_set_data(void *value)
+{
+  /* Call the backend */
+  return __objc_thread_set_data(value);
+}
+
+/*
+  Returns the thread's local storage pointer.  Returns NULL on failure.
+  */
+void *
+objc_thread_get_data(void)
+{
+  /* Call the backend */
+  return __objc_thread_get_data();
+}
+
+/* Frontend mutex functions */
+
+/*
+  Allocate a mutex.  Return the mutex pointer if successful or NULL if the
+  allocation failed for any reason.
+  */
+objc_mutex_t
+objc_mutex_allocate(void)
+{
+  objc_mutex_t mutex;
+
+  /* Allocate the mutex structure */
+  if (!(mutex = (objc_mutex_t)objc_malloc(sizeof(struct objc_mutex))))
+    return NULL;
+
+  /* Call backend to create the mutex */
+  if (__objc_mutex_allocate(mutex))
+    {
+      /* failed! */
+      objc_free(mutex);
+      return NULL;
+    }
+
+  /* Initialize mutex */
+  mutex->owner = NULL;
+  mutex->depth = 0;
+  return mutex;
+}
+
+/*
+  Deallocate a mutex.  Note that this includes an implicit mutex_lock to
+  insure that no one else is using the lock.  It is legal to deallocate
+  a lock if we have a lock on it, but illegal to deallocate a lock held
+  by anyone else.
+  Returns the number of locks on the thread.  (1 for deallocate).
+  */
+int
+objc_mutex_deallocate(objc_mutex_t mutex)
+{
+  int depth;
+
+  /* Valid mutex? */
+  if (!mutex)
+    return -1;
+
+  /* Acquire lock on mutex */
+  depth = objc_mutex_lock(mutex);
+
+  /* Call backend to destroy mutex */
+  if (__objc_mutex_deallocate(mutex))
+    return -1;
+
+  /* Free the mutex structure */
+  objc_free(mutex);
+
+  /* Return last depth */
+  return depth;
+}
+
+/*
+  Grab a lock on a mutex.  If this thread already has a lock on this mutex
+  then we increment the lock count.  If another thread has a lock on the 
+  mutex we block and wait for the thread to release the lock.
+  Returns the lock count on the mutex held by this thread.
+  */
+int
+objc_mutex_lock(objc_mutex_t mutex)
+{
+  objc_thread_t thread_id;
+  int status;
+
+  /* Valid mutex? */
+  if (!mutex)
+    return -1;
+
+  /* If we already own the lock then increment depth */
+  thread_id = objc_thread_id();
+  if (mutex->owner == thread_id)
+    return ++mutex->depth;
+
+  /* Call the backend to lock the mutex */
+  status = __objc_mutex_lock(mutex);
+
+  /* Failed? */
+  if (status)
+    return status;
+
+  /* Successfully locked the thread */
+  mutex->owner = thread_id;
+  return mutex->depth = 1;
+}
+
+/*
+  Try to grab a lock on a mutex.  If this thread already has a lock on
+  this mutex then we increment the lock count and return it.  If another
+  thread has a lock on the mutex returns -1.
+  */
+int
+objc_mutex_trylock(objc_mutex_t mutex)
+{
+  objc_thread_t thread_id;
+  int status;
+
+  /* Valid mutex? */
+  if (!mutex)
+    return -1;
+
+  /* If we already own the lock then increment depth */ 
+  thread_id = objc_thread_id();
+  if (mutex->owner == thread_id)
+    return ++mutex->depth;
+    
+  /* Call the backend to try to lock the mutex */
+  status = __objc_mutex_trylock(mutex);
+
+  /* Failed? */
+  if (status)
+    return status;
+
+  /* Successfully locked the thread */
+  mutex->owner = thread_id;
+  return mutex->depth = 1;
+}
+
+/* 
+  Unlocks the mutex by one level.
+  Decrements the lock count on this mutex by one.
+  If the lock count reaches zero, release the lock on the mutex.
+  Returns the lock count on the mutex.
+  It is an error to attempt to unlock a mutex which this thread 
+  doesn't hold in which case return -1 and the mutex is unaffected.
+  */
+int
+objc_mutex_unlock(objc_mutex_t mutex)
+{
+  objc_thread_t thread_id;
+  int status;
+
+  /* Valid mutex? */
+  if (!mutex)
+    return -1;
+
+  /* If another thread owns the lock then abort */
+  thread_id = objc_thread_id();
+  if (mutex->owner != thread_id)
+    return -1;
+
+  /* Decrement depth and return */
+  if (mutex->depth > 1)
+    return --mutex->depth;
+
+  /* Depth down to zero so we are no longer the owner */
+  mutex->depth = 0;
+  mutex->owner = NULL;
+
+  /* Have the backend unlock the mutex */
+  status = __objc_mutex_unlock(mutex);
+
+  /* Failed? */
+  if (status)
+    return status;
+
+  return 0;
+}
+
+/* Frontend condition mutex functions */
+
+/*
+  Allocate a condition.  Return the condition pointer if successful or NULL
+  if the allocation failed for any reason.
+  */
+objc_condition_t 
+objc_condition_allocate(void)
+{
+  objc_condition_t condition;
+    
+  /* Allocate the condition mutex structure */
+  if (!(condition = 
+       (objc_condition_t)objc_malloc(sizeof(struct objc_condition))))
+    return NULL;
+
+  /* Call the backend to create the condition mutex */
+  if (__objc_condition_allocate(condition))
+    {
+      /* failed! */
+      objc_free(condition);
+      return NULL;
+    }
+
+  /* Success! */
+  return condition;
+}
+
+/*
+  Deallocate a condition. Note that this includes an implicit 
+  condition_broadcast to insure that waiting threads have the opportunity
+  to wake.  It is legal to dealloc a condition only if no other
+  thread is/will be using it. Here we do NOT check for other threads
+  waiting but just wake them up.
+  */
+int
+objc_condition_deallocate(objc_condition_t condition)
+{
+  /* Broadcast the condition */
+  if (objc_condition_broadcast(condition))
+    return -1;
+
+  /* Call the backend to destroy */
+  if (__objc_condition_deallocate(condition))
+    return -1;
+
+  /* Free the condition mutex structure */
+  objc_free(condition);
+
+  return 0;
+}
+
+/*
+  Wait on the condition unlocking the mutex until objc_condition_signal()
+  or objc_condition_broadcast() are called for the same condition. The
+  given mutex *must* have the depth set to 1 so that it can be unlocked
+  here, so that someone else can lock it and signal/broadcast the condition.
+  The mutex is used to lock access to the shared data that make up the
+  "condition" predicate.
+  */
+int
+objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
+{
+  objc_thread_t thread_id;
+
+  /* Valid arguments? */
+  if (!mutex || !condition)
+    return -1;
+
+  /* Make sure we are owner of mutex */
+  thread_id = objc_thread_id();
+  if (mutex->owner != thread_id)
+    return -1;
+
+  /* Cannot be locked more than once */
+  if (mutex->depth > 1)
+    return -1;
+
+  /* Virtually unlock the mutex */
+  mutex->depth = 0;
+  mutex->owner = (objc_thread_t)NULL;
+
+  /* Call the backend to wait */
+  __objc_condition_wait(condition, mutex);
+
+  /* Make ourselves owner of the mutex */
+  mutex->owner = thread_id;
+  mutex->depth = 1;
+
+  return 0;
+}
+
+/*
+  Wake up all threads waiting on this condition. It is recommended that 
+  the called would lock the same mutex as the threads in objc_condition_wait
+  before changing the "condition predicate" and make this call and unlock it
+  right away after this call.
+  */
+int
+objc_condition_broadcast(objc_condition_t condition)
+{
+  /* Valid condition mutex? */
+  if (!condition)
+    return -1;
+
+  return __objc_condition_broadcast(condition);
+}
+
+/*
+  Wake up one thread waiting on this condition. It is recommended that 
+  the called would lock the same mutex as the threads in objc_condition_wait
+  before changing the "condition predicate" and make this call and unlock it
+  right away after this call.
+  */
+int
+objc_condition_signal(objc_condition_t condition)
+{
+  /* Valid condition mutex? */
+  if (!condition)
+    return -1;
+
+  return __objc_condition_signal(condition);
+}
+
+/* End of File */
diff --git a/libobjc/thr.h b/libobjc/thr.h
new file mode 100644 (file)
index 0000000..f904733
--- /dev/null
@@ -0,0 +1,143 @@
+/* Thread and mutex controls for Objective C.
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC 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 General Public License for more details.
+
+GNU CC is free software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2, or (at your option) any later version.
+
+GNU CC 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 General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along with
+GNU CC; see the file COPYING.  If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files
+   compiled with GCC to produce an executable, this does not cause
+   the resulting executable to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+
+#ifndef __thread_INCLUDE_GNU
+#define __thread_INCLUDE_GNU
+
+#include "objc/objc.h"
+
+/*************************************************************************
+ *  Universal static variables:
+ */
+extern int __objc_thread_exit_status;      /* Global exit status.   */
+
+/********
+ *  Thread safe implementation types and functions.  
+ */
+
+/* Thread priorities */
+#define OBJC_THREAD_INTERACTIVE_PRIORITY        2
+#define OBJC_THREAD_BACKGROUND_PRIORITY         1
+#define OBJC_THREAD_LOW_PRIORITY                0
+
+/* A thread */
+typedef void * objc_thread_t;
+
+/* This structure represents a single mutual exclusion lock. */
+struct objc_mutex
+{
+  volatile objc_thread_t owner;     /* Id of thread that owns. */
+  volatile int depth;               /* # of acquires. */
+  void * backend;                   /* Specific to backend */
+};
+typedef struct objc_mutex *objc_mutex_t;
+
+/* This structure represents a single condition mutex */
+struct objc_condition
+{
+  void * backend;                   /* Specific to backend */
+};
+typedef struct objc_condition *objc_condition_t;
+
+/* Frontend mutex functions */
+objc_mutex_t objc_mutex_allocate(void);
+int objc_mutex_deallocate(objc_mutex_t mutex);
+int objc_mutex_lock(objc_mutex_t mutex);
+int objc_mutex_unlock(objc_mutex_t mutex);
+int objc_mutex_trylock(objc_mutex_t mutex);
+
+/* Frontend condition mutex functions */
+objc_condition_t objc_condition_allocate(void);
+int objc_condition_deallocate(objc_condition_t condition);
+int objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex);
+int objc_condition_signal(objc_condition_t condition);
+int objc_condition_broadcast(objc_condition_t condition);
+
+/* Frontend thread functions */
+objc_thread_t objc_thread_detach(SEL selector, id object, id argument);
+void objc_thread_yield(void);
+int objc_thread_exit(void);
+int objc_thread_set_priority(int priority);
+int objc_thread_get_priority(void);
+void * objc_thread_get_data(void);
+int objc_thread_set_data(void *value);
+objc_thread_t objc_thread_id(void);
+
+/*
+  Use this to set the hook function that will be called when the 
+  runtime initially becomes multi threaded.
+  The hook function is only called once, meaning only when the 
+  2nd thread is spawned, not for each and every thread.
+
+  It returns the previous hook function or NULL if there is none.
+
+  A program outside of the runtime could set this to some function so
+  it can be informed; for example, the GNUstep Base Library sets it 
+  so it can implement the NSBecomingMultiThreaded notification.
+  */
+typedef void (*objc_thread_callback)();
+objc_thread_callback objc_set_thread_callback(objc_thread_callback func);
+
+/* Backend initialization functions */
+int __objc_init_thread_system(void);
+int __objc_fini_thread_system(void);
+
+/* Backend mutex functions */
+int __objc_mutex_allocate(objc_mutex_t mutex);
+int __objc_mutex_deallocate(objc_mutex_t mutex);
+int __objc_mutex_lock(objc_mutex_t mutex);
+int __objc_mutex_trylock(objc_mutex_t mutex);
+int __objc_mutex_unlock(objc_mutex_t mutex);
+
+/* Backend condition mutex functions */
+int __objc_condition_allocate(objc_condition_t condition);
+int __objc_condition_deallocate(objc_condition_t condition);
+int __objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex);
+int __objc_condition_broadcast(objc_condition_t condition);
+int __objc_condition_signal(objc_condition_t condition);
+
+/* Backend thread functions */
+objc_thread_t __objc_thread_detach(void (*func)(void *arg), void *arg);
+int __objc_thread_set_priority(int priority);
+int __objc_thread_get_priority(void);
+void __objc_thread_yield(void);
+int __objc_thread_exit(void);
+objc_thread_t __objc_thread_id(void);
+int __objc_thread_set_data(void *value);
+void * __objc_thread_get_data(void);
+
+#endif /* not __thread_INCLUDE_GNU */
diff --git a/libobjc/typedstream.h b/libobjc/typedstream.h
new file mode 100644 (file)
index 0000000..eb4642f
--- /dev/null
@@ -0,0 +1,132 @@
+/* GNU Objective-C Typed Streams interface.
+   Copyright (C) 1993, 1995 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU CC 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 General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with files compiled
+   with GCC to produce an executable, this does not cause the resulting
+   executable to be covered by the GNU General Public License.  This
+   exception does not however invalidate any other reasons why the
+   executable file might be covered by the GNU General Public License. */
+
+#ifndef __typedstream_INCLUDE_GNU
+#define __typedstream_INCLUDE_GNU
+
+#include "objc/objc.h"
+#include "objc/hash.h"
+#include <stdio.h>
+
+typedef int (*objc_typed_read_func)(void*, char*, int);
+typedef int (*objc_typed_write_func)(void*, const char*, int);
+typedef int (*objc_typed_flush_func)(void*);
+typedef int (*objc_typed_eof_func)(void*);
+
+#define OBJC_READONLY   0x01
+#define OBJC_WRITEONLY  0x02
+
+#define OBJC_MANAGED_STREAM  0x01
+#define OBJC_FILE_STREAM     0x02
+#define OBJC_MEMORY_STREAM   0x04
+
+#define OBJC_TYPED_STREAM_VERSION 0x01
+
+typedef struct objc_typed_stream {
+  void* physical;
+  cache_ptr object_table;      /* read/written objects */
+  cache_ptr stream_table;      /* other read/written but shared things.. */
+  cache_ptr class_table;       /* class version mapping */
+  cache_ptr object_refs;       /* forward references */
+  int mode;                    /* OBJC_READONLY or OBJC_WRITEONLY */
+  int type;                    /* MANAGED, FILE, MEMORY etc bit string */
+  int version;                 /* version used when writing */
+  int writing_root_p;
+  objc_typed_read_func read;
+  objc_typed_write_func write;
+  objc_typed_eof_func eof;
+  objc_typed_flush_func flush;
+} TypedStream;
+
+/* opcode masks */
+#define _B_VALUE   0x1fU
+#define _B_CODE    0xe0U
+#define _B_SIGN    0x10U
+#define _B_NUMBER  0x0fU
+
+/* standard opcodes */
+#define _B_INVALID 0x00U
+#define _B_SINT    0x20U
+#define _B_NINT    0x40U
+#define _B_SSTR    0x60U
+#define _B_NSTR    0x80U
+#define _B_RCOMM   0xa0U
+#define _B_UCOMM   0xc0U
+#define _B_EXT     0xe0U
+
+/* eXtension opcodes */
+#define _BX_OBJECT  0x00U
+#define _BX_CLASS   0x01U
+#define _BX_SEL     0x02U
+#define _BX_OBJREF  0x03U
+#define _BX_OBJROOT 0x04U
+#define _BX_EXT     0x1fU
+
+/*
+** Read and write objects as specified by TYPE.  All the `last'
+** arguments are pointers to the objects to read/write.  
+*/
+
+int objc_write_type (TypedStream* stream, const char* type, const void* data);
+int objc_read_type (TypedStream* stream, const char* type, void* data);
+
+int objc_write_types (TypedStream* stream, const char* type, ...);
+int objc_read_types (TypedStream* stream, const char* type, ...);
+
+int objc_write_object_reference (TypedStream* stream, id object);
+int objc_write_root_object (TypedStream* stream, id object);
+
+long objc_get_stream_class_version (TypedStream* stream, Class class);
+
+
+/*
+** Convenience functions
+*/
+
+int objc_write_array (TypedStream* stream, const char* type,
+                     int count, const void* data);
+int objc_read_array (TypedStream* stream, const char* type,
+                    int count, void* data);
+
+int objc_write_object (TypedStream* stream, id object);
+int objc_read_object (TypedStream* stream, id* object);
+
+
+
+/*
+** Open a typed stream for reading or writing.  MODE may be either of
+** OBJC_READONLY or OBJC_WRITEONLY.  
+*/
+
+TypedStream* objc_open_typed_stream (FILE* physical, int mode);
+TypedStream* objc_open_typed_stream_for_file (const char* file_name, int mode);
+
+void objc_close_typed_stream (TypedStream* stream);
+
+BOOL objc_end_of_typed_stream (TypedStream* stream);
+void objc_flush_typed_stream (TypedStream* stream);
+
+#endif /* not __typedstream_INCLUDE_GNU */