From c72fc2d9d984e877d5f912c3e59e8a12d1aad03c Mon Sep 17 00:00:00 2001 From: Tom Wood Date: Sat, 10 Apr 1993 01:05:19 +0000 Subject: [PATCH] Initial revision From-SVN: r4077 --- gcc/objc/Object.h | 121 ++++ gcc/objc/Object.m | 454 ++++++++++++++ gcc/objc/Protocol.h | 57 ++ gcc/objc/Protocol.m | 127 ++++ gcc/objc/archive.c | 1378 +++++++++++++++++++++++++++++++++++++++++++ gcc/objc/class.c | 378 ++++++++++++ gcc/objc/init.c | 272 +++++++++ gcc/objc/misc.c | 78 +++ gcc/objc/objc-api.h | 440 ++++++++++++++ gcc/objc/objects.c | 75 +++ gcc/objc/runtime.h | 71 +++ gcc/objc/sarray.c | 428 ++++++++++++++ gcc/objc/sarray.h | 224 +++++++ gcc/objc/selector.c | 143 +++++ gcc/objc/sendmsg.c | 574 ++++++++++++++++++ 15 files changed, 4820 insertions(+) create mode 100644 gcc/objc/Object.h create mode 100644 gcc/objc/Object.m create mode 100644 gcc/objc/Protocol.h create mode 100644 gcc/objc/Protocol.m create mode 100644 gcc/objc/archive.c create mode 100644 gcc/objc/class.c create mode 100644 gcc/objc/init.c create mode 100644 gcc/objc/misc.c create mode 100644 gcc/objc/objc-api.h create mode 100644 gcc/objc/objects.c create mode 100644 gcc/objc/runtime.h create mode 100644 gcc/objc/sarray.c create mode 100644 gcc/objc/sarray.h create mode 100644 gcc/objc/selector.c create mode 100644 gcc/objc/sendmsg.c diff --git a/gcc/objc/Object.h b/gcc/objc/Object.h new file mode 100644 index 00000000000..33e10bc6b5d --- /dev/null +++ b/gcc/objc/Object.h @@ -0,0 +1,121 @@ +/* Interface for the Object class for Objective-C. + 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 +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, 675 Mass Ave, Cambridge, MA 02139, 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 + +/* + * All classes are derived from Object. As such, + * this is the overhead tacked onto those objects. + */ +@interface Object +{ + Class_t 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_t)class; +- (Class_t)superClass; +- (MetaClass_t)metaClass; +- (const char *)name; + + /* Identifying and comparing objects */ +- self; +- (unsigned int)hash; +- (BOOL)isEqual:anObject; + + /* Testing object type */ +- (BOOL)isMetaClass; +- (BOOL)isClass; +- (BOOL)isInstance; + + /* Testing inheritance relationships */ +- (BOOL)isKindOf:(Class_t)aClassObject; +- (BOOL)isMemberOf:(Class_t)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 */ +- forward:(SEL)aSel :(arglist_t)argFrame; +- performv:(SEL)aSel :(arglist_t)argFrame; + + /* Posing */ ++ poseAs:(Class_t)aClassObject; +- (Class_t)transmuteClassTo:(Class_t)aClassObject; + + /* Enforcing intentions */ +- subclassResponsibility:(SEL)aSel; +- notImplemented:(SEL)aSel; + + /* Error handling */ +- doesNotRecognize:(SEL)aSel; +- error:(const char *)aString, ...; + + /* Archiving */ ++ (int)version; ++ setVersion:(int)aVersion; + +- read: (TypedStream*)aStream; +- write: (TypedStream*)aStream; + ++ read: (TypedStream*)aStream; ++ write: (TypedStream*)aStream; + +@end + +#endif diff --git a/gcc/objc/Object.m b/gcc/objc/Object.m new file mode 100644 index 00000000000..330c9a80e7a --- /dev/null +++ b/gcc/objc/Object.m @@ -0,0 +1,454 @@ +/* The implementation of class Object for Objective-C. + 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 +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, 675 Mass Ave, Cambridge, MA 02139, 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 +#include +#include +#include + +extern int strlen(const char *); +extern int strcmp(const char *, const char *); +extern int read(int, void *, int); +extern int write(int, const void *, int); +extern int errno; + +#define MAX_CLASS_NAME_LEN 256 + +@implementation Object + +// Initialize a class +// often overridden by subclasses ++ initialize +{ + return self; +} + +// Initialize an instance +// this method does not apply to class objects +// often overridden by subclasses (should call superclass version) +- init +{ + return self; +} + +// Create and initialize an instance of a class +// not usually overridden by subclasses (should call superclass version) ++ new +{ + return [[self alloc] init]; +} + +// Creates an instance of a class +// should NOT be overridden by subclasses ++ alloc +{ + return class_create_instance(self); +} + +// Free an instance +// this method does not apply to class objects +// often overridden by subclasses (should call superclass version) +- free +{ + return object_dispose(self); +} + +// Create a copy of the receiving instance +// this method does not apply to class objects +// not usually overridden by subclasses +- copy +{ + return [[self shallowCopy] deepen]; +} + +// Creates a copy of only the receiving instance +// this method does not apply to class objects +// should NOT be overridden by subclasses +- shallowCopy +{ + return object_copy(self); +} + +// Deepens a shallow copy of an instance +// this method does not apply to class objects +// often overridden by subclasses (should call superclass version) +- deepen +{ + return self; +} + +// Creates a recursive copy of the receiving instance +// this method does not apply to class objects +// may be overridden by subclasses +// Not correct, but included for compatibility with Stepstone +- deepCopy +{ + return [self copy]; +} + +// Return the class object or the class of an instance +// not usually overridden by subclasses +- (Class_t)class +{ + return object_get_class(self); +} + +// Return the superclass of a class or instance +// not usually overridden by subclasses +- (Class_t)superClass +{ + return object_get_super_class(self); +} + +// Return the metaclass of a class or instance +// not usually overridden by subclasses +- (MetaClass_t)metaClass +{ + return object_get_meta_class(self); +} + +// Return the character string name of a class or an instance's class +// not usually overridden by subclasses +- (const char *)name +{ + return object_get_class_name(self); +} + +// Return the receiving class or instance object +// not usually overridden by subclasses +- self +{ + return self; +} + +// Return a hash value for a class or instance object +// not usually overridden by subclasses +- (unsigned int)hash +{ + return (unsigned int)self; +} + +// Indicates if anObject is the receiving class or instance object +// not usually overridden by subclasses +- (BOOL)isEqual:anObject +{ + return self==anObject; +} + +// Indicates if the receiver is a metaclass object +// should NOT be overridden by subclasses +- (BOOL)isMetaClass +{ + return NO; +} + +// Indicates if the receiver is a class object +// should NOT be overridden by subclasses +- (BOOL)isClass +{ + return object_is_class(self); +} + +// Indicates if the receiver is an instance object +// should NOT be overridden by subclasses +- (BOOL)isInstance +{ + return object_is_instance(self); +} + +// Indicates if the receiver is a type of aClassObject +// not usually overridden by subclasses +- (BOOL)isKindOf:(Class_t)aClassObject +{ + Class_t class; + + for (class = self->isa; class!=Nil; class = class_get_super_class(class)) + if (class==aClassObject) + return YES; + return NO; +} + +// Indicates if the receiver is a member of the aClassObject class +// not usually overridden by subclasses +- (BOOL)isMemberOf:(Class_t)aClassObject +{ + return self->isa==aClassObject; +} + +// Indicates if the receiver is a type of the class named aClassName +// not usually overridden by subclasses +- (BOOL)isKindOfClassNamed:(const char *)aClassName +{ + Class_t 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; +} + +// Indicates if the receiver is a member of the class named aClassName +// not usually overridden by subclasses +- (BOOL)isMemberOfClassNamed:(const char *)aClassName +{ + return ((aClassName!=NULL) + &&!strcmp(class_get_class_name(self->isa), aClassName)); +} + +// Indicates if instances of a class respond to the message aSel +// not usually overridden by subclasses ++ (BOOL)instancesRespondTo:(SEL)aSel +{ + return class_get_instance_method(self, aSel)!=METHOD_NULL; +} + +// Indicates if the receiving class or instance responds to the message aSel +// not usually overridden by subclasses +- (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); +} + +// Returns the address of a class's instance method +// not usually overridden by subclasses ++ (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 +- (BOOL) conformsTo: (Protocol*)aProtocol +{ + int i; + struct objc_protocol_list* proto_list; + + for (proto_list = isa->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; + } + } + + return NO; +} + + +// Returns the address of a class's class or an instance's instance method +// not usually overridden by subclasses +- (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))); +} + +// Returns a method description for a class's instance method aSel +// not usually overridden by subclasses ++ (struct objc_method_description *)descriptionForInstanceMethod:(SEL)aSel +{ + return ((struct objc_method_description *) + class_get_instance_method(self, aSel)); +} + +// Returns a description for a class's class or an instance's instance method +// not usually overridden by subclasses +- (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))); +} + +// Sends the message aSel, which takes no parameters, to the receiver +// not usually overridden by subclasses +- 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); +} + +// Sends the message aSel, which takes one id parameter, to the receiver +// not usually overridden by subclasses +- 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); +} + +// Sends the message aSel, which takes two id parameters, to the receiver +// not usually overridden by subclasses +- 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); +} + +// Forwards a message to which a class or instance object does not respond +// may be overridden by subclasses +- forward:(SEL)aSel :(arglist_t)argFrame +{ + return [self doesNotRecognize: aSel]; +} + +// Sends a message aSel, of arbitrary arguments, to the receiver +// should NOT be overridden by subclasses +- performv:(SEL)aSel :(arglist_t)argFrame +{ + return objc_msg_sendv(self, aSel, method_get_argsize(0), argFrame); +} + +// Instructs the runtime system that the receiver is to pose for aClassObject +// not usually overridden by subclasses ++ poseAs:(Class_t)aClassObject +{ + return class_pose_as(self, aClassObject); +} + +// Changes the receiver's class to be aClassObject +// this method does not apply to class objects +// not usually overridden by subclasses +- (Class_t)transmuteClassTo:(Class_t)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_t old_isa = isa; + isa = aClassObject; + return old_isa; + } + return nil; +} + +// Indicates that a subclass did not override a class or instance message +// it was supposed to have overridden +// not usually overridden by subclasses +- subclassResponsibility:(SEL)aSel +{ + return [self error:"subclass should override %s", sel_get_name(aSel)]; +} + +// Indicates that a class or instance method has not been implemented +// may be overridden by subclasses +- notImplemented:(SEL)aSel +{ + return [self error:"method %s not implemented", sel_get_name(aSel)]; +} + +// Reports that a class or instance does not recognize the message aSel +// not usually overridden by subclasses +- doesNotRecognize:(SEL)aSel +{ + return [self error:"%s does not recognize %s", + object_get_class_name(self), sel_get_name(aSel)]; +} + +// Reports an error +// not usually overridden by subclasses +- error:(const char *)aString, ... +{ +#define FMT "error: %s (%s)\n%s\n" + char fmt[(strlen(FMT)+strlen(object_get_class_name(self)) + +((aString!=NULL)?strlen(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_error)(self, fmt, ap); + va_end(ap); + return nil; +#undef FMT +} + +// Returns the class's version number +// not usually overridden by subclasses ++ (int)version +{ + return class_get_version(self); +} + +// Sets the class's version number +// not usually overridden by subclasses ++ setVersion:(int)aVersion +{ + class_set_version(self, aVersion); + return 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; +} + +// These are used to read or write class information, such as static +// variables used in that class. The version number of the class being +// read can be obtained from +// objc_typed_stream_class_version(stream, class_name) + ++ write: (TypedStream*)aStream +{ + // [super write: aStream]; + return self; +} + ++ read: (TypedStream*)aStream +{ + // [super read: aStream]; + return self; +} + + + +@end diff --git a/gcc/objc/Protocol.h b/gcc/objc/Protocol.h new file mode 100644 index 00000000000..1b26bcb33b6 --- /dev/null +++ b/gcc/objc/Protocol.h @@ -0,0 +1,57 @@ +/* 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, 675 Mass Ave, Cambridge, MA 02139, 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 + +@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/gcc/objc/Protocol.m b/gcc/objc/Protocol.m new file mode 100644 index 00000000000..f4999bc8e44 --- /dev/null +++ b/gcc/objc/Protocol.m @@ -0,0 +1,127 @@ +/* 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, 675 Mass Ave, Cambridge, MA 02139, 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 +#include + +/* 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/gcc/objc/archive.c b/gcc/objc/archive.c new file mode 100644 index 00000000000..8398984f744 --- /dev/null +++ b/gcc/objc/archive.c @@ -0,0 +1,1378 @@ +/* GNU Objective C Runtime archiving + Copyright (C) 1993 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, 675 Mass Ave, Cambridge, MA 02139, 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. */ + +/* +** Note: This version assumes that int and longs are both 32bit. +** Most API function are in the bottom of this file. +*/ + +#include "runtime.h" +#include + +#define __objc_fatal(format, args...) \ + { fprintf(stderr, "archining: "); \ + fprintf(stderr, format, ## args); \ + fprintf(stderr, "\n"); abort(); } + +/* Declare some functions... */ + +static int +objc_read_class (struct objc_typed_stream* stream, Class** class); + +static int +objc_sizeof_type(const char* type); + +static int +objc_write_use_common (struct objc_typed_stream* stream, unsigned int key); + +static int +objc_write_register_common (struct objc_typed_stream* stream, + unsigned int key); + +static int +objc_write_class (struct objc_typed_stream* stream, + struct objc_class* class); + +static const char* +__objc_skip_type (const char* type); + +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 <= 0xffU) + return __objc_code_unsigned_char (buf, val); + + else + { + buf[0] = _B_NINT|0x02; + buf[1] = val/0x100; + buf[2] = val%0x100; + return 3; + } +} + +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) +{ + if (val > 0) + return __objc_code_unsigned_short (buf, val); + + if (val > -0x7f) /* val > -128 */ + return __objc_code_char (buf, val); + + else + { + int len = __objc_code_unsigned_short (buf, -val); + buf[0] |= _B_SIGN; + return len; + } +} + +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 < 0x10000) + return __objc_code_unsigned_short (buf, val%0x10000); + + else if (val < 0x1000000) + { + buf[0] = _B_NINT|3; + buf[1] = val/0x10000; + buf[2] = (val%0x10000)/0x100; + buf[3] = val%0x100; + return 4; + } + + else + { + buf[0] = _B_NINT|4; + buf[1] = val/0x1000000; + buf[2] = (val%0x1000000)/0x10000; + buf[3] = (val%0x10000)/0x100; + buf[4] = val%0x100; + return 5; + } +} + +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) +{ + if (val >= 0) + return __objc_code_unsigned_int (buf, val); + + if (val > -0x7f) + return __objc_code_char (buf, val); + + else + { + int len = __objc_code_unsigned_int (buf, -val); + buf[0] |= _B_SIGN; + return len; + } +} + +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); +} + +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 int key; + if ((key = (unsigned int)hash_value_for_key (stream->stream_table, string))) + return objc_write_use_common (stream, key); + else + { + int length; + hash_add (&stream->stream_table, (void*)key=(unsigned int)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 int key) +{ + unsigned char buf[sizeof (unsigned int)+2]; + int len = __objc_code_unsigned_int (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 int key) +{ + unsigned char buf[sizeof (unsigned int)+2]; + int len = __objc_code_unsigned_int (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 + abort(); +} + +__inline__ int +__objc_write_object (struct objc_typed_stream* stream, id object) +{ + unsigned char buf = '\0'; + SEL write_sel = sel_get_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 (struct objc_typed_stream* stream, id object) +{ + unsigned int key; + if ((key = (unsigned int)hash_value_for_key (stream->stream_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->stream_table, (void*)key=(unsigned int)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) +{ + unsigned char buf = '\0'; + SEL write_sel = sel_get_uid ("write:"); + __objc_write_extension (stream, _BX_CLASS); + objc_write_string_atomic(stream, (char*)class->name, + strlen(class->name)); + objc_write_unsigned_int (stream, CLS_GETNUMBER(class)); + (*objc_msg_lookup(class, write_sel))(class, write_sel, stream); + return (*stream->write)(stream->physical, &buf, 1); +} + + +static int +objc_write_class (struct objc_typed_stream* stream, + struct objc_class* class) +{ + unsigned int key; + if ((key = (unsigned int)hash_value_for_key (stream->stream_table, class))) + return objc_write_use_common (stream, key); + else + { + int length; + hash_add (&stream->stream_table, (void*)key=(unsigned int)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 = sel_get_name (selector); + __objc_write_extension (stream, _BX_SEL); + return objc_write_string (stream, sel_name, strlen(sel_name)); +} + +int +objc_write_selector (struct objc_typed_stream* stream, SEL selector) +{ + const char* sel_name = sel_get_name (selector); + unsigned int key; + if ((key = (unsigned int)hash_value_for_key (stream->stream_table, sel_name))) + return objc_write_use_common (stream, key); + else + { + int length; + hash_add (&stream->stream_table, (void*)key=(unsigned int)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_fatal("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_fatal("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_fatal("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_fatal("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_fatal("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_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_fatal("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; +} + +__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 int key = 0; + + if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */ + { + len = __objc_read_nbyte_uint(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*)malloc(length+1); + if (key) + hash_add (&stream->stream_table, (void*)key, *string); + len = (*stream->read)(stream->physical, *string, length); + (*string)[length] = '\0'; + } + break; + + case _B_UCOMM: + { + len = __objc_read_nbyte_uint(stream, (buf[0] & _B_VALUE), &key); + (*string) = hash_value_for_key (stream->stream_table, (void*)key); + } + break; + + case _B_NSTR: + { + unsigned int nbytes = buf[0]&_B_VALUE; + len = __objc_read_nbyte_uint(stream, nbytes, &nbytes); + if (len) { + (*string) = (char*)malloc(nbytes); + if (key) + hash_add (&stream->stream_table, (void*)key, *string); + len = (*stream->read)(stream->physical, *string, buf[0]&_B_VALUE); + (*string)[nbytes] = '\0'; + } + } + break; + + default: + __objc_fatal("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_uid ("read:"); + unsigned int key = 0; + + if ((buf[0]&_B_CODE) == _B_RCOMM) /* register common */ + { + len = __objc_read_nbyte_uint(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->stream_table, (void*)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_fatal("expected null-byte, got opcode %c", buf[0]); + } + + else if ((buf[0]&_B_CODE) == _B_UCOMM) + { + if (key) + __objc_fatal("cannot register use upcode..."); + len = __objc_read_nbyte_uint(stream, (buf[0] & _B_VALUE), &key); + (*object) = hash_value_for_key (stream->stream_table, (void*)key); + } + + else + __objc_fatal("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))) + { + SEL read_sel = sel_get_uid ("read:"); + unsigned int key = 0; + + if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */ + { + len = __objc_read_nbyte_uint(stream, (buf[0] & _B_VALUE), &key); + len = (*stream->read)(stream->physical, buf, 1); + } + + if (buf[0] == (_B_EXT | _BX_CLASS)) + { + char* class_name; + int version; + + /* get class */ + len = objc_read_string (stream, &class_name); + (*class) = objc_get_class(class_name); + free (class_name); + + /* register */ + if (key) + hash_add (&stream->stream_table, (void*)key, *class); + + /* call +read: */ + (*objc_msg_lookup(*class, read_sel))(*class, read_sel, stream); + + objc_read_unsigned_int(stream, &version); + hash_add (&stream->class_table, (*class)->name, (void*)version); + + /* check null-byte */ + len = (*stream->read)(stream->physical, buf, 1); + if (buf[0] != '\0') + __objc_fatal("expected null-byte, got opcode %c", buf[0]); + } + + else if ((buf[0]&_B_CODE) == _B_UCOMM) + { + if (key) + __objc_fatal("cannot register use upcode..."); + len = __objc_read_nbyte_uint(stream, (buf[0] & _B_VALUE), &key); + (*class) = hash_value_for_key (stream->stream_table, (void*)key); + if (!*class) + __objc_fatal("cannot find class for key %x", key); + } + + else + __objc_fatal("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 int key = 0; + + if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */ + { + len = __objc_read_nbyte_uint(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); + (*selector) = sel_get_uid(selector_name); + free (selector_name); + + /* register */ + if (key) + hash_add (&stream->stream_table, (void*)key, *selector); + } + + else if ((buf[0]&_B_CODE) == _B_UCOMM) + { + if (key) + __objc_fatal("cannot register use upcode..."); + len = __objc_read_nbyte_uint(stream, (buf[0] & _B_VALUE), &key); + (*selector) = hash_value_for_key (stream->stream_table, (void*)key); + } + + else + __objc_fatal("expected selector, got opcode %c", buf[0]); + } + return len; +} + +static int +objc_sizeof_type(const char* type) +{ + 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: + case _C_LNG: + return sizeof(int); + break; + + case _C_UINT: + case _C_ULNG: + return sizeof(unsigned int); + break; + + case _C_ATOM: + case _C_CHARPTR: + return sizeof(char*); + break; + + default: + fprintf(stderr, "objc_write_type: cannot parse typespec: %s\n", type); + abort(); + } +} + + +static const char* +__objc_skip_type (const char* type) +{ + switch (*type) { + case _C_ID: + 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_FLT: + case _C_DBL: + return ++type; + break; + + case _C_ARY_B: + while(isdigit(*++type)); + type = __objc_skip_type(type); + if (*type == _C_ARY_E) + return ++type; + else + __objc_fatal("cannot parse typespec: %s", type); + break; + + default: + fprintf(stderr, "objc_read_types: cannot parse typespec: %s\n", type); + abort(); + } +} + +/* +** 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: + case _C_LNG: + return objc_write_int(stream, *(int*)data); + break; + + case _C_UINT: + case _C_ULNG: + return objc_write_unsigned_int(stream, *(unsigned int*)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; + + default: + fprintf(stderr, "objc_write_type: cannot parse typespec: %s\n", type); + abort(); + } +} + +/* +** 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: + case _C_LNG: + return objc_read_int (stream, (int*)data); + break; + + case _C_UINT: + case _C_ULNG: + return objc_read_unsigned_int (stream, (unsigned int*)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; + + default: + fprintf(stderr, "objc_write_type: cannot parse typespec: %s\n", type); + abort(); + } +} + +/* +** 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_type (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: + case _C_LNG: + res = objc_write_int(stream, *va_arg(args, int*)); + break; + + case _C_UINT: + case _C_ULNG: + res = objc_write_unsigned_int(stream, *va_arg(args, unsigned int*)); + 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_type (t); + if (*t != _C_ARY_E) + __objc_fatal("expected `]', got: %s", t); + } + break; + + default: + fprintf(stderr, "objc_write_type: cannot parse typespec: %s\n", type); + abort(); + } + } + 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_type(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: + case _C_LNG: + res = objc_read_int(stream, va_arg(args, int*)); + break; + + case _C_UINT: + case _C_ULNG: + res = objc_read_unsigned_int(stream, va_arg(args, unsigned int*)); + 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_type (t); + if (*t != _C_ARY_E) + __objc_fatal("expected `]', got: %s", t); + } + break; + + default: + fprintf(stderr, "objc_read_type: cannot parse typespec: %s\n", type); + abort(); + } + } + 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]", 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]", 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_fatal ("TypedStream not open for writing"); +} + +static int +__objc_no_read(FILE* file, char* data, int len) +{ + __objc_fatal ("TypedStream not open for reading"); +} + +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_fatal ("cannot handle TypedStream version %d", stream->version); +} + +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); +} + +/* +** Open the stream PHYSICAL in MODE +*/ + +TypedStream* +objc_open_typed_stream (FILE* physical, int mode) +{ + int fflush(FILE*); + + TypedStream* s = (TypedStream*)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->eof = (objc_typed_eof_func)__objc_feof; + s->flush = (objc_typed_flush_func)fflush; + if (mode == OBJC_READONLY) + { + s->class_table = hash_new(8, (hash_func_type)hash_string, + (compare_func_type)compare_strings); + 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->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->type == (OBJC_MANAGED_STREAM | OBJC_FILE_STREAM)) + fclose ((FILE*)stream->physical); + hash_delete (stream->stream_table); + if (stream->mode == OBJC_READONLY) + hash_delete (stream->class_table); + 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); +} + diff --git a/gcc/objc/class.c b/gcc/objc/class.c new file mode 100644 index 00000000000..a5feef57b97 --- /dev/null +++ b/gcc/objc/class.c @@ -0,0 +1,378 @@ +/* GNU Objective C Runtime class related functions + Copyright (C) 1993 Free Software Foundation, Inc. + +Author: Kresten Krab Thorup, 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, 675 Mass Ave, Cambridge, MA 02139, 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 */ + + +/* The table of classname->class. Used for objc_lookup_class and friends */ +static cache_ptr __objc_class_hash = 0; + +/* 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_t (*_objc_lookup_class)(const char* name) = 0; + + +/* True when class links has been resolved */ +BOOL __objc_class_links_resolved = NO; + + +/* 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_class_hash + = hash_new (CLASS_HASH_SIZE, + (hash_func_type) hash_string, + (compare_func_type) compare_strings); +} + +/* 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_t class) +{ + Class_t h_class; + + /* 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); + } +} + +/* 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_t objc_lookup_class (const char* name) +{ + Class_t class; + + /* Make sure the class hash table exists. */ + assert (__objc_class_hash); + + class = hash_value_for_key (__objc_class_hash, name); + + 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_t +objc_get_class (const char *name) +{ + Class_t class; + + /* Make sure the class hash table exists. */ + assert (__objc_class_hash); + + class = hash_value_for_key (__objc_class_hash, name); + + if (class) + return class; + + if (_objc_lookup_class) + class = (*_objc_lookup_class)(name); + + if(class) + return class; + + fprintf(stderr, "objc runtime: cannot find class %s\n", name); + abort(); +} + + +/* 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_t class1; + Class_t object_class = objc_get_class ("Object"); + + assert(object_class); + + /* Assign subclass links */ + for (node = hash_next (__objc_class_hash, NULL); node; + node = hash_next (__objc_class_hash, node)) + { + Class_t 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_t 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_t class1 = node->value; + Class_t 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; + } + } +} + + +/* This is a incomplete implementation of posing. This function does the + bulk of the work but does not initialize the class method caches. That is + a run-time specific operation. + +I implement posing by hiding SUPER_CLASS, creating new class and meta class + structures, initializing it with IMPOSTOR, and changing it such that it is + identified as SUPER_CLASS. SUPER_CLASS remains in the hierarchy but is + inaccessible by the means. The class hierarchy is then re arranged such + that all of the subclasses of SUPER_CLASS now inherit from the new class + structures -- except the impostor itself. The only dramatic effect on the + application is that subclasses of SUPER_CLASS cannot do a [ .... + super_class ] and expect their real super class. */ +Class_t +class_pose_as (Class_t impostor, Class_t super_class) +{ + Class_t new_class = (Class_t) calloc (1, sizeof (Class)); + MetaClass_t new_meta_class = + (MetaClass_t) __objc_xmalloc(sizeof (MetaClass)); + node_ptr node; + char *new_name = (char *)__objc_xmalloc (strlen (super_class->name) + 12); + + /* We must know the state of the hierachy. Do initial setup if needed */ + if(!CLS_ISRESOLV(impostor)) + __objc_resolve_class_links(); + + assert (new_class); + assert (new_meta_class); + assert (new_name); + + assert (CLS_ISCLASS(impostor)); + assert (CLS_ISCLASS(super_class)); + + assert (impostor->instance_size == super_class->instance_size); + + /* Create the impostor class. */ + new_class->class_pointer = new_meta_class; + new_class->super_class = super_class; + new_class->name = super_class->name; + new_class->version = super_class->version; + new_class->info = super_class->info; + new_class->instance_size = super_class->instance_size; + new_class->ivars = super_class->ivars; + new_class->methods = impostor->methods; +#ifdef OBJC_SPARSE_LOOKUP + new_class->dtable = impostor->dtable; +#else + new_class->cache = impostor->cache; +#endif + + /* Create the impostor meta class. */ + new_meta_class->class_pointer = super_class->class_pointer->class_pointer; + new_meta_class->super_class = super_class->class_pointer->super_class; + new_meta_class->name = super_class->class_pointer->name; + new_meta_class->version = super_class->class_pointer->version; + new_meta_class->info = super_class->class_pointer->info; + new_meta_class->instance_size = super_class->class_pointer->instance_size; + new_meta_class->ivars = super_class->class_pointer->ivars; + new_meta_class->methods = impostor->class_pointer->methods; +#ifdef OBJC_SPARSE_LOOKUP + new_meta_class->dtable = impostor->class_pointer->dtable; +#else + new_meta_class->cache = impostor->class_pointer->cache; +#endif + + /* Now change super/subclass links of all related classes. This is rather + complex, since we have both super_class link, and subclass_list for the + involved classes. */ + { + Class_t *classpp; + MetaClass_t *metaclasspp; + + /* Remove impostor from subclass list of super_class */ + for (classpp = &(super_class->subclass_list); + *classpp; + classpp = &((*classpp)->sibling_class)) + { + if (*classpp == impostor) + *classpp = (*classpp)->sibling_class; + if (*classpp == 0) + break; + } + + /* Do the same for the meta classes */ + + for (metaclasspp = &(super_class->class_pointer->subclass_list); + *metaclasspp; + metaclasspp = &((*metaclasspp)->sibling_class)) + { + if (*metaclasspp == impostor->class_pointer) + *metaclasspp = (*metaclasspp)->sibling_class; + if (*metaclasspp == 0) + break; + } + + /* From the loop above, classpp now points to the sibling_class entry */ + /* of the last element in the list of subclasses for super_class */ + + /* Append the subclass list of impostor to the subclass list of */ + /* superclass, and excange those two and set subclass of */ + /* super_class to be impostor only */ + + *classpp = impostor->subclass_list; + new_class->subclass_list = super_class->subclass_list; + super_class->subclass_list = new_class; + new_class->sibling_class = 0; + + /* Do the same thing for the meta classes */ + *metaclasspp = impostor->class_pointer->subclass_list; + new_meta_class->subclass_list = super_class->class_pointer->subclass_list; + super_class->class_pointer->subclass_list = new_meta_class; + new_meta_class->sibling_class = 0; + + /* Update superclass links for all subclasses of new_class */ + for (classpp = &(new_class->subclass_list); *classpp; + classpp = &((*classpp)->sibling_class)) + (*classpp)->super_class = new_class; + + for (metaclasspp = &(new_meta_class->subclass_list); *metaclasspp; + metaclasspp = &((*metaclasspp)->sibling_class)) + (*metaclasspp)->super_class = new_meta_class; + + } + + /* Delete the class from the hash table, change its name so that it can no + longer be found, then place it back into the hash table using its new + name. + + Don't worry about the class number. It is already assigned. + memory is lost with the hash key.) */ + hash_remove (__objc_class_hash, super_class->name); + sprintf (new_name, "%s*", super_class->name); + super_class->name = new_name; + super_class->class_pointer->name = new_name; + hash_add (&__objc_class_hash, super_class->name, super_class); + + /* Place the impostor class in class hash table and assign it a class + number. */ + __objc_add_class_to_hash (new_class); + + /* Now update dispatch tables for new_class and it's subclasses */ + __objc_update_dispatch_table_for_class ((Class_t) new_meta_class); + __objc_update_dispatch_table_for_class (new_class); + + return new_class; +} + +#ifdef OBJC_HASH_LOOKUP +__objc_class_hash_tables_size () +{ + node_ptr node; + Class_t class1; + int total = 0; + + for (node = hash_next (__objc_class_hash, NULL); node; + node = hash_next (__objc_class_hash, node)) + { + Class_t class1 = node->value; + total += (class1->cache->mask)*sizeof(struct objc_bucket); + total += sizeof(struct objc_cache); + } + + return total; +} +#endif diff --git a/gcc/objc/init.c b/gcc/objc/init.c new file mode 100644 index 00000000000..eb31f246d68 --- /dev/null +++ b/gcc/objc/init.c @@ -0,0 +1,272 @@ +/* GNU Objective C Runtime initialization + Copyright (C) 1993 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, 675 Mass Ave, Cambridge, MA 02139, 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 5 +#define PROTOCOL_VERSION 2 + +/* This list contains all modules currently loaded into the runtime */ +static struct objc_list* __objc_module_list = 0; + +/* This list contains all proto_list's not yet assigned class links */ +static struct objc_list* unclaimed_proto_list = 0; + +/* 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_t, struct objc_protocol_list*); + +/* Is all categories/classes resolved? */ +BOOL __objc_dangling_categories = NO; + +/* 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) +{ + /* Has we processed any constructors previously? This flag 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.h) generated by gcc */ + Symtab_t symtab = module->symtab; + + /* Pointer to the class Object class object */ + Class_t object_class; + + /* 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) + { + __objc_init_selector_tables(); + __objc_init_class_tables(); + __objc_init_dispatch_tables(); + previous_constructors = 1; + } + + /* Save the module pointer for later processing. (not currently used) */ + __objc_module_list = list_cons(module, __objc_module_list); + + /* 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_t class = (Class_t) symtab->defs[i]; + + /* 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); + + /* 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_t) class->class_pointer); + + /* Install the fake dispatch tables */ + __objc_install_premature_dtable(class); + __objc_install_premature_dtable(class->class_pointer); + + if (class->protocols) + __objc_init_protocols (class->protocols); + } + + /* Replace referenced selectors from names to SEL's. */ + for (i = 0; selectors[i]; ++i) + selectors[i] = sel_register_name ((const char *) selectors[i]); + + /* 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_t 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_t) class->class_pointer, + category->class_methods); + + if (category->protocols) + { + __objc_init_protocols (category->protocols); + __objc_class_add_protocols (class, category->protocols); + } + + } + else + { + /* The object to which the category methods belong can't be found. + Save the information. */ + unclaimed_categories = list_cons(category, unclaimed_categories); + } + } + + /* Scan the unclaimed category hash. Attempt to attach any unclaimed + categories to objects. */ + for (cell = &unclaimed_categories; + *cell; + *cell && (cell = &(*cell)->tail)) + { + Category_t category = (*cell)->head; + Class_t 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_t) class->class_pointer, + category->class_methods); + + if (category->protocols) + { + __objc_init_protocols (category->protocols); + __objc_class_add_protocols (class, category->protocols); + } + + } + } + + 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; + } + +} + +/* 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))) + { + fprintf (stderr, "Module %s version %d doesn't match runtime %d\n", + module->name, module->version, OBJC_VERSION); + if(module->version > OBJC_VERSION) + fprintf (stderr, "Runtime (libobjc.a) is out of date\n"); + else if (module->version < OBJC_VERSION) + fprintf (stderr, "Compiler (gcc) is out of date\n"); + else + fprintf (stderr, "Objective C internal error -- bad Module size\n"); + abort (); + } +} + +static void +__objc_init_protocols (struct objc_protocol_list* protos) +{ + int i; + Class_t proto_class; + + if (! protos) + return; + + proto_class = objc_lookup_class("Protocol"); + + if (proto_class == 0 && ! list_find (&unclaimed_proto_list, protos)) + { + unclaimed_proto_list = list_cons (protos, unclaimed_proto_list); + return; + } + + assert (protos->next == 0); /* only single ones allowed */ + + for(i = 0; i < protos->count; i++) + { + if (((int)((id)protos->list[i])->class_pointer) == PROTOCOL_VERSION) + ((id)protos->list[i])->class_pointer = proto_class; + else + { + fprintf (stderr, + "Version %d doesn't protocol version %d\n", + ((int)((id)protos->list[i])->class_pointer), + PROTOCOL_VERSION); + abort (); + } + } +} + +static void __objc_class_add_protocols (Class_t class, + struct objc_protocol_list* protos) +{ +#ifndef NeXT_OBJC /* force class Protocol to be linked in */ + extern char* __objc_class_name_Protocol; + char* x = __objc_class_name_Protocol; +#endif + + /* Well... */ + if (! protos) + return; + + /* Add it... */ + protos->next = class->protocols; + class->protocols = protos; +} diff --git a/gcc/objc/misc.c b/gcc/objc/misc.c new file mode 100644 index 00000000000..53adc82fa99 --- /dev/null +++ b/gcc/objc/misc.c @@ -0,0 +1,78 @@ +/* GNU Objective C Runtime Miscellanious + Copyright (C) 1993 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, 675 Mass Ave, Cambridge, MA 02139, 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" + +void* malloc(size_t); +void* realloc(void*, size_t); + +void objc_error(id object, const char* fmt, va_list); + +void (*_objc_error)(id, const char*, va_list) = objc_error; +/* id (*_objc_object_alloc)(Class_t) = 0; */ +/* id (*_objc_object_dispose)(id) = 0; */ +/* id (*_objc_object_copy)(id) = 0; */ + +void +objc_error(id object, const char* fmt, va_list ap) +{ + vfprintf (stderr, fmt, ap); + abort (); +} + +volatile void +objc_fatal(const char* msg) +{ + write(2, msg, strlen(msg)); + abort(); +} + +void* +__objc_xmalloc(size_t size) +{ + void* res = malloc(size); + if(!res) + objc_fatal("Virtual memory exhausted\n"); + return res; +} + +void* +__objc_xrealloc(void* mem, size_t size) +{ + void* res = realloc(mem, size); + if(!res) + objc_fatal("Virtual memory exhausted\n"); + return res; +} + +void* +__objc_xcalloc(size_t nelem, size_t size) +{ + void* res = (void*)calloc(nelem, size); + if(!res) + objc_fatal("Virtual memory exhausted\n"); + return res; +} diff --git a/gcc/objc/objc-api.h b/gcc/objc/objc-api.h new file mode 100644 index 00000000000..ff2b3f22679 --- /dev/null +++ b/gcc/objc/objc-api.h @@ -0,0 +1,440 @@ +/* GNU Objective-C Runtime API. + 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 +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, 675 Mass Ave, Cambridge, MA 02139, 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 +#include + +static const ARGSIZE = 96; /* for `method_get_argsize()' */ + +/* +** Points to the function that the runtime system calls to handle +** an error. By default, it prints formatted error messages to the +** standard error stream and calls abort to produce a core file. +** The function is guaranteed to be passed a valid object and a +** non-NULL format string. +*/ +extern void (*_objc_error)(id object, const char *format, va_list args); + +/* +** 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_t (*_objc_lookup_class)(const char *name); + +/* +** Points to the function that the runtime system calls to allocate +** memory for new instances. Called through class_create_instance(), +** this function should return a valid block of memory of at least +** class_get_instance_size() bytes, or nil upon failure. The +** function is guaranteed to be passed a valid class object. +*/ +extern id (*_objc_object_alloc)(Class_t class); + +/* +** Points to the function that the runtime system calls to create +** an exact copy of an object. Called through object_copy(), this +** function should return a new instance of object's class created +** by class_create_instance() which is bit-identical to object, or +** nil upon failure. The function is guaranteed to be passed a +** valid instance object. +*/ +extern id (*_objc_object_copy)(id object); + +/* +** Points to the function that the runtime system calls to free +** instances. Called through object_dispose(), this function +** should free the memory pointed to by object and return nil. +** This function is not responsible for freeing memory pointed +** to by any of the object's instance variables. The function +** is guaranteed to be passed a valid instance object. +*/ +extern id (*_objc_object_dispose)(id object); + +/* +** Searches for a class method specified by aSel, starting with the +** metaclass class and proceeding up the class hierarchy, until either +** the method is found or the root class has been examined. Returns +** a pointer to the method's Method structure if found. Returns the +** value METHOD_NULL if the method is not found, class is not a +** metaclass object, or aSel is not a valid selector. +*/ +Method_t class_get_class_method(MetaClass_t class, SEL aSel); + +/* +** Searches for an instance method specified by aSel, starting with +** the class class and proceeding up the class hierarchy, until either +** the method is found or the root class has been examined. Returns +** a pointer to the method's Method structure if found. Returns the +** value METHOD_NULL if the method is not found, class is not a class +** object, or aSel is not a valid selector. +*/ +Method_t class_get_instance_method(Class_t class, SEL aSel); + +/* +** Causes impostor to pose as its superclass. Messages sent to the +** superclass will actually be sent to the posing class. Instance +** variables may not be declared in the posing class. The posing +** class can add new methods to the class or override existing methods +** in the superclass. Returns non-nil on success. Returns nil if +** either of impostor or superclass are not class objects, impostor is +** not a subclass of superclass, or upon some other error. +*/ +Class_t class_pose_as(Class_t impostor, Class_t superclass); + +/* +** Returns 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_t objc_get_class(const char *name); + +/* +** Returns 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_t objc_lookup_class(const char *name); + +/* +** Returns the method name associated with selector, or NULL +** if selector is not defined. +*/ +const char *sel_get_name(SEL selector); + +/* +** Returns the selector associated with the method name name. If name +** has not been defined or name is NULL, 0 is returned. +*/ +SEL sel_get_uid(const char *name); + +/* +** Registers a selector for name and returns the new selector. If +** name is NULL or the empty string (""), 0 is returned. +*/ +SEL sel_register_name(const char *name); + +/* +** Indicate if aSel is a valid selector. This is not a safe +** operation, and it should really never be nessecary to use. +*/ +BOOL sel_is_mapped (SEL aSel); + +/*******************************************************************/ +/* */ +/* Internal __inline functions */ +/* */ +/*******************************************************************/ + +/* +** Allocates memory for a new object of class class by calling the +** function specified by the variable _objc_object_alloc if non-zero, +** otherwise uses a default method. Then, initializes the object's +** isa instance variable to class, and returns the new object. +** Returns nil if the memory could not be allocated or class is not +** a class object. +*/ +extern inline id +class_create_instance(Class_t class) +{ + id new = nil; + if (CLS_ISCLASS(class)) + new = (_objc_object_alloc + ?(*_objc_object_alloc)(class) + :(id)malloc(class->instance_size)); + if (new!=nil) + new->class_pointer = class; + return new; +} + +/* +** Returns name of the class class or empty string if class is not +** a class object. If class is Nil, the string "Nil" is returned. +*/ +static inline const char * +class_get_class_name(Class_t class) +{ + return CLS_ISCLASS(class)?class->name:((class==Nil)?"Nil":0); +} + +/* +** Returns the size of an instance of class class in bytes, or 0 if +** class is not a class. The size of an instance is at least 4 bytes. +*/ +static inline long +class_get_instance_size(Class_t class) +{ + return CLS_ISCLASS(class)?class->instance_size:0; +} + +/* +** Returns a pointer to class's metaclass, or Nil if class is not a +** class. +*/ +static inline MetaClass_t +class_get_meta_class(Class_t class) +{ + return CLS_ISCLASS(class)?class->class_pointer:Nil; +} + +/* +** Returns a pointer to class's superclass, or Nil if class is not a +** class. Note that the superclass of Object is Nil. +*/ +static inline Class_t +class_get_super_class(Class_t class) +{ + return CLS_ISCLASS(class)?class->super_class:Nil; +} + +/* +** Returns the version number for the class, or -1 if class is not a +** class. +*/ +static inline int +class_get_version(Class_t class) +{ + return CLS_ISCLASS(class)?class->version:-1; +} + +/* +** Returns YES if class is a class, or NO if not. +*/ +static inline BOOL +class_is_class(Class_t class) +{ + return CLS_ISCLASS(class); +} + +/* +** Returns YES if class is a metaclass, or NO if not. +*/ +static inline BOOL +class_is_meta_class(Class_t class) +{ + return CLS_ISMETA(class); +} + + +/* +** Sets the version number of class class. Does nothing if class is +** not a class. +*/ +static inline void +class_set_version(Class_t class, long version) +{ + if (CLS_ISCLASS(class)) + class->version = version; +} + +/* +** Returns the size in bytes of the argument frame to a method. Since +** at least two parameters (self and _cmd) are sent to each method, this +** value will be at least 8. If method is not a valid method, 0 is +** returned. +** +** Currently, the frame size info is only reliable on a NeXT, so until +** we get this fixed, we'll use a value which is most possibly large +** enough. You can possibly reduce this value (96) on anything but a +** Sparc if you don't return structs from the methods forwarded to. +*/ +static inline unsigned int +method_get_argsize(Method_t method) +{ + return ARGSIZE; /* This was a magic number (96)... */ +} + +/* +** Returns a pointer to the implementation of method method. If method +** is not a method, NULL is returned. +*/ +static inline IMP +method_get_imp(Method_t method) +{ + return (method!=METHOD_NULL)?method->method_imp:(IMP)0; +} + +/* +** Returns the implementation (pointer to function) of the method +** identified by a (class, selector) pair. Use this, and *not* +** objc_msg_lookup, since objc_msg_lookup may eventually return a +** pointer to an internal function which does lazy initialization... +*/ +IMP get_imp (Class_t class, SEL sel); + +/* +** Creates a new instance object that's an exact copy of object by +** calling the function pointed to by the variable _objc_object_copy if +** non-zero, otherwise uses a default method. Returns the new object. +** Returns nil if object is not an instance object, memory for the new +** object could not be allocated, or some other error occurred. +*/ +extern inline id +object_copy(id object) +{ + if ((object!=nil)&&CLS_ISCLASS(object->class_pointer)) + { + if (_objc_object_copy) + return (*_objc_object_copy)(object); + else + { + id copy = class_create_instance(object->class_pointer); + if (copy!=nil) + bcopy(object, copy, object->class_pointer->instance_size); + return copy; + } + return nil; + } +} + +/* +** Frees the memory occupied by object by calling the function pointed +** to by the variable _objc_object_dispose if non-zero, otherwise uses +** a default method. Always returns nil. If object is not an instance +** object, does nothing. +*/ +extern inline id +object_dispose(id object) +{ + if ((object!=nil)&&CLS_ISCLASS(object->class_pointer)) + { + if (_objc_object_dispose) + (*_objc_object_dispose)(object); + else + free(object); + } + return nil; +} + +/* +** Returns the class of an object. If object is an instance, this is +** its class object. If object is a class object, returns object (this +** is arguably not correct, but is implemented this way for compatibility +** with NeXT (and Stepstone?)). If object is a metaclass object, or +** object is nil, returns Nil. +*/ +static inline Class_t +object_get_class(id object) +{ + return ((object!=nil) + ? (CLS_ISCLASS(object->class_pointer) + ? object->class_pointer + : (CLS_ISMETA(object->class_pointer) + ? (Class_t)object + : Nil)) + : Nil); +} + +/* +** Returns the name of the class of object. If object is an instace, +** this is the name of its class. If object is a class or a metaclass, +** returns its name. If object is nil, returns "Nil". +*/ +static inline const char * +object_get_class_name(id object) +{ + return ((object!=nil)?(CLS_ISCLASS(object->class_pointer) + ?object->class_pointer->name + :((Class_t)object)->name) + :"Nil"); +} + +/* +** Returns the metaclass of an object. If object is an instance or a +** class, this is the metaclass object for it. If object is a metaclass +** object, or object is nil, returns Nil. +*/ +static inline MetaClass_t +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); +} + +/* +** Returns the superclass of object. If object is an instance or +** a class, this is its superclass-object for it. If object is a +** metaclass-object or nil, this is Nil. +*/ +static inline Class_t +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_t)object)->super_class + :Nil)) + :Nil); +} + +/* +** YES if object is a class, NO if not. +*/ +static inline BOOL +object_is_class(id object) +{ + return CLS_ISCLASS((Class_t)object); +} + +/* +** YES if object is an instance, NO if not. +*/ +static inline BOOL +object_is_instance(id object) +{ + return (object!=nil)&&CLS_ISCLASS(object->class_pointer); +} + +/* +** YES if object is a metaclass, NO if not. +*/ +static inline BOOL +object_is_meta_class(id object) +{ + return CLS_ISMETA((Class_t)object); +} + +/* +** Functions used for archiving. This is not documented yet! +*/ + +TypedStream* new_typed_stream(FILE* physical); +void free_typed_stream(TypedStream* stream); + +void objc_write_object(TypedStream* stream, id object); +int objc_read_object(TypedStream* stream, id *object); + +void objc_write_type(TypedStream* stream, const char* type, const void* data); +void objc_read_type(TypedStream* stream, const char* type, void* data); + +#endif /* not __objc_api_INCLUDE_GNU */ diff --git a/gcc/objc/objects.c b/gcc/objc/objects.c new file mode 100644 index 00000000000..a8f1a3be94f --- /dev/null +++ b/gcc/objc/objects.c @@ -0,0 +1,75 @@ +/* GNU Objective C Runtime class related functions + Copyright (C) 1993 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, 675 Mass Ave, Cambridge, MA 02139, 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 */ + +id __objc_object_alloc(Class_t); +id __objc_object_dispose(id); +id __objc_object_copy(id); + +id (*_objc_object_alloc)(Class_t) = __objc_object_alloc; +id (*_objc_object_dispose)(id) = __objc_object_dispose; +id (*_objc_object_copy)(id) = __objc_object_copy; + +id +class_create_instance(Class_t class) +{ + id res = (*_objc_object_alloc)(class); + res->class_pointer = class; + return res; +} + +id +object_copy(id object) +{ + return (*_objc_object_copy)(object); +} + +id +object_dispose(id object) +{ + return (*_objc_object_dispose)(object); +} + +id __objc_object_alloc(Class_t class) +{ + return (id)__objc_xmalloc(class->instance_size); +} + +id __objc_object_dispose(id object) +{ + free(object); + return 0; +} + +id __objc_object_copy(id object) +{ + id copy = class_create_instance(object->class_pointer); + bcopy(object, copy, object->class_pointer->instance_size); + return copy; +} + + diff --git a/gcc/objc/runtime.h b/gcc/objc/runtime.h new file mode 100644 index 00000000000..6f2a55041da --- /dev/null +++ b/gcc/objc/runtime.h @@ -0,0 +1,71 @@ +/* GNU Objective C Runtime internal declarations + Copyright (C) 1993 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, 675 Mass Ave, Cambridge, MA 02139, 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 /* core data types */ +#include /* runtime api functions */ + +#include /* hash structures */ +#include /* linear lists */ + +#include /* argh! I hate this */ +#include /* for varargs and va_list's */ +#include + +#include + +extern void __objc_add_class_to_hash(Class_t); /* (objc-class.c) */ +extern void __objc_init_selector_tables(); /* (objc-sel.c) */ +extern void __objc_init_class_tables(); /* (objc-class.c) */ +extern void __objc_init_dispatch_tables(); /* (objc-dispatch.c) */ +extern void __objc_install_premature_dtable(Class_t); /* (objc-dispatch.c) */ +extern void __objc_resolve_class_links(); /* (objc-class.c) */ +extern void __objc_register_selectors_from_class(Class_t); /* (objc-sel.c) */ +extern void __objc_update_dispatch_table_for_class (Class_t);/* (objc-msg.c) */ + +/* 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; + +#ifdef DEBUG +#define DEBUG_PRINTF printf +#else +#define DEBUG_PRINTF +#endif + + +/* standard functions */ +int bcopy(void*, void*, size_t); +int vprintf(const char*, va_list); + + +#endif /* not __objc_runtime_INCLUDE_GNU */ + + diff --git a/gcc/objc/sarray.c b/gcc/objc/sarray.c new file mode 100644 index 00000000000..8bab62c2c81 --- /dev/null +++ b/gcc/objc/sarray.c @@ -0,0 +1,428 @@ +/* Sparse Arrays for Objective C dispatch tables + 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, 675 Mass Ave, Cambridge, MA 02139, 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 +#include +#include "assert.h" + +int nbuckets = 0; +int nindices = 0; +int narrays = 0; +int idxsize = 0; + +#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 + +void +sarray_at_put(struct sarray* array, sidx index, void* element) +{ + struct sindex** the_index; + struct sbucket** the_bucket; +#ifdef OBJC_SPARSE3 + unsigned int ioffset; +#endif + unsigned int boffset; + unsigned int 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 + ioffset = index/INDEX_CAPACITY; + boffset = (index/BUCKET_SIZE)%INDEX_SIZE; + eoffset = index%BUCKET_SIZE; +#endif + + 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 */ + *the_index = (struct sindex*)__objc_xmalloc(sizeof(struct sindex)); + bcopy(array->empty_index, *the_index, sizeof(struct sindex)); + (*the_index)->version = array->version; + the_bucket = &((*the_index)->buckets[boffset]); + nindices += 1; + + } else if ((*the_index)->version != array->version) { + + /* This index must be lazy copied */ + struct sindex* old_index = *the_index; + *the_index = (struct sindex*)__objc_xmalloc(sizeof(struct sindex)); + bcopy(old_index, *the_index, sizeof(struct sindex)); + (*the_index)->version = array->version; + 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 */ + *the_bucket = (struct sbucket*)__objc_xmalloc(sizeof(struct sbucket)); + bcopy(array->empty_bucket, *the_bucket, sizeof(struct sbucket)); + (*the_bucket)->version = array->version; + nbuckets += 1; + + } else if ((*the_bucket)->version != array->version) { + + /* Perform lazy copy. */ + struct sbucket* old_bucket = *the_bucket; + *the_bucket = (struct sbucket*)__objc_xmalloc(sizeof(struct sbucket)); + bcopy(old_bucket, *the_bucket, sizeof(struct sbucket)); + (*the_bucket)->version = array->version; + 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) +{ +#ifdef OBJC_SPARSE3 + unsigned num_indices = ((size-1)/(INDEX_CAPACITY))+1; +#else /* OBJC_SPARSE2 */ + unsigned num_indices = ((size-1)/BUCKET_SIZE)+1; +#endif + int counter; + struct sarray* arr; + + assert(size > 0); + + /* Allocate core array */ + arr = (struct sarray*) __objc_xmalloc(sizeof(struct sarray)); + arr->version = 0; + narrays += 1; + + /* Initialize members */ +#ifdef OBJC_SPARSE3 + arr->capacity = num_indices*INDEX_CAPACITY; + arr->indices = (struct sindex**) + __objc_xmalloc(sizeof(struct sindex*)*num_indices); + idxsize += num_indices; + + arr->empty_index = (struct sindex*) __objc_xmalloc(sizeof(struct sindex)); + arr->empty_index->version = 0; + nindices += 1; + +#else /* OBJC_SPARSE2 */ + arr->capacity = num_indices*BUCKET_SIZE; + arr->buckets = (struct sbucket**) + __objc_xmalloc(sizeof(struct sbucket*)*num_indices); + idxsize += num_indices; + +#endif + + arr->empty_bucket = (struct sbucket*) __objc_xmalloc(sizeof(struct sbucket)); + arr->empty_bucket->version = 0; + nbuckets += 1; + + arr->ref_count = 1; + arr->is_copy_of = (struct sarray*)0; + + for (counter=0; counterempty_bucket->elems[counter] = default_element; + +#ifdef OBJC_SPARSE3 + for (counter=0; counterempty_index->buckets[counter] = arr->empty_bucket; + + for (counter=0; counterindices[counter] = arr->empty_index; + +#else /* OBJC_SPARSE2 */ + + for (counter=0; counterbuckets[counter] = arr->empty_bucket; + +#endif + + return arr; +} + + +/* Reallocate the sparse array to hold `newsize' entries */ + +void +sarray_realloc(struct sarray* array, int newsize) +{ +#ifdef OBJC_SPARSE3 + int old_max_index = (array->capacity-1)/INDEX_CAPACITY; + int new_max_index = ((newsize-1)/INDEX_CAPACITY); + int rounded_size = (new_max_index+1)*INDEX_CAPACITY; + +#else /* OBJC_SPARSE2 */ + int old_max_index = (array->capacity-1)/BUCKET_SIZE; + int new_max_index = ((newsize-1)/BUCKET_SIZE); + int rounded_size = (new_max_index+1)*BUCKET_SIZE; + +#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... */ + + if(rounded_size < array->capacity) + { + /* update capacity */ + array->capacity = rounded_size; + + /* free buckets above new_max_index */ + for(counter = old_max_index; counter > new_max_index; counter-- ) { +#ifdef OBJC_SPARSE3 + struct sindex* idx = array->indices[counter]; + if((idx != array->empty_index) && (idx->version == array->version)) { + int c2; + for(c2=0; c2buckets[c2]; + if((bkt != array->empty_bucket) && (bkt->version == array->version)) + { + free(bkt); + nbuckets -= 1; + } + } + free(idx); + nindices -= 1; + } +#else /* OBJC_SPARSE2 */ + struct sbucket* bkt = array->buckets[counter]; + if ((bkt != array->empty_bucket) && (bkt->version == array->version)) + { + free(bkt); + nbuckets -= 1; + } +#endif + } + +#ifdef OBJC_SPARSE3 + /* realloc to free the space above new_max_index */ + array->indices = (struct sindex**) + __objc_xrealloc(array->indices, + (new_max_index+1)*sizeof(struct sindex*)); +#else /* OBJC_SPARSE2 */ + array->buckets = (struct sbucket**) + __objc_xrealloc(array->buckets, + (new_max_index+1)*sizeof(struct sbucket*)); +#endif + idxsize -= (old_max_index-new_max_index); + + return; + } + + /* We are asked to extend the array -- reallocate the bucket table, */ + /* and insert empty_bucket in newly allocated places. */ + if(rounded_size > array->capacity) + { + /* update capacity */ + array->capacity = rounded_size; + +#ifdef OBJC_SPARSE3 + /* realloc to make room in table above old_max_index */ + array->indices = (struct sindex**) + __objc_xrealloc(array->indices, + (new_max_index+1)*sizeof(struct sindex*)); + + /* reset entries above old_max_index to empty_bucket */ + for(counter = old_max_index+1; counter <= new_max_index; counter++) + array->indices[counter] = array->empty_index; + +#else /* OBJC_SPARSE2 */ + + /* realloc to make room in table above old_max_index */ + array->buckets = (struct sbucket**) + __objc_xrealloc(array->buckets, + (new_max_index+1)*sizeof(struct sbucket*)); + + /* reset entries above old_max_index to empty_bucket */ + for(counter = old_max_index+1; counter <= new_max_index; counter++) + array->buckets[counter] = array->empty_bucket; + +#endif + idxsize += (new_max_index-old_max_index); + return; + } +} + + +/* Free a sparse array allocated with sarray_new */ + +void +sarray_free(struct sarray* array) { +#ifdef OBJC_SPARSE3 + unsigned int old_max_index = (array->capacity-1)/INDEX_CAPACITY; +#else + unsigned int old_max_index = (array->capacity-1)/BUCKET_SIZE; +#endif + int counter = 0; + + assert(array->ref_count != 0); /* Freed multiple times!!! */ + + if(--(array->ref_count) != 0) /* There exists copies of me */ + return; + + 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 = array->indices[counter]; + if((idx != array->empty_index) && (idx->version == array->version)) { + int c2; + for(c2=0; c2buckets[c2]; + if((bkt != array->empty_bucket) && (bkt->version == array->version)) + { + free(bkt); + nbuckets -= 1; + } + } + free(idx); + nindices -= 1; + } +#else /* OBJC_SPARSE2 */ + struct sbucket* bkt = array->buckets[counter]; + if ((bkt != array->empty_bucket) && (bkt->version == array->version)) + { + free(bkt); + nbuckets -= 1; + } +#endif + } + +#ifdef OBJC_SPARSE3 + /* free empty_index */ + if(array->empty_index->version == array->version) { + free(array->empty_index); + nindices -= 1; + } +#endif + + /* free empty_bucket */ + if(array->empty_bucket->version == array->version) { + free(array->empty_bucket); + nbuckets -= 1; + } + +#ifdef OBJC_SPARSE3 + /* free bucket table */ + free(array->indices); + idxsize -= (old_max_index+1); + +#else + /* free bucket table */ + free(array->buckets); + idxsize -= (old_max_index+1); + +#endif + + /* free array */ + free(array); + narrays -= 1; +} + +/* This is a lazy copy. Only the core of the structure is actually */ +/* copied. */ + +struct sarray* +sarray_lazy_copy(struct sarray* oarr) +{ +#ifdef OBJC_SPARSE3 + unsigned num_indices = ((oarr->capacity-1)/INDEX_CAPACITY)+1; +#else /* OBJC_SPARSE2 */ + unsigned num_indices = ((oarr->capacity-1)/BUCKET_SIZE)+1; +#endif + struct sarray* arr; + + /* Allocate core array */ + arr = (struct sarray*) __objc_xmalloc(sizeof(struct sarray)); + bcopy(oarr, arr, sizeof(struct sarray)); + arr->version = oarr->version + 1; + arr->is_copy_of = oarr; + oarr->ref_count += 1; + arr->ref_count = 1; + +#ifdef OBJC_SPARSE3 + /* Copy bucket table */ + arr->indices = (struct sindex**) + __objc_xmalloc(sizeof(struct sindex*)*num_indices); + bcopy(oarr->indices, arr->indices, + sizeof(struct sindex*)*num_indices); +#else + /* Copy bucket table */ + arr->buckets = (struct sbucket**) + __objc_xmalloc(sizeof(struct sbucket*)*num_indices); + bcopy(oarr->buckets, arr->buckets, + sizeof(struct sbucket*)*num_indices); +#endif + + idxsize += num_indices; + narrays += 1; + + return arr; +} diff --git a/gcc/objc/sarray.h b/gcc/objc/sarray.h new file mode 100644 index 00000000000..b22295758c5 --- /dev/null +++ b/gcc/objc/sarray.h @@ -0,0 +1,224 @@ +/* Sparse Arrays for Objective C dispatch tables + Copyright (C) 1993 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, 675 Mass Ave, Cambridge, MA 02139, 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_SPARSE3 /* 2-level sparse array */ +/* #define OBJC_SPARSE2 */ /* 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 + +extern int nbuckets; /* for stats */ +extern int nindices; +extern int narrays; +extern int idxsize; + +#include + +#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< + 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 */ + return array-> + indices[index/INDEX_CAPACITY]-> + buckets[(index/BUCKET_SIZE)%INDEX_SIZE]-> + elems[index%BUCKET_SIZE]; +#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/gcc/objc/selector.c b/gcc/objc/selector.c new file mode 100644 index 00000000000..44a73f64f5d --- /dev/null +++ b/gcc/objc/selector.c @@ -0,0 +1,143 @@ +/* GNU Objective C Runtime selector related functions + Copyright (C) 1993 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, 675 Mass Ave, Cambridge, MA 02139, 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 + +/* Initial selector hash table size. Value doesnt matter much */ +#define SELECTOR_HASH_SIZE 128 + +/* Tables mapping selector names to uid and opposite */ +static struct sarray* __objc_selector_array = 0; /* uid -> name */ +static cache_ptr __objc_selector_hash = 0; /* name -> uid */ + +static void register_selectors_from_list(MethodList_t); + +/* Number of selectors stored in each of the above tables */ +int __objc_selector_max_index = 0; + +void __objc_init_selector_tables() +{ + __objc_selector_array = 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_t 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_name ((char*)method->method_name); + i += 1; + } +} + +/* return selector representing name */ +SEL +sel_get_uid (const char *name) +{ + return (SEL) hash_value_for_key (__objc_selector_hash, name); +} + +/* Get name of selector. If selector is unknown, the empty string "" + is returned */ +const char* +sel_get_name (SEL selector) +{ + if ((soffset_decode((unsigned)selector) > 0) + && (soffset_decode((unsigned)selector) <= __objc_selector_max_index)) + return sarray_get (__objc_selector_array, (sidx) selector); + else + return NULL; +} + +BOOL +sel_is_mapped (SEL selector) +{ + unsigned int idx = soffset_decode ((sidx)selector); + return ((idx > 0) && (idx <= __objc_selector_max_index)); +} + +#ifdef OBJC_SPARSE_LOOKUP +/* The uninstalled dispatch table */ +extern struct sarray* __objc_uninstalled_dtable; +#endif + +/* Store the passed selector name in the selector record and return its + selector value (value returned by sel_get_uid). */ +SEL +sel_register_name (const char *sel) +{ + SEL j; + sidx i; + + if ((j = sel_get_uid ((const char *) sel))) + return j; + + /* Save the selector name. */ + __objc_selector_max_index += 1; + i = soffset_encode(__objc_selector_max_index); + + DEBUG_PRINTF ("Record selector %s as: %#x\n", sel, i); + + sarray_at_put_safe (__objc_selector_array, i, (void *) sel); + hash_add (&__objc_selector_hash, (void *) sel, (void *) i); + +#ifdef OBJC_SPARSE_LOOKUP + sarray_realloc(__objc_uninstalled_dtable, __objc_selector_max_index+1); +#endif + + return (SEL) i; +} + diff --git a/gcc/objc/sendmsg.c b/gcc/objc/sendmsg.c new file mode 100644 index 00000000000..90688a5d008 --- /dev/null +++ b/gcc/objc/sendmsg.c @@ -0,0 +1,574 @@ +/* GNU Objective C Runtime message lookup + Copyright (C) 1993 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, 675 Mass Ave, Cambridge, MA 02139, 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" + +#ifdef OBJC_SPARSE_LOOKUP +const char* __objc_sparse_lookup_id = "Method lookup uses sparse arrays"; +#endif + +#ifdef OBJC_HASH_LOOKUP +const char* __objc_hash_lookup_id = "Method lookup uses hash caching"; +#endif + +#ifdef OBJC_HASH_LOOKUP +#include +#endif + +#ifdef OBJC_SPARSE_LOOKUP +/* The uninstalled dispatch table */ +struct sarray* __objc_uninstalled_dtable = 0; +#endif + +/* Send +initialize to class */ +static void __objc_send_initialize(Class_t); + +static void __objc_install_dispatch_table_for_class (Class_t); + +/* Forward declare some functions */ +#ifdef OBJC_SPARSE_LOOKUP +static void __objc_init_install_dtable(id, SEL); +#endif +static id __objc_missing_method(id, SEL, ...); +static Method_t search_for_method_in_hierarchy (Class_t class, SEL sel); +static Method_t search_for_method_in_list(MethodList_t list, SEL op); +id nil_method(id, SEL, ...); + +id +nil_method(id receiver, SEL op, ...) +{ + return receiver; +} + +/* Given a class and selector, return the selector's implementation. */ +__inline__ IMP +get_imp (Class_t class, SEL sel) +{ +#ifdef OBJC_SPARSE_LOOKUP + void* res = sarray_get (class->dtable, (unsigned int) sel); + if(res == __objc_init_install_dtable) + __objc_install_dispatch_table_for_class (class); + return sarray_get (class->dtable, (unsigned int) sel); +#else + return cache_get (class, sel); +#endif +} + +/* This is the lookup function. All entries in the table are either a + valid method *or* one of `__objc_missing_method' which calls + forward:: etc, or `__objc_init_install_dtable' which installs the + real dtable */ +__inline__ IMP +objc_msg_lookup(id receiver, SEL op) +{ + if(receiver) +#ifdef OBJC_HASH_LOOKUP + return cache_get(receiver->class_pointer, op); +#else + return sarray_get(receiver->class_pointer->dtable, (sidx)op); +#endif + 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; +} + +retval_t +objc_msg_sendv(id object, SEL op, size_t frame_size, arglist_t arg_frame) +{ +#ifdef __objc_frame_receiver + __objc_frame_receiver(arg_frame) = object; + __objc_frame_selector(arg_frame) = op; + return __builtin_apply((apply_t)get_imp(object->class_pointer, op), + arg_frame, + frame_size); +#else +#warning performv:: will not work + (*_objc_error)(object, "objc_msg_sendv (performv::) not supported\n", 0); + return 0; +#endif +} + +void __objc_init_dispatch_tables() +{ +#ifdef OBJC_SPARSE_LOOKUP + __objc_uninstalled_dtable + = sarray_new(200, __objc_init_install_dtable); +#endif +} + +#ifdef OBJC_SPARSE_LOOKUP +/* This one is a bit hairy. This function is installed in the + premature dispatch table, and thus called once for each class, + namely when the very first message is send to it. */ + +static void __objc_init_install_dtable(id receiver, SEL op) +{ + __label__ allready_initialized; + IMP imp; + void* args; + void* result; + + /* If the class has not yet had it's class links resolved, we must + re-compute all class links */ + if(!CLS_ISRESOLV(receiver->class_pointer)) + __objc_resolve_class_links(); + + /* 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) + goto allready_initialized; + + 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_t)receiver)); + assert(CLS_ISMETA(receiver->class_pointer)); + + /* Install real dtable for factory methods */ + __objc_install_dispatch_table_for_class (receiver->class_pointer); + + if(op != sel_get_uid ("initialize")) + __objc_send_initialize((Class_t)receiver); + else + CLS_SETINITIALIZED((Class_t)receiver); + } + +allready_initialized: + + /* Get real method for this in newly installed dtable */ + imp = get_imp(receiver->class_pointer, op); + + args = __builtin_apply_args(); + result = __builtin_apply((apply_t)imp, args, 96); + __builtin_return (result); + +} +#endif + +/* 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_t class) +{ +#ifdef OBJC_SPARSE_LOOKUP + assert(__objc_uninstalled_dtable); + class->dtable = __objc_uninstalled_dtable; +#else + class->cache = (Cache_t)__objc_xcalloc(1, sizeof(Cache)); +#endif +} + +/* Send +initialize to class if not already done */ +static void __objc_send_initialize(Class_t class) +{ + Method_t m; + IMP imp; + + /* 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); + + if(class->super_class) + __objc_send_initialize(class->super_class); + + m = search_for_method_in_list(class->class_pointer->methods, + sel_get_uid("initialize")); + if(m != NULL) + { + CLS_SETINITIALIZED(class); + (*m->method_imp) ((id) class, sel_get_uid("initialize")); + } + } +} + +static void +__objc_install_dispatch_table_for_class (Class_t class) +{ +#ifdef OBJC_SPARSE_LOOKUP + Class_t super = class->super_class; + MethodList_t mlist; + int counter; + + if (super != 0 && (super->dtable == __objc_uninstalled_dtable)) + __objc_install_dispatch_table_for_class (super); + + /* Allocate dtable if nessecary */ + if (super == 0) + { + class->dtable = sarray_new (__objc_selector_max_index, + __objc_missing_method); + } + else + class->dtable = sarray_lazy_copy (super->dtable); + + for (mlist = class->methods; mlist; mlist = mlist->method_next) + { + counter = mlist->method_count - 1; + while (counter >= 0) + { + Method_t method = &(mlist->method_list[counter]); + sarray_at_put (class->dtable, + (sidx) method->method_name, + method->method_imp); + counter -= 1; + } + } +#endif +} + +void __objc_update_dispatch_table_for_class (Class_t class) +{ + Class_t next; +#ifdef OBJC_SPARSE_LOOKUP + struct sarray* save; +#else + Cache_t save; +#endif + + /* not yet installed -- skip it */ +#ifdef OBJC_SPARSE_LOOKUP + if (class->dtable == __objc_uninstalled_dtable) +#else + if (class->cache->mask == 0) +#endif + return; + +#ifdef OBJC_SPARSE_LOOKUP + save = class->dtable; + __objc_install_premature_dtable (class); + sarray_free (save); + +#else + save = class->cache; + __objc_install_premature_dtable (class); + free(save); + +#endif + + if (class->subclass_list) /* Traverse subclasses */ + for (next = class->subclass_list; next; next = next->sibling_class) + __objc_update_dispatch_table_for_class (next); +} + + +/* 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 issued. + + This one is only called for categories. Class objects have their + methods installed rightaway, and their selectors are made into + SEL's by the function __objc_register_selectors_from_class. */ +void +class_add_method_list (Class_t 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 transmogriffed to SEL's */ + method->method_name = sel_register_name ((char*)method->method_name); + + if (search_for_method_in_list (class->methods, method->method_name)) + { + /* Duplication. Print a error message an change the method name + to NULL. */ + fprintf (stderr, "attempt to add a existing method: %s\n", + sel_get_name(method->method_name)); + method->method_name = 0; + } + } + } + + /* Add the methods to the class's method list. */ + list->method_next = class->methods; + class->methods = list; +} + + +Method_t +class_get_instance_method(Class_t class, SEL op) +{ + return search_for_method_in_hierarchy(class, op); +} + +Method_t +class_get_class_method(MetaClass_t 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_t cls, SEL sel) +{ + Method_t method = NULL; + Class_t 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. */ +static 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 == op) + return method; + } + + /* The method wasn't found. Follow the link to the next list of + methods. */ + method_list = method_list->method_next; + } + + return NULL; +} + + +/* This fuction is installed in the dispatch table for all methods which are + not implemented. Thus, it is called when a selector is not recognized. */ +static id +__objc_missing_method (id object, SEL sel, ...) +{ + IMP imp; + SEL frwd_sel; + SEL err_sel; + + /* first try if the object understands forward:: */ + frwd_sel = sel_get_uid("forward::"); + imp = get_imp(object->class_pointer, frwd_sel); + if(imp != __objc_missing_method) + { + void *result, *args = __builtin_apply_args(); + result = (*imp)(object, frwd_sel, sel, args); + __builtin_return(result); + } + + /* If the object recognizes the doesNotRecognize: method then we're going + to send it. */ + err_sel = sel_get_uid ("doesNotRecognize:"); + imp = get_imp (object->class_pointer, err_sel); + if (imp != __objc_missing_method) + { + return (*imp) (object, err_sel, sel); + } + + /* The object doesn't recognize the method. Check for responding to + error:. If it does then sent it. */ + { + char msg[256 + strlen (sel_get_name (sel)) + + strlen (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_uid ("error:"); + imp = get_imp (object->class_pointer, err_sel); + if (imp != __objc_missing_method) + return (*imp) (object, sel_get_uid ("error:"), msg); + + /* The object doesn't respond to doesNotRecognize: or error:; Therefore, + a default action is taken. */ + fprintf (stderr, "fatal: %s\n", msg); + abort (); + } +} + +int __objc_print_dtable_stats() +{ + int total = 0; + printf("memory usage: (%s)\n", +#ifdef OBJC_SPARSE_LOOKUP +#ifdef OBJC_SPARSE2 + "2-level sparse arrays" +#else + "3-level sparse arrays" +#endif +#else + "hash-cache" +#endif + ); + +#ifdef OBJC_SPARSE_LOOKUP + printf("arrays: %d = %d bytes\n", narrays, narrays*sizeof(struct sarray)); + total += narrays*sizeof(struct sarray); +#ifdef OBJC_SPARSE3 + printf("indices: %d = %d bytes\n", nindices, nindices*sizeof(struct sindex)); + total += nindices*sizeof(struct sindex); +#endif + printf("buckets: %d = %d bytes\n", nbuckets, nbuckets*sizeof(struct sbucket)); + total += nbuckets*sizeof(struct sbucket); + + printf("idxtables: %d = %d bytes\n", idxsize, idxsize*sizeof(void*)); + total += idxsize*sizeof(void*); +#else /* HASH_LOOKUP */ + total = __objc_class_hash_tables_size (); +#endif + printf("-----------------------------------\n"); + printf("total: %d bytes\n", total); + printf("===================================\n"); + } + +#ifdef OBJC_HASH_LOOKUP +static Cache_t __objc_cache_insert(Cache_t cache, SEL op, IMP imp); + +static Cache_t +__objc_double_cache(Cache_t cache) +{ + int i; + Cache_t newc = (Cache_t)__objc_xcalloc(1, sizeof(Cache) + +(sizeof(Cache)*2*(cache->mask+1))); + newc->occupied = cache->occupied; + newc->mask = ((cache->mask)<<1) | 1; + for(i=0; i <= cache->mask; i++) + newc = __objc_cache_insert(newc, + cache->buckets[i].method_selector, + cache->buckets[i].method_imp); + free(cache); + return newc; +} + + +static Cache_t +__objc_cache_insert(Cache_t cache, SEL op, IMP imp) +{ + int index = ((unsigned int)op)&(cache)->mask; + + if(op == 0) + return cache; + + do + { + if((cache)->buckets[index].method_selector == 0) + { + (cache)->buckets[index].method_selector = op; + (cache)->buckets[index].method_imp = imp; + (cache)->occupied += 1; + return cache; + } + } + while (--index >= 0); + + cache = __objc_double_cache(cache); + return __objc_cache_insert(cache, op, imp); +} + +void* +__objc_cache_miss(Class_t class, SEL op) +{ + Method_t m; + Cache_t cache = class->cache; + + if(!CLS_ISRESOLV(class)) + __objc_resolve_class_links(); + + m = search_for_method_in_hierarchy(class, op); + + if(!CLS_ISINITIALIZED(class)) + if(CLS_ISMETA(class)) + __objc_send_initialize(objc_get_class(class->name)); + else + __objc_send_initialize(class); + + if(m == NULL) + return __objc_missing_method; + + if((cache->occupied+2)*2 > cache->mask) + class->cache = __objc_double_cache(cache); + + class->cache = __objc_cache_insert(class->cache, op, m->method_imp); + return m->method_imp; +} + +#endif + + -- 2.30.2