Commit c72fc2d9 by Tom Wood

Initial revision

From-SVN: r4077
parent b2055d6d
/* 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 <objc/objc.h>
/*
* 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
/* 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 <objc/Object.h>
#include <objc/Protocol.h>
#include <objc/objc-api.h>
#include <objc/objc-archive.h>
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
/* 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 <objc/Object.h>
@interface Protocol : Object
{
@private
char *protocol_name;
struct objc_protocol_list *protocol_list;
struct objc_method_description_list *instance_methods, *class_methods;
}
/* Obtaining attributes intrinsic to the protocol */
- (const char *)name;
/* Testing protocol conformance */
- (BOOL) conformsTo: (Protocol *)aProtocolObject;
/* Looking up information specific to a protocol */
- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel;
- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel;
@end
#endif __Protocol_INCLUDE_GNU
/* 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 <objc/Protocol.h>
#include <objc/objc-api.h>
/* Method description list */
struct objc_method_description_list {
int count;
struct objc_method_description list[1];
};
@implementation Protocol
{
@private
char *protocol_name;
struct objc_protocol_list *protocol_list;
struct objc_method_description_list *instance_methods, *class_methods;
}
/* Obtaining attributes intrinsic to the protocol */
- (const char *)name
{
return protocol_name;
}
/* Testing protocol conformance */
- (BOOL) conformsTo: (Protocol *)aProtocolObject
{
int i;
struct objc_protocol_list* proto_list;
if (!strcmp(aProtocolObject->protocol_name, self->protocol_name))
return YES;
for (proto_list = protocol_list; proto_list; proto_list = proto_list->next)
{
for (i=0; i < proto_list->count; i++)
{
if ([proto_list->list[i] conformsTo: aProtocolObject])
return YES;
}
}
return NO;
}
/* Looking up information specific to a protocol */
- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel
{
int i;
struct objc_protocol_list* proto_list;
const char* name = sel_get_name (aSel);
struct objc_method_description *result;
for (i = 0; i < instance_methods->count; i++)
{
if (!strcmp ((char*)instance_methods->list[i].name, name))
return &(instance_methods->list[i]);
}
for (proto_list = protocol_list; proto_list; proto_list = proto_list->next)
{
for (i=0; i < proto_list->count; i++)
{
if ((result = [proto_list->list[i]
descriptionForInstanceMethod: aSel]))
return result;
}
}
return NULL;
}
- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel;
{
int i;
struct objc_protocol_list* proto_list;
const char* name = sel_get_name (aSel);
struct objc_method_description *result;
for (i = 0; i < class_methods->count; i++)
{
if (!strcmp ((char*)class_methods->list[i].name, name))
return &(class_methods->list[i]);
}
for (proto_list = protocol_list; proto_list; proto_list = proto_list->next)
{
for (i=0; i < proto_list->count; i++)
{
if ((result = [proto_list->list[i]
descriptionForClassMethod: aSel]))
return result;
}
}
return NULL;
}
@end
/* 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 <objc/objc-archive.h>
#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<type>]", data)
*/
int
objc_write_array (TypedStream* stream, const char* type,
int count, const void* data)
{
int off = objc_sizeof_type(type);
const char* where = data;
while (count-- > 0)
{
objc_write_type(stream, type, where);
where += off;
}
return 1;
}
/*
** Read an array of COUNT elements of TYPE into the memory address
** DATA. The memory pointed to by data is supposed to be allocated
** by the callee. This is equivalent of
** objc_read_type (stream, "[N<type>]", data)
*/
int
objc_read_array (TypedStream* stream, const char* type,
int count, void* data)
{
int off = objc_sizeof_type(type);
char* where = (char*)data;
while (count-- > 0)
{
objc_read_type(stream, type, where);
where += off;
}
return 1;
}
static int
__objc_fread(FILE* file, char* data, int len)
{
return fread(data, len, 1, file);
}
static int
__objc_fwrite(FILE* file, char* data, int len)
{
return fwrite(data, len, 1, file);
}
static int
__objc_feof(FILE* file)
{
return feof(file);
}
static int
__objc_no_write(FILE* file, char* data, int len)
{
__objc_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);
}
/* 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
/* 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;
}
/* 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;
}
/* 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 <stdlib.h>
#include <objc/objc.h>
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 */
/* 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;
}
/* 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 <objc/objc.h> /* core data types */
#include <objc/objc-api.h> /* runtime api functions */
#include <objc/hash.h> /* hash structures */
#include <objc/list.h> /* linear lists */
#include <stdio.h> /* argh! I hate this */
#include <stdarg.h> /* for varargs and va_list's */
#include <stdlib.h>
#include <assert.h>
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 */
/* 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 <objc/sarray.h>
#include <stdio.h>
#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; counter<BUCKET_SIZE; counter++)
arr->empty_bucket->elems[counter] = default_element;
#ifdef OBJC_SPARSE3
for (counter=0; counter<INDEX_SIZE; counter++)
arr->empty_index->buckets[counter] = arr->empty_bucket;
for (counter=0; counter<num_indices; counter++)
arr->indices[counter] = arr->empty_index;
#else /* OBJC_SPARSE2 */
for (counter=0; counter<num_indices; counter++)
arr->buckets[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; c2<INDEX_SIZE; c2++) {
struct sbucket* bkt = idx->buckets[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; c2<INDEX_SIZE; c2++) {
struct sbucket* bkt = idx->buckets[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;
}
/* 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 <stddef.h>
extern int nbuckets; /* for stats */
extern int nindices;
extern int narrays;
extern int idxsize;
#include <assert.h>
#if defined(sparc) || defined(OBJC_SPARSE2)
#define PRECOMPUTE_SELECTORS
#endif
#ifdef OBJC_SPARSE3
/* Buckets are 8 words each */
#define BUCKET_BITS 3
#define BUCKET_SIZE (1<<BUCKET_BITS)
#define BUCKET_MASK (BUCKET_SIZE-1)
/* Indices are 16 words each */
#define INDEX_BITS 4
#define INDEX_SIZE (1<<INDEX_BITS)
#define INDEX_MASK (INDEX_SIZE-1)
#define INDEX_CAPACITY (BUCKET_SIZE*INDEX_SIZE)
#else /* OBJC_SPARSE2 */
/* Buckets are 32 words each */
#define BUCKET_BITS 5
#define BUCKET_SIZE (1<<BUCKET_BITS)
#define BUCKET_MASK (BUCKET_SIZE-1)
#endif /* OBJC_SPARSE2 */
typedef unsigned int sidx;
#ifdef PRECOMPUTE_SELECTORS
struct soffset {
#ifdef OBJC_SPARSE3
unsigned char unused;
unsigned char eoffset;
unsigned char boffset;
unsigned char ioffset;
#else /* OBJC_SPARSE2 */
#ifdef sparc
unsigned int boffset : 30 - BUCKET_BITS;
unsigned int eoffset : BUCKET_BITS;
unsigned int unused : 2;
#else
unsigned short boffset;
unsigned short eoffset;
#endif
#endif /* OBJC_SPARSE2 */
};
union sofftype {
struct soffset off;
sidx idx;
};
#endif /* not PRECOMPUTE_SELECTORS */
void * __objc_xrealloc (void *optr, size_t size);
void * __objc_xmalloc (size_t size);
struct sbucket {
void* elems[BUCKET_SIZE]; /* elements stored in array */
short version; /* used for copy-on-write */
};
#ifdef OBJC_SPARSE3
struct sindex {
struct sbucket* buckets[INDEX_SIZE];
short version;
};
#endif /* OBJC_SPARSE3 */
struct sarray {
#ifdef OBJC_SPARSE3
struct sindex** indices;
struct sindex* empty_index;
#else /* OBJC_SPARSE2 */
struct sbucket** buckets;
#endif /* OBJC_SPARSE2 */
struct sbucket* empty_bucket;
short version;
short ref_count;
struct sarray* is_copy_of;
int capacity;
};
struct sarray* sarray_new(int, void* default_element);
void sarray_free(struct sarray*);
struct sarray* sarray_lazy_copy(struct sarray*);
struct sarray* sarray_hard_copy(struct sarray*); /* ... like the name? */
void sarray_realloc(struct sarray*, int new_size);
void sarray_at_put(struct sarray*, sidx index, void* elem);
void sarray_at_put_safe(struct sarray*, sidx index, void* elem);
#ifdef PRECOMPUTE_SELECTORS
/* Transform soffset values to ints and vica verca */
static inline unsigned int
soffset_decode(sidx index)
{
union sofftype x;
x.idx = index;
#ifdef OBJC_SPARSE3
return x.off.eoffset
+ (x.off.boffset*BUCKET_SIZE)
+ (x.off.ioffset*INDEX_CAPACITY);
#else /* OBJC_SPARSE2 */
return x.off.eoffset + (x.off.boffset*BUCKET_SIZE);
#endif /* OBJC_SPARSE2 */
}
static inline sidx
soffset_encode(unsigned int offset)
{
union sofftype x;
x.off.eoffset = offset%BUCKET_SIZE;
#ifdef OBJC_SPARSE3
x.off.boffset = (offset/BUCKET_SIZE)%INDEX_SIZE;
x.off.ioffset = offset/INDEX_CAPACITY;
#else /* OBJC_SPARSE2 */
x.off.boffset = offset/BUCKET_SIZE;
#endif
return (sidx)x.idx;
}
#else /* not PRECOMPUTE_SELECTORS */
static inline unsigned int
soffset_decode(sidx index)
{
return index;
}
static inline sidx
soffset_encode(unsigned int offset)
{
return offset;
}
#endif /* not PRECOMPUTE_SELECTORS */
/* Get element from the Sparse array `array' at offset `index' */
static inline void* sarray_get(struct sarray* array, sidx index)
{
#ifdef PRECOMPUTE_SELECTORS
union sofftype x;
x.idx = index;
#ifdef OBJC_SPARSE3
return
array->
indices[x.off.ioffset]->
buckets[x.off.boffset]->
elems[x.off.eoffset];
#else /* OBJC_SPARSE2 */
return array->buckets[x.off.boffset]->elems[x.off.eoffset];
#endif /* OBJC_SPARSE2 */
#else /* not PRECOMPUTE_SELECTORS */
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 */
/* 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 <objc/sarray.h>
/* 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;
}
/* 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 <objc/cache.h>
#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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment